first commit
SAP-ERP Portal CI/CD / build (push) Failing after 5m20s

This commit is contained in:
John
2026-09-23 17:31:02 +05:30
commit 69b4e68baf
51657 changed files with 3864077 additions and 0 deletions
+122
View File
@@ -0,0 +1,122 @@
import type { Logger } from "../client.mjs";
import { AnthropicError, APIUserAbortError } from "../error.mjs";
import { type RequestOptions } from "../internal/request-options.mjs";
import { type BetaContentBlock, type BetaMCPToolUseBlock, type BetaMessage, type BetaMessageParam, Messages as BetaMessages, type BetaRawMessageStreamEvent as BetaMessageStreamEvent, type BetaServerToolUseBlock, type BetaTextCitation, type BetaToolUseBlock, type MessageCreateParams, type MessageCreateParamsBase } from "../resources/beta/messages/messages.mjs";
import { type ParsedBetaMessage } from "./beta-parser.mjs";
export interface MessageStreamEvents {
connect: () => void;
streamEvent: (event: BetaMessageStreamEvent, snapshot: BetaMessage) => void;
text: (textDelta: string, textSnapshot: string) => void;
citation: (citation: BetaTextCitation, citationsSnapshot: BetaTextCitation[]) => void;
inputJson: (partialJson: string, jsonSnapshot: unknown) => void;
thinking: (thinkingDelta: string, thinkingSnapshot: string) => void;
signature: (signature: string) => void;
compaction: (compactedContent: string) => void;
message: (message: BetaMessage) => void;
contentBlock: (content: BetaContentBlock) => void;
finalMessage: (message: BetaMessage) => void;
error: (error: AnthropicError) => void;
abort: (error: APIUserAbortError) => void;
end: () => void;
}
export type TracksToolInput = BetaToolUseBlock | BetaServerToolUseBlock | BetaMCPToolUseBlock;
export declare class BetaMessageStream<ParsedT = null> implements AsyncIterable<BetaMessageStreamEvent> {
#private;
messages: BetaMessageParam[];
receivedMessages: ParsedBetaMessage<ParsedT>[];
controller: AbortController;
constructor(params: MessageCreateParamsBase | null, opts?: {
logger?: Logger | undefined;
});
get response(): Response | null | undefined;
get request_id(): string | null | undefined;
/**
* Returns the `MessageStream` data, the raw `Response` instance and the ID of the request,
* returned vie the `request-id` header which is useful for debugging requests and resporting
* issues to Anthropic.
*
* This is the same as the `APIPromise.withResponse()` method.
*
* This method will raise an error if you created the stream using `MessageStream.fromReadableStream`
* as no `Response` is available.
*/
withResponse(): Promise<{
data: BetaMessageStream<ParsedT>;
response: Response;
request_id: string | null | undefined;
}>;
/**
* Intended for use on the frontend, consuming a stream produced with
* `.toReadableStream()` on the backend.
*
* Note that messages sent to the model do not appear in `.on('message')`
* in this context.
*/
static fromReadableStream(stream: ReadableStream): BetaMessageStream;
static createMessage<ParsedT>(messages: BetaMessages, params: MessageCreateParamsBase, options?: RequestOptions, { logger }?: {
logger?: Logger | undefined;
}): BetaMessageStream<ParsedT>;
protected _run(executor: () => Promise<any>): void;
protected _addMessageParam(message: BetaMessageParam): void;
protected _addMessage(message: ParsedBetaMessage<ParsedT>, emit?: boolean): void;
protected _createMessage(messages: BetaMessages, params: MessageCreateParams, options?: RequestOptions): Promise<void>;
protected _connected(response: Response | null): void;
get ended(): boolean;
get errored(): boolean;
get aborted(): boolean;
abort(): void;
/**
* Adds the listener function to the end of the listeners array for the event.
* No checks are made to see if the listener has already been added. Multiple calls passing
* the same combination of event and listener will result in the listener being added, and
* called, multiple times.
* @returns this MessageStream, so that calls can be chained
*/
on<Event extends keyof MessageStreamEvents>(event: Event, listener: MessageStreamEvents[Event]): this;
/**
* Removes the specified listener from the listener array for the event.
* off() will remove, at most, one instance of a listener from the listener array. If any single
* listener has been added multiple times to the listener array for the specified event, then
* off() must be called multiple times to remove each instance.
* @returns this MessageStream, so that calls can be chained
*/
off<Event extends keyof MessageStreamEvents>(event: Event, listener: MessageStreamEvents[Event]): this;
/**
* Adds a one-time listener function for the event. The next time the event is triggered,
* this listener is removed and then invoked.
* @returns this MessageStream, so that calls can be chained
*/
once<Event extends keyof MessageStreamEvents>(event: Event, listener: MessageStreamEvents[Event]): this;
/**
* This is similar to `.once()`, but returns a Promise that resolves the next time
* the event is triggered, instead of calling a listener callback.
* @returns a Promise that resolves the next time given event is triggered,
* or rejects if an error is emitted. (If you request the 'error' event,
* returns a promise that resolves with the error).
*
* Example:
*
* const message = await stream.emitted('message') // rejects if the stream errors
*/
emitted<Event extends keyof MessageStreamEvents>(event: Event): Promise<Parameters<MessageStreamEvents[Event]> extends [infer Param] ? Param : Parameters<MessageStreamEvents[Event]> extends [] ? void : Parameters<MessageStreamEvents[Event]>>;
done(): Promise<void>;
get currentMessage(): BetaMessage | undefined;
/**
* @returns a promise that resolves with the the final assistant Message response,
* or rejects if an error occurred or the stream ended prematurely without producing a Message.
* If structured outputs were used, this will be a ParsedMessage with a `parsed` field.
*/
finalMessage(): Promise<ParsedBetaMessage<ParsedT>>;
/**
* @returns a promise that resolves with the the final assistant Message's text response, concatenated
* together if there are more than one text blocks.
* Rejects if an error occurred or the stream ended prematurely without producing a Message.
*/
finalText(): Promise<string>;
protected _emit<Event extends keyof MessageStreamEvents>(event: Event, ...args: Parameters<MessageStreamEvents[Event]>): void;
protected _emitFinal(): void;
protected _fromReadableStream(readableStream: ReadableStream, options?: RequestOptions): Promise<void>;
[Symbol.asyncIterator](): AsyncIterator<BetaMessageStreamEvent>;
toReadableStream(): ReadableStream;
}
//# sourceMappingURL=BetaMessageStream.d.mts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"BetaMessageStream.d.mts","sourceRoot":"","sources":["../src/lib/BetaMessageStream.ts"],"names":[],"mappings":"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"}
+122
View File
@@ -0,0 +1,122 @@
import type { Logger } from "../client.js";
import { AnthropicError, APIUserAbortError } from "../error.js";
import { type RequestOptions } from "../internal/request-options.js";
import { type BetaContentBlock, type BetaMCPToolUseBlock, type BetaMessage, type BetaMessageParam, Messages as BetaMessages, type BetaRawMessageStreamEvent as BetaMessageStreamEvent, type BetaServerToolUseBlock, type BetaTextCitation, type BetaToolUseBlock, type MessageCreateParams, type MessageCreateParamsBase } from "../resources/beta/messages/messages.js";
import { type ParsedBetaMessage } from "./beta-parser.js";
export interface MessageStreamEvents {
connect: () => void;
streamEvent: (event: BetaMessageStreamEvent, snapshot: BetaMessage) => void;
text: (textDelta: string, textSnapshot: string) => void;
citation: (citation: BetaTextCitation, citationsSnapshot: BetaTextCitation[]) => void;
inputJson: (partialJson: string, jsonSnapshot: unknown) => void;
thinking: (thinkingDelta: string, thinkingSnapshot: string) => void;
signature: (signature: string) => void;
compaction: (compactedContent: string) => void;
message: (message: BetaMessage) => void;
contentBlock: (content: BetaContentBlock) => void;
finalMessage: (message: BetaMessage) => void;
error: (error: AnthropicError) => void;
abort: (error: APIUserAbortError) => void;
end: () => void;
}
export type TracksToolInput = BetaToolUseBlock | BetaServerToolUseBlock | BetaMCPToolUseBlock;
export declare class BetaMessageStream<ParsedT = null> implements AsyncIterable<BetaMessageStreamEvent> {
#private;
messages: BetaMessageParam[];
receivedMessages: ParsedBetaMessage<ParsedT>[];
controller: AbortController;
constructor(params: MessageCreateParamsBase | null, opts?: {
logger?: Logger | undefined;
});
get response(): Response | null | undefined;
get request_id(): string | null | undefined;
/**
* Returns the `MessageStream` data, the raw `Response` instance and the ID of the request,
* returned vie the `request-id` header which is useful for debugging requests and resporting
* issues to Anthropic.
*
* This is the same as the `APIPromise.withResponse()` method.
*
* This method will raise an error if you created the stream using `MessageStream.fromReadableStream`
* as no `Response` is available.
*/
withResponse(): Promise<{
data: BetaMessageStream<ParsedT>;
response: Response;
request_id: string | null | undefined;
}>;
/**
* Intended for use on the frontend, consuming a stream produced with
* `.toReadableStream()` on the backend.
*
* Note that messages sent to the model do not appear in `.on('message')`
* in this context.
*/
static fromReadableStream(stream: ReadableStream): BetaMessageStream;
static createMessage<ParsedT>(messages: BetaMessages, params: MessageCreateParamsBase, options?: RequestOptions, { logger }?: {
logger?: Logger | undefined;
}): BetaMessageStream<ParsedT>;
protected _run(executor: () => Promise<any>): void;
protected _addMessageParam(message: BetaMessageParam): void;
protected _addMessage(message: ParsedBetaMessage<ParsedT>, emit?: boolean): void;
protected _createMessage(messages: BetaMessages, params: MessageCreateParams, options?: RequestOptions): Promise<void>;
protected _connected(response: Response | null): void;
get ended(): boolean;
get errored(): boolean;
get aborted(): boolean;
abort(): void;
/**
* Adds the listener function to the end of the listeners array for the event.
* No checks are made to see if the listener has already been added. Multiple calls passing
* the same combination of event and listener will result in the listener being added, and
* called, multiple times.
* @returns this MessageStream, so that calls can be chained
*/
on<Event extends keyof MessageStreamEvents>(event: Event, listener: MessageStreamEvents[Event]): this;
/**
* Removes the specified listener from the listener array for the event.
* off() will remove, at most, one instance of a listener from the listener array. If any single
* listener has been added multiple times to the listener array for the specified event, then
* off() must be called multiple times to remove each instance.
* @returns this MessageStream, so that calls can be chained
*/
off<Event extends keyof MessageStreamEvents>(event: Event, listener: MessageStreamEvents[Event]): this;
/**
* Adds a one-time listener function for the event. The next time the event is triggered,
* this listener is removed and then invoked.
* @returns this MessageStream, so that calls can be chained
*/
once<Event extends keyof MessageStreamEvents>(event: Event, listener: MessageStreamEvents[Event]): this;
/**
* This is similar to `.once()`, but returns a Promise that resolves the next time
* the event is triggered, instead of calling a listener callback.
* @returns a Promise that resolves the next time given event is triggered,
* or rejects if an error is emitted. (If you request the 'error' event,
* returns a promise that resolves with the error).
*
* Example:
*
* const message = await stream.emitted('message') // rejects if the stream errors
*/
emitted<Event extends keyof MessageStreamEvents>(event: Event): Promise<Parameters<MessageStreamEvents[Event]> extends [infer Param] ? Param : Parameters<MessageStreamEvents[Event]> extends [] ? void : Parameters<MessageStreamEvents[Event]>>;
done(): Promise<void>;
get currentMessage(): BetaMessage | undefined;
/**
* @returns a promise that resolves with the the final assistant Message response,
* or rejects if an error occurred or the stream ended prematurely without producing a Message.
* If structured outputs were used, this will be a ParsedMessage with a `parsed` field.
*/
finalMessage(): Promise<ParsedBetaMessage<ParsedT>>;
/**
* @returns a promise that resolves with the the final assistant Message's text response, concatenated
* together if there are more than one text blocks.
* Rejects if an error occurred or the stream ended prematurely without producing a Message.
*/
finalText(): Promise<string>;
protected _emit<Event extends keyof MessageStreamEvents>(event: Event, ...args: Parameters<MessageStreamEvents[Event]>): void;
protected _emitFinal(): void;
protected _fromReadableStream(readableStream: ReadableStream, options?: RequestOptions): Promise<void>;
[Symbol.asyncIterator](): AsyncIterator<BetaMessageStreamEvent>;
toReadableStream(): ReadableStream;
}
//# sourceMappingURL=BetaMessageStream.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"BetaMessageStream.d.ts","sourceRoot":"","sources":["../src/lib/BetaMessageStream.ts"],"names":[],"mappings":"OACO,KAAK,EAAE,MAAM,EAAE;OACf,EAAE,cAAc,EAAE,iBAAiB,EAAE;OAErC,EAAE,KAAK,cAAc,EAAE;OACvB,EACL,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,gBAAgB,EACrB,QAAQ,IAAI,YAAY,EACxB,KAAK,yBAAyB,IAAI,sBAAsB,EACxD,KAAK,sBAAsB,EAE3B,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAE7B;OAEM,EAAyB,KAAK,iBAAiB,EAAE;AAExD,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,WAAW,EAAE,CAAC,KAAK,EAAE,sBAAsB,EAAE,QAAQ,EAAE,WAAW,KAAK,IAAI,CAAC;IAC5E,IAAI,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACxD,QAAQ,EAAE,CAAC,QAAQ,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,KAAK,IAAI,CAAC;IACtF,SAAS,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,KAAK,IAAI,CAAC;IAChE,QAAQ,EAAE,CAAC,aAAa,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,KAAK,IAAI,CAAC;IACpE,SAAS,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,UAAU,EAAE,CAAC,gBAAgB,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/C,OAAO,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IACxC,YAAY,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAClD,YAAY,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,IAAI,CAAC;IAC7C,KAAK,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IACvC,KAAK,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAC1C,GAAG,EAAE,MAAM,IAAI,CAAC;CACjB;AASD,MAAM,MAAM,eAAe,GAAG,gBAAgB,GAAG,sBAAsB,GAAG,mBAAmB,CAAC;AAM9F,qBAAa,iBAAiB,CAAC,OAAO,GAAG,IAAI,CAAE,YAAW,aAAa,CAAC,sBAAsB,CAAC;;IAC7F,QAAQ,EAAE,gBAAgB,EAAE,CAAM;IAClC,gBAAgB,EAAE,iBAAiB,CAAC,OAAO,CAAC,EAAE,CAAM;IAIpD,UAAU,EAAE,eAAe,CAAyB;gBAoBxC,MAAM,EAAE,uBAAuB,GAAG,IAAI,EAAE,IAAI,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE;IAsB1F,IAAI,QAAQ,IAAI,QAAQ,GAAG,IAAI,GAAG,SAAS,CAE1C;IAED,IAAI,UAAU,IAAI,MAAM,GAAG,IAAI,GAAG,SAAS,CAE1C;IAED;;;;;;;;;OASG;IACG,YAAY,IAAI,OAAO,CAAC;QAC5B,IAAI,EAAE,iBAAiB,CAAC,OAAO,CAAC,CAAC;QACjC,QAAQ,EAAE,QAAQ,CAAC;QACnB,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;KACvC,CAAC;IAeF;;;;;;OAMG;IACH,MAAM,CAAC,kBAAkB,CAAC,MAAM,EAAE,cAAc,GAAG,iBAAiB;IAMpE,MAAM,CAAC,aAAa,CAAC,OAAO,EAC1B,QAAQ,EAAE,YAAY,EACtB,MAAM,EAAE,uBAAuB,EAC/B,OAAO,CAAC,EAAE,cAAc,EACxB,EAAE,MAAM,EAAE,GAAE;QAAE,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAO,GAC/C,iBAAiB,CAAC,OAAO,CAAC;IAgB7B,SAAS,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC;IAO3C,SAAS,CAAC,gBAAgB,CAAC,OAAO,EAAE,gBAAgB;IAIpD,SAAS,CAAC,WAAW,CAAC,OAAO,EAAE,iBAAiB,CAAC,OAAO,CAAC,EAAE,IAAI,UAAO;cAOtD,cAAc,CAC5B,QAAQ,EAAE,YAAY,EACtB,MAAM,EAAE,mBAAmB,EAC3B,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,IAAI,CAAC;IA4BhB,SAAS,CAAC,UAAU,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI;IAQ9C,IAAI,KAAK,IAAI,OAAO,CAEnB;IAED,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,KAAK;IAIL;;;;;;OAMG;IACH,EAAE,CAAC,KAAK,SAAS,MAAM,mBAAmB,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,mBAAmB,CAAC,KAAK,CAAC,GAAG,IAAI;IAOrG;;;;;;OAMG;IACH,GAAG,CAAC,KAAK,SAAS,MAAM,mBAAmB,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,mBAAmB,CAAC,KAAK,CAAC,GAAG,IAAI;IAQtG;;;;OAIG;IACH,IAAI,CAAC,KAAK,SAAS,MAAM,mBAAmB,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,mBAAmB,CAAC,KAAK,CAAC,GAAG,IAAI;IAOvG;;;;;;;;;;OAUG;IACH,OAAO,CAAC,KAAK,SAAS,MAAM,mBAAmB,EAC7C,KAAK,EAAE,KAAK,GACX,OAAO,CACR,UAAU,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,GAAG,KAAK,GAClE,UAAU,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC,SAAS,EAAE,GAAG,IAAI,GACxD,UAAU,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC,CACzC;IAQK,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAK3B,IAAI,cAAc,IAAI,WAAW,GAAG,SAAS,CAE5C;IASD;;;;OAIG;IACG,YAAY,IAAI,OAAO,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAmBzD;;;;OAIG;IACG,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC;IA0BlC,SAAS,CAAC,KAAK,CAAC,KAAK,SAAS,MAAM,mBAAmB,EACrD,KAAK,EAAE,KAAK,EACZ,GAAG,IAAI,EAAE,UAAU,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC;IA8CjD,SAAS,CAAC,UAAU;cA8FJ,mBAAmB,CACjC,cAAc,EAAE,cAAc,EAC9B,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,IAAI,CAAC;IAuKhB,CAAC,MAAM,CAAC,aAAa,CAAC,IAAI,aAAa,CAAC,sBAAsB,CAAC;IA6D/D,gBAAgB,IAAI,cAAc;CAInC"}
+620
View File
@@ -0,0 +1,620 @@
"use strict";
var _BetaMessageStream_instances, _BetaMessageStream_currentMessageSnapshot, _BetaMessageStream_params, _BetaMessageStream_connectedPromise, _BetaMessageStream_resolveConnectedPromise, _BetaMessageStream_rejectConnectedPromise, _BetaMessageStream_endPromise, _BetaMessageStream_resolveEndPromise, _BetaMessageStream_rejectEndPromise, _BetaMessageStream_listeners, _BetaMessageStream_ended, _BetaMessageStream_errored, _BetaMessageStream_aborted, _BetaMessageStream_catchingPromiseCreated, _BetaMessageStream_response, _BetaMessageStream_request_id, _BetaMessageStream_logger, _BetaMessageStream_getFinalMessage, _BetaMessageStream_getFinalText, _BetaMessageStream_handleError, _BetaMessageStream_beginRequest, _BetaMessageStream_addStreamEvent, _BetaMessageStream_endRequest, _BetaMessageStream_accumulateMessage;
Object.defineProperty(exports, "__esModule", { value: true });
exports.BetaMessageStream = void 0;
const tslib_1 = require("../internal/tslib.js");
const parser_1 = require("../_vendor/partial-json-parser/parser.js");
const error_1 = require("../error.js");
const errors_1 = require("../internal/errors.js");
const streaming_1 = require("../streaming.js");
const beta_parser_1 = require("./beta-parser.js");
const JSON_BUF_PROPERTY = '__json_buf';
function tracksToolInput(content) {
return content.type === 'tool_use' || content.type === 'server_tool_use' || content.type === 'mcp_tool_use';
}
class BetaMessageStream {
constructor(params, opts) {
_BetaMessageStream_instances.add(this);
this.messages = [];
this.receivedMessages = [];
_BetaMessageStream_currentMessageSnapshot.set(this, void 0);
_BetaMessageStream_params.set(this, null);
this.controller = new AbortController();
_BetaMessageStream_connectedPromise.set(this, void 0);
_BetaMessageStream_resolveConnectedPromise.set(this, () => { });
_BetaMessageStream_rejectConnectedPromise.set(this, () => { });
_BetaMessageStream_endPromise.set(this, void 0);
_BetaMessageStream_resolveEndPromise.set(this, () => { });
_BetaMessageStream_rejectEndPromise.set(this, () => { });
_BetaMessageStream_listeners.set(this, {});
_BetaMessageStream_ended.set(this, false);
_BetaMessageStream_errored.set(this, false);
_BetaMessageStream_aborted.set(this, false);
_BetaMessageStream_catchingPromiseCreated.set(this, false);
_BetaMessageStream_response.set(this, void 0);
_BetaMessageStream_request_id.set(this, void 0);
_BetaMessageStream_logger.set(this, void 0);
_BetaMessageStream_handleError.set(this, (error) => {
tslib_1.__classPrivateFieldSet(this, _BetaMessageStream_errored, true, "f");
if ((0, errors_1.isAbortError)(error)) {
error = new error_1.APIUserAbortError();
}
if (error instanceof error_1.APIUserAbortError) {
tslib_1.__classPrivateFieldSet(this, _BetaMessageStream_aborted, true, "f");
return this._emit('abort', error);
}
if (error instanceof error_1.AnthropicError) {
return this._emit('error', error);
}
if (error instanceof Error) {
const anthropicError = new error_1.AnthropicError(error.message);
// @ts-ignore
anthropicError.cause = error;
return this._emit('error', anthropicError);
}
return this._emit('error', new error_1.AnthropicError(String(error)));
});
tslib_1.__classPrivateFieldSet(this, _BetaMessageStream_connectedPromise, new Promise((resolve, reject) => {
tslib_1.__classPrivateFieldSet(this, _BetaMessageStream_resolveConnectedPromise, resolve, "f");
tslib_1.__classPrivateFieldSet(this, _BetaMessageStream_rejectConnectedPromise, reject, "f");
}), "f");
tslib_1.__classPrivateFieldSet(this, _BetaMessageStream_endPromise, new Promise((resolve, reject) => {
tslib_1.__classPrivateFieldSet(this, _BetaMessageStream_resolveEndPromise, resolve, "f");
tslib_1.__classPrivateFieldSet(this, _BetaMessageStream_rejectEndPromise, reject, "f");
}), "f");
// Don't let these promises cause unhandled rejection errors.
// we will manually cause an unhandled rejection error later
// if the user hasn't registered any error listener or called
// any promise-returning method.
tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_connectedPromise, "f").catch(() => { });
tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_endPromise, "f").catch(() => { });
tslib_1.__classPrivateFieldSet(this, _BetaMessageStream_params, params, "f");
tslib_1.__classPrivateFieldSet(this, _BetaMessageStream_logger, opts?.logger ?? console, "f");
}
get response() {
return tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_response, "f");
}
get request_id() {
return tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_request_id, "f");
}
/**
* Returns the `MessageStream` data, the raw `Response` instance and the ID of the request,
* returned vie the `request-id` header which is useful for debugging requests and resporting
* issues to Anthropic.
*
* This is the same as the `APIPromise.withResponse()` method.
*
* This method will raise an error if you created the stream using `MessageStream.fromReadableStream`
* as no `Response` is available.
*/
async withResponse() {
tslib_1.__classPrivateFieldSet(this, _BetaMessageStream_catchingPromiseCreated, true, "f");
const response = await tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_connectedPromise, "f");
if (!response) {
throw new Error('Could not resolve a `Response` object');
}
return {
data: this,
response,
request_id: response.headers.get('request-id'),
};
}
/**
* Intended for use on the frontend, consuming a stream produced with
* `.toReadableStream()` on the backend.
*
* Note that messages sent to the model do not appear in `.on('message')`
* in this context.
*/
static fromReadableStream(stream) {
const runner = new BetaMessageStream(null);
runner._run(() => runner._fromReadableStream(stream));
return runner;
}
static createMessage(messages, params, options, { logger } = {}) {
const runner = new BetaMessageStream(params, { logger });
for (const message of params.messages) {
runner._addMessageParam(message);
}
tslib_1.__classPrivateFieldSet(runner, _BetaMessageStream_params, { ...params, stream: true }, "f");
runner._run(() => runner._createMessage(messages, { ...params, stream: true }, { ...options, headers: { ...options?.headers, 'X-Stainless-Helper-Method': 'stream' } }));
return runner;
}
_run(executor) {
executor().then(() => {
this._emitFinal();
this._emit('end');
}, tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_handleError, "f"));
}
_addMessageParam(message) {
this.messages.push(message);
}
_addMessage(message, emit = true) {
this.receivedMessages.push(message);
if (emit) {
this._emit('message', message);
}
}
async _createMessage(messages, params, options) {
const signal = options?.signal;
let abortHandler;
if (signal) {
if (signal.aborted)
this.controller.abort();
abortHandler = this.controller.abort.bind(this.controller);
signal.addEventListener('abort', abortHandler);
}
try {
tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_instances, "m", _BetaMessageStream_beginRequest).call(this);
const { response, data: stream } = await messages
.create({ ...params, stream: true }, { ...options, signal: this.controller.signal })
.withResponse();
this._connected(response);
for await (const event of stream) {
tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_instances, "m", _BetaMessageStream_addStreamEvent).call(this, event);
}
if (stream.controller.signal?.aborted) {
throw new error_1.APIUserAbortError();
}
tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_instances, "m", _BetaMessageStream_endRequest).call(this);
}
finally {
if (signal && abortHandler) {
signal.removeEventListener('abort', abortHandler);
}
}
}
_connected(response) {
if (this.ended)
return;
tslib_1.__classPrivateFieldSet(this, _BetaMessageStream_response, response, "f");
tslib_1.__classPrivateFieldSet(this, _BetaMessageStream_request_id, response?.headers.get('request-id'), "f");
tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_resolveConnectedPromise, "f").call(this, response);
this._emit('connect');
}
get ended() {
return tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_ended, "f");
}
get errored() {
return tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_errored, "f");
}
get aborted() {
return tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_aborted, "f");
}
abort() {
this.controller.abort();
}
/**
* Adds the listener function to the end of the listeners array for the event.
* No checks are made to see if the listener has already been added. Multiple calls passing
* the same combination of event and listener will result in the listener being added, and
* called, multiple times.
* @returns this MessageStream, so that calls can be chained
*/
on(event, listener) {
const listeners = tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_listeners, "f")[event] || (tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_listeners, "f")[event] = []);
listeners.push({ listener });
return this;
}
/**
* Removes the specified listener from the listener array for the event.
* off() will remove, at most, one instance of a listener from the listener array. If any single
* listener has been added multiple times to the listener array for the specified event, then
* off() must be called multiple times to remove each instance.
* @returns this MessageStream, so that calls can be chained
*/
off(event, listener) {
const listeners = tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_listeners, "f")[event];
if (!listeners)
return this;
const index = listeners.findIndex((l) => l.listener === listener);
if (index >= 0)
listeners.splice(index, 1);
return this;
}
/**
* Adds a one-time listener function for the event. The next time the event is triggered,
* this listener is removed and then invoked.
* @returns this MessageStream, so that calls can be chained
*/
once(event, listener) {
const listeners = tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_listeners, "f")[event] || (tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_listeners, "f")[event] = []);
listeners.push({ listener, once: true });
return this;
}
/**
* This is similar to `.once()`, but returns a Promise that resolves the next time
* the event is triggered, instead of calling a listener callback.
* @returns a Promise that resolves the next time given event is triggered,
* or rejects if an error is emitted. (If you request the 'error' event,
* returns a promise that resolves with the error).
*
* Example:
*
* const message = await stream.emitted('message') // rejects if the stream errors
*/
emitted(event) {
return new Promise((resolve, reject) => {
tslib_1.__classPrivateFieldSet(this, _BetaMessageStream_catchingPromiseCreated, true, "f");
if (event !== 'error')
this.once('error', reject);
this.once(event, resolve);
});
}
async done() {
tslib_1.__classPrivateFieldSet(this, _BetaMessageStream_catchingPromiseCreated, true, "f");
await tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_endPromise, "f");
}
get currentMessage() {
return tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_currentMessageSnapshot, "f");
}
/**
* @returns a promise that resolves with the the final assistant Message response,
* or rejects if an error occurred or the stream ended prematurely without producing a Message.
* If structured outputs were used, this will be a ParsedMessage with a `parsed` field.
*/
async finalMessage() {
await this.done();
return tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_instances, "m", _BetaMessageStream_getFinalMessage).call(this);
}
/**
* @returns a promise that resolves with the the final assistant Message's text response, concatenated
* together if there are more than one text blocks.
* Rejects if an error occurred or the stream ended prematurely without producing a Message.
*/
async finalText() {
await this.done();
return tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_instances, "m", _BetaMessageStream_getFinalText).call(this);
}
_emit(event, ...args) {
// make sure we don't emit any MessageStreamEvents after end
if (tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_ended, "f"))
return;
if (event === 'end') {
tslib_1.__classPrivateFieldSet(this, _BetaMessageStream_ended, true, "f");
tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_resolveEndPromise, "f").call(this);
}
const listeners = tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_listeners, "f")[event];
if (listeners) {
tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_listeners, "f")[event] = listeners.filter((l) => !l.once);
listeners.forEach(({ listener }) => listener(...args));
}
if (event === 'abort') {
const error = args[0];
if (!tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_catchingPromiseCreated, "f") && !listeners?.length) {
Promise.reject(error);
}
tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_rejectConnectedPromise, "f").call(this, error);
tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_rejectEndPromise, "f").call(this, error);
this._emit('end');
return;
}
if (event === 'error') {
// NOTE: _emit('error', error) should only be called from #handleError().
const error = args[0];
if (!tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_catchingPromiseCreated, "f") && !listeners?.length) {
// Trigger an unhandled rejection if the user hasn't registered any error handlers.
// If you are seeing stack traces here, make sure to handle errors via either:
// - runner.on('error', () => ...)
// - await runner.done()
// - await runner.final...()
// - etc.
Promise.reject(error);
}
tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_rejectConnectedPromise, "f").call(this, error);
tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_rejectEndPromise, "f").call(this, error);
this._emit('end');
}
}
_emitFinal() {
const finalMessage = this.receivedMessages.at(-1);
if (finalMessage) {
this._emit('finalMessage', tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_instances, "m", _BetaMessageStream_getFinalMessage).call(this));
}
}
async _fromReadableStream(readableStream, options) {
const signal = options?.signal;
let abortHandler;
if (signal) {
if (signal.aborted)
this.controller.abort();
abortHandler = this.controller.abort.bind(this.controller);
signal.addEventListener('abort', abortHandler);
}
try {
tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_instances, "m", _BetaMessageStream_beginRequest).call(this);
this._connected(null);
const stream = streaming_1.Stream.fromReadableStream(readableStream, this.controller);
for await (const event of stream) {
tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_instances, "m", _BetaMessageStream_addStreamEvent).call(this, event);
}
if (stream.controller.signal?.aborted) {
throw new error_1.APIUserAbortError();
}
tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_instances, "m", _BetaMessageStream_endRequest).call(this);
}
finally {
if (signal && abortHandler) {
signal.removeEventListener('abort', abortHandler);
}
}
}
[(_BetaMessageStream_currentMessageSnapshot = new WeakMap(), _BetaMessageStream_params = new WeakMap(), _BetaMessageStream_connectedPromise = new WeakMap(), _BetaMessageStream_resolveConnectedPromise = new WeakMap(), _BetaMessageStream_rejectConnectedPromise = new WeakMap(), _BetaMessageStream_endPromise = new WeakMap(), _BetaMessageStream_resolveEndPromise = new WeakMap(), _BetaMessageStream_rejectEndPromise = new WeakMap(), _BetaMessageStream_listeners = new WeakMap(), _BetaMessageStream_ended = new WeakMap(), _BetaMessageStream_errored = new WeakMap(), _BetaMessageStream_aborted = new WeakMap(), _BetaMessageStream_catchingPromiseCreated = new WeakMap(), _BetaMessageStream_response = new WeakMap(), _BetaMessageStream_request_id = new WeakMap(), _BetaMessageStream_logger = new WeakMap(), _BetaMessageStream_handleError = new WeakMap(), _BetaMessageStream_instances = new WeakSet(), _BetaMessageStream_getFinalMessage = function _BetaMessageStream_getFinalMessage() {
if (this.receivedMessages.length === 0) {
throw new error_1.AnthropicError('stream ended without producing a Message with role=assistant');
}
return this.receivedMessages.at(-1);
}, _BetaMessageStream_getFinalText = function _BetaMessageStream_getFinalText() {
if (this.receivedMessages.length === 0) {
throw new error_1.AnthropicError('stream ended without producing a Message with role=assistant');
}
const textBlocks = this.receivedMessages
.at(-1)
.content.filter((block) => block.type === 'text')
.map((block) => block.text);
if (textBlocks.length === 0) {
throw new error_1.AnthropicError('stream ended without producing a content block with type=text');
}
return textBlocks.join(' ');
}, _BetaMessageStream_beginRequest = function _BetaMessageStream_beginRequest() {
if (this.ended)
return;
tslib_1.__classPrivateFieldSet(this, _BetaMessageStream_currentMessageSnapshot, undefined, "f");
}, _BetaMessageStream_addStreamEvent = function _BetaMessageStream_addStreamEvent(event) {
if (this.ended)
return;
const messageSnapshot = tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_instances, "m", _BetaMessageStream_accumulateMessage).call(this, event);
this._emit('streamEvent', event, messageSnapshot);
switch (event.type) {
case 'content_block_delta': {
const content = messageSnapshot.content.at(-1);
switch (event.delta.type) {
case 'text_delta': {
if (content.type === 'text') {
this._emit('text', event.delta.text, content.text || '');
}
break;
}
case 'citations_delta': {
if (content.type === 'text') {
this._emit('citation', event.delta.citation, content.citations ?? []);
}
break;
}
case 'input_json_delta': {
if (tracksToolInput(content) && content.input) {
this._emit('inputJson', event.delta.partial_json, content.input);
}
break;
}
case 'thinking_delta': {
if (content.type === 'thinking') {
this._emit('thinking', event.delta.thinking, content.thinking);
}
break;
}
case 'signature_delta': {
if (content.type === 'thinking') {
this._emit('signature', content.signature);
}
break;
}
case 'compaction_delta': {
if (content.type === 'compaction' && content.content) {
this._emit('compaction', content.content);
}
break;
}
default:
checkNever(event.delta);
}
break;
}
case 'message_stop': {
this._addMessageParam(messageSnapshot);
this._addMessage((0, beta_parser_1.maybeParseBetaMessage)(messageSnapshot, tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_params, "f"), { logger: tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_logger, "f") }), true);
break;
}
case 'content_block_stop': {
this._emit('contentBlock', messageSnapshot.content.at(-1));
break;
}
case 'message_start': {
tslib_1.__classPrivateFieldSet(this, _BetaMessageStream_currentMessageSnapshot, messageSnapshot, "f");
break;
}
case 'content_block_start':
case 'message_delta':
break;
}
}, _BetaMessageStream_endRequest = function _BetaMessageStream_endRequest() {
if (this.ended) {
throw new error_1.AnthropicError(`stream has ended, this shouldn't happen`);
}
const snapshot = tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_currentMessageSnapshot, "f");
if (!snapshot) {
throw new error_1.AnthropicError(`request ended without sending any chunks`);
}
tslib_1.__classPrivateFieldSet(this, _BetaMessageStream_currentMessageSnapshot, undefined, "f");
return (0, beta_parser_1.maybeParseBetaMessage)(snapshot, tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_params, "f"), { logger: tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_logger, "f") });
}, _BetaMessageStream_accumulateMessage = function _BetaMessageStream_accumulateMessage(event) {
let snapshot = tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_currentMessageSnapshot, "f");
if (event.type === 'message_start') {
if (snapshot) {
throw new error_1.AnthropicError(`Unexpected event order, got ${event.type} before receiving "message_stop"`);
}
return event.message;
}
if (!snapshot) {
throw new error_1.AnthropicError(`Unexpected event order, got ${event.type} before "message_start"`);
}
switch (event.type) {
case 'message_stop':
return snapshot;
case 'message_delta':
snapshot.container = event.delta.container;
snapshot.stop_reason = event.delta.stop_reason;
snapshot.stop_sequence = event.delta.stop_sequence;
snapshot.usage.output_tokens = event.usage.output_tokens;
snapshot.context_management = event.context_management;
if (event.usage.input_tokens != null) {
snapshot.usage.input_tokens = event.usage.input_tokens;
}
if (event.usage.cache_creation_input_tokens != null) {
snapshot.usage.cache_creation_input_tokens = event.usage.cache_creation_input_tokens;
}
if (event.usage.cache_read_input_tokens != null) {
snapshot.usage.cache_read_input_tokens = event.usage.cache_read_input_tokens;
}
if (event.usage.server_tool_use != null) {
snapshot.usage.server_tool_use = event.usage.server_tool_use;
}
if (event.usage.iterations != null) {
snapshot.usage.iterations = event.usage.iterations;
}
return snapshot;
case 'content_block_start':
snapshot.content.push(event.content_block);
return snapshot;
case 'content_block_delta': {
const snapshotContent = snapshot.content.at(event.index);
switch (event.delta.type) {
case 'text_delta': {
if (snapshotContent?.type === 'text') {
snapshot.content[event.index] = {
...snapshotContent,
text: (snapshotContent.text || '') + event.delta.text,
};
}
break;
}
case 'citations_delta': {
if (snapshotContent?.type === 'text') {
snapshot.content[event.index] = {
...snapshotContent,
citations: [...(snapshotContent.citations ?? []), event.delta.citation],
};
}
break;
}
case 'input_json_delta': {
if (snapshotContent && tracksToolInput(snapshotContent)) {
// we need to keep track of the raw JSON string as well so that we can
// re-parse it for each delta, for now we just store it as an untyped
// non-enumerable property on the snapshot
let jsonBuf = snapshotContent[JSON_BUF_PROPERTY] || '';
jsonBuf += event.delta.partial_json;
const newContent = { ...snapshotContent };
Object.defineProperty(newContent, JSON_BUF_PROPERTY, {
value: jsonBuf,
enumerable: false,
writable: true,
});
if (jsonBuf) {
try {
newContent.input = (0, parser_1.partialParse)(jsonBuf);
}
catch (err) {
const error = new error_1.AnthropicError(`Unable to parse tool parameter JSON from model. Please retry your request or adjust your prompt. Error: ${err}. JSON: ${jsonBuf}`);
tslib_1.__classPrivateFieldGet(this, _BetaMessageStream_handleError, "f").call(this, error);
}
}
snapshot.content[event.index] = newContent;
}
break;
}
case 'thinking_delta': {
if (snapshotContent?.type === 'thinking') {
snapshot.content[event.index] = {
...snapshotContent,
thinking: snapshotContent.thinking + event.delta.thinking,
};
}
break;
}
case 'signature_delta': {
if (snapshotContent?.type === 'thinking') {
snapshot.content[event.index] = {
...snapshotContent,
signature: event.delta.signature,
};
}
break;
}
case 'compaction_delta': {
if (snapshotContent?.type === 'compaction') {
snapshot.content[event.index] = {
...snapshotContent,
content: (snapshotContent.content || '') + event.delta.content,
};
}
break;
}
default:
checkNever(event.delta);
}
return snapshot;
}
case 'content_block_stop':
return snapshot;
}
}, Symbol.asyncIterator)]() {
const pushQueue = [];
const readQueue = [];
let done = false;
this.on('streamEvent', (event) => {
const reader = readQueue.shift();
if (reader) {
reader.resolve(event);
}
else {
pushQueue.push(event);
}
});
this.on('end', () => {
done = true;
for (const reader of readQueue) {
reader.resolve(undefined);
}
readQueue.length = 0;
});
this.on('abort', (err) => {
done = true;
for (const reader of readQueue) {
reader.reject(err);
}
readQueue.length = 0;
});
this.on('error', (err) => {
done = true;
for (const reader of readQueue) {
reader.reject(err);
}
readQueue.length = 0;
});
return {
next: async () => {
if (!pushQueue.length) {
if (done) {
return { value: undefined, done: true };
}
return new Promise((resolve, reject) => readQueue.push({ resolve, reject })).then((chunk) => (chunk ? { value: chunk, done: false } : { value: undefined, done: true }));
}
const chunk = pushQueue.shift();
return { value: chunk, done: false };
},
return: async () => {
this.abort();
return { value: undefined, done: true };
},
};
}
toReadableStream() {
const stream = new streaming_1.Stream(this[Symbol.asyncIterator].bind(this), this.controller);
return stream.toReadableStream();
}
}
exports.BetaMessageStream = BetaMessageStream;
// used to ensure exhaustive case matching without throwing a runtime error
function checkNever(x) { }
//# sourceMappingURL=BetaMessageStream.js.map
File diff suppressed because one or more lines are too long
+616
View File
@@ -0,0 +1,616 @@
var _BetaMessageStream_instances, _BetaMessageStream_currentMessageSnapshot, _BetaMessageStream_params, _BetaMessageStream_connectedPromise, _BetaMessageStream_resolveConnectedPromise, _BetaMessageStream_rejectConnectedPromise, _BetaMessageStream_endPromise, _BetaMessageStream_resolveEndPromise, _BetaMessageStream_rejectEndPromise, _BetaMessageStream_listeners, _BetaMessageStream_ended, _BetaMessageStream_errored, _BetaMessageStream_aborted, _BetaMessageStream_catchingPromiseCreated, _BetaMessageStream_response, _BetaMessageStream_request_id, _BetaMessageStream_logger, _BetaMessageStream_getFinalMessage, _BetaMessageStream_getFinalText, _BetaMessageStream_handleError, _BetaMessageStream_beginRequest, _BetaMessageStream_addStreamEvent, _BetaMessageStream_endRequest, _BetaMessageStream_accumulateMessage;
import { __classPrivateFieldGet, __classPrivateFieldSet } from "../internal/tslib.mjs";
import { partialParse } from "../_vendor/partial-json-parser/parser.mjs";
import { AnthropicError, APIUserAbortError } from "../error.mjs";
import { isAbortError } from "../internal/errors.mjs";
import { Stream } from "../streaming.mjs";
import { maybeParseBetaMessage } from "./beta-parser.mjs";
const JSON_BUF_PROPERTY = '__json_buf';
function tracksToolInput(content) {
return content.type === 'tool_use' || content.type === 'server_tool_use' || content.type === 'mcp_tool_use';
}
export class BetaMessageStream {
constructor(params, opts) {
_BetaMessageStream_instances.add(this);
this.messages = [];
this.receivedMessages = [];
_BetaMessageStream_currentMessageSnapshot.set(this, void 0);
_BetaMessageStream_params.set(this, null);
this.controller = new AbortController();
_BetaMessageStream_connectedPromise.set(this, void 0);
_BetaMessageStream_resolveConnectedPromise.set(this, () => { });
_BetaMessageStream_rejectConnectedPromise.set(this, () => { });
_BetaMessageStream_endPromise.set(this, void 0);
_BetaMessageStream_resolveEndPromise.set(this, () => { });
_BetaMessageStream_rejectEndPromise.set(this, () => { });
_BetaMessageStream_listeners.set(this, {});
_BetaMessageStream_ended.set(this, false);
_BetaMessageStream_errored.set(this, false);
_BetaMessageStream_aborted.set(this, false);
_BetaMessageStream_catchingPromiseCreated.set(this, false);
_BetaMessageStream_response.set(this, void 0);
_BetaMessageStream_request_id.set(this, void 0);
_BetaMessageStream_logger.set(this, void 0);
_BetaMessageStream_handleError.set(this, (error) => {
__classPrivateFieldSet(this, _BetaMessageStream_errored, true, "f");
if (isAbortError(error)) {
error = new APIUserAbortError();
}
if (error instanceof APIUserAbortError) {
__classPrivateFieldSet(this, _BetaMessageStream_aborted, true, "f");
return this._emit('abort', error);
}
if (error instanceof AnthropicError) {
return this._emit('error', error);
}
if (error instanceof Error) {
const anthropicError = new AnthropicError(error.message);
// @ts-ignore
anthropicError.cause = error;
return this._emit('error', anthropicError);
}
return this._emit('error', new AnthropicError(String(error)));
});
__classPrivateFieldSet(this, _BetaMessageStream_connectedPromise, new Promise((resolve, reject) => {
__classPrivateFieldSet(this, _BetaMessageStream_resolveConnectedPromise, resolve, "f");
__classPrivateFieldSet(this, _BetaMessageStream_rejectConnectedPromise, reject, "f");
}), "f");
__classPrivateFieldSet(this, _BetaMessageStream_endPromise, new Promise((resolve, reject) => {
__classPrivateFieldSet(this, _BetaMessageStream_resolveEndPromise, resolve, "f");
__classPrivateFieldSet(this, _BetaMessageStream_rejectEndPromise, reject, "f");
}), "f");
// Don't let these promises cause unhandled rejection errors.
// we will manually cause an unhandled rejection error later
// if the user hasn't registered any error listener or called
// any promise-returning method.
__classPrivateFieldGet(this, _BetaMessageStream_connectedPromise, "f").catch(() => { });
__classPrivateFieldGet(this, _BetaMessageStream_endPromise, "f").catch(() => { });
__classPrivateFieldSet(this, _BetaMessageStream_params, params, "f");
__classPrivateFieldSet(this, _BetaMessageStream_logger, opts?.logger ?? console, "f");
}
get response() {
return __classPrivateFieldGet(this, _BetaMessageStream_response, "f");
}
get request_id() {
return __classPrivateFieldGet(this, _BetaMessageStream_request_id, "f");
}
/**
* Returns the `MessageStream` data, the raw `Response` instance and the ID of the request,
* returned vie the `request-id` header which is useful for debugging requests and resporting
* issues to Anthropic.
*
* This is the same as the `APIPromise.withResponse()` method.
*
* This method will raise an error if you created the stream using `MessageStream.fromReadableStream`
* as no `Response` is available.
*/
async withResponse() {
__classPrivateFieldSet(this, _BetaMessageStream_catchingPromiseCreated, true, "f");
const response = await __classPrivateFieldGet(this, _BetaMessageStream_connectedPromise, "f");
if (!response) {
throw new Error('Could not resolve a `Response` object');
}
return {
data: this,
response,
request_id: response.headers.get('request-id'),
};
}
/**
* Intended for use on the frontend, consuming a stream produced with
* `.toReadableStream()` on the backend.
*
* Note that messages sent to the model do not appear in `.on('message')`
* in this context.
*/
static fromReadableStream(stream) {
const runner = new BetaMessageStream(null);
runner._run(() => runner._fromReadableStream(stream));
return runner;
}
static createMessage(messages, params, options, { logger } = {}) {
const runner = new BetaMessageStream(params, { logger });
for (const message of params.messages) {
runner._addMessageParam(message);
}
__classPrivateFieldSet(runner, _BetaMessageStream_params, { ...params, stream: true }, "f");
runner._run(() => runner._createMessage(messages, { ...params, stream: true }, { ...options, headers: { ...options?.headers, 'X-Stainless-Helper-Method': 'stream' } }));
return runner;
}
_run(executor) {
executor().then(() => {
this._emitFinal();
this._emit('end');
}, __classPrivateFieldGet(this, _BetaMessageStream_handleError, "f"));
}
_addMessageParam(message) {
this.messages.push(message);
}
_addMessage(message, emit = true) {
this.receivedMessages.push(message);
if (emit) {
this._emit('message', message);
}
}
async _createMessage(messages, params, options) {
const signal = options?.signal;
let abortHandler;
if (signal) {
if (signal.aborted)
this.controller.abort();
abortHandler = this.controller.abort.bind(this.controller);
signal.addEventListener('abort', abortHandler);
}
try {
__classPrivateFieldGet(this, _BetaMessageStream_instances, "m", _BetaMessageStream_beginRequest).call(this);
const { response, data: stream } = await messages
.create({ ...params, stream: true }, { ...options, signal: this.controller.signal })
.withResponse();
this._connected(response);
for await (const event of stream) {
__classPrivateFieldGet(this, _BetaMessageStream_instances, "m", _BetaMessageStream_addStreamEvent).call(this, event);
}
if (stream.controller.signal?.aborted) {
throw new APIUserAbortError();
}
__classPrivateFieldGet(this, _BetaMessageStream_instances, "m", _BetaMessageStream_endRequest).call(this);
}
finally {
if (signal && abortHandler) {
signal.removeEventListener('abort', abortHandler);
}
}
}
_connected(response) {
if (this.ended)
return;
__classPrivateFieldSet(this, _BetaMessageStream_response, response, "f");
__classPrivateFieldSet(this, _BetaMessageStream_request_id, response?.headers.get('request-id'), "f");
__classPrivateFieldGet(this, _BetaMessageStream_resolveConnectedPromise, "f").call(this, response);
this._emit('connect');
}
get ended() {
return __classPrivateFieldGet(this, _BetaMessageStream_ended, "f");
}
get errored() {
return __classPrivateFieldGet(this, _BetaMessageStream_errored, "f");
}
get aborted() {
return __classPrivateFieldGet(this, _BetaMessageStream_aborted, "f");
}
abort() {
this.controller.abort();
}
/**
* Adds the listener function to the end of the listeners array for the event.
* No checks are made to see if the listener has already been added. Multiple calls passing
* the same combination of event and listener will result in the listener being added, and
* called, multiple times.
* @returns this MessageStream, so that calls can be chained
*/
on(event, listener) {
const listeners = __classPrivateFieldGet(this, _BetaMessageStream_listeners, "f")[event] || (__classPrivateFieldGet(this, _BetaMessageStream_listeners, "f")[event] = []);
listeners.push({ listener });
return this;
}
/**
* Removes the specified listener from the listener array for the event.
* off() will remove, at most, one instance of a listener from the listener array. If any single
* listener has been added multiple times to the listener array for the specified event, then
* off() must be called multiple times to remove each instance.
* @returns this MessageStream, so that calls can be chained
*/
off(event, listener) {
const listeners = __classPrivateFieldGet(this, _BetaMessageStream_listeners, "f")[event];
if (!listeners)
return this;
const index = listeners.findIndex((l) => l.listener === listener);
if (index >= 0)
listeners.splice(index, 1);
return this;
}
/**
* Adds a one-time listener function for the event. The next time the event is triggered,
* this listener is removed and then invoked.
* @returns this MessageStream, so that calls can be chained
*/
once(event, listener) {
const listeners = __classPrivateFieldGet(this, _BetaMessageStream_listeners, "f")[event] || (__classPrivateFieldGet(this, _BetaMessageStream_listeners, "f")[event] = []);
listeners.push({ listener, once: true });
return this;
}
/**
* This is similar to `.once()`, but returns a Promise that resolves the next time
* the event is triggered, instead of calling a listener callback.
* @returns a Promise that resolves the next time given event is triggered,
* or rejects if an error is emitted. (If you request the 'error' event,
* returns a promise that resolves with the error).
*
* Example:
*
* const message = await stream.emitted('message') // rejects if the stream errors
*/
emitted(event) {
return new Promise((resolve, reject) => {
__classPrivateFieldSet(this, _BetaMessageStream_catchingPromiseCreated, true, "f");
if (event !== 'error')
this.once('error', reject);
this.once(event, resolve);
});
}
async done() {
__classPrivateFieldSet(this, _BetaMessageStream_catchingPromiseCreated, true, "f");
await __classPrivateFieldGet(this, _BetaMessageStream_endPromise, "f");
}
get currentMessage() {
return __classPrivateFieldGet(this, _BetaMessageStream_currentMessageSnapshot, "f");
}
/**
* @returns a promise that resolves with the the final assistant Message response,
* or rejects if an error occurred or the stream ended prematurely without producing a Message.
* If structured outputs were used, this will be a ParsedMessage with a `parsed` field.
*/
async finalMessage() {
await this.done();
return __classPrivateFieldGet(this, _BetaMessageStream_instances, "m", _BetaMessageStream_getFinalMessage).call(this);
}
/**
* @returns a promise that resolves with the the final assistant Message's text response, concatenated
* together if there are more than one text blocks.
* Rejects if an error occurred or the stream ended prematurely without producing a Message.
*/
async finalText() {
await this.done();
return __classPrivateFieldGet(this, _BetaMessageStream_instances, "m", _BetaMessageStream_getFinalText).call(this);
}
_emit(event, ...args) {
// make sure we don't emit any MessageStreamEvents after end
if (__classPrivateFieldGet(this, _BetaMessageStream_ended, "f"))
return;
if (event === 'end') {
__classPrivateFieldSet(this, _BetaMessageStream_ended, true, "f");
__classPrivateFieldGet(this, _BetaMessageStream_resolveEndPromise, "f").call(this);
}
const listeners = __classPrivateFieldGet(this, _BetaMessageStream_listeners, "f")[event];
if (listeners) {
__classPrivateFieldGet(this, _BetaMessageStream_listeners, "f")[event] = listeners.filter((l) => !l.once);
listeners.forEach(({ listener }) => listener(...args));
}
if (event === 'abort') {
const error = args[0];
if (!__classPrivateFieldGet(this, _BetaMessageStream_catchingPromiseCreated, "f") && !listeners?.length) {
Promise.reject(error);
}
__classPrivateFieldGet(this, _BetaMessageStream_rejectConnectedPromise, "f").call(this, error);
__classPrivateFieldGet(this, _BetaMessageStream_rejectEndPromise, "f").call(this, error);
this._emit('end');
return;
}
if (event === 'error') {
// NOTE: _emit('error', error) should only be called from #handleError().
const error = args[0];
if (!__classPrivateFieldGet(this, _BetaMessageStream_catchingPromiseCreated, "f") && !listeners?.length) {
// Trigger an unhandled rejection if the user hasn't registered any error handlers.
// If you are seeing stack traces here, make sure to handle errors via either:
// - runner.on('error', () => ...)
// - await runner.done()
// - await runner.final...()
// - etc.
Promise.reject(error);
}
__classPrivateFieldGet(this, _BetaMessageStream_rejectConnectedPromise, "f").call(this, error);
__classPrivateFieldGet(this, _BetaMessageStream_rejectEndPromise, "f").call(this, error);
this._emit('end');
}
}
_emitFinal() {
const finalMessage = this.receivedMessages.at(-1);
if (finalMessage) {
this._emit('finalMessage', __classPrivateFieldGet(this, _BetaMessageStream_instances, "m", _BetaMessageStream_getFinalMessage).call(this));
}
}
async _fromReadableStream(readableStream, options) {
const signal = options?.signal;
let abortHandler;
if (signal) {
if (signal.aborted)
this.controller.abort();
abortHandler = this.controller.abort.bind(this.controller);
signal.addEventListener('abort', abortHandler);
}
try {
__classPrivateFieldGet(this, _BetaMessageStream_instances, "m", _BetaMessageStream_beginRequest).call(this);
this._connected(null);
const stream = Stream.fromReadableStream(readableStream, this.controller);
for await (const event of stream) {
__classPrivateFieldGet(this, _BetaMessageStream_instances, "m", _BetaMessageStream_addStreamEvent).call(this, event);
}
if (stream.controller.signal?.aborted) {
throw new APIUserAbortError();
}
__classPrivateFieldGet(this, _BetaMessageStream_instances, "m", _BetaMessageStream_endRequest).call(this);
}
finally {
if (signal && abortHandler) {
signal.removeEventListener('abort', abortHandler);
}
}
}
[(_BetaMessageStream_currentMessageSnapshot = new WeakMap(), _BetaMessageStream_params = new WeakMap(), _BetaMessageStream_connectedPromise = new WeakMap(), _BetaMessageStream_resolveConnectedPromise = new WeakMap(), _BetaMessageStream_rejectConnectedPromise = new WeakMap(), _BetaMessageStream_endPromise = new WeakMap(), _BetaMessageStream_resolveEndPromise = new WeakMap(), _BetaMessageStream_rejectEndPromise = new WeakMap(), _BetaMessageStream_listeners = new WeakMap(), _BetaMessageStream_ended = new WeakMap(), _BetaMessageStream_errored = new WeakMap(), _BetaMessageStream_aborted = new WeakMap(), _BetaMessageStream_catchingPromiseCreated = new WeakMap(), _BetaMessageStream_response = new WeakMap(), _BetaMessageStream_request_id = new WeakMap(), _BetaMessageStream_logger = new WeakMap(), _BetaMessageStream_handleError = new WeakMap(), _BetaMessageStream_instances = new WeakSet(), _BetaMessageStream_getFinalMessage = function _BetaMessageStream_getFinalMessage() {
if (this.receivedMessages.length === 0) {
throw new AnthropicError('stream ended without producing a Message with role=assistant');
}
return this.receivedMessages.at(-1);
}, _BetaMessageStream_getFinalText = function _BetaMessageStream_getFinalText() {
if (this.receivedMessages.length === 0) {
throw new AnthropicError('stream ended without producing a Message with role=assistant');
}
const textBlocks = this.receivedMessages
.at(-1)
.content.filter((block) => block.type === 'text')
.map((block) => block.text);
if (textBlocks.length === 0) {
throw new AnthropicError('stream ended without producing a content block with type=text');
}
return textBlocks.join(' ');
}, _BetaMessageStream_beginRequest = function _BetaMessageStream_beginRequest() {
if (this.ended)
return;
__classPrivateFieldSet(this, _BetaMessageStream_currentMessageSnapshot, undefined, "f");
}, _BetaMessageStream_addStreamEvent = function _BetaMessageStream_addStreamEvent(event) {
if (this.ended)
return;
const messageSnapshot = __classPrivateFieldGet(this, _BetaMessageStream_instances, "m", _BetaMessageStream_accumulateMessage).call(this, event);
this._emit('streamEvent', event, messageSnapshot);
switch (event.type) {
case 'content_block_delta': {
const content = messageSnapshot.content.at(-1);
switch (event.delta.type) {
case 'text_delta': {
if (content.type === 'text') {
this._emit('text', event.delta.text, content.text || '');
}
break;
}
case 'citations_delta': {
if (content.type === 'text') {
this._emit('citation', event.delta.citation, content.citations ?? []);
}
break;
}
case 'input_json_delta': {
if (tracksToolInput(content) && content.input) {
this._emit('inputJson', event.delta.partial_json, content.input);
}
break;
}
case 'thinking_delta': {
if (content.type === 'thinking') {
this._emit('thinking', event.delta.thinking, content.thinking);
}
break;
}
case 'signature_delta': {
if (content.type === 'thinking') {
this._emit('signature', content.signature);
}
break;
}
case 'compaction_delta': {
if (content.type === 'compaction' && content.content) {
this._emit('compaction', content.content);
}
break;
}
default:
checkNever(event.delta);
}
break;
}
case 'message_stop': {
this._addMessageParam(messageSnapshot);
this._addMessage(maybeParseBetaMessage(messageSnapshot, __classPrivateFieldGet(this, _BetaMessageStream_params, "f"), { logger: __classPrivateFieldGet(this, _BetaMessageStream_logger, "f") }), true);
break;
}
case 'content_block_stop': {
this._emit('contentBlock', messageSnapshot.content.at(-1));
break;
}
case 'message_start': {
__classPrivateFieldSet(this, _BetaMessageStream_currentMessageSnapshot, messageSnapshot, "f");
break;
}
case 'content_block_start':
case 'message_delta':
break;
}
}, _BetaMessageStream_endRequest = function _BetaMessageStream_endRequest() {
if (this.ended) {
throw new AnthropicError(`stream has ended, this shouldn't happen`);
}
const snapshot = __classPrivateFieldGet(this, _BetaMessageStream_currentMessageSnapshot, "f");
if (!snapshot) {
throw new AnthropicError(`request ended without sending any chunks`);
}
__classPrivateFieldSet(this, _BetaMessageStream_currentMessageSnapshot, undefined, "f");
return maybeParseBetaMessage(snapshot, __classPrivateFieldGet(this, _BetaMessageStream_params, "f"), { logger: __classPrivateFieldGet(this, _BetaMessageStream_logger, "f") });
}, _BetaMessageStream_accumulateMessage = function _BetaMessageStream_accumulateMessage(event) {
let snapshot = __classPrivateFieldGet(this, _BetaMessageStream_currentMessageSnapshot, "f");
if (event.type === 'message_start') {
if (snapshot) {
throw new AnthropicError(`Unexpected event order, got ${event.type} before receiving "message_stop"`);
}
return event.message;
}
if (!snapshot) {
throw new AnthropicError(`Unexpected event order, got ${event.type} before "message_start"`);
}
switch (event.type) {
case 'message_stop':
return snapshot;
case 'message_delta':
snapshot.container = event.delta.container;
snapshot.stop_reason = event.delta.stop_reason;
snapshot.stop_sequence = event.delta.stop_sequence;
snapshot.usage.output_tokens = event.usage.output_tokens;
snapshot.context_management = event.context_management;
if (event.usage.input_tokens != null) {
snapshot.usage.input_tokens = event.usage.input_tokens;
}
if (event.usage.cache_creation_input_tokens != null) {
snapshot.usage.cache_creation_input_tokens = event.usage.cache_creation_input_tokens;
}
if (event.usage.cache_read_input_tokens != null) {
snapshot.usage.cache_read_input_tokens = event.usage.cache_read_input_tokens;
}
if (event.usage.server_tool_use != null) {
snapshot.usage.server_tool_use = event.usage.server_tool_use;
}
if (event.usage.iterations != null) {
snapshot.usage.iterations = event.usage.iterations;
}
return snapshot;
case 'content_block_start':
snapshot.content.push(event.content_block);
return snapshot;
case 'content_block_delta': {
const snapshotContent = snapshot.content.at(event.index);
switch (event.delta.type) {
case 'text_delta': {
if (snapshotContent?.type === 'text') {
snapshot.content[event.index] = {
...snapshotContent,
text: (snapshotContent.text || '') + event.delta.text,
};
}
break;
}
case 'citations_delta': {
if (snapshotContent?.type === 'text') {
snapshot.content[event.index] = {
...snapshotContent,
citations: [...(snapshotContent.citations ?? []), event.delta.citation],
};
}
break;
}
case 'input_json_delta': {
if (snapshotContent && tracksToolInput(snapshotContent)) {
// we need to keep track of the raw JSON string as well so that we can
// re-parse it for each delta, for now we just store it as an untyped
// non-enumerable property on the snapshot
let jsonBuf = snapshotContent[JSON_BUF_PROPERTY] || '';
jsonBuf += event.delta.partial_json;
const newContent = { ...snapshotContent };
Object.defineProperty(newContent, JSON_BUF_PROPERTY, {
value: jsonBuf,
enumerable: false,
writable: true,
});
if (jsonBuf) {
try {
newContent.input = partialParse(jsonBuf);
}
catch (err) {
const error = new AnthropicError(`Unable to parse tool parameter JSON from model. Please retry your request or adjust your prompt. Error: ${err}. JSON: ${jsonBuf}`);
__classPrivateFieldGet(this, _BetaMessageStream_handleError, "f").call(this, error);
}
}
snapshot.content[event.index] = newContent;
}
break;
}
case 'thinking_delta': {
if (snapshotContent?.type === 'thinking') {
snapshot.content[event.index] = {
...snapshotContent,
thinking: snapshotContent.thinking + event.delta.thinking,
};
}
break;
}
case 'signature_delta': {
if (snapshotContent?.type === 'thinking') {
snapshot.content[event.index] = {
...snapshotContent,
signature: event.delta.signature,
};
}
break;
}
case 'compaction_delta': {
if (snapshotContent?.type === 'compaction') {
snapshot.content[event.index] = {
...snapshotContent,
content: (snapshotContent.content || '') + event.delta.content,
};
}
break;
}
default:
checkNever(event.delta);
}
return snapshot;
}
case 'content_block_stop':
return snapshot;
}
}, Symbol.asyncIterator)]() {
const pushQueue = [];
const readQueue = [];
let done = false;
this.on('streamEvent', (event) => {
const reader = readQueue.shift();
if (reader) {
reader.resolve(event);
}
else {
pushQueue.push(event);
}
});
this.on('end', () => {
done = true;
for (const reader of readQueue) {
reader.resolve(undefined);
}
readQueue.length = 0;
});
this.on('abort', (err) => {
done = true;
for (const reader of readQueue) {
reader.reject(err);
}
readQueue.length = 0;
});
this.on('error', (err) => {
done = true;
for (const reader of readQueue) {
reader.reject(err);
}
readQueue.length = 0;
});
return {
next: async () => {
if (!pushQueue.length) {
if (done) {
return { value: undefined, done: true };
}
return new Promise((resolve, reject) => readQueue.push({ resolve, reject })).then((chunk) => (chunk ? { value: chunk, done: false } : { value: undefined, done: true }));
}
const chunk = pushQueue.shift();
return { value: chunk, done: false };
},
return: async () => {
this.abort();
return { value: undefined, done: true };
},
};
}
toReadableStream() {
const stream = new Stream(this[Symbol.asyncIterator].bind(this), this.controller);
return stream.toReadableStream();
}
}
// used to ensure exhaustive case matching without throwing a runtime error
function checkNever(x) { }
//# sourceMappingURL=BetaMessageStream.mjs.map
File diff suppressed because one or more lines are too long
+121
View File
@@ -0,0 +1,121 @@
import { AnthropicError, APIUserAbortError } from "../error.mjs";
import { type ContentBlock, Messages, type Message, type MessageStreamEvent, type MessageParam, type MessageCreateParams, type MessageCreateParamsBase, type TextCitation, type ToolUseBlock, type ServerToolUseBlock } from "../resources/messages.mjs";
import { RequestOptions } from "../internal/request-options.mjs";
import type { Logger } from "../client.mjs";
import { type ParsedMessage } from "./parser.mjs";
export interface MessageStreamEvents<ParsedT = null> {
connect: () => void;
streamEvent: (event: MessageStreamEvent, snapshot: Message) => void;
text: (textDelta: string, textSnapshot: string) => void;
citation: (citation: TextCitation, citationsSnapshot: TextCitation[]) => void;
inputJson: (partialJson: string, jsonSnapshot: unknown) => void;
thinking: (thinkingDelta: string, thinkingSnapshot: string) => void;
signature: (signature: string) => void;
message: (message: ParsedMessage<ParsedT>) => void;
contentBlock: (content: ContentBlock) => void;
finalMessage: (message: ParsedMessage<ParsedT>) => void;
error: (error: AnthropicError) => void;
abort: (error: APIUserAbortError) => void;
end: () => void;
}
export type TracksToolInput = ToolUseBlock | ServerToolUseBlock;
export declare class MessageStream<ParsedT = null> implements AsyncIterable<MessageStreamEvent> {
#private;
messages: MessageParam[];
receivedMessages: ParsedMessage<ParsedT>[];
controller: AbortController;
constructor(params: MessageCreateParamsBase | null, opts?: {
logger?: Logger | undefined;
});
get response(): Response | null | undefined;
get request_id(): string | null | undefined;
/**
* Returns the `MessageStream` data, the raw `Response` instance and the ID of the request,
* returned vie the `request-id` header which is useful for debugging requests and resporting
* issues to Anthropic.
*
* This is the same as the `APIPromise.withResponse()` method.
*
* This method will raise an error if you created the stream using `MessageStream.fromReadableStream`
* as no `Response` is available.
*/
withResponse(): Promise<{
data: MessageStream<ParsedT>;
response: Response;
request_id: string | null | undefined;
}>;
/**
* Intended for use on the frontend, consuming a stream produced with
* `.toReadableStream()` on the backend.
*
* Note that messages sent to the model do not appear in `.on('message')`
* in this context.
*/
static fromReadableStream(stream: ReadableStream): MessageStream;
static createMessage<ParsedT>(messages: Messages, params: MessageCreateParamsBase, options?: RequestOptions, { logger }?: {
logger?: Logger | undefined;
}): MessageStream<ParsedT>;
protected _run(executor: () => Promise<any>): void;
protected _addMessageParam(message: MessageParam): void;
protected _addMessage(message: ParsedMessage<ParsedT>, emit?: boolean): void;
protected _createMessage(messages: Messages, params: MessageCreateParams, options?: RequestOptions): Promise<void>;
protected _connected(response: Response | null): void;
get ended(): boolean;
get errored(): boolean;
get aborted(): boolean;
abort(): void;
/**
* Adds the listener function to the end of the listeners array for the event.
* No checks are made to see if the listener has already been added. Multiple calls passing
* the same combination of event and listener will result in the listener being added, and
* called, multiple times.
* @returns this MessageStream, so that calls can be chained
*/
on<Event extends keyof MessageStreamEvents<ParsedT>>(event: Event, listener: MessageStreamEvents<ParsedT>[Event]): this;
/**
* Removes the specified listener from the listener array for the event.
* off() will remove, at most, one instance of a listener from the listener array. If any single
* listener has been added multiple times to the listener array for the specified event, then
* off() must be called multiple times to remove each instance.
* @returns this MessageStream, so that calls can be chained
*/
off<Event extends keyof MessageStreamEvents<ParsedT>>(event: Event, listener: MessageStreamEvents<ParsedT>[Event]): this;
/**
* Adds a one-time listener function for the event. The next time the event is triggered,
* this listener is removed and then invoked.
* @returns this MessageStream, so that calls can be chained
*/
once<Event extends keyof MessageStreamEvents<ParsedT>>(event: Event, listener: MessageStreamEvents<ParsedT>[Event]): this;
/**
* This is similar to `.once()`, but returns a Promise that resolves the next time
* the event is triggered, instead of calling a listener callback.
* @returns a Promise that resolves the next time given event is triggered,
* or rejects if an error is emitted. (If you request the 'error' event,
* returns a promise that resolves with the error).
*
* Example:
*
* const message = await stream.emitted('message') // rejects if the stream errors
*/
emitted<Event extends keyof MessageStreamEvents<ParsedT>>(event: Event): Promise<Parameters<MessageStreamEvents<ParsedT>[Event]> extends [infer Param] ? Param : Parameters<MessageStreamEvents<ParsedT>[Event]> extends [] ? void : Parameters<MessageStreamEvents<ParsedT>[Event]>>;
done(): Promise<void>;
get currentMessage(): Message | undefined;
/**
* @returns a promise that resolves with the the final assistant Message response,
* or rejects if an error occurred or the stream ended prematurely without producing a Message.
* If structured outputs were used, this will be a ParsedMessage with a `parsed_output` field.
*/
finalMessage(): Promise<ParsedMessage<ParsedT>>;
/**
* @returns a promise that resolves with the the final assistant Message's text response, concatenated
* together if there are more than one text blocks.
* Rejects if an error occurred or the stream ended prematurely without producing a Message.
*/
finalText(): Promise<string>;
protected _emit<Event extends keyof MessageStreamEvents<ParsedT>>(event: Event, ...args: Parameters<MessageStreamEvents<ParsedT>[Event]>): void;
protected _emitFinal(): void;
protected _fromReadableStream(readableStream: ReadableStream, options?: RequestOptions): Promise<void>;
[Symbol.asyncIterator](): AsyncIterator<MessageStreamEvent>;
toReadableStream(): ReadableStream;
}
//# sourceMappingURL=MessageStream.d.mts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"MessageStream.d.mts","sourceRoot":"","sources":["../src/lib/MessageStream.ts"],"names":[],"mappings":"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"}
+121
View File
@@ -0,0 +1,121 @@
import { AnthropicError, APIUserAbortError } from "../error.js";
import { type ContentBlock, Messages, type Message, type MessageStreamEvent, type MessageParam, type MessageCreateParams, type MessageCreateParamsBase, type TextCitation, type ToolUseBlock, type ServerToolUseBlock } from "../resources/messages.js";
import { RequestOptions } from "../internal/request-options.js";
import type { Logger } from "../client.js";
import { type ParsedMessage } from "./parser.js";
export interface MessageStreamEvents<ParsedT = null> {
connect: () => void;
streamEvent: (event: MessageStreamEvent, snapshot: Message) => void;
text: (textDelta: string, textSnapshot: string) => void;
citation: (citation: TextCitation, citationsSnapshot: TextCitation[]) => void;
inputJson: (partialJson: string, jsonSnapshot: unknown) => void;
thinking: (thinkingDelta: string, thinkingSnapshot: string) => void;
signature: (signature: string) => void;
message: (message: ParsedMessage<ParsedT>) => void;
contentBlock: (content: ContentBlock) => void;
finalMessage: (message: ParsedMessage<ParsedT>) => void;
error: (error: AnthropicError) => void;
abort: (error: APIUserAbortError) => void;
end: () => void;
}
export type TracksToolInput = ToolUseBlock | ServerToolUseBlock;
export declare class MessageStream<ParsedT = null> implements AsyncIterable<MessageStreamEvent> {
#private;
messages: MessageParam[];
receivedMessages: ParsedMessage<ParsedT>[];
controller: AbortController;
constructor(params: MessageCreateParamsBase | null, opts?: {
logger?: Logger | undefined;
});
get response(): Response | null | undefined;
get request_id(): string | null | undefined;
/**
* Returns the `MessageStream` data, the raw `Response` instance and the ID of the request,
* returned vie the `request-id` header which is useful for debugging requests and resporting
* issues to Anthropic.
*
* This is the same as the `APIPromise.withResponse()` method.
*
* This method will raise an error if you created the stream using `MessageStream.fromReadableStream`
* as no `Response` is available.
*/
withResponse(): Promise<{
data: MessageStream<ParsedT>;
response: Response;
request_id: string | null | undefined;
}>;
/**
* Intended for use on the frontend, consuming a stream produced with
* `.toReadableStream()` on the backend.
*
* Note that messages sent to the model do not appear in `.on('message')`
* in this context.
*/
static fromReadableStream(stream: ReadableStream): MessageStream;
static createMessage<ParsedT>(messages: Messages, params: MessageCreateParamsBase, options?: RequestOptions, { logger }?: {
logger?: Logger | undefined;
}): MessageStream<ParsedT>;
protected _run(executor: () => Promise<any>): void;
protected _addMessageParam(message: MessageParam): void;
protected _addMessage(message: ParsedMessage<ParsedT>, emit?: boolean): void;
protected _createMessage(messages: Messages, params: MessageCreateParams, options?: RequestOptions): Promise<void>;
protected _connected(response: Response | null): void;
get ended(): boolean;
get errored(): boolean;
get aborted(): boolean;
abort(): void;
/**
* Adds the listener function to the end of the listeners array for the event.
* No checks are made to see if the listener has already been added. Multiple calls passing
* the same combination of event and listener will result in the listener being added, and
* called, multiple times.
* @returns this MessageStream, so that calls can be chained
*/
on<Event extends keyof MessageStreamEvents<ParsedT>>(event: Event, listener: MessageStreamEvents<ParsedT>[Event]): this;
/**
* Removes the specified listener from the listener array for the event.
* off() will remove, at most, one instance of a listener from the listener array. If any single
* listener has been added multiple times to the listener array for the specified event, then
* off() must be called multiple times to remove each instance.
* @returns this MessageStream, so that calls can be chained
*/
off<Event extends keyof MessageStreamEvents<ParsedT>>(event: Event, listener: MessageStreamEvents<ParsedT>[Event]): this;
/**
* Adds a one-time listener function for the event. The next time the event is triggered,
* this listener is removed and then invoked.
* @returns this MessageStream, so that calls can be chained
*/
once<Event extends keyof MessageStreamEvents<ParsedT>>(event: Event, listener: MessageStreamEvents<ParsedT>[Event]): this;
/**
* This is similar to `.once()`, but returns a Promise that resolves the next time
* the event is triggered, instead of calling a listener callback.
* @returns a Promise that resolves the next time given event is triggered,
* or rejects if an error is emitted. (If you request the 'error' event,
* returns a promise that resolves with the error).
*
* Example:
*
* const message = await stream.emitted('message') // rejects if the stream errors
*/
emitted<Event extends keyof MessageStreamEvents<ParsedT>>(event: Event): Promise<Parameters<MessageStreamEvents<ParsedT>[Event]> extends [infer Param] ? Param : Parameters<MessageStreamEvents<ParsedT>[Event]> extends [] ? void : Parameters<MessageStreamEvents<ParsedT>[Event]>>;
done(): Promise<void>;
get currentMessage(): Message | undefined;
/**
* @returns a promise that resolves with the the final assistant Message response,
* or rejects if an error occurred or the stream ended prematurely without producing a Message.
* If structured outputs were used, this will be a ParsedMessage with a `parsed_output` field.
*/
finalMessage(): Promise<ParsedMessage<ParsedT>>;
/**
* @returns a promise that resolves with the the final assistant Message's text response, concatenated
* together if there are more than one text blocks.
* Rejects if an error occurred or the stream ended prematurely without producing a Message.
*/
finalText(): Promise<string>;
protected _emit<Event extends keyof MessageStreamEvents<ParsedT>>(event: Event, ...args: Parameters<MessageStreamEvents<ParsedT>[Event]>): void;
protected _emitFinal(): void;
protected _fromReadableStream(readableStream: ReadableStream, options?: RequestOptions): Promise<void>;
[Symbol.asyncIterator](): AsyncIterator<MessageStreamEvent>;
toReadableStream(): ReadableStream;
}
//# sourceMappingURL=MessageStream.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"MessageStream.d.ts","sourceRoot":"","sources":["../src/lib/MessageStream.ts"],"names":[],"mappings":"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"}
+595
View File
@@ -0,0 +1,595 @@
"use strict";
var _MessageStream_instances, _MessageStream_currentMessageSnapshot, _MessageStream_params, _MessageStream_connectedPromise, _MessageStream_resolveConnectedPromise, _MessageStream_rejectConnectedPromise, _MessageStream_endPromise, _MessageStream_resolveEndPromise, _MessageStream_rejectEndPromise, _MessageStream_listeners, _MessageStream_ended, _MessageStream_errored, _MessageStream_aborted, _MessageStream_catchingPromiseCreated, _MessageStream_response, _MessageStream_request_id, _MessageStream_logger, _MessageStream_getFinalMessage, _MessageStream_getFinalText, _MessageStream_handleError, _MessageStream_beginRequest, _MessageStream_addStreamEvent, _MessageStream_endRequest, _MessageStream_accumulateMessage;
Object.defineProperty(exports, "__esModule", { value: true });
exports.MessageStream = void 0;
const tslib_1 = require("../internal/tslib.js");
const errors_1 = require("../internal/errors.js");
const error_1 = require("../error.js");
const streaming_1 = require("../streaming.js");
const parser_1 = require("../_vendor/partial-json-parser/parser.js");
const parser_2 = require("./parser.js");
const JSON_BUF_PROPERTY = '__json_buf';
function tracksToolInput(content) {
return content.type === 'tool_use' || content.type === 'server_tool_use';
}
class MessageStream {
constructor(params, opts) {
_MessageStream_instances.add(this);
this.messages = [];
this.receivedMessages = [];
_MessageStream_currentMessageSnapshot.set(this, void 0);
_MessageStream_params.set(this, null);
this.controller = new AbortController();
_MessageStream_connectedPromise.set(this, void 0);
_MessageStream_resolveConnectedPromise.set(this, () => { });
_MessageStream_rejectConnectedPromise.set(this, () => { });
_MessageStream_endPromise.set(this, void 0);
_MessageStream_resolveEndPromise.set(this, () => { });
_MessageStream_rejectEndPromise.set(this, () => { });
_MessageStream_listeners.set(this, {});
_MessageStream_ended.set(this, false);
_MessageStream_errored.set(this, false);
_MessageStream_aborted.set(this, false);
_MessageStream_catchingPromiseCreated.set(this, false);
_MessageStream_response.set(this, void 0);
_MessageStream_request_id.set(this, void 0);
_MessageStream_logger.set(this, void 0);
_MessageStream_handleError.set(this, (error) => {
tslib_1.__classPrivateFieldSet(this, _MessageStream_errored, true, "f");
if ((0, errors_1.isAbortError)(error)) {
error = new error_1.APIUserAbortError();
}
if (error instanceof error_1.APIUserAbortError) {
tslib_1.__classPrivateFieldSet(this, _MessageStream_aborted, true, "f");
return this._emit('abort', error);
}
if (error instanceof error_1.AnthropicError) {
return this._emit('error', error);
}
if (error instanceof Error) {
const anthropicError = new error_1.AnthropicError(error.message);
// @ts-ignore
anthropicError.cause = error;
return this._emit('error', anthropicError);
}
return this._emit('error', new error_1.AnthropicError(String(error)));
});
tslib_1.__classPrivateFieldSet(this, _MessageStream_connectedPromise, new Promise((resolve, reject) => {
tslib_1.__classPrivateFieldSet(this, _MessageStream_resolveConnectedPromise, resolve, "f");
tslib_1.__classPrivateFieldSet(this, _MessageStream_rejectConnectedPromise, reject, "f");
}), "f");
tslib_1.__classPrivateFieldSet(this, _MessageStream_endPromise, new Promise((resolve, reject) => {
tslib_1.__classPrivateFieldSet(this, _MessageStream_resolveEndPromise, resolve, "f");
tslib_1.__classPrivateFieldSet(this, _MessageStream_rejectEndPromise, reject, "f");
}), "f");
// Don't let these promises cause unhandled rejection errors.
// we will manually cause an unhandled rejection error later
// if the user hasn't registered any error listener or called
// any promise-returning method.
tslib_1.__classPrivateFieldGet(this, _MessageStream_connectedPromise, "f").catch(() => { });
tslib_1.__classPrivateFieldGet(this, _MessageStream_endPromise, "f").catch(() => { });
tslib_1.__classPrivateFieldSet(this, _MessageStream_params, params, "f");
tslib_1.__classPrivateFieldSet(this, _MessageStream_logger, opts?.logger ?? console, "f");
}
get response() {
return tslib_1.__classPrivateFieldGet(this, _MessageStream_response, "f");
}
get request_id() {
return tslib_1.__classPrivateFieldGet(this, _MessageStream_request_id, "f");
}
/**
* Returns the `MessageStream` data, the raw `Response` instance and the ID of the request,
* returned vie the `request-id` header which is useful for debugging requests and resporting
* issues to Anthropic.
*
* This is the same as the `APIPromise.withResponse()` method.
*
* This method will raise an error if you created the stream using `MessageStream.fromReadableStream`
* as no `Response` is available.
*/
async withResponse() {
tslib_1.__classPrivateFieldSet(this, _MessageStream_catchingPromiseCreated, true, "f");
const response = await tslib_1.__classPrivateFieldGet(this, _MessageStream_connectedPromise, "f");
if (!response) {
throw new Error('Could not resolve a `Response` object');
}
return {
data: this,
response,
request_id: response.headers.get('request-id'),
};
}
/**
* Intended for use on the frontend, consuming a stream produced with
* `.toReadableStream()` on the backend.
*
* Note that messages sent to the model do not appear in `.on('message')`
* in this context.
*/
static fromReadableStream(stream) {
const runner = new MessageStream(null);
runner._run(() => runner._fromReadableStream(stream));
return runner;
}
static createMessage(messages, params, options, { logger } = {}) {
const runner = new MessageStream(params, { logger });
for (const message of params.messages) {
runner._addMessageParam(message);
}
tslib_1.__classPrivateFieldSet(runner, _MessageStream_params, { ...params, stream: true }, "f");
runner._run(() => runner._createMessage(messages, { ...params, stream: true }, { ...options, headers: { ...options?.headers, 'X-Stainless-Helper-Method': 'stream' } }));
return runner;
}
_run(executor) {
executor().then(() => {
this._emitFinal();
this._emit('end');
}, tslib_1.__classPrivateFieldGet(this, _MessageStream_handleError, "f"));
}
_addMessageParam(message) {
this.messages.push(message);
}
_addMessage(message, emit = true) {
this.receivedMessages.push(message);
if (emit) {
this._emit('message', message);
}
}
async _createMessage(messages, params, options) {
const signal = options?.signal;
let abortHandler;
if (signal) {
if (signal.aborted)
this.controller.abort();
abortHandler = this.controller.abort.bind(this.controller);
signal.addEventListener('abort', abortHandler);
}
try {
tslib_1.__classPrivateFieldGet(this, _MessageStream_instances, "m", _MessageStream_beginRequest).call(this);
const { response, data: stream } = await messages
.create({ ...params, stream: true }, { ...options, signal: this.controller.signal })
.withResponse();
this._connected(response);
for await (const event of stream) {
tslib_1.__classPrivateFieldGet(this, _MessageStream_instances, "m", _MessageStream_addStreamEvent).call(this, event);
}
if (stream.controller.signal?.aborted) {
throw new error_1.APIUserAbortError();
}
tslib_1.__classPrivateFieldGet(this, _MessageStream_instances, "m", _MessageStream_endRequest).call(this);
}
finally {
if (signal && abortHandler) {
signal.removeEventListener('abort', abortHandler);
}
}
}
_connected(response) {
if (this.ended)
return;
tslib_1.__classPrivateFieldSet(this, _MessageStream_response, response, "f");
tslib_1.__classPrivateFieldSet(this, _MessageStream_request_id, response?.headers.get('request-id'), "f");
tslib_1.__classPrivateFieldGet(this, _MessageStream_resolveConnectedPromise, "f").call(this, response);
this._emit('connect');
}
get ended() {
return tslib_1.__classPrivateFieldGet(this, _MessageStream_ended, "f");
}
get errored() {
return tslib_1.__classPrivateFieldGet(this, _MessageStream_errored, "f");
}
get aborted() {
return tslib_1.__classPrivateFieldGet(this, _MessageStream_aborted, "f");
}
abort() {
this.controller.abort();
}
/**
* Adds the listener function to the end of the listeners array for the event.
* No checks are made to see if the listener has already been added. Multiple calls passing
* the same combination of event and listener will result in the listener being added, and
* called, multiple times.
* @returns this MessageStream, so that calls can be chained
*/
on(event, listener) {
const listeners = tslib_1.__classPrivateFieldGet(this, _MessageStream_listeners, "f")[event] || (tslib_1.__classPrivateFieldGet(this, _MessageStream_listeners, "f")[event] = []);
listeners.push({ listener });
return this;
}
/**
* Removes the specified listener from the listener array for the event.
* off() will remove, at most, one instance of a listener from the listener array. If any single
* listener has been added multiple times to the listener array for the specified event, then
* off() must be called multiple times to remove each instance.
* @returns this MessageStream, so that calls can be chained
*/
off(event, listener) {
const listeners = tslib_1.__classPrivateFieldGet(this, _MessageStream_listeners, "f")[event];
if (!listeners)
return this;
const index = listeners.findIndex((l) => l.listener === listener);
if (index >= 0)
listeners.splice(index, 1);
return this;
}
/**
* Adds a one-time listener function for the event. The next time the event is triggered,
* this listener is removed and then invoked.
* @returns this MessageStream, so that calls can be chained
*/
once(event, listener) {
const listeners = tslib_1.__classPrivateFieldGet(this, _MessageStream_listeners, "f")[event] || (tslib_1.__classPrivateFieldGet(this, _MessageStream_listeners, "f")[event] = []);
listeners.push({ listener, once: true });
return this;
}
/**
* This is similar to `.once()`, but returns a Promise that resolves the next time
* the event is triggered, instead of calling a listener callback.
* @returns a Promise that resolves the next time given event is triggered,
* or rejects if an error is emitted. (If you request the 'error' event,
* returns a promise that resolves with the error).
*
* Example:
*
* const message = await stream.emitted('message') // rejects if the stream errors
*/
emitted(event) {
return new Promise((resolve, reject) => {
tslib_1.__classPrivateFieldSet(this, _MessageStream_catchingPromiseCreated, true, "f");
if (event !== 'error')
this.once('error', reject);
this.once(event, resolve);
});
}
async done() {
tslib_1.__classPrivateFieldSet(this, _MessageStream_catchingPromiseCreated, true, "f");
await tslib_1.__classPrivateFieldGet(this, _MessageStream_endPromise, "f");
}
get currentMessage() {
return tslib_1.__classPrivateFieldGet(this, _MessageStream_currentMessageSnapshot, "f");
}
/**
* @returns a promise that resolves with the the final assistant Message response,
* or rejects if an error occurred or the stream ended prematurely without producing a Message.
* If structured outputs were used, this will be a ParsedMessage with a `parsed_output` field.
*/
async finalMessage() {
await this.done();
return tslib_1.__classPrivateFieldGet(this, _MessageStream_instances, "m", _MessageStream_getFinalMessage).call(this);
}
/**
* @returns a promise that resolves with the the final assistant Message's text response, concatenated
* together if there are more than one text blocks.
* Rejects if an error occurred or the stream ended prematurely without producing a Message.
*/
async finalText() {
await this.done();
return tslib_1.__classPrivateFieldGet(this, _MessageStream_instances, "m", _MessageStream_getFinalText).call(this);
}
_emit(event, ...args) {
// make sure we don't emit any MessageStreamEvents after end
if (tslib_1.__classPrivateFieldGet(this, _MessageStream_ended, "f"))
return;
if (event === 'end') {
tslib_1.__classPrivateFieldSet(this, _MessageStream_ended, true, "f");
tslib_1.__classPrivateFieldGet(this, _MessageStream_resolveEndPromise, "f").call(this);
}
const listeners = tslib_1.__classPrivateFieldGet(this, _MessageStream_listeners, "f")[event];
if (listeners) {
tslib_1.__classPrivateFieldGet(this, _MessageStream_listeners, "f")[event] = listeners.filter((l) => !l.once);
listeners.forEach(({ listener }) => listener(...args));
}
if (event === 'abort') {
const error = args[0];
if (!tslib_1.__classPrivateFieldGet(this, _MessageStream_catchingPromiseCreated, "f") && !listeners?.length) {
Promise.reject(error);
}
tslib_1.__classPrivateFieldGet(this, _MessageStream_rejectConnectedPromise, "f").call(this, error);
tslib_1.__classPrivateFieldGet(this, _MessageStream_rejectEndPromise, "f").call(this, error);
this._emit('end');
return;
}
if (event === 'error') {
// NOTE: _emit('error', error) should only be called from #handleError().
const error = args[0];
if (!tslib_1.__classPrivateFieldGet(this, _MessageStream_catchingPromiseCreated, "f") && !listeners?.length) {
// Trigger an unhandled rejection if the user hasn't registered any error handlers.
// If you are seeing stack traces here, make sure to handle errors via either:
// - runner.on('error', () => ...)
// - await runner.done()
// - await runner.final...()
// - etc.
Promise.reject(error);
}
tslib_1.__classPrivateFieldGet(this, _MessageStream_rejectConnectedPromise, "f").call(this, error);
tslib_1.__classPrivateFieldGet(this, _MessageStream_rejectEndPromise, "f").call(this, error);
this._emit('end');
}
}
_emitFinal() {
const finalMessage = this.receivedMessages.at(-1);
if (finalMessage) {
this._emit('finalMessage', tslib_1.__classPrivateFieldGet(this, _MessageStream_instances, "m", _MessageStream_getFinalMessage).call(this));
}
}
async _fromReadableStream(readableStream, options) {
const signal = options?.signal;
let abortHandler;
if (signal) {
if (signal.aborted)
this.controller.abort();
abortHandler = this.controller.abort.bind(this.controller);
signal.addEventListener('abort', abortHandler);
}
try {
tslib_1.__classPrivateFieldGet(this, _MessageStream_instances, "m", _MessageStream_beginRequest).call(this);
this._connected(null);
const stream = streaming_1.Stream.fromReadableStream(readableStream, this.controller);
for await (const event of stream) {
tslib_1.__classPrivateFieldGet(this, _MessageStream_instances, "m", _MessageStream_addStreamEvent).call(this, event);
}
if (stream.controller.signal?.aborted) {
throw new error_1.APIUserAbortError();
}
tslib_1.__classPrivateFieldGet(this, _MessageStream_instances, "m", _MessageStream_endRequest).call(this);
}
finally {
if (signal && abortHandler) {
signal.removeEventListener('abort', abortHandler);
}
}
}
[(_MessageStream_currentMessageSnapshot = new WeakMap(), _MessageStream_params = new WeakMap(), _MessageStream_connectedPromise = new WeakMap(), _MessageStream_resolveConnectedPromise = new WeakMap(), _MessageStream_rejectConnectedPromise = new WeakMap(), _MessageStream_endPromise = new WeakMap(), _MessageStream_resolveEndPromise = new WeakMap(), _MessageStream_rejectEndPromise = new WeakMap(), _MessageStream_listeners = new WeakMap(), _MessageStream_ended = new WeakMap(), _MessageStream_errored = new WeakMap(), _MessageStream_aborted = new WeakMap(), _MessageStream_catchingPromiseCreated = new WeakMap(), _MessageStream_response = new WeakMap(), _MessageStream_request_id = new WeakMap(), _MessageStream_logger = new WeakMap(), _MessageStream_handleError = new WeakMap(), _MessageStream_instances = new WeakSet(), _MessageStream_getFinalMessage = function _MessageStream_getFinalMessage() {
if (this.receivedMessages.length === 0) {
throw new error_1.AnthropicError('stream ended without producing a Message with role=assistant');
}
return this.receivedMessages.at(-1);
}, _MessageStream_getFinalText = function _MessageStream_getFinalText() {
if (this.receivedMessages.length === 0) {
throw new error_1.AnthropicError('stream ended without producing a Message with role=assistant');
}
const textBlocks = this.receivedMessages
.at(-1)
.content.filter((block) => block.type === 'text')
.map((block) => block.text);
if (textBlocks.length === 0) {
throw new error_1.AnthropicError('stream ended without producing a content block with type=text');
}
return textBlocks.join(' ');
}, _MessageStream_beginRequest = function _MessageStream_beginRequest() {
if (this.ended)
return;
tslib_1.__classPrivateFieldSet(this, _MessageStream_currentMessageSnapshot, undefined, "f");
}, _MessageStream_addStreamEvent = function _MessageStream_addStreamEvent(event) {
if (this.ended)
return;
const messageSnapshot = tslib_1.__classPrivateFieldGet(this, _MessageStream_instances, "m", _MessageStream_accumulateMessage).call(this, event);
this._emit('streamEvent', event, messageSnapshot);
switch (event.type) {
case 'content_block_delta': {
const content = messageSnapshot.content.at(-1);
switch (event.delta.type) {
case 'text_delta': {
if (content.type === 'text') {
this._emit('text', event.delta.text, content.text || '');
}
break;
}
case 'citations_delta': {
if (content.type === 'text') {
this._emit('citation', event.delta.citation, content.citations ?? []);
}
break;
}
case 'input_json_delta': {
if (tracksToolInput(content) && content.input) {
this._emit('inputJson', event.delta.partial_json, content.input);
}
break;
}
case 'thinking_delta': {
if (content.type === 'thinking') {
this._emit('thinking', event.delta.thinking, content.thinking);
}
break;
}
case 'signature_delta': {
if (content.type === 'thinking') {
this._emit('signature', content.signature);
}
break;
}
default:
checkNever(event.delta);
}
break;
}
case 'message_stop': {
this._addMessageParam(messageSnapshot);
this._addMessage((0, parser_2.maybeParseMessage)(messageSnapshot, tslib_1.__classPrivateFieldGet(this, _MessageStream_params, "f"), { logger: tslib_1.__classPrivateFieldGet(this, _MessageStream_logger, "f") }), true);
break;
}
case 'content_block_stop': {
this._emit('contentBlock', messageSnapshot.content.at(-1));
break;
}
case 'message_start': {
tslib_1.__classPrivateFieldSet(this, _MessageStream_currentMessageSnapshot, messageSnapshot, "f");
break;
}
case 'content_block_start':
case 'message_delta':
break;
}
}, _MessageStream_endRequest = function _MessageStream_endRequest() {
if (this.ended) {
throw new error_1.AnthropicError(`stream has ended, this shouldn't happen`);
}
const snapshot = tslib_1.__classPrivateFieldGet(this, _MessageStream_currentMessageSnapshot, "f");
if (!snapshot) {
throw new error_1.AnthropicError(`request ended without sending any chunks`);
}
tslib_1.__classPrivateFieldSet(this, _MessageStream_currentMessageSnapshot, undefined, "f");
return (0, parser_2.maybeParseMessage)(snapshot, tslib_1.__classPrivateFieldGet(this, _MessageStream_params, "f"), { logger: tslib_1.__classPrivateFieldGet(this, _MessageStream_logger, "f") });
}, _MessageStream_accumulateMessage = function _MessageStream_accumulateMessage(event) {
let snapshot = tslib_1.__classPrivateFieldGet(this, _MessageStream_currentMessageSnapshot, "f");
if (event.type === 'message_start') {
if (snapshot) {
throw new error_1.AnthropicError(`Unexpected event order, got ${event.type} before receiving "message_stop"`);
}
return event.message;
}
if (!snapshot) {
throw new error_1.AnthropicError(`Unexpected event order, got ${event.type} before "message_start"`);
}
switch (event.type) {
case 'message_stop':
return snapshot;
case 'message_delta':
snapshot.stop_reason = event.delta.stop_reason;
snapshot.stop_sequence = event.delta.stop_sequence;
snapshot.usage.output_tokens = event.usage.output_tokens;
// Update other usage fields if they exist in the event
if (event.usage.input_tokens != null) {
snapshot.usage.input_tokens = event.usage.input_tokens;
}
if (event.usage.cache_creation_input_tokens != null) {
snapshot.usage.cache_creation_input_tokens = event.usage.cache_creation_input_tokens;
}
if (event.usage.cache_read_input_tokens != null) {
snapshot.usage.cache_read_input_tokens = event.usage.cache_read_input_tokens;
}
if (event.usage.server_tool_use != null) {
snapshot.usage.server_tool_use = event.usage.server_tool_use;
}
return snapshot;
case 'content_block_start':
snapshot.content.push({ ...event.content_block });
return snapshot;
case 'content_block_delta': {
const snapshotContent = snapshot.content.at(event.index);
switch (event.delta.type) {
case 'text_delta': {
if (snapshotContent?.type === 'text') {
snapshot.content[event.index] = {
...snapshotContent,
text: (snapshotContent.text || '') + event.delta.text,
};
}
break;
}
case 'citations_delta': {
if (snapshotContent?.type === 'text') {
snapshot.content[event.index] = {
...snapshotContent,
citations: [...(snapshotContent.citations ?? []), event.delta.citation],
};
}
break;
}
case 'input_json_delta': {
if (snapshotContent && tracksToolInput(snapshotContent)) {
// we need to keep track of the raw JSON string as well so that we can
// re-parse it for each delta, for now we just store it as an untyped
// non-enumerable property on the snapshot
let jsonBuf = snapshotContent[JSON_BUF_PROPERTY] || '';
jsonBuf += event.delta.partial_json;
const newContent = { ...snapshotContent };
Object.defineProperty(newContent, JSON_BUF_PROPERTY, {
value: jsonBuf,
enumerable: false,
writable: true,
});
if (jsonBuf) {
newContent.input = (0, parser_1.partialParse)(jsonBuf);
}
snapshot.content[event.index] = newContent;
}
break;
}
case 'thinking_delta': {
if (snapshotContent?.type === 'thinking') {
snapshot.content[event.index] = {
...snapshotContent,
thinking: snapshotContent.thinking + event.delta.thinking,
};
}
break;
}
case 'signature_delta': {
if (snapshotContent?.type === 'thinking') {
snapshot.content[event.index] = {
...snapshotContent,
signature: event.delta.signature,
};
}
break;
}
default:
checkNever(event.delta);
}
return snapshot;
}
case 'content_block_stop':
return snapshot;
}
}, Symbol.asyncIterator)]() {
const pushQueue = [];
const readQueue = [];
let done = false;
this.on('streamEvent', (event) => {
const reader = readQueue.shift();
if (reader) {
reader.resolve(event);
}
else {
pushQueue.push(event);
}
});
this.on('end', () => {
done = true;
for (const reader of readQueue) {
reader.resolve(undefined);
}
readQueue.length = 0;
});
this.on('abort', (err) => {
done = true;
for (const reader of readQueue) {
reader.reject(err);
}
readQueue.length = 0;
});
this.on('error', (err) => {
done = true;
for (const reader of readQueue) {
reader.reject(err);
}
readQueue.length = 0;
});
return {
next: async () => {
if (!pushQueue.length) {
if (done) {
return { value: undefined, done: true };
}
return new Promise((resolve, reject) => readQueue.push({ resolve, reject })).then((chunk) => (chunk ? { value: chunk, done: false } : { value: undefined, done: true }));
}
const chunk = pushQueue.shift();
return { value: chunk, done: false };
},
return: async () => {
this.abort();
return { value: undefined, done: true };
},
};
}
toReadableStream() {
const stream = new streaming_1.Stream(this[Symbol.asyncIterator].bind(this), this.controller);
return stream.toReadableStream();
}
}
exports.MessageStream = MessageStream;
// used to ensure exhaustive case matching without throwing a runtime error
function checkNever(x) { }
//# sourceMappingURL=MessageStream.js.map
File diff suppressed because one or more lines are too long
+591
View File
@@ -0,0 +1,591 @@
var _MessageStream_instances, _MessageStream_currentMessageSnapshot, _MessageStream_params, _MessageStream_connectedPromise, _MessageStream_resolveConnectedPromise, _MessageStream_rejectConnectedPromise, _MessageStream_endPromise, _MessageStream_resolveEndPromise, _MessageStream_rejectEndPromise, _MessageStream_listeners, _MessageStream_ended, _MessageStream_errored, _MessageStream_aborted, _MessageStream_catchingPromiseCreated, _MessageStream_response, _MessageStream_request_id, _MessageStream_logger, _MessageStream_getFinalMessage, _MessageStream_getFinalText, _MessageStream_handleError, _MessageStream_beginRequest, _MessageStream_addStreamEvent, _MessageStream_endRequest, _MessageStream_accumulateMessage;
import { __classPrivateFieldGet, __classPrivateFieldSet } from "../internal/tslib.mjs";
import { isAbortError } from "../internal/errors.mjs";
import { AnthropicError, APIUserAbortError } from "../error.mjs";
import { Stream } from "../streaming.mjs";
import { partialParse } from "../_vendor/partial-json-parser/parser.mjs";
import { maybeParseMessage } from "./parser.mjs";
const JSON_BUF_PROPERTY = '__json_buf';
function tracksToolInput(content) {
return content.type === 'tool_use' || content.type === 'server_tool_use';
}
export class MessageStream {
constructor(params, opts) {
_MessageStream_instances.add(this);
this.messages = [];
this.receivedMessages = [];
_MessageStream_currentMessageSnapshot.set(this, void 0);
_MessageStream_params.set(this, null);
this.controller = new AbortController();
_MessageStream_connectedPromise.set(this, void 0);
_MessageStream_resolveConnectedPromise.set(this, () => { });
_MessageStream_rejectConnectedPromise.set(this, () => { });
_MessageStream_endPromise.set(this, void 0);
_MessageStream_resolveEndPromise.set(this, () => { });
_MessageStream_rejectEndPromise.set(this, () => { });
_MessageStream_listeners.set(this, {});
_MessageStream_ended.set(this, false);
_MessageStream_errored.set(this, false);
_MessageStream_aborted.set(this, false);
_MessageStream_catchingPromiseCreated.set(this, false);
_MessageStream_response.set(this, void 0);
_MessageStream_request_id.set(this, void 0);
_MessageStream_logger.set(this, void 0);
_MessageStream_handleError.set(this, (error) => {
__classPrivateFieldSet(this, _MessageStream_errored, true, "f");
if (isAbortError(error)) {
error = new APIUserAbortError();
}
if (error instanceof APIUserAbortError) {
__classPrivateFieldSet(this, _MessageStream_aborted, true, "f");
return this._emit('abort', error);
}
if (error instanceof AnthropicError) {
return this._emit('error', error);
}
if (error instanceof Error) {
const anthropicError = new AnthropicError(error.message);
// @ts-ignore
anthropicError.cause = error;
return this._emit('error', anthropicError);
}
return this._emit('error', new AnthropicError(String(error)));
});
__classPrivateFieldSet(this, _MessageStream_connectedPromise, new Promise((resolve, reject) => {
__classPrivateFieldSet(this, _MessageStream_resolveConnectedPromise, resolve, "f");
__classPrivateFieldSet(this, _MessageStream_rejectConnectedPromise, reject, "f");
}), "f");
__classPrivateFieldSet(this, _MessageStream_endPromise, new Promise((resolve, reject) => {
__classPrivateFieldSet(this, _MessageStream_resolveEndPromise, resolve, "f");
__classPrivateFieldSet(this, _MessageStream_rejectEndPromise, reject, "f");
}), "f");
// Don't let these promises cause unhandled rejection errors.
// we will manually cause an unhandled rejection error later
// if the user hasn't registered any error listener or called
// any promise-returning method.
__classPrivateFieldGet(this, _MessageStream_connectedPromise, "f").catch(() => { });
__classPrivateFieldGet(this, _MessageStream_endPromise, "f").catch(() => { });
__classPrivateFieldSet(this, _MessageStream_params, params, "f");
__classPrivateFieldSet(this, _MessageStream_logger, opts?.logger ?? console, "f");
}
get response() {
return __classPrivateFieldGet(this, _MessageStream_response, "f");
}
get request_id() {
return __classPrivateFieldGet(this, _MessageStream_request_id, "f");
}
/**
* Returns the `MessageStream` data, the raw `Response` instance and the ID of the request,
* returned vie the `request-id` header which is useful for debugging requests and resporting
* issues to Anthropic.
*
* This is the same as the `APIPromise.withResponse()` method.
*
* This method will raise an error if you created the stream using `MessageStream.fromReadableStream`
* as no `Response` is available.
*/
async withResponse() {
__classPrivateFieldSet(this, _MessageStream_catchingPromiseCreated, true, "f");
const response = await __classPrivateFieldGet(this, _MessageStream_connectedPromise, "f");
if (!response) {
throw new Error('Could not resolve a `Response` object');
}
return {
data: this,
response,
request_id: response.headers.get('request-id'),
};
}
/**
* Intended for use on the frontend, consuming a stream produced with
* `.toReadableStream()` on the backend.
*
* Note that messages sent to the model do not appear in `.on('message')`
* in this context.
*/
static fromReadableStream(stream) {
const runner = new MessageStream(null);
runner._run(() => runner._fromReadableStream(stream));
return runner;
}
static createMessage(messages, params, options, { logger } = {}) {
const runner = new MessageStream(params, { logger });
for (const message of params.messages) {
runner._addMessageParam(message);
}
__classPrivateFieldSet(runner, _MessageStream_params, { ...params, stream: true }, "f");
runner._run(() => runner._createMessage(messages, { ...params, stream: true }, { ...options, headers: { ...options?.headers, 'X-Stainless-Helper-Method': 'stream' } }));
return runner;
}
_run(executor) {
executor().then(() => {
this._emitFinal();
this._emit('end');
}, __classPrivateFieldGet(this, _MessageStream_handleError, "f"));
}
_addMessageParam(message) {
this.messages.push(message);
}
_addMessage(message, emit = true) {
this.receivedMessages.push(message);
if (emit) {
this._emit('message', message);
}
}
async _createMessage(messages, params, options) {
const signal = options?.signal;
let abortHandler;
if (signal) {
if (signal.aborted)
this.controller.abort();
abortHandler = this.controller.abort.bind(this.controller);
signal.addEventListener('abort', abortHandler);
}
try {
__classPrivateFieldGet(this, _MessageStream_instances, "m", _MessageStream_beginRequest).call(this);
const { response, data: stream } = await messages
.create({ ...params, stream: true }, { ...options, signal: this.controller.signal })
.withResponse();
this._connected(response);
for await (const event of stream) {
__classPrivateFieldGet(this, _MessageStream_instances, "m", _MessageStream_addStreamEvent).call(this, event);
}
if (stream.controller.signal?.aborted) {
throw new APIUserAbortError();
}
__classPrivateFieldGet(this, _MessageStream_instances, "m", _MessageStream_endRequest).call(this);
}
finally {
if (signal && abortHandler) {
signal.removeEventListener('abort', abortHandler);
}
}
}
_connected(response) {
if (this.ended)
return;
__classPrivateFieldSet(this, _MessageStream_response, response, "f");
__classPrivateFieldSet(this, _MessageStream_request_id, response?.headers.get('request-id'), "f");
__classPrivateFieldGet(this, _MessageStream_resolveConnectedPromise, "f").call(this, response);
this._emit('connect');
}
get ended() {
return __classPrivateFieldGet(this, _MessageStream_ended, "f");
}
get errored() {
return __classPrivateFieldGet(this, _MessageStream_errored, "f");
}
get aborted() {
return __classPrivateFieldGet(this, _MessageStream_aborted, "f");
}
abort() {
this.controller.abort();
}
/**
* Adds the listener function to the end of the listeners array for the event.
* No checks are made to see if the listener has already been added. Multiple calls passing
* the same combination of event and listener will result in the listener being added, and
* called, multiple times.
* @returns this MessageStream, so that calls can be chained
*/
on(event, listener) {
const listeners = __classPrivateFieldGet(this, _MessageStream_listeners, "f")[event] || (__classPrivateFieldGet(this, _MessageStream_listeners, "f")[event] = []);
listeners.push({ listener });
return this;
}
/**
* Removes the specified listener from the listener array for the event.
* off() will remove, at most, one instance of a listener from the listener array. If any single
* listener has been added multiple times to the listener array for the specified event, then
* off() must be called multiple times to remove each instance.
* @returns this MessageStream, so that calls can be chained
*/
off(event, listener) {
const listeners = __classPrivateFieldGet(this, _MessageStream_listeners, "f")[event];
if (!listeners)
return this;
const index = listeners.findIndex((l) => l.listener === listener);
if (index >= 0)
listeners.splice(index, 1);
return this;
}
/**
* Adds a one-time listener function for the event. The next time the event is triggered,
* this listener is removed and then invoked.
* @returns this MessageStream, so that calls can be chained
*/
once(event, listener) {
const listeners = __classPrivateFieldGet(this, _MessageStream_listeners, "f")[event] || (__classPrivateFieldGet(this, _MessageStream_listeners, "f")[event] = []);
listeners.push({ listener, once: true });
return this;
}
/**
* This is similar to `.once()`, but returns a Promise that resolves the next time
* the event is triggered, instead of calling a listener callback.
* @returns a Promise that resolves the next time given event is triggered,
* or rejects if an error is emitted. (If you request the 'error' event,
* returns a promise that resolves with the error).
*
* Example:
*
* const message = await stream.emitted('message') // rejects if the stream errors
*/
emitted(event) {
return new Promise((resolve, reject) => {
__classPrivateFieldSet(this, _MessageStream_catchingPromiseCreated, true, "f");
if (event !== 'error')
this.once('error', reject);
this.once(event, resolve);
});
}
async done() {
__classPrivateFieldSet(this, _MessageStream_catchingPromiseCreated, true, "f");
await __classPrivateFieldGet(this, _MessageStream_endPromise, "f");
}
get currentMessage() {
return __classPrivateFieldGet(this, _MessageStream_currentMessageSnapshot, "f");
}
/**
* @returns a promise that resolves with the the final assistant Message response,
* or rejects if an error occurred or the stream ended prematurely without producing a Message.
* If structured outputs were used, this will be a ParsedMessage with a `parsed_output` field.
*/
async finalMessage() {
await this.done();
return __classPrivateFieldGet(this, _MessageStream_instances, "m", _MessageStream_getFinalMessage).call(this);
}
/**
* @returns a promise that resolves with the the final assistant Message's text response, concatenated
* together if there are more than one text blocks.
* Rejects if an error occurred or the stream ended prematurely without producing a Message.
*/
async finalText() {
await this.done();
return __classPrivateFieldGet(this, _MessageStream_instances, "m", _MessageStream_getFinalText).call(this);
}
_emit(event, ...args) {
// make sure we don't emit any MessageStreamEvents after end
if (__classPrivateFieldGet(this, _MessageStream_ended, "f"))
return;
if (event === 'end') {
__classPrivateFieldSet(this, _MessageStream_ended, true, "f");
__classPrivateFieldGet(this, _MessageStream_resolveEndPromise, "f").call(this);
}
const listeners = __classPrivateFieldGet(this, _MessageStream_listeners, "f")[event];
if (listeners) {
__classPrivateFieldGet(this, _MessageStream_listeners, "f")[event] = listeners.filter((l) => !l.once);
listeners.forEach(({ listener }) => listener(...args));
}
if (event === 'abort') {
const error = args[0];
if (!__classPrivateFieldGet(this, _MessageStream_catchingPromiseCreated, "f") && !listeners?.length) {
Promise.reject(error);
}
__classPrivateFieldGet(this, _MessageStream_rejectConnectedPromise, "f").call(this, error);
__classPrivateFieldGet(this, _MessageStream_rejectEndPromise, "f").call(this, error);
this._emit('end');
return;
}
if (event === 'error') {
// NOTE: _emit('error', error) should only be called from #handleError().
const error = args[0];
if (!__classPrivateFieldGet(this, _MessageStream_catchingPromiseCreated, "f") && !listeners?.length) {
// Trigger an unhandled rejection if the user hasn't registered any error handlers.
// If you are seeing stack traces here, make sure to handle errors via either:
// - runner.on('error', () => ...)
// - await runner.done()
// - await runner.final...()
// - etc.
Promise.reject(error);
}
__classPrivateFieldGet(this, _MessageStream_rejectConnectedPromise, "f").call(this, error);
__classPrivateFieldGet(this, _MessageStream_rejectEndPromise, "f").call(this, error);
this._emit('end');
}
}
_emitFinal() {
const finalMessage = this.receivedMessages.at(-1);
if (finalMessage) {
this._emit('finalMessage', __classPrivateFieldGet(this, _MessageStream_instances, "m", _MessageStream_getFinalMessage).call(this));
}
}
async _fromReadableStream(readableStream, options) {
const signal = options?.signal;
let abortHandler;
if (signal) {
if (signal.aborted)
this.controller.abort();
abortHandler = this.controller.abort.bind(this.controller);
signal.addEventListener('abort', abortHandler);
}
try {
__classPrivateFieldGet(this, _MessageStream_instances, "m", _MessageStream_beginRequest).call(this);
this._connected(null);
const stream = Stream.fromReadableStream(readableStream, this.controller);
for await (const event of stream) {
__classPrivateFieldGet(this, _MessageStream_instances, "m", _MessageStream_addStreamEvent).call(this, event);
}
if (stream.controller.signal?.aborted) {
throw new APIUserAbortError();
}
__classPrivateFieldGet(this, _MessageStream_instances, "m", _MessageStream_endRequest).call(this);
}
finally {
if (signal && abortHandler) {
signal.removeEventListener('abort', abortHandler);
}
}
}
[(_MessageStream_currentMessageSnapshot = new WeakMap(), _MessageStream_params = new WeakMap(), _MessageStream_connectedPromise = new WeakMap(), _MessageStream_resolveConnectedPromise = new WeakMap(), _MessageStream_rejectConnectedPromise = new WeakMap(), _MessageStream_endPromise = new WeakMap(), _MessageStream_resolveEndPromise = new WeakMap(), _MessageStream_rejectEndPromise = new WeakMap(), _MessageStream_listeners = new WeakMap(), _MessageStream_ended = new WeakMap(), _MessageStream_errored = new WeakMap(), _MessageStream_aborted = new WeakMap(), _MessageStream_catchingPromiseCreated = new WeakMap(), _MessageStream_response = new WeakMap(), _MessageStream_request_id = new WeakMap(), _MessageStream_logger = new WeakMap(), _MessageStream_handleError = new WeakMap(), _MessageStream_instances = new WeakSet(), _MessageStream_getFinalMessage = function _MessageStream_getFinalMessage() {
if (this.receivedMessages.length === 0) {
throw new AnthropicError('stream ended without producing a Message with role=assistant');
}
return this.receivedMessages.at(-1);
}, _MessageStream_getFinalText = function _MessageStream_getFinalText() {
if (this.receivedMessages.length === 0) {
throw new AnthropicError('stream ended without producing a Message with role=assistant');
}
const textBlocks = this.receivedMessages
.at(-1)
.content.filter((block) => block.type === 'text')
.map((block) => block.text);
if (textBlocks.length === 0) {
throw new AnthropicError('stream ended without producing a content block with type=text');
}
return textBlocks.join(' ');
}, _MessageStream_beginRequest = function _MessageStream_beginRequest() {
if (this.ended)
return;
__classPrivateFieldSet(this, _MessageStream_currentMessageSnapshot, undefined, "f");
}, _MessageStream_addStreamEvent = function _MessageStream_addStreamEvent(event) {
if (this.ended)
return;
const messageSnapshot = __classPrivateFieldGet(this, _MessageStream_instances, "m", _MessageStream_accumulateMessage).call(this, event);
this._emit('streamEvent', event, messageSnapshot);
switch (event.type) {
case 'content_block_delta': {
const content = messageSnapshot.content.at(-1);
switch (event.delta.type) {
case 'text_delta': {
if (content.type === 'text') {
this._emit('text', event.delta.text, content.text || '');
}
break;
}
case 'citations_delta': {
if (content.type === 'text') {
this._emit('citation', event.delta.citation, content.citations ?? []);
}
break;
}
case 'input_json_delta': {
if (tracksToolInput(content) && content.input) {
this._emit('inputJson', event.delta.partial_json, content.input);
}
break;
}
case 'thinking_delta': {
if (content.type === 'thinking') {
this._emit('thinking', event.delta.thinking, content.thinking);
}
break;
}
case 'signature_delta': {
if (content.type === 'thinking') {
this._emit('signature', content.signature);
}
break;
}
default:
checkNever(event.delta);
}
break;
}
case 'message_stop': {
this._addMessageParam(messageSnapshot);
this._addMessage(maybeParseMessage(messageSnapshot, __classPrivateFieldGet(this, _MessageStream_params, "f"), { logger: __classPrivateFieldGet(this, _MessageStream_logger, "f") }), true);
break;
}
case 'content_block_stop': {
this._emit('contentBlock', messageSnapshot.content.at(-1));
break;
}
case 'message_start': {
__classPrivateFieldSet(this, _MessageStream_currentMessageSnapshot, messageSnapshot, "f");
break;
}
case 'content_block_start':
case 'message_delta':
break;
}
}, _MessageStream_endRequest = function _MessageStream_endRequest() {
if (this.ended) {
throw new AnthropicError(`stream has ended, this shouldn't happen`);
}
const snapshot = __classPrivateFieldGet(this, _MessageStream_currentMessageSnapshot, "f");
if (!snapshot) {
throw new AnthropicError(`request ended without sending any chunks`);
}
__classPrivateFieldSet(this, _MessageStream_currentMessageSnapshot, undefined, "f");
return maybeParseMessage(snapshot, __classPrivateFieldGet(this, _MessageStream_params, "f"), { logger: __classPrivateFieldGet(this, _MessageStream_logger, "f") });
}, _MessageStream_accumulateMessage = function _MessageStream_accumulateMessage(event) {
let snapshot = __classPrivateFieldGet(this, _MessageStream_currentMessageSnapshot, "f");
if (event.type === 'message_start') {
if (snapshot) {
throw new AnthropicError(`Unexpected event order, got ${event.type} before receiving "message_stop"`);
}
return event.message;
}
if (!snapshot) {
throw new AnthropicError(`Unexpected event order, got ${event.type} before "message_start"`);
}
switch (event.type) {
case 'message_stop':
return snapshot;
case 'message_delta':
snapshot.stop_reason = event.delta.stop_reason;
snapshot.stop_sequence = event.delta.stop_sequence;
snapshot.usage.output_tokens = event.usage.output_tokens;
// Update other usage fields if they exist in the event
if (event.usage.input_tokens != null) {
snapshot.usage.input_tokens = event.usage.input_tokens;
}
if (event.usage.cache_creation_input_tokens != null) {
snapshot.usage.cache_creation_input_tokens = event.usage.cache_creation_input_tokens;
}
if (event.usage.cache_read_input_tokens != null) {
snapshot.usage.cache_read_input_tokens = event.usage.cache_read_input_tokens;
}
if (event.usage.server_tool_use != null) {
snapshot.usage.server_tool_use = event.usage.server_tool_use;
}
return snapshot;
case 'content_block_start':
snapshot.content.push({ ...event.content_block });
return snapshot;
case 'content_block_delta': {
const snapshotContent = snapshot.content.at(event.index);
switch (event.delta.type) {
case 'text_delta': {
if (snapshotContent?.type === 'text') {
snapshot.content[event.index] = {
...snapshotContent,
text: (snapshotContent.text || '') + event.delta.text,
};
}
break;
}
case 'citations_delta': {
if (snapshotContent?.type === 'text') {
snapshot.content[event.index] = {
...snapshotContent,
citations: [...(snapshotContent.citations ?? []), event.delta.citation],
};
}
break;
}
case 'input_json_delta': {
if (snapshotContent && tracksToolInput(snapshotContent)) {
// we need to keep track of the raw JSON string as well so that we can
// re-parse it for each delta, for now we just store it as an untyped
// non-enumerable property on the snapshot
let jsonBuf = snapshotContent[JSON_BUF_PROPERTY] || '';
jsonBuf += event.delta.partial_json;
const newContent = { ...snapshotContent };
Object.defineProperty(newContent, JSON_BUF_PROPERTY, {
value: jsonBuf,
enumerable: false,
writable: true,
});
if (jsonBuf) {
newContent.input = partialParse(jsonBuf);
}
snapshot.content[event.index] = newContent;
}
break;
}
case 'thinking_delta': {
if (snapshotContent?.type === 'thinking') {
snapshot.content[event.index] = {
...snapshotContent,
thinking: snapshotContent.thinking + event.delta.thinking,
};
}
break;
}
case 'signature_delta': {
if (snapshotContent?.type === 'thinking') {
snapshot.content[event.index] = {
...snapshotContent,
signature: event.delta.signature,
};
}
break;
}
default:
checkNever(event.delta);
}
return snapshot;
}
case 'content_block_stop':
return snapshot;
}
}, Symbol.asyncIterator)]() {
const pushQueue = [];
const readQueue = [];
let done = false;
this.on('streamEvent', (event) => {
const reader = readQueue.shift();
if (reader) {
reader.resolve(event);
}
else {
pushQueue.push(event);
}
});
this.on('end', () => {
done = true;
for (const reader of readQueue) {
reader.resolve(undefined);
}
readQueue.length = 0;
});
this.on('abort', (err) => {
done = true;
for (const reader of readQueue) {
reader.reject(err);
}
readQueue.length = 0;
});
this.on('error', (err) => {
done = true;
for (const reader of readQueue) {
reader.reject(err);
}
readQueue.length = 0;
});
return {
next: async () => {
if (!pushQueue.length) {
if (done) {
return { value: undefined, done: true };
}
return new Promise((resolve, reject) => readQueue.push({ resolve, reject })).then((chunk) => (chunk ? { value: chunk, done: false } : { value: undefined, done: true }));
}
const chunk = pushQueue.shift();
return { value: chunk, done: false };
},
return: async () => {
this.abort();
return { value: undefined, done: true };
},
};
}
toReadableStream() {
const stream = new Stream(this[Symbol.asyncIterator].bind(this), this.controller);
return stream.toReadableStream();
}
}
// used to ensure exhaustive case matching without throwing a runtime error
function checkNever(x) { }
//# sourceMappingURL=MessageStream.mjs.map
File diff suppressed because one or more lines are too long
+37
View File
@@ -0,0 +1,37 @@
import type { Logger } from "../client.mjs";
import { BetaContentBlock, BetaJSONOutputFormat, BetaMessage, BetaOutputConfig, BetaTextBlock, MessageCreateParams } from "../resources/beta/messages/messages.mjs";
type Simplify<T> = {
[KeyType in keyof T]: T[KeyType];
} & {};
type AutoParseableBetaOutputConfig = Omit<BetaOutputConfig, 'format'> & {
format?: BetaJSONOutputFormat | AutoParseableBetaOutputFormat<any> | null;
};
export type BetaParseableMessageCreateParams = Simplify<Omit<MessageCreateParams, 'output_format' | 'output_config'> & {
/**
* @deprecated Use `output_config.format` instead. This parameter will be removed in a future
* release.
*/
output_format?: BetaJSONOutputFormat | AutoParseableBetaOutputFormat<any> | null;
output_config?: AutoParseableBetaOutputConfig | null;
}>;
export type ExtractParsedContentFromBetaParams<Params extends BetaParseableMessageCreateParams> = Params['output_format'] extends AutoParseableBetaOutputFormat<infer P> ? P : Params['output_config'] extends {
format: AutoParseableBetaOutputFormat<infer P>;
} ? P : null;
export type AutoParseableBetaOutputFormat<ParsedT> = BetaJSONOutputFormat & {
parse(content: string): ParsedT;
};
export type ParsedBetaMessage<ParsedT> = BetaMessage & {
content: Array<ParsedBetaContentBlock<ParsedT>>;
parsed_output: ParsedT | null;
};
export type ParsedBetaContentBlock<ParsedT> = (BetaTextBlock & {
parsed_output: ParsedT | null;
}) | Exclude<BetaContentBlock, BetaTextBlock>;
export declare function maybeParseBetaMessage<Params extends BetaParseableMessageCreateParams | null>(message: BetaMessage, params: Params, opts: {
logger: Logger;
}): ParsedBetaMessage<ExtractParsedContentFromBetaParams<NonNullable<Params>>>;
export declare function parseBetaMessage<Params extends BetaParseableMessageCreateParams>(message: BetaMessage, params: Params, opts: {
logger: Logger;
}): ParsedBetaMessage<ExtractParsedContentFromBetaParams<Params>>;
export {};
//# sourceMappingURL=beta-parser.d.mts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"beta-parser.d.mts","sourceRoot":"","sources":["../src/lib/beta-parser.ts"],"names":[],"mappings":"OAAO,KAAK,EAAE,MAAM,EAAE;OAEf,EACL,gBAAgB,EAChB,oBAAoB,EACpB,WAAW,EACX,gBAAgB,EAChB,aAAa,EACb,mBAAmB,EACpB;AAGD,KAAK,QAAQ,CAAC,CAAC,IAAI;KAAG,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC;CAAE,GAAG,EAAE,CAAC;AAE7D,KAAK,6BAA6B,GAAG,IAAI,CAAC,gBAAgB,EAAE,QAAQ,CAAC,GAAG;IACtE,MAAM,CAAC,EAAE,oBAAoB,GAAG,6BAA6B,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;CAC3E,CAAC;AAEF,MAAM,MAAM,gCAAgC,GAAG,QAAQ,CACrD,IAAI,CAAC,mBAAmB,EAAE,eAAe,GAAG,eAAe,CAAC,GAAG;IAC7D;;;OAGG;IACH,aAAa,CAAC,EAAE,oBAAoB,GAAG,6BAA6B,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;IACjF,aAAa,CAAC,EAAE,6BAA6B,GAAG,IAAI,CAAC;CACtD,CACF,CAAC;AAEF,MAAM,MAAM,kCAAkC,CAAC,MAAM,SAAS,gCAAgC,IAC5F,MAAM,CAAC,eAAe,CAAC,SAAS,6BAA6B,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GACxE,MAAM,CAAC,eAAe,CAAC,SAAS;IAAE,MAAM,EAAE,6BAA6B,CAAC,MAAM,CAAC,CAAC,CAAA;CAAE,GAAG,CAAC,GACtF,IAAI,CAAC;AAET,MAAM,MAAM,6BAA6B,CAAC,OAAO,IAAI,oBAAoB,GAAG;IAC1E,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,iBAAiB,CAAC,OAAO,IAAI,WAAW,GAAG;IACrD,OAAO,EAAE,KAAK,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC,CAAC;IAChD,aAAa,EAAE,OAAO,GAAG,IAAI,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,sBAAsB,CAAC,OAAO,IACtC,CAAC,aAAa,GAAG;IAAE,aAAa,EAAE,OAAO,GAAG,IAAI,CAAA;CAAE,CAAC,GACnD,OAAO,CAAC,gBAAgB,EAAE,aAAa,CAAC,CAAC;AAS7C,wBAAgB,qBAAqB,CAAC,MAAM,SAAS,gCAAgC,GAAG,IAAI,EAC1F,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GACvB,iBAAiB,CAAC,kCAAkC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,CA6B5E;AAED,wBAAgB,gBAAgB,CAAC,MAAM,SAAS,gCAAgC,EAC9E,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GACvB,iBAAiB,CAAC,kCAAkC,CAAC,MAAM,CAAC,CAAC,CAkC/D"}
+37
View File
@@ -0,0 +1,37 @@
import type { Logger } from "../client.js";
import { BetaContentBlock, BetaJSONOutputFormat, BetaMessage, BetaOutputConfig, BetaTextBlock, MessageCreateParams } from "../resources/beta/messages/messages.js";
type Simplify<T> = {
[KeyType in keyof T]: T[KeyType];
} & {};
type AutoParseableBetaOutputConfig = Omit<BetaOutputConfig, 'format'> & {
format?: BetaJSONOutputFormat | AutoParseableBetaOutputFormat<any> | null;
};
export type BetaParseableMessageCreateParams = Simplify<Omit<MessageCreateParams, 'output_format' | 'output_config'> & {
/**
* @deprecated Use `output_config.format` instead. This parameter will be removed in a future
* release.
*/
output_format?: BetaJSONOutputFormat | AutoParseableBetaOutputFormat<any> | null;
output_config?: AutoParseableBetaOutputConfig | null;
}>;
export type ExtractParsedContentFromBetaParams<Params extends BetaParseableMessageCreateParams> = Params['output_format'] extends AutoParseableBetaOutputFormat<infer P> ? P : Params['output_config'] extends {
format: AutoParseableBetaOutputFormat<infer P>;
} ? P : null;
export type AutoParseableBetaOutputFormat<ParsedT> = BetaJSONOutputFormat & {
parse(content: string): ParsedT;
};
export type ParsedBetaMessage<ParsedT> = BetaMessage & {
content: Array<ParsedBetaContentBlock<ParsedT>>;
parsed_output: ParsedT | null;
};
export type ParsedBetaContentBlock<ParsedT> = (BetaTextBlock & {
parsed_output: ParsedT | null;
}) | Exclude<BetaContentBlock, BetaTextBlock>;
export declare function maybeParseBetaMessage<Params extends BetaParseableMessageCreateParams | null>(message: BetaMessage, params: Params, opts: {
logger: Logger;
}): ParsedBetaMessage<ExtractParsedContentFromBetaParams<NonNullable<Params>>>;
export declare function parseBetaMessage<Params extends BetaParseableMessageCreateParams>(message: BetaMessage, params: Params, opts: {
logger: Logger;
}): ParsedBetaMessage<ExtractParsedContentFromBetaParams<Params>>;
export {};
//# sourceMappingURL=beta-parser.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"beta-parser.d.ts","sourceRoot":"","sources":["../src/lib/beta-parser.ts"],"names":[],"mappings":"OAAO,KAAK,EAAE,MAAM,EAAE;OAEf,EACL,gBAAgB,EAChB,oBAAoB,EACpB,WAAW,EACX,gBAAgB,EAChB,aAAa,EACb,mBAAmB,EACpB;AAGD,KAAK,QAAQ,CAAC,CAAC,IAAI;KAAG,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC;CAAE,GAAG,EAAE,CAAC;AAE7D,KAAK,6BAA6B,GAAG,IAAI,CAAC,gBAAgB,EAAE,QAAQ,CAAC,GAAG;IACtE,MAAM,CAAC,EAAE,oBAAoB,GAAG,6BAA6B,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;CAC3E,CAAC;AAEF,MAAM,MAAM,gCAAgC,GAAG,QAAQ,CACrD,IAAI,CAAC,mBAAmB,EAAE,eAAe,GAAG,eAAe,CAAC,GAAG;IAC7D;;;OAGG;IACH,aAAa,CAAC,EAAE,oBAAoB,GAAG,6BAA6B,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;IACjF,aAAa,CAAC,EAAE,6BAA6B,GAAG,IAAI,CAAC;CACtD,CACF,CAAC;AAEF,MAAM,MAAM,kCAAkC,CAAC,MAAM,SAAS,gCAAgC,IAC5F,MAAM,CAAC,eAAe,CAAC,SAAS,6BAA6B,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GACxE,MAAM,CAAC,eAAe,CAAC,SAAS;IAAE,MAAM,EAAE,6BAA6B,CAAC,MAAM,CAAC,CAAC,CAAA;CAAE,GAAG,CAAC,GACtF,IAAI,CAAC;AAET,MAAM,MAAM,6BAA6B,CAAC,OAAO,IAAI,oBAAoB,GAAG;IAC1E,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,iBAAiB,CAAC,OAAO,IAAI,WAAW,GAAG;IACrD,OAAO,EAAE,KAAK,CAAC,sBAAsB,CAAC,OAAO,CAAC,CAAC,CAAC;IAChD,aAAa,EAAE,OAAO,GAAG,IAAI,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,sBAAsB,CAAC,OAAO,IACtC,CAAC,aAAa,GAAG;IAAE,aAAa,EAAE,OAAO,GAAG,IAAI,CAAA;CAAE,CAAC,GACnD,OAAO,CAAC,gBAAgB,EAAE,aAAa,CAAC,CAAC;AAS7C,wBAAgB,qBAAqB,CAAC,MAAM,SAAS,gCAAgC,GAAG,IAAI,EAC1F,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GACvB,iBAAiB,CAAC,kCAAkC,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,CA6B5E;AAED,wBAAgB,gBAAgB,CAAC,MAAM,SAAS,gCAAgC,EAC9E,OAAO,EAAE,WAAW,EACpB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GACvB,iBAAiB,CAAC,kCAAkC,CAAC,MAAM,CAAC,CAAC,CAkC/D"}
+79
View File
@@ -0,0 +1,79 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.maybeParseBetaMessage = maybeParseBetaMessage;
exports.parseBetaMessage = parseBetaMessage;
const error_1 = require("../core/error.js");
function getOutputFormat(params) {
// Prefer output_format (deprecated) over output_config.format for backward compatibility
return params?.output_format ?? params?.output_config?.format;
}
function maybeParseBetaMessage(message, params, opts) {
const outputFormat = getOutputFormat(params);
if (!params || !('parse' in (outputFormat ?? {}))) {
return {
...message,
content: message.content.map((block) => {
if (block.type === 'text') {
const parsedBlock = Object.defineProperty({ ...block }, 'parsed_output', {
value: null,
enumerable: false,
});
return Object.defineProperty(parsedBlock, 'parsed', {
get() {
opts.logger.warn('The `parsed` property on `text` blocks is deprecated, please use `parsed_output` instead.');
return null;
},
enumerable: false,
});
}
return block;
}),
parsed_output: null,
};
}
return parseBetaMessage(message, params, opts);
}
function parseBetaMessage(message, params, opts) {
let firstParsedOutput = null;
const content = message.content.map((block) => {
if (block.type === 'text') {
const parsedOutput = parseBetaOutputFormat(params, block.text);
if (firstParsedOutput === null) {
firstParsedOutput = parsedOutput;
}
const parsedBlock = Object.defineProperty({ ...block }, 'parsed_output', {
value: parsedOutput,
enumerable: false,
});
return Object.defineProperty(parsedBlock, 'parsed', {
get() {
opts.logger.warn('The `parsed` property on `text` blocks is deprecated, please use `parsed_output` instead.');
return parsedOutput;
},
enumerable: false,
});
}
return block;
});
return {
...message,
content,
parsed_output: firstParsedOutput,
};
}
function parseBetaOutputFormat(params, content) {
const outputFormat = getOutputFormat(params);
if (outputFormat?.type !== 'json_schema') {
return null;
}
try {
if ('parse' in outputFormat) {
return outputFormat.parse(content);
}
return JSON.parse(content);
}
catch (error) {
throw new error_1.AnthropicError(`Failed to parse structured output: ${error}`);
}
}
//# sourceMappingURL=beta-parser.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"beta-parser.js","sourceRoot":"","sources":["../src/lib/beta-parser.ts"],"names":[],"mappings":"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"}
+75
View File
@@ -0,0 +1,75 @@
import { AnthropicError } from "../core/error.mjs";
function getOutputFormat(params) {
// Prefer output_format (deprecated) over output_config.format for backward compatibility
return params?.output_format ?? params?.output_config?.format;
}
export function maybeParseBetaMessage(message, params, opts) {
const outputFormat = getOutputFormat(params);
if (!params || !('parse' in (outputFormat ?? {}))) {
return {
...message,
content: message.content.map((block) => {
if (block.type === 'text') {
const parsedBlock = Object.defineProperty({ ...block }, 'parsed_output', {
value: null,
enumerable: false,
});
return Object.defineProperty(parsedBlock, 'parsed', {
get() {
opts.logger.warn('The `parsed` property on `text` blocks is deprecated, please use `parsed_output` instead.');
return null;
},
enumerable: false,
});
}
return block;
}),
parsed_output: null,
};
}
return parseBetaMessage(message, params, opts);
}
export function parseBetaMessage(message, params, opts) {
let firstParsedOutput = null;
const content = message.content.map((block) => {
if (block.type === 'text') {
const parsedOutput = parseBetaOutputFormat(params, block.text);
if (firstParsedOutput === null) {
firstParsedOutput = parsedOutput;
}
const parsedBlock = Object.defineProperty({ ...block }, 'parsed_output', {
value: parsedOutput,
enumerable: false,
});
return Object.defineProperty(parsedBlock, 'parsed', {
get() {
opts.logger.warn('The `parsed` property on `text` blocks is deprecated, please use `parsed_output` instead.');
return parsedOutput;
},
enumerable: false,
});
}
return block;
});
return {
...message,
content,
parsed_output: firstParsedOutput,
};
}
function parseBetaOutputFormat(params, content) {
const outputFormat = getOutputFormat(params);
if (outputFormat?.type !== 'json_schema') {
return null;
}
try {
if ('parse' in outputFormat) {
return outputFormat.parse(content);
}
return JSON.parse(content);
}
catch (error) {
throw new AnthropicError(`Failed to parse structured output: ${error}`);
}
}
//# sourceMappingURL=beta-parser.mjs.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"beta-parser.mjs","sourceRoot":"","sources":["../src/lib/beta-parser.ts"],"names":[],"mappings":"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"}
+4
View File
@@ -0,0 +1,4 @@
export { type AccessToken, type AccessTokenProvider } from "./credentials/types.mjs";
export { type AnthropicConfig, type AuthenticationInfo, loadConfig } from "../core/credentials.mjs";
export { resolveCredentialsFromConfig } from "./credentials/credential-chain.mjs";
//# sourceMappingURL=credentials.d.mts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"credentials.d.mts","sourceRoot":"","sources":["../src/lib/credentials.ts"],"names":[],"mappings":"OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,mBAAmB,EAAE;OAC9C,EAAE,KAAK,eAAe,EAAE,KAAK,kBAAkB,EAAE,UAAU,EAAE;OAC7D,EAAE,4BAA4B,EAAE"}
+4
View File
@@ -0,0 +1,4 @@
export { type AccessToken, type AccessTokenProvider } from "./credentials/types.js";
export { type AnthropicConfig, type AuthenticationInfo, loadConfig } from "../core/credentials.js";
export { resolveCredentialsFromConfig } from "./credentials/credential-chain.js";
//# sourceMappingURL=credentials.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"credentials.d.ts","sourceRoot":"","sources":["../src/lib/credentials.ts"],"names":[],"mappings":"OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,mBAAmB,EAAE;OAC9C,EAAE,KAAK,eAAe,EAAE,KAAK,kBAAkB,EAAE,UAAU,EAAE;OAC7D,EAAE,4BAA4B,EAAE"}
+8
View File
@@ -0,0 +1,8 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveCredentialsFromConfig = exports.loadConfig = void 0;
var credentials_1 = require("../core/credentials.js");
Object.defineProperty(exports, "loadConfig", { enumerable: true, get: function () { return credentials_1.loadConfig; } });
var credential_chain_1 = require("./credentials/credential-chain.js");
Object.defineProperty(exports, "resolveCredentialsFromConfig", { enumerable: true, get: function () { return credential_chain_1.resolveCredentialsFromConfig; } });
//# sourceMappingURL=credentials.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"credentials.js","sourceRoot":"","sources":["../src/lib/credentials.ts"],"names":[],"mappings":";;;AACA,sDAAgG;AAAxC,yGAAA,UAAU,OAAA;AAClE,sEAA8E;AAArE,gIAAA,4BAA4B,OAAA"}
+3
View File
@@ -0,0 +1,3 @@
export { loadConfig } from "../core/credentials.mjs";
export { resolveCredentialsFromConfig } from "./credentials/credential-chain.mjs";
//# sourceMappingURL=credentials.mjs.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"credentials.mjs","sourceRoot":"","sources":["../src/lib/credentials.ts"],"names":[],"mappings":"OACO,EAAiD,UAAU,EAAE;OAC7D,EAAE,4BAA4B,EAAE"}
+39
View File
@@ -0,0 +1,39 @@
import type { Fetch } from "../../internal/builtin-types.mjs";
import { type AnthropicConfig } from "../../core/credentials.mjs";
import type { CredentialResult } from "./types.mjs";
/**
* Builds a {@link CredentialResult} from an explicit {@link AnthropicConfig}.
*
* Use this when constructing a client from an in-memory config object rather
* than from profile files or environment variables.
*
* For `oidc_federation`, `authentication.credentials_path` is optional —
* if omitted, every call performs a fresh exchange with no on-disk cache.
* For `user_oauth`, `authentication.credentials_path` is required (it is
* where the access/refresh tokens live).
*/
export type ResolverOptions = {
baseURL: string;
fetch: Fetch;
userAgent?: string | undefined;
onCacheWriteError?: ((err: unknown) => void) | undefined;
onSafetyWarning?: ((msg: string) => void) | undefined;
};
export declare function resolveCredentialsFromConfig(config: AnthropicConfig, options: ResolverOptions): CredentialResult;
/**
* Resolves a {@link CredentialResult} from the environment. Returns `null`
* when no credentials can be resolved.
*
* Resolution order:
*
* 1. Config file for the active profile (or the explicit `profile` argument)
* → dispatch on `authentication.type` (`oidc_federation`, `user_oauth`)
* 2. Environment variables `ANTHROPIC_FEDERATION_RULE_ID` +
* `ANTHROPIC_ORGANIZATION_ID` (+ identity token) → OIDC federation
* 3. Nothing matches → `null`
*
* Passing `profile` selects `<config_dir>/configs/<profile>.json` directly,
* skipping `ANTHROPIC_PROFILE` / `active_config` resolution.
*/
export declare function defaultCredentials(options: ResolverOptions, profile?: string): Promise<CredentialResult | null>;
//# sourceMappingURL=credential-chain.d.mts.map
@@ -0,0 +1 @@
{"version":3,"file":"credential-chain.d.mts","sourceRoot":"","sources":["../../src/lib/credentials/credential-chain.ts"],"names":[],"mappings":"OAAO,KAAK,EAAE,KAAK,EAAE;OAEd,EAIL,KAAK,eAAe,EACrB;OACM,KAAK,EAAuB,gBAAgB,EAAyB;AAY5E;;;;;;;;;;GAUG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,iBAAiB,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACzD,eAAe,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CACvD,CAAC;AAEF,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,eAAe,EACvB,OAAO,EAAE,eAAe,GACvB,gBAAgB,CAkBlB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,eAAe,EACxB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CA6BlC"}
+39
View File
@@ -0,0 +1,39 @@
import type { Fetch } from "../../internal/builtin-types.js";
import { type AnthropicConfig } from "../../core/credentials.js";
import type { CredentialResult } from "./types.js";
/**
* Builds a {@link CredentialResult} from an explicit {@link AnthropicConfig}.
*
* Use this when constructing a client from an in-memory config object rather
* than from profile files or environment variables.
*
* For `oidc_federation`, `authentication.credentials_path` is optional —
* if omitted, every call performs a fresh exchange with no on-disk cache.
* For `user_oauth`, `authentication.credentials_path` is required (it is
* where the access/refresh tokens live).
*/
export type ResolverOptions = {
baseURL: string;
fetch: Fetch;
userAgent?: string | undefined;
onCacheWriteError?: ((err: unknown) => void) | undefined;
onSafetyWarning?: ((msg: string) => void) | undefined;
};
export declare function resolveCredentialsFromConfig(config: AnthropicConfig, options: ResolverOptions): CredentialResult;
/**
* Resolves a {@link CredentialResult} from the environment. Returns `null`
* when no credentials can be resolved.
*
* Resolution order:
*
* 1. Config file for the active profile (or the explicit `profile` argument)
* → dispatch on `authentication.type` (`oidc_federation`, `user_oauth`)
* 2. Environment variables `ANTHROPIC_FEDERATION_RULE_ID` +
* `ANTHROPIC_ORGANIZATION_ID` (+ identity token) → OIDC federation
* 3. Nothing matches → `null`
*
* Passing `profile` selects `<config_dir>/configs/<profile>.json` directly,
* skipping `ANTHROPIC_PROFILE` / `active_config` resolution.
*/
export declare function defaultCredentials(options: ResolverOptions, profile?: string): Promise<CredentialResult | null>;
//# sourceMappingURL=credential-chain.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"credential-chain.d.ts","sourceRoot":"","sources":["../../src/lib/credentials/credential-chain.ts"],"names":[],"mappings":"OAAO,KAAK,EAAE,KAAK,EAAE;OAEd,EAIL,KAAK,eAAe,EACrB;OACM,KAAK,EAAuB,gBAAgB,EAAyB;AAY5E;;;;;;;;;;GAUG;AACH,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,iBAAiB,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;IACzD,eAAe,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CACvD,CAAC;AAEF,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,eAAe,EACvB,OAAO,EAAE,eAAe,GACvB,gBAAgB,CAkBlB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,EAAE,eAAe,EACxB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC,CA6BlC"}
+245
View File
@@ -0,0 +1,245 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveCredentialsFromConfig = resolveCredentialsFromConfig;
exports.defaultCredentials = defaultCredentials;
const env_1 = require("../../internal/utils/env.js");
const credentials_1 = require("../../core/credentials.js");
const types_1 = require("./types.js");
const time_1 = require("../../internal/utils/time.js");
const identity_token_1 = require("./identity-token.js");
const oidc_federation_1 = require("./oidc-federation.js");
const user_oauth_1 = require("./user-oauth.js");
function resolveCredentialsFromConfig(config, options) {
const credentialsPath = config.authentication.credentials_path ?? null;
const effectiveBaseURL = (config.base_url || options.baseURL).replace(/\/+$/, '');
const provider = buildProvider(config, credentialsPath, effectiveBaseURL, options);
const extraHeaders = {};
// For federation profiles workspace_id is sent in the jwt-bearer exchange
// body, not as a request header (the minted token is already
// workspace-scoped, so the header would be ignored).
if (config.workspace_id && config.authentication.type === 'user_oauth') {
extraHeaders['anthropic-workspace-id'] = config.workspace_id;
}
// Surface the profile's own base_url (not the options.baseURL fallback) so
// the client can adopt it for outbound API requests when the caller didn't
// pin one explicitly. Echoing options.baseURL back would defeat precedence.
return { provider, extraHeaders, baseURL: config.base_url || undefined };
}
/**
* Resolves a {@link CredentialResult} from the environment. Returns `null`
* when no credentials can be resolved.
*
* Resolution order:
*
* 1. Config file for the active profile (or the explicit `profile` argument)
* → dispatch on `authentication.type` (`oidc_federation`, `user_oauth`)
* 2. Environment variables `ANTHROPIC_FEDERATION_RULE_ID` +
* `ANTHROPIC_ORGANIZATION_ID` (+ identity token) → OIDC federation
* 3. Nothing matches → `null`
*
* Passing `profile` selects `<config_dir>/configs/<profile>.json` directly,
* skipping `ANTHROPIC_PROFILE` / `active_config` resolution.
*/
async function defaultCredentials(options, profile) {
const loaded = await (0, credentials_1.loadConfigWithSource)(profile);
if (!loaded) {
return null;
}
const { config, fromFile } = loaded;
// For file-loaded configs, default credentials_path to the per-profile
// location so user_oauth and federation caching work. Shallow-clone first
// so callers that retain a reference to the loaded config don't observe the
// patched-in default.
//
// Env-only credentials (no profile file on disk) skip the disk cache —
// matching the other SDKs. A disk cache keyed by profile path would
// re-serve a stale token after a change to ANTHROPIC_WORKSPACE_ID (or
// ANTHROPIC_ORGANIZATION_ID / ANTHROPIC_FEDERATION_RULE_ID) until the
// cached token expired, so the env-only chain stays in-memory only.
const withPath = config.authentication.credentials_path || !fromFile ?
config
: {
...config,
authentication: {
...config.authentication,
credentials_path: (await (0, credentials_1.getCredentialsPath)(config, profile)) ?? undefined,
},
};
return resolveCredentialsFromConfig(withPath, options);
}
function buildProvider(config, credentialsPath, baseURL, options) {
switch (config.authentication.type) {
case 'oidc_federation': {
const auth = config.authentication;
const identityProvider = resolveIdentityTokenProvider(auth);
if (!identityProvider) {
throw new types_1.WorkloadIdentityError('oidc_federation config requires an identity token (set authentication.identity_token, ' +
'ANTHROPIC_IDENTITY_TOKEN_FILE, or ANTHROPIC_IDENTITY_TOKEN)');
}
if (!auth.federation_rule_id) {
throw new types_1.WorkloadIdentityError("oidc_federation config requires 'federation_rule_id'. Set it in authentication.federation_rule_id in your profile, or via ANTHROPIC_FEDERATION_RULE_ID (profile takes precedence).");
}
if (!config.organization_id) {
throw new types_1.WorkloadIdentityError('oidc_federation config requires organization_id (set ANTHROPIC_ORGANIZATION_ID or config.organization_id)');
}
const exchange = (0, oidc_federation_1.oidcFederationProvider)({
identityTokenProvider: identityProvider,
federationRuleId: auth.federation_rule_id,
organizationId: config.organization_id,
serviceAccountId: auth.service_account_id,
workspaceId: config.workspace_id,
baseURL,
fetch: options.fetch,
userAgent: options.userAgent,
});
// If there's a credentials file path, wrap the exchange with file caching
// (check file for fresh token before exchanging, write back after).
if (credentialsPath) {
return cachedExchangeProvider(exchange, credentialsPath, options.onCacheWriteError, options.onSafetyWarning);
}
return exchange;
}
case 'user_oauth': {
if (!credentialsPath) {
throw new types_1.WorkloadIdentityError('user_oauth config requires authentication.credentials_path ' +
'(or load via a profile so it defaults to <config_dir>/credentials/<profile>.json)');
}
return (0, user_oauth_1.userOAuthProvider)({
credentialsPath,
clientId: config.authentication.client_id,
baseURL,
fetch: options.fetch,
userAgent: options.userAgent,
onSafetyWarning: options.onSafetyWarning,
});
}
default: {
const t = config.authentication.type;
throw new types_1.WorkloadIdentityError(`authentication.type "${t}" is not a known authentication type`);
}
}
}
/**
* Resolves the identity token provider from config fields or environment variables.
*
* Resolution order:
* 1. `identity_token.path` from the config (source: "file")
* 2. `ANTHROPIC_IDENTITY_TOKEN_FILE` env var
* 3. `ANTHROPIC_IDENTITY_TOKEN` env var (static value)
*/
function resolveIdentityTokenProvider(auth) {
if (auth.identity_token) {
// Cast needed to stringify an unknown source value for the error message:
// the on-disk JSON may contain a source this SDK version doesn't know about.
const source = auth.identity_token.source;
if (source !== 'file') {
throw new types_1.WorkloadIdentityError(`identity_token.source "${source}" is not supported by this SDK version (only "file")`);
}
if (!auth.identity_token.path) {
throw new types_1.WorkloadIdentityError(`identity_token.source "file" requires a non-empty path`);
}
return (0, identity_token_1.identityTokenFromFile)(auth.identity_token.path);
}
const tokenFile = (0, env_1.readEnv)('ANTHROPIC_IDENTITY_TOKEN_FILE');
if (tokenFile) {
return (0, identity_token_1.identityTokenFromFile)(tokenFile);
}
const tokenValue = (0, env_1.readEnv)('ANTHROPIC_IDENTITY_TOKEN');
if (tokenValue) {
return (0, identity_token_1.identityTokenFromValue)(tokenValue);
}
return null;
}
/**
* Wraps a federation exchange provider with credential file caching.
* Checks the file for a fresh token before exchanging, and writes the
* result back after a successful exchange (best-effort, atomic replace).
*
* Note: this is not cross-process serialized — two SDK instances that
* miss the cache simultaneously will both perform a full exchange and
* the last writer wins. That is acceptable: federation exchanges are
* idempotent and the cache is an optimization, not a correctness gate.
*/
function cachedExchangeProvider(exchange, credentialsPath, onCacheWriteError, onSafetyWarning) {
return async (opts) => {
const fs = await Promise.resolve().then(() => __importStar(require('node:fs')));
await (0, types_1.checkCredentialsFileSafety)(credentialsPath, onSafetyWarning);
// Try cached credentials file
let existing;
try {
const raw = await fs.promises.readFile(credentialsPath, 'utf-8');
existing = JSON.parse(raw);
const token = existing?.['access_token'];
if (token && !opts?.forceRefresh) {
const expiresAt = existing?.['expires_at'];
if (expiresAt == null || (0, time_1.nowAsSeconds)() < expiresAt - types_1.MANDATORY_REFRESH_THRESHOLD_IN_SECONDS) {
return { token, expiresAt: expiresAt ?? null };
}
}
}
catch (err) {
// ENOENT or invalid-JSON → no usable cache, exchange fresh. Other
// errors (EACCES, EISDIR, …) indicate a broken cache path; surface to
// the optional hook so they're at least debuggable, then proceed.
const code = err?.code;
if (code !== 'ENOENT' && !(err instanceof SyntaxError)) {
onCacheWriteError?.(err);
}
}
// Exchange for a new token
const result = await exchange(opts);
// Write cache back (best-effort). Preserve any unknown keys from the
// existing file (notably refresh_token, in the unlikely case this path
// is shared with a user_oauth profile) so the federation cache writer
// doesn't clobber material it didn't own.
try {
await (0, types_1.writeCredentialsFileAtomic)(credentialsPath, {
...(existing ?? {}),
version: credentials_1.CREDENTIALS_FILE_VERSION,
type: 'oauth_token',
access_token: result.token,
expires_at: result.expiresAt,
});
}
catch (err) {
// Best-effort caching: surface to the optional hook but never fail
// the exchange itself.
onCacheWriteError?.(err);
}
return result;
};
}
//# sourceMappingURL=credential-chain.js.map
File diff suppressed because one or more lines are too long
+208
View File
@@ -0,0 +1,208 @@
import { readEnv } from "../../internal/utils/env.mjs";
import { CREDENTIALS_FILE_VERSION, loadConfigWithSource, getCredentialsPath, } from "../../core/credentials.mjs";
import { MANDATORY_REFRESH_THRESHOLD_IN_SECONDS, WorkloadIdentityError, checkCredentialsFileSafety, writeCredentialsFileAtomic, } from "./types.mjs";
import { nowAsSeconds } from "../../internal/utils/time.mjs";
import { identityTokenFromFile, identityTokenFromValue } from "./identity-token.mjs";
import { oidcFederationProvider } from "./oidc-federation.mjs";
import { userOAuthProvider } from "./user-oauth.mjs";
export function resolveCredentialsFromConfig(config, options) {
const credentialsPath = config.authentication.credentials_path ?? null;
const effectiveBaseURL = (config.base_url || options.baseURL).replace(/\/+$/, '');
const provider = buildProvider(config, credentialsPath, effectiveBaseURL, options);
const extraHeaders = {};
// For federation profiles workspace_id is sent in the jwt-bearer exchange
// body, not as a request header (the minted token is already
// workspace-scoped, so the header would be ignored).
if (config.workspace_id && config.authentication.type === 'user_oauth') {
extraHeaders['anthropic-workspace-id'] = config.workspace_id;
}
// Surface the profile's own base_url (not the options.baseURL fallback) so
// the client can adopt it for outbound API requests when the caller didn't
// pin one explicitly. Echoing options.baseURL back would defeat precedence.
return { provider, extraHeaders, baseURL: config.base_url || undefined };
}
/**
* Resolves a {@link CredentialResult} from the environment. Returns `null`
* when no credentials can be resolved.
*
* Resolution order:
*
* 1. Config file for the active profile (or the explicit `profile` argument)
* → dispatch on `authentication.type` (`oidc_federation`, `user_oauth`)
* 2. Environment variables `ANTHROPIC_FEDERATION_RULE_ID` +
* `ANTHROPIC_ORGANIZATION_ID` (+ identity token) → OIDC federation
* 3. Nothing matches → `null`
*
* Passing `profile` selects `<config_dir>/configs/<profile>.json` directly,
* skipping `ANTHROPIC_PROFILE` / `active_config` resolution.
*/
export async function defaultCredentials(options, profile) {
const loaded = await loadConfigWithSource(profile);
if (!loaded) {
return null;
}
const { config, fromFile } = loaded;
// For file-loaded configs, default credentials_path to the per-profile
// location so user_oauth and federation caching work. Shallow-clone first
// so callers that retain a reference to the loaded config don't observe the
// patched-in default.
//
// Env-only credentials (no profile file on disk) skip the disk cache —
// matching the other SDKs. A disk cache keyed by profile path would
// re-serve a stale token after a change to ANTHROPIC_WORKSPACE_ID (or
// ANTHROPIC_ORGANIZATION_ID / ANTHROPIC_FEDERATION_RULE_ID) until the
// cached token expired, so the env-only chain stays in-memory only.
const withPath = config.authentication.credentials_path || !fromFile ?
config
: {
...config,
authentication: {
...config.authentication,
credentials_path: (await getCredentialsPath(config, profile)) ?? undefined,
},
};
return resolveCredentialsFromConfig(withPath, options);
}
function buildProvider(config, credentialsPath, baseURL, options) {
switch (config.authentication.type) {
case 'oidc_federation': {
const auth = config.authentication;
const identityProvider = resolveIdentityTokenProvider(auth);
if (!identityProvider) {
throw new WorkloadIdentityError('oidc_federation config requires an identity token (set authentication.identity_token, ' +
'ANTHROPIC_IDENTITY_TOKEN_FILE, or ANTHROPIC_IDENTITY_TOKEN)');
}
if (!auth.federation_rule_id) {
throw new WorkloadIdentityError("oidc_federation config requires 'federation_rule_id'. Set it in authentication.federation_rule_id in your profile, or via ANTHROPIC_FEDERATION_RULE_ID (profile takes precedence).");
}
if (!config.organization_id) {
throw new WorkloadIdentityError('oidc_federation config requires organization_id (set ANTHROPIC_ORGANIZATION_ID or config.organization_id)');
}
const exchange = oidcFederationProvider({
identityTokenProvider: identityProvider,
federationRuleId: auth.federation_rule_id,
organizationId: config.organization_id,
serviceAccountId: auth.service_account_id,
workspaceId: config.workspace_id,
baseURL,
fetch: options.fetch,
userAgent: options.userAgent,
});
// If there's a credentials file path, wrap the exchange with file caching
// (check file for fresh token before exchanging, write back after).
if (credentialsPath) {
return cachedExchangeProvider(exchange, credentialsPath, options.onCacheWriteError, options.onSafetyWarning);
}
return exchange;
}
case 'user_oauth': {
if (!credentialsPath) {
throw new WorkloadIdentityError('user_oauth config requires authentication.credentials_path ' +
'(or load via a profile so it defaults to <config_dir>/credentials/<profile>.json)');
}
return userOAuthProvider({
credentialsPath,
clientId: config.authentication.client_id,
baseURL,
fetch: options.fetch,
userAgent: options.userAgent,
onSafetyWarning: options.onSafetyWarning,
});
}
default: {
const t = config.authentication.type;
throw new WorkloadIdentityError(`authentication.type "${t}" is not a known authentication type`);
}
}
}
/**
* Resolves the identity token provider from config fields or environment variables.
*
* Resolution order:
* 1. `identity_token.path` from the config (source: "file")
* 2. `ANTHROPIC_IDENTITY_TOKEN_FILE` env var
* 3. `ANTHROPIC_IDENTITY_TOKEN` env var (static value)
*/
function resolveIdentityTokenProvider(auth) {
if (auth.identity_token) {
// Cast needed to stringify an unknown source value for the error message:
// the on-disk JSON may contain a source this SDK version doesn't know about.
const source = auth.identity_token.source;
if (source !== 'file') {
throw new WorkloadIdentityError(`identity_token.source "${source}" is not supported by this SDK version (only "file")`);
}
if (!auth.identity_token.path) {
throw new WorkloadIdentityError(`identity_token.source "file" requires a non-empty path`);
}
return identityTokenFromFile(auth.identity_token.path);
}
const tokenFile = readEnv('ANTHROPIC_IDENTITY_TOKEN_FILE');
if (tokenFile) {
return identityTokenFromFile(tokenFile);
}
const tokenValue = readEnv('ANTHROPIC_IDENTITY_TOKEN');
if (tokenValue) {
return identityTokenFromValue(tokenValue);
}
return null;
}
/**
* Wraps a federation exchange provider with credential file caching.
* Checks the file for a fresh token before exchanging, and writes the
* result back after a successful exchange (best-effort, atomic replace).
*
* Note: this is not cross-process serialized — two SDK instances that
* miss the cache simultaneously will both perform a full exchange and
* the last writer wins. That is acceptable: federation exchanges are
* idempotent and the cache is an optimization, not a correctness gate.
*/
function cachedExchangeProvider(exchange, credentialsPath, onCacheWriteError, onSafetyWarning) {
return async (opts) => {
const fs = await import('node:fs');
await checkCredentialsFileSafety(credentialsPath, onSafetyWarning);
// Try cached credentials file
let existing;
try {
const raw = await fs.promises.readFile(credentialsPath, 'utf-8');
existing = JSON.parse(raw);
const token = existing?.['access_token'];
if (token && !opts?.forceRefresh) {
const expiresAt = existing?.['expires_at'];
if (expiresAt == null || nowAsSeconds() < expiresAt - MANDATORY_REFRESH_THRESHOLD_IN_SECONDS) {
return { token, expiresAt: expiresAt ?? null };
}
}
}
catch (err) {
// ENOENT or invalid-JSON → no usable cache, exchange fresh. Other
// errors (EACCES, EISDIR, …) indicate a broken cache path; surface to
// the optional hook so they're at least debuggable, then proceed.
const code = err?.code;
if (code !== 'ENOENT' && !(err instanceof SyntaxError)) {
onCacheWriteError?.(err);
}
}
// Exchange for a new token
const result = await exchange(opts);
// Write cache back (best-effort). Preserve any unknown keys from the
// existing file (notably refresh_token, in the unlikely case this path
// is shared with a user_oauth profile) so the federation cache writer
// doesn't clobber material it didn't own.
try {
await writeCredentialsFileAtomic(credentialsPath, {
...(existing ?? {}),
version: CREDENTIALS_FILE_VERSION,
type: 'oauth_token',
access_token: result.token,
expires_at: result.expiresAt,
});
}
catch (err) {
// Best-effort caching: surface to the optional hook but never fail
// the exchange itself.
onCacheWriteError?.(err);
}
return result;
};
}
//# sourceMappingURL=credential-chain.mjs.map
File diff suppressed because one or more lines are too long
+11
View File
@@ -0,0 +1,11 @@
import type { IdentityTokenProvider } from "./types.mjs";
/**
* Reads a JWT from a file on every call. Supports automatic rotation
* (e.g. Kubernetes projected service-account tokens).
*/
export declare function identityTokenFromFile(path: string): IdentityTokenProvider;
/**
* Wraps a static JWT string as an {@link IdentityTokenProvider}.
*/
export declare function identityTokenFromValue(token: string): IdentityTokenProvider;
//# sourceMappingURL=identity-token.d.mts.map
@@ -0,0 +1 @@
{"version":3,"file":"identity-token.d.mts","sourceRoot":"","sources":["../../src/lib/credentials/identity-token.ts"],"names":[],"mappings":"OACO,KAAK,EAAE,qBAAqB,EAAE;AAErC;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,qBAAqB,CAmBzE;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,qBAAqB,CAK3E"}
+11
View File
@@ -0,0 +1,11 @@
import type { IdentityTokenProvider } from "./types.js";
/**
* Reads a JWT from a file on every call. Supports automatic rotation
* (e.g. Kubernetes projected service-account tokens).
*/
export declare function identityTokenFromFile(path: string): IdentityTokenProvider;
/**
* Wraps a static JWT string as an {@link IdentityTokenProvider}.
*/
export declare function identityTokenFromValue(token: string): IdentityTokenProvider;
//# sourceMappingURL=identity-token.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"identity-token.d.ts","sourceRoot":"","sources":["../../src/lib/credentials/identity-token.ts"],"names":[],"mappings":"OACO,KAAK,EAAE,qBAAqB,EAAE;AAErC;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,qBAAqB,CAmBzE;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,qBAAqB,CAK3E"}
+72
View File
@@ -0,0 +1,72 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.identityTokenFromFile = identityTokenFromFile;
exports.identityTokenFromValue = identityTokenFromValue;
const error_1 = require("../../core/error.js");
/**
* Reads a JWT from a file on every call. Supports automatic rotation
* (e.g. Kubernetes projected service-account tokens).
*/
function identityTokenFromFile(path) {
if (!path) {
throw new error_1.AnthropicError('Identity token file path is empty');
}
return async () => {
const fs = await Promise.resolve().then(() => __importStar(require('node:fs')));
let content;
try {
content = await fs.promises.readFile(path, 'utf-8');
}
catch (err) {
throw new error_1.AnthropicError(`Failed to read identity token file at ${path}: ${err}`);
}
const token = content.trim();
if (!token) {
throw new error_1.AnthropicError(`Identity token file at ${path} is empty`);
}
return token;
};
}
/**
* Wraps a static JWT string as an {@link IdentityTokenProvider}.
*/
function identityTokenFromValue(token) {
if (!token) {
throw new error_1.AnthropicError('Identity token value is empty');
}
return () => token;
}
//# sourceMappingURL=identity-token.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"identity-token.js","sourceRoot":"","sources":["../../src/lib/credentials/identity-token.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAOA,sDAmBC;AAKD,wDAKC;AApCD,+CAAkD;AAGlD;;;GAGG;AACH,SAAgB,qBAAqB,CAAC,IAAY;IAChD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,sBAAc,CAAC,mCAAmC,CAAC,CAAC;IAChE,CAAC;IAED,OAAO,KAAK,IAAI,EAAE;QAChB,MAAM,EAAE,GAAG,wDAAa,SAAS,GAAC,CAAC;QACnC,IAAI,OAAe,CAAC;QACpB,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACtD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,sBAAc,CAAC,yCAAyC,IAAI,KAAK,GAAG,EAAE,CAAC,CAAC;QACpF,CAAC;QACD,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;QAC7B,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,sBAAc,CAAC,0BAA0B,IAAI,WAAW,CAAC,CAAC;QACtE,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,SAAgB,sBAAsB,CAAC,KAAa;IAClD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,sBAAc,CAAC,+BAA+B,CAAC,CAAC;IAC5D,CAAC;IACD,OAAO,GAAG,EAAE,CAAC,KAAK,CAAC;AACrB,CAAC"}
+35
View File
@@ -0,0 +1,35 @@
import { AnthropicError } from "../../core/error.mjs";
/**
* Reads a JWT from a file on every call. Supports automatic rotation
* (e.g. Kubernetes projected service-account tokens).
*/
export function identityTokenFromFile(path) {
if (!path) {
throw new AnthropicError('Identity token file path is empty');
}
return async () => {
const fs = await import('node:fs');
let content;
try {
content = await fs.promises.readFile(path, 'utf-8');
}
catch (err) {
throw new AnthropicError(`Failed to read identity token file at ${path}: ${err}`);
}
const token = content.trim();
if (!token) {
throw new AnthropicError(`Identity token file at ${path} is empty`);
}
return token;
};
}
/**
* Wraps a static JWT string as an {@link IdentityTokenProvider}.
*/
export function identityTokenFromValue(token) {
if (!token) {
throw new AnthropicError('Identity token value is empty');
}
return () => token;
}
//# sourceMappingURL=identity-token.mjs.map
@@ -0,0 +1 @@
{"version":3,"file":"identity-token.mjs","sourceRoot":"","sources":["../../src/lib/credentials/identity-token.ts"],"names":[],"mappings":"OAAO,EAAE,cAAc,EAAE;AAGzB;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAY;IAChD,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,cAAc,CAAC,mCAAmC,CAAC,CAAC;IAChE,CAAC;IAED,OAAO,KAAK,IAAI,EAAE;QAChB,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;QACnC,IAAI,OAAe,CAAC;QACpB,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACtD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,cAAc,CAAC,yCAAyC,IAAI,KAAK,GAAG,EAAE,CAAC,CAAC;QACpF,CAAC;QACD,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;QAC7B,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,MAAM,IAAI,cAAc,CAAC,0BAA0B,IAAI,WAAW,CAAC,CAAC;QACtE,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,sBAAsB,CAAC,KAAa;IAClD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,cAAc,CAAC,+BAA+B,CAAC,CAAC;IAC5D,CAAC;IACD,OAAO,GAAG,EAAE,CAAC,KAAK,CAAC;AACrB,CAAC"}
+40
View File
@@ -0,0 +1,40 @@
import type { Fetch } from "../../internal/builtin-types.mjs";
import type { AccessTokenProvider, IdentityTokenProvider } from "./types.mjs";
export type OIDCFederationConfig = {
identityTokenProvider: IdentityTokenProvider;
federationRuleId: string;
organizationId: string;
serviceAccountId?: string | undefined;
/**
* Optional `wrkspc_*` tagged ID, or the literal `"default"` to scope the
* token to the organization's default workspace. When omitted the server
* picks the rule's sole enabled workspace, else the org default if the rule
* covers it. Required when the rule enables more than one non-default
* workspace, or to target a specific workspace other than the one the
* server would pick. The minted token is workspace-scoped: per-request
* workspace selection (the `anthropic-workspace-id` header) is not supported
* for federation tokens — switching workspaces requires a new token exchange
* with a different `workspaceId`.
*/
workspaceId?: string | undefined;
baseURL: string;
fetch: Fetch;
/**
* Overrides the outgoing User-Agent header on the token exchange. When
* empty, sends an SDK-identified UA so the token endpoint's access logs
* identify the caller.
*/
userAgent?: string | undefined;
};
/**
* Exchanges an external OIDC JWT for an Anthropic access token via the
* RFC 7523 jwt-bearer grant.
*
* Each invocation performs a fresh token exchange. Wrap in a
* {@link TokenCache} to avoid exchanging on every request.
*
* Federation grants do not return a refresh token — callers re-exchange
* their assertion on expiry.
*/
export declare function oidcFederationProvider(config: OIDCFederationConfig): AccessTokenProvider;
//# sourceMappingURL=oidc-federation.d.mts.map
@@ -0,0 +1 @@
{"version":3,"file":"oidc-federation.d.mts","sourceRoot":"","sources":["../../src/lib/credentials/oidc-federation.ts"],"names":[],"mappings":"OAAO,KAAK,EAAE,KAAK,EAAE;OACd,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE;AAc1D,MAAM,MAAM,oBAAoB,GAAG;IACjC,qBAAqB,EAAE,qBAAqB,CAAC;IAC7C,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC;;;;;;;;;;OAUG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,KAAK,CAAC;IACb;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,oBAAoB,GAAG,mBAAmB,CAuFxF"}
+40
View File
@@ -0,0 +1,40 @@
import type { Fetch } from "../../internal/builtin-types.js";
import type { AccessTokenProvider, IdentityTokenProvider } from "./types.js";
export type OIDCFederationConfig = {
identityTokenProvider: IdentityTokenProvider;
federationRuleId: string;
organizationId: string;
serviceAccountId?: string | undefined;
/**
* Optional `wrkspc_*` tagged ID, or the literal `"default"` to scope the
* token to the organization's default workspace. When omitted the server
* picks the rule's sole enabled workspace, else the org default if the rule
* covers it. Required when the rule enables more than one non-default
* workspace, or to target a specific workspace other than the one the
* server would pick. The minted token is workspace-scoped: per-request
* workspace selection (the `anthropic-workspace-id` header) is not supported
* for federation tokens — switching workspaces requires a new token exchange
* with a different `workspaceId`.
*/
workspaceId?: string | undefined;
baseURL: string;
fetch: Fetch;
/**
* Overrides the outgoing User-Agent header on the token exchange. When
* empty, sends an SDK-identified UA so the token endpoint's access logs
* identify the caller.
*/
userAgent?: string | undefined;
};
/**
* Exchanges an external OIDC JWT for an Anthropic access token via the
* RFC 7523 jwt-bearer grant.
*
* Each invocation performs a fresh token exchange. Wrap in a
* {@link TokenCache} to avoid exchanging on every request.
*
* Federation grants do not return a refresh token — callers re-exchange
* their assertion on expiry.
*/
export declare function oidcFederationProvider(config: OIDCFederationConfig): AccessTokenProvider;
//# sourceMappingURL=oidc-federation.d.ts.map
@@ -0,0 +1 @@
{"version":3,"file":"oidc-federation.d.ts","sourceRoot":"","sources":["../../src/lib/credentials/oidc-federation.ts"],"names":[],"mappings":"OAAO,KAAK,EAAE,KAAK,EAAE;OACd,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE;AAc1D,MAAM,MAAM,oBAAoB,GAAG;IACjC,qBAAqB,EAAE,qBAAqB,CAAC;IAC7C,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC;;;;;;;;;;OAUG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,KAAK,CAAC;IACb;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAChC,CAAC;AAEF;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,oBAAoB,GAAG,mBAAmB,CAuFxF"}
+82
View File
@@ -0,0 +1,82 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.oidcFederationProvider = oidcFederationProvider;
const types_1 = require("./types.js");
const time_1 = require("../../internal/utils/time.js");
const version_1 = require("../../version.js");
/**
* Exchanges an external OIDC JWT for an Anthropic access token via the
* RFC 7523 jwt-bearer grant.
*
* Each invocation performs a fresh token exchange. Wrap in a
* {@link TokenCache} to avoid exchanging on every request.
*
* Federation grants do not return a refresh token — callers re-exchange
* their assertion on expiry.
*/
function oidcFederationProvider(config) {
return async () => {
(0, types_1.requireSecureTokenEndpoint)(config.baseURL);
const jwt = await config.identityTokenProvider();
// The token endpoint enforces a 16 KiB assertion limit; surface a clear
// client-side error so misconfigured projected-token sources are
// diagnosable without a server round-trip.
if (jwt.length > 16 * 1024) {
throw new types_1.WorkloadIdentityError(`Identity token is ${Math.ceil(jwt.length / 1024)} KiB, exceeds the 16 KiB assertion limit`);
}
const body = {
grant_type: types_1.GRANT_TYPE_JWT_BEARER,
assertion: jwt,
federation_rule_id: config.federationRuleId,
organization_id: config.organizationId,
};
if (config.serviceAccountId) {
body['service_account_id'] = config.serviceAccountId;
}
if (config.workspaceId) {
body['workspace_id'] = config.workspaceId;
}
const url = `${config.baseURL}${types_1.TOKEN_ENDPOINT}`;
let resp;
try {
resp = await config.fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'anthropic-beta': `${types_1.OAUTH_API_BETA_HEADER},${types_1.FEDERATION_BETA_HEADER}`,
'User-Agent': config.userAgent || `anthropic-sdk-typescript/${version_1.VERSION} oidcFederationProvider`,
},
body: JSON.stringify(body),
});
}
catch (err) {
throw new types_1.WorkloadIdentityError(`Failed to reach token endpoint ${url}: ${err}`);
}
const requestId = resp.headers.get('Request-Id');
if (!resp.ok) {
const text = await resp.text().catch(() => '');
const redacted = (0, types_1.redactSensitive)(text);
// A 401 is hard to debug from the status code alone, so surface
// guidance: check the federation rule, optionally set a workspace ID
// (the most common fix when no workspaceId is configured), and point at
// the Workload identity page in Claude Console for the server-side
// authentication event log. Other statuses (5xx, 400, ...) get no hint.
let hint = '';
if (resp.status === 401) {
const hintMiddle = config.workspaceId ? '' : ("If your federation rule is scoped to multiple workspaces, set the ANTHROPIC_WORKSPACE_ID environment variable, the 'workspace_id' config key, or the `workspaceId` option. ");
hint = ` Ensure your federation rule matches your identity token. ${hintMiddle}View your authentication events in the Workload identity page of Claude Console for more details.`;
}
throw new types_1.WorkloadIdentityError(`Token exchange failed with status ${resp.status}${requestId ? ` (request-id ${requestId})` : ''}: ${redacted}${hint}`, resp.status, redacted, requestId);
}
const data = await (0, types_1.parseTokenResponse)(resp, requestId);
const expiresIn = Number(data.expires_in);
if (!Number.isFinite(expiresIn)) {
throw new types_1.WorkloadIdentityError(`Token endpoint response missing required fields: ${JSON.stringify((0, types_1.redactSensitive)(data))}`, resp.status, (0, types_1.redactSensitive)(data), requestId);
}
return {
token: data.access_token,
expiresAt: (0, time_1.nowAsSeconds)() + expiresIn,
};
};
}
//# sourceMappingURL=oidc-federation.js.map
@@ -0,0 +1 @@
{"version":3,"file":"oidc-federation.js","sourceRoot":"","sources":["../../src/lib/credentials/oidc-federation.ts"],"names":[],"mappings":"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"}
+79
View File
@@ -0,0 +1,79 @@
import { FEDERATION_BETA_HEADER, GRANT_TYPE_JWT_BEARER, OAUTH_API_BETA_HEADER, TOKEN_ENDPOINT, WorkloadIdentityError, parseTokenResponse, redactSensitive, requireSecureTokenEndpoint, } from "./types.mjs";
import { nowAsSeconds } from "../../internal/utils/time.mjs";
import { VERSION } from "../../version.mjs";
/**
* Exchanges an external OIDC JWT for an Anthropic access token via the
* RFC 7523 jwt-bearer grant.
*
* Each invocation performs a fresh token exchange. Wrap in a
* {@link TokenCache} to avoid exchanging on every request.
*
* Federation grants do not return a refresh token — callers re-exchange
* their assertion on expiry.
*/
export function oidcFederationProvider(config) {
return async () => {
requireSecureTokenEndpoint(config.baseURL);
const jwt = await config.identityTokenProvider();
// The token endpoint enforces a 16 KiB assertion limit; surface a clear
// client-side error so misconfigured projected-token sources are
// diagnosable without a server round-trip.
if (jwt.length > 16 * 1024) {
throw new WorkloadIdentityError(`Identity token is ${Math.ceil(jwt.length / 1024)} KiB, exceeds the 16 KiB assertion limit`);
}
const body = {
grant_type: GRANT_TYPE_JWT_BEARER,
assertion: jwt,
federation_rule_id: config.federationRuleId,
organization_id: config.organizationId,
};
if (config.serviceAccountId) {
body['service_account_id'] = config.serviceAccountId;
}
if (config.workspaceId) {
body['workspace_id'] = config.workspaceId;
}
const url = `${config.baseURL}${TOKEN_ENDPOINT}`;
let resp;
try {
resp = await config.fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'anthropic-beta': `${OAUTH_API_BETA_HEADER},${FEDERATION_BETA_HEADER}`,
'User-Agent': config.userAgent || `anthropic-sdk-typescript/${VERSION} oidcFederationProvider`,
},
body: JSON.stringify(body),
});
}
catch (err) {
throw new WorkloadIdentityError(`Failed to reach token endpoint ${url}: ${err}`);
}
const requestId = resp.headers.get('Request-Id');
if (!resp.ok) {
const text = await resp.text().catch(() => '');
const redacted = redactSensitive(text);
// A 401 is hard to debug from the status code alone, so surface
// guidance: check the federation rule, optionally set a workspace ID
// (the most common fix when no workspaceId is configured), and point at
// the Workload identity page in Claude Console for the server-side
// authentication event log. Other statuses (5xx, 400, ...) get no hint.
let hint = '';
if (resp.status === 401) {
const hintMiddle = config.workspaceId ? '' : ("If your federation rule is scoped to multiple workspaces, set the ANTHROPIC_WORKSPACE_ID environment variable, the 'workspace_id' config key, or the `workspaceId` option. ");
hint = ` Ensure your federation rule matches your identity token. ${hintMiddle}View your authentication events in the Workload identity page of Claude Console for more details.`;
}
throw new WorkloadIdentityError(`Token exchange failed with status ${resp.status}${requestId ? ` (request-id ${requestId})` : ''}: ${redacted}${hint}`, resp.status, redacted, requestId);
}
const data = await parseTokenResponse(resp, requestId);
const expiresIn = Number(data.expires_in);
if (!Number.isFinite(expiresIn)) {
throw new WorkloadIdentityError(`Token endpoint response missing required fields: ${JSON.stringify(redactSensitive(data))}`, resp.status, redactSensitive(data), requestId);
}
return {
token: data.access_token,
expiresAt: nowAsSeconds() + expiresIn,
};
};
}
//# sourceMappingURL=oidc-federation.mjs.map
@@ -0,0 +1 @@
{"version":3,"file":"oidc-federation.mjs","sourceRoot":"","sources":["../../src/lib/credentials/oidc-federation.ts"],"names":[],"mappings":"OAEO,EACL,sBAAsB,EACtB,qBAAqB,EACrB,qBAAqB,EACrB,cAAc,EACd,qBAAqB,EACrB,kBAAkB,EAClB,eAAe,EACf,0BAA0B,GAC3B;OACM,EAAE,YAAY,EAAE;OAChB,EAAE,OAAO,EAAE;AA6BlB;;;;;;;;;GASG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAA4B;IACjE,OAAO,KAAK,IAAI,EAAE;QAChB,0BAA0B,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAE3C,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,qBAAqB,EAAE,CAAC;QACjD,wEAAwE;QACxE,iEAAiE;QACjE,2CAA2C;QAC3C,IAAI,GAAG,CAAC,MAAM,GAAG,EAAE,GAAG,IAAI,EAAE,CAAC;YAC3B,MAAM,IAAI,qBAAqB,CAC7B,qBAAqB,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC,0CAA0C,CAC5F,CAAC;QACJ,CAAC;QAED,MAAM,IAAI,GAA2B;YACnC,UAAU,EAAE,qBAAqB;YACjC,SAAS,EAAE,GAAG;YACd,kBAAkB,EAAE,MAAM,CAAC,gBAAgB;YAC3C,eAAe,EAAE,MAAM,CAAC,cAAc;SACvC,CAAC;QACF,IAAI,MAAM,CAAC,gBAAgB,EAAE,CAAC;YAC5B,IAAI,CAAC,oBAAoB,CAAC,GAAG,MAAM,CAAC,gBAAgB,CAAC;QACvD,CAAC;QACD,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;YACvB,IAAI,CAAC,cAAc,CAAC,GAAG,MAAM,CAAC,WAAW,CAAC;QAC5C,CAAC;QAED,MAAM,GAAG,GAAG,GAAG,MAAM,CAAC,OAAO,GAAG,cAAc,EAAE,CAAC;QACjD,IAAI,IAAc,CAAC;QACnB,IAAI,CAAC;YACH,IAAI,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE;gBAC7B,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,cAAc,EAAE,kBAAkB;oBAClC,gBAAgB,EAAE,GAAG,qBAAqB,IAAI,sBAAsB,EAAE;oBACtE,YAAY,EAAE,MAAM,CAAC,SAAS,IAAI,4BAA4B,OAAO,yBAAyB;iBAC/F;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;aAC3B,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,IAAI,qBAAqB,CAAC,kCAAkC,GAAG,KAAK,GAAG,EAAE,CAAC,CAAC;QACnF,CAAC;QAED,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QAEjD,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;YACb,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;YAC/C,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC;YACvC,gEAAgE;YAChE,qEAAqE;YACrE,wEAAwE;YACxE,mEAAmE;YACnE,wEAAwE;YACxE,IAAI,IAAI,GAAG,EAAE,CAAC;YACd,IAAI,IAAI,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBACxB,MAAM,UAAU,GACd,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CACxB,6KAA6K,CAC9K,CAAC;gBACJ,IAAI,GAAG,6DAA6D,UAAU,mGAAmG,CAAC;YACpL,CAAC;YACD,MAAM,IAAI,qBAAqB,CAC7B,qCAAqC,IAAI,CAAC,MAAM,GAC9C,SAAS,CAAC,CAAC,CAAC,gBAAgB,SAAS,GAAG,CAAC,CAAC,CAAC,EAC7C,KAAK,QAAQ,GAAG,IAAI,EAAE,EACtB,IAAI,CAAC,MAAM,EACX,QAAQ,EACR,SAAS,CACV,CAAC;QACJ,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,kBAAkB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QACvD,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC1C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;YAChC,MAAM,IAAI,qBAAqB,CAC7B,oDAAoD,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,EAAE,EAC3F,IAAI,CAAC,MAAM,EACX,eAAe,CAAC,IAAI,CAAC,EACrB,SAAS,CACV,CAAC;QACJ,CAAC;QAED,OAAO;YACL,KAAK,EAAE,IAAI,CAAC,YAAY;YACxB,SAAS,EAAE,YAAY,EAAE,GAAG,SAAS;SACtC,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
+54
View File
@@ -0,0 +1,54 @@
import type { AccessTokenProvider } from "./types.mjs";
/**
* Wraps an {@link AccessTokenProvider} with two-tier proactive refresh
* and concurrent deduplication.
*
* Refresh policy on each {@link getToken} call:
*
* - No cached token → call provider (blocking), cache, return.
* - Cached with `expiresAt == null` → return cached forever.
* - More than 120s remaining → return cached.
* - 30–120s remaining (advisory window) → return stale token immediately,
* kick off background refresh. On failure, log and keep stale.
* - Less than 30s remaining or expired (mandatory) → block and refresh.
* On failure, throw.
*
* Concurrent mandatory callers coalesce into a single provider call.
*/
export declare class TokenCache {
private provider;
private cached;
private pendingRefresh;
private nextForce;
private lastAdvisoryError;
private onAdvisoryRefreshError;
constructor(provider: AccessTokenProvider, onAdvisoryRefreshError?: (err: unknown) => void);
getToken(): Promise<string>;
/**
* Clears the cached token and marks the next {@link getToken} as a forced
* refresh, so the underlying provider bypasses any on-disk freshness check.
* Called after a 401 — the server has just told us the token is bad even
* if its `expires_at` still looks fresh.
*/
invalidate(): void;
/**
* Mandatory refresh. Joins any in-flight refresh unless forced — a forced
* refresh must not coalesce into a non-forced one that may re-serve the
* same stale disk token.
*/
private refresh;
/**
* Advisory background refresh. Shares the same in-flight promise as
* mandatory refreshes for deduplication, but swallows errors so the
* stale cached token keeps being served. Backs off for
* {@link ADVISORY_REFRESH_BACKOFF_IN_SECONDS} after a failure so an
* outage during the advisory window doesn't hammer the token endpoint.
*/
private backgroundRefresh;
/**
* Core refresh. Sets {@link pendingRefresh} so concurrent callers
* (both advisory and mandatory) coalesce into a single provider call.
*/
private doRefresh;
}
//# sourceMappingURL=token-cache.d.mts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"token-cache.d.mts","sourceRoot":"","sources":["../../src/lib/credentials/token-cache.ts"],"names":[],"mappings":"OAAO,KAAK,EAAe,mBAAmB,EAAE;AAQhD;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,cAAc,CAAqC;IAC3D,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,sBAAsB,CAAuC;gBAEzD,QAAQ,EAAE,mBAAmB,EAAE,sBAAsB,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI;IAKpF,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;IA6BjC;;;;;OAKG;IACH,UAAU,IAAI,IAAI;IAKlB;;;;OAIG;IACH,OAAO,CAAC,OAAO;IAOf;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;IAezB;;;OAGG;IACH,OAAO,CAAC,SAAS;CAclB"}
+54
View File
@@ -0,0 +1,54 @@
import type { AccessTokenProvider } from "./types.js";
/**
* Wraps an {@link AccessTokenProvider} with two-tier proactive refresh
* and concurrent deduplication.
*
* Refresh policy on each {@link getToken} call:
*
* - No cached token → call provider (blocking), cache, return.
* - Cached with `expiresAt == null` → return cached forever.
* - More than 120s remaining → return cached.
* - 30–120s remaining (advisory window) → return stale token immediately,
* kick off background refresh. On failure, log and keep stale.
* - Less than 30s remaining or expired (mandatory) → block and refresh.
* On failure, throw.
*
* Concurrent mandatory callers coalesce into a single provider call.
*/
export declare class TokenCache {
private provider;
private cached;
private pendingRefresh;
private nextForce;
private lastAdvisoryError;
private onAdvisoryRefreshError;
constructor(provider: AccessTokenProvider, onAdvisoryRefreshError?: (err: unknown) => void);
getToken(): Promise<string>;
/**
* Clears the cached token and marks the next {@link getToken} as a forced
* refresh, so the underlying provider bypasses any on-disk freshness check.
* Called after a 401 — the server has just told us the token is bad even
* if its `expires_at` still looks fresh.
*/
invalidate(): void;
/**
* Mandatory refresh. Joins any in-flight refresh unless forced — a forced
* refresh must not coalesce into a non-forced one that may re-serve the
* same stale disk token.
*/
private refresh;
/**
* Advisory background refresh. Shares the same in-flight promise as
* mandatory refreshes for deduplication, but swallows errors so the
* stale cached token keeps being served. Backs off for
* {@link ADVISORY_REFRESH_BACKOFF_IN_SECONDS} after a failure so an
* outage during the advisory window doesn't hammer the token endpoint.
*/
private backgroundRefresh;
/**
* Core refresh. Sets {@link pendingRefresh} so concurrent callers
* (both advisory and mandatory) coalesce into a single provider call.
*/
private doRefresh;
}
//# sourceMappingURL=token-cache.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"token-cache.d.ts","sourceRoot":"","sources":["../../src/lib/credentials/token-cache.ts"],"names":[],"mappings":"OAAO,KAAK,EAAe,mBAAmB,EAAE;AAQhD;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,cAAc,CAAqC;IAC3D,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,sBAAsB,CAAuC;gBAEzD,QAAQ,EAAE,mBAAmB,EAAE,sBAAsB,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI;IAKpF,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;IA6BjC;;;;;OAKG;IACH,UAAU,IAAI,IAAI;IAKlB;;;;OAIG;IACH,OAAO,CAAC,OAAO;IAOf;;;;;;OAMG;IACH,OAAO,CAAC,iBAAiB;IAezB;;;OAGG;IACH,OAAO,CAAC,SAAS;CAclB"}
+112
View File
@@ -0,0 +1,112 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TokenCache = void 0;
const types_1 = require("./types.js");
const time_1 = require("../../internal/utils/time.js");
/**
* Wraps an {@link AccessTokenProvider} with two-tier proactive refresh
* and concurrent deduplication.
*
* Refresh policy on each {@link getToken} call:
*
* - No cached token → call provider (blocking), cache, return.
* - Cached with `expiresAt == null` → return cached forever.
* - More than 120s remaining → return cached.
* - 30–120s remaining (advisory window) → return stale token immediately,
* kick off background refresh. On failure, log and keep stale.
* - Less than 30s remaining or expired (mandatory) → block and refresh.
* On failure, throw.
*
* Concurrent mandatory callers coalesce into a single provider call.
*/
class TokenCache {
constructor(provider, onAdvisoryRefreshError) {
this.cached = null;
this.pendingRefresh = null;
this.nextForce = false;
this.lastAdvisoryError = 0;
this.provider = provider;
this.onAdvisoryRefreshError = onAdvisoryRefreshError;
}
async getToken() {
const force = this.nextForce;
this.nextForce = false;
const cached = this.cached;
if (force || cached == null) {
const token = await this.refresh(force);
return token.token;
}
if (cached.expiresAt == null) {
return cached.token;
}
const remaining = cached.expiresAt - (0, time_1.nowAsSeconds)();
if (remaining > types_1.ADVISORY_REFRESH_THRESHOLD_IN_SECONDS) {
return cached.token;
}
if (remaining > types_1.MANDATORY_REFRESH_THRESHOLD_IN_SECONDS) {
this.backgroundRefresh();
return cached.token;
}
const token = await this.refresh();
return token.token;
}
/**
* Clears the cached token and marks the next {@link getToken} as a forced
* refresh, so the underlying provider bypasses any on-disk freshness check.
* Called after a 401 — the server has just told us the token is bad even
* if its `expires_at` still looks fresh.
*/
invalidate() {
this.cached = null;
this.nextForce = true;
}
/**
* Mandatory refresh. Joins any in-flight refresh unless forced — a forced
* refresh must not coalesce into a non-forced one that may re-serve the
* same stale disk token.
*/
refresh(force = false) {
if (this.pendingRefresh && !force) {
return this.pendingRefresh;
}
return this.doRefresh(force);
}
/**
* Advisory background refresh. Shares the same in-flight promise as
* mandatory refreshes for deduplication, but swallows errors so the
* stale cached token keeps being served. Backs off for
* {@link ADVISORY_REFRESH_BACKOFF_IN_SECONDS} after a failure so an
* outage during the advisory window doesn't hammer the token endpoint.
*/
backgroundRefresh() {
if (this.pendingRefresh) {
return;
}
if ((0, time_1.nowAsSeconds)() - this.lastAdvisoryError < types_1.ADVISORY_REFRESH_BACKOFF_IN_SECONDS) {
return;
}
this.doRefresh().catch((err) => {
this.lastAdvisoryError = (0, time_1.nowAsSeconds)();
// Advisory failure: keep serving the stale cached token, but surface
// the error to the caller-provided hook so it can be logged.
this.onAdvisoryRefreshError?.(err);
});
}
/**
* Core refresh. Sets {@link pendingRefresh} so concurrent callers
* (both advisory and mandatory) coalesce into a single provider call.
*/
doRefresh(force = false) {
this.pendingRefresh = this.provider(force ? { forceRefresh: true } : undefined).then((token) => {
this.cached = token;
this.pendingRefresh = null;
return token;
}, (err) => {
this.pendingRefresh = null;
throw err;
});
return this.pendingRefresh;
}
}
exports.TokenCache = TokenCache;
//# sourceMappingURL=token-cache.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"token-cache.js","sourceRoot":"","sources":["../../src/lib/credentials/token-cache.ts"],"names":[],"mappings":"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"}
+108
View File
@@ -0,0 +1,108 @@
import { ADVISORY_REFRESH_BACKOFF_IN_SECONDS, ADVISORY_REFRESH_THRESHOLD_IN_SECONDS, MANDATORY_REFRESH_THRESHOLD_IN_SECONDS, } from "./types.mjs";
import { nowAsSeconds } from "../../internal/utils/time.mjs";
/**
* Wraps an {@link AccessTokenProvider} with two-tier proactive refresh
* and concurrent deduplication.
*
* Refresh policy on each {@link getToken} call:
*
* - No cached token → call provider (blocking), cache, return.
* - Cached with `expiresAt == null` → return cached forever.
* - More than 120s remaining → return cached.
* - 30–120s remaining (advisory window) → return stale token immediately,
* kick off background refresh. On failure, log and keep stale.
* - Less than 30s remaining or expired (mandatory) → block and refresh.
* On failure, throw.
*
* Concurrent mandatory callers coalesce into a single provider call.
*/
export class TokenCache {
constructor(provider, onAdvisoryRefreshError) {
this.cached = null;
this.pendingRefresh = null;
this.nextForce = false;
this.lastAdvisoryError = 0;
this.provider = provider;
this.onAdvisoryRefreshError = onAdvisoryRefreshError;
}
async getToken() {
const force = this.nextForce;
this.nextForce = false;
const cached = this.cached;
if (force || cached == null) {
const token = await this.refresh(force);
return token.token;
}
if (cached.expiresAt == null) {
return cached.token;
}
const remaining = cached.expiresAt - nowAsSeconds();
if (remaining > ADVISORY_REFRESH_THRESHOLD_IN_SECONDS) {
return cached.token;
}
if (remaining > MANDATORY_REFRESH_THRESHOLD_IN_SECONDS) {
this.backgroundRefresh();
return cached.token;
}
const token = await this.refresh();
return token.token;
}
/**
* Clears the cached token and marks the next {@link getToken} as a forced
* refresh, so the underlying provider bypasses any on-disk freshness check.
* Called after a 401 — the server has just told us the token is bad even
* if its `expires_at` still looks fresh.
*/
invalidate() {
this.cached = null;
this.nextForce = true;
}
/**
* Mandatory refresh. Joins any in-flight refresh unless forced — a forced
* refresh must not coalesce into a non-forced one that may re-serve the
* same stale disk token.
*/
refresh(force = false) {
if (this.pendingRefresh && !force) {
return this.pendingRefresh;
}
return this.doRefresh(force);
}
/**
* Advisory background refresh. Shares the same in-flight promise as
* mandatory refreshes for deduplication, but swallows errors so the
* stale cached token keeps being served. Backs off for
* {@link ADVISORY_REFRESH_BACKOFF_IN_SECONDS} after a failure so an
* outage during the advisory window doesn't hammer the token endpoint.
*/
backgroundRefresh() {
if (this.pendingRefresh) {
return;
}
if (nowAsSeconds() - this.lastAdvisoryError < ADVISORY_REFRESH_BACKOFF_IN_SECONDS) {
return;
}
this.doRefresh().catch((err) => {
this.lastAdvisoryError = nowAsSeconds();
// Advisory failure: keep serving the stale cached token, but surface
// the error to the caller-provided hook so it can be logged.
this.onAdvisoryRefreshError?.(err);
});
}
/**
* Core refresh. Sets {@link pendingRefresh} so concurrent callers
* (both advisory and mandatory) coalesce into a single provider call.
*/
doRefresh(force = false) {
this.pendingRefresh = this.provider(force ? { forceRefresh: true } : undefined).then((token) => {
this.cached = token;
this.pendingRefresh = null;
return token;
}, (err) => {
this.pendingRefresh = null;
throw err;
});
return this.pendingRefresh;
}
}
//# sourceMappingURL=token-cache.mjs.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"token-cache.mjs","sourceRoot":"","sources":["../../src/lib/credentials/token-cache.ts"],"names":[],"mappings":"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"}
+96
View File
@@ -0,0 +1,96 @@
import { AnthropicError } from "../../core/error.mjs";
export type AccessToken = {
token: string;
/** Unix epoch seconds. `null` means no expiry (cache forever). */
expiresAt: number | null;
};
/**
* Mints or returns a cached access token.
*
* The optional `opts.forceRefresh` flag, set by {@link TokenCache.invalidate}
* after a 401, tells providers with on-disk caches (user_oauth, cachedExchange)
* to bypass their freshness short-circuit and always fetch fresh. Providers
* without a cache can ignore it.
*/
export type AccessTokenProvider = (opts?: {
forceRefresh?: boolean;
}) => Promise<AccessToken>;
export type IdentityTokenProvider = () => string | Promise<string>;
export type CredentialResult = {
provider: AccessTokenProvider;
extraHeaders: Record<string, string>;
/**
* The `base_url` from the resolved config/profile, if any. The client
* applies this to outbound API requests when no explicit `baseURL` (constructor
* option or `ANTHROPIC_BASE_URL` env) was given, so a profile pointing at a
* non-default API host both mints its token against that host AND sends
* subsequent API requests there.
*/
baseURL?: string | undefined;
};
/** Response body from `POST /v1/oauth/token`. */
export type TokenEndpointResponse = {
access_token?: string;
expires_in?: number;
refresh_token?: string;
};
export declare const GRANT_TYPE_JWT_BEARER = "urn:ietf:params:oauth:grant-type:jwt-bearer";
export declare const GRANT_TYPE_REFRESH_TOKEN = "refresh_token";
export declare const TOKEN_ENDPOINT = "/v1/oauth/token";
/**
* `anthropic-beta` value required on authenticated API requests using an
* OAuth bearer token, and on `refresh_token` grants against the token endpoint.
*/
export declare const OAUTH_API_BETA_HEADER = "oauth-2025-04-20";
/**
* `anthropic-beta` value required on jwt-bearer exchanges against the token
* endpoint. It routes the request to the federation service; it must NOT be
* sent on `refresh_token` grants, which are handled by a different backend.
*/
export declare const FEDERATION_BETA_HEADER = "oidc-federation-2026-04-01";
export declare const ADVISORY_REFRESH_THRESHOLD_IN_SECONDS = 120;
export declare const MANDATORY_REFRESH_THRESHOLD_IN_SECONDS = 30;
export declare const ADVISORY_REFRESH_BACKOFF_IN_SECONDS = 5;
/**
* Rejects base URLs that would cause a JWT assertion or refresh token to be
* sent over cleartext HTTP. Loopback hosts are allowed for local development.
*/
export declare function requireSecureTokenEndpoint(baseURL: string): void;
/**
* Reads the response body as text, parses it as a token-endpoint JSON
* response, validates `access_token` is present, and rejects a non-Bearer
* `token_type` when one is provided. Reads at most
* {@link MAX_TOKEN_RESPONSE_BYTES} from the body stream.
*/
export declare function parseTokenResponse(resp: Response, requestId: string | null): Promise<TokenEndpointResponse & {
access_token: string;
}>;
/**
* Returns a redacted copy of a token-endpoint error body for safe inclusion
* in an exception. Strings are truncated; objects keep only the RFC 6749
* §5.2 error fields.
*/
export declare function redactSensitive(body: unknown): unknown;
/**
* Best-effort safety check on a credentials file before reading it.
*
* On POSIX: resolves symlinks (so containerized deployments that mount the
* credential as a symlink to a tmpfs-backed file keep working), then rejects
* the resolved target if it is group- or world- readable or writable. A uid
* mismatch on the resolved target is surfaced via `onWarn` since
* root-written/app-read is common in init-container setups. No-op on Windows.
*/
export declare function checkCredentialsFileSafety(path: string, onWarn?: (msg: string) => void): Promise<void>;
/**
* Atomically writes JSON to `targetPath` via a `.tmp` sibling + rename,
* with fsync on the file and (best-effort) on the parent directory.
* Creates the parent directory with mode 0700 and the file with mode 0600.
*/
export declare function writeCredentialsFileAtomic(targetPath: string, data: unknown): Promise<void>;
export declare class WorkloadIdentityError extends AnthropicError {
readonly statusCode: number | null;
readonly body: unknown;
readonly requestId: string | null;
constructor(message: string, statusCode?: number | null, body?: unknown, requestId?: string | null);
}
//# sourceMappingURL=types.d.mts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"types.d.mts","sourceRoot":"","sources":["../../src/lib/credentials/types.ts"],"names":[],"mappings":"OAAO,EAAE,cAAc,EAAE;AAEzB,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,kEAAkE;IAClE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,IAAI,CAAC,EAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;AAE9F,MAAM,MAAM,qBAAqB,GAAG,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;AAEnE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9B,CAAC;AAEF,iDAAiD;AACjD,MAAM,MAAM,qBAAqB,GAAG;IAClC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,eAAO,MAAM,qBAAqB,gDAAgD,CAAC;AACnF,eAAO,MAAM,wBAAwB,kBAAkB,CAAC;AACxD,eAAO,MAAM,cAAc,oBAAoB,CAAC;AAEhD;;;GAGG;AACH,eAAO,MAAM,qBAAqB,qBAAqB,CAAC;AAExD;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,+BAA+B,CAAC;AAEnE,eAAO,MAAM,qCAAqC,MAAM,CAAC;AACzD,eAAO,MAAM,sCAAsC,KAAK,CAAC;AACzD,eAAO,MAAM,mCAAmC,IAAI,CAAC;AAIrD;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAehE;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,QAAQ,EACd,SAAS,EAAE,MAAM,GAAG,IAAI,GACvB,OAAO,CAAC,qBAAqB,GAAG;IAAE,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC,CA8B3D;AAQD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAoBtD;AAED;;;;;;;;GAQG;AACH,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,MAAM,EACZ,MAAM,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAiD,GACzE,OAAO,CAAC,IAAI,CAAC,CAgCf;AAED;;;;GAIG;AACH,wBAAsB,0BAA0B,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAkCjG;AAmCD,qBAAa,qBAAsB,SAAQ,cAAc;IACvD,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;gBAGhC,OAAO,EAAE,MAAM,EACf,UAAU,GAAE,MAAM,GAAG,IAAW,EAChC,IAAI,GAAE,OAAc,EACpB,SAAS,GAAE,MAAM,GAAG,IAAW;CAOlC"}
+96
View File
@@ -0,0 +1,96 @@
import { AnthropicError } from "../../core/error.js";
export type AccessToken = {
token: string;
/** Unix epoch seconds. `null` means no expiry (cache forever). */
expiresAt: number | null;
};
/**
* Mints or returns a cached access token.
*
* The optional `opts.forceRefresh` flag, set by {@link TokenCache.invalidate}
* after a 401, tells providers with on-disk caches (user_oauth, cachedExchange)
* to bypass their freshness short-circuit and always fetch fresh. Providers
* without a cache can ignore it.
*/
export type AccessTokenProvider = (opts?: {
forceRefresh?: boolean;
}) => Promise<AccessToken>;
export type IdentityTokenProvider = () => string | Promise<string>;
export type CredentialResult = {
provider: AccessTokenProvider;
extraHeaders: Record<string, string>;
/**
* The `base_url` from the resolved config/profile, if any. The client
* applies this to outbound API requests when no explicit `baseURL` (constructor
* option or `ANTHROPIC_BASE_URL` env) was given, so a profile pointing at a
* non-default API host both mints its token against that host AND sends
* subsequent API requests there.
*/
baseURL?: string | undefined;
};
/** Response body from `POST /v1/oauth/token`. */
export type TokenEndpointResponse = {
access_token?: string;
expires_in?: number;
refresh_token?: string;
};
export declare const GRANT_TYPE_JWT_BEARER = "urn:ietf:params:oauth:grant-type:jwt-bearer";
export declare const GRANT_TYPE_REFRESH_TOKEN = "refresh_token";
export declare const TOKEN_ENDPOINT = "/v1/oauth/token";
/**
* `anthropic-beta` value required on authenticated API requests using an
* OAuth bearer token, and on `refresh_token` grants against the token endpoint.
*/
export declare const OAUTH_API_BETA_HEADER = "oauth-2025-04-20";
/**
* `anthropic-beta` value required on jwt-bearer exchanges against the token
* endpoint. It routes the request to the federation service; it must NOT be
* sent on `refresh_token` grants, which are handled by a different backend.
*/
export declare const FEDERATION_BETA_HEADER = "oidc-federation-2026-04-01";
export declare const ADVISORY_REFRESH_THRESHOLD_IN_SECONDS = 120;
export declare const MANDATORY_REFRESH_THRESHOLD_IN_SECONDS = 30;
export declare const ADVISORY_REFRESH_BACKOFF_IN_SECONDS = 5;
/**
* Rejects base URLs that would cause a JWT assertion or refresh token to be
* sent over cleartext HTTP. Loopback hosts are allowed for local development.
*/
export declare function requireSecureTokenEndpoint(baseURL: string): void;
/**
* Reads the response body as text, parses it as a token-endpoint JSON
* response, validates `access_token` is present, and rejects a non-Bearer
* `token_type` when one is provided. Reads at most
* {@link MAX_TOKEN_RESPONSE_BYTES} from the body stream.
*/
export declare function parseTokenResponse(resp: Response, requestId: string | null): Promise<TokenEndpointResponse & {
access_token: string;
}>;
/**
* Returns a redacted copy of a token-endpoint error body for safe inclusion
* in an exception. Strings are truncated; objects keep only the RFC 6749
* §5.2 error fields.
*/
export declare function redactSensitive(body: unknown): unknown;
/**
* Best-effort safety check on a credentials file before reading it.
*
* On POSIX: resolves symlinks (so containerized deployments that mount the
* credential as a symlink to a tmpfs-backed file keep working), then rejects
* the resolved target if it is group- or world- readable or writable. A uid
* mismatch on the resolved target is surfaced via `onWarn` since
* root-written/app-read is common in init-container setups. No-op on Windows.
*/
export declare function checkCredentialsFileSafety(path: string, onWarn?: (msg: string) => void): Promise<void>;
/**
* Atomically writes JSON to `targetPath` via a `.tmp` sibling + rename,
* with fsync on the file and (best-effort) on the parent directory.
* Creates the parent directory with mode 0700 and the file with mode 0600.
*/
export declare function writeCredentialsFileAtomic(targetPath: string, data: unknown): Promise<void>;
export declare class WorkloadIdentityError extends AnthropicError {
readonly statusCode: number | null;
readonly body: unknown;
readonly requestId: string | null;
constructor(message: string, statusCode?: number | null, body?: unknown, requestId?: string | null);
}
//# sourceMappingURL=types.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/lib/credentials/types.ts"],"names":[],"mappings":"OAAO,EAAE,cAAc,EAAE;AAEzB,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,kEAAkE;IAClE,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,MAAM,mBAAmB,GAAG,CAAC,IAAI,CAAC,EAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAE,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;AAE9F,MAAM,MAAM,qBAAqB,GAAG,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;AAEnE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACrC;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC9B,CAAC;AAEF,iDAAiD;AACjD,MAAM,MAAM,qBAAqB,GAAG;IAClC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,eAAO,MAAM,qBAAqB,gDAAgD,CAAC;AACnF,eAAO,MAAM,wBAAwB,kBAAkB,CAAC;AACxD,eAAO,MAAM,cAAc,oBAAoB,CAAC;AAEhD;;;GAGG;AACH,eAAO,MAAM,qBAAqB,qBAAqB,CAAC;AAExD;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,+BAA+B,CAAC;AAEnE,eAAO,MAAM,qCAAqC,MAAM,CAAC;AACzD,eAAO,MAAM,sCAAsC,KAAK,CAAC;AACzD,eAAO,MAAM,mCAAmC,IAAI,CAAC;AAIrD;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAehE;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,QAAQ,EACd,SAAS,EAAE,MAAM,GAAG,IAAI,GACvB,OAAO,CAAC,qBAAqB,GAAG;IAAE,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC,CA8B3D;AAQD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAoBtD;AAED;;;;;;;;GAQG;AACH,wBAAsB,0BAA0B,CAC9C,IAAI,EAAE,MAAM,EACZ,MAAM,GAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAiD,GACzE,OAAO,CAAC,IAAI,CAAC,CAgCf;AAED;;;;GAIG;AACH,wBAAsB,0BAA0B,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAkCjG;AAmCD,qBAAa,qBAAsB,SAAQ,cAAc;IACvD,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;gBAGhC,OAAO,EAAE,MAAM,EACf,UAAU,GAAE,MAAM,GAAG,IAAW,EAChC,IAAI,GAAE,OAAc,EACpB,SAAS,GAAE,MAAM,GAAG,IAAW;CAOlC"}
+266
View File
@@ -0,0 +1,266 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.WorkloadIdentityError = exports.ADVISORY_REFRESH_BACKOFF_IN_SECONDS = exports.MANDATORY_REFRESH_THRESHOLD_IN_SECONDS = exports.ADVISORY_REFRESH_THRESHOLD_IN_SECONDS = exports.FEDERATION_BETA_HEADER = exports.OAUTH_API_BETA_HEADER = exports.TOKEN_ENDPOINT = exports.GRANT_TYPE_REFRESH_TOKEN = exports.GRANT_TYPE_JWT_BEARER = void 0;
exports.requireSecureTokenEndpoint = requireSecureTokenEndpoint;
exports.parseTokenResponse = parseTokenResponse;
exports.redactSensitive = redactSensitive;
exports.checkCredentialsFileSafety = checkCredentialsFileSafety;
exports.writeCredentialsFileAtomic = writeCredentialsFileAtomic;
const error_1 = require("../../core/error.js");
exports.GRANT_TYPE_JWT_BEARER = 'urn:ietf:params:oauth:grant-type:jwt-bearer';
exports.GRANT_TYPE_REFRESH_TOKEN = 'refresh_token';
exports.TOKEN_ENDPOINT = '/v1/oauth/token';
/**
* `anthropic-beta` value required on authenticated API requests using an
* OAuth bearer token, and on `refresh_token` grants against the token endpoint.
*/
exports.OAUTH_API_BETA_HEADER = 'oauth-2025-04-20';
/**
* `anthropic-beta` value required on jwt-bearer exchanges against the token
* endpoint. It routes the request to the federation service; it must NOT be
* sent on `refresh_token` grants, which are handled by a different backend.
*/
exports.FEDERATION_BETA_HEADER = 'oidc-federation-2026-04-01';
exports.ADVISORY_REFRESH_THRESHOLD_IN_SECONDS = 120;
exports.MANDATORY_REFRESH_THRESHOLD_IN_SECONDS = 30;
exports.ADVISORY_REFRESH_BACKOFF_IN_SECONDS = 5;
const MAX_TOKEN_RESPONSE_BYTES = 1 << 20;
/**
* Rejects base URLs that would cause a JWT assertion or refresh token to be
* sent over cleartext HTTP. Loopback hosts are allowed for local development.
*/
function requireSecureTokenEndpoint(baseURL) {
if (!baseURL)
return;
let u;
try {
u = new URL(baseURL);
}
catch (err) {
throw new WorkloadIdentityError(`Invalid token endpoint base URL "${baseURL}": ${err}`);
}
if (u.protocol === 'https:')
return;
// WHATWG URL.hostname returns bracketed IPv6 ("[::1]"); Go's net/url strips them.
const host = u.hostname.toLowerCase().replace(/^\[|\]$/g, '');
if (u.protocol === 'http:' && (host === 'localhost' || host === '127.0.0.1' || host === '::1')) {
return;
}
throw new WorkloadIdentityError(`Refusing to send credential over non-https token endpoint "${baseURL}"`);
}
/**
* Reads the response body as text, parses it as a token-endpoint JSON
* response, validates `access_token` is present, and rejects a non-Bearer
* `token_type` when one is provided. Reads at most
* {@link MAX_TOKEN_RESPONSE_BYTES} from the body stream.
*/
async function parseTokenResponse(resp, requestId) {
const text = await readLimitedText(resp);
let data;
try {
data = JSON.parse(text);
}
catch {
throw new WorkloadIdentityError(`Token endpoint returned non-JSON response (status ${resp.status})`, resp.status, redactSensitive(text), requestId);
}
if (!data.access_token) {
throw new WorkloadIdentityError(`Token endpoint response missing access_token: ${JSON.stringify(redactSensitive(data))}`, resp.status, redactSensitive(data), requestId);
}
if (data.token_type && data.token_type.toLowerCase() !== 'bearer') {
throw new WorkloadIdentityError(`Token endpoint response: unsupported token_type "${data.token_type}" (want Bearer)`, resp.status, redactSensitive(data), requestId);
}
return data;
}
const MAX_ERROR_BODY_CHARS = 2000;
// RFC 6749 §5.2 standard error-response fields. Anything else in a token
// endpoint error body is potentially echoed input (assertion, refresh_token,
// access_token, …) and is dropped rather than allowlisted-with-exceptions.
const SAFE_ERROR_KEYS = new Set(['error', 'error_description', 'error_uri']);
/**
* Returns a redacted copy of a token-endpoint error body for safe inclusion
* in an exception. Strings are truncated; objects keep only the RFC 6749
* §5.2 error fields.
*/
function redactSensitive(body) {
if (body == null)
return body;
if (typeof body === 'string') {
let parsed;
try {
parsed = JSON.parse(body);
}
catch {
if (body.length <= MAX_ERROR_BODY_CHARS)
return body;
return body.slice(0, MAX_ERROR_BODY_CHARS) + `... <${body.length - MAX_ERROR_BODY_CHARS} more chars>`;
}
return JSON.stringify(redactSensitive(parsed));
}
if (typeof body === 'object' && !Array.isArray(body)) {
const out = {};
for (const [k, v] of Object.entries(body)) {
if (SAFE_ERROR_KEYS.has(k))
out[k] = v;
}
return out;
}
return null;
}
/**
* Best-effort safety check on a credentials file before reading it.
*
* On POSIX: resolves symlinks (so containerized deployments that mount the
* credential as a symlink to a tmpfs-backed file keep working), then rejects
* the resolved target if it is group- or world- readable or writable. A uid
* mismatch on the resolved target is surfaced via `onWarn` since
* root-written/app-read is common in init-container setups. No-op on Windows.
*/
async function checkCredentialsFileSafety(path, onWarn = (m) => console.warn(`anthropic-sdk: ${m}`)) {
if (typeof process === 'undefined' || process.platform === 'win32')
return;
const fs = await Promise.resolve().then(() => __importStar(require('node:fs')));
let resolved = path;
let st;
try {
resolved = await fs.promises.realpath(path);
st = await fs.promises.stat(resolved);
}
catch {
return; // ENOENT etc — let the subsequent read surface a precise error
}
const mode = st.mode & 0o777;
// 0o022 = group/world write; 0o044 = group/world read.
if (mode & 0o022) {
throw new WorkloadIdentityError(`Credentials file at ${resolved} is group/world-writable (mode 0o${mode.toString(8)}); ` +
`this allows other local users to plant tokens. Run \`chmod 600 ${resolved}\`.`);
}
if (mode & 0o044) {
throw new WorkloadIdentityError(`Credentials file at ${resolved} is group/world-readable (mode 0o${mode.toString(8)}); ` +
`run \`chmod 600 ${resolved}\` before retrying.`);
}
if (typeof process.getuid === 'function' && st.uid !== process.getuid()) {
onWarn(`credentials file at ${resolved} is owned by uid ${st.uid} (current process uid ${process.getuid()}); ` + `verify this is intentional.`);
}
}
/**
* Atomically writes JSON to `targetPath` via a `.tmp` sibling + rename,
* with fsync on the file and (best-effort) on the parent directory.
* Creates the parent directory with mode 0700 and the file with mode 0600.
*/
async function writeCredentialsFileAtomic(targetPath, data) {
const fs = await Promise.resolve().then(() => __importStar(require('node:fs')));
const path = await Promise.resolve().then(() => __importStar(require('node:path')));
const dir = path.dirname(targetPath);
await fs.promises.mkdir(dir, { recursive: true, mode: 0o700 });
// Unique temp name avoids two concurrent writers (different processes or
// SDK instances) racing on the same '.tmp' sibling and corrupting each
// other's bytes mid-write before the rename.
const tmpPath = `${targetPath}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
try {
const fh = await fs.promises.open(tmpPath, 'w', 0o600);
try {
await fh.writeFile(JSON.stringify(data, null, 2));
await fh.sync();
}
finally {
await fh.close();
}
await fs.promises.rename(tmpPath, targetPath);
}
catch (err) {
// Don't leak the temp file if anything between create and rename failed.
await fs.promises.unlink(tmpPath).catch(() => { });
throw err;
}
// fsync the parent directory so the rename survives a crash.
try {
const dirFh = await fs.promises.open(dir, 'r');
try {
await dirFh.sync();
}
finally {
await dirFh.close();
}
}
catch {
// Directory fsync is best-effort (unsupported on some platforms, e.g. Windows).
}
}
async function readLimitedText(resp) {
if (!resp.body) {
return '';
}
const reader = resp.body.getReader();
const chunks = [];
let received = 0;
for (;;) {
const { done, value } = await reader.read();
if (done)
break;
if (received + value.length > MAX_TOKEN_RESPONSE_BYTES) {
const remaining = MAX_TOKEN_RESPONSE_BYTES - received;
if (remaining > 0)
chunks.push(value.subarray(0, remaining));
await reader.cancel();
break;
}
chunks.push(value);
received += value.length;
}
let merged;
if (chunks.length === 1) {
merged = chunks[0];
}
else {
merged = new Uint8Array(chunks.reduce((n, c) => n + c.length, 0));
let offset = 0;
for (const c of chunks) {
merged.set(c, offset);
offset += c.length;
}
}
return new TextDecoder('utf-8').decode(merged);
}
class WorkloadIdentityError extends error_1.AnthropicError {
constructor(message, statusCode = null, body = null, requestId = null) {
super(message);
this.statusCode = statusCode;
this.body = body;
this.requestId = requestId;
}
}
exports.WorkloadIdentityError = WorkloadIdentityError;
//# sourceMappingURL=types.js.map
File diff suppressed because one or more lines are too long
+224
View File
@@ -0,0 +1,224 @@
import { AnthropicError } from "../../core/error.mjs";
export const GRANT_TYPE_JWT_BEARER = 'urn:ietf:params:oauth:grant-type:jwt-bearer';
export const GRANT_TYPE_REFRESH_TOKEN = 'refresh_token';
export const TOKEN_ENDPOINT = '/v1/oauth/token';
/**
* `anthropic-beta` value required on authenticated API requests using an
* OAuth bearer token, and on `refresh_token` grants against the token endpoint.
*/
export const OAUTH_API_BETA_HEADER = 'oauth-2025-04-20';
/**
* `anthropic-beta` value required on jwt-bearer exchanges against the token
* endpoint. It routes the request to the federation service; it must NOT be
* sent on `refresh_token` grants, which are handled by a different backend.
*/
export const FEDERATION_BETA_HEADER = 'oidc-federation-2026-04-01';
export const ADVISORY_REFRESH_THRESHOLD_IN_SECONDS = 120;
export const MANDATORY_REFRESH_THRESHOLD_IN_SECONDS = 30;
export const ADVISORY_REFRESH_BACKOFF_IN_SECONDS = 5;
const MAX_TOKEN_RESPONSE_BYTES = 1 << 20;
/**
* Rejects base URLs that would cause a JWT assertion or refresh token to be
* sent over cleartext HTTP. Loopback hosts are allowed for local development.
*/
export function requireSecureTokenEndpoint(baseURL) {
if (!baseURL)
return;
let u;
try {
u = new URL(baseURL);
}
catch (err) {
throw new WorkloadIdentityError(`Invalid token endpoint base URL "${baseURL}": ${err}`);
}
if (u.protocol === 'https:')
return;
// WHATWG URL.hostname returns bracketed IPv6 ("[::1]"); Go's net/url strips them.
const host = u.hostname.toLowerCase().replace(/^\[|\]$/g, '');
if (u.protocol === 'http:' && (host === 'localhost' || host === '127.0.0.1' || host === '::1')) {
return;
}
throw new WorkloadIdentityError(`Refusing to send credential over non-https token endpoint "${baseURL}"`);
}
/**
* Reads the response body as text, parses it as a token-endpoint JSON
* response, validates `access_token` is present, and rejects a non-Bearer
* `token_type` when one is provided. Reads at most
* {@link MAX_TOKEN_RESPONSE_BYTES} from the body stream.
*/
export async function parseTokenResponse(resp, requestId) {
const text = await readLimitedText(resp);
let data;
try {
data = JSON.parse(text);
}
catch {
throw new WorkloadIdentityError(`Token endpoint returned non-JSON response (status ${resp.status})`, resp.status, redactSensitive(text), requestId);
}
if (!data.access_token) {
throw new WorkloadIdentityError(`Token endpoint response missing access_token: ${JSON.stringify(redactSensitive(data))}`, resp.status, redactSensitive(data), requestId);
}
if (data.token_type && data.token_type.toLowerCase() !== 'bearer') {
throw new WorkloadIdentityError(`Token endpoint response: unsupported token_type "${data.token_type}" (want Bearer)`, resp.status, redactSensitive(data), requestId);
}
return data;
}
const MAX_ERROR_BODY_CHARS = 2000;
// RFC 6749 §5.2 standard error-response fields. Anything else in a token
// endpoint error body is potentially echoed input (assertion, refresh_token,
// access_token, …) and is dropped rather than allowlisted-with-exceptions.
const SAFE_ERROR_KEYS = new Set(['error', 'error_description', 'error_uri']);
/**
* Returns a redacted copy of a token-endpoint error body for safe inclusion
* in an exception. Strings are truncated; objects keep only the RFC 6749
* §5.2 error fields.
*/
export function redactSensitive(body) {
if (body == null)
return body;
if (typeof body === 'string') {
let parsed;
try {
parsed = JSON.parse(body);
}
catch {
if (body.length <= MAX_ERROR_BODY_CHARS)
return body;
return body.slice(0, MAX_ERROR_BODY_CHARS) + `... <${body.length - MAX_ERROR_BODY_CHARS} more chars>`;
}
return JSON.stringify(redactSensitive(parsed));
}
if (typeof body === 'object' && !Array.isArray(body)) {
const out = {};
for (const [k, v] of Object.entries(body)) {
if (SAFE_ERROR_KEYS.has(k))
out[k] = v;
}
return out;
}
return null;
}
/**
* Best-effort safety check on a credentials file before reading it.
*
* On POSIX: resolves symlinks (so containerized deployments that mount the
* credential as a symlink to a tmpfs-backed file keep working), then rejects
* the resolved target if it is group- or world- readable or writable. A uid
* mismatch on the resolved target is surfaced via `onWarn` since
* root-written/app-read is common in init-container setups. No-op on Windows.
*/
export async function checkCredentialsFileSafety(path, onWarn = (m) => console.warn(`anthropic-sdk: ${m}`)) {
if (typeof process === 'undefined' || process.platform === 'win32')
return;
const fs = await import('node:fs');
let resolved = path;
let st;
try {
resolved = await fs.promises.realpath(path);
st = await fs.promises.stat(resolved);
}
catch {
return; // ENOENT etc — let the subsequent read surface a precise error
}
const mode = st.mode & 0o777;
// 0o022 = group/world write; 0o044 = group/world read.
if (mode & 0o022) {
throw new WorkloadIdentityError(`Credentials file at ${resolved} is group/world-writable (mode 0o${mode.toString(8)}); ` +
`this allows other local users to plant tokens. Run \`chmod 600 ${resolved}\`.`);
}
if (mode & 0o044) {
throw new WorkloadIdentityError(`Credentials file at ${resolved} is group/world-readable (mode 0o${mode.toString(8)}); ` +
`run \`chmod 600 ${resolved}\` before retrying.`);
}
if (typeof process.getuid === 'function' && st.uid !== process.getuid()) {
onWarn(`credentials file at ${resolved} is owned by uid ${st.uid} (current process uid ${process.getuid()}); ` + `verify this is intentional.`);
}
}
/**
* Atomically writes JSON to `targetPath` via a `.tmp` sibling + rename,
* with fsync on the file and (best-effort) on the parent directory.
* Creates the parent directory with mode 0700 and the file with mode 0600.
*/
export async function writeCredentialsFileAtomic(targetPath, data) {
const fs = await import('node:fs');
const path = await import('node:path');
const dir = path.dirname(targetPath);
await fs.promises.mkdir(dir, { recursive: true, mode: 0o700 });
// Unique temp name avoids two concurrent writers (different processes or
// SDK instances) racing on the same '.tmp' sibling and corrupting each
// other's bytes mid-write before the rename.
const tmpPath = `${targetPath}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
try {
const fh = await fs.promises.open(tmpPath, 'w', 0o600);
try {
await fh.writeFile(JSON.stringify(data, null, 2));
await fh.sync();
}
finally {
await fh.close();
}
await fs.promises.rename(tmpPath, targetPath);
}
catch (err) {
// Don't leak the temp file if anything between create and rename failed.
await fs.promises.unlink(tmpPath).catch(() => { });
throw err;
}
// fsync the parent directory so the rename survives a crash.
try {
const dirFh = await fs.promises.open(dir, 'r');
try {
await dirFh.sync();
}
finally {
await dirFh.close();
}
}
catch {
// Directory fsync is best-effort (unsupported on some platforms, e.g. Windows).
}
}
async function readLimitedText(resp) {
if (!resp.body) {
return '';
}
const reader = resp.body.getReader();
const chunks = [];
let received = 0;
for (;;) {
const { done, value } = await reader.read();
if (done)
break;
if (received + value.length > MAX_TOKEN_RESPONSE_BYTES) {
const remaining = MAX_TOKEN_RESPONSE_BYTES - received;
if (remaining > 0)
chunks.push(value.subarray(0, remaining));
await reader.cancel();
break;
}
chunks.push(value);
received += value.length;
}
let merged;
if (chunks.length === 1) {
merged = chunks[0];
}
else {
merged = new Uint8Array(chunks.reduce((n, c) => n + c.length, 0));
let offset = 0;
for (const c of chunks) {
merged.set(c, offset);
offset += c.length;
}
}
return new TextDecoder('utf-8').decode(merged);
}
export class WorkloadIdentityError extends AnthropicError {
constructor(message, statusCode = null, body = null, requestId = null) {
super(message);
this.statusCode = statusCode;
this.body = body;
this.requestId = requestId;
}
}
//# sourceMappingURL=types.mjs.map
File diff suppressed because one or more lines are too long
+21
View File
@@ -0,0 +1,21 @@
import type { Fetch } from "../../internal/builtin-types.mjs";
import type { AccessTokenProvider } from "./types.mjs";
export type UserOAuthConfig = {
credentialsPath: string;
clientId?: string | undefined;
baseURL: string;
fetch: Fetch;
userAgent?: string | undefined;
onSafetyWarning?: ((msg: string) => void) | undefined;
};
/**
* Reads a user-oauth credential file. Returns the cached access token while
* fresh; on expiry performs a `refresh_token` grant and writes the new
* tokens back to the credentials file (atomic replace, fsync'd).
*
* If `clientId` is empty, the access token is treated as static — the
* credentials file is read on every call but no refresh is attempted, and
* an expired token without a `refresh_token` raises.
*/
export declare function userOAuthProvider(config: UserOAuthConfig): AccessTokenProvider;
//# sourceMappingURL=user-oauth.d.mts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"user-oauth.d.mts","sourceRoot":"","sources":["../../src/lib/credentials/user-oauth.ts"],"names":[],"mappings":"OAAO,KAAK,EAAE,KAAK,EAAE;OAEd,KAAK,EAAE,mBAAmB,EAAE;AAgBnC,MAAM,MAAM,eAAe,GAAG;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,eAAe,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CACvD,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,eAAe,GAAG,mBAAmB,CA2G9E"}
+21
View File
@@ -0,0 +1,21 @@
import type { Fetch } from "../../internal/builtin-types.js";
import type { AccessTokenProvider } from "./types.js";
export type UserOAuthConfig = {
credentialsPath: string;
clientId?: string | undefined;
baseURL: string;
fetch: Fetch;
userAgent?: string | undefined;
onSafetyWarning?: ((msg: string) => void) | undefined;
};
/**
* Reads a user-oauth credential file. Returns the cached access token while
* fresh; on expiry performs a `refresh_token` grant and writes the new
* tokens back to the credentials file (atomic replace, fsync'd).
*
* If `clientId` is empty, the access token is treated as static — the
* credentials file is read on every call but no refresh is attempted, and
* an expired token without a `refresh_token` raises.
*/
export declare function userOAuthProvider(config: UserOAuthConfig): AccessTokenProvider;
//# sourceMappingURL=user-oauth.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"user-oauth.d.ts","sourceRoot":"","sources":["../../src/lib/credentials/user-oauth.ts"],"names":[],"mappings":"OAAO,KAAK,EAAE,KAAK,EAAE;OAEd,KAAK,EAAE,mBAAmB,EAAE;AAgBnC,MAAM,MAAM,eAAe,GAAG;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,eAAe,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CACvD,CAAC;AAEF;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,eAAe,GAAG,mBAAmB,CA2G9E"}
+130
View File
@@ -0,0 +1,130 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.userOAuthProvider = userOAuthProvider;
const credentials_1 = require("../../core/credentials.js");
const types_1 = require("./types.js");
const time_1 = require("../../internal/utils/time.js");
const version_1 = require("../../version.js");
/**
* Reads a user-oauth credential file. Returns the cached access token while
* fresh; on expiry performs a `refresh_token` grant and writes the new
* tokens back to the credentials file (atomic replace, fsync'd).
*
* If `clientId` is empty, the access token is treated as static — the
* credentials file is read on every call but no refresh is attempted, and
* an expired token without a `refresh_token` raises.
*/
function userOAuthProvider(config) {
return async (opts) => {
const fs = await Promise.resolve().then(() => __importStar(require('node:fs')));
await (0, types_1.checkCredentialsFileSafety)(config.credentialsPath, config.onSafetyWarning);
let raw;
try {
raw = await fs.promises.readFile(config.credentialsPath, 'utf-8');
}
catch (err) {
throw new types_1.WorkloadIdentityError(`Credentials file not found at ${config.credentialsPath}: ${err}`);
}
let creds;
try {
creds = JSON.parse(raw);
}
catch (err) {
throw new types_1.WorkloadIdentityError(`Credentials file at ${config.credentialsPath} is not valid JSON: ${err}`);
}
const accessToken = creds.access_token;
if (!accessToken) {
throw new types_1.WorkloadIdentityError(`Credentials file at ${config.credentialsPath} must include 'access_token'`);
}
// Return cached token if still fresh (or no expiry info), unless the
// caller is forcing a refresh after a 401 — then go straight to refresh
// even if the file's expires_at still looks valid.
const expiresAt = creds.expires_at;
if (!opts?.forceRefresh &&
(expiresAt == null || (0, time_1.nowAsSeconds)() < expiresAt - types_1.MANDATORY_REFRESH_THRESHOLD_IN_SECONDS)) {
return { token: accessToken, expiresAt: expiresAt ?? null };
}
const refreshToken = creds.refresh_token;
if (!config.clientId || !refreshToken) {
throw new types_1.WorkloadIdentityError(`Access token at ${config.credentialsPath} has expired and no refresh is available ` +
`(client_id ${config.clientId ? 'set' : 'empty'}, refresh_token ${refreshToken ? 'set' : 'empty'})`);
}
(0, types_1.requireSecureTokenEndpoint)(config.baseURL);
const body = {
grant_type: types_1.GRANT_TYPE_REFRESH_TOKEN,
refresh_token: refreshToken,
client_id: config.clientId,
};
const url = `${config.baseURL}${types_1.TOKEN_ENDPOINT}`;
let resp;
try {
resp = await config.fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'anthropic-beta': types_1.OAUTH_API_BETA_HEADER,
'User-Agent': config.userAgent || `anthropic-sdk-typescript/${version_1.VERSION} userOAuthProvider`,
},
body: JSON.stringify(body),
});
}
catch (err) {
throw new types_1.WorkloadIdentityError(`User OAuth refresh failed to reach token endpoint: ${err}`);
}
const requestId = resp.headers.get('Request-Id');
if (!resp.ok) {
const text = await resp.text().catch(() => '');
throw new types_1.WorkloadIdentityError(`User OAuth refresh failed (HTTP ${resp.status}): ${(0, types_1.redactSensitive)(text)}`, resp.status, (0, types_1.redactSensitive)(text), requestId);
}
const data = await (0, types_1.parseTokenResponse)(resp, requestId);
const expiresIn = Number(data.expires_in);
if (!Number.isFinite(expiresIn)) {
throw new types_1.WorkloadIdentityError(`User OAuth refresh response missing or invalid expires_in: ${JSON.stringify((0, types_1.redactSensitive)(data))}`, resp.status, (0, types_1.redactSensitive)(data), requestId);
}
const newExpiresAt = (0, time_1.nowAsSeconds)() + expiresIn;
const newRefreshToken = data.refresh_token || refreshToken;
await (0, types_1.writeCredentialsFileAtomic)(config.credentialsPath, {
...creds,
version: credentials_1.CREDENTIALS_FILE_VERSION,
type: 'oauth_token',
access_token: data.access_token,
expires_at: newExpiresAt,
refresh_token: newRefreshToken,
});
return { token: data.access_token, expiresAt: newExpiresAt };
};
}
//# sourceMappingURL=user-oauth.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"user-oauth.js","sourceRoot":"","sources":["../../src/lib/credentials/user-oauth.ts"],"names":[],"mappings":"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"}
+94
View File
@@ -0,0 +1,94 @@
import { CREDENTIALS_FILE_VERSION } from "../../core/credentials.mjs";
import { GRANT_TYPE_REFRESH_TOKEN, MANDATORY_REFRESH_THRESHOLD_IN_SECONDS, OAUTH_API_BETA_HEADER, TOKEN_ENDPOINT, WorkloadIdentityError, checkCredentialsFileSafety, parseTokenResponse, redactSensitive, requireSecureTokenEndpoint, writeCredentialsFileAtomic, } from "./types.mjs";
import { nowAsSeconds } from "../../internal/utils/time.mjs";
import { VERSION } from "../../version.mjs";
/**
* Reads a user-oauth credential file. Returns the cached access token while
* fresh; on expiry performs a `refresh_token` grant and writes the new
* tokens back to the credentials file (atomic replace, fsync'd).
*
* If `clientId` is empty, the access token is treated as static — the
* credentials file is read on every call but no refresh is attempted, and
* an expired token without a `refresh_token` raises.
*/
export function userOAuthProvider(config) {
return async (opts) => {
const fs = await import('node:fs');
await checkCredentialsFileSafety(config.credentialsPath, config.onSafetyWarning);
let raw;
try {
raw = await fs.promises.readFile(config.credentialsPath, 'utf-8');
}
catch (err) {
throw new WorkloadIdentityError(`Credentials file not found at ${config.credentialsPath}: ${err}`);
}
let creds;
try {
creds = JSON.parse(raw);
}
catch (err) {
throw new WorkloadIdentityError(`Credentials file at ${config.credentialsPath} is not valid JSON: ${err}`);
}
const accessToken = creds.access_token;
if (!accessToken) {
throw new WorkloadIdentityError(`Credentials file at ${config.credentialsPath} must include 'access_token'`);
}
// Return cached token if still fresh (or no expiry info), unless the
// caller is forcing a refresh after a 401 — then go straight to refresh
// even if the file's expires_at still looks valid.
const expiresAt = creds.expires_at;
if (!opts?.forceRefresh &&
(expiresAt == null || nowAsSeconds() < expiresAt - MANDATORY_REFRESH_THRESHOLD_IN_SECONDS)) {
return { token: accessToken, expiresAt: expiresAt ?? null };
}
const refreshToken = creds.refresh_token;
if (!config.clientId || !refreshToken) {
throw new WorkloadIdentityError(`Access token at ${config.credentialsPath} has expired and no refresh is available ` +
`(client_id ${config.clientId ? 'set' : 'empty'}, refresh_token ${refreshToken ? 'set' : 'empty'})`);
}
requireSecureTokenEndpoint(config.baseURL);
const body = {
grant_type: GRANT_TYPE_REFRESH_TOKEN,
refresh_token: refreshToken,
client_id: config.clientId,
};
const url = `${config.baseURL}${TOKEN_ENDPOINT}`;
let resp;
try {
resp = await config.fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'anthropic-beta': OAUTH_API_BETA_HEADER,
'User-Agent': config.userAgent || `anthropic-sdk-typescript/${VERSION} userOAuthProvider`,
},
body: JSON.stringify(body),
});
}
catch (err) {
throw new WorkloadIdentityError(`User OAuth refresh failed to reach token endpoint: ${err}`);
}
const requestId = resp.headers.get('Request-Id');
if (!resp.ok) {
const text = await resp.text().catch(() => '');
throw new WorkloadIdentityError(`User OAuth refresh failed (HTTP ${resp.status}): ${redactSensitive(text)}`, resp.status, redactSensitive(text), requestId);
}
const data = await parseTokenResponse(resp, requestId);
const expiresIn = Number(data.expires_in);
if (!Number.isFinite(expiresIn)) {
throw new WorkloadIdentityError(`User OAuth refresh response missing or invalid expires_in: ${JSON.stringify(redactSensitive(data))}`, resp.status, redactSensitive(data), requestId);
}
const newExpiresAt = nowAsSeconds() + expiresIn;
const newRefreshToken = data.refresh_token || refreshToken;
await writeCredentialsFileAtomic(config.credentialsPath, {
...creds,
version: CREDENTIALS_FILE_VERSION,
type: 'oauth_token',
access_token: data.access_token,
expires_at: newExpiresAt,
refresh_token: newRefreshToken,
});
return { token: data.access_token, expiresAt: newExpiresAt };
};
}
//# sourceMappingURL=user-oauth.mjs.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"user-oauth.mjs","sourceRoot":"","sources":["../../src/lib/credentials/user-oauth.ts"],"names":[],"mappings":"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"}
+32
View File
@@ -0,0 +1,32 @@
import type { Logger } from "../client.mjs";
import { ContentBlock, JSONOutputFormat, Message, OutputConfig, TextBlock, MessageCreateParams } from "../resources/messages/messages.mjs";
type Simplify<T> = {
[KeyType in keyof T]: T[KeyType];
} & {};
type AutoParseableOutputConfig = Omit<OutputConfig, 'format'> & {
format?: JSONOutputFormat | AutoParseableOutputFormat<any> | null;
};
export type ParseableMessageCreateParams = Simplify<Omit<MessageCreateParams, 'output_config'> & {
output_config?: AutoParseableOutputConfig | null;
}>;
export type ExtractParsedContentFromParams<Params extends ParseableMessageCreateParams> = Params['output_config'] extends {
format: AutoParseableOutputFormat<infer P>;
} ? P : null;
export type AutoParseableOutputFormat<ParsedT> = JSONOutputFormat & {
parse(content: string): ParsedT;
};
export type ParsedMessage<ParsedT> = Message & {
content: Array<ParsedContentBlock<ParsedT>>;
parsed_output: ParsedT | null;
};
export type ParsedContentBlock<ParsedT> = (TextBlock & {
parsed_output: ParsedT | null;
}) | Exclude<ContentBlock, TextBlock>;
export declare function maybeParseMessage<Params extends ParseableMessageCreateParams | null>(message: Message, params: Params, opts: {
logger: Logger;
}): ParsedMessage<ExtractParsedContentFromParams<NonNullable<Params>>>;
export declare function parseMessage<Params extends ParseableMessageCreateParams>(message: Message, params: Params, opts: {
logger: Logger;
}): ParsedMessage<ExtractParsedContentFromParams<Params>>;
export {};
//# sourceMappingURL=parser.d.mts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"parser.d.mts","sourceRoot":"","sources":["../src/lib/parser.ts"],"names":[],"mappings":"OAAO,KAAK,EAAE,MAAM,EAAE;OAEf,EACL,YAAY,EACZ,gBAAgB,EAChB,OAAO,EACP,YAAY,EACZ,SAAS,EACT,mBAAmB,EACpB;AAGD,KAAK,QAAQ,CAAC,CAAC,IAAI;KAAG,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC;CAAE,GAAG,EAAE,CAAC;AAE7D,KAAK,yBAAyB,GAAG,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,GAAG;IAC9D,MAAM,CAAC,EAAE,gBAAgB,GAAG,yBAAyB,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;CACnE,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG,QAAQ,CACjD,IAAI,CAAC,mBAAmB,EAAE,eAAe,CAAC,GAAG;IAC3C,aAAa,CAAC,EAAE,yBAAyB,GAAG,IAAI,CAAC;CAClD,CACF,CAAC;AAEF,MAAM,MAAM,8BAA8B,CAAC,MAAM,SAAS,4BAA4B,IACpF,MAAM,CAAC,eAAe,CAAC,SAAS;IAAE,MAAM,EAAE,yBAAyB,CAAC,MAAM,CAAC,CAAC,CAAA;CAAE,GAAG,CAAC,GAAG,IAAI,CAAC;AAE5F,MAAM,MAAM,yBAAyB,CAAC,OAAO,IAAI,gBAAgB,GAAG;IAClE,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,aAAa,CAAC,OAAO,IAAI,OAAO,GAAG;IAC7C,OAAO,EAAE,KAAK,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC;IAC5C,aAAa,EAAE,OAAO,GAAG,IAAI,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,kBAAkB,CAAC,OAAO,IAClC,CAAC,SAAS,GAAG;IAAE,aAAa,EAAE,OAAO,GAAG,IAAI,CAAA;CAAE,CAAC,GAC/C,OAAO,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;AAQrC,wBAAgB,iBAAiB,CAAC,MAAM,SAAS,4BAA4B,GAAG,IAAI,EAClF,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GACvB,aAAa,CAAC,8BAA8B,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,CAqBpE;AAED,wBAAgB,YAAY,CAAC,MAAM,SAAS,4BAA4B,EACtE,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GACvB,aAAa,CAAC,8BAA8B,CAAC,MAAM,CAAC,CAAC,CA2BvD"}
+32
View File
@@ -0,0 +1,32 @@
import type { Logger } from "../client.js";
import { ContentBlock, JSONOutputFormat, Message, OutputConfig, TextBlock, MessageCreateParams } from "../resources/messages/messages.js";
type Simplify<T> = {
[KeyType in keyof T]: T[KeyType];
} & {};
type AutoParseableOutputConfig = Omit<OutputConfig, 'format'> & {
format?: JSONOutputFormat | AutoParseableOutputFormat<any> | null;
};
export type ParseableMessageCreateParams = Simplify<Omit<MessageCreateParams, 'output_config'> & {
output_config?: AutoParseableOutputConfig | null;
}>;
export type ExtractParsedContentFromParams<Params extends ParseableMessageCreateParams> = Params['output_config'] extends {
format: AutoParseableOutputFormat<infer P>;
} ? P : null;
export type AutoParseableOutputFormat<ParsedT> = JSONOutputFormat & {
parse(content: string): ParsedT;
};
export type ParsedMessage<ParsedT> = Message & {
content: Array<ParsedContentBlock<ParsedT>>;
parsed_output: ParsedT | null;
};
export type ParsedContentBlock<ParsedT> = (TextBlock & {
parsed_output: ParsedT | null;
}) | Exclude<ContentBlock, TextBlock>;
export declare function maybeParseMessage<Params extends ParseableMessageCreateParams | null>(message: Message, params: Params, opts: {
logger: Logger;
}): ParsedMessage<ExtractParsedContentFromParams<NonNullable<Params>>>;
export declare function parseMessage<Params extends ParseableMessageCreateParams>(message: Message, params: Params, opts: {
logger: Logger;
}): ParsedMessage<ExtractParsedContentFromParams<Params>>;
export {};
//# sourceMappingURL=parser.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/lib/parser.ts"],"names":[],"mappings":"OAAO,KAAK,EAAE,MAAM,EAAE;OAEf,EACL,YAAY,EACZ,gBAAgB,EAChB,OAAO,EACP,YAAY,EACZ,SAAS,EACT,mBAAmB,EACpB;AAGD,KAAK,QAAQ,CAAC,CAAC,IAAI;KAAG,OAAO,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC;CAAE,GAAG,EAAE,CAAC;AAE7D,KAAK,yBAAyB,GAAG,IAAI,CAAC,YAAY,EAAE,QAAQ,CAAC,GAAG;IAC9D,MAAM,CAAC,EAAE,gBAAgB,GAAG,yBAAyB,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;CACnE,CAAC;AAEF,MAAM,MAAM,4BAA4B,GAAG,QAAQ,CACjD,IAAI,CAAC,mBAAmB,EAAE,eAAe,CAAC,GAAG;IAC3C,aAAa,CAAC,EAAE,yBAAyB,GAAG,IAAI,CAAC;CAClD,CACF,CAAC;AAEF,MAAM,MAAM,8BAA8B,CAAC,MAAM,SAAS,4BAA4B,IACpF,MAAM,CAAC,eAAe,CAAC,SAAS;IAAE,MAAM,EAAE,yBAAyB,CAAC,MAAM,CAAC,CAAC,CAAA;CAAE,GAAG,CAAC,GAAG,IAAI,CAAC;AAE5F,MAAM,MAAM,yBAAyB,CAAC,OAAO,IAAI,gBAAgB,GAAG;IAClE,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;CACjC,CAAC;AAEF,MAAM,MAAM,aAAa,CAAC,OAAO,IAAI,OAAO,GAAG;IAC7C,OAAO,EAAE,KAAK,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC;IAC5C,aAAa,EAAE,OAAO,GAAG,IAAI,CAAC;CAC/B,CAAC;AAEF,MAAM,MAAM,kBAAkB,CAAC,OAAO,IAClC,CAAC,SAAS,GAAG;IAAE,aAAa,EAAE,OAAO,GAAG,IAAI,CAAA;CAAE,CAAC,GAC/C,OAAO,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;AAQrC,wBAAgB,iBAAiB,CAAC,MAAM,SAAS,4BAA4B,GAAG,IAAI,EAClF,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GACvB,aAAa,CAAC,8BAA8B,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,CAqBpE;AAED,wBAAgB,YAAY,CAAC,MAAM,SAAS,4BAA4B,EACtE,OAAO,EAAE,OAAO,EAChB,MAAM,EAAE,MAAM,EACd,IAAI,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GACvB,aAAa,CAAC,8BAA8B,CAAC,MAAM,CAAC,CAAC,CA2BvD"}
+66
View File
@@ -0,0 +1,66 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.maybeParseMessage = maybeParseMessage;
exports.parseMessage = parseMessage;
const error_1 = require("../core/error.js");
function getOutputFormat(params) {
return params?.output_config?.format;
}
function maybeParseMessage(message, params, opts) {
const outputFormat = getOutputFormat(params);
if (!params || !('parse' in (outputFormat ?? {}))) {
return {
...message,
content: message.content.map((block) => {
if (block.type === 'text') {
const parsedBlock = Object.defineProperty({ ...block }, 'parsed_output', {
value: null,
enumerable: false,
});
return parsedBlock;
}
return block;
}),
parsed_output: null,
};
}
return parseMessage(message, params, opts);
}
function parseMessage(message, params, opts) {
let firstParsedOutput = null;
const content = message.content.map((block) => {
if (block.type === 'text') {
const parsedOutput = parseOutputFormat(params, block.text);
if (firstParsedOutput === null) {
firstParsedOutput = parsedOutput;
}
const parsedBlock = Object.defineProperty({ ...block }, 'parsed_output', {
value: parsedOutput,
enumerable: false,
});
return parsedBlock;
}
return block;
});
return {
...message,
content,
parsed_output: firstParsedOutput,
};
}
function parseOutputFormat(params, content) {
const outputFormat = getOutputFormat(params);
if (outputFormat?.type !== 'json_schema') {
return null;
}
try {
if ('parse' in outputFormat) {
return outputFormat.parse(content);
}
return JSON.parse(content);
}
catch (error) {
throw new error_1.AnthropicError(`Failed to parse structured output: ${error}`);
}
}
//# sourceMappingURL=parser.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/lib/parser.ts"],"names":[],"mappings":";;AA8CA,8CAyBC;AAED,oCA+BC;AAvGD,4CAA+C;AAuC/C,SAAS,eAAe,CACtB,MAA2C;IAE3C,OAAO,MAAM,EAAE,aAAa,EAAE,MAAM,CAAC;AACvC,CAAC;AAED,SAAgB,iBAAiB,CAC/B,OAAgB,EAChB,MAAc,EACd,IAAwB;IAExB,MAAM,YAAY,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IAC7C,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,OAAO,IAAI,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;QAClD,OAAO;YACL,GAAG,OAAO;YACV,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;gBACrC,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;oBAC1B,MAAM,WAAW,GAAG,MAAM,CAAC,cAAc,CAAC,EAAE,GAAG,KAAK,EAAE,EAAE,eAAe,EAAE;wBACvE,KAAK,EAAE,IAAI;wBACX,UAAU,EAAE,KAAK;qBAClB,CAA4E,CAAC;oBAE9E,OAAO,WAAW,CAAC;gBACrB,CAAC;gBACD,OAAO,KAAK,CAAC;YACf,CAAC,CAAC;YACF,aAAa,EAAE,IAAI;SACkD,CAAC;IAC1E,CAAC;IAED,OAAO,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;AAC7C,CAAC;AAED,SAAgB,YAAY,CAC1B,OAAgB,EAChB,MAAc,EACd,IAAwB;IAExB,IAAI,iBAAiB,GAAwD,IAAI,CAAC;IAElF,MAAM,OAAO,GAAsE,OAAO,CAAC,OAAO,CAAC,GAAG,CACpG,CAAC,KAAK,EAAE,EAAE;QACR,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC1B,MAAM,YAAY,GAAG,iBAAiB,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YAE3D,IAAI,iBAAiB,KAAK,IAAI,EAAE,CAAC;gBAC/B,iBAAiB,GAAG,YAAY,CAAC;YACnC,CAAC;YAED,MAAM,WAAW,GAAG,MAAM,CAAC,cAAc,CAAC,EAAE,GAAG,KAAK,EAAE,EAAE,eAAe,EAAE;gBACvE,KAAK,EAAE,YAAY;gBACnB,UAAU,EAAE,KAAK;aAClB,CAA+D,CAAC;YACjE,OAAO,WAAW,CAAC;QACrB,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CACF,CAAC;IAEF,OAAO;QACL,GAAG,OAAO;QACV,OAAO;QACP,aAAa,EAAE,iBAAiB;KACwB,CAAC;AAC7D,CAAC;AAED,SAAS,iBAAiB,CACxB,MAAc,EACd,OAAe;IAEf,MAAM,YAAY,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IAC7C,IAAI,YAAY,EAAE,IAAI,KAAK,aAAa,EAAE,CAAC;QACzC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC;QACH,IAAI,OAAO,IAAI,YAAY,EAAE,CAAC;YAC5B,OAAO,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACrC,CAAC;QAED,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,sBAAc,CAAC,sCAAsC,KAAK,EAAE,CAAC,CAAC;IAC1E,CAAC;AACH,CAAC"}
+62
View File
@@ -0,0 +1,62 @@
import { AnthropicError } from "../core/error.mjs";
function getOutputFormat(params) {
return params?.output_config?.format;
}
export function maybeParseMessage(message, params, opts) {
const outputFormat = getOutputFormat(params);
if (!params || !('parse' in (outputFormat ?? {}))) {
return {
...message,
content: message.content.map((block) => {
if (block.type === 'text') {
const parsedBlock = Object.defineProperty({ ...block }, 'parsed_output', {
value: null,
enumerable: false,
});
return parsedBlock;
}
return block;
}),
parsed_output: null,
};
}
return parseMessage(message, params, opts);
}
export function parseMessage(message, params, opts) {
let firstParsedOutput = null;
const content = message.content.map((block) => {
if (block.type === 'text') {
const parsedOutput = parseOutputFormat(params, block.text);
if (firstParsedOutput === null) {
firstParsedOutput = parsedOutput;
}
const parsedBlock = Object.defineProperty({ ...block }, 'parsed_output', {
value: parsedOutput,
enumerable: false,
});
return parsedBlock;
}
return block;
});
return {
...message,
content,
parsed_output: firstParsedOutput,
};
}
function parseOutputFormat(params, content) {
const outputFormat = getOutputFormat(params);
if (outputFormat?.type !== 'json_schema') {
return null;
}
try {
if ('parse' in outputFormat) {
return outputFormat.parse(content);
}
return JSON.parse(content);
}
catch (error) {
throw new AnthropicError(`Failed to parse structured output: ${error}`);
}
}
//# sourceMappingURL=parser.mjs.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"parser.mjs","sourceRoot":"","sources":["../src/lib/parser.ts"],"names":[],"mappings":"OACO,EAAE,cAAc,EAAE;AAuCzB,SAAS,eAAe,CACtB,MAA2C;IAE3C,OAAO,MAAM,EAAE,aAAa,EAAE,MAAM,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,OAAgB,EAChB,MAAc,EACd,IAAwB;IAExB,MAAM,YAAY,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IAC7C,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,OAAO,IAAI,CAAC,YAAY,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;QAClD,OAAO;YACL,GAAG,OAAO;YACV,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;gBACrC,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;oBAC1B,MAAM,WAAW,GAAG,MAAM,CAAC,cAAc,CAAC,EAAE,GAAG,KAAK,EAAE,EAAE,eAAe,EAAE;wBACvE,KAAK,EAAE,IAAI;wBACX,UAAU,EAAE,KAAK;qBAClB,CAA4E,CAAC;oBAE9E,OAAO,WAAW,CAAC;gBACrB,CAAC;gBACD,OAAO,KAAK,CAAC;YACf,CAAC,CAAC;YACF,aAAa,EAAE,IAAI;SACkD,CAAC;IAC1E,CAAC;IAED,OAAO,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,OAAgB,EAChB,MAAc,EACd,IAAwB;IAExB,IAAI,iBAAiB,GAAwD,IAAI,CAAC;IAElF,MAAM,OAAO,GAAsE,OAAO,CAAC,OAAO,CAAC,GAAG,CACpG,CAAC,KAAK,EAAE,EAAE;QACR,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC1B,MAAM,YAAY,GAAG,iBAAiB,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;YAE3D,IAAI,iBAAiB,KAAK,IAAI,EAAE,CAAC;gBAC/B,iBAAiB,GAAG,YAAY,CAAC;YACnC,CAAC;YAED,MAAM,WAAW,GAAG,MAAM,CAAC,cAAc,CAAC,EAAE,GAAG,KAAK,EAAE,EAAE,eAAe,EAAE;gBACvE,KAAK,EAAE,YAAY;gBACnB,UAAU,EAAE,KAAK;aAClB,CAA+D,CAAC;YACjE,OAAO,WAAW,CAAC;QACrB,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC,CACF,CAAC;IAEF,OAAO;QACL,GAAG,OAAO;QACV,OAAO;QACP,aAAa,EAAE,iBAAiB;KACwB,CAAC;AAC7D,CAAC;AAED,SAAS,iBAAiB,CACxB,MAAc,EACd,OAAe;IAEf,MAAM,YAAY,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IAC7C,IAAI,YAAY,EAAE,IAAI,KAAK,aAAa,EAAE,CAAC;QACzC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,IAAI,CAAC;QACH,IAAI,OAAO,IAAI,YAAY,EAAE,CAAC;YAC5B,OAAO,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACrC,CAAC;QAED,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,cAAc,CAAC,sCAAsC,KAAK,EAAE,CAAC,CAAC;IAC1E,CAAC;AACH,CAAC"}
+34
View File
@@ -0,0 +1,34 @@
/**
* Shared utilities for tracking SDK helper usage.
*/
import type { BetaMessageParam, BetaToolUnion } from "../resources/beta.mjs";
/**
* Symbol used to mark objects created by SDK helpers for tracking.
* The value is the helper name (e.g., 'mcpTool', 'betaZodTool').
*/
export declare const SDK_HELPER_SYMBOL: unique symbol;
type StainlessHelperObject = {
[SDK_HELPER_SYMBOL]: string;
};
export declare function wasCreatedByStainlessHelper(value: unknown): value is StainlessHelperObject;
/**
* Collects helper names from tools and messages arrays.
* Returns a deduplicated array of helper names found.
*/
export declare function collectStainlessHelpers(tools: BetaToolUnion[] | undefined, messages: BetaMessageParam[] | undefined): string[];
/**
* Builds x-stainless-helper header value from tools and messages.
* Returns an empty object if no helpers are found.
*/
export declare function stainlessHelperHeader(tools: BetaToolUnion[] | undefined, messages: BetaMessageParam[] | undefined): {
'x-stainless-helper'?: string;
};
/**
* Builds x-stainless-helper header value from a file object.
* Returns an empty object if the file is not marked with a helper.
*/
export declare function stainlessHelperHeaderFromFile(file: unknown): {
'x-stainless-helper'?: string;
};
export {};
//# sourceMappingURL=stainless-helper-header.d.mts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"stainless-helper-header.d.mts","sourceRoot":"","sources":["../src/lib/stainless-helper-header.ts"],"names":[],"mappings":"AAAA;;GAEG;OAEI,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE;AAE/C;;;GAGG;AACH,eAAO,MAAM,iBAAiB,eAA0C,CAAC;AAEzE,KAAK,qBAAqB,GAAG;IAAE,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7D,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,qBAAqB,CAE1F;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,aAAa,EAAE,GAAG,SAAS,EAClC,QAAQ,EAAE,gBAAgB,EAAE,GAAG,SAAS,GACvC,MAAM,EAAE,CA8BV;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,aAAa,EAAE,GAAG,SAAS,EAClC,QAAQ,EAAE,gBAAgB,EAAE,GAAG,SAAS,GACvC;IAAE,oBAAoB,CAAC,EAAE,MAAM,CAAA;CAAE,CAInC;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,OAAO,GAAG;IAAE,oBAAoB,CAAC,EAAE,MAAM,CAAA;CAAE,CAK9F"}
+34
View File
@@ -0,0 +1,34 @@
/**
* Shared utilities for tracking SDK helper usage.
*/
import type { BetaMessageParam, BetaToolUnion } from "../resources/beta.js";
/**
* Symbol used to mark objects created by SDK helpers for tracking.
* The value is the helper name (e.g., 'mcpTool', 'betaZodTool').
*/
export declare const SDK_HELPER_SYMBOL: unique symbol;
type StainlessHelperObject = {
[SDK_HELPER_SYMBOL]: string;
};
export declare function wasCreatedByStainlessHelper(value: unknown): value is StainlessHelperObject;
/**
* Collects helper names from tools and messages arrays.
* Returns a deduplicated array of helper names found.
*/
export declare function collectStainlessHelpers(tools: BetaToolUnion[] | undefined, messages: BetaMessageParam[] | undefined): string[];
/**
* Builds x-stainless-helper header value from tools and messages.
* Returns an empty object if no helpers are found.
*/
export declare function stainlessHelperHeader(tools: BetaToolUnion[] | undefined, messages: BetaMessageParam[] | undefined): {
'x-stainless-helper'?: string;
};
/**
* Builds x-stainless-helper header value from a file object.
* Returns an empty object if the file is not marked with a helper.
*/
export declare function stainlessHelperHeaderFromFile(file: unknown): {
'x-stainless-helper'?: string;
};
export {};
//# sourceMappingURL=stainless-helper-header.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"stainless-helper-header.d.ts","sourceRoot":"","sources":["../src/lib/stainless-helper-header.ts"],"names":[],"mappings":"AAAA;;GAEG;OAEI,KAAK,EAAE,gBAAgB,EAAE,aAAa,EAAE;AAE/C;;;GAGG;AACH,eAAO,MAAM,iBAAiB,eAA0C,CAAC;AAEzE,KAAK,qBAAqB,GAAG;IAAE,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7D,wBAAgB,2BAA2B,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,qBAAqB,CAE1F;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,aAAa,EAAE,GAAG,SAAS,EAClC,QAAQ,EAAE,gBAAgB,EAAE,GAAG,SAAS,GACvC,MAAM,EAAE,CA8BV;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,aAAa,EAAE,GAAG,SAAS,EAClC,QAAQ,EAAE,gBAAgB,EAAE,GAAG,SAAS,GACvC;IAAE,oBAAoB,CAAC,EAAE,MAAM,CAAA;CAAE,CAInC;AAED;;;GAGG;AACH,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,OAAO,GAAG;IAAE,oBAAoB,CAAC,EAAE,MAAM,CAAA;CAAE,CAK9F"}
+70
View File
@@ -0,0 +1,70 @@
"use strict";
/**
* Shared utilities for tracking SDK helper usage.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.SDK_HELPER_SYMBOL = void 0;
exports.wasCreatedByStainlessHelper = wasCreatedByStainlessHelper;
exports.collectStainlessHelpers = collectStainlessHelpers;
exports.stainlessHelperHeader = stainlessHelperHeader;
exports.stainlessHelperHeaderFromFile = stainlessHelperHeaderFromFile;
/**
* Symbol used to mark objects created by SDK helpers for tracking.
* The value is the helper name (e.g., 'mcpTool', 'betaZodTool').
*/
exports.SDK_HELPER_SYMBOL = Symbol('anthropic.sdk.stainlessHelper');
function wasCreatedByStainlessHelper(value) {
return typeof value === 'object' && value !== null && exports.SDK_HELPER_SYMBOL in value;
}
/**
* Collects helper names from tools and messages arrays.
* Returns a deduplicated array of helper names found.
*/
function collectStainlessHelpers(tools, messages) {
const helpers = new Set();
// Collect from tools
if (tools) {
for (const tool of tools) {
if (wasCreatedByStainlessHelper(tool)) {
helpers.add(tool[exports.SDK_HELPER_SYMBOL]);
}
}
}
// Collect from messages and their content blocks
if (messages) {
for (const message of messages) {
if (wasCreatedByStainlessHelper(message)) {
helpers.add(message[exports.SDK_HELPER_SYMBOL]);
}
if (Array.isArray(message.content)) {
for (const block of message.content) {
if (wasCreatedByStainlessHelper(block)) {
helpers.add(block[exports.SDK_HELPER_SYMBOL]);
}
}
}
}
}
return Array.from(helpers);
}
/**
* Builds x-stainless-helper header value from tools and messages.
* Returns an empty object if no helpers are found.
*/
function stainlessHelperHeader(tools, messages) {
const helpers = collectStainlessHelpers(tools, messages);
if (helpers.length === 0)
return {};
return { 'x-stainless-helper': helpers.join(', ') };
}
/**
* Builds x-stainless-helper header value from a file object.
* Returns an empty object if the file is not marked with a helper.
*/
function stainlessHelperHeaderFromFile(file) {
if (wasCreatedByStainlessHelper(file)) {
return { 'x-stainless-helper': file[exports.SDK_HELPER_SYMBOL] };
}
return {};
}
//# sourceMappingURL=stainless-helper-header.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"stainless-helper-header.js","sourceRoot":"","sources":["../src/lib/stainless-helper-header.ts"],"names":[],"mappings":";AAAA;;GAEG;;;AAYH,kEAEC;AAMD,0DAiCC;AAMD,sDAOC;AAMD,sEAKC;AAzED;;;GAGG;AACU,QAAA,iBAAiB,GAAG,MAAM,CAAC,+BAA+B,CAAC,CAAC;AAIzE,SAAgB,2BAA2B,CAAC,KAAc;IACxD,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,yBAAiB,IAAI,KAAK,CAAC;AACnF,CAAC;AAED;;;GAGG;AACH,SAAgB,uBAAuB,CACrC,KAAkC,EAClC,QAAwC;IAExC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAElC,qBAAqB;IACrB,IAAI,KAAK,EAAE,CAAC;QACV,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,2BAA2B,CAAC,IAAI,CAAC,EAAE,CAAC;gBACtC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,yBAAiB,CAAC,CAAC,CAAC;YACvC,CAAC;QACH,CAAC;IACH,CAAC;IAED,iDAAiD;IACjD,IAAI,QAAQ,EAAE,CAAC;QACb,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,IAAI,2BAA2B,CAAC,OAAO,CAAC,EAAE,CAAC;gBACzC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,yBAAiB,CAAC,CAAC,CAAC;YAC1C,CAAC;YAED,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;gBACnC,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;oBACpC,IAAI,2BAA2B,CAAC,KAAK,CAAC,EAAE,CAAC;wBACvC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,yBAAiB,CAAC,CAAC,CAAC;oBACxC,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AAC7B,CAAC;AAED;;;GAGG;AACH,SAAgB,qBAAqB,CACnC,KAAkC,EAClC,QAAwC;IAExC,MAAM,OAAO,GAAG,uBAAuB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACzD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACpC,OAAO,EAAE,oBAAoB,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AACtD,CAAC;AAED;;;GAGG;AACH,SAAgB,6BAA6B,CAAC,IAAa;IACzD,IAAI,2BAA2B,CAAC,IAAI,CAAC,EAAE,CAAC;QACtC,OAAO,EAAE,oBAAoB,EAAE,IAAI,CAAC,yBAAiB,CAAC,EAAE,CAAC;IAC3D,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC"}
+63
View File
@@ -0,0 +1,63 @@
/**
* Shared utilities for tracking SDK helper usage.
*/
/**
* Symbol used to mark objects created by SDK helpers for tracking.
* The value is the helper name (e.g., 'mcpTool', 'betaZodTool').
*/
export const SDK_HELPER_SYMBOL = Symbol('anthropic.sdk.stainlessHelper');
export function wasCreatedByStainlessHelper(value) {
return typeof value === 'object' && value !== null && SDK_HELPER_SYMBOL in value;
}
/**
* Collects helper names from tools and messages arrays.
* Returns a deduplicated array of helper names found.
*/
export function collectStainlessHelpers(tools, messages) {
const helpers = new Set();
// Collect from tools
if (tools) {
for (const tool of tools) {
if (wasCreatedByStainlessHelper(tool)) {
helpers.add(tool[SDK_HELPER_SYMBOL]);
}
}
}
// Collect from messages and their content blocks
if (messages) {
for (const message of messages) {
if (wasCreatedByStainlessHelper(message)) {
helpers.add(message[SDK_HELPER_SYMBOL]);
}
if (Array.isArray(message.content)) {
for (const block of message.content) {
if (wasCreatedByStainlessHelper(block)) {
helpers.add(block[SDK_HELPER_SYMBOL]);
}
}
}
}
}
return Array.from(helpers);
}
/**
* Builds x-stainless-helper header value from tools and messages.
* Returns an empty object if no helpers are found.
*/
export function stainlessHelperHeader(tools, messages) {
const helpers = collectStainlessHelpers(tools, messages);
if (helpers.length === 0)
return {};
return { 'x-stainless-helper': helpers.join(', ') };
}
/**
* Builds x-stainless-helper header value from a file object.
* Returns an empty object if the file is not marked with a helper.
*/
export function stainlessHelperHeaderFromFile(file) {
if (wasCreatedByStainlessHelper(file)) {
return { 'x-stainless-helper': file[SDK_HELPER_SYMBOL] };
}
return {};
}
//# sourceMappingURL=stainless-helper-header.mjs.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"stainless-helper-header.mjs","sourceRoot":"","sources":["../src/lib/stainless-helper-header.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,MAAM,CAAC,+BAA+B,CAAC,CAAC;AAIzE,MAAM,UAAU,2BAA2B,CAAC,KAAc;IACxD,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,iBAAiB,IAAI,KAAK,CAAC;AACnF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,uBAAuB,CACrC,KAAkC,EAClC,QAAwC;IAExC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;IAElC,qBAAqB;IACrB,IAAI,KAAK,EAAE,CAAC;QACV,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,IAAI,2BAA2B,CAAC,IAAI,CAAC,EAAE,CAAC;gBACtC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC;YACvC,CAAC;QACH,CAAC;IACH,CAAC;IAED,iDAAiD;IACjD,IAAI,QAAQ,EAAE,CAAC;QACb,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,IAAI,2BAA2B,CAAC,OAAO,CAAC,EAAE,CAAC;gBACzC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC;YAC1C,CAAC;YAED,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;gBACnC,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;oBACpC,IAAI,2BAA2B,CAAC,KAAK,CAAC,EAAE,CAAC;wBACvC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC,CAAC;oBACxC,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AAC7B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CACnC,KAAkC,EAClC,QAAwC;IAExC,MAAM,OAAO,GAAG,uBAAuB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACzD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACpC,OAAO,EAAE,oBAAoB,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AACtD,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,6BAA6B,CAAC,IAAa;IACzD,IAAI,2BAA2B,CAAC,IAAI,CAAC,EAAE,CAAC;QACtC,OAAO,EAAE,oBAAoB,EAAE,IAAI,CAAC,iBAAiB,CAAC,EAAE,CAAC;IAC3D,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC"}
+16
View File
@@ -0,0 +1,16 @@
import { BetaMemoryTool20250818, BetaTool, BetaToolBash20241022, BetaToolBash20250124, BetaToolComputerUse20241022, BetaToolComputerUse20250124, BetaToolComputerUse20251124, BetaToolResultContentBlockParam, BetaToolTextEditor20241022, BetaToolTextEditor20250124, BetaToolTextEditor20250429, BetaToolTextEditor20250728, BetaToolUseBlock } from "../../resources/beta.mjs";
export type Promisable<T> = T | Promise<T>;
/**
* Tool types that can be implemented on the client.
* Excludes server-side tools like code execution, web search, and MCP toolsets.
*/
export type BetaClientRunnableToolType = BetaTool | BetaMemoryTool20250818 | BetaToolBash20241022 | BetaToolBash20250124 | BetaToolComputerUse20241022 | BetaToolComputerUse20250124 | BetaToolComputerUse20251124 | BetaToolTextEditor20241022 | BetaToolTextEditor20250124 | BetaToolTextEditor20250429 | BetaToolTextEditor20250728;
export type BetaToolRunContext = {
toolUseBlock: BetaToolUseBlock;
signal?: AbortSignal | null | undefined;
};
export type BetaRunnableTool<Input = any> = BetaClientRunnableToolType & {
run: (args: Input, context?: BetaToolRunContext) => Promisable<string | Array<BetaToolResultContentBlockParam>>;
parse: (content: unknown) => Input;
};
//# sourceMappingURL=BetaRunnableTool.d.mts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"BetaRunnableTool.d.mts","sourceRoot":"","sources":["../../src/lib/tools/BetaRunnableTool.ts"],"names":[],"mappings":"OAAO,EACL,sBAAsB,EACtB,QAAQ,EACR,oBAAoB,EACpB,oBAAoB,EACpB,2BAA2B,EAC3B,2BAA2B,EAC3B,2BAA2B,EAC3B,+BAA+B,EAC/B,0BAA0B,EAC1B,0BAA0B,EAC1B,0BAA0B,EAC1B,0BAA0B,EAC1B,gBAAgB,EACjB;AAED,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AAE3C;;;GAGG;AACH,MAAM,MAAM,0BAA0B,GAClC,QAAQ,GACR,sBAAsB,GACtB,oBAAoB,GACpB,oBAAoB,GACpB,2BAA2B,GAC3B,2BAA2B,GAC3B,2BAA2B,GAC3B,0BAA0B,GAC1B,0BAA0B,GAC1B,0BAA0B,GAC1B,0BAA0B,CAAC;AAE/B,MAAM,MAAM,kBAAkB,GAAG;IAC/B,YAAY,EAAE,gBAAgB,CAAC;IAC/B,MAAM,CAAC,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,CAAC;CACzC,CAAC;AAIF,MAAM,MAAM,gBAAgB,CAAC,KAAK,GAAG,GAAG,IAAI,0BAA0B,GAAG;IACvE,GAAG,EAAE,CACH,IAAI,EAAE,KAAK,EACX,OAAO,CAAC,EAAE,kBAAkB,KACzB,UAAU,CAAC,MAAM,GAAG,KAAK,CAAC,+BAA+B,CAAC,CAAC,CAAC;IACjE,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,KAAK,CAAC;CACpC,CAAC"}
+16
View File
@@ -0,0 +1,16 @@
import { BetaMemoryTool20250818, BetaTool, BetaToolBash20241022, BetaToolBash20250124, BetaToolComputerUse20241022, BetaToolComputerUse20250124, BetaToolComputerUse20251124, BetaToolResultContentBlockParam, BetaToolTextEditor20241022, BetaToolTextEditor20250124, BetaToolTextEditor20250429, BetaToolTextEditor20250728, BetaToolUseBlock } from "../../resources/beta.js";
export type Promisable<T> = T | Promise<T>;
/**
* Tool types that can be implemented on the client.
* Excludes server-side tools like code execution, web search, and MCP toolsets.
*/
export type BetaClientRunnableToolType = BetaTool | BetaMemoryTool20250818 | BetaToolBash20241022 | BetaToolBash20250124 | BetaToolComputerUse20241022 | BetaToolComputerUse20250124 | BetaToolComputerUse20251124 | BetaToolTextEditor20241022 | BetaToolTextEditor20250124 | BetaToolTextEditor20250429 | BetaToolTextEditor20250728;
export type BetaToolRunContext = {
toolUseBlock: BetaToolUseBlock;
signal?: AbortSignal | null | undefined;
};
export type BetaRunnableTool<Input = any> = BetaClientRunnableToolType & {
run: (args: Input, context?: BetaToolRunContext) => Promisable<string | Array<BetaToolResultContentBlockParam>>;
parse: (content: unknown) => Input;
};
//# sourceMappingURL=BetaRunnableTool.d.ts.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"file":"BetaRunnableTool.d.ts","sourceRoot":"","sources":["../../src/lib/tools/BetaRunnableTool.ts"],"names":[],"mappings":"OAAO,EACL,sBAAsB,EACtB,QAAQ,EACR,oBAAoB,EACpB,oBAAoB,EACpB,2BAA2B,EAC3B,2BAA2B,EAC3B,2BAA2B,EAC3B,+BAA+B,EAC/B,0BAA0B,EAC1B,0BAA0B,EAC1B,0BAA0B,EAC1B,0BAA0B,EAC1B,gBAAgB,EACjB;AAED,MAAM,MAAM,UAAU,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AAE3C;;;GAGG;AACH,MAAM,MAAM,0BAA0B,GAClC,QAAQ,GACR,sBAAsB,GACtB,oBAAoB,GACpB,oBAAoB,GACpB,2BAA2B,GAC3B,2BAA2B,GAC3B,2BAA2B,GAC3B,0BAA0B,GAC1B,0BAA0B,GAC1B,0BAA0B,GAC1B,0BAA0B,CAAC;AAE/B,MAAM,MAAM,kBAAkB,GAAG;IAC/B,YAAY,EAAE,gBAAgB,CAAC;IAC/B,MAAM,CAAC,EAAE,WAAW,GAAG,IAAI,GAAG,SAAS,CAAC;CACzC,CAAC;AAIF,MAAM,MAAM,gBAAgB,CAAC,KAAK,GAAG,GAAG,IAAI,0BAA0B,GAAG;IACvE,GAAG,EAAE,CACH,IAAI,EAAE,KAAK,EACX,OAAO,CAAC,EAAE,kBAAkB,KACzB,UAAU,CAAC,MAAM,GAAG,KAAK,CAAC,+BAA+B,CAAC,CAAC,CAAC;IACjE,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,KAAK,CAAC;CACpC,CAAC"}

Some files were not shown because too many files have changed in this diff Show More