edge-pi-cli 0.1.1
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/dist/auth/anthropic-oauth.d.ts +10 -0
- package/dist/auth/anthropic-oauth.d.ts.map +1 -0
- package/dist/auth/anthropic-oauth.js +97 -0
- package/dist/auth/anthropic-oauth.js.map +1 -0
- package/dist/auth/auth-storage.d.ts +46 -0
- package/dist/auth/auth-storage.d.ts.map +1 -0
- package/dist/auth/auth-storage.js +213 -0
- package/dist/auth/auth-storage.js.map +1 -0
- package/dist/auth/github-copilot-oauth.d.ts +8 -0
- package/dist/auth/github-copilot-oauth.d.ts.map +1 -0
- package/dist/auth/github-copilot-oauth.js +131 -0
- package/dist/auth/github-copilot-oauth.js.map +1 -0
- package/dist/auth/index.d.ts +6 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +5 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/openai-codex-oauth.d.ts +8 -0
- package/dist/auth/openai-codex-oauth.d.ts.map +1 -0
- package/dist/auth/openai-codex-oauth.js +131 -0
- package/dist/auth/openai-codex-oauth.js.map +1 -0
- package/dist/auth/types.d.ts +41 -0
- package/dist/auth/types.d.ts.map +1 -0
- package/dist/auth/types.js +5 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/cli/args.d.ts +35 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/args.js +191 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +8 -0
- package/dist/cli.js.map +1 -0
- package/dist/context.d.ts +16 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +38 -0
- package/dist/context.js.map +1 -0
- package/dist/main.d.ts +8 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +313 -0
- package/dist/main.js.map +1 -0
- package/dist/model-factory.d.ts +45 -0
- package/dist/model-factory.d.ts.map +1 -0
- package/dist/model-factory.js +175 -0
- package/dist/model-factory.js.map +1 -0
- package/dist/modes/interactive/bash-helpers.d.ts +31 -0
- package/dist/modes/interactive/bash-helpers.d.ts.map +1 -0
- package/dist/modes/interactive/bash-helpers.js +68 -0
- package/dist/modes/interactive/bash-helpers.js.map +1 -0
- package/dist/modes/interactive/components/assistant-message.d.ts +19 -0
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/assistant-message.js +54 -0
- package/dist/modes/interactive/components/assistant-message.js.map +1 -0
- package/dist/modes/interactive/components/bash-execution.d.ts +18 -0
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -0
- package/dist/modes/interactive/components/bash-execution.js +77 -0
- package/dist/modes/interactive/components/bash-execution.js.map +1 -0
- package/dist/modes/interactive/components/compaction-summary.d.ts +18 -0
- package/dist/modes/interactive/components/compaction-summary.d.ts.map +1 -0
- package/dist/modes/interactive/components/compaction-summary.js +45 -0
- package/dist/modes/interactive/components/compaction-summary.js.map +1 -0
- package/dist/modes/interactive/components/footer.d.ts +20 -0
- package/dist/modes/interactive/components/footer.d.ts.map +1 -0
- package/dist/modes/interactive/components/footer.js +82 -0
- package/dist/modes/interactive/components/footer.js.map +1 -0
- package/dist/modes/interactive/components/tool-execution.d.ts +30 -0
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -0
- package/dist/modes/interactive/components/tool-execution.js +133 -0
- package/dist/modes/interactive/components/tool-execution.js.map +1 -0
- package/dist/modes/interactive/components/user-message.d.ts +9 -0
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/user-message.js +17 -0
- package/dist/modes/interactive/components/user-message.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts +49 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -0
- package/dist/modes/interactive/interactive-mode.js +1397 -0
- package/dist/modes/interactive/interactive-mode.js.map +1 -0
- package/dist/modes/interactive/theme.d.ts +26 -0
- package/dist/modes/interactive/theme.d.ts.map +1 -0
- package/dist/modes/interactive/theme.js +64 -0
- package/dist/modes/interactive/theme.js.map +1 -0
- package/dist/modes/interactive-mode.d.ts +5 -0
- package/dist/modes/interactive-mode.d.ts.map +1 -0
- package/dist/modes/interactive-mode.js +5 -0
- package/dist/modes/interactive-mode.js.map +1 -0
- package/dist/modes/print-mode.d.ts +20 -0
- package/dist/modes/print-mode.d.ts.map +1 -0
- package/dist/modes/print-mode.js +56 -0
- package/dist/modes/print-mode.js.map +1 -0
- package/dist/prompts.d.ts +53 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +132 -0
- package/dist/prompts.js.map +1 -0
- package/dist/settings.d.ts +34 -0
- package/dist/settings.d.ts.map +1 -0
- package/dist/settings.js +73 -0
- package/dist/settings.js.map +1 -0
- package/dist/skills.d.ts +51 -0
- package/dist/skills.d.ts.map +1 -0
- package/dist/skills.js +304 -0
- package/dist/skills.js.map +1 -0
- package/dist/utils/bash-executor.d.ts +32 -0
- package/dist/utils/bash-executor.d.ts.map +1 -0
- package/dist/utils/bash-executor.js +166 -0
- package/dist/utils/bash-executor.js.map +1 -0
- package/dist/utils/clipboard-image.d.ts +24 -0
- package/dist/utils/clipboard-image.d.ts.map +1 -0
- package/dist/utils/clipboard-image.js +211 -0
- package/dist/utils/clipboard-image.js.map +1 -0
- package/dist/utils/find-fd.d.ts +12 -0
- package/dist/utils/find-fd.d.ts.map +1 -0
- package/dist/utils/find-fd.js +33 -0
- package/dist/utils/find-fd.js.map +1 -0
- package/dist/utils/frontmatter.d.ts +7 -0
- package/dist/utils/frontmatter.d.ts.map +1 -0
- package/dist/utils/frontmatter.js +25 -0
- package/dist/utils/frontmatter.js.map +1 -0
- package/package.json +39 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA8HH,wBAAsB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,iBA+MxC","sourcesContent":["/**\n * Main entry point for the edge-pi CLI.\n *\n * Handles argument parsing, model creation, skill loading,\n * session management, auth, and mode dispatch.\n */\n\nimport { existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport chalk from \"chalk\";\nimport type { CodingAgentConfig, ModelMessage } from \"edge-pi\";\nimport { CodingAgent, SessionManager } from \"edge-pi\";\nimport {\n\tAuthStorage,\n\tanthropicOAuthProvider,\n\tgithubCopilotOAuthProvider,\n\topenaiCodexOAuthProvider,\n} from \"./auth/index.js\";\nimport { parseArgs, printHelp, printModels } from \"./cli/args.js\";\nimport { loadContextFiles } from \"./context.js\";\nimport { createModel } from \"./model-factory.js\";\nimport { runInteractiveMode } from \"./modes/interactive-mode.js\";\nimport { runPrintMode } from \"./modes/print-mode.js\";\nimport { loadPrompts } from \"./prompts.js\";\nimport { SettingsManager } from \"./settings.js\";\nimport { formatSkillsForPrompt, loadSkills, type Skill } from \"./skills.js\";\nimport { findFd } from \"./utils/find-fd.js\";\n\nconst VERSION = \"0.1.0\";\nconst CONFIG_DIR_NAME = \".pi\";\n\nfunction getAgentDir(): string {\n\tconst envDir = process.env.PI_CODING_AGENT_DIR;\n\tif (envDir) {\n\t\tif (envDir === \"~\") return homedir();\n\t\tif (envDir.startsWith(\"~/\")) return homedir() + envDir.slice(1);\n\t\treturn envDir;\n\t}\n\treturn join(homedir(), CONFIG_DIR_NAME, \"agent\");\n}\n\nfunction getSessionsDir(): string {\n\treturn join(getAgentDir(), \"sessions\");\n}\n\nfunction getAuthPath(): string {\n\treturn join(getAgentDir(), \"auth.json\");\n}\n\n/**\n * Read all content from piped stdin.\n * Returns undefined if stdin is a TTY.\n */\nasync function readPipedStdin(): Promise<string | undefined> {\n\tif (process.stdin.isTTY) {\n\t\treturn undefined;\n\t}\n\treturn new Promise((resolve) => {\n\t\tlet data = \"\";\n\t\tprocess.stdin.setEncoding(\"utf8\");\n\t\tprocess.stdin.on(\"data\", (chunk) => {\n\t\t\tdata += chunk;\n\t\t});\n\t\tprocess.stdin.on(\"end\", () => {\n\t\t\tresolve(data.trim() || undefined);\n\t\t});\n\t\tprocess.stdin.resume();\n\t});\n}\n\n/**\n * Process @file arguments into text content.\n */\nfunction processFileArgs(fileArgs: string[]): string {\n\tconst parts: string[] = [];\n\tfor (const filePath of fileArgs) {\n\t\tconst resolved = resolve(filePath);\n\t\tif (!existsSync(resolved)) {\n\t\t\tconsole.error(chalk.yellow(`Warning: File not found: ${resolved}`));\n\t\t\tcontinue;\n\t\t}\n\t\ttry {\n\t\t\tconst content = readFileSync(resolved, \"utf-8\");\n\t\t\tparts.push(`<file path=\"${resolved}\">\\n${content}\\n</file>`);\n\t\t} catch (error) {\n\t\t\tconsole.error(chalk.yellow(`Warning: Could not read ${resolved}: ${(error as Error).message}`));\n\t\t}\n\t}\n\treturn parts.length > 0 ? `${parts.join(\"\\n\\n\")}\\n\\n` : \"\";\n}\n\n/**\n * Create a session directory path based on the current working directory.\n */\nfunction getProjectSessionDir(cwd: string): string {\n\tconst sanitized = cwd.replace(/\\//g, \"--\").replace(/^--/, \"\");\n\treturn join(getSessionsDir(), sanitized);\n}\n\n/**\n * Find the most recent session file in a directory.\n */\nfunction findRecentSession(sessionDir: string): string | undefined {\n\tif (!existsSync(sessionDir)) return undefined;\n\ttry {\n\t\tconst files = readdirSync(sessionDir)\n\t\t\t.filter((f: string) => f.endsWith(\".jsonl\"))\n\t\t\t.map((f: string) => ({\n\t\t\t\tname: f,\n\t\t\t\tpath: join(sessionDir, f),\n\t\t\t\tmtime: statSync(join(sessionDir, f)).mtime.getTime(),\n\t\t\t}))\n\t\t\t.sort((a: { mtime: number }, b: { mtime: number }) => b.mtime - a.mtime);\n\t\treturn files[0]?.path;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * Create and configure AuthStorage with built-in OAuth providers.\n */\nfunction createAuthStorage(): AuthStorage {\n\tconst authStorage = new AuthStorage(getAuthPath());\n\tauthStorage.registerProvider(anthropicOAuthProvider);\n\tauthStorage.registerProvider(githubCopilotOAuthProvider);\n\tauthStorage.registerProvider(openaiCodexOAuthProvider);\n\treturn authStorage;\n}\n\nexport async function main(args: string[]) {\n\tconst parsed = parseArgs(args);\n\n\tif (parsed.version) {\n\t\tconsole.log(VERSION);\n\t\treturn;\n\t}\n\n\tif (parsed.help) {\n\t\tprintHelp();\n\t\treturn;\n\t}\n\n\tif (parsed.listModels) {\n\t\tprintModels();\n\t\treturn;\n\t}\n\n\t// Read piped stdin\n\tconst stdinContent = await readPipedStdin();\n\tif (stdinContent !== undefined) {\n\t\tparsed.print = true;\n\t\tparsed.messages.unshift(stdinContent);\n\t}\n\n\t// Process @file arguments\n\tlet initialMessage: string | undefined;\n\tif (parsed.fileArgs.length > 0) {\n\t\tconst fileContent = processFileArgs(parsed.fileArgs);\n\t\tif (parsed.messages.length > 0) {\n\t\t\tinitialMessage = fileContent + parsed.messages.shift();\n\t\t} else {\n\t\t\tinitialMessage = fileContent;\n\t\t}\n\t}\n\n\tconst cwd = process.cwd();\n\tconst isInteractive = !parsed.print && parsed.mode === undefined;\n\tconst mode = parsed.mode || \"text\";\n\n\t// Set up auth storage\n\tconst authStorage = createAuthStorage();\n\n\t// Set up settings persistence\n\tconst settingsManager = SettingsManager.create(getAgentDir());\n\n\t// Apply CLI --api-key override\n\tif (parsed.apiKey && parsed.provider) {\n\t\tauthStorage.setRuntimeApiKey(parsed.provider, parsed.apiKey);\n\t}\n\n\t// Create model (async - may resolve OAuth tokens)\n\t// Fall back to saved defaults when CLI args are not specified\n\tconst { model, provider, modelId } = await createModel({\n\t\tprovider: parsed.provider ?? settingsManager.getDefaultProvider(),\n\t\tmodel: parsed.model ?? settingsManager.getDefaultModel(),\n\t\tapiKey: parsed.apiKey,\n\t\tauthStorage,\n\t});\n\n\t// Load skills\n\tlet skills: Skill[] = [];\n\tif (!parsed.noSkills) {\n\t\tconst skillResult = loadSkills({\n\t\t\tcwd,\n\t\t\tskillPaths: parsed.skills,\n\t\t\tincludeDefaults: true,\n\t\t});\n\t\tskills = skillResult.skills;\n\n\t\tif (parsed.verbose && skillResult.diagnostics.length > 0) {\n\t\t\tfor (const d of skillResult.diagnostics) {\n\t\t\t\tconsole.error(chalk.yellow(`Skill warning: ${d.message} (${d.path})`));\n\t\t\t}\n\t\t}\n\t}\n\n\t// Load context files (AGENTS.md)\n\tconst contextFiles = loadContextFiles(cwd);\n\tif (parsed.verbose && contextFiles.length > 0) {\n\t\tconsole.log(chalk.dim(`Loaded ${contextFiles.length} context file(s).`));\n\t}\n\n\t// Load prompt templates\n\tconst promptsResult = loadPrompts({ cwd });\n\tconst prompts = promptsResult.prompts;\n\tif (parsed.verbose && promptsResult.diagnostics.length > 0) {\n\t\tfor (const d of promptsResult.diagnostics) {\n\t\t\tconsole.error(chalk.yellow(`Prompt warning: ${d.message} (${d.path})`));\n\t\t}\n\t}\n\n\t// Find fd binary for @ file autocomplete\n\tconst fdPath = findFd();\n\n\t// Build system prompt additions\n\tconst skillsPrompt = formatSkillsForPrompt(skills);\n\tconst appendParts: string[] = [];\n\tif (skillsPrompt) {\n\t\tappendParts.push(skillsPrompt);\n\t}\n\tif (parsed.appendSystemPrompt) {\n\t\tappendParts.push(parsed.appendSystemPrompt);\n\t}\n\tconst appendSystemPrompt = appendParts.length > 0 ? appendParts.join(\"\\n\\n\") : undefined;\n\n\t// Set up session manager\n\tconst sessionDir = parsed.sessionDir ?? getProjectSessionDir(cwd);\n\tlet sessionManager: SessionManager | undefined;\n\tif (parsed.noSession) {\n\t\tsessionManager = SessionManager.inMemory(cwd);\n\t} else if (parsed.session) {\n\t\tsessionManager = SessionManager.open(parsed.session, parsed.sessionDir);\n\t} else if (parsed.resume) {\n\t\t// Start a new session; the TUI will show the session picker on startup\n\t\tsessionManager = SessionManager.create(cwd, sessionDir);\n\t} else if (parsed.continue) {\n\t\tconst recentFile = findRecentSession(sessionDir);\n\t\tif (recentFile) {\n\t\t\tsessionManager = SessionManager.open(recentFile, sessionDir);\n\t\t} else {\n\t\t\tif (parsed.verbose) {\n\t\t\t\tconsole.log(chalk.dim(\"No previous session found, starting new.\"));\n\t\t\t}\n\t\t\tsessionManager = SessionManager.create(cwd, sessionDir);\n\t\t}\n\t} else {\n\t\tsessionManager = SessionManager.create(cwd, sessionDir);\n\t}\n\n\t// Restore messages from session if continuing\n\tlet restoredMessages: ModelMessage[] = [];\n\tif (parsed.continue && sessionManager) {\n\t\tconst context = sessionManager.buildSessionContext();\n\t\trestoredMessages = context.messages;\n\t}\n\n\t// Create agent config\n\tconst agentConfig: CodingAgentConfig = {\n\t\tmodel,\n\t\tcwd,\n\t\ttoolSet: parsed.toolSet ?? \"coding\",\n\t\tthinkingLevel: parsed.thinking,\n\t};\n\n\tif (parsed.systemPrompt) {\n\t\tagentConfig.systemPrompt = parsed.systemPrompt;\n\t\tif (appendSystemPrompt) {\n\t\t\tagentConfig.systemPrompt += `\\n\\n${appendSystemPrompt}`;\n\t\t}\n\t} else {\n\t\tagentConfig.systemPromptOptions = {\n\t\t\tappendSystemPrompt,\n\t\t\tcontextFiles,\n\t\t};\n\t}\n\n\t// Create agent\n\tconst agent = new CodingAgent(agentConfig);\n\n\t// Restore session messages\n\tif (restoredMessages.length > 0) {\n\t\tagent.setMessages(restoredMessages);\n\t\tif (parsed.verbose) {\n\t\t\tconsole.log(chalk.dim(`Restored ${restoredMessages.length} messages from session.`));\n\t\t}\n\t}\n\n\t// Dispatch to mode\n\tif (isInteractive) {\n\t\tawait runInteractiveMode(agent, {\n\t\t\tinitialMessage,\n\t\t\tinitialMessages: parsed.messages,\n\t\t\tsessionManager,\n\t\t\tsessionDir,\n\t\t\tagentConfig,\n\t\t\tresumeOnStart: parsed.resume,\n\t\t\tskills,\n\t\t\tcontextFiles,\n\t\t\tprompts,\n\t\t\tverbose: parsed.verbose,\n\t\t\tprovider,\n\t\t\tmodelId,\n\t\t\tauthStorage,\n\t\t\tsettingsManager,\n\t\t\tfdPath,\n\t\t\tonModelChange: async (newProvider: string, newModelId: string) => {\n\t\t\t\tconst { model: newModel } = await createModel({\n\t\t\t\t\tprovider: newProvider,\n\t\t\t\t\tmodel: newModelId,\n\t\t\t\t\tauthStorage,\n\t\t\t\t});\n\t\t\t\treturn new CodingAgent({\n\t\t\t\t\t...agentConfig,\n\t\t\t\t\tmodel: newModel,\n\t\t\t\t});\n\t\t\t},\n\t\t});\n\t} else {\n\t\tawait runPrintMode(agent, {\n\t\t\tmode,\n\t\t\tmessages: parsed.messages,\n\t\t\tinitialMessage,\n\t\t\tsessionManager,\n\t\t});\n\t\tprocess.exit(0);\n\t}\n}\n"]}
|
package/dist/main.js
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main entry point for the edge-pi CLI.
|
|
3
|
+
*
|
|
4
|
+
* Handles argument parsing, model creation, skill loading,
|
|
5
|
+
* session management, auth, and mode dispatch.
|
|
6
|
+
*/
|
|
7
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
8
|
+
import { homedir } from "node:os";
|
|
9
|
+
import { join, resolve } from "node:path";
|
|
10
|
+
import chalk from "chalk";
|
|
11
|
+
import { CodingAgent, SessionManager } from "edge-pi";
|
|
12
|
+
import { AuthStorage, anthropicOAuthProvider, githubCopilotOAuthProvider, openaiCodexOAuthProvider, } from "./auth/index.js";
|
|
13
|
+
import { parseArgs, printHelp, printModels } from "./cli/args.js";
|
|
14
|
+
import { loadContextFiles } from "./context.js";
|
|
15
|
+
import { createModel } from "./model-factory.js";
|
|
16
|
+
import { runInteractiveMode } from "./modes/interactive-mode.js";
|
|
17
|
+
import { runPrintMode } from "./modes/print-mode.js";
|
|
18
|
+
import { loadPrompts } from "./prompts.js";
|
|
19
|
+
import { SettingsManager } from "./settings.js";
|
|
20
|
+
import { formatSkillsForPrompt, loadSkills } from "./skills.js";
|
|
21
|
+
import { findFd } from "./utils/find-fd.js";
|
|
22
|
+
const VERSION = "0.1.0";
|
|
23
|
+
const CONFIG_DIR_NAME = ".pi";
|
|
24
|
+
function getAgentDir() {
|
|
25
|
+
const envDir = process.env.PI_CODING_AGENT_DIR;
|
|
26
|
+
if (envDir) {
|
|
27
|
+
if (envDir === "~")
|
|
28
|
+
return homedir();
|
|
29
|
+
if (envDir.startsWith("~/"))
|
|
30
|
+
return homedir() + envDir.slice(1);
|
|
31
|
+
return envDir;
|
|
32
|
+
}
|
|
33
|
+
return join(homedir(), CONFIG_DIR_NAME, "agent");
|
|
34
|
+
}
|
|
35
|
+
function getSessionsDir() {
|
|
36
|
+
return join(getAgentDir(), "sessions");
|
|
37
|
+
}
|
|
38
|
+
function getAuthPath() {
|
|
39
|
+
return join(getAgentDir(), "auth.json");
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Read all content from piped stdin.
|
|
43
|
+
* Returns undefined if stdin is a TTY.
|
|
44
|
+
*/
|
|
45
|
+
async function readPipedStdin() {
|
|
46
|
+
if (process.stdin.isTTY) {
|
|
47
|
+
return undefined;
|
|
48
|
+
}
|
|
49
|
+
return new Promise((resolve) => {
|
|
50
|
+
let data = "";
|
|
51
|
+
process.stdin.setEncoding("utf8");
|
|
52
|
+
process.stdin.on("data", (chunk) => {
|
|
53
|
+
data += chunk;
|
|
54
|
+
});
|
|
55
|
+
process.stdin.on("end", () => {
|
|
56
|
+
resolve(data.trim() || undefined);
|
|
57
|
+
});
|
|
58
|
+
process.stdin.resume();
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Process @file arguments into text content.
|
|
63
|
+
*/
|
|
64
|
+
function processFileArgs(fileArgs) {
|
|
65
|
+
const parts = [];
|
|
66
|
+
for (const filePath of fileArgs) {
|
|
67
|
+
const resolved = resolve(filePath);
|
|
68
|
+
if (!existsSync(resolved)) {
|
|
69
|
+
console.error(chalk.yellow(`Warning: File not found: ${resolved}`));
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
const content = readFileSync(resolved, "utf-8");
|
|
74
|
+
parts.push(`<file path="${resolved}">\n${content}\n</file>`);
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
console.error(chalk.yellow(`Warning: Could not read ${resolved}: ${error.message}`));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return parts.length > 0 ? `${parts.join("\n\n")}\n\n` : "";
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Create a session directory path based on the current working directory.
|
|
84
|
+
*/
|
|
85
|
+
function getProjectSessionDir(cwd) {
|
|
86
|
+
const sanitized = cwd.replace(/\//g, "--").replace(/^--/, "");
|
|
87
|
+
return join(getSessionsDir(), sanitized);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Find the most recent session file in a directory.
|
|
91
|
+
*/
|
|
92
|
+
function findRecentSession(sessionDir) {
|
|
93
|
+
if (!existsSync(sessionDir))
|
|
94
|
+
return undefined;
|
|
95
|
+
try {
|
|
96
|
+
const files = readdirSync(sessionDir)
|
|
97
|
+
.filter((f) => f.endsWith(".jsonl"))
|
|
98
|
+
.map((f) => ({
|
|
99
|
+
name: f,
|
|
100
|
+
path: join(sessionDir, f),
|
|
101
|
+
mtime: statSync(join(sessionDir, f)).mtime.getTime(),
|
|
102
|
+
}))
|
|
103
|
+
.sort((a, b) => b.mtime - a.mtime);
|
|
104
|
+
return files[0]?.path;
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Create and configure AuthStorage with built-in OAuth providers.
|
|
112
|
+
*/
|
|
113
|
+
function createAuthStorage() {
|
|
114
|
+
const authStorage = new AuthStorage(getAuthPath());
|
|
115
|
+
authStorage.registerProvider(anthropicOAuthProvider);
|
|
116
|
+
authStorage.registerProvider(githubCopilotOAuthProvider);
|
|
117
|
+
authStorage.registerProvider(openaiCodexOAuthProvider);
|
|
118
|
+
return authStorage;
|
|
119
|
+
}
|
|
120
|
+
export async function main(args) {
|
|
121
|
+
const parsed = parseArgs(args);
|
|
122
|
+
if (parsed.version) {
|
|
123
|
+
console.log(VERSION);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (parsed.help) {
|
|
127
|
+
printHelp();
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
if (parsed.listModels) {
|
|
131
|
+
printModels();
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
// Read piped stdin
|
|
135
|
+
const stdinContent = await readPipedStdin();
|
|
136
|
+
if (stdinContent !== undefined) {
|
|
137
|
+
parsed.print = true;
|
|
138
|
+
parsed.messages.unshift(stdinContent);
|
|
139
|
+
}
|
|
140
|
+
// Process @file arguments
|
|
141
|
+
let initialMessage;
|
|
142
|
+
if (parsed.fileArgs.length > 0) {
|
|
143
|
+
const fileContent = processFileArgs(parsed.fileArgs);
|
|
144
|
+
if (parsed.messages.length > 0) {
|
|
145
|
+
initialMessage = fileContent + parsed.messages.shift();
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
initialMessage = fileContent;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const cwd = process.cwd();
|
|
152
|
+
const isInteractive = !parsed.print && parsed.mode === undefined;
|
|
153
|
+
const mode = parsed.mode || "text";
|
|
154
|
+
// Set up auth storage
|
|
155
|
+
const authStorage = createAuthStorage();
|
|
156
|
+
// Set up settings persistence
|
|
157
|
+
const settingsManager = SettingsManager.create(getAgentDir());
|
|
158
|
+
// Apply CLI --api-key override
|
|
159
|
+
if (parsed.apiKey && parsed.provider) {
|
|
160
|
+
authStorage.setRuntimeApiKey(parsed.provider, parsed.apiKey);
|
|
161
|
+
}
|
|
162
|
+
// Create model (async - may resolve OAuth tokens)
|
|
163
|
+
// Fall back to saved defaults when CLI args are not specified
|
|
164
|
+
const { model, provider, modelId } = await createModel({
|
|
165
|
+
provider: parsed.provider ?? settingsManager.getDefaultProvider(),
|
|
166
|
+
model: parsed.model ?? settingsManager.getDefaultModel(),
|
|
167
|
+
apiKey: parsed.apiKey,
|
|
168
|
+
authStorage,
|
|
169
|
+
});
|
|
170
|
+
// Load skills
|
|
171
|
+
let skills = [];
|
|
172
|
+
if (!parsed.noSkills) {
|
|
173
|
+
const skillResult = loadSkills({
|
|
174
|
+
cwd,
|
|
175
|
+
skillPaths: parsed.skills,
|
|
176
|
+
includeDefaults: true,
|
|
177
|
+
});
|
|
178
|
+
skills = skillResult.skills;
|
|
179
|
+
if (parsed.verbose && skillResult.diagnostics.length > 0) {
|
|
180
|
+
for (const d of skillResult.diagnostics) {
|
|
181
|
+
console.error(chalk.yellow(`Skill warning: ${d.message} (${d.path})`));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
// Load context files (AGENTS.md)
|
|
186
|
+
const contextFiles = loadContextFiles(cwd);
|
|
187
|
+
if (parsed.verbose && contextFiles.length > 0) {
|
|
188
|
+
console.log(chalk.dim(`Loaded ${contextFiles.length} context file(s).`));
|
|
189
|
+
}
|
|
190
|
+
// Load prompt templates
|
|
191
|
+
const promptsResult = loadPrompts({ cwd });
|
|
192
|
+
const prompts = promptsResult.prompts;
|
|
193
|
+
if (parsed.verbose && promptsResult.diagnostics.length > 0) {
|
|
194
|
+
for (const d of promptsResult.diagnostics) {
|
|
195
|
+
console.error(chalk.yellow(`Prompt warning: ${d.message} (${d.path})`));
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
// Find fd binary for @ file autocomplete
|
|
199
|
+
const fdPath = findFd();
|
|
200
|
+
// Build system prompt additions
|
|
201
|
+
const skillsPrompt = formatSkillsForPrompt(skills);
|
|
202
|
+
const appendParts = [];
|
|
203
|
+
if (skillsPrompt) {
|
|
204
|
+
appendParts.push(skillsPrompt);
|
|
205
|
+
}
|
|
206
|
+
if (parsed.appendSystemPrompt) {
|
|
207
|
+
appendParts.push(parsed.appendSystemPrompt);
|
|
208
|
+
}
|
|
209
|
+
const appendSystemPrompt = appendParts.length > 0 ? appendParts.join("\n\n") : undefined;
|
|
210
|
+
// Set up session manager
|
|
211
|
+
const sessionDir = parsed.sessionDir ?? getProjectSessionDir(cwd);
|
|
212
|
+
let sessionManager;
|
|
213
|
+
if (parsed.noSession) {
|
|
214
|
+
sessionManager = SessionManager.inMemory(cwd);
|
|
215
|
+
}
|
|
216
|
+
else if (parsed.session) {
|
|
217
|
+
sessionManager = SessionManager.open(parsed.session, parsed.sessionDir);
|
|
218
|
+
}
|
|
219
|
+
else if (parsed.resume) {
|
|
220
|
+
// Start a new session; the TUI will show the session picker on startup
|
|
221
|
+
sessionManager = SessionManager.create(cwd, sessionDir);
|
|
222
|
+
}
|
|
223
|
+
else if (parsed.continue) {
|
|
224
|
+
const recentFile = findRecentSession(sessionDir);
|
|
225
|
+
if (recentFile) {
|
|
226
|
+
sessionManager = SessionManager.open(recentFile, sessionDir);
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
if (parsed.verbose) {
|
|
230
|
+
console.log(chalk.dim("No previous session found, starting new."));
|
|
231
|
+
}
|
|
232
|
+
sessionManager = SessionManager.create(cwd, sessionDir);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
sessionManager = SessionManager.create(cwd, sessionDir);
|
|
237
|
+
}
|
|
238
|
+
// Restore messages from session if continuing
|
|
239
|
+
let restoredMessages = [];
|
|
240
|
+
if (parsed.continue && sessionManager) {
|
|
241
|
+
const context = sessionManager.buildSessionContext();
|
|
242
|
+
restoredMessages = context.messages;
|
|
243
|
+
}
|
|
244
|
+
// Create agent config
|
|
245
|
+
const agentConfig = {
|
|
246
|
+
model,
|
|
247
|
+
cwd,
|
|
248
|
+
toolSet: parsed.toolSet ?? "coding",
|
|
249
|
+
thinkingLevel: parsed.thinking,
|
|
250
|
+
};
|
|
251
|
+
if (parsed.systemPrompt) {
|
|
252
|
+
agentConfig.systemPrompt = parsed.systemPrompt;
|
|
253
|
+
if (appendSystemPrompt) {
|
|
254
|
+
agentConfig.systemPrompt += `\n\n${appendSystemPrompt}`;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
agentConfig.systemPromptOptions = {
|
|
259
|
+
appendSystemPrompt,
|
|
260
|
+
contextFiles,
|
|
261
|
+
};
|
|
262
|
+
}
|
|
263
|
+
// Create agent
|
|
264
|
+
const agent = new CodingAgent(agentConfig);
|
|
265
|
+
// Restore session messages
|
|
266
|
+
if (restoredMessages.length > 0) {
|
|
267
|
+
agent.setMessages(restoredMessages);
|
|
268
|
+
if (parsed.verbose) {
|
|
269
|
+
console.log(chalk.dim(`Restored ${restoredMessages.length} messages from session.`));
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
// Dispatch to mode
|
|
273
|
+
if (isInteractive) {
|
|
274
|
+
await runInteractiveMode(agent, {
|
|
275
|
+
initialMessage,
|
|
276
|
+
initialMessages: parsed.messages,
|
|
277
|
+
sessionManager,
|
|
278
|
+
sessionDir,
|
|
279
|
+
agentConfig,
|
|
280
|
+
resumeOnStart: parsed.resume,
|
|
281
|
+
skills,
|
|
282
|
+
contextFiles,
|
|
283
|
+
prompts,
|
|
284
|
+
verbose: parsed.verbose,
|
|
285
|
+
provider,
|
|
286
|
+
modelId,
|
|
287
|
+
authStorage,
|
|
288
|
+
settingsManager,
|
|
289
|
+
fdPath,
|
|
290
|
+
onModelChange: async (newProvider, newModelId) => {
|
|
291
|
+
const { model: newModel } = await createModel({
|
|
292
|
+
provider: newProvider,
|
|
293
|
+
model: newModelId,
|
|
294
|
+
authStorage,
|
|
295
|
+
});
|
|
296
|
+
return new CodingAgent({
|
|
297
|
+
...agentConfig,
|
|
298
|
+
model: newModel,
|
|
299
|
+
});
|
|
300
|
+
},
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
else {
|
|
304
|
+
await runPrintMode(agent, {
|
|
305
|
+
mode,
|
|
306
|
+
messages: parsed.messages,
|
|
307
|
+
initialMessage,
|
|
308
|
+
sessionManager,
|
|
309
|
+
});
|
|
310
|
+
process.exit(0);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
//# sourceMappingURL=main.js.map
|
package/dist/main.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Main entry point for the edge-pi CLI.\n *\n * Handles argument parsing, model creation, skill loading,\n * session management, auth, and mode dispatch.\n */\n\nimport { existsSync, readdirSync, readFileSync, statSync } from \"node:fs\";\nimport { homedir } from \"node:os\";\nimport { join, resolve } from \"node:path\";\nimport chalk from \"chalk\";\nimport type { CodingAgentConfig, ModelMessage } from \"edge-pi\";\nimport { CodingAgent, SessionManager } from \"edge-pi\";\nimport {\n\tAuthStorage,\n\tanthropicOAuthProvider,\n\tgithubCopilotOAuthProvider,\n\topenaiCodexOAuthProvider,\n} from \"./auth/index.js\";\nimport { parseArgs, printHelp, printModels } from \"./cli/args.js\";\nimport { loadContextFiles } from \"./context.js\";\nimport { createModel } from \"./model-factory.js\";\nimport { runInteractiveMode } from \"./modes/interactive-mode.js\";\nimport { runPrintMode } from \"./modes/print-mode.js\";\nimport { loadPrompts } from \"./prompts.js\";\nimport { SettingsManager } from \"./settings.js\";\nimport { formatSkillsForPrompt, loadSkills, type Skill } from \"./skills.js\";\nimport { findFd } from \"./utils/find-fd.js\";\n\nconst VERSION = \"0.1.0\";\nconst CONFIG_DIR_NAME = \".pi\";\n\nfunction getAgentDir(): string {\n\tconst envDir = process.env.PI_CODING_AGENT_DIR;\n\tif (envDir) {\n\t\tif (envDir === \"~\") return homedir();\n\t\tif (envDir.startsWith(\"~/\")) return homedir() + envDir.slice(1);\n\t\treturn envDir;\n\t}\n\treturn join(homedir(), CONFIG_DIR_NAME, \"agent\");\n}\n\nfunction getSessionsDir(): string {\n\treturn join(getAgentDir(), \"sessions\");\n}\n\nfunction getAuthPath(): string {\n\treturn join(getAgentDir(), \"auth.json\");\n}\n\n/**\n * Read all content from piped stdin.\n * Returns undefined if stdin is a TTY.\n */\nasync function readPipedStdin(): Promise<string | undefined> {\n\tif (process.stdin.isTTY) {\n\t\treturn undefined;\n\t}\n\treturn new Promise((resolve) => {\n\t\tlet data = \"\";\n\t\tprocess.stdin.setEncoding(\"utf8\");\n\t\tprocess.stdin.on(\"data\", (chunk) => {\n\t\t\tdata += chunk;\n\t\t});\n\t\tprocess.stdin.on(\"end\", () => {\n\t\t\tresolve(data.trim() || undefined);\n\t\t});\n\t\tprocess.stdin.resume();\n\t});\n}\n\n/**\n * Process @file arguments into text content.\n */\nfunction processFileArgs(fileArgs: string[]): string {\n\tconst parts: string[] = [];\n\tfor (const filePath of fileArgs) {\n\t\tconst resolved = resolve(filePath);\n\t\tif (!existsSync(resolved)) {\n\t\t\tconsole.error(chalk.yellow(`Warning: File not found: ${resolved}`));\n\t\t\tcontinue;\n\t\t}\n\t\ttry {\n\t\t\tconst content = readFileSync(resolved, \"utf-8\");\n\t\t\tparts.push(`<file path=\"${resolved}\">\\n${content}\\n</file>`);\n\t\t} catch (error) {\n\t\t\tconsole.error(chalk.yellow(`Warning: Could not read ${resolved}: ${(error as Error).message}`));\n\t\t}\n\t}\n\treturn parts.length > 0 ? `${parts.join(\"\\n\\n\")}\\n\\n` : \"\";\n}\n\n/**\n * Create a session directory path based on the current working directory.\n */\nfunction getProjectSessionDir(cwd: string): string {\n\tconst sanitized = cwd.replace(/\\//g, \"--\").replace(/^--/, \"\");\n\treturn join(getSessionsDir(), sanitized);\n}\n\n/**\n * Find the most recent session file in a directory.\n */\nfunction findRecentSession(sessionDir: string): string | undefined {\n\tif (!existsSync(sessionDir)) return undefined;\n\ttry {\n\t\tconst files = readdirSync(sessionDir)\n\t\t\t.filter((f: string) => f.endsWith(\".jsonl\"))\n\t\t\t.map((f: string) => ({\n\t\t\t\tname: f,\n\t\t\t\tpath: join(sessionDir, f),\n\t\t\t\tmtime: statSync(join(sessionDir, f)).mtime.getTime(),\n\t\t\t}))\n\t\t\t.sort((a: { mtime: number }, b: { mtime: number }) => b.mtime - a.mtime);\n\t\treturn files[0]?.path;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\n/**\n * Create and configure AuthStorage with built-in OAuth providers.\n */\nfunction createAuthStorage(): AuthStorage {\n\tconst authStorage = new AuthStorage(getAuthPath());\n\tauthStorage.registerProvider(anthropicOAuthProvider);\n\tauthStorage.registerProvider(githubCopilotOAuthProvider);\n\tauthStorage.registerProvider(openaiCodexOAuthProvider);\n\treturn authStorage;\n}\n\nexport async function main(args: string[]) {\n\tconst parsed = parseArgs(args);\n\n\tif (parsed.version) {\n\t\tconsole.log(VERSION);\n\t\treturn;\n\t}\n\n\tif (parsed.help) {\n\t\tprintHelp();\n\t\treturn;\n\t}\n\n\tif (parsed.listModels) {\n\t\tprintModels();\n\t\treturn;\n\t}\n\n\t// Read piped stdin\n\tconst stdinContent = await readPipedStdin();\n\tif (stdinContent !== undefined) {\n\t\tparsed.print = true;\n\t\tparsed.messages.unshift(stdinContent);\n\t}\n\n\t// Process @file arguments\n\tlet initialMessage: string | undefined;\n\tif (parsed.fileArgs.length > 0) {\n\t\tconst fileContent = processFileArgs(parsed.fileArgs);\n\t\tif (parsed.messages.length > 0) {\n\t\t\tinitialMessage = fileContent + parsed.messages.shift();\n\t\t} else {\n\t\t\tinitialMessage = fileContent;\n\t\t}\n\t}\n\n\tconst cwd = process.cwd();\n\tconst isInteractive = !parsed.print && parsed.mode === undefined;\n\tconst mode = parsed.mode || \"text\";\n\n\t// Set up auth storage\n\tconst authStorage = createAuthStorage();\n\n\t// Set up settings persistence\n\tconst settingsManager = SettingsManager.create(getAgentDir());\n\n\t// Apply CLI --api-key override\n\tif (parsed.apiKey && parsed.provider) {\n\t\tauthStorage.setRuntimeApiKey(parsed.provider, parsed.apiKey);\n\t}\n\n\t// Create model (async - may resolve OAuth tokens)\n\t// Fall back to saved defaults when CLI args are not specified\n\tconst { model, provider, modelId } = await createModel({\n\t\tprovider: parsed.provider ?? settingsManager.getDefaultProvider(),\n\t\tmodel: parsed.model ?? settingsManager.getDefaultModel(),\n\t\tapiKey: parsed.apiKey,\n\t\tauthStorage,\n\t});\n\n\t// Load skills\n\tlet skills: Skill[] = [];\n\tif (!parsed.noSkills) {\n\t\tconst skillResult = loadSkills({\n\t\t\tcwd,\n\t\t\tskillPaths: parsed.skills,\n\t\t\tincludeDefaults: true,\n\t\t});\n\t\tskills = skillResult.skills;\n\n\t\tif (parsed.verbose && skillResult.diagnostics.length > 0) {\n\t\t\tfor (const d of skillResult.diagnostics) {\n\t\t\t\tconsole.error(chalk.yellow(`Skill warning: ${d.message} (${d.path})`));\n\t\t\t}\n\t\t}\n\t}\n\n\t// Load context files (AGENTS.md)\n\tconst contextFiles = loadContextFiles(cwd);\n\tif (parsed.verbose && contextFiles.length > 0) {\n\t\tconsole.log(chalk.dim(`Loaded ${contextFiles.length} context file(s).`));\n\t}\n\n\t// Load prompt templates\n\tconst promptsResult = loadPrompts({ cwd });\n\tconst prompts = promptsResult.prompts;\n\tif (parsed.verbose && promptsResult.diagnostics.length > 0) {\n\t\tfor (const d of promptsResult.diagnostics) {\n\t\t\tconsole.error(chalk.yellow(`Prompt warning: ${d.message} (${d.path})`));\n\t\t}\n\t}\n\n\t// Find fd binary for @ file autocomplete\n\tconst fdPath = findFd();\n\n\t// Build system prompt additions\n\tconst skillsPrompt = formatSkillsForPrompt(skills);\n\tconst appendParts: string[] = [];\n\tif (skillsPrompt) {\n\t\tappendParts.push(skillsPrompt);\n\t}\n\tif (parsed.appendSystemPrompt) {\n\t\tappendParts.push(parsed.appendSystemPrompt);\n\t}\n\tconst appendSystemPrompt = appendParts.length > 0 ? appendParts.join(\"\\n\\n\") : undefined;\n\n\t// Set up session manager\n\tconst sessionDir = parsed.sessionDir ?? getProjectSessionDir(cwd);\n\tlet sessionManager: SessionManager | undefined;\n\tif (parsed.noSession) {\n\t\tsessionManager = SessionManager.inMemory(cwd);\n\t} else if (parsed.session) {\n\t\tsessionManager = SessionManager.open(parsed.session, parsed.sessionDir);\n\t} else if (parsed.resume) {\n\t\t// Start a new session; the TUI will show the session picker on startup\n\t\tsessionManager = SessionManager.create(cwd, sessionDir);\n\t} else if (parsed.continue) {\n\t\tconst recentFile = findRecentSession(sessionDir);\n\t\tif (recentFile) {\n\t\t\tsessionManager = SessionManager.open(recentFile, sessionDir);\n\t\t} else {\n\t\t\tif (parsed.verbose) {\n\t\t\t\tconsole.log(chalk.dim(\"No previous session found, starting new.\"));\n\t\t\t}\n\t\t\tsessionManager = SessionManager.create(cwd, sessionDir);\n\t\t}\n\t} else {\n\t\tsessionManager = SessionManager.create(cwd, sessionDir);\n\t}\n\n\t// Restore messages from session if continuing\n\tlet restoredMessages: ModelMessage[] = [];\n\tif (parsed.continue && sessionManager) {\n\t\tconst context = sessionManager.buildSessionContext();\n\t\trestoredMessages = context.messages;\n\t}\n\n\t// Create agent config\n\tconst agentConfig: CodingAgentConfig = {\n\t\tmodel,\n\t\tcwd,\n\t\ttoolSet: parsed.toolSet ?? \"coding\",\n\t\tthinkingLevel: parsed.thinking,\n\t};\n\n\tif (parsed.systemPrompt) {\n\t\tagentConfig.systemPrompt = parsed.systemPrompt;\n\t\tif (appendSystemPrompt) {\n\t\t\tagentConfig.systemPrompt += `\\n\\n${appendSystemPrompt}`;\n\t\t}\n\t} else {\n\t\tagentConfig.systemPromptOptions = {\n\t\t\tappendSystemPrompt,\n\t\t\tcontextFiles,\n\t\t};\n\t}\n\n\t// Create agent\n\tconst agent = new CodingAgent(agentConfig);\n\n\t// Restore session messages\n\tif (restoredMessages.length > 0) {\n\t\tagent.setMessages(restoredMessages);\n\t\tif (parsed.verbose) {\n\t\t\tconsole.log(chalk.dim(`Restored ${restoredMessages.length} messages from session.`));\n\t\t}\n\t}\n\n\t// Dispatch to mode\n\tif (isInteractive) {\n\t\tawait runInteractiveMode(agent, {\n\t\t\tinitialMessage,\n\t\t\tinitialMessages: parsed.messages,\n\t\t\tsessionManager,\n\t\t\tsessionDir,\n\t\t\tagentConfig,\n\t\t\tresumeOnStart: parsed.resume,\n\t\t\tskills,\n\t\t\tcontextFiles,\n\t\t\tprompts,\n\t\t\tverbose: parsed.verbose,\n\t\t\tprovider,\n\t\t\tmodelId,\n\t\t\tauthStorage,\n\t\t\tsettingsManager,\n\t\t\tfdPath,\n\t\t\tonModelChange: async (newProvider: string, newModelId: string) => {\n\t\t\t\tconst { model: newModel } = await createModel({\n\t\t\t\t\tprovider: newProvider,\n\t\t\t\t\tmodel: newModelId,\n\t\t\t\t\tauthStorage,\n\t\t\t\t});\n\t\t\t\treturn new CodingAgent({\n\t\t\t\t\t...agentConfig,\n\t\t\t\t\tmodel: newModel,\n\t\t\t\t});\n\t\t\t},\n\t\t});\n\t} else {\n\t\tawait runPrintMode(agent, {\n\t\t\tmode,\n\t\t\tmessages: parsed.messages,\n\t\t\tinitialMessage,\n\t\t\tsessionManager,\n\t\t});\n\t\tprocess.exit(0);\n\t}\n}\n"]}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model factory - creates Vercel AI SDK LanguageModel instances
|
|
3
|
+
* from provider name + model ID + optional API key.
|
|
4
|
+
*
|
|
5
|
+
* Supports OAuth tokens for Anthropic (Bearer auth with special headers).
|
|
6
|
+
*/
|
|
7
|
+
import type { LanguageModel } from "ai";
|
|
8
|
+
import type { AuthStorage } from "./auth/auth-storage.js";
|
|
9
|
+
export interface ProviderConfig {
|
|
10
|
+
name: string;
|
|
11
|
+
envVar: string;
|
|
12
|
+
defaultModel: string;
|
|
13
|
+
createModel: (modelId: string, apiKey?: string) => Promise<LanguageModel>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Detect which provider to use based on AuthStorage or environment variables.
|
|
17
|
+
*/
|
|
18
|
+
export declare function detectProvider(authStorage?: AuthStorage): ProviderConfig | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Get a provider config by name.
|
|
21
|
+
*/
|
|
22
|
+
export declare function getProvider(name: string): ProviderConfig | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* List all supported provider names.
|
|
25
|
+
*/
|
|
26
|
+
export declare function listProviders(): string[];
|
|
27
|
+
/**
|
|
28
|
+
* Get the latest recommended models for each provider.
|
|
29
|
+
*/
|
|
30
|
+
export declare function getLatestModels(): Record<string, string[]>;
|
|
31
|
+
/**
|
|
32
|
+
* Create a LanguageModel from provider name, model ID, and optional API key.
|
|
33
|
+
* Uses AuthStorage for credential resolution (OAuth + API key + env vars).
|
|
34
|
+
*/
|
|
35
|
+
export declare function createModel(options: {
|
|
36
|
+
provider?: string;
|
|
37
|
+
model?: string;
|
|
38
|
+
apiKey?: string;
|
|
39
|
+
authStorage?: AuthStorage;
|
|
40
|
+
}): Promise<{
|
|
41
|
+
model: LanguageModel;
|
|
42
|
+
provider: string;
|
|
43
|
+
modelId: string;
|
|
44
|
+
}>;
|
|
45
|
+
//# sourceMappingURL=model-factory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-factory.d.ts","sourceRoot":"","sources":["../src/model-factory.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,IAAI,CAAC;AAGxC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAE1D,MAAM,WAAW,cAAc;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,aAAa,CAAC,CAAC;CAC1E;AAwFD;;GAEG;AACH,wBAAgB,cAAc,CAAC,WAAW,CAAC,EAAE,WAAW,GAAG,cAAc,GAAG,SAAS,CAcpF;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAEpE;AAED;;GAEG;AACH,wBAAgB,aAAa,IAAI,MAAM,EAAE,CAExC;AAED;;GAEG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAQ1D;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE;IAC1C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,WAAW,CAAC;CAC1B,GAAG,OAAO,CAAC;IACX,KAAK,EAAE,aAAa,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CAChB,CAAC,CAwCD","sourcesContent":["/**\n * Model factory - creates Vercel AI SDK LanguageModel instances\n * from provider name + model ID + optional API key.\n *\n * Supports OAuth tokens for Anthropic (Bearer auth with special headers).\n */\n\nimport type { LanguageModel } from \"ai\";\nimport chalk from \"chalk\";\nimport { isAnthropicOAuthToken } from \"./auth/anthropic-oauth.js\";\nimport type { AuthStorage } from \"./auth/auth-storage.js\";\n\nexport interface ProviderConfig {\n\tname: string;\n\tenvVar: string;\n\tdefaultModel: string;\n\tcreateModel: (modelId: string, apiKey?: string) => Promise<LanguageModel>;\n}\n\nasync function createAnthropicModelWithOAuth(modelId: string, apiKey: string): Promise<LanguageModel> {\n\tconst { createAnthropic } = await import(\"@ai-sdk/anthropic\");\n\n\tif (isAnthropicOAuthToken(apiKey)) {\n\t\tconst provider = createAnthropic({\n\t\t\tauthToken: apiKey,\n\t\t\theaders: {\n\t\t\t\t\"anthropic-beta\": \"claude-code-20250219,oauth-2025-04-20\",\n\t\t\t\t\"user-agent\": \"epi/0.1.0 (external, cli)\",\n\t\t\t},\n\t\t});\n\t\treturn provider(modelId);\n\t}\n\n\tconst provider = createAnthropic({ apiKey });\n\treturn provider(modelId);\n}\n\nconst providers: Record<string, ProviderConfig> = {\n\tanthropic: {\n\t\tname: \"anthropic\",\n\t\tenvVar: \"ANTHROPIC_API_KEY\",\n\t\tdefaultModel: \"claude-opus-4-6\",\n\t\tcreateModel: async (modelId: string, apiKey?: string) => {\n\t\t\tif (apiKey) {\n\t\t\t\treturn createAnthropicModelWithOAuth(modelId, apiKey);\n\t\t\t}\n\t\t\tconst { createAnthropic } = await import(\"@ai-sdk/anthropic\");\n\t\t\tconst provider = createAnthropic();\n\t\t\treturn provider(modelId);\n\t\t},\n\t},\n\topenai: {\n\t\tname: \"openai\",\n\t\tenvVar: \"OPENAI_API_KEY\",\n\t\tdefaultModel: \"gpt-5.3\",\n\t\tcreateModel: async (modelId: string, apiKey?: string) => {\n\t\t\tconst { createOpenAI } = await import(\"@ai-sdk/openai\");\n\t\t\tconst provider = createOpenAI(apiKey ? { apiKey } : undefined);\n\t\t\treturn provider(modelId);\n\t\t},\n\t},\n\tgoogle: {\n\t\tname: \"google\",\n\t\tenvVar: \"GEMINI_API_KEY\",\n\t\tdefaultModel: \"gemini-3-flash\",\n\t\tcreateModel: async (modelId: string, apiKey?: string) => {\n\t\t\tconst { createGoogleGenerativeAI } = await import(\"@ai-sdk/google\");\n\t\t\tconst provider = createGoogleGenerativeAI(apiKey ? { apiKey } : undefined);\n\t\t\treturn provider(modelId);\n\t\t},\n\t},\n\t\"openai-codex\": {\n\t\tname: \"openai-codex\",\n\t\tenvVar: \"OPENAI_API_KEY\",\n\t\tdefaultModel: \"gpt-5.3-codex\",\n\t\tcreateModel: async (modelId: string, apiKey?: string) => {\n\t\t\tconst { createOpenAI } = await import(\"@ai-sdk/openai\");\n\t\t\tconst provider = createOpenAI({\n\t\t\t\tapiKey: apiKey ?? \"\",\n\t\t\t\tbaseURL: \"https://chatgpt.com/backend-api\",\n\t\t\t});\n\t\t\treturn provider(modelId);\n\t\t},\n\t},\n\t\"github-copilot\": {\n\t\tname: \"github-copilot\",\n\t\tenvVar: \"GITHUB_TOKEN\",\n\t\tdefaultModel: \"claude-sonnet-4.5\",\n\t\tcreateModel: async (modelId: string, apiKey?: string) => {\n\t\t\tconst { createOpenAI } = await import(\"@ai-sdk/openai\");\n\t\t\tconst provider = createOpenAI({\n\t\t\t\tapiKey: apiKey ?? \"\",\n\t\t\t\tbaseURL: \"https://api.individual.githubcopilot.com\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"User-Agent\": \"GitHubCopilotChat/0.35.0\",\n\t\t\t\t\t\"Editor-Version\": \"vscode/1.107.0\",\n\t\t\t\t\t\"Editor-Plugin-Version\": \"copilot-chat/0.35.0\",\n\t\t\t\t\t\"Copilot-Integration-Id\": \"vscode-chat\",\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn provider.chat(modelId);\n\t\t},\n\t},\n};\n\n/**\n * Detect which provider to use based on AuthStorage or environment variables.\n */\nexport function detectProvider(authStorage?: AuthStorage): ProviderConfig | undefined {\n\tif (authStorage) {\n\t\tfor (const config of Object.values(providers)) {\n\t\t\tif (authStorage.hasAuth(config.name)) {\n\t\t\t\treturn config;\n\t\t\t}\n\t\t}\n\t}\n\tfor (const config of Object.values(providers)) {\n\t\tif (process.env[config.envVar]) {\n\t\t\treturn config;\n\t\t}\n\t}\n\treturn undefined;\n}\n\n/**\n * Get a provider config by name.\n */\nexport function getProvider(name: string): ProviderConfig | undefined {\n\treturn providers[name];\n}\n\n/**\n * List all supported provider names.\n */\nexport function listProviders(): string[] {\n\treturn Object.keys(providers);\n}\n\n/**\n * Get the latest recommended models for each provider.\n */\nexport function getLatestModels(): Record<string, string[]> {\n\treturn {\n\t\tanthropic: [\"claude-opus-4-6\", \"claude-sonnet-4-5\", \"claude-haiku-4-5\"],\n\t\topenai: [\"gpt-5.2-codex\", \"gpt-5.3-codex\"],\n\t\tgoogle: [\"gemini-3-flash-preview\", \"gemini-3-pro-preview\"],\n\t\t\"openai-codex\": [\"gpt-5.3-codex\", \"gpt-5.2-codex\", \"gpt-5.1-codex-max\"],\n\t\t\"github-copilot\": [\"claude-sonnet-4.5\", \"gpt-5.2\", \"gemini-3-pro-preview\", \"gemini-3-flash-preview\"],\n\t};\n}\n\n/**\n * Create a LanguageModel from provider name, model ID, and optional API key.\n * Uses AuthStorage for credential resolution (OAuth + API key + env vars).\n */\nexport async function createModel(options: {\n\tprovider?: string;\n\tmodel?: string;\n\tapiKey?: string;\n\tauthStorage?: AuthStorage;\n}): Promise<{\n\tmodel: LanguageModel;\n\tprovider: string;\n\tmodelId: string;\n}> {\n\tconst { provider: providerName, model: modelId, apiKey, authStorage } = options;\n\n\tlet config: ProviderConfig | undefined;\n\n\tif (providerName) {\n\t\tconfig = getProvider(providerName);\n\t\tif (!config) {\n\t\t\tconsole.error(chalk.red(`Unknown provider: ${providerName}`));\n\t\t\tconsole.error(`Supported providers: ${listProviders().join(\", \")}`);\n\t\t\tprocess.exit(1);\n\t\t}\n\t} else {\n\t\tconfig = detectProvider(authStorage);\n\t\tif (!config) {\n\t\t\tconsole.error(chalk.red(\"No API key found. Set one of:\"));\n\t\t\tfor (const p of Object.values(providers)) {\n\t\t\t\tconsole.error(` ${p.envVar} (for ${p.name})`);\n\t\t\t}\n\t\t\tconsole.error(\"Or use: epi /login\");\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\n\tconst resolvedModelId = modelId ?? config.defaultModel;\n\n\t// Resolve API key: explicit > AuthStorage > env var\n\tlet resolvedApiKey = apiKey;\n\tif (!resolvedApiKey && authStorage) {\n\t\tresolvedApiKey = await authStorage.getApiKey(config.name);\n\t}\n\n\ttry {\n\t\tconst model = await config.createModel(resolvedModelId, resolvedApiKey);\n\t\treturn { model, provider: config.name, modelId: resolvedModelId };\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : \"Unknown error\";\n\t\tconsole.error(chalk.red(`Failed to create model ${config.name}/${resolvedModelId}: ${message}`));\n\t\tprocess.exit(1);\n\t}\n}\n"]}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model factory - creates Vercel AI SDK LanguageModel instances
|
|
3
|
+
* from provider name + model ID + optional API key.
|
|
4
|
+
*
|
|
5
|
+
* Supports OAuth tokens for Anthropic (Bearer auth with special headers).
|
|
6
|
+
*/
|
|
7
|
+
import chalk from "chalk";
|
|
8
|
+
import { isAnthropicOAuthToken } from "./auth/anthropic-oauth.js";
|
|
9
|
+
async function createAnthropicModelWithOAuth(modelId, apiKey) {
|
|
10
|
+
const { createAnthropic } = await import("@ai-sdk/anthropic");
|
|
11
|
+
if (isAnthropicOAuthToken(apiKey)) {
|
|
12
|
+
const provider = createAnthropic({
|
|
13
|
+
authToken: apiKey,
|
|
14
|
+
headers: {
|
|
15
|
+
"anthropic-beta": "claude-code-20250219,oauth-2025-04-20",
|
|
16
|
+
"user-agent": "epi/0.1.0 (external, cli)",
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
return provider(modelId);
|
|
20
|
+
}
|
|
21
|
+
const provider = createAnthropic({ apiKey });
|
|
22
|
+
return provider(modelId);
|
|
23
|
+
}
|
|
24
|
+
const providers = {
|
|
25
|
+
anthropic: {
|
|
26
|
+
name: "anthropic",
|
|
27
|
+
envVar: "ANTHROPIC_API_KEY",
|
|
28
|
+
defaultModel: "claude-opus-4-6",
|
|
29
|
+
createModel: async (modelId, apiKey) => {
|
|
30
|
+
if (apiKey) {
|
|
31
|
+
return createAnthropicModelWithOAuth(modelId, apiKey);
|
|
32
|
+
}
|
|
33
|
+
const { createAnthropic } = await import("@ai-sdk/anthropic");
|
|
34
|
+
const provider = createAnthropic();
|
|
35
|
+
return provider(modelId);
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
openai: {
|
|
39
|
+
name: "openai",
|
|
40
|
+
envVar: "OPENAI_API_KEY",
|
|
41
|
+
defaultModel: "gpt-5.3",
|
|
42
|
+
createModel: async (modelId, apiKey) => {
|
|
43
|
+
const { createOpenAI } = await import("@ai-sdk/openai");
|
|
44
|
+
const provider = createOpenAI(apiKey ? { apiKey } : undefined);
|
|
45
|
+
return provider(modelId);
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
google: {
|
|
49
|
+
name: "google",
|
|
50
|
+
envVar: "GEMINI_API_KEY",
|
|
51
|
+
defaultModel: "gemini-3-flash",
|
|
52
|
+
createModel: async (modelId, apiKey) => {
|
|
53
|
+
const { createGoogleGenerativeAI } = await import("@ai-sdk/google");
|
|
54
|
+
const provider = createGoogleGenerativeAI(apiKey ? { apiKey } : undefined);
|
|
55
|
+
return provider(modelId);
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
"openai-codex": {
|
|
59
|
+
name: "openai-codex",
|
|
60
|
+
envVar: "OPENAI_API_KEY",
|
|
61
|
+
defaultModel: "gpt-5.3-codex",
|
|
62
|
+
createModel: async (modelId, apiKey) => {
|
|
63
|
+
const { createOpenAI } = await import("@ai-sdk/openai");
|
|
64
|
+
const provider = createOpenAI({
|
|
65
|
+
apiKey: apiKey ?? "",
|
|
66
|
+
baseURL: "https://chatgpt.com/backend-api",
|
|
67
|
+
});
|
|
68
|
+
return provider(modelId);
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
"github-copilot": {
|
|
72
|
+
name: "github-copilot",
|
|
73
|
+
envVar: "GITHUB_TOKEN",
|
|
74
|
+
defaultModel: "claude-sonnet-4.5",
|
|
75
|
+
createModel: async (modelId, apiKey) => {
|
|
76
|
+
const { createOpenAI } = await import("@ai-sdk/openai");
|
|
77
|
+
const provider = createOpenAI({
|
|
78
|
+
apiKey: apiKey ?? "",
|
|
79
|
+
baseURL: "https://api.individual.githubcopilot.com",
|
|
80
|
+
headers: {
|
|
81
|
+
"User-Agent": "GitHubCopilotChat/0.35.0",
|
|
82
|
+
"Editor-Version": "vscode/1.107.0",
|
|
83
|
+
"Editor-Plugin-Version": "copilot-chat/0.35.0",
|
|
84
|
+
"Copilot-Integration-Id": "vscode-chat",
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
return provider.chat(modelId);
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Detect which provider to use based on AuthStorage or environment variables.
|
|
93
|
+
*/
|
|
94
|
+
export function detectProvider(authStorage) {
|
|
95
|
+
if (authStorage) {
|
|
96
|
+
for (const config of Object.values(providers)) {
|
|
97
|
+
if (authStorage.hasAuth(config.name)) {
|
|
98
|
+
return config;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
for (const config of Object.values(providers)) {
|
|
103
|
+
if (process.env[config.envVar]) {
|
|
104
|
+
return config;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return undefined;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Get a provider config by name.
|
|
111
|
+
*/
|
|
112
|
+
export function getProvider(name) {
|
|
113
|
+
return providers[name];
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* List all supported provider names.
|
|
117
|
+
*/
|
|
118
|
+
export function listProviders() {
|
|
119
|
+
return Object.keys(providers);
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Get the latest recommended models for each provider.
|
|
123
|
+
*/
|
|
124
|
+
export function getLatestModels() {
|
|
125
|
+
return {
|
|
126
|
+
anthropic: ["claude-opus-4-6", "claude-sonnet-4-5", "claude-haiku-4-5"],
|
|
127
|
+
openai: ["gpt-5.2-codex", "gpt-5.3-codex"],
|
|
128
|
+
google: ["gemini-3-flash-preview", "gemini-3-pro-preview"],
|
|
129
|
+
"openai-codex": ["gpt-5.3-codex", "gpt-5.2-codex", "gpt-5.1-codex-max"],
|
|
130
|
+
"github-copilot": ["claude-sonnet-4.5", "gpt-5.2", "gemini-3-pro-preview", "gemini-3-flash-preview"],
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Create a LanguageModel from provider name, model ID, and optional API key.
|
|
135
|
+
* Uses AuthStorage for credential resolution (OAuth + API key + env vars).
|
|
136
|
+
*/
|
|
137
|
+
export async function createModel(options) {
|
|
138
|
+
const { provider: providerName, model: modelId, apiKey, authStorage } = options;
|
|
139
|
+
let config;
|
|
140
|
+
if (providerName) {
|
|
141
|
+
config = getProvider(providerName);
|
|
142
|
+
if (!config) {
|
|
143
|
+
console.error(chalk.red(`Unknown provider: ${providerName}`));
|
|
144
|
+
console.error(`Supported providers: ${listProviders().join(", ")}`);
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
config = detectProvider(authStorage);
|
|
150
|
+
if (!config) {
|
|
151
|
+
console.error(chalk.red("No API key found. Set one of:"));
|
|
152
|
+
for (const p of Object.values(providers)) {
|
|
153
|
+
console.error(` ${p.envVar} (for ${p.name})`);
|
|
154
|
+
}
|
|
155
|
+
console.error("Or use: epi /login");
|
|
156
|
+
process.exit(1);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
const resolvedModelId = modelId ?? config.defaultModel;
|
|
160
|
+
// Resolve API key: explicit > AuthStorage > env var
|
|
161
|
+
let resolvedApiKey = apiKey;
|
|
162
|
+
if (!resolvedApiKey && authStorage) {
|
|
163
|
+
resolvedApiKey = await authStorage.getApiKey(config.name);
|
|
164
|
+
}
|
|
165
|
+
try {
|
|
166
|
+
const model = await config.createModel(resolvedModelId, resolvedApiKey);
|
|
167
|
+
return { model, provider: config.name, modelId: resolvedModelId };
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
171
|
+
console.error(chalk.red(`Failed to create model ${config.name}/${resolvedModelId}: ${message}`));
|
|
172
|
+
process.exit(1);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=model-factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-factory.js","sourceRoot":"","sources":["../src/model-factory.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAUlE,KAAK,UAAU,6BAA6B,CAAC,OAAe,EAAE,MAAc,EAA0B;IACrG,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;IAE9D,IAAI,qBAAqB,CAAC,MAAM,CAAC,EAAE,CAAC;QACnC,MAAM,QAAQ,GAAG,eAAe,CAAC;YAChC,SAAS,EAAE,MAAM;YACjB,OAAO,EAAE;gBACR,gBAAgB,EAAE,uCAAuC;gBACzD,YAAY,EAAE,2BAA2B;aACzC;SACD,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC1B,CAAC;IAED,MAAM,QAAQ,GAAG,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IAC7C,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC;AAAA,CACzB;AAED,MAAM,SAAS,GAAmC;IACjD,SAAS,EAAE;QACV,IAAI,EAAE,WAAW;QACjB,MAAM,EAAE,mBAAmB;QAC3B,YAAY,EAAE,iBAAiB;QAC/B,WAAW,EAAE,KAAK,EAAE,OAAe,EAAE,MAAe,EAAE,EAAE,CAAC;YACxD,IAAI,MAAM,EAAE,CAAC;gBACZ,OAAO,6BAA6B,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YACvD,CAAC;YACD,MAAM,EAAE,eAAe,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;YAC9D,MAAM,QAAQ,GAAG,eAAe,EAAE,CAAC;YACnC,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC;QAAA,CACzB;KACD;IACD,MAAM,EAAE;QACP,IAAI,EAAE,QAAQ;QACd,MAAM,EAAE,gBAAgB;QACxB,YAAY,EAAE,SAAS;QACvB,WAAW,EAAE,KAAK,EAAE,OAAe,EAAE,MAAe,EAAE,EAAE,CAAC;YACxD,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC;YACxD,MAAM,QAAQ,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;YAC/D,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC;QAAA,CACzB;KACD;IACD,MAAM,EAAE;QACP,IAAI,EAAE,QAAQ;QACd,MAAM,EAAE,gBAAgB;QACxB,YAAY,EAAE,gBAAgB;QAC9B,WAAW,EAAE,KAAK,EAAE,OAAe,EAAE,MAAe,EAAE,EAAE,CAAC;YACxD,MAAM,EAAE,wBAAwB,EAAE,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC;YACpE,MAAM,QAAQ,GAAG,wBAAwB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;YAC3E,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC;QAAA,CACzB;KACD;IACD,cAAc,EAAE;QACf,IAAI,EAAE,cAAc;QACpB,MAAM,EAAE,gBAAgB;QACxB,YAAY,EAAE,eAAe;QAC7B,WAAW,EAAE,KAAK,EAAE,OAAe,EAAE,MAAe,EAAE,EAAE,CAAC;YACxD,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC;YACxD,MAAM,QAAQ,GAAG,YAAY,CAAC;gBAC7B,MAAM,EAAE,MAAM,IAAI,EAAE;gBACpB,OAAO,EAAE,iCAAiC;aAC1C,CAAC,CAAC;YACH,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC;QAAA,CACzB;KACD;IACD,gBAAgB,EAAE;QACjB,IAAI,EAAE,gBAAgB;QACtB,MAAM,EAAE,cAAc;QACtB,YAAY,EAAE,mBAAmB;QACjC,WAAW,EAAE,KAAK,EAAE,OAAe,EAAE,MAAe,EAAE,EAAE,CAAC;YACxD,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC;YACxD,MAAM,QAAQ,GAAG,YAAY,CAAC;gBAC7B,MAAM,EAAE,MAAM,IAAI,EAAE;gBACpB,OAAO,EAAE,0CAA0C;gBACnD,OAAO,EAAE;oBACR,YAAY,EAAE,0BAA0B;oBACxC,gBAAgB,EAAE,gBAAgB;oBAClC,uBAAuB,EAAE,qBAAqB;oBAC9C,wBAAwB,EAAE,aAAa;iBACvC;aACD,CAAC,CAAC;YACH,OAAO,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAAA,CAC9B;KACD;CACD,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,WAAyB,EAA8B;IACrF,IAAI,WAAW,EAAE,CAAC;QACjB,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;YAC/C,IAAI,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;gBACtC,OAAO,MAAM,CAAC;YACf,CAAC;QACF,CAAC;IACF,CAAC;IACD,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;QAC/C,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;YAChC,OAAO,MAAM,CAAC;QACf,CAAC;IACF,CAAC;IACD,OAAO,SAAS,CAAC;AAAA,CACjB;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,IAAY,EAA8B;IACrE,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC;AAAA,CACvB;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,GAAa;IACzC,OAAO,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;AAAA,CAC9B;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,GAA6B;IAC3D,OAAO;QACN,SAAS,EAAE,CAAC,iBAAiB,EAAE,mBAAmB,EAAE,kBAAkB,CAAC;QACvE,MAAM,EAAE,CAAC,eAAe,EAAE,eAAe,CAAC;QAC1C,MAAM,EAAE,CAAC,wBAAwB,EAAE,sBAAsB,CAAC;QAC1D,cAAc,EAAE,CAAC,eAAe,EAAE,eAAe,EAAE,mBAAmB,CAAC;QACvE,gBAAgB,EAAE,CAAC,mBAAmB,EAAE,SAAS,EAAE,sBAAsB,EAAE,wBAAwB,CAAC;KACpG,CAAC;AAAA,CACF;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAKjC,EAIE;IACF,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,OAAO,CAAC;IAEhF,IAAI,MAAkC,CAAC;IAEvC,IAAI,YAAY,EAAE,CAAC;QAClB,MAAM,GAAG,WAAW,CAAC,YAAY,CAAC,CAAC;QACnC,IAAI,CAAC,MAAM,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,qBAAqB,YAAY,EAAE,CAAC,CAAC,CAAC;YAC9D,OAAO,CAAC,KAAK,CAAC,wBAAwB,aAAa,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACpE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC;IACF,CAAC;SAAM,CAAC;QACP,MAAM,GAAG,cAAc,CAAC,WAAW,CAAC,CAAC;QACrC,IAAI,CAAC,MAAM,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAC,CAAC;YAC1D,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC1C,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,SAAS,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC;YAChD,CAAC;YACD,OAAO,CAAC,KAAK,CAAC,oBAAoB,CAAC,CAAC;YACpC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACjB,CAAC;IACF,CAAC;IAED,MAAM,eAAe,GAAG,OAAO,IAAI,MAAM,CAAC,YAAY,CAAC;IAEvD,oDAAoD;IACpD,IAAI,cAAc,GAAG,MAAM,CAAC;IAC5B,IAAI,CAAC,cAAc,IAAI,WAAW,EAAE,CAAC;QACpC,cAAc,GAAG,MAAM,WAAW,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC3D,CAAC;IAED,IAAI,CAAC;QACJ,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,eAAe,EAAE,cAAc,CAAC,CAAC;QACxE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,eAAe,EAAE,CAAC;IACnE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;QACzE,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,0BAA0B,MAAM,CAAC,IAAI,IAAI,eAAe,KAAK,OAAO,EAAE,CAAC,CAAC,CAAC;QACjG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;AAAA,CACD","sourcesContent":["/**\n * Model factory - creates Vercel AI SDK LanguageModel instances\n * from provider name + model ID + optional API key.\n *\n * Supports OAuth tokens for Anthropic (Bearer auth with special headers).\n */\n\nimport type { LanguageModel } from \"ai\";\nimport chalk from \"chalk\";\nimport { isAnthropicOAuthToken } from \"./auth/anthropic-oauth.js\";\nimport type { AuthStorage } from \"./auth/auth-storage.js\";\n\nexport interface ProviderConfig {\n\tname: string;\n\tenvVar: string;\n\tdefaultModel: string;\n\tcreateModel: (modelId: string, apiKey?: string) => Promise<LanguageModel>;\n}\n\nasync function createAnthropicModelWithOAuth(modelId: string, apiKey: string): Promise<LanguageModel> {\n\tconst { createAnthropic } = await import(\"@ai-sdk/anthropic\");\n\n\tif (isAnthropicOAuthToken(apiKey)) {\n\t\tconst provider = createAnthropic({\n\t\t\tauthToken: apiKey,\n\t\t\theaders: {\n\t\t\t\t\"anthropic-beta\": \"claude-code-20250219,oauth-2025-04-20\",\n\t\t\t\t\"user-agent\": \"epi/0.1.0 (external, cli)\",\n\t\t\t},\n\t\t});\n\t\treturn provider(modelId);\n\t}\n\n\tconst provider = createAnthropic({ apiKey });\n\treturn provider(modelId);\n}\n\nconst providers: Record<string, ProviderConfig> = {\n\tanthropic: {\n\t\tname: \"anthropic\",\n\t\tenvVar: \"ANTHROPIC_API_KEY\",\n\t\tdefaultModel: \"claude-opus-4-6\",\n\t\tcreateModel: async (modelId: string, apiKey?: string) => {\n\t\t\tif (apiKey) {\n\t\t\t\treturn createAnthropicModelWithOAuth(modelId, apiKey);\n\t\t\t}\n\t\t\tconst { createAnthropic } = await import(\"@ai-sdk/anthropic\");\n\t\t\tconst provider = createAnthropic();\n\t\t\treturn provider(modelId);\n\t\t},\n\t},\n\topenai: {\n\t\tname: \"openai\",\n\t\tenvVar: \"OPENAI_API_KEY\",\n\t\tdefaultModel: \"gpt-5.3\",\n\t\tcreateModel: async (modelId: string, apiKey?: string) => {\n\t\t\tconst { createOpenAI } = await import(\"@ai-sdk/openai\");\n\t\t\tconst provider = createOpenAI(apiKey ? { apiKey } : undefined);\n\t\t\treturn provider(modelId);\n\t\t},\n\t},\n\tgoogle: {\n\t\tname: \"google\",\n\t\tenvVar: \"GEMINI_API_KEY\",\n\t\tdefaultModel: \"gemini-3-flash\",\n\t\tcreateModel: async (modelId: string, apiKey?: string) => {\n\t\t\tconst { createGoogleGenerativeAI } = await import(\"@ai-sdk/google\");\n\t\t\tconst provider = createGoogleGenerativeAI(apiKey ? { apiKey } : undefined);\n\t\t\treturn provider(modelId);\n\t\t},\n\t},\n\t\"openai-codex\": {\n\t\tname: \"openai-codex\",\n\t\tenvVar: \"OPENAI_API_KEY\",\n\t\tdefaultModel: \"gpt-5.3-codex\",\n\t\tcreateModel: async (modelId: string, apiKey?: string) => {\n\t\t\tconst { createOpenAI } = await import(\"@ai-sdk/openai\");\n\t\t\tconst provider = createOpenAI({\n\t\t\t\tapiKey: apiKey ?? \"\",\n\t\t\t\tbaseURL: \"https://chatgpt.com/backend-api\",\n\t\t\t});\n\t\t\treturn provider(modelId);\n\t\t},\n\t},\n\t\"github-copilot\": {\n\t\tname: \"github-copilot\",\n\t\tenvVar: \"GITHUB_TOKEN\",\n\t\tdefaultModel: \"claude-sonnet-4.5\",\n\t\tcreateModel: async (modelId: string, apiKey?: string) => {\n\t\t\tconst { createOpenAI } = await import(\"@ai-sdk/openai\");\n\t\t\tconst provider = createOpenAI({\n\t\t\t\tapiKey: apiKey ?? \"\",\n\t\t\t\tbaseURL: \"https://api.individual.githubcopilot.com\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"User-Agent\": \"GitHubCopilotChat/0.35.0\",\n\t\t\t\t\t\"Editor-Version\": \"vscode/1.107.0\",\n\t\t\t\t\t\"Editor-Plugin-Version\": \"copilot-chat/0.35.0\",\n\t\t\t\t\t\"Copilot-Integration-Id\": \"vscode-chat\",\n\t\t\t\t},\n\t\t\t});\n\t\t\treturn provider.chat(modelId);\n\t\t},\n\t},\n};\n\n/**\n * Detect which provider to use based on AuthStorage or environment variables.\n */\nexport function detectProvider(authStorage?: AuthStorage): ProviderConfig | undefined {\n\tif (authStorage) {\n\t\tfor (const config of Object.values(providers)) {\n\t\t\tif (authStorage.hasAuth(config.name)) {\n\t\t\t\treturn config;\n\t\t\t}\n\t\t}\n\t}\n\tfor (const config of Object.values(providers)) {\n\t\tif (process.env[config.envVar]) {\n\t\t\treturn config;\n\t\t}\n\t}\n\treturn undefined;\n}\n\n/**\n * Get a provider config by name.\n */\nexport function getProvider(name: string): ProviderConfig | undefined {\n\treturn providers[name];\n}\n\n/**\n * List all supported provider names.\n */\nexport function listProviders(): string[] {\n\treturn Object.keys(providers);\n}\n\n/**\n * Get the latest recommended models for each provider.\n */\nexport function getLatestModels(): Record<string, string[]> {\n\treturn {\n\t\tanthropic: [\"claude-opus-4-6\", \"claude-sonnet-4-5\", \"claude-haiku-4-5\"],\n\t\topenai: [\"gpt-5.2-codex\", \"gpt-5.3-codex\"],\n\t\tgoogle: [\"gemini-3-flash-preview\", \"gemini-3-pro-preview\"],\n\t\t\"openai-codex\": [\"gpt-5.3-codex\", \"gpt-5.2-codex\", \"gpt-5.1-codex-max\"],\n\t\t\"github-copilot\": [\"claude-sonnet-4.5\", \"gpt-5.2\", \"gemini-3-pro-preview\", \"gemini-3-flash-preview\"],\n\t};\n}\n\n/**\n * Create a LanguageModel from provider name, model ID, and optional API key.\n * Uses AuthStorage for credential resolution (OAuth + API key + env vars).\n */\nexport async function createModel(options: {\n\tprovider?: string;\n\tmodel?: string;\n\tapiKey?: string;\n\tauthStorage?: AuthStorage;\n}): Promise<{\n\tmodel: LanguageModel;\n\tprovider: string;\n\tmodelId: string;\n}> {\n\tconst { provider: providerName, model: modelId, apiKey, authStorage } = options;\n\n\tlet config: ProviderConfig | undefined;\n\n\tif (providerName) {\n\t\tconfig = getProvider(providerName);\n\t\tif (!config) {\n\t\t\tconsole.error(chalk.red(`Unknown provider: ${providerName}`));\n\t\t\tconsole.error(`Supported providers: ${listProviders().join(\", \")}`);\n\t\t\tprocess.exit(1);\n\t\t}\n\t} else {\n\t\tconfig = detectProvider(authStorage);\n\t\tif (!config) {\n\t\t\tconsole.error(chalk.red(\"No API key found. Set one of:\"));\n\t\t\tfor (const p of Object.values(providers)) {\n\t\t\t\tconsole.error(` ${p.envVar} (for ${p.name})`);\n\t\t\t}\n\t\t\tconsole.error(\"Or use: epi /login\");\n\t\t\tprocess.exit(1);\n\t\t}\n\t}\n\n\tconst resolvedModelId = modelId ?? config.defaultModel;\n\n\t// Resolve API key: explicit > AuthStorage > env var\n\tlet resolvedApiKey = apiKey;\n\tif (!resolvedApiKey && authStorage) {\n\t\tresolvedApiKey = await authStorage.getApiKey(config.name);\n\t}\n\n\ttry {\n\t\tconst model = await config.createModel(resolvedModelId, resolvedApiKey);\n\t\treturn { model, provider: config.name, modelId: resolvedModelId };\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : \"Unknown error\";\n\t\tconsole.error(chalk.red(`Failed to create model ${config.name}/${resolvedModelId}: ${message}`));\n\t\tprocess.exit(1);\n\t}\n}\n"]}
|