@code-yeongyu/senpi-ai 2026.10.1-3 → 2026.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -3
- package/dist/api/anthropic-messages.js +152 -28
- package/dist/api/anthropic-tool-references.d.ts +14 -0
- package/dist/api/anthropic-tool-references.js +107 -0
- package/dist/api/constrained-sampling.d.ts +8 -2
- package/dist/api/constrained-sampling.js +19 -8
- package/dist/api/openai-responses-shared.js +7 -5
- package/dist/api-registry.d.ts +5 -1
- package/dist/api-registry.js +16 -6
- package/dist/auth/oauth/anthropic.js +52 -1
- package/dist/compat.js +4 -1
- package/dist/env-api-keys.d.ts +5 -0
- package/dist/env-api-keys.js +5 -0
- package/dist/providers/anthropic.js +24 -2
- package/dist/providers/data/.manifest.json +1 -1
- package/dist/providers/data/amazon-bedrock.json +1 -1
- package/dist/providers/data/opencode.json +1 -1
- package/dist/providers/data/opengateway.json +1 -1
- package/dist/providers/data/openrouter.json +1 -1
- package/dist/providers/data/radius.json +1 -1
- package/dist/providers/data/together.json +1 -1
- package/dist/providers/data/venice.json +1 -1
- package/dist/providers/opengateway-catalog.d.ts +43 -0
- package/dist/providers/opengateway-catalog.js +151 -0
- package/dist/providers/opengateway-refresh.d.ts +20 -0
- package/dist/providers/opengateway-refresh.js +129 -0
- package/dist/providers/opengateway.js +13 -2
- package/dist/utils/oauth-page.js +1 -1
- package/dist/utils/openai-input-cap.d.ts +4 -0
- package/dist/utils/openai-input-cap.js +18 -0
- package/dist/utils/overflow.js +2 -1
- package/package.json +6 -2
package/dist/api-registry.js
CHANGED
|
@@ -32,7 +32,7 @@ function wrapStreamSimple(api, streamSimple) {
|
|
|
32
32
|
return streamSimple(model, context, options);
|
|
33
33
|
};
|
|
34
34
|
}
|
|
35
|
-
function createRegisteredProvider(provider, sourceId) {
|
|
35
|
+
function createRegisteredProvider(provider, sourceId, survivesClear) {
|
|
36
36
|
return {
|
|
37
37
|
provider: {
|
|
38
38
|
api: provider.api,
|
|
@@ -40,12 +40,13 @@ function createRegisteredProvider(provider, sourceId) {
|
|
|
40
40
|
streamSimple: wrapStreamSimple(provider.api, provider.streamSimple),
|
|
41
41
|
},
|
|
42
42
|
sourceId,
|
|
43
|
+
...(survivesClear ? { survivesClear } : {}),
|
|
43
44
|
};
|
|
44
45
|
}
|
|
45
|
-
export function registerApiProvider(provider, sourceId) {
|
|
46
|
+
export function registerApiProvider(provider, sourceId, options) {
|
|
46
47
|
const scope = getActiveProviderScope();
|
|
47
48
|
const registry = scope ? scope.overlay : apiProviderRegistry;
|
|
48
|
-
registry.set(provider.api, createRegisteredProvider(provider, sourceId));
|
|
49
|
+
registry.set(provider.api, createRegisteredProvider(provider, sourceId, options?.survivesClear));
|
|
49
50
|
}
|
|
50
51
|
/** Registers a builtin once, retaining its immutable identity for active scopes. */
|
|
51
52
|
export function registerBuiltinApiProvider(provider) {
|
|
@@ -85,12 +86,21 @@ export function unregisterApiProviders(sourceId) {
|
|
|
85
86
|
const scope = getActiveProviderScope();
|
|
86
87
|
const registry = scope ? scope.overlay : apiProviderRegistry;
|
|
87
88
|
for (const [api, entry] of registry.entries()) {
|
|
88
|
-
if (entry.sourceId
|
|
89
|
-
|
|
89
|
+
if (entry.sourceId !== sourceId)
|
|
90
|
+
continue;
|
|
91
|
+
registry.delete(api);
|
|
92
|
+
// A scope overlay already falls back to the builtin; the global registry must get it back.
|
|
93
|
+
const builtin = builtinApiProviderRegistry.get(api);
|
|
94
|
+
if (!scope && builtin)
|
|
95
|
+
registry.set(api, builtin);
|
|
90
96
|
}
|
|
91
97
|
}
|
|
92
98
|
export function clearApiProviders() {
|
|
93
99
|
const scope = getActiveProviderScope();
|
|
94
|
-
|
|
100
|
+
const registry = scope ? scope.overlay : apiProviderRegistry;
|
|
101
|
+
for (const [api, entry] of registry) {
|
|
102
|
+
if (!entry.survivesClear)
|
|
103
|
+
registry.delete(api);
|
|
104
|
+
}
|
|
95
105
|
}
|
|
96
106
|
//# sourceMappingURL=api-registry.js.map
|
|
@@ -26,6 +26,10 @@ const CALLBACK_HOST = getProviderEnvValue("PI_OAUTH_CALLBACK_HOST") || "127.0.0.
|
|
|
26
26
|
* redirect on this machine with "State mismatch".
|
|
27
27
|
*/
|
|
28
28
|
const LOGIN_IDLE_TIMEOUT_MS = 10 * 60 * 1000;
|
|
29
|
+
/** Anthropic page that shows the authorization code to copy (browser on another machine). */
|
|
30
|
+
const COPY_CODE_REDIRECT_URI = "https://platform.claude.com/oauth/code/callback";
|
|
31
|
+
const ANTHROPIC_BROWSER_LOGIN_METHOD = "browser";
|
|
32
|
+
const ANTHROPIC_COPY_CODE_LOGIN_METHOD = "copy_code";
|
|
29
33
|
const SCOPES = "org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload";
|
|
30
34
|
async function getNodeApis() {
|
|
31
35
|
if (nodeApis)
|
|
@@ -192,6 +196,37 @@ function loginTimedOutMessage() {
|
|
|
192
196
|
const minutes = Math.round(LOGIN_IDLE_TIMEOUT_MS / 60_000);
|
|
193
197
|
return `Anthropic login timed out after ${minutes} minutes without a browser callback or a pasted redirect URL. Run the login again.`;
|
|
194
198
|
}
|
|
199
|
+
async function loginAnthropicCopyCode(interaction) {
|
|
200
|
+
const { verifier, challenge } = await generatePKCE();
|
|
201
|
+
const authParams = new URLSearchParams({
|
|
202
|
+
code: "true",
|
|
203
|
+
client_id: CLIENT_ID,
|
|
204
|
+
response_type: "code",
|
|
205
|
+
redirect_uri: COPY_CODE_REDIRECT_URI,
|
|
206
|
+
scope: SCOPES,
|
|
207
|
+
code_challenge: challenge,
|
|
208
|
+
code_challenge_method: "S256",
|
|
209
|
+
state: verifier,
|
|
210
|
+
});
|
|
211
|
+
interaction.notify({
|
|
212
|
+
type: "auth_url",
|
|
213
|
+
url: `${AUTHORIZE_URL}?${authParams.toString()}`,
|
|
214
|
+
instructions: "Complete login in your browser, then copy the code Anthropic shows and paste it here.",
|
|
215
|
+
});
|
|
216
|
+
const input = await interaction.prompt({
|
|
217
|
+
type: "manual_code",
|
|
218
|
+
message: "Paste the code Anthropic shows after you sign in:",
|
|
219
|
+
placeholder: "code#state",
|
|
220
|
+
signal: interaction.signal,
|
|
221
|
+
});
|
|
222
|
+
const parsed = parseAuthorizationInput(input);
|
|
223
|
+
if (parsed.state && parsed.state !== verifier)
|
|
224
|
+
throw new Error("OAuth state mismatch");
|
|
225
|
+
if (!parsed.code)
|
|
226
|
+
throw new Error("Missing authorization code");
|
|
227
|
+
interaction.notify({ type: "progress", message: "Exchanging authorization code for tokens..." });
|
|
228
|
+
return await exchangeAuthorizationCode(parsed.code, parsed.state ?? verifier, verifier, COPY_CODE_REDIRECT_URI, interaction.signal);
|
|
229
|
+
}
|
|
195
230
|
/**
|
|
196
231
|
* Refresh Anthropic OAuth token
|
|
197
232
|
*/
|
|
@@ -224,7 +259,23 @@ async function refreshAnthropicToken(refreshToken, signal) {
|
|
|
224
259
|
export const anthropicOAuth = {
|
|
225
260
|
name: "Anthropic (Claude Pro/Max)",
|
|
226
261
|
isSubscription: true,
|
|
227
|
-
login
|
|
262
|
+
async login(interaction) {
|
|
263
|
+
const method = await interaction.prompt({
|
|
264
|
+
type: "select",
|
|
265
|
+
message: "Select Anthropic login method:",
|
|
266
|
+
options: [
|
|
267
|
+
{ id: ANTHROPIC_BROWSER_LOGIN_METHOD, label: "Browser login (default)" },
|
|
268
|
+
{ id: ANTHROPIC_COPY_CODE_LOGIN_METHOD, label: "Copy code login (headless)" },
|
|
269
|
+
],
|
|
270
|
+
});
|
|
271
|
+
if (method === ANTHROPIC_COPY_CODE_LOGIN_METHOD) {
|
|
272
|
+
return loginAnthropicCopyCode(interaction);
|
|
273
|
+
}
|
|
274
|
+
if (method !== ANTHROPIC_BROWSER_LOGIN_METHOD) {
|
|
275
|
+
throw new Error(`Unknown Anthropic login method: ${method}`);
|
|
276
|
+
}
|
|
277
|
+
return loginAnthropic(interaction);
|
|
278
|
+
},
|
|
228
279
|
refresh: (credential, signal) => refreshAnthropicToken(credential.refresh, signal),
|
|
229
280
|
async toAuth(credential) {
|
|
230
281
|
return { apiKey: credential.access };
|
package/dist/compat.js
CHANGED
|
@@ -58,7 +58,10 @@ import { clearApiProviders, getApiProvider, registerApiProvider, registerBuiltin
|
|
|
58
58
|
export function registerFauxProvider(options = {}) {
|
|
59
59
|
const core = createFauxCore(options);
|
|
60
60
|
const sourceId = `faux-provider-${Math.random().toString(36).slice(2, 10)}`;
|
|
61
|
-
|
|
61
|
+
// A session reload runs resetApiProviders(); the caller's provider must still answer afterwards, in or out of a provider scope.
|
|
62
|
+
registerApiProvider({ api: core.api, stream: core.stream, streamSimple: core.streamSimple }, sourceId, {
|
|
63
|
+
survivesClear: true,
|
|
64
|
+
});
|
|
62
65
|
return {
|
|
63
66
|
api: core.api,
|
|
64
67
|
models: core.models,
|
package/dist/env-api-keys.d.ts
CHANGED
|
@@ -2,6 +2,11 @@ import type { KnownProvider, ProviderEnv } from "./types.ts";
|
|
|
2
2
|
export declare const ANTHROPIC_AUTH_TOKEN_ENV = "ANTHROPIC_AUTH_TOKEN";
|
|
3
3
|
export declare const ANTHROPIC_OAUTH_TOKEN_ENV = "ANTHROPIC_OAUTH_TOKEN";
|
|
4
4
|
export declare const ANTHROPIC_API_KEY_ENV = "ANTHROPIC_API_KEY";
|
|
5
|
+
export declare const ANTHROPIC_FEDERATION_RULE_ID_ENV = "ANTHROPIC_FEDERATION_RULE_ID";
|
|
6
|
+
export declare const ANTHROPIC_ORGANIZATION_ID_ENV = "ANTHROPIC_ORGANIZATION_ID";
|
|
7
|
+
export declare const ANTHROPIC_SERVICE_ACCOUNT_ID_ENV = "ANTHROPIC_SERVICE_ACCOUNT_ID";
|
|
8
|
+
export declare const ANTHROPIC_IDENTITY_TOKEN_FILE_ENV = "ANTHROPIC_IDENTITY_TOKEN_FILE";
|
|
9
|
+
export declare const ANTHROPIC_WORKSPACE_ID_ENV = "ANTHROPIC_WORKSPACE_ID";
|
|
5
10
|
/**
|
|
6
11
|
* The canonical provider-id to API-key env-var mapping. Exported so numbered
|
|
7
12
|
* env credential slots (`OPENAI_API_KEY_2`, ...) can generalize over the same
|
package/dist/env-api-keys.js
CHANGED
|
@@ -30,6 +30,11 @@ import { getProviderEnvValue } from "./utils/provider-env.js";
|
|
|
30
30
|
export const ANTHROPIC_AUTH_TOKEN_ENV = "ANTHROPIC_AUTH_TOKEN";
|
|
31
31
|
export const ANTHROPIC_OAUTH_TOKEN_ENV = "ANTHROPIC_OAUTH_TOKEN";
|
|
32
32
|
export const ANTHROPIC_API_KEY_ENV = "ANTHROPIC_API_KEY";
|
|
33
|
+
export const ANTHROPIC_FEDERATION_RULE_ID_ENV = "ANTHROPIC_FEDERATION_RULE_ID";
|
|
34
|
+
export const ANTHROPIC_ORGANIZATION_ID_ENV = "ANTHROPIC_ORGANIZATION_ID";
|
|
35
|
+
export const ANTHROPIC_SERVICE_ACCOUNT_ID_ENV = "ANTHROPIC_SERVICE_ACCOUNT_ID";
|
|
36
|
+
export const ANTHROPIC_IDENTITY_TOKEN_FILE_ENV = "ANTHROPIC_IDENTITY_TOKEN_FILE";
|
|
37
|
+
export const ANTHROPIC_WORKSPACE_ID_ENV = "ANTHROPIC_WORKSPACE_ID";
|
|
33
38
|
let cachedVertexAdcCredentialsExists = null;
|
|
34
39
|
function hasVertexAdcCredentials(env) {
|
|
35
40
|
const explicitCredentialsPath = env?.GOOGLE_APPLICATION_CREDENTIALS;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { anthropicMessagesApi } from "../api/anthropic-messages.lazy.js";
|
|
2
2
|
import { lazyOAuth } from "../auth/helpers.js";
|
|
3
3
|
import { loadAnthropicOAuth } from "../auth/oauth/load.js";
|
|
4
|
-
import { ANTHROPIC_API_KEY_ENV, ANTHROPIC_AUTH_TOKEN_ENV, ANTHROPIC_OAUTH_TOKEN_ENV } from "../env-api-keys.js";
|
|
4
|
+
import { ANTHROPIC_API_KEY_ENV, ANTHROPIC_AUTH_TOKEN_ENV, ANTHROPIC_FEDERATION_RULE_ID_ENV, ANTHROPIC_IDENTITY_TOKEN_FILE_ENV, ANTHROPIC_OAUTH_TOKEN_ENV, ANTHROPIC_ORGANIZATION_ID_ENV, ANTHROPIC_SERVICE_ACCOUNT_ID_ENV, ANTHROPIC_WORKSPACE_ID_ENV, } from "../env-api-keys.js";
|
|
5
5
|
import { createProvider } from "../models.js";
|
|
6
6
|
import { ANTHROPIC_MODELS } from "./anthropic.models.js";
|
|
7
7
|
const anthropicStreams = anthropicMessagesApi();
|
|
@@ -35,7 +35,29 @@ function anthropicApiKeyAuth() {
|
|
|
35
35
|
if (apiKey)
|
|
36
36
|
return { auth: { apiKey }, source: envVar };
|
|
37
37
|
}
|
|
38
|
-
|
|
38
|
+
// Workload identity federation: the Anthropic SDK exchanges the identity
|
|
39
|
+
// token for a short-lived access token and refreshes it itself. Last in
|
|
40
|
+
// line so keys and ANTHROPIC_AUTH_TOKEN keep winning, as in the SDK. The
|
|
41
|
+
// ids are provider config rather than auth, so they travel in `env`.
|
|
42
|
+
const federation = {};
|
|
43
|
+
for (const envVar of [
|
|
44
|
+
ANTHROPIC_FEDERATION_RULE_ID_ENV,
|
|
45
|
+
ANTHROPIC_ORGANIZATION_ID_ENV,
|
|
46
|
+
ANTHROPIC_IDENTITY_TOKEN_FILE_ENV,
|
|
47
|
+
]) {
|
|
48
|
+
const value = await ctx.env(envVar);
|
|
49
|
+
signal.throwIfAborted();
|
|
50
|
+
if (!value)
|
|
51
|
+
return undefined;
|
|
52
|
+
federation[envVar] = value;
|
|
53
|
+
}
|
|
54
|
+
for (const envVar of [ANTHROPIC_SERVICE_ACCOUNT_ID_ENV, ANTHROPIC_WORKSPACE_ID_ENV]) {
|
|
55
|
+
const value = await ctx.env(envVar);
|
|
56
|
+
signal.throwIfAborted();
|
|
57
|
+
if (value)
|
|
58
|
+
federation[envVar] = value;
|
|
59
|
+
}
|
|
60
|
+
return { auth: {}, env: federation, source: "workload identity federation" };
|
|
39
61
|
},
|
|
40
62
|
};
|
|
41
63
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"schemaVersion":6,"generatedAt":"2026-10-
|
|
1
|
+
{"schemaVersion":6,"generatedAt":"2026-10-02T22:50:53.634Z","structureHash":"82b3cba4eb1c5d62599d476cc6b56a775a9f444ec8db80e4bd3a8c0a2740d63c","files":{"alibaba-token-plan.json":"16b3c6c78d72c73ca34a1e79ce71c0cf36ba409d02e83a17396ce0949a1d5363","amazon-bedrock.json":"657f75bb65b8a3bc07019f2f2e71e5d0e01f5da49756d24ee9b2f3d06f1fb4c0","ant-ling.json":"628feab3ef6c7d80ce96f75999804e1d9f05fec3851e5302e30fff3b69db90f6","anthropic.json":"2c0c4389cd71d21d4553075f399922892078192b0490fead423544e360af1a43","azure-openai-responses.json":"a3e52bd78f0220aa32bce519e13955b8e6e567860416343844cbe19902f4a682","bai.json":"7e6861aaf27d8af34e1771c6092602194fcec0a0046e6adfab9c3ee54d32b99f","baseten.json":"b04d1423d6958bec857ffccc36726f091ec1a8f3a09cf0836b64cc94b0ee697d","cerebras.json":"3eccb58c5de3d0dc7ba49dc6e477ba29f00cb3150ce0a35cc90d59e6b5ab368e","chatgpt-subscription.json":"08f365952647594f5b07337666255f8510ef2e09bfb704530843b63121ed07f4","cloudflare-ai-gateway.json":"294a902cc45474824f71a6cb288ba0f8764401f0ae9b9b8f9bcab76f8800d503","cloudflare-workers-ai.json":"b9f76cc83756a05920a8a7046c515919a1de918331b860a211f309bb716c4bb0","deepseek.json":"10a296fb3e898f7715c80890c8af0af4f5fd58f22dbcd6612fdc5d762157ccdc","fireworks.json":"c3dc21dc2e95428fc9a38e3840c05bbf90f94e4c25dee0eedf0b385b49ff65a4","github-copilot.json":"9ed67e5c0104ec237e9befcb9a50a18bd7d65e1539cd9f0638d2bc20bd82ef60","google-vertex.json":"40090c680538bc867f2624d986a449f361be4dfa6ba96d7a27402050f036e009","google.json":"c0f5a633e5e5a5044432db3e537f4bc674d4de18df09325c6349547fcc13cabe","groq.json":"910952ad9dad07856983459d63fce0148dba1203edd7b10d801a77c5824f3405","huggingface.json":"1b8604bd31a5e05fe43a40ec1ec6dfc1a51a28b0b020151ef057fab43f57c923","meta.json":"0c7e9a370a7005c5a89dc94f0f93bc67b8935c3f802de324f11af6ff56fff429","minimax-cn.json":"f962c20ee6a6a342820f778e9dc363a18a524bf11998f6e2c57f2989915d62e5","minimax.json":"17a432f80d0ea6dd26eade80a4314250d3a855678601728a5f70dd8fa0d21dd2","mistral.json":"10f33bff9adf1248f7e6848e5890c265399f1f94e5b42cfdc28c109d547af98d","moonshotai-cn.json":"bfb0641eee9587118da525607d86e55840de13310cd934785bc3bdcfeb6f88e3","moonshotai.json":"79ac5b6861a10d592e884c05a546fdd0b58fe383ce3453ecdb32d43926cba9dd","nvidia.json":"a75e963dc81779e3eec01c051b12277c15ec37c5b94ab51ece9e9ccb3b16407c","openai.json":"9104538118053eed57f90852332b6ad274a4e4792389bdbc9cfca1ae400c705a","opencode-go.json":"82e416a53efa53fed4c4e3dc0ed837fe2a0d53f110528f65af843e6fa08599e5","opencode.json":"ae373c624a66b824f543ef76fb5f1849b4910390952e6ae18b13bdb3e2d63b37","opengateway.json":"2d4275641f1b1154dfc6ffb320bedc1bcb1e33a0d12a4e9fba147ee3edec7d64","openrouter.json":"0bc2c2aaa62397b911aed335070bce85ef952aeed29be0c2d46d825063b8dda5","qwen-token-plan-cn.json":"bd36a6d9cc2048deafacc85f7f0f2e51868c8b7aa09502e3e4e60e3e8b76f690","qwen-token-plan-individual.json":"35ffb0ba1a1ce3f68fc73262dd5e07fbcb5bf5911ad1e6e17bc0621e2894676a","qwen-token-plan.json":"ca27e49e62965221badead8e867b2007035c6b6a15a97c47219bd23d2c83c2b1","radius.json":"a8cf2f2cc7dae90d8bbfdd8a2f8a0e058e7923dbdb3ba2cdaed7dd1aecd25a2a","together.json":"bbd7fad86cacfefe3d3d61f6638f22e9b6f5d575722fd1112a16aafe0485c46d","typesafe.json":"a4429a2f696d5cc5273969af61a7310bda07d6bdcd27bf9ac9c76c534cc43716","venice.json":"70fdecec11c5b7331a3b5bb79dc8df2a4f63fffcb4d80d986708980252eafd47","vercel-ai-gateway.json":"384fa5b1d8e8b59576fddbf353150e1fe04e2c3ef393c683a34e7662ca6184b3","xai.json":"d7771a1450b21c1f6aee887ca6fea61b6e7b3fc13b4dee5cf72a71134fe623bc","xiaomi-token-plan-ams.json":"4dd8f838e21d7aa7caf3122d0b0b1e5a02a33bc012b96e6a75bd119334152d7a","xiaomi-token-plan-cn.json":"46414ff49973b95a0c14bd33e8992cc1938a50ce2c7cea9e2e5cc02de8f1df31","xiaomi-token-plan-sgp.json":"f5b3f66bf7bb7238592d4a9667cad983a008d7539a66e9896b02422622800935","xiaomi.json":"8b62c158d55d5a165f35cd72c1fc82ab89ff8e73f08960105fc41b822f236f69","zai-coding-cn.json":"2a7f59d15f588a97d218997883f2506dfc52d3ee320697d39f14213a050fc912","zai.json":"e9cc408e1756de8abc70148d63d89d11a63c65b7ef06ad0661cd1328e4209ba3"}}
|