@oh-my-pi/pi-ai 8.0.20 → 8.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +11 -12
  2. package/package.json +49 -26
  3. package/src/cli.ts +7 -7
  4. package/src/index.ts +2 -1
  5. package/src/models.generated.ts +100 -101
  6. package/src/providers/amazon-bedrock.ts +12 -13
  7. package/src/providers/anthropic.ts +67 -37
  8. package/src/providers/cursor.ts +57 -57
  9. package/src/providers/google-gemini-cli-usage.ts +2 -2
  10. package/src/providers/google-gemini-cli.ts +8 -10
  11. package/src/providers/google-shared.ts +12 -13
  12. package/src/providers/google-vertex.ts +7 -7
  13. package/src/providers/google.ts +8 -8
  14. package/src/providers/openai-codex/request-transformer.ts +6 -6
  15. package/src/providers/openai-codex-responses.ts +28 -28
  16. package/src/providers/openai-completions.ts +39 -39
  17. package/src/providers/openai-responses.ts +31 -31
  18. package/src/providers/transform-messages.ts +3 -3
  19. package/src/storage.ts +29 -19
  20. package/src/stream.ts +6 -6
  21. package/src/types.ts +1 -2
  22. package/src/usage/claude.ts +4 -4
  23. package/src/usage/github-copilot.ts +3 -4
  24. package/src/usage/google-antigravity.ts +3 -3
  25. package/src/usage/openai-codex.ts +4 -4
  26. package/src/usage/zai.ts +3 -3
  27. package/src/usage.ts +0 -1
  28. package/src/utils/event-stream.ts +4 -4
  29. package/src/utils/oauth/anthropic.ts +0 -1
  30. package/src/utils/oauth/callback-server.ts +2 -3
  31. package/src/utils/oauth/github-copilot.ts +2 -3
  32. package/src/utils/oauth/google-antigravity.ts +0 -1
  33. package/src/utils/oauth/google-gemini-cli.ts +2 -3
  34. package/src/utils/oauth/index.ts +11 -12
  35. package/src/utils/oauth/openai-codex.ts +0 -1
  36. package/src/utils/overflow.ts +2 -2
  37. package/src/utils/retry.ts +78 -0
  38. package/src/utils/validation.ts +4 -5
  39. package/tsconfig.json +0 -42
@@ -1,22 +1,3 @@
1
- import { calculateCost } from "@oh-my-pi/pi-ai/models";
2
- import { getEnvApiKey } from "@oh-my-pi/pi-ai/stream";
3
- import type {
4
- Api,
5
- AssistantMessage,
6
- Context,
7
- Model,
8
- StopReason,
9
- StreamFunction,
10
- StreamOptions,
11
- TextContent,
12
- ThinkingContent,
13
- Tool,
14
- ToolCall,
15
- } from "@oh-my-pi/pi-ai/types";
16
- import { AssistantMessageEventStream } from "@oh-my-pi/pi-ai/utils/event-stream";
17
- import { parseStreamingJson } from "@oh-my-pi/pi-ai/utils/json-parse";
18
- import { formatErrorMessageWithRetryAfter } from "@oh-my-pi/pi-ai/utils/retry-after";
19
- import { sanitizeSurrogates } from "@oh-my-pi/pi-ai/utils/sanitize-unicode";
20
1
  import OpenAI from "openai";
21
2
  import type {
22
3
  Tool as OpenAITool,
@@ -29,6 +10,25 @@ import type {
29
10
  ResponseOutputMessage,
30
11
  ResponseReasoningItem,
31
12
  } from "openai/resources/responses/responses";
13
+ import { calculateCost } from "../models";
14
+ import { getEnvApiKey } from "../stream";
15
+ import type {
16
+ Api,
17
+ AssistantMessage,
18
+ Context,
19
+ Model,
20
+ StopReason,
21
+ StreamFunction,
22
+ StreamOptions,
23
+ TextContent,
24
+ ThinkingContent,
25
+ Tool,
26
+ ToolCall,
27
+ } from "../types";
28
+ import { AssistantMessageEventStream } from "../utils/event-stream";
29
+ import { parseStreamingJson } from "../utils/json-parse";
30
+ import { formatErrorMessageWithRetryAfter } from "../utils/retry-after";
31
+ import { sanitizeSurrogates } from "../utils/sanitize-unicode";
32
32
  import { transformMessages } from "./transform-messages";
33
33
 
34
34
  /** Fast deterministic hash to shorten long strings */
@@ -245,7 +245,7 @@ export const streamOpenAIResponses: StreamFunction<"openai-responses"> = (
245
245
  const item = event.item;
246
246
 
247
247
  if (item.type === "reasoning" && currentBlock && currentBlock.type === "thinking") {
248
- currentBlock.thinking = item.summary?.map((s) => s.text).join("\n\n") || "";
248
+ currentBlock.thinking = item.summary?.map(s => s.text).join("\n\n") || "";
249
249
  currentBlock.thinkingSignature = JSON.stringify(item);
250
250
  stream.push({
251
251
  type: "thinking_end",
@@ -255,7 +255,7 @@ export const streamOpenAIResponses: StreamFunction<"openai-responses"> = (
255
255
  });
256
256
  currentBlock = null;
257
257
  } else if (item.type === "message" && currentBlock && currentBlock.type === "text") {
258
- currentBlock.text = item.content.map((c) => (c.type === "output_text" ? c.text : c.refusal)).join("");
258
+ currentBlock.text = item.content.map(c => (c.type === "output_text" ? c.text : c.refusal)).join("");
259
259
  currentBlock.textSignature = item.id;
260
260
  stream.push({
261
261
  type: "text_end",
@@ -293,7 +293,7 @@ export const streamOpenAIResponses: StreamFunction<"openai-responses"> = (
293
293
  calculateCost(model, output.usage);
294
294
  // Map status to stop reason
295
295
  output.stopReason = mapStopReason(response?.status);
296
- if (output.content.some((b) => b.type === "toolCall") && output.stopReason === "stop") {
296
+ if (output.content.some(b => b.type === "toolCall") && output.stopReason === "stop") {
297
297
  output.stopReason = "toolUse";
298
298
  }
299
299
  }
@@ -358,12 +358,12 @@ function createClient(
358
358
  headers["Openai-Intent"] = "conversation-edits";
359
359
 
360
360
  // Copilot requires this header when sending images
361
- const hasImages = messages.some((msg) => {
361
+ const hasImages = messages.some(msg => {
362
362
  if (msg.role === "user" && Array.isArray(msg.content)) {
363
- return msg.content.some((c) => c.type === "image");
363
+ return msg.content.some(c => c.type === "image");
364
364
  }
365
365
  if (msg.role === "toolResult" && Array.isArray(msg.content)) {
366
- return msg.content.some((c) => c.type === "image");
366
+ return msg.content.some(c => c.type === "image");
367
367
  }
368
368
  return false;
369
369
  });
@@ -491,9 +491,9 @@ function convertMessages(
491
491
  });
492
492
  // Filter out images if model doesn't support them, and empty text blocks
493
493
  let filteredContent = !model.input.includes("image")
494
- ? content.filter((c) => c.type !== "input_image")
494
+ ? content.filter(c => c.type !== "input_image")
495
495
  : content;
496
- filteredContent = filteredContent.filter((c) => {
496
+ filteredContent = filteredContent.filter(c => {
497
497
  if (c.type === "input_text") {
498
498
  return c.text.trim().length > 0;
499
499
  }
@@ -567,10 +567,10 @@ function convertMessages(
567
567
  } else if (msg.role === "toolResult") {
568
568
  // Extract text and image content
569
569
  const textResult = msg.content
570
- .filter((c) => c.type === "text")
571
- .map((c) => (c as any).text)
570
+ .filter(c => c.type === "text")
571
+ .map(c => (c as any).text)
572
572
  .join("\n");
573
- const hasImages = msg.content.some((c) => c.type === "image");
573
+ const hasImages = msg.content.some(c => c.type === "image");
574
574
  const normalized = normalizeResponsesToolCallId(msg.toolCallId);
575
575
  if (strictResponsesPairing && !knownCallIds.has(normalized.callId)) {
576
576
  continue;
@@ -618,7 +618,7 @@ function convertMessages(
618
618
  }
619
619
 
620
620
  function convertTools(tools: Tool[]): OpenAITool[] {
621
- return tools.map((tool) => ({
621
+ return tools.map(tool => ({
622
622
  type: "function",
623
623
  name: tool.name,
624
624
  description: tool.description,
@@ -1,4 +1,4 @@
1
- import type { Api, AssistantMessage, Message, Model, ToolCall, ToolResultMessage } from "@oh-my-pi/pi-ai/types";
1
+ import type { Api, AssistantMessage, Message, Model, ToolCall, ToolResultMessage } from "../types";
2
2
 
3
3
  /**
4
4
  * Normalize tool call ID for cross-provider compatibility.
@@ -98,7 +98,7 @@ export function transformMessages<TApi extends Api>(messages: Message[], model:
98
98
  const needsToolCallIdNormalization = targetRequiresStrictIds && (crossProviderSwitch || copilotCrossApiSwitch);
99
99
 
100
100
  // Transform message from different provider/model
101
- const transformedContent = assistantMsg.content.flatMap((block) => {
101
+ const transformedContent = assistantMsg.content.flatMap(block => {
102
102
  if (block.type === "thinking") {
103
103
  // Skip empty thinking blocks, convert others to plain text
104
104
  if (!block.thinking || block.thinking.trim() === "") return [];
@@ -173,7 +173,7 @@ export function transformMessages<TApi extends Api>(messages: Message[], model:
173
173
 
174
174
  const assistantMsg = msg as AssistantMessage;
175
175
  const isErroredAssistant = assistantMsg.stopReason === "error" || assistantMsg.stopReason === "aborted";
176
- const toolCalls = assistantMsg.content.filter((b) => b.type === "toolCall") as ToolCall[];
176
+ const toolCalls = assistantMsg.content.filter(b => b.type === "toolCall") as ToolCall[];
177
177
 
178
178
  result.push(msg);
179
179
 
package/src/storage.ts CHANGED
@@ -4,9 +4,9 @@
4
4
  */
5
5
 
6
6
  import { Database } from "bun:sqlite";
7
- import { chmodSync, existsSync, mkdirSync } from "node:fs";
8
- import { homedir } from "node:os";
9
- import { dirname, join } from "node:path";
7
+ import * as fs from "node:fs/promises";
8
+ import * as os from "node:os";
9
+ import * as path from "node:path";
10
10
  import type { OAuthCredentials } from "./utils/oauth/types";
11
11
 
12
12
  type AuthCredential = { type: "api_key"; key: string } | ({ type: "oauth" } & OAuthCredentials);
@@ -24,7 +24,7 @@ type AuthRow = {
24
24
  * Get the agent config directory (e.g., ~/.omp/agent/)
25
25
  */
26
26
  function getAgentDir(): string {
27
- const configDir = process.env.OMP_CODING_AGENT_DIR || join(homedir(), ".omp", "agent");
27
+ const configDir = process.env.OMP_CODING_AGENT_DIR || path.join(os.homedir(), ".omp", "agent");
28
28
  return configDir;
29
29
  }
30
30
 
@@ -32,7 +32,7 @@ function getAgentDir(): string {
32
32
  * Get path to agent.db
33
33
  */
34
34
  function getAgentDbPath(): string {
35
- return join(getAgentDir(), "agent.db");
35
+ return path.join(getAgentDir(), "agent.db");
36
36
  }
37
37
 
38
38
  function serializeCredential(credential: AuthCredential): { credentialType: string; data: string } | null {
@@ -79,6 +79,8 @@ function deserializeCredential(row: AuthRow): AuthCredential | null {
79
79
 
80
80
  /**
81
81
  * Simple storage class for CLI auth credentials.
82
+ *
83
+ * Use `CliAuthStorage.create()` to instantiate (async initialization).
82
84
  */
83
85
  export class CliAuthStorage {
84
86
  private db: Database;
@@ -87,20 +89,8 @@ export class CliAuthStorage {
87
89
  private listAllStmt: ReturnType<Database["prepare"]>;
88
90
  private deleteByProviderStmt: ReturnType<Database["prepare"]>;
89
91
 
90
- constructor(dbPath: string = getAgentDbPath()) {
91
- // Ensure directory exists with secure permissions
92
- const dir = dirname(dbPath);
93
- if (!existsSync(dir)) {
94
- mkdirSync(dir, { recursive: true, mode: 0o700 });
95
- }
96
-
97
- this.db = new Database(dbPath);
98
- // Harden database file permissions to prevent credential leakage
99
- try {
100
- chmodSync(dbPath, 0o600);
101
- } catch {
102
- // Ignore chmod failures (e.g., Windows)
103
- }
92
+ private constructor(db: Database) {
93
+ this.db = db;
104
94
  this.initializeSchema();
105
95
 
106
96
  this.insertStmt = this.db.prepare(
@@ -111,6 +101,26 @@ export class CliAuthStorage {
111
101
  this.deleteByProviderStmt = this.db.prepare("DELETE FROM auth_credentials WHERE provider = ?");
112
102
  }
113
103
 
104
+ static async create(dbPath: string = getAgentDbPath()): Promise<CliAuthStorage> {
105
+ const dir = path.dirname(dbPath);
106
+ const dirExists = await fs
107
+ .stat(dir)
108
+ .then(s => s.isDirectory())
109
+ .catch(() => false);
110
+ if (!dirExists) {
111
+ await fs.mkdir(dir, { recursive: true, mode: 0o700 });
112
+ }
113
+
114
+ const db = new Database(dbPath);
115
+ try {
116
+ await fs.chmod(dbPath, 0o600);
117
+ } catch {
118
+ // Ignore chmod failures (e.g., Windows)
119
+ }
120
+
121
+ return new CliAuthStorage(db);
122
+ }
123
+
114
124
  private initializeSchema(): void {
115
125
  this.db.exec(`
116
126
  PRAGMA journal_mode=WAL;
package/src/stream.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { existsSync } from "node:fs";
2
- import { homedir } from "node:os";
3
- import { join } from "node:path";
1
+ import * as fs from "node:fs";
2
+ import * as os from "node:os";
3
+ import * as path from "node:path";
4
4
  import { supportsXhigh } from "./models";
5
5
  import { type BedrockOptions, streamBedrock } from "./providers/amazon-bedrock";
6
6
  import { type AnthropicOptions, streamAnthropic } from "./providers/anthropic";
@@ -34,10 +34,10 @@ function hasVertexAdcCredentials(): boolean {
34
34
  if (cachedVertexAdcCredentialsExists === null) {
35
35
  const gacPath = process.env.GOOGLE_APPLICATION_CREDENTIALS;
36
36
  if (gacPath) {
37
- cachedVertexAdcCredentialsExists = existsSync(gacPath);
37
+ cachedVertexAdcCredentialsExists = fs.existsSync(gacPath);
38
38
  } else {
39
- cachedVertexAdcCredentialsExists = existsSync(
40
- join(homedir(), ".config", "gcloud", "application_default_credentials.json"),
39
+ cachedVertexAdcCredentialsExists = fs.existsSync(
40
+ path.join(os.homedir(), ".config", "gcloud", "application_default_credentials.json"),
41
41
  );
42
42
  }
43
43
  }
package/src/types.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { TSchema } from "@sinclair/typebox";
1
2
  import type { BedrockOptions } from "./providers/amazon-bedrock";
2
3
  import type { AnthropicOptions } from "./providers/anthropic";
3
4
  import type { CursorOptions } from "./providers/cursor";
@@ -237,8 +238,6 @@ export interface CursorExecHandlers {
237
238
  onToolResult?: CursorToolResultHandler;
238
239
  }
239
240
 
240
- import type { TSchema } from "@sinclair/typebox";
241
-
242
241
  export interface Tool<TParameters extends TSchema = TSchema> {
243
242
  name: string;
244
243
  description: string;
@@ -7,7 +7,7 @@ import type {
7
7
  UsageReport,
8
8
  UsageStatus,
9
9
  UsageWindow,
10
- } from "@oh-my-pi/pi-ai/usage";
10
+ } from "../usage";
11
11
 
12
12
  const DEFAULT_ENDPOINT = "https://api.anthropic.com/api/oauth";
13
13
  const DEFAULT_CACHE_TTL_MS = 60_000;
@@ -241,10 +241,10 @@ function buildCacheKey(params: UsageFetchParams): string {
241
241
 
242
242
  function resolveCacheExpiry(now: number, limits: UsageLimit[]): number {
243
243
  const earliestReset = limits
244
- .map((limit) => limit.window?.resetsAt)
244
+ .map(limit => limit.window?.resetsAt)
245
245
  .filter((value): value is number => typeof value === "number" && Number.isFinite(value))
246
246
  .reduce((min, value) => (min === undefined ? value : Math.min(min, value)), undefined as number | undefined);
247
- const exhausted = limits.some((limit) => limit.status === "exhausted");
247
+ const exhausted = limits.some(limit => limit.status === "exhausted");
248
248
  if (earliestReset === undefined) return now + DEFAULT_CACHE_TTL_MS;
249
249
  if (exhausted) return earliestReset;
250
250
  return Math.min(now + DEFAULT_CACHE_TTL_MS, earliestReset);
@@ -351,5 +351,5 @@ async function fetchClaudeUsage(params: UsageFetchParams, ctx: UsageFetchContext
351
351
  export const claudeUsageProvider: UsageProvider = {
352
352
  id: "anthropic",
353
353
  fetchUsage: fetchClaudeUsage,
354
- supports: (params) => params.provider === "anthropic" && params.credential.type === "oauth",
354
+ supports: params => params.provider === "anthropic" && params.credential.type === "oauth",
355
355
  };
@@ -3,7 +3,6 @@
3
3
  *
4
4
  * Normalizes Copilot quota usage into the shared UsageReport schema.
5
5
  */
6
-
7
6
  import type {
8
7
  UsageAmount,
9
8
  UsageCacheEntry,
@@ -14,7 +13,7 @@ import type {
14
13
  UsageReport,
15
14
  UsageStatus,
16
15
  UsageWindow,
17
- } from "@oh-my-pi/pi-ai/usage";
16
+ } from "../usage";
18
17
 
19
18
  const COPILOT_HEADERS = {
20
19
  "User-Agent": "GitHubCopilotChat/0.35.0",
@@ -316,7 +315,7 @@ function normalizeBillingUsage(data: BillingUsageResponse): UsageLimit[] {
316
315
  };
317
316
 
318
317
  const premiumItems = data.usageItems.filter(
319
- (item) => item.sku === "Copilot Premium Request" || item.sku.includes("Premium"),
318
+ item => item.sku === "Copilot Premium Request" || item.sku.includes("Premium"),
320
319
  );
321
320
  const totalUsed = premiumItems.reduce((sum, item) => sum + item.grossQuantity, 0);
322
321
  const totalLimit = premiumItems.reduce((sum, item) => sum + (item.limit ?? 0), 0) || undefined;
@@ -359,7 +358,7 @@ function normalizeBillingUsage(data: BillingUsageResponse): UsageLimit[] {
359
358
  function resolveCacheTtl(now: number, report: UsageReport | null): UsageCacheEntry["expiresAt"] {
360
359
  if (!report) return now + DEFAULT_CACHE_TTL_MS;
361
360
  const resetInMs = report.limits
362
- .map((limit) => limit.window?.resetInMs)
361
+ .map(limit => limit.window?.resetInMs)
363
362
  .find((value): value is number => typeof value === "number" && Number.isFinite(value));
364
363
  if (!resetInMs || resetInMs <= 0) return now + DEFAULT_CACHE_TTL_MS;
365
364
  return now + Math.min(MAX_CACHE_TTL_MS, resetInMs);
@@ -7,8 +7,8 @@ import type {
7
7
  UsageReport,
8
8
  UsageStatus,
9
9
  UsageWindow,
10
- } from "@oh-my-pi/pi-ai/usage";
11
- import { refreshAntigravityToken } from "@oh-my-pi/pi-ai/utils/oauth/google-antigravity";
10
+ } from "../usage";
11
+ import { refreshAntigravityToken } from "../utils/oauth/google-antigravity";
12
12
 
13
13
  interface AntigravityQuotaInfo {
14
14
  remainingFraction?: number;
@@ -214,5 +214,5 @@ async function fetchAntigravityUsage(params: UsageFetchParams, ctx: UsageFetchCo
214
214
  export const antigravityUsageProvider: UsageProvider = {
215
215
  id: "google-antigravity",
216
216
  fetchUsage: fetchAntigravityUsage,
217
- supports: (params) => params.provider === "google-antigravity",
217
+ supports: params => params.provider === "google-antigravity",
218
218
  };
@@ -1,5 +1,5 @@
1
1
  import { Buffer } from "node:buffer";
2
- import { CODEX_BASE_URL } from "@oh-my-pi/pi-ai/providers/openai-codex/constants";
2
+ import { CODEX_BASE_URL } from "../providers/openai-codex/constants";
3
3
  import type {
4
4
  UsageAmount,
5
5
  UsageCache,
@@ -9,7 +9,7 @@ import type {
9
9
  UsageProvider,
10
10
  UsageReport,
11
11
  UsageWindow,
12
- } from "@oh-my-pi/pi-ai/usage";
12
+ } from "../usage";
13
13
 
14
14
  const CODEX_USAGE_PATH = "wham/usage";
15
15
  const DEFAULT_CACHE_TTL_MS = 60_000;
@@ -264,9 +264,9 @@ function buildUsageLimit(args: {
264
264
  function resolveCacheExpiry(args: { report: UsageReport | null; nowMs: number }): number {
265
265
  const { report, nowMs } = args;
266
266
  if (!report) return nowMs + DEFAULT_CACHE_TTL_MS;
267
- const exhausted = report.limits.some((limit) => limit.status === "exhausted");
267
+ const exhausted = report.limits.some(limit => limit.status === "exhausted");
268
268
  const resetCandidates = report.limits
269
- .map((limit) => limit.window?.resetsAt)
269
+ .map(limit => limit.window?.resetsAt)
270
270
  .filter((value): value is number => typeof value === "number" && Number.isFinite(value));
271
271
  const earliestReset = resetCandidates.length > 0 ? Math.min(...resetCandidates) : undefined;
272
272
  if (exhausted && earliestReset) return earliestReset;
package/src/usage/zai.ts CHANGED
@@ -7,7 +7,7 @@ import type {
7
7
  UsageReport,
8
8
  UsageStatus,
9
9
  UsageWindow,
10
- } from "@oh-my-pi/pi-ai/usage";
10
+ } from "../usage";
11
11
 
12
12
  const DEFAULT_ENDPOINT = "https://api.z.ai";
13
13
  const QUOTA_PATH = "/api/monitor/usage/quota/limit";
@@ -133,7 +133,7 @@ function buildCacheKey(params: UsageFetchParams): string {
133
133
 
134
134
  function resolveCacheExpiry(now: number, limits: UsageLimit[]): number {
135
135
  const earliestReset = limits
136
- .map((limit) => limit.window?.resetsAt)
136
+ .map(limit => limit.window?.resetsAt)
137
137
  .filter((value): value is number => typeof value === "number" && Number.isFinite(value))
138
138
  .reduce((min, value) => (min === undefined ? value : Math.min(min, value)), undefined as number | undefined);
139
139
  if (!earliestReset) return now + DEFAULT_CACHE_TTL_MS;
@@ -288,5 +288,5 @@ async function fetchZaiUsage(params: UsageFetchParams, ctx: UsageFetchContext):
288
288
  export const zaiUsageProvider: UsageProvider = {
289
289
  id: "zai",
290
290
  fetchUsage: fetchZaiUsage,
291
- supports: (params) => params.provider === "zai" && params.credential.type === "api_key",
291
+ supports: params => params.provider === "zai" && params.credential.type === "api_key",
292
292
  };
package/src/usage.ts CHANGED
@@ -4,7 +4,6 @@
4
4
  * Provides a normalized schema to represent multiple limit windows, model tiers,
5
5
  * and shared quotas across providers.
6
6
  */
7
-
8
7
  import type { Provider } from "./types";
9
8
 
10
9
  export type UsageUnit = "percent" | "tokens" | "requests" | "usd" | "minutes" | "bytes" | "unknown";
@@ -1,4 +1,4 @@
1
- import type { AssistantMessage, AssistantMessageEvent } from "@oh-my-pi/pi-ai/types";
1
+ import type { AssistantMessage, AssistantMessageEvent } from "../types";
2
2
 
3
3
  // Generic event stream class for async iteration
4
4
  export class EventStream<T, R = T> implements AsyncIterable<T> {
@@ -53,7 +53,7 @@ export class EventStream<T, R = T> implements AsyncIterable<T> {
53
53
  } else if (this.done) {
54
54
  return;
55
55
  } else {
56
- const result = await new Promise<IteratorResult<T>>((resolve) => this.waiting.push(resolve));
56
+ const result = await new Promise<IteratorResult<T>>(resolve => this.waiting.push(resolve));
57
57
  if (result.done) return;
58
58
  yield result.value;
59
59
  }
@@ -68,8 +68,8 @@ export class EventStream<T, R = T> implements AsyncIterable<T> {
68
68
  export class AssistantMessageEventStream extends EventStream<AssistantMessageEvent, AssistantMessage> {
69
69
  constructor() {
70
70
  super(
71
- (event) => event.type === "done" || event.type === "error",
72
- (event) => {
71
+ event => event.type === "done" || event.type === "error",
72
+ event => {
73
73
  if (event.type === "done") {
74
74
  return event.message;
75
75
  } else if (event.type === "error") {
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * Anthropic OAuth flow (Claude Pro/Max)
3
3
  */
4
-
5
4
  import { OAuthCallbackFlow } from "./callback-server";
6
5
  import { generatePKCE } from "./pkce";
7
6
  import type { OAuthController, OAuthCredentials } from "./types";
@@ -10,7 +10,6 @@
10
10
  * - generateAuthUrl(): Build provider-specific authorization URL
11
11
  * - exchangeToken(): Exchange authorization code for tokens
12
12
  */
13
-
14
13
  import templateHtml from "./oauth.html" with { type: "text" };
15
14
  import type { OAuthController, OAuthCredentials } from "./types";
16
15
 
@@ -63,7 +62,7 @@ export abstract class OAuthCallbackFlow {
63
62
  const bytes = new Uint8Array(16);
64
63
  crypto.getRandomValues(bytes);
65
64
  return Array.from(bytes)
66
- .map((value) => value.toString(16).padStart(2, "0"))
65
+ .map(value => value.toString(16).padStart(2, "0"))
67
66
  .join("");
68
67
  }
69
68
 
@@ -125,7 +124,7 @@ export abstract class OAuthCallbackFlow {
125
124
  hostname: DEFAULT_HOSTNAME,
126
125
  port,
127
126
  reusePort: false,
128
- fetch: (req) => this.handleCallback(req, expectedState),
127
+ fetch: req => this.handleCallback(req, expectedState),
129
128
  });
130
129
  }
131
130
 
@@ -1,9 +1,8 @@
1
1
  /**
2
2
  * GitHub Copilot OAuth flow
3
3
  */
4
-
5
- import { getModels } from "@oh-my-pi/pi-ai/models";
6
4
  import { abortableSleep } from "@oh-my-pi/pi-utils";
5
+ import { getModels } from "../../models";
7
6
  import type { OAuthCredentials } from "./types";
8
7
 
9
8
  const decode = (s: string) => atob(s);
@@ -279,7 +278,7 @@ async function enableAllGitHubCopilotModels(
279
278
  ): Promise<void> {
280
279
  const models = getModels("github-copilot");
281
280
  await Promise.all(
282
- models.map(async (model) => {
281
+ models.map(async model => {
283
282
  const success = await enableGitHubCopilotModel(token, model.id, enterpriseDomain);
284
283
  onProgress?.(model.id, success);
285
284
  }),
@@ -2,7 +2,6 @@
2
2
  * Antigravity OAuth flow (Gemini 3, Claude, GPT-OSS via Google Cloud)
3
3
  * Uses different OAuth credentials than google-gemini-cli for access to additional models.
4
4
  */
5
-
6
5
  import { OAuthCallbackFlow } from "./callback-server";
7
6
  import { generatePKCE } from "./pkce";
8
7
  import type { OAuthController, OAuthCredentials } from "./types";
@@ -2,7 +2,6 @@
2
2
  * Gemini CLI OAuth flow (Google Cloud Code Assist)
3
3
  * Standard Gemini models only (gemini-2.0-flash, gemini-2.5-*)
4
4
  */
5
-
6
5
  import { OAuthCallbackFlow } from "./callback-server";
7
6
  import { generatePKCE } from "./pkce";
8
7
  import type { OAuthController, OAuthCredentials } from "./types";
@@ -49,7 +48,7 @@ interface GoogleRpcErrorResponse {
49
48
 
50
49
  function getDefaultTier(allowedTiers?: Array<{ id?: string; isDefault?: boolean }>): { id?: string } {
51
50
  if (!allowedTiers || allowedTiers.length === 0) return { id: TIER_LEGACY };
52
- const defaultTier = allowedTiers.find((t) => t.isDefault);
51
+ const defaultTier = allowedTiers.find(t => t.isDefault);
53
52
  return defaultTier ?? { id: TIER_LEGACY };
54
53
  }
55
54
 
@@ -58,7 +57,7 @@ function isVpcScAffectedUser(payload: unknown): boolean {
58
57
  if (!("error" in payload)) return false;
59
58
  const error = (payload as GoogleRpcErrorResponse).error;
60
59
  if (!error?.details || !Array.isArray(error.details)) return false;
61
- return error.details.some((detail) => detail.reason === "SECURITY_POLICY_VIOLATED");
60
+ return error.details.some(detail => detail.reason === "SECURITY_POLICY_VIOLATED");
62
61
  }
63
62
 
64
63
  async function pollOperation(
@@ -1,3 +1,14 @@
1
+ // ============================================================================
2
+ // High-level API
3
+ // ============================================================================
4
+ import { refreshAnthropicToken } from "./anthropic";
5
+ import { refreshCursorToken } from "./cursor";
6
+ import { refreshGitHubCopilotToken } from "./github-copilot";
7
+ import { refreshAntigravityToken } from "./google-antigravity";
8
+ import { refreshGoogleCloudToken } from "./google-gemini-cli";
9
+ import { refreshOpenAICodexToken } from "./openai-codex";
10
+ import type { OAuthCredentials, OAuthProvider, OAuthProviderInfo } from "./types";
11
+
1
12
  /**
2
13
  * OAuth credential management for AI providers.
3
14
  *
@@ -36,18 +47,6 @@ export { loginOpenAICodex, refreshOpenAICodexToken } from "./openai-codex";
36
47
 
37
48
  export * from "./types";
38
49
 
39
- // ============================================================================
40
- // High-level API
41
- // ============================================================================
42
-
43
- import { refreshAnthropicToken } from "./anthropic";
44
- import { refreshCursorToken } from "./cursor";
45
- import { refreshGitHubCopilotToken } from "./github-copilot";
46
- import { refreshAntigravityToken } from "./google-antigravity";
47
- import { refreshGoogleCloudToken } from "./google-gemini-cli";
48
- import { refreshOpenAICodexToken } from "./openai-codex";
49
- import type { OAuthCredentials, OAuthProvider, OAuthProviderInfo } from "./types";
50
-
51
50
  /**
52
51
  * Refresh token for any OAuth provider.
53
52
  * Saves the new credentials and returns the new access token.
@@ -1,7 +1,6 @@
1
1
  /**
2
2
  * OpenAI Codex (ChatGPT OAuth) flow
3
3
  */
4
-
5
4
  import { OAuthCallbackFlow } from "./callback-server";
6
5
  import { generatePKCE } from "./pkce";
7
6
  import type { OAuthController, OAuthCredentials } from "./types";
@@ -1,4 +1,4 @@
1
- import type { AssistantMessage } from "@oh-my-pi/pi-ai/types";
1
+ import type { AssistantMessage } from "../types";
2
2
 
3
3
  /**
4
4
  * Regex patterns to detect context overflow errors from different providers.
@@ -85,7 +85,7 @@ export function isContextOverflow(message: AssistantMessage, contextWindow?: num
85
85
  // Case 1: Check error message patterns
86
86
  if (message.stopReason === "error" && message.errorMessage) {
87
87
  // Check known patterns
88
- if (OVERFLOW_PATTERNS.some((p) => p.test(message.errorMessage!))) {
88
+ if (OVERFLOW_PATTERNS.some(p => p.test(message.errorMessage!))) {
89
89
  return true;
90
90
  }
91
91