feat(ai): add Models runtime with provider-owned auth (phase 1)
New Models/MutableModels/createModels collection: provider map, async
model listing (best-effort aggregation), getAuth decision tree with
double-checked locked OAuth refresh, stream/complete with per-field
auth merge over lazyStream.
Auth substrate: ProviderAuth { apiKey?, oauth? }, one type-tagged
credential per provider, CredentialStore (read/modify/delete; modify
is the only write path, serialized RMW), OAuthAuth login/refresh/toAuth
split, prompt()/notify() login callbacks, browser-safe default
AuthContext.
types.ts: Provider alias renamed to ProviderId; ApiOptionsMap and
ApiStreamOptions<TApi> for typed per-API stream options; hasApi()
runtime narrowing guard.
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
import type { AuthContext } from "./types.ts";
|
||||
|
||||
interface NodeFsModule {
|
||||
access(path: string): Promise<void>;
|
||||
}
|
||||
|
||||
interface NodeOsModule {
|
||||
homedir(): string;
|
||||
}
|
||||
|
||||
// Variable specifier so browser bundlers do not try to resolve node builtins.
|
||||
const importNodeModule = (specifier: string): Promise<unknown> => import(specifier);
|
||||
|
||||
function getProcessEnv(): Record<string, string | undefined> | undefined {
|
||||
const proc = (globalThis as { process?: { env?: Record<string, string | undefined> } }).process;
|
||||
return proc?.env;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default auth context: env vars from `process.env` (undefined in browsers),
|
||||
* file existence via node:fs (always false in browsers).
|
||||
*/
|
||||
export function defaultProviderAuthContext(): AuthContext {
|
||||
return {
|
||||
async env(name: string): Promise<string | undefined> {
|
||||
const value = getProcessEnv()?.[name];
|
||||
return typeof value === "string" && value.trim().length > 0 ? value : undefined;
|
||||
},
|
||||
|
||||
async fileExists(path: string): Promise<boolean> {
|
||||
try {
|
||||
const fs = (await importNodeModule("node:fs/promises")) as NodeFsModule;
|
||||
let resolved = path;
|
||||
if (resolved.startsWith("~")) {
|
||||
const os = (await importNodeModule("node:os")) as NodeOsModule;
|
||||
resolved = os.homedir() + resolved.slice(1);
|
||||
}
|
||||
await fs.access(resolved);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user