mirror of
https://github.com/getcompanion-ai/co-mono.git
synced 2026-04-15 07:04:45 +00:00
fix(ai,coding-agent): make pi-ai browser-safe and move OAuth runtime exports
- add browser smoke bundling check to root check + pre-commit - lazy-load Bedrock provider registration to avoid browser graph traversal - remove top-level OAuth runtime exports from @mariozechner/pi-ai - add @mariozechner/pi-ai/oauth subpath export and update coding-agent imports - move proxy dispatcher init to coding-agent CLI (Node-only) - document Bedrock/OAuth browser limitations closes #1814
This commit is contained in:
parent
2af0c98b5f
commit
e0754fdbb3
26 changed files with 216 additions and 59 deletions
|
|
@ -11,6 +11,25 @@ if [ $? -ne 0 ]; then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
RUN_BROWSER_SMOKE=0
|
||||||
|
for file in $STAGED_FILES; do
|
||||||
|
case "$file" in
|
||||||
|
packages/ai/*|packages/web-ui/*|package.json|package-lock.json)
|
||||||
|
RUN_BROWSER_SMOKE=1
|
||||||
|
break
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ $RUN_BROWSER_SMOKE -eq 1 ]; then
|
||||||
|
echo "Running browser smoke check..."
|
||||||
|
npm run check:browser-smoke
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "❌ Browser smoke check failed."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
# Restage files that were previously staged and may have been modified by formatting
|
# Restage files that were previously staged and may have been modified by formatting
|
||||||
for file in $STAGED_FILES; do
|
for file in $STAGED_FILES; do
|
||||||
if [ -f "$file" ]; then
|
if [ -f "$file" ]; then
|
||||||
|
|
@ -18,4 +37,4 @@ for file in $STAGED_FILES; do
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "✅ All pre-commit checks passed!"
|
echo "✅ All pre-commit checks passed!"
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,8 @@
|
||||||
"build": "cd packages/tui && npm run build && cd ../ai && npm run build && cd ../agent && npm run build && cd ../coding-agent && npm run build && cd ../mom && npm run build && cd ../web-ui && npm run build && cd ../pods && npm run build",
|
"build": "cd packages/tui && npm run build && cd ../ai && npm run build && cd ../agent && npm run build && cd ../coding-agent && npm run build && cd ../mom && npm run build && cd ../web-ui && npm run build && cd ../pods && npm run build",
|
||||||
"dev": "concurrently --names \"ai,agent,coding-agent,mom,web-ui,tui\" --prefix-colors \"cyan,yellow,red,white,green,magenta\" \"cd packages/ai && npm run dev\" \"cd packages/agent && npm run dev\" \"cd packages/coding-agent && npm run dev\" \"cd packages/mom && npm run dev\" \"cd packages/web-ui && npm run dev\" \"cd packages/tui && npm run dev\"",
|
"dev": "concurrently --names \"ai,agent,coding-agent,mom,web-ui,tui\" --prefix-colors \"cyan,yellow,red,white,green,magenta\" \"cd packages/ai && npm run dev\" \"cd packages/agent && npm run dev\" \"cd packages/coding-agent && npm run dev\" \"cd packages/mom && npm run dev\" \"cd packages/web-ui && npm run dev\" \"cd packages/tui && npm run dev\"",
|
||||||
"dev:tsc": "concurrently --names \"ai,web-ui\" --prefix-colors \"cyan,green\" \"cd packages/ai && npm run dev:tsc\" \"cd packages/web-ui && npm run dev:tsc\"",
|
"dev:tsc": "concurrently --names \"ai,web-ui\" --prefix-colors \"cyan,green\" \"cd packages/ai && npm run dev:tsc\" \"cd packages/web-ui && npm run dev:tsc\"",
|
||||||
"check": "biome check --write --error-on-warnings . && tsgo --noEmit && cd packages/web-ui && npm run check",
|
"check": "biome check --write --error-on-warnings . && tsgo --noEmit && npm run check:browser-smoke && cd packages/web-ui && npm run check",
|
||||||
|
"check:browser-smoke": "sh -c 'esbuild scripts/browser-smoke-entry.ts --bundle --platform=browser --format=esm --log-limit=0 --outfile=/tmp/pi-browser-smoke.js > /tmp/pi-browser-smoke-errors.log 2>&1 || { echo \"Browser smoke check failed. See /tmp/pi-browser-smoke-errors.log\"; exit 1; }'",
|
||||||
"test": "npm run test --workspaces --if-present",
|
"test": "npm run test --workspaces --if-present",
|
||||||
"version:patch": "npm version patch -ws --no-git-tag-version && node scripts/sync-versions.js && shx rm -rf node_modules packages/*/node_modules package-lock.json && npm install",
|
"version:patch": "npm version patch -ws --no-git-tag-version && node scripts/sync-versions.js && shx rm -rf node_modules packages/*/node_modules package-lock.json && npm install",
|
||||||
"version:minor": "npm version minor -ws --no-git-tag-version && node scripts/sync-versions.js && shx rm -rf node_modules packages/*/node_modules package-lock.json && npm install",
|
"version:minor": "npm version minor -ws --no-git-tag-version && node scripts/sync-versions.js && shx rm -rf node_modules packages/*/node_modules package-lock.json && npm install",
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,10 @@
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Breaking Changes
|
||||||
|
|
||||||
|
- Moved Node OAuth runtime exports off the top-level package entry. Import OAuth login/refresh functions from `@mariozechner/pi-ai/oauth` instead of `@mariozechner/pi-ai` ([#1814](https://github.com/badlogic/pi-mono/issues/1814))
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Added `gemini-3.1-flash-lite-preview` fallback model entry for the `google` provider so it remains selectable until upstream model catalogs include it ([#1785](https://github.com/badlogic/pi-mono/issues/1785), thanks [@n-WN](https://github.com/n-WN)).
|
- Added `gemini-3.1-flash-lite-preview` fallback model entry for the `google` provider so it remains selectable until upstream model catalogs include it ([#1785](https://github.com/badlogic/pi-mono/issues/1785), thanks [@n-WN](https://github.com/n-WN)).
|
||||||
|
|
@ -9,6 +13,7 @@
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed Gemini 3.1 thinking-level detection in `google` and `google-vertex` providers so `gemini-3.1-*` models use Gemini 3 level-based thinking config instead of budget fallback ([#1785](https://github.com/badlogic/pi-mono/issues/1785), thanks [@n-WN](https://github.com/n-WN)).
|
- Fixed Gemini 3.1 thinking-level detection in `google` and `google-vertex` providers so `gemini-3.1-*` models use Gemini 3 level-based thinking config instead of budget fallback ([#1785](https://github.com/badlogic/pi-mono/issues/1785), thanks [@n-WN](https://github.com/n-WN)).
|
||||||
|
- Fixed browser bundling failures by lazy-loading the Bedrock provider and removing Node-only side effects from the default browser import graph ([#1814](https://github.com/badlogic/pi-mono/issues/1814))
|
||||||
|
|
||||||
## [0.55.4] - 2026-03-02
|
## [0.55.4] - 2026-03-02
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ Unified LLM API with automatic model discovery, provider configuration, token an
|
||||||
- [Cross-Provider Handoffs](#cross-provider-handoffs)
|
- [Cross-Provider Handoffs](#cross-provider-handoffs)
|
||||||
- [Context Serialization](#context-serialization)
|
- [Context Serialization](#context-serialization)
|
||||||
- [Browser Usage](#browser-usage)
|
- [Browser Usage](#browser-usage)
|
||||||
|
- [Browser Compatibility Notes](#browser-compatibility-notes)
|
||||||
- [Environment Variables](#environment-variables-nodejs-only)
|
- [Environment Variables](#environment-variables-nodejs-only)
|
||||||
- [Checking Environment Variables](#checking-environment-variables)
|
- [Checking Environment Variables](#checking-environment-variables)
|
||||||
- [OAuth Providers](#oauth-providers)
|
- [OAuth Providers](#oauth-providers)
|
||||||
|
|
@ -888,6 +889,13 @@ const response = await complete(model, {
|
||||||
|
|
||||||
> **Security Warning**: Exposing API keys in frontend code is dangerous. Anyone can extract and abuse your keys. Only use this approach for internal tools or demos. For production applications, use a backend proxy that keeps your API keys secure.
|
> **Security Warning**: Exposing API keys in frontend code is dangerous. Anyone can extract and abuse your keys. Only use this approach for internal tools or demos. For production applications, use a backend proxy that keeps your API keys secure.
|
||||||
|
|
||||||
|
### Browser Compatibility Notes
|
||||||
|
|
||||||
|
- Amazon Bedrock (`bedrock-converse-stream`) is not supported in browser environments.
|
||||||
|
- OAuth login flows are not supported in browser environments. Use the `@mariozechner/pi-ai/oauth` entry point in Node.js.
|
||||||
|
- In browser builds, Bedrock can still appear in model lists. Calls to Bedrock models fail at runtime.
|
||||||
|
- Use a server-side proxy or backend service if you need Bedrock or OAuth-based auth from a web app.
|
||||||
|
|
||||||
### Environment Variables (Node.js only)
|
### Environment Variables (Node.js only)
|
||||||
|
|
||||||
In Node.js environments, you can set environment variables to avoid passing API keys:
|
In Node.js environments, you can set environment variables to avoid passing API keys:
|
||||||
|
|
@ -1018,7 +1026,7 @@ Credentials are saved to `auth.json` in the current directory.
|
||||||
|
|
||||||
### Programmatic OAuth
|
### Programmatic OAuth
|
||||||
|
|
||||||
The library provides login and token refresh functions. Credential storage is the caller's responsibility.
|
The library provides login and token refresh functions via the `@mariozechner/pi-ai/oauth` entry point. Credential storage is the caller's responsibility.
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import {
|
import {
|
||||||
|
|
@ -1036,13 +1044,13 @@ import {
|
||||||
// Types
|
// Types
|
||||||
type OAuthProvider, // 'anthropic' | 'openai-codex' | 'github-copilot' | 'google-gemini-cli' | 'google-antigravity'
|
type OAuthProvider, // 'anthropic' | 'openai-codex' | 'github-copilot' | 'google-gemini-cli' | 'google-antigravity'
|
||||||
type OAuthCredentials,
|
type OAuthCredentials,
|
||||||
} from '@mariozechner/pi-ai';
|
} from '@mariozechner/pi-ai/oauth';
|
||||||
```
|
```
|
||||||
|
|
||||||
### Login Flow Example
|
### Login Flow Example
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { loginGitHubCopilot } from '@mariozechner/pi-ai';
|
import { loginGitHubCopilot } from '@mariozechner/pi-ai/oauth';
|
||||||
import { writeFileSync } from 'fs';
|
import { writeFileSync } from 'fs';
|
||||||
|
|
||||||
const credentials = await loginGitHubCopilot({
|
const credentials = await loginGitHubCopilot({
|
||||||
|
|
@ -1066,7 +1074,8 @@ writeFileSync('auth.json', JSON.stringify(auth, null, 2));
|
||||||
Use `getOAuthApiKey()` to get an API key, automatically refreshing if expired:
|
Use `getOAuthApiKey()` to get an API key, automatically refreshing if expired:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
import { getModel, complete, getOAuthApiKey } from '@mariozechner/pi-ai';
|
import { getModel, complete } from '@mariozechner/pi-ai';
|
||||||
|
import { getOAuthApiKey } from '@mariozechner/pi-ai/oauth';
|
||||||
import { readFileSync, writeFileSync } from 'fs';
|
import { readFileSync, writeFileSync } from 'fs';
|
||||||
|
|
||||||
// Load your stored credentials
|
// Load your stored credentials
|
||||||
|
|
|
||||||
1
packages/ai/oauth.d.ts
vendored
Normal file
1
packages/ai/oauth.d.ts
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export * from "./src/oauth.js";
|
||||||
1
packages/ai/oauth.js
Normal file
1
packages/ai/oauth.js
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export * from "./dist/oauth.js";
|
||||||
|
|
@ -5,11 +5,23 @@
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
|
"import": "./dist/index.js"
|
||||||
|
},
|
||||||
|
"./oauth": {
|
||||||
|
"types": "./oauth.d.ts",
|
||||||
|
"import": "./oauth.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"pi-ai": "./dist/cli.js"
|
"pi-ai": "./dist/cli.js"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"dist",
|
"dist",
|
||||||
|
"oauth.js",
|
||||||
|
"oauth.d.ts",
|
||||||
"README.md"
|
"README.md"
|
||||||
],
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,20 @@ let _existsSync: typeof import("node:fs").existsSync | null = null;
|
||||||
let _homedir: typeof import("node:os").homedir | null = null;
|
let _homedir: typeof import("node:os").homedir | null = null;
|
||||||
let _join: typeof import("node:path").join | null = null;
|
let _join: typeof import("node:path").join | null = null;
|
||||||
|
|
||||||
|
type DynamicImport = (specifier: string) => Promise<unknown>;
|
||||||
|
|
||||||
|
const dynamicImport = new Function("specifier", "return import(specifier);") as DynamicImport;
|
||||||
|
|
||||||
// Eagerly load in Node.js/Bun environment only
|
// Eagerly load in Node.js/Bun environment only
|
||||||
if (typeof process !== "undefined" && (process.versions?.node || process.versions?.bun)) {
|
if (typeof process !== "undefined" && (process.versions?.node || process.versions?.bun)) {
|
||||||
import("node:fs").then((m) => {
|
dynamicImport("node:fs").then((m) => {
|
||||||
_existsSync = m.existsSync;
|
_existsSync = (m as typeof import("node:fs")).existsSync;
|
||||||
});
|
});
|
||||||
import("node:os").then((m) => {
|
dynamicImport("node:os").then((m) => {
|
||||||
_homedir = m.homedir;
|
_homedir = (m as typeof import("node:os")).homedir;
|
||||||
});
|
});
|
||||||
import("node:path").then((m) => {
|
dynamicImport("node:path").then((m) => {
|
||||||
_join = m.join;
|
_join = (m as typeof import("node:path")).join;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,16 @@ export * from "./stream.js";
|
||||||
export * from "./types.js";
|
export * from "./types.js";
|
||||||
export * from "./utils/event-stream.js";
|
export * from "./utils/event-stream.js";
|
||||||
export * from "./utils/json-parse.js";
|
export * from "./utils/json-parse.js";
|
||||||
export * from "./utils/oauth/index.js";
|
export type {
|
||||||
|
OAuthAuthInfo,
|
||||||
|
OAuthCredentials,
|
||||||
|
OAuthLoginCallbacks,
|
||||||
|
OAuthPrompt,
|
||||||
|
OAuthProvider,
|
||||||
|
OAuthProviderId,
|
||||||
|
OAuthProviderInfo,
|
||||||
|
OAuthProviderInterface,
|
||||||
|
} from "./utils/oauth/types.js";
|
||||||
export * from "./utils/overflow.js";
|
export * from "./utils/overflow.js";
|
||||||
export * from "./utils/typebox-helpers.js";
|
export * from "./utils/typebox-helpers.js";
|
||||||
export * from "./utils/validation.js";
|
export * from "./utils/validation.js";
|
||||||
|
|
|
||||||
1
packages/ai/src/oauth.ts
Normal file
1
packages/ai/src/oauth.ts
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
export * from "./utils/oauth/index.js";
|
||||||
|
|
@ -1,12 +1,19 @@
|
||||||
// NEVER convert to top-level import - breaks browser/Vite builds (web-ui)
|
import type * as NodeOs from "node:os";
|
||||||
let _os: typeof import("node:os") | null = null;
|
import type { Tool as OpenAITool, ResponseInput, ResponseStreamEvent } from "openai/resources/responses/responses.js";
|
||||||
|
|
||||||
|
// NEVER convert to top-level runtime imports - breaks browser/Vite builds (web-ui)
|
||||||
|
let _os: typeof NodeOs | null = null;
|
||||||
|
|
||||||
|
type DynamicImport = (specifier: string) => Promise<unknown>;
|
||||||
|
|
||||||
|
const dynamicImport = new Function("specifier", "return import(specifier);") as DynamicImport;
|
||||||
|
|
||||||
if (typeof process !== "undefined" && (process.versions?.node || process.versions?.bun)) {
|
if (typeof process !== "undefined" && (process.versions?.node || process.versions?.bun)) {
|
||||||
import("node:os").then((m) => {
|
dynamicImport("node:os").then((m) => {
|
||||||
_os = m;
|
_os = m as typeof NodeOs;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
import type { Tool as OpenAITool, ResponseInput, ResponseStreamEvent } from "openai/resources/responses/responses.js";
|
|
||||||
import { getEnvApiKey } from "../env-api-keys.js";
|
import { getEnvApiKey } from "../env-api-keys.js";
|
||||||
import { supportsXhigh } from "../models.js";
|
import { supportsXhigh } from "../models.js";
|
||||||
import type {
|
import type {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,13 @@
|
||||||
import { clearApiProviders, registerApiProvider } from "../api-registry.js";
|
import { clearApiProviders, registerApiProvider } from "../api-registry.js";
|
||||||
import { streamBedrock, streamSimpleBedrock } from "./amazon-bedrock.js";
|
import type {
|
||||||
|
AssistantMessage,
|
||||||
|
AssistantMessageEvent,
|
||||||
|
Context,
|
||||||
|
Model,
|
||||||
|
SimpleStreamOptions,
|
||||||
|
StreamOptions,
|
||||||
|
} from "../types.js";
|
||||||
|
import { AssistantMessageEventStream } from "../utils/event-stream.js";
|
||||||
import { streamAnthropic, streamSimpleAnthropic } from "./anthropic.js";
|
import { streamAnthropic, streamSimpleAnthropic } from "./anthropic.js";
|
||||||
import { streamAzureOpenAIResponses, streamSimpleAzureOpenAIResponses } from "./azure-openai-responses.js";
|
import { streamAzureOpenAIResponses, streamSimpleAzureOpenAIResponses } from "./azure-openai-responses.js";
|
||||||
import { streamGoogle, streamSimpleGoogle } from "./google.js";
|
import { streamGoogle, streamSimpleGoogle } from "./google.js";
|
||||||
|
|
@ -9,6 +17,100 @@ import { streamOpenAICodexResponses, streamSimpleOpenAICodexResponses } from "./
|
||||||
import { streamOpenAICompletions, streamSimpleOpenAICompletions } from "./openai-completions.js";
|
import { streamOpenAICompletions, streamSimpleOpenAICompletions } from "./openai-completions.js";
|
||||||
import { streamOpenAIResponses, streamSimpleOpenAIResponses } from "./openai-responses.js";
|
import { streamOpenAIResponses, streamSimpleOpenAIResponses } from "./openai-responses.js";
|
||||||
|
|
||||||
|
interface BedrockProviderModule {
|
||||||
|
streamBedrock: (
|
||||||
|
model: Model<"bedrock-converse-stream">,
|
||||||
|
context: Context,
|
||||||
|
options?: StreamOptions,
|
||||||
|
) => AssistantMessageEventStream;
|
||||||
|
streamSimpleBedrock: (
|
||||||
|
model: Model<"bedrock-converse-stream">,
|
||||||
|
context: Context,
|
||||||
|
options?: SimpleStreamOptions,
|
||||||
|
) => AssistantMessageEventStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
type DynamicImport = (specifier: string) => Promise<unknown>;
|
||||||
|
|
||||||
|
const dynamicImport = new Function("specifier", "return import(specifier);") as DynamicImport;
|
||||||
|
|
||||||
|
async function loadBedrockProviderModule(): Promise<BedrockProviderModule> {
|
||||||
|
const module = await dynamicImport("./amazon-bedrock.js");
|
||||||
|
return module as BedrockProviderModule;
|
||||||
|
}
|
||||||
|
|
||||||
|
function forwardStream(target: AssistantMessageEventStream, source: AssistantMessageEventStream): void {
|
||||||
|
(async () => {
|
||||||
|
for await (const event of source as AsyncIterable<AssistantMessageEvent>) {
|
||||||
|
target.push(event);
|
||||||
|
}
|
||||||
|
target.end();
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
||||||
|
function createLazyLoadErrorMessage(model: Model<"bedrock-converse-stream">, error: unknown): AssistantMessage {
|
||||||
|
return {
|
||||||
|
role: "assistant",
|
||||||
|
content: [],
|
||||||
|
api: "bedrock-converse-stream",
|
||||||
|
provider: model.provider,
|
||||||
|
model: model.id,
|
||||||
|
usage: {
|
||||||
|
input: 0,
|
||||||
|
output: 0,
|
||||||
|
cacheRead: 0,
|
||||||
|
cacheWrite: 0,
|
||||||
|
totalTokens: 0,
|
||||||
|
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
||||||
|
},
|
||||||
|
stopReason: "error",
|
||||||
|
errorMessage: error instanceof Error ? error.message : String(error),
|
||||||
|
timestamp: Date.now(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function streamBedrockLazy(
|
||||||
|
model: Model<"bedrock-converse-stream">,
|
||||||
|
context: Context,
|
||||||
|
options?: StreamOptions,
|
||||||
|
): AssistantMessageEventStream {
|
||||||
|
const outer = new AssistantMessageEventStream();
|
||||||
|
|
||||||
|
loadBedrockProviderModule()
|
||||||
|
.then((module) => {
|
||||||
|
const inner = module.streamBedrock(model, context, options);
|
||||||
|
forwardStream(outer, inner);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
const message = createLazyLoadErrorMessage(model, error);
|
||||||
|
outer.push({ type: "error", reason: "error", error: message });
|
||||||
|
outer.end(message);
|
||||||
|
});
|
||||||
|
|
||||||
|
return outer;
|
||||||
|
}
|
||||||
|
|
||||||
|
function streamSimpleBedrockLazy(
|
||||||
|
model: Model<"bedrock-converse-stream">,
|
||||||
|
context: Context,
|
||||||
|
options?: SimpleStreamOptions,
|
||||||
|
): AssistantMessageEventStream {
|
||||||
|
const outer = new AssistantMessageEventStream();
|
||||||
|
|
||||||
|
loadBedrockProviderModule()
|
||||||
|
.then((module) => {
|
||||||
|
const inner = module.streamSimpleBedrock(model, context, options);
|
||||||
|
forwardStream(outer, inner);
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
const message = createLazyLoadErrorMessage(model, error);
|
||||||
|
outer.push({ type: "error", reason: "error", error: message });
|
||||||
|
outer.end(message);
|
||||||
|
});
|
||||||
|
|
||||||
|
return outer;
|
||||||
|
}
|
||||||
|
|
||||||
export function registerBuiltInApiProviders(): void {
|
export function registerBuiltInApiProviders(): void {
|
||||||
registerApiProvider({
|
registerApiProvider({
|
||||||
api: "anthropic-messages",
|
api: "anthropic-messages",
|
||||||
|
|
@ -60,8 +162,8 @@ export function registerBuiltInApiProviders(): void {
|
||||||
|
|
||||||
registerApiProvider({
|
registerApiProvider({
|
||||||
api: "bedrock-converse-stream",
|
api: "bedrock-converse-stream",
|
||||||
stream: streamBedrock,
|
stream: streamBedrockLazy,
|
||||||
streamSimple: streamSimpleBedrock,
|
streamSimple: streamSimpleBedrockLazy,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import "./providers/register-builtins.js";
|
import "./providers/register-builtins.js";
|
||||||
import "./utils/http-proxy.js";
|
|
||||||
|
|
||||||
import { getApiProvider } from "./api-registry.js";
|
import { getApiProvider } from "./api-registry.js";
|
||||||
import type {
|
import type {
|
||||||
|
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
||||||
/**
|
|
||||||
* Set up HTTP proxy according to env variables for `fetch` based SDKs in Node.js.
|
|
||||||
* Bun has builtin support for this.
|
|
||||||
*
|
|
||||||
* This module should be imported early by any code that needs proxy support for fetch().
|
|
||||||
* ES modules are cached, so importing multiple times is safe - setup only runs once.
|
|
||||||
*/
|
|
||||||
if (typeof process !== "undefined" && process.versions?.node) {
|
|
||||||
import("undici").then((m) => {
|
|
||||||
const { EnvHttpProxyAgent, setGlobalDispatcher } = m;
|
|
||||||
setGlobalDispatcher(new EnvHttpProxyAgent());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
@ -9,9 +9,6 @@
|
||||||
* - Antigravity (Gemini 3, Claude, GPT-OSS via Google Cloud)
|
* - Antigravity (Gemini 3, Claude, GPT-OSS via Google Cloud)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// Set up HTTP proxy for fetch() calls (respects HTTP_PROXY, HTTPS_PROXY env vars)
|
|
||||||
import "../http-proxy.js";
|
|
||||||
|
|
||||||
// Anthropic
|
// Anthropic
|
||||||
export { anthropicOAuthProvider, loginAnthropic, refreshAnthropicToken } from "./anthropic.js";
|
export { anthropicOAuthProvider, loginAnthropic, refreshAnthropicToken } from "./anthropic.js";
|
||||||
// GitHub Copilot
|
// GitHub Copilot
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@
|
||||||
"minimatch": "^10.2.3",
|
"minimatch": "^10.2.3",
|
||||||
"proper-lockfile": "^4.1.2",
|
"proper-lockfile": "^4.1.2",
|
||||||
"strip-ansi": "^7.1.0",
|
"strip-ansi": "^7.1.0",
|
||||||
|
"undici": "^7.19.1",
|
||||||
"yaml": "^2.8.2"
|
"yaml": "^2.8.2"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,9 @@
|
||||||
*/
|
*/
|
||||||
process.title = "pi";
|
process.title = "pi";
|
||||||
|
|
||||||
|
import { EnvHttpProxyAgent, setGlobalDispatcher } from "undici";
|
||||||
import { main } from "./main.js";
|
import { main } from "./main.js";
|
||||||
|
|
||||||
|
setGlobalDispatcher(new EnvHttpProxyAgent());
|
||||||
|
|
||||||
main(process.argv.slice(2));
|
main(process.argv.slice(2));
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,11 @@
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getEnvApiKey,
|
getEnvApiKey,
|
||||||
getOAuthApiKey,
|
|
||||||
getOAuthProvider,
|
|
||||||
getOAuthProviders,
|
|
||||||
type OAuthCredentials,
|
type OAuthCredentials,
|
||||||
type OAuthLoginCallbacks,
|
type OAuthLoginCallbacks,
|
||||||
type OAuthProviderId,
|
type OAuthProviderId,
|
||||||
} from "@mariozechner/pi-ai";
|
} from "@mariozechner/pi-ai";
|
||||||
|
import { getOAuthApiKey, getOAuthProvider, getOAuthProviders } from "@mariozechner/pi-ai/oauth";
|
||||||
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
|
||||||
import { dirname, join } from "path";
|
import { dirname, join } from "path";
|
||||||
import lockfile from "proper-lockfile";
|
import lockfile from "proper-lockfile";
|
||||||
|
|
|
||||||
|
|
@ -14,11 +14,10 @@ import {
|
||||||
type OpenAICompletionsCompat,
|
type OpenAICompletionsCompat,
|
||||||
type OpenAIResponsesCompat,
|
type OpenAIResponsesCompat,
|
||||||
registerApiProvider,
|
registerApiProvider,
|
||||||
registerOAuthProvider,
|
|
||||||
resetApiProviders,
|
resetApiProviders,
|
||||||
resetOAuthProviders,
|
|
||||||
type SimpleStreamOptions,
|
type SimpleStreamOptions,
|
||||||
} from "@mariozechner/pi-ai";
|
} from "@mariozechner/pi-ai";
|
||||||
|
import { registerOAuthProvider, resetOAuthProviders } from "@mariozechner/pi-ai/oauth";
|
||||||
import { type Static, Type } from "@sinclair/typebox";
|
import { type Static, Type } from "@sinclair/typebox";
|
||||||
import AjvModule from "ajv";
|
import AjvModule from "ajv";
|
||||||
import { existsSync, readFileSync } from "fs";
|
import { existsSync, readFileSync } from "fs";
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { getOAuthProviders } from "@mariozechner/pi-ai";
|
import { getOAuthProviders } from "@mariozechner/pi-ai/oauth";
|
||||||
import { Container, type Focusable, getEditorKeybindings, Input, Spacer, Text, type TUI } from "@mariozechner/pi-tui";
|
import { Container, type Focusable, getEditorKeybindings, Input, Spacer, Text, type TUI } from "@mariozechner/pi-tui";
|
||||||
import { exec } from "child_process";
|
import { exec } from "child_process";
|
||||||
import { theme } from "../theme/theme.js";
|
import { theme } from "../theme/theme.js";
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { getOAuthProviders, type OAuthProviderInterface } from "@mariozechner/pi-ai";
|
import type { OAuthProviderInterface } from "@mariozechner/pi-ai";
|
||||||
|
import { getOAuthProviders } from "@mariozechner/pi-ai/oauth";
|
||||||
import { Container, getEditorKeybindings, Spacer, TruncatedText } from "@mariozechner/pi-tui";
|
import { Container, getEditorKeybindings, Spacer, TruncatedText } from "@mariozechner/pi-tui";
|
||||||
import type { AuthStorage } from "../../../core/auth-storage.js";
|
import type { AuthStorage } from "../../../core/auth-storage.js";
|
||||||
import { theme } from "../theme/theme.js";
|
import { theme } from "../theme/theme.js";
|
||||||
|
|
|
||||||
|
|
@ -8,14 +8,7 @@ import * as fs from "node:fs";
|
||||||
import * as os from "node:os";
|
import * as os from "node:os";
|
||||||
import * as path from "node:path";
|
import * as path from "node:path";
|
||||||
import type { AgentMessage } from "@mariozechner/pi-agent-core";
|
import type { AgentMessage } from "@mariozechner/pi-agent-core";
|
||||||
import {
|
import type { AssistantMessage, ImageContent, Message, Model, OAuthProviderId } from "@mariozechner/pi-ai";
|
||||||
type AssistantMessage,
|
|
||||||
getOAuthProviders,
|
|
||||||
type ImageContent,
|
|
||||||
type Message,
|
|
||||||
type Model,
|
|
||||||
type OAuthProvider,
|
|
||||||
} from "@mariozechner/pi-ai";
|
|
||||||
import type {
|
import type {
|
||||||
AutocompleteItem,
|
AutocompleteItem,
|
||||||
EditorAction,
|
EditorAction,
|
||||||
|
|
@ -3625,7 +3618,9 @@ export class InteractiveMode {
|
||||||
await this.showLoginDialog(providerId);
|
await this.showLoginDialog(providerId);
|
||||||
} else {
|
} else {
|
||||||
// Logout flow
|
// Logout flow
|
||||||
const providerInfo = getOAuthProviders().find((p) => p.id === providerId);
|
const providerInfo = this.session.modelRegistry.authStorage
|
||||||
|
.getOAuthProviders()
|
||||||
|
.find((p) => p.id === providerId);
|
||||||
const providerName = providerInfo?.name || providerId;
|
const providerName = providerInfo?.name || providerId;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
@ -3648,7 +3643,7 @@ export class InteractiveMode {
|
||||||
}
|
}
|
||||||
|
|
||||||
private async showLoginDialog(providerId: string): Promise<void> {
|
private async showLoginDialog(providerId: string): Promise<void> {
|
||||||
const providerInfo = getOAuthProviders().find((p) => p.id === providerId);
|
const providerInfo = this.session.modelRegistry.authStorage.getOAuthProviders().find((p) => p.id === providerId);
|
||||||
const providerName = providerInfo?.name || providerId;
|
const providerName = providerInfo?.name || providerId;
|
||||||
|
|
||||||
// Providers that use callback servers (can paste redirect URL)
|
// Providers that use callback servers (can paste redirect URL)
|
||||||
|
|
@ -3682,7 +3677,7 @@ export class InteractiveMode {
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.session.modelRegistry.authStorage.login(providerId as OAuthProvider, {
|
await this.session.modelRegistry.authStorage.login(providerId as OAuthProviderId, {
|
||||||
onAuth: (info: { url: string; instructions?: string }) => {
|
onAuth: (info: { url: string; instructions?: string }) => {
|
||||||
dialog.showAuth(info.url, info.instructions);
|
dialog.showAuth(info.url, info.instructions);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
||||||
import { tmpdir } from "node:os";
|
import { tmpdir } from "node:os";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { registerOAuthProvider } from "@mariozechner/pi-ai";
|
import { registerOAuthProvider } from "@mariozechner/pi-ai/oauth";
|
||||||
import lockfile from "proper-lockfile";
|
import lockfile from "proper-lockfile";
|
||||||
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
import { afterEach, beforeEach, describe, expect, test, vi } from "vitest";
|
||||||
import { AuthStorage } from "../src/core/auth-storage.js";
|
import { AuthStorage } from "../src/core/auth-storage.js";
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node
|
||||||
import { tmpdir } from "node:os";
|
import { tmpdir } from "node:os";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import type { Api, Context, Model, OpenAICompletionsCompat } from "@mariozechner/pi-ai";
|
import type { Api, Context, Model, OpenAICompletionsCompat } from "@mariozechner/pi-ai";
|
||||||
import { getApiProvider, getOAuthProvider } from "@mariozechner/pi-ai";
|
import { getApiProvider } from "@mariozechner/pi-ai";
|
||||||
|
import { getOAuthProvider } from "@mariozechner/pi-ai/oauth";
|
||||||
import { afterEach, beforeEach, describe, expect, test } from "vitest";
|
import { afterEach, beforeEach, describe, expect, test } from "vitest";
|
||||||
import { AuthStorage } from "../src/core/auth-storage.js";
|
import { AuthStorage } from "../src/core/auth-storage.js";
|
||||||
import { clearApiKeyCache, ModelRegistry } from "../src/core/model-registry.js";
|
import { clearApiKeyCache, ModelRegistry } from "../src/core/model-registry.js";
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,8 @@ import { chmodSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync }
|
||||||
import { homedir, tmpdir } from "node:os";
|
import { homedir, tmpdir } from "node:os";
|
||||||
import { dirname, join } from "node:path";
|
import { dirname, join } from "node:path";
|
||||||
import { Agent } from "@mariozechner/pi-agent-core";
|
import { Agent } from "@mariozechner/pi-agent-core";
|
||||||
import { getModel, getOAuthApiKey, type OAuthCredentials, type OAuthProvider } from "@mariozechner/pi-ai";
|
import { getModel, type OAuthCredentials, type OAuthProvider } from "@mariozechner/pi-ai";
|
||||||
|
import { getOAuthApiKey } from "@mariozechner/pi-ai/oauth";
|
||||||
import { AgentSession } from "../src/core/agent-session.js";
|
import { AgentSession } from "../src/core/agent-session.js";
|
||||||
import { AuthStorage } from "../src/core/auth-storage.js";
|
import { AuthStorage } from "../src/core/auth-storage.js";
|
||||||
import { createExtensionRuntime } from "../src/core/extensions/loader.js";
|
import { createExtensionRuntime } from "../src/core/extensions/loader.js";
|
||||||
|
|
|
||||||
4
scripts/browser-smoke-entry.ts
Normal file
4
scripts/browser-smoke-entry.ts
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
import { complete, getModel } from "@mariozechner/pi-ai";
|
||||||
|
|
||||||
|
const model = getModel("google", "gemini-2.5-flash");
|
||||||
|
console.log(model.id, typeof complete);
|
||||||
Loading…
Add table
Add a link
Reference in a new issue