mastracode 0.2.0 → 0.3.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.
- package/CHANGELOG.md +144 -0
- package/README.md +9 -1
- package/dist/agents/instructions.d.ts.map +1 -1
- package/dist/agents/prompts/base.d.ts +2 -0
- package/dist/agents/prompts/base.d.ts.map +1 -1
- package/dist/agents/prompts/build.d.ts +1 -1
- package/dist/agents/prompts/build.d.ts.map +1 -1
- package/dist/agents/prompts/index.d.ts +1 -2
- package/dist/agents/prompts/index.d.ts.map +1 -1
- package/dist/agents/prompts/plan.d.ts +1 -1
- package/dist/agents/prompts/plan.d.ts.map +1 -1
- package/dist/agents/prompts/tool-guidance.d.ts +11 -0
- package/dist/agents/prompts/tool-guidance.d.ts.map +1 -0
- package/dist/agents/workspace.d.ts.map +1 -1
- package/dist/auth/storage.d.ts +0 -48
- package/dist/auth/storage.d.ts.map +1 -1
- package/dist/{chunk-LVGWM7ZS.cjs → chunk-7K5VFY2N.cjs} +768 -1373
- package/dist/chunk-7K5VFY2N.cjs.map +1 -0
- package/dist/chunk-7TFV3VBB.cjs +8823 -0
- package/dist/chunk-7TFV3VBB.cjs.map +1 -0
- package/dist/chunk-C6XKRHRK.cjs +853 -0
- package/dist/chunk-C6XKRHRK.cjs.map +1 -0
- package/dist/{chunk-7V6U7OKQ.js → chunk-HHX6BKLR.js} +234 -121
- package/dist/chunk-HHX6BKLR.js.map +1 -0
- package/dist/{chunk-FYTZFUHD.js → chunk-LYETHS2L.js} +6389 -5481
- package/dist/chunk-LYETHS2L.js.map +1 -0
- package/dist/{chunk-BYMDWH2E.js → chunk-V4HZ2AVV.js} +729 -1332
- package/dist/chunk-V4HZ2AVV.js.map +1 -0
- package/dist/chunk-VRZZSUQE.js +835 -0
- package/dist/chunk-VRZZSUQE.js.map +1 -0
- package/dist/{chunk-QDLLGD43.cjs → chunk-VZFPT5N7.cjs} +274 -161
- package/dist/chunk-VZFPT5N7.cjs.map +1 -0
- package/dist/cli.cjs +64 -21
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +61 -18
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.d.ts +6 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/onboarding/index.d.ts +7 -0
- package/dist/onboarding/index.d.ts.map +1 -0
- package/dist/onboarding/onboarding-inline.d.ts +95 -0
- package/dist/onboarding/onboarding-inline.d.ts.map +1 -0
- package/dist/onboarding/packs.d.ts +46 -0
- package/dist/onboarding/packs.d.ts.map +1 -0
- package/dist/onboarding/settings.d.ts +110 -0
- package/dist/onboarding/settings.d.ts.map +1 -0
- package/dist/storage-52Y5MKTG.js +3 -0
- package/dist/storage-52Y5MKTG.js.map +1 -0
- package/dist/storage-JFFX7LJJ.cjs +24 -0
- package/dist/storage-JFFX7LJJ.cjs.map +1 -0
- package/dist/tools/string-replace-lsp.d.ts.map +1 -1
- package/dist/tui/command-dispatch.d.ts +8 -0
- package/dist/tui/command-dispatch.d.ts.map +1 -0
- package/dist/tui/commands/cost.d.ts +3 -0
- package/dist/tui/commands/cost.d.ts.map +1 -0
- package/dist/tui/commands/diff.d.ts +3 -0
- package/dist/tui/commands/diff.d.ts.map +1 -0
- package/dist/tui/commands/exit.d.ts +3 -0
- package/dist/tui/commands/exit.d.ts.map +1 -0
- package/dist/tui/commands/help.d.ts +3 -0
- package/dist/tui/commands/help.d.ts.map +1 -0
- package/dist/tui/commands/hooks.d.ts +3 -0
- package/dist/tui/commands/hooks.d.ts.map +1 -0
- package/dist/tui/commands/index.d.ts +27 -0
- package/dist/tui/commands/index.d.ts.map +1 -0
- package/dist/tui/commands/login.d.ts +3 -0
- package/dist/tui/commands/login.d.ts.map +1 -0
- package/dist/tui/commands/mcp.d.ts +3 -0
- package/dist/tui/commands/mcp.d.ts.map +1 -0
- package/dist/tui/commands/mode.d.ts +3 -0
- package/dist/tui/commands/mode.d.ts.map +1 -0
- package/dist/tui/commands/models-pack.d.ts +3 -0
- package/dist/tui/commands/models-pack.d.ts.map +1 -0
- package/dist/tui/commands/models.d.ts +3 -0
- package/dist/tui/commands/models.d.ts.map +1 -0
- package/dist/tui/commands/name.d.ts +3 -0
- package/dist/tui/commands/name.d.ts.map +1 -0
- package/dist/tui/commands/new.d.ts +3 -0
- package/dist/tui/commands/new.d.ts.map +1 -0
- package/dist/tui/commands/om.d.ts +3 -0
- package/dist/tui/commands/om.d.ts.map +1 -0
- package/dist/tui/commands/permissions.d.ts +3 -0
- package/dist/tui/commands/permissions.d.ts.map +1 -0
- package/dist/tui/commands/resource.d.ts +3 -0
- package/dist/tui/commands/resource.d.ts.map +1 -0
- package/dist/tui/commands/review.d.ts +3 -0
- package/dist/tui/commands/review.d.ts.map +1 -0
- package/dist/tui/commands/sandbox.d.ts +3 -0
- package/dist/tui/commands/sandbox.d.ts.map +1 -0
- package/dist/tui/commands/settings.d.ts +3 -0
- package/dist/tui/commands/settings.d.ts.map +1 -0
- package/dist/tui/commands/setup.d.ts +3 -0
- package/dist/tui/commands/setup.d.ts.map +1 -0
- package/dist/tui/commands/skills.d.ts +3 -0
- package/dist/tui/commands/skills.d.ts.map +1 -0
- package/dist/tui/commands/subagents.d.ts +3 -0
- package/dist/tui/commands/subagents.d.ts.map +1 -0
- package/dist/tui/commands/think.d.ts +3 -0
- package/dist/tui/commands/think.d.ts.map +1 -0
- package/dist/tui/commands/thread-tag-dir.d.ts +3 -0
- package/dist/tui/commands/thread-tag-dir.d.ts.map +1 -0
- package/dist/tui/commands/threads.d.ts +3 -0
- package/dist/tui/commands/threads.d.ts.map +1 -0
- package/dist/tui/commands/types.d.ts +28 -0
- package/dist/tui/commands/types.d.ts.map +1 -0
- package/dist/tui/commands/yolo.d.ts +3 -0
- package/dist/tui/commands/yolo.d.ts.map +1 -0
- package/dist/tui/components/banner.d.ts +9 -0
- package/dist/tui/components/banner.d.ts.map +1 -0
- package/dist/tui/components/help-overlay.d.ts +15 -0
- package/dist/tui/components/help-overlay.d.ts.map +1 -0
- package/dist/tui/components/model-selector.d.ts +3 -0
- package/dist/tui/components/model-selector.d.ts.map +1 -1
- package/dist/tui/components/om-progress.d.ts +4 -30
- package/dist/tui/components/om-progress.d.ts.map +1 -1
- package/dist/tui/components/settings.d.ts +5 -0
- package/dist/tui/components/settings.d.ts.map +1 -1
- package/dist/tui/components/tool-execution-enhanced.d.ts.map +1 -1
- package/dist/tui/display.d.ts +12 -0
- package/dist/tui/display.d.ts.map +1 -0
- package/dist/tui/event-dispatch.d.ts +11 -0
- package/dist/tui/event-dispatch.d.ts.map +1 -0
- package/dist/tui/handlers/agent-lifecycle.d.ts +6 -0
- package/dist/tui/handlers/agent-lifecycle.d.ts.map +1 -0
- package/dist/tui/handlers/index.d.ts +8 -0
- package/dist/tui/handlers/index.d.ts.map +1 -0
- package/dist/tui/handlers/message.d.ts +6 -0
- package/dist/tui/handlers/message.d.ts.map +1 -0
- package/dist/tui/handlers/om.d.ts +11 -0
- package/dist/tui/handlers/om.d.ts.map +1 -0
- package/dist/tui/handlers/prompts.d.ts +20 -0
- package/dist/tui/handlers/prompts.d.ts.map +1 -0
- package/dist/tui/handlers/subagent.d.ts +6 -0
- package/dist/tui/handlers/subagent.d.ts.map +1 -0
- package/dist/tui/handlers/tool.d.ts +30 -0
- package/dist/tui/handlers/tool.d.ts.map +1 -0
- package/dist/tui/handlers/types.d.ts +33 -0
- package/dist/tui/handlers/types.d.ts.map +1 -0
- package/dist/tui/mastra-tui.d.ts +9 -221
- package/dist/tui/mastra-tui.d.ts.map +1 -1
- package/dist/tui/render-messages.d.ts +22 -0
- package/dist/tui/render-messages.d.ts.map +1 -0
- package/dist/tui/setup.d.ts +17 -0
- package/dist/tui/setup.d.ts.map +1 -0
- package/dist/tui/shell.d.ts +3 -0
- package/dist/tui/shell.d.ts.map +1 -0
- package/dist/tui/state.d.ts +4 -24
- package/dist/tui/state.d.ts.map +1 -1
- package/dist/tui/status-line.d.ts +7 -0
- package/dist/tui/status-line.d.ts.map +1 -0
- package/dist/tui.cjs +17 -17
- package/dist/tui.js +2 -2
- package/dist/utils/project.d.ts +37 -9
- package/dist/utils/project.d.ts.map +1 -1
- package/dist/utils/storage-factory.d.ts +21 -0
- package/dist/utils/storage-factory.d.ts.map +1 -0
- package/package.json +7 -6
- package/dist/chunk-7V6U7OKQ.js.map +0 -1
- package/dist/chunk-BQ4ZKTYN.cjs +0 -7915
- package/dist/chunk-BQ4ZKTYN.cjs.map +0 -1
- package/dist/chunk-BYMDWH2E.js.map +0 -1
- package/dist/chunk-FYTZFUHD.js.map +0 -1
- package/dist/chunk-LVGWM7ZS.cjs.map +0 -1
- package/dist/chunk-QDLLGD43.cjs.map +0 -1
|
@@ -1,1253 +1,31 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import * as
|
|
4
|
-
import
|
|
5
|
-
import
|
|
1
|
+
import { AuthStorage, getAppDataDir } from './chunk-VRZZSUQE.js';
|
|
2
|
+
import { exec, spawn } from 'child_process';
|
|
3
|
+
import * as fs8 from 'fs';
|
|
4
|
+
import { readFileSync, writeFileSync, promises, existsSync, mkdirSync } from 'fs';
|
|
5
|
+
import { homedir } from 'os';
|
|
6
6
|
import * as path from 'path';
|
|
7
7
|
import path__default, { resolve, join, dirname } from 'path';
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import
|
|
13
|
-
import {
|
|
14
|
-
import
|
|
15
|
-
import
|
|
16
|
-
import
|
|
17
|
-
import {
|
|
18
|
-
import
|
|
19
|
-
import
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import { tavily } from '@tavily/core';
|
|
26
|
-
import { parse, Lang } from '@ast-grep/napi';
|
|
27
|
-
import chalk from 'chalk';
|
|
28
|
-
|
|
29
|
-
// src/utils/project.ts
|
|
30
|
-
function git(args, cwd) {
|
|
31
|
-
try {
|
|
32
|
-
return execSync(`git ${args}`, {
|
|
33
|
-
cwd,
|
|
34
|
-
encoding: "utf-8",
|
|
35
|
-
stdio: ["pipe", "pipe", "pipe"]
|
|
36
|
-
}).trim();
|
|
37
|
-
} catch {
|
|
38
|
-
return void 0;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
function slugify(str) {
|
|
42
|
-
return str.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
43
|
-
}
|
|
44
|
-
function shortHash(str) {
|
|
45
|
-
return createHash("sha256").update(str).digest("hex").slice(0, 12);
|
|
46
|
-
}
|
|
47
|
-
function normalizeGitUrl(url) {
|
|
48
|
-
return url.replace(/\.git$/, "").replace(/^git@([^:]+):/, "https://$1/").replace(/^ssh:\/\/git@/, "https://").toLowerCase();
|
|
49
|
-
}
|
|
50
|
-
function detectProject(projectPath) {
|
|
51
|
-
const absolutePath = path__default.resolve(projectPath);
|
|
52
|
-
const gitDir = git("rev-parse --git-dir", absolutePath);
|
|
53
|
-
const isGitRepo = gitDir !== void 0;
|
|
54
|
-
let rootPath = absolutePath;
|
|
55
|
-
let gitUrl;
|
|
56
|
-
let gitBranch;
|
|
57
|
-
let isWorktree = false;
|
|
58
|
-
let mainRepoPath;
|
|
59
|
-
if (isGitRepo) {
|
|
60
|
-
rootPath = git("rev-parse --show-toplevel", absolutePath) || absolutePath;
|
|
61
|
-
const commonDir = git("rev-parse --git-common-dir", absolutePath);
|
|
62
|
-
if (commonDir && commonDir !== ".git" && commonDir !== gitDir) {
|
|
63
|
-
isWorktree = true;
|
|
64
|
-
mainRepoPath = path__default.dirname(path__default.resolve(rootPath, commonDir));
|
|
65
|
-
}
|
|
66
|
-
gitUrl = git("remote get-url origin", absolutePath);
|
|
67
|
-
if (!gitUrl) {
|
|
68
|
-
const remotes = git("remote", absolutePath);
|
|
69
|
-
if (remotes) {
|
|
70
|
-
const firstRemote = remotes.split("\n")[0];
|
|
71
|
-
if (firstRemote) {
|
|
72
|
-
gitUrl = git(`remote get-url ${firstRemote}`, absolutePath);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
gitBranch = git("rev-parse --abbrev-ref HEAD", absolutePath);
|
|
77
|
-
}
|
|
78
|
-
let resourceIdSource;
|
|
79
|
-
if (gitUrl) {
|
|
80
|
-
resourceIdSource = normalizeGitUrl(gitUrl);
|
|
81
|
-
} else if (mainRepoPath) {
|
|
82
|
-
resourceIdSource = mainRepoPath;
|
|
83
|
-
} else {
|
|
84
|
-
resourceIdSource = rootPath;
|
|
85
|
-
}
|
|
86
|
-
const baseName = gitUrl ? gitUrl.split("/").pop()?.replace(/\.git$/, "") || "project" : path__default.basename(rootPath);
|
|
87
|
-
const resourceId = `${slugify(baseName)}-${shortHash(resourceIdSource)}`;
|
|
88
|
-
return {
|
|
89
|
-
resourceId,
|
|
90
|
-
name: baseName,
|
|
91
|
-
rootPath,
|
|
92
|
-
gitUrl,
|
|
93
|
-
gitBranch,
|
|
94
|
-
isWorktree,
|
|
95
|
-
mainRepoPath
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
function getAppDataDir() {
|
|
99
|
-
const platform = os.platform();
|
|
100
|
-
let baseDir;
|
|
101
|
-
if (platform === "darwin") {
|
|
102
|
-
baseDir = path__default.join(os.homedir(), "Library", "Application Support");
|
|
103
|
-
} else if (platform === "win32") {
|
|
104
|
-
baseDir = process.env.APPDATA || path__default.join(os.homedir(), "AppData", "Roaming");
|
|
105
|
-
} else {
|
|
106
|
-
baseDir = process.env.XDG_DATA_HOME || path__default.join(os.homedir(), ".local", "share");
|
|
107
|
-
}
|
|
108
|
-
const appDir = path__default.join(baseDir, "mastracode");
|
|
109
|
-
if (!fs9__default.existsSync(appDir)) {
|
|
110
|
-
fs9__default.mkdirSync(appDir, { recursive: true });
|
|
111
|
-
}
|
|
112
|
-
return appDir;
|
|
113
|
-
}
|
|
114
|
-
function getDatabasePath() {
|
|
115
|
-
if (process.env.MASTRA_DB_PATH) {
|
|
116
|
-
return process.env.MASTRA_DB_PATH;
|
|
117
|
-
}
|
|
118
|
-
return path__default.join(getAppDataDir(), "mastra.db");
|
|
119
|
-
}
|
|
120
|
-
function getStorageConfig(projectDir) {
|
|
121
|
-
if (process.env.MASTRA_DB_URL) {
|
|
122
|
-
return {
|
|
123
|
-
url: process.env.MASTRA_DB_URL,
|
|
124
|
-
authToken: process.env.MASTRA_DB_AUTH_TOKEN,
|
|
125
|
-
isRemote: !process.env.MASTRA_DB_URL.startsWith("file:")
|
|
126
|
-
};
|
|
127
|
-
}
|
|
128
|
-
if (projectDir) {
|
|
129
|
-
const projectConfig = loadDatabaseConfig(path__default.join(projectDir, ".mastracode", "database.json"));
|
|
130
|
-
if (projectConfig) return projectConfig;
|
|
131
|
-
}
|
|
132
|
-
const globalConfig = loadDatabaseConfig(path__default.join(os.homedir(), ".mastracode", "database.json"));
|
|
133
|
-
if (globalConfig) return globalConfig;
|
|
134
|
-
return {
|
|
135
|
-
url: `file:${getDatabasePath()}`,
|
|
136
|
-
isRemote: false
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
function loadDatabaseConfig(filePath) {
|
|
140
|
-
try {
|
|
141
|
-
if (!fs9__default.existsSync(filePath)) return null;
|
|
142
|
-
const raw = fs9__default.readFileSync(filePath, "utf-8");
|
|
143
|
-
const parsed = JSON.parse(raw);
|
|
144
|
-
if (typeof parsed?.url === "string" && parsed.url) {
|
|
145
|
-
return {
|
|
146
|
-
url: parsed.url,
|
|
147
|
-
authToken: typeof parsed.authToken === "string" ? parsed.authToken : void 0,
|
|
148
|
-
isRemote: !parsed.url.startsWith("file:")
|
|
149
|
-
};
|
|
150
|
-
}
|
|
151
|
-
return null;
|
|
152
|
-
} catch {
|
|
153
|
-
return null;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
function getUserId(projectDir) {
|
|
157
|
-
if (process.env.MASTRA_USER_ID) {
|
|
158
|
-
return process.env.MASTRA_USER_ID;
|
|
159
|
-
}
|
|
160
|
-
const cwd = projectDir || process.cwd();
|
|
161
|
-
const email = git("config user.email", cwd);
|
|
162
|
-
if (email) {
|
|
163
|
-
return email;
|
|
164
|
-
}
|
|
165
|
-
return os.userInfo().username || "unknown";
|
|
166
|
-
}
|
|
167
|
-
function getOmScope(projectDir) {
|
|
168
|
-
const envScope = process.env.MASTRA_OM_SCOPE;
|
|
169
|
-
if (envScope === "thread" || envScope === "resource") {
|
|
170
|
-
return envScope;
|
|
171
|
-
}
|
|
172
|
-
if (projectDir) {
|
|
173
|
-
const scope2 = loadOmScopeFromConfig(path__default.join(projectDir, ".mastracode", "database.json"));
|
|
174
|
-
if (scope2) return scope2;
|
|
175
|
-
}
|
|
176
|
-
const scope = loadOmScopeFromConfig(path__default.join(os.homedir(), ".mastracode", "database.json"));
|
|
177
|
-
if (scope) return scope;
|
|
178
|
-
return "thread";
|
|
179
|
-
}
|
|
180
|
-
function loadOmScopeFromConfig(filePath) {
|
|
181
|
-
try {
|
|
182
|
-
if (!fs9__default.existsSync(filePath)) return null;
|
|
183
|
-
const raw = fs9__default.readFileSync(filePath, "utf-8");
|
|
184
|
-
const parsed = JSON.parse(raw);
|
|
185
|
-
if (parsed?.omScope === "thread" || parsed?.omScope === "resource") {
|
|
186
|
-
return parsed.omScope;
|
|
187
|
-
}
|
|
188
|
-
return null;
|
|
189
|
-
} catch {
|
|
190
|
-
return null;
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
function getResourceIdOverride(projectDir) {
|
|
194
|
-
if (process.env.MASTRA_RESOURCE_ID) {
|
|
195
|
-
return process.env.MASTRA_RESOURCE_ID;
|
|
196
|
-
}
|
|
197
|
-
if (projectDir) {
|
|
198
|
-
const rid2 = loadStringField(path__default.join(projectDir, ".mastracode", "database.json"), "resourceId");
|
|
199
|
-
if (rid2) return rid2;
|
|
200
|
-
}
|
|
201
|
-
const rid = loadStringField(path__default.join(os.homedir(), ".mastracode", "database.json"), "resourceId");
|
|
202
|
-
if (rid) return rid;
|
|
203
|
-
return null;
|
|
204
|
-
}
|
|
205
|
-
function loadStringField(filePath, field) {
|
|
206
|
-
try {
|
|
207
|
-
if (!fs9__default.existsSync(filePath)) return null;
|
|
208
|
-
const raw = fs9__default.readFileSync(filePath, "utf-8");
|
|
209
|
-
const parsed = JSON.parse(raw);
|
|
210
|
-
const value = parsed?.[field];
|
|
211
|
-
if (typeof value === "string" && value) {
|
|
212
|
-
return value;
|
|
213
|
-
}
|
|
214
|
-
return null;
|
|
215
|
-
} catch {
|
|
216
|
-
return null;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
// src/auth/pkce.ts
|
|
221
|
-
function base64urlEncode(bytes) {
|
|
222
|
-
let binary = "";
|
|
223
|
-
for (const byte of bytes) {
|
|
224
|
-
binary += String.fromCharCode(byte);
|
|
225
|
-
}
|
|
226
|
-
return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
227
|
-
}
|
|
228
|
-
async function generatePKCE() {
|
|
229
|
-
const verifierBytes = new Uint8Array(32);
|
|
230
|
-
crypto.getRandomValues(verifierBytes);
|
|
231
|
-
const verifier = base64urlEncode(verifierBytes);
|
|
232
|
-
const encoder = new TextEncoder();
|
|
233
|
-
const data = encoder.encode(verifier);
|
|
234
|
-
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
235
|
-
const challenge = base64urlEncode(new Uint8Array(hashBuffer));
|
|
236
|
-
return { verifier, challenge };
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// src/auth/providers/anthropic.ts
|
|
240
|
-
var decode = (s) => atob(s);
|
|
241
|
-
var CLIENT_ID = decode("OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl");
|
|
242
|
-
var AUTHORIZE_URL = "https://claude.ai/oauth/authorize";
|
|
243
|
-
var TOKEN_URL = "https://console.anthropic.com/v1/oauth/token";
|
|
244
|
-
var REDIRECT_URI = "https://console.anthropic.com/oauth/code/callback";
|
|
245
|
-
var SCOPES = "org:create_api_key user:profile user:inference";
|
|
246
|
-
async function loginAnthropic(onAuthUrl, onPromptCode) {
|
|
247
|
-
const { verifier, challenge } = await generatePKCE();
|
|
248
|
-
const authParams = new URLSearchParams({
|
|
249
|
-
code: "true",
|
|
250
|
-
client_id: CLIENT_ID,
|
|
251
|
-
response_type: "code",
|
|
252
|
-
redirect_uri: REDIRECT_URI,
|
|
253
|
-
scope: SCOPES,
|
|
254
|
-
code_challenge: challenge,
|
|
255
|
-
code_challenge_method: "S256",
|
|
256
|
-
state: verifier
|
|
257
|
-
});
|
|
258
|
-
const authUrl = `${AUTHORIZE_URL}?${authParams.toString()}`;
|
|
259
|
-
onAuthUrl(authUrl);
|
|
260
|
-
const authCode = await onPromptCode();
|
|
261
|
-
const splits = authCode.split("#");
|
|
262
|
-
const code = splits[0];
|
|
263
|
-
const state = splits[1];
|
|
264
|
-
const tokenResponse = await fetch(TOKEN_URL, {
|
|
265
|
-
method: "POST",
|
|
266
|
-
headers: {
|
|
267
|
-
"Content-Type": "application/json"
|
|
268
|
-
},
|
|
269
|
-
body: JSON.stringify({
|
|
270
|
-
grant_type: "authorization_code",
|
|
271
|
-
client_id: CLIENT_ID,
|
|
272
|
-
code,
|
|
273
|
-
state,
|
|
274
|
-
redirect_uri: REDIRECT_URI,
|
|
275
|
-
code_verifier: verifier
|
|
276
|
-
})
|
|
277
|
-
});
|
|
278
|
-
if (!tokenResponse.ok) {
|
|
279
|
-
const error = await tokenResponse.text();
|
|
280
|
-
throw new Error(`Token exchange failed: ${error}`);
|
|
281
|
-
}
|
|
282
|
-
const tokenData = await tokenResponse.json();
|
|
283
|
-
const expiresAt = Date.now() + tokenData.expires_in * 1e3 - 5 * 60 * 1e3;
|
|
284
|
-
return {
|
|
285
|
-
refresh: tokenData.refresh_token,
|
|
286
|
-
access: tokenData.access_token,
|
|
287
|
-
expires: expiresAt
|
|
288
|
-
};
|
|
289
|
-
}
|
|
290
|
-
async function refreshAnthropicToken(refreshToken) {
|
|
291
|
-
const response = await fetch(TOKEN_URL, {
|
|
292
|
-
method: "POST",
|
|
293
|
-
headers: { "Content-Type": "application/json" },
|
|
294
|
-
body: JSON.stringify({
|
|
295
|
-
grant_type: "refresh_token",
|
|
296
|
-
client_id: CLIENT_ID,
|
|
297
|
-
refresh_token: refreshToken
|
|
298
|
-
})
|
|
299
|
-
});
|
|
300
|
-
if (!response.ok) {
|
|
301
|
-
const error = await response.text();
|
|
302
|
-
throw new Error(`Anthropic token refresh failed: ${error}`);
|
|
303
|
-
}
|
|
304
|
-
const data = await response.json();
|
|
305
|
-
return {
|
|
306
|
-
refresh: data.refresh_token,
|
|
307
|
-
access: data.access_token,
|
|
308
|
-
expires: Date.now() + data.expires_in * 1e3 - 5 * 60 * 1e3
|
|
309
|
-
};
|
|
310
|
-
}
|
|
311
|
-
var anthropicOAuthProvider = {
|
|
312
|
-
id: "anthropic",
|
|
313
|
-
name: "Anthropic (Claude Pro/Max)",
|
|
314
|
-
async login(callbacks) {
|
|
315
|
-
return loginAnthropic(
|
|
316
|
-
(url) => callbacks.onAuth({ url }),
|
|
317
|
-
() => callbacks.onPrompt({ message: "Paste the authorization code:" })
|
|
318
|
-
);
|
|
319
|
-
},
|
|
320
|
-
async refreshToken(credentials) {
|
|
321
|
-
return refreshAnthropicToken(credentials.refresh);
|
|
322
|
-
},
|
|
323
|
-
getApiKey(credentials) {
|
|
324
|
-
return credentials.access;
|
|
325
|
-
}
|
|
326
|
-
};
|
|
327
|
-
|
|
328
|
-
// src/auth/providers/openai-codex.ts
|
|
329
|
-
var _randomBytes = null;
|
|
330
|
-
var _http = null;
|
|
331
|
-
if (typeof process !== "undefined" && (process.versions?.node || process.versions?.bun)) {
|
|
332
|
-
import('crypto').then((m) => {
|
|
333
|
-
_randomBytes = m.randomBytes;
|
|
334
|
-
});
|
|
335
|
-
import('http').then((m) => {
|
|
336
|
-
_http = m;
|
|
337
|
-
});
|
|
338
|
-
}
|
|
339
|
-
var CLIENT_ID2 = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
340
|
-
var AUTHORIZE_URL2 = "https://auth.openai.com/oauth/authorize";
|
|
341
|
-
var TOKEN_URL2 = "https://auth.openai.com/oauth/token";
|
|
342
|
-
var REDIRECT_URI2 = "http://localhost:1455/auth/callback";
|
|
343
|
-
var SCOPE = "openid profile email offline_access";
|
|
344
|
-
var JWT_CLAIM_PATH = "https://api.openai.com/auth";
|
|
345
|
-
var SUCCESS_HTML = `<!doctype html>
|
|
346
|
-
<html lang="en">
|
|
347
|
-
<head>
|
|
348
|
-
<meta charset="utf-8" />
|
|
349
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
350
|
-
<title>Authentication successful</title>
|
|
351
|
-
</head>
|
|
352
|
-
<body>
|
|
353
|
-
<p>Authentication successful. Return to your terminal to continue.</p>
|
|
354
|
-
</body>
|
|
355
|
-
</html>`;
|
|
356
|
-
function createState() {
|
|
357
|
-
if (!_randomBytes) {
|
|
358
|
-
throw new Error("OpenAI Codex OAuth is only available in Node.js environments");
|
|
359
|
-
}
|
|
360
|
-
return _randomBytes(16).toString("hex");
|
|
361
|
-
}
|
|
362
|
-
function parseAuthorizationInput(input) {
|
|
363
|
-
const value = input.trim();
|
|
364
|
-
if (!value) return {};
|
|
365
|
-
try {
|
|
366
|
-
const url = new URL(value);
|
|
367
|
-
return {
|
|
368
|
-
code: url.searchParams.get("code") ?? void 0,
|
|
369
|
-
state: url.searchParams.get("state") ?? void 0
|
|
370
|
-
};
|
|
371
|
-
} catch {
|
|
372
|
-
}
|
|
373
|
-
if (value.includes("#")) {
|
|
374
|
-
const [code, state] = value.split("#", 2);
|
|
375
|
-
return { code, state };
|
|
376
|
-
}
|
|
377
|
-
if (value.includes("code=")) {
|
|
378
|
-
const params = new URLSearchParams(value);
|
|
379
|
-
return {
|
|
380
|
-
code: params.get("code") ?? void 0,
|
|
381
|
-
state: params.get("state") ?? void 0
|
|
382
|
-
};
|
|
383
|
-
}
|
|
384
|
-
return { code: value };
|
|
385
|
-
}
|
|
386
|
-
function decodeJwt(token) {
|
|
387
|
-
try {
|
|
388
|
-
const parts = token.split(".");
|
|
389
|
-
if (parts.length !== 3) return null;
|
|
390
|
-
const payload = parts[1] ?? "";
|
|
391
|
-
const decoded = atob(payload);
|
|
392
|
-
return JSON.parse(decoded);
|
|
393
|
-
} catch {
|
|
394
|
-
return null;
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
async function exchangeAuthorizationCode(code, verifier, redirectUri = REDIRECT_URI2) {
|
|
398
|
-
const response = await fetch(TOKEN_URL2, {
|
|
399
|
-
method: "POST",
|
|
400
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
401
|
-
body: new URLSearchParams({
|
|
402
|
-
grant_type: "authorization_code",
|
|
403
|
-
client_id: CLIENT_ID2,
|
|
404
|
-
code,
|
|
405
|
-
code_verifier: verifier,
|
|
406
|
-
redirect_uri: redirectUri
|
|
407
|
-
})
|
|
408
|
-
});
|
|
409
|
-
if (!response.ok) {
|
|
410
|
-
const text = await response.text().catch(() => "");
|
|
411
|
-
console.error("[openai-codex] code->token failed:", response.status, text);
|
|
412
|
-
return { type: "failed" };
|
|
413
|
-
}
|
|
414
|
-
const json = await response.json();
|
|
415
|
-
if (!json.access_token || !json.refresh_token || typeof json.expires_in !== "number") {
|
|
416
|
-
console.error("[openai-codex] token response missing fields:", json);
|
|
417
|
-
return { type: "failed" };
|
|
418
|
-
}
|
|
419
|
-
return {
|
|
420
|
-
type: "success",
|
|
421
|
-
access: json.access_token,
|
|
422
|
-
refresh: json.refresh_token,
|
|
423
|
-
expires: Date.now() + json.expires_in * 1e3
|
|
424
|
-
};
|
|
425
|
-
}
|
|
426
|
-
async function refreshAccessToken(refreshToken) {
|
|
427
|
-
try {
|
|
428
|
-
const response = await fetch(TOKEN_URL2, {
|
|
429
|
-
method: "POST",
|
|
430
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
431
|
-
body: new URLSearchParams({
|
|
432
|
-
grant_type: "refresh_token",
|
|
433
|
-
refresh_token: refreshToken,
|
|
434
|
-
client_id: CLIENT_ID2
|
|
435
|
-
})
|
|
436
|
-
});
|
|
437
|
-
if (!response.ok) {
|
|
438
|
-
const text = await response.text().catch(() => "");
|
|
439
|
-
console.error("[openai-codex] Token refresh failed:", response.status, text);
|
|
440
|
-
return { type: "failed" };
|
|
441
|
-
}
|
|
442
|
-
const json = await response.json();
|
|
443
|
-
if (!json.access_token || !json.refresh_token || typeof json.expires_in !== "number") {
|
|
444
|
-
console.error("[openai-codex] Token refresh response missing fields:", json);
|
|
445
|
-
return { type: "failed" };
|
|
446
|
-
}
|
|
447
|
-
return {
|
|
448
|
-
type: "success",
|
|
449
|
-
access: json.access_token,
|
|
450
|
-
refresh: json.refresh_token,
|
|
451
|
-
expires: Date.now() + json.expires_in * 1e3
|
|
452
|
-
};
|
|
453
|
-
} catch (error) {
|
|
454
|
-
console.error("[openai-codex] Token refresh error:", error);
|
|
455
|
-
return { type: "failed" };
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
async function createAuthorizationFlow(originator = "pi") {
|
|
459
|
-
const { verifier, challenge } = await generatePKCE();
|
|
460
|
-
const state = createState();
|
|
461
|
-
const url = new URL(AUTHORIZE_URL2);
|
|
462
|
-
url.searchParams.set("response_type", "code");
|
|
463
|
-
url.searchParams.set("client_id", CLIENT_ID2);
|
|
464
|
-
url.searchParams.set("redirect_uri", REDIRECT_URI2);
|
|
465
|
-
url.searchParams.set("scope", SCOPE);
|
|
466
|
-
url.searchParams.set("code_challenge", challenge);
|
|
467
|
-
url.searchParams.set("code_challenge_method", "S256");
|
|
468
|
-
url.searchParams.set("state", state);
|
|
469
|
-
url.searchParams.set("id_token_add_organizations", "true");
|
|
470
|
-
url.searchParams.set("codex_cli_simplified_flow", "true");
|
|
471
|
-
url.searchParams.set("originator", originator);
|
|
472
|
-
return { verifier, state, url: url.toString() };
|
|
473
|
-
}
|
|
474
|
-
function startLocalOAuthServer(state) {
|
|
475
|
-
if (!_http) {
|
|
476
|
-
throw new Error("OpenAI Codex OAuth is only available in Node.js environments");
|
|
477
|
-
}
|
|
478
|
-
let lastCode = null;
|
|
479
|
-
let cancelled = false;
|
|
480
|
-
const server = _http.createServer((req, res) => {
|
|
481
|
-
try {
|
|
482
|
-
const url = new URL(req.url || "", "http://localhost");
|
|
483
|
-
if (url.pathname !== "/auth/callback") {
|
|
484
|
-
res.statusCode = 404;
|
|
485
|
-
res.end("Not found");
|
|
486
|
-
return;
|
|
487
|
-
}
|
|
488
|
-
if (url.searchParams.get("state") !== state) {
|
|
489
|
-
res.statusCode = 400;
|
|
490
|
-
res.end("State mismatch");
|
|
491
|
-
return;
|
|
492
|
-
}
|
|
493
|
-
const code = url.searchParams.get("code");
|
|
494
|
-
if (!code) {
|
|
495
|
-
res.statusCode = 400;
|
|
496
|
-
res.end("Missing authorization code");
|
|
497
|
-
return;
|
|
498
|
-
}
|
|
499
|
-
res.statusCode = 200;
|
|
500
|
-
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
501
|
-
res.end(SUCCESS_HTML);
|
|
502
|
-
lastCode = code;
|
|
503
|
-
} catch {
|
|
504
|
-
res.statusCode = 500;
|
|
505
|
-
res.end("Internal error");
|
|
506
|
-
}
|
|
507
|
-
});
|
|
508
|
-
return new Promise((resolve10) => {
|
|
509
|
-
server.listen(1455, "127.0.0.1", () => {
|
|
510
|
-
resolve10({
|
|
511
|
-
close: () => server.close(),
|
|
512
|
-
cancelWait: () => {
|
|
513
|
-
cancelled = true;
|
|
514
|
-
},
|
|
515
|
-
waitForCode: async () => {
|
|
516
|
-
const sleep = () => new Promise((r) => setTimeout(r, 100));
|
|
517
|
-
for (let i = 0; i < 600; i += 1) {
|
|
518
|
-
if (lastCode) return { code: lastCode };
|
|
519
|
-
if (cancelled) return null;
|
|
520
|
-
await sleep();
|
|
521
|
-
}
|
|
522
|
-
return null;
|
|
523
|
-
}
|
|
524
|
-
});
|
|
525
|
-
}).on("error", (err) => {
|
|
526
|
-
console.error(
|
|
527
|
-
"[openai-codex] Failed to bind http://127.0.0.1:1455 (",
|
|
528
|
-
err.code,
|
|
529
|
-
") Falling back to manual paste."
|
|
530
|
-
);
|
|
531
|
-
resolve10({
|
|
532
|
-
close: () => {
|
|
533
|
-
try {
|
|
534
|
-
server.close();
|
|
535
|
-
} catch {
|
|
536
|
-
}
|
|
537
|
-
},
|
|
538
|
-
cancelWait: () => {
|
|
539
|
-
},
|
|
540
|
-
waitForCode: async () => null
|
|
541
|
-
});
|
|
542
|
-
});
|
|
543
|
-
});
|
|
544
|
-
}
|
|
545
|
-
function getAccountId(accessToken) {
|
|
546
|
-
const payload = decodeJwt(accessToken);
|
|
547
|
-
const auth = payload?.[JWT_CLAIM_PATH];
|
|
548
|
-
const accountId = auth?.chatgpt_account_id;
|
|
549
|
-
return typeof accountId === "string" && accountId.length > 0 ? accountId : null;
|
|
550
|
-
}
|
|
551
|
-
async function loginOpenAICodex(options) {
|
|
552
|
-
const { verifier, state, url } = await createAuthorizationFlow(options.originator);
|
|
553
|
-
const server = await startLocalOAuthServer(state);
|
|
554
|
-
options.onAuth({
|
|
555
|
-
url,
|
|
556
|
-
instructions: "A browser window should open. Complete login to finish."
|
|
557
|
-
});
|
|
558
|
-
let code;
|
|
559
|
-
try {
|
|
560
|
-
if (options.onManualCodeInput) {
|
|
561
|
-
let manualCode;
|
|
562
|
-
let manualError;
|
|
563
|
-
const manualPromise = options.onManualCodeInput().then((input) => {
|
|
564
|
-
manualCode = input;
|
|
565
|
-
server.cancelWait();
|
|
566
|
-
}).catch((err) => {
|
|
567
|
-
manualError = err instanceof Error ? err : new Error(String(err));
|
|
568
|
-
server.cancelWait();
|
|
569
|
-
});
|
|
570
|
-
const result = await server.waitForCode();
|
|
571
|
-
if (manualError) {
|
|
572
|
-
throw manualError;
|
|
573
|
-
}
|
|
574
|
-
if (result?.code) {
|
|
575
|
-
code = result.code;
|
|
576
|
-
} else if (manualCode) {
|
|
577
|
-
const parsed = parseAuthorizationInput(manualCode);
|
|
578
|
-
if (parsed.state && parsed.state !== state) {
|
|
579
|
-
throw new Error("State mismatch");
|
|
580
|
-
}
|
|
581
|
-
code = parsed.code;
|
|
582
|
-
}
|
|
583
|
-
if (!code) {
|
|
584
|
-
await manualPromise;
|
|
585
|
-
if (manualError) {
|
|
586
|
-
throw manualError;
|
|
587
|
-
}
|
|
588
|
-
if (manualCode) {
|
|
589
|
-
const parsed = parseAuthorizationInput(manualCode);
|
|
590
|
-
if (parsed.state && parsed.state !== state) {
|
|
591
|
-
throw new Error("State mismatch");
|
|
592
|
-
}
|
|
593
|
-
code = parsed.code;
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
} else {
|
|
597
|
-
const result = await server.waitForCode();
|
|
598
|
-
if (result?.code) {
|
|
599
|
-
code = result.code;
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
if (!code) {
|
|
603
|
-
const input = await options.onPrompt({
|
|
604
|
-
message: "Paste the authorization code (or full redirect URL):"
|
|
605
|
-
});
|
|
606
|
-
const parsed = parseAuthorizationInput(input);
|
|
607
|
-
if (parsed.state && parsed.state !== state) {
|
|
608
|
-
throw new Error("State mismatch");
|
|
609
|
-
}
|
|
610
|
-
code = parsed.code;
|
|
611
|
-
}
|
|
612
|
-
if (!code) {
|
|
613
|
-
throw new Error("Missing authorization code");
|
|
614
|
-
}
|
|
615
|
-
const tokenResult = await exchangeAuthorizationCode(code, verifier);
|
|
616
|
-
if (tokenResult.type !== "success") {
|
|
617
|
-
throw new Error("Token exchange failed");
|
|
618
|
-
}
|
|
619
|
-
const accountId = getAccountId(tokenResult.access);
|
|
620
|
-
if (!accountId) {
|
|
621
|
-
throw new Error("Failed to extract accountId from token");
|
|
622
|
-
}
|
|
623
|
-
return {
|
|
624
|
-
access: tokenResult.access,
|
|
625
|
-
refresh: tokenResult.refresh,
|
|
626
|
-
expires: tokenResult.expires,
|
|
627
|
-
accountId
|
|
628
|
-
};
|
|
629
|
-
} finally {
|
|
630
|
-
server.close();
|
|
631
|
-
}
|
|
632
|
-
}
|
|
633
|
-
async function refreshOpenAICodexToken(refreshToken) {
|
|
634
|
-
const result = await refreshAccessToken(refreshToken);
|
|
635
|
-
if (result.type !== "success") {
|
|
636
|
-
throw new Error("Failed to refresh OpenAI Codex token");
|
|
637
|
-
}
|
|
638
|
-
const accountId = getAccountId(result.access);
|
|
639
|
-
if (!accountId) {
|
|
640
|
-
throw new Error("Failed to extract accountId from token");
|
|
641
|
-
}
|
|
642
|
-
return {
|
|
643
|
-
access: result.access,
|
|
644
|
-
refresh: result.refresh,
|
|
645
|
-
expires: result.expires,
|
|
646
|
-
accountId
|
|
647
|
-
};
|
|
648
|
-
}
|
|
649
|
-
var openaiCodexOAuthProvider = {
|
|
650
|
-
id: "openai-codex",
|
|
651
|
-
name: "ChatGPT Plus/Pro (Codex Subscription)",
|
|
652
|
-
usesCallbackServer: true,
|
|
653
|
-
async login(callbacks) {
|
|
654
|
-
return loginOpenAICodex({
|
|
655
|
-
onAuth: callbacks.onAuth,
|
|
656
|
-
onPrompt: callbacks.onPrompt,
|
|
657
|
-
onProgress: callbacks.onProgress,
|
|
658
|
-
onManualCodeInput: callbacks.onManualCodeInput
|
|
659
|
-
});
|
|
660
|
-
},
|
|
661
|
-
async refreshToken(credentials) {
|
|
662
|
-
return refreshOpenAICodexToken(credentials.refresh);
|
|
663
|
-
},
|
|
664
|
-
getApiKey(credentials) {
|
|
665
|
-
return credentials.access;
|
|
666
|
-
}
|
|
667
|
-
};
|
|
668
|
-
|
|
669
|
-
// src/auth/storage.ts
|
|
670
|
-
var PROVIDER_DEFAULT_MODELS = {
|
|
671
|
-
anthropic: "anthropic/claude-opus-4-6",
|
|
672
|
-
"openai-codex": "openai/gpt-5.3-codex"
|
|
673
|
-
};
|
|
674
|
-
var oauthProviderRegistry = /* @__PURE__ */ new Map([
|
|
675
|
-
[anthropicOAuthProvider.id, anthropicOAuthProvider],
|
|
676
|
-
[openaiCodexOAuthProvider.id, openaiCodexOAuthProvider]
|
|
677
|
-
]);
|
|
678
|
-
function getOAuthProvider(id) {
|
|
679
|
-
return oauthProviderRegistry.get(id);
|
|
680
|
-
}
|
|
681
|
-
function getOAuthProviders() {
|
|
682
|
-
return Array.from(oauthProviderRegistry.values());
|
|
683
|
-
}
|
|
684
|
-
var AuthStorage = class {
|
|
685
|
-
constructor(authPath = join(getAppDataDir(), "auth.json")) {
|
|
686
|
-
this.authPath = authPath;
|
|
687
|
-
this.reload();
|
|
688
|
-
}
|
|
689
|
-
data = {};
|
|
690
|
-
/**
|
|
691
|
-
* Reload credentials from disk.
|
|
692
|
-
*/
|
|
693
|
-
reload() {
|
|
694
|
-
if (!existsSync(this.authPath)) {
|
|
695
|
-
this.data = {};
|
|
696
|
-
return;
|
|
697
|
-
}
|
|
698
|
-
try {
|
|
699
|
-
this.data = JSON.parse(readFileSync(this.authPath, "utf-8"));
|
|
700
|
-
} catch {
|
|
701
|
-
this.data = {};
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
/**
|
|
705
|
-
* Save credentials to disk.
|
|
706
|
-
*/
|
|
707
|
-
save() {
|
|
708
|
-
const dir = dirname(this.authPath);
|
|
709
|
-
if (!existsSync(dir)) {
|
|
710
|
-
mkdirSync(dir, { recursive: true, mode: 448 });
|
|
711
|
-
}
|
|
712
|
-
writeFileSync(this.authPath, JSON.stringify(this.data, null, 2), "utf-8");
|
|
713
|
-
chmodSync(this.authPath, 384);
|
|
714
|
-
}
|
|
715
|
-
/**
|
|
716
|
-
* Get credential for a provider.
|
|
717
|
-
*/
|
|
718
|
-
get(provider) {
|
|
719
|
-
return this.data[provider] ?? void 0;
|
|
720
|
-
}
|
|
721
|
-
/**
|
|
722
|
-
* Set credential for a provider.
|
|
723
|
-
*/
|
|
724
|
-
set(provider, credential) {
|
|
725
|
-
this.data[provider] = credential;
|
|
726
|
-
this.save();
|
|
727
|
-
}
|
|
728
|
-
/**
|
|
729
|
-
* Remove credential for a provider.
|
|
730
|
-
*/
|
|
731
|
-
remove(provider) {
|
|
732
|
-
delete this.data[provider];
|
|
733
|
-
this.save();
|
|
734
|
-
}
|
|
735
|
-
/**
|
|
736
|
-
* List all providers with credentials.
|
|
737
|
-
*/
|
|
738
|
-
list() {
|
|
739
|
-
return Object.keys(this.data);
|
|
740
|
-
}
|
|
741
|
-
/**
|
|
742
|
-
* Check if credentials exist for a provider.
|
|
743
|
-
*/
|
|
744
|
-
has(provider) {
|
|
745
|
-
return provider in this.data;
|
|
746
|
-
}
|
|
747
|
-
/**
|
|
748
|
-
* Check if logged in via OAuth for a provider.
|
|
749
|
-
*/
|
|
750
|
-
isLoggedIn(provider) {
|
|
751
|
-
const cred = this.data[provider];
|
|
752
|
-
return cred?.type === "oauth";
|
|
753
|
-
}
|
|
754
|
-
/**
|
|
755
|
-
* Login to an OAuth provider.
|
|
756
|
-
*/
|
|
757
|
-
async login(providerId, callbacks) {
|
|
758
|
-
const provider = getOAuthProvider(providerId);
|
|
759
|
-
if (!provider) {
|
|
760
|
-
throw new Error(`Unknown OAuth provider: ${providerId}`);
|
|
761
|
-
}
|
|
762
|
-
const credentials = await provider.login(callbacks);
|
|
763
|
-
this.set(providerId, { type: "oauth", ...credentials });
|
|
764
|
-
}
|
|
765
|
-
/**
|
|
766
|
-
* Logout from a provider.
|
|
767
|
-
*/
|
|
768
|
-
logout(provider) {
|
|
769
|
-
this.remove(provider);
|
|
770
|
-
}
|
|
771
|
-
/**
|
|
772
|
-
* Get API key for a provider, auto-refreshing OAuth tokens if needed.
|
|
773
|
-
*/
|
|
774
|
-
async getApiKey(providerId) {
|
|
775
|
-
const cred = this.data[providerId];
|
|
776
|
-
if (cred?.type === "api_key") {
|
|
777
|
-
return cred.key;
|
|
778
|
-
}
|
|
779
|
-
if (cred?.type === "oauth") {
|
|
780
|
-
const provider = getOAuthProvider(providerId);
|
|
781
|
-
if (!provider) {
|
|
782
|
-
return void 0;
|
|
783
|
-
}
|
|
784
|
-
if (Date.now() >= cred.expires) {
|
|
785
|
-
try {
|
|
786
|
-
const newCreds = await provider.refreshToken(cred);
|
|
787
|
-
this.set(providerId, { type: "oauth", ...newCreds });
|
|
788
|
-
return provider.getApiKey(newCreds);
|
|
789
|
-
} catch {
|
|
790
|
-
return void 0;
|
|
791
|
-
}
|
|
792
|
-
}
|
|
793
|
-
return provider.getApiKey(cred);
|
|
794
|
-
}
|
|
795
|
-
return void 0;
|
|
796
|
-
}
|
|
797
|
-
/**
|
|
798
|
-
* Get the last used model ID (global preference).
|
|
799
|
-
*/
|
|
800
|
-
getLastModelId() {
|
|
801
|
-
return this.data._lastModelId;
|
|
802
|
-
}
|
|
803
|
-
/**
|
|
804
|
-
* Set the last used model ID (global preference).
|
|
805
|
-
*/
|
|
806
|
-
setLastModelId(modelId) {
|
|
807
|
-
this.data._lastModelId = modelId;
|
|
808
|
-
this.save();
|
|
809
|
-
}
|
|
810
|
-
/**
|
|
811
|
-
* Get the global model ID for a specific mode.
|
|
812
|
-
* @param modeId Mode ID (e.g., "build", "plan", "explore")
|
|
813
|
-
*/
|
|
814
|
-
getModeModelId(modeId) {
|
|
815
|
-
return this.data[`_modeModelId_${modeId}`];
|
|
816
|
-
}
|
|
817
|
-
/**
|
|
818
|
-
* Set the global model ID for a specific mode.
|
|
819
|
-
* @param modeId Mode ID (e.g., "build", "plan", "explore")
|
|
820
|
-
* @param modelId Full model ID
|
|
821
|
-
*/
|
|
822
|
-
setModeModelId(modeId, modelId) {
|
|
823
|
-
this.data[`_modeModelId_${modeId}`] = modelId;
|
|
824
|
-
this.save();
|
|
825
|
-
}
|
|
826
|
-
/**
|
|
827
|
-
* Get the global subagent model ID for an agent type.
|
|
828
|
-
* @param agentType Optional agent type (explore, plan, execute)
|
|
829
|
-
*/
|
|
830
|
-
getSubagentModelId(agentType) {
|
|
831
|
-
if (agentType) {
|
|
832
|
-
return this.data[`_subagentModelId_${agentType}`];
|
|
833
|
-
}
|
|
834
|
-
return this.data._subagentModelId;
|
|
835
|
-
}
|
|
836
|
-
/**
|
|
837
|
-
* Set the global subagent model ID for an agent type.
|
|
838
|
-
* @param modelId Full model ID
|
|
839
|
-
* @param agentType Optional agent type (explore, plan, execute)
|
|
840
|
-
*/
|
|
841
|
-
setSubagentModelId(modelId, agentType) {
|
|
842
|
-
if (agentType) {
|
|
843
|
-
this.data[`_subagentModelId_${agentType}`] = modelId;
|
|
844
|
-
} else {
|
|
845
|
-
this.data._subagentModelId = modelId;
|
|
846
|
-
}
|
|
847
|
-
this.save();
|
|
848
|
-
}
|
|
849
|
-
/**
|
|
850
|
-
* Get the default model for a provider after login.
|
|
851
|
-
*/
|
|
852
|
-
getDefaultModelForProvider(providerId) {
|
|
853
|
-
return PROVIDER_DEFAULT_MODELS[providerId];
|
|
854
|
-
}
|
|
855
|
-
// ===========================================================================
|
|
856
|
-
// Model Usage Ranking
|
|
857
|
-
// ===========================================================================
|
|
858
|
-
getModelRanks() {
|
|
859
|
-
return this.data._modelRanks ?? {};
|
|
860
|
-
}
|
|
861
|
-
/**
|
|
862
|
-
* Get the use count for a model (0 if never used).
|
|
863
|
-
*/
|
|
864
|
-
getModelUseCount(modelId) {
|
|
865
|
-
return this.getModelRanks()[modelId] ?? 0;
|
|
866
|
-
}
|
|
867
|
-
/**
|
|
868
|
-
* Get all model use counts.
|
|
869
|
-
*/
|
|
870
|
-
getAllModelUseCounts() {
|
|
871
|
-
return { ...this.getModelRanks() };
|
|
872
|
-
}
|
|
873
|
-
/**
|
|
874
|
-
* Increment the use count for a model and persist.
|
|
875
|
-
*/
|
|
876
|
-
incrementModelUseCount(modelId) {
|
|
877
|
-
const ranks = this.getModelRanks();
|
|
878
|
-
ranks[modelId] = (ranks[modelId] ?? 0) + 1;
|
|
879
|
-
this.data._modelRanks = ranks;
|
|
880
|
-
this.save();
|
|
881
|
-
}
|
|
882
|
-
};
|
|
883
|
-
var claudeCodeIdentity = "You are Claude Code, Anthropic's official CLI for Claude.";
|
|
884
|
-
var authStorageInstance = null;
|
|
885
|
-
function getAuthStorage() {
|
|
886
|
-
if (!authStorageInstance) {
|
|
887
|
-
authStorageInstance = new AuthStorage();
|
|
888
|
-
}
|
|
889
|
-
return authStorageInstance;
|
|
890
|
-
}
|
|
891
|
-
function setAuthStorage(storage) {
|
|
892
|
-
authStorageInstance = storage;
|
|
893
|
-
}
|
|
894
|
-
var claudeCodeMiddleware = {
|
|
895
|
-
specificationVersion: "v3",
|
|
896
|
-
transformParams: async ({ params }) => {
|
|
897
|
-
const systemMessage = {
|
|
898
|
-
role: "system",
|
|
899
|
-
content: claudeCodeIdentity
|
|
900
|
-
};
|
|
901
|
-
if (params.temperature) {
|
|
902
|
-
delete params.topP;
|
|
903
|
-
}
|
|
904
|
-
return {
|
|
905
|
-
...params,
|
|
906
|
-
prompt: [systemMessage, ...params.prompt]
|
|
907
|
-
};
|
|
908
|
-
}
|
|
909
|
-
};
|
|
910
|
-
var promptCacheMiddleware = {
|
|
911
|
-
specificationVersion: "v3",
|
|
912
|
-
transformParams: async ({ params }) => {
|
|
913
|
-
const prompt = [...params.prompt];
|
|
914
|
-
const cacheControl = { type: "ephemeral", ttl: "5m" };
|
|
915
|
-
const addCacheToMessage = (msg) => {
|
|
916
|
-
if (typeof msg.content === "string") {
|
|
917
|
-
return {
|
|
918
|
-
...msg,
|
|
919
|
-
providerOptions: {
|
|
920
|
-
...msg.providerOptions,
|
|
921
|
-
anthropic: { ...msg.providerOptions?.anthropic, cacheControl }
|
|
922
|
-
}
|
|
923
|
-
};
|
|
924
|
-
}
|
|
925
|
-
if (Array.isArray(msg.content) && msg.content.length > 0) {
|
|
926
|
-
const content = [...msg.content];
|
|
927
|
-
const lastPart = content[content.length - 1];
|
|
928
|
-
content[content.length - 1] = {
|
|
929
|
-
...lastPart,
|
|
930
|
-
providerOptions: {
|
|
931
|
-
...lastPart.providerOptions,
|
|
932
|
-
anthropic: { ...lastPart.providerOptions?.anthropic, cacheControl }
|
|
933
|
-
}
|
|
934
|
-
};
|
|
935
|
-
return { ...msg, content };
|
|
936
|
-
}
|
|
937
|
-
return msg;
|
|
938
|
-
};
|
|
939
|
-
let lastSystemIdx = -1;
|
|
940
|
-
for (let i = prompt.length - 1; i >= 0; i--) {
|
|
941
|
-
if (prompt[i].role === "system") {
|
|
942
|
-
lastSystemIdx = i;
|
|
943
|
-
break;
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
|
-
if (lastSystemIdx >= 0) {
|
|
947
|
-
prompt[lastSystemIdx] = addCacheToMessage(prompt[lastSystemIdx]);
|
|
948
|
-
}
|
|
949
|
-
const lastIdx = prompt.length - 1;
|
|
950
|
-
if (lastIdx >= 0 && lastIdx !== lastSystemIdx) {
|
|
951
|
-
prompt[lastIdx] = addCacheToMessage(prompt[lastIdx]);
|
|
952
|
-
}
|
|
953
|
-
return { ...params, prompt };
|
|
954
|
-
}
|
|
955
|
-
};
|
|
956
|
-
function opencodeClaudeMaxProvider(modelId = "claude-sonnet-4-20250514") {
|
|
957
|
-
if (process.env.NODE_ENV === "test" || process.env.VITEST) {
|
|
958
|
-
const anthropic2 = createAnthropic({
|
|
959
|
-
apiKey: process.env.ANTHROPIC_API_KEY || "test-api-key"
|
|
960
|
-
});
|
|
961
|
-
return wrapLanguageModel({
|
|
962
|
-
model: anthropic2(modelId),
|
|
963
|
-
middleware: [claudeCodeMiddleware, promptCacheMiddleware]
|
|
964
|
-
});
|
|
965
|
-
}
|
|
966
|
-
const oauthFetch = async (url, init) => {
|
|
967
|
-
const authStorage2 = getAuthStorage();
|
|
968
|
-
authStorage2.reload();
|
|
969
|
-
const accessToken = await authStorage2.getApiKey("anthropic");
|
|
970
|
-
if (!accessToken) {
|
|
971
|
-
throw new Error("Not logged in to Anthropic. Run /login first.");
|
|
972
|
-
}
|
|
973
|
-
return fetch(url, {
|
|
974
|
-
...init,
|
|
975
|
-
headers: {
|
|
976
|
-
Authorization: `Bearer ${accessToken}`,
|
|
977
|
-
"anthropic-beta": "oauth-2025-04-20,claude-code-20250219,interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14",
|
|
978
|
-
"anthropic-version": "2023-06-01"
|
|
979
|
-
}
|
|
980
|
-
});
|
|
981
|
-
};
|
|
982
|
-
const anthropic = createAnthropic({
|
|
983
|
-
// Provide a dummy API key - the actual auth is handled via OAuth in oauthFetch
|
|
984
|
-
// This prevents the SDK from throwing "API key is missing" at model creation time
|
|
985
|
-
apiKey: "oauth-placeholder",
|
|
986
|
-
fetch: oauthFetch
|
|
987
|
-
});
|
|
988
|
-
return wrapLanguageModel({
|
|
989
|
-
model: anthropic(modelId),
|
|
990
|
-
middleware: [claudeCodeMiddleware, promptCacheMiddleware]
|
|
991
|
-
});
|
|
992
|
-
}
|
|
993
|
-
var CODEX_API_ENDPOINT = "https://chatgpt.com/backend-api/codex/responses";
|
|
994
|
-
var authStorageInstance2 = null;
|
|
995
|
-
function getAuthStorage2() {
|
|
996
|
-
if (!authStorageInstance2) {
|
|
997
|
-
authStorageInstance2 = new AuthStorage();
|
|
998
|
-
}
|
|
999
|
-
return authStorageInstance2;
|
|
1000
|
-
}
|
|
1001
|
-
function setAuthStorage2(storage) {
|
|
1002
|
-
authStorageInstance2 = storage;
|
|
1003
|
-
}
|
|
1004
|
-
var CODEX_INSTRUCTIONS = `You are an interactive CLI tool that helps users with software engineering tasks. Use the instructions below and the tools available to you to assist the user.
|
|
1005
|
-
|
|
1006
|
-
IMPORTANT: You should be concise, direct, and helpful. Focus on solving the user's problem efficiently.`;
|
|
1007
|
-
var openaiCodexMiddleware = {
|
|
1008
|
-
specificationVersion: "v3",
|
|
1009
|
-
transformParams: async ({ params }) => {
|
|
1010
|
-
if (params.temperature) {
|
|
1011
|
-
delete params.topP;
|
|
1012
|
-
}
|
|
1013
|
-
params.providerOptions = {
|
|
1014
|
-
...params.providerOptions,
|
|
1015
|
-
openai: {
|
|
1016
|
-
...params.providerOptions?.openai ?? {},
|
|
1017
|
-
// Codex API requires instructions
|
|
1018
|
-
instructions: CODEX_INSTRUCTIONS,
|
|
1019
|
-
// Codex API requires store to be false
|
|
1020
|
-
store: false
|
|
1021
|
-
}
|
|
1022
|
-
};
|
|
1023
|
-
return params;
|
|
1024
|
-
}
|
|
1025
|
-
};
|
|
1026
|
-
function openaiCodexProvider(modelId = "codex-mini-latest") {
|
|
1027
|
-
if (process.env.NODE_ENV === "test" || process.env.VITEST) {
|
|
1028
|
-
const openai2 = createOpenAI({
|
|
1029
|
-
apiKey: process.env.OPENAI_API_KEY || "test-api-key"
|
|
1030
|
-
});
|
|
1031
|
-
return wrapLanguageModel({
|
|
1032
|
-
model: openai2.responses(modelId),
|
|
1033
|
-
middleware: [openaiCodexMiddleware]
|
|
1034
|
-
});
|
|
1035
|
-
}
|
|
1036
|
-
const oauthFetch = async (url, init) => {
|
|
1037
|
-
const authStorage2 = getAuthStorage2();
|
|
1038
|
-
authStorage2.reload();
|
|
1039
|
-
const cred = authStorage2.get("openai-codex");
|
|
1040
|
-
if (!cred || cred.type !== "oauth") {
|
|
1041
|
-
throw new Error("Not logged in to OpenAI Codex. Run /login first.");
|
|
1042
|
-
}
|
|
1043
|
-
let accessToken = cred.access;
|
|
1044
|
-
if (Date.now() >= cred.expires) {
|
|
1045
|
-
const refreshedToken = await authStorage2.getApiKey("openai-codex");
|
|
1046
|
-
if (!refreshedToken) {
|
|
1047
|
-
throw new Error("Failed to refresh OpenAI Codex token. Please /login again.");
|
|
1048
|
-
}
|
|
1049
|
-
accessToken = refreshedToken;
|
|
1050
|
-
authStorage2.reload();
|
|
1051
|
-
}
|
|
1052
|
-
const accountId = cred.accountId;
|
|
1053
|
-
const headers = new Headers();
|
|
1054
|
-
if (init?.headers) {
|
|
1055
|
-
if (init.headers instanceof Headers) {
|
|
1056
|
-
init.headers.forEach((value, key) => {
|
|
1057
|
-
if (key.toLowerCase() !== "authorization") {
|
|
1058
|
-
headers.set(key, value);
|
|
1059
|
-
}
|
|
1060
|
-
});
|
|
1061
|
-
} else if (Array.isArray(init.headers)) {
|
|
1062
|
-
for (const [key, value] of init.headers) {
|
|
1063
|
-
if (key.toLowerCase() !== "authorization" && value !== void 0) {
|
|
1064
|
-
headers.set(key, String(value));
|
|
1065
|
-
}
|
|
1066
|
-
}
|
|
1067
|
-
} else {
|
|
1068
|
-
for (const [key, value] of Object.entries(init.headers)) {
|
|
1069
|
-
if (key.toLowerCase() !== "authorization" && value !== void 0) {
|
|
1070
|
-
headers.set(key, String(value));
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
}
|
|
1074
|
-
}
|
|
1075
|
-
headers.set("Authorization", `Bearer ${accessToken}`);
|
|
1076
|
-
if (accountId) {
|
|
1077
|
-
headers.set("ChatGPT-Account-Id", accountId);
|
|
1078
|
-
}
|
|
1079
|
-
const parsed = url instanceof URL ? url : new URL(typeof url === "string" ? url : url.url);
|
|
1080
|
-
const shouldRewrite = parsed.pathname.includes("/v1/responses") || parsed.pathname.includes("/chat/completions");
|
|
1081
|
-
const finalUrl = shouldRewrite ? new URL(CODEX_API_ENDPOINT) : parsed;
|
|
1082
|
-
return fetch(finalUrl, {
|
|
1083
|
-
...init,
|
|
1084
|
-
headers
|
|
1085
|
-
});
|
|
1086
|
-
};
|
|
1087
|
-
const openai = createOpenAI({
|
|
1088
|
-
// Use a dummy API key since we're using OAuth
|
|
1089
|
-
apiKey: "oauth-dummy-key",
|
|
1090
|
-
fetch: oauthFetch
|
|
1091
|
-
});
|
|
1092
|
-
return wrapLanguageModel({
|
|
1093
|
-
model: openai.responses(modelId),
|
|
1094
|
-
middleware: [openaiCodexMiddleware]
|
|
1095
|
-
});
|
|
1096
|
-
}
|
|
1097
|
-
var authStorage = new AuthStorage();
|
|
1098
|
-
function resolveModel(modelId) {
|
|
1099
|
-
authStorage.reload();
|
|
1100
|
-
const isAnthropicModel = modelId.startsWith("anthropic/");
|
|
1101
|
-
const isOpenAIModel = modelId.startsWith("openai/");
|
|
1102
|
-
const isMoonshotModel = modelId.startsWith("moonshotai/");
|
|
1103
|
-
if (isMoonshotModel) {
|
|
1104
|
-
if (!process.env.MOONSHOT_AI_API_KEY) {
|
|
1105
|
-
throw new Error(`Need MOONSHOT_AI_API_KEY`);
|
|
1106
|
-
}
|
|
1107
|
-
return createAnthropic({
|
|
1108
|
-
apiKey: process.env.MOONSHOT_AI_API_KEY,
|
|
1109
|
-
baseURL: "https://api.moonshot.ai/anthropic/v1",
|
|
1110
|
-
name: "moonshotai.anthropicv1"
|
|
1111
|
-
})(modelId.substring("moonshotai/".length));
|
|
1112
|
-
} else if (isAnthropicModel) {
|
|
1113
|
-
return opencodeClaudeMaxProvider(modelId.substring(`anthropic/`.length));
|
|
1114
|
-
} else if (isOpenAIModel && authStorage.isLoggedIn("openai-codex")) {
|
|
1115
|
-
return openaiCodexProvider(modelId.substring(`openai/`.length));
|
|
1116
|
-
} else {
|
|
1117
|
-
return new ModelRouterLanguageModel(modelId);
|
|
1118
|
-
}
|
|
1119
|
-
}
|
|
1120
|
-
function getDynamicModel({
|
|
1121
|
-
requestContext
|
|
1122
|
-
}) {
|
|
1123
|
-
const harnessContext = requestContext.get("harness");
|
|
1124
|
-
const modelId = harnessContext?.state?.currentModelId;
|
|
1125
|
-
if (!modelId) {
|
|
1126
|
-
throw new Error("No model selected. Use /models to select a model first.");
|
|
1127
|
-
}
|
|
1128
|
-
return resolveModel(modelId);
|
|
1129
|
-
}
|
|
1130
|
-
|
|
1131
|
-
// src/agents/subagents/execute.ts
|
|
1132
|
-
var executeSubagent = {
|
|
1133
|
-
id: "execute",
|
|
1134
|
-
name: "Execute",
|
|
1135
|
-
instructions: `You are a focused execution agent. Your job is to complete a specific, well-defined task by making the necessary changes to the codebase.
|
|
1136
|
-
|
|
1137
|
-
## Rules
|
|
1138
|
-
- You have FULL ACCESS to read, write, and execute within your task scope.
|
|
1139
|
-
- Stay focused on the specific task given. Do not make unrelated changes.
|
|
1140
|
-
- Read files before modifying them \u2014 use view first, then string_replace_lsp or write_file.
|
|
1141
|
-
- Verify your changes work by running relevant tests or checking for errors.
|
|
1142
|
-
|
|
1143
|
-
## Tool Strategy
|
|
1144
|
-
- **Read first**: Always view a file before editing it
|
|
1145
|
-
- **Edit precisely**: Use string_replace_lsp with enough context to match uniquely
|
|
1146
|
-
- **Use specialized tools**: Prefer view/search_content/find_files over shell commands for reading
|
|
1147
|
-
- **Parallelize**: Make independent tool calls together (e.g., view multiple files at once)
|
|
1148
|
-
|
|
1149
|
-
## Workflow
|
|
1150
|
-
. Understand the task and explore relevant code
|
|
1151
|
-
. For complex tasks (3+ steps): use task_write to track progress
|
|
1152
|
-
. Make changes incrementally \u2014 verify each change before moving on
|
|
1153
|
-
. Run tests or type-check to verify
|
|
1154
|
-
. If you created tasks: ALWAYS call task_check before finishing
|
|
1155
|
-
|
|
1156
|
-
## Efficiency
|
|
1157
|
-
Your output returns to the parent agent. Be concise:
|
|
1158
|
-
- Don't repeat file contents in your response
|
|
1159
|
-
- Summarize what changed, don't narrate each step
|
|
1160
|
-
- Keep your final summary under 300 words
|
|
1161
|
-
|
|
1162
|
-
## Output Format
|
|
1163
|
-
End with a structured summary:
|
|
1164
|
-
. **Completed**: What you implemented (1-2 sentences)
|
|
1165
|
-
. **Changes**: Files modified/created
|
|
1166
|
-
. **Verification**: How you verified it works
|
|
1167
|
-
. **Notes**: Follow-up needed (if any)`,
|
|
1168
|
-
allowedTools: [
|
|
1169
|
-
// Read tools
|
|
1170
|
-
"view",
|
|
1171
|
-
"search_content",
|
|
1172
|
-
"find_files",
|
|
1173
|
-
// Write tools
|
|
1174
|
-
"string_replace_lsp",
|
|
1175
|
-
"write_file",
|
|
1176
|
-
// Execution tool
|
|
1177
|
-
"execute_command",
|
|
1178
|
-
// Task tracking (built-in harness tools)
|
|
1179
|
-
"task_write",
|
|
1180
|
-
"task_check"
|
|
1181
|
-
]
|
|
1182
|
-
};
|
|
1183
|
-
|
|
1184
|
-
// src/agents/subagents/explore.ts
|
|
1185
|
-
var exploreSubagent = {
|
|
1186
|
-
id: "explore",
|
|
1187
|
-
name: "Explore",
|
|
1188
|
-
instructions: `You are an expert code explorer. Your job is to investigate a codebase and answer a specific question or gather specific information.
|
|
1189
|
-
|
|
1190
|
-
## Rules
|
|
1191
|
-
- You have READ-ONLY access. You cannot modify files or run commands.
|
|
1192
|
-
- Be thorough \u2014 search broadly first, then drill into relevant files.
|
|
1193
|
-
- After gathering enough information, produce a clear, concise summary of your findings.
|
|
1194
|
-
|
|
1195
|
-
## Tool Strategy
|
|
1196
|
-
- **Start broad**: Use find_files (glob) to understand project structure
|
|
1197
|
-
- **Search smart**: Use search_content (grep) with specific patterns \u2014 avoid overly broad searches
|
|
1198
|
-
- **Read efficiently**: Use view with view_range for large files \u2014 don't read entire files if you only need a section
|
|
1199
|
-
- **Parallelize**: Make multiple independent tool calls in one round when exploring different areas
|
|
1200
|
-
|
|
1201
|
-
## Efficiency
|
|
1202
|
-
Your output returns to the parent agent. Be concise:
|
|
1203
|
-
- Don't include raw file contents in your response \u2014 summarize what you found
|
|
1204
|
-
- Reference files by path and line number, not by copying code
|
|
1205
|
-
- If a search returns many results, report the count and key examples, not every match
|
|
1206
|
-
|
|
1207
|
-
## Output Format
|
|
1208
|
-
End with a structured summary:
|
|
1209
|
-
. **Answer**: Direct answer to the question (1-2 sentences)
|
|
1210
|
-
. **Key Files**: Most relevant files with line numbers
|
|
1211
|
-
. **Details**: Additional context if needed
|
|
1212
|
-
|
|
1213
|
-
Keep your summary under 300 words.`,
|
|
1214
|
-
allowedTools: ["view", "search_content", "find_files"]
|
|
1215
|
-
};
|
|
1216
|
-
|
|
1217
|
-
// src/agents/subagents/plan.ts
|
|
1218
|
-
var planSubagent = {
|
|
1219
|
-
id: "plan",
|
|
1220
|
-
name: "Plan",
|
|
1221
|
-
instructions: `You are an expert software architect and planner. Your job is to analyze a codebase and produce a detailed implementation plan for a given task.
|
|
1222
|
-
|
|
1223
|
-
## Rules
|
|
1224
|
-
- You have READ-ONLY access. You cannot modify files or run commands.
|
|
1225
|
-
- First, explore the codebase to understand existing patterns, architecture, and conventions.
|
|
1226
|
-
- Produce a concrete, actionable plan \u2014 not vague suggestions.
|
|
1227
|
-
|
|
1228
|
-
## Tool Strategy
|
|
1229
|
-
- **Discover structure**: Use find_files (glob) to understand project layout and find relevant files
|
|
1230
|
-
- **Find patterns**: Use search_content (grep) to locate existing implementations, imports, and conventions
|
|
1231
|
-
- **Understand deeply**: Use view with view_range to read specific sections of key files
|
|
1232
|
-
- **Parallelize**: Make multiple independent tool calls when exploring different areas
|
|
1233
|
-
|
|
1234
|
-
## Efficiency
|
|
1235
|
-
Your output returns to the parent agent. Be concise:
|
|
1236
|
-
- Don't include raw file contents \u2014 reference by path and line number
|
|
1237
|
-
- Focus on actionable details, not general observations
|
|
1238
|
-
- If you find many similar patterns, describe the pattern once with examples
|
|
1239
|
-
|
|
1240
|
-
## Output Format
|
|
1241
|
-
Structure your plan as:
|
|
1242
|
-
|
|
1243
|
-
. **Summary**: One-paragraph overview (2-3 sentences)
|
|
1244
|
-
. **Files to Change**: List each file with specific changes needed
|
|
1245
|
-
. **Implementation Order**: Numbered steps in dependency order
|
|
1246
|
-
. **Risks**: Potential issues or edge cases (if any)
|
|
8
|
+
import { promisify } from 'util';
|
|
9
|
+
import { createTool } from '@mastra/core/tools';
|
|
10
|
+
import { z } from 'zod';
|
|
11
|
+
import { Tiktoken } from 'js-tiktoken/lite';
|
|
12
|
+
import o200k_base from 'js-tiktoken/ranks/o200k_base';
|
|
13
|
+
import { execa, ExecaError } from 'execa';
|
|
14
|
+
import stripAnsi from 'strip-ansi';
|
|
15
|
+
import treeKill from 'tree-kill';
|
|
16
|
+
import { StreamMessageReader, StreamMessageWriter, createMessageConnection } from 'vscode-jsonrpc/node.js';
|
|
17
|
+
import { Position, TextDocumentIdentifier } from 'vscode-languageserver-protocol';
|
|
18
|
+
import { createRequire } from 'module';
|
|
19
|
+
import { pathToFileURL } from 'url';
|
|
20
|
+
import { distance } from 'fastest-levenshtein';
|
|
21
|
+
import { createAnthropic } from '@ai-sdk/anthropic';
|
|
22
|
+
import { wrapLanguageModel } from 'ai';
|
|
23
|
+
import { createOpenAI } from '@ai-sdk/openai';
|
|
24
|
+
import { ModelRouterLanguageModel } from '@mastra/core/llm';
|
|
25
|
+
import { tavily } from '@tavily/core';
|
|
26
|
+
import { parse, Lang } from '@ast-grep/napi';
|
|
27
|
+
import chalk from 'chalk';
|
|
1247
28
|
|
|
1248
|
-
Be specific about code locations (file paths, function names, line numbers). Keep the plan actionable and under 500 words.`,
|
|
1249
|
-
allowedTools: ["view", "search_content", "find_files"]
|
|
1250
|
-
};
|
|
1251
29
|
var enc = new Tiktoken(o200k_base);
|
|
1252
30
|
function sanitizeInput(text) {
|
|
1253
31
|
if (!text) return "";
|
|
@@ -1456,7 +234,7 @@ Usage notes:
|
|
|
1456
234
|
assertPathAllowed(absolutePath, root, allowedPaths);
|
|
1457
235
|
await validatePath2("view", absolutePath);
|
|
1458
236
|
if (await isDirectory(absolutePath)) {
|
|
1459
|
-
const { stdout, stderr } = await execAsync(`find "${absolutePath}" -maxdepth 2 -not -path '
|
|
237
|
+
const { stdout, stderr } = await execAsync(`find "${absolutePath}" -maxdepth 2 -not -path '*/.*'`);
|
|
1460
238
|
if (stderr) {
|
|
1461
239
|
throw new Error(stderr);
|
|
1462
240
|
}
|
|
@@ -1964,7 +742,52 @@ ${truncateStringForTokenEstimate(cleanOutput, 1e3)}` : "[User aborted command]"
|
|
|
1964
742
|
}
|
|
1965
743
|
});
|
|
1966
744
|
}
|
|
1967
|
-
createExecuteCommandTool();
|
|
745
|
+
createExecuteCommandTool();
|
|
746
|
+
|
|
747
|
+
// src/lsp/language.ts
|
|
748
|
+
var LANGUAGE_EXTENSIONS = {
|
|
749
|
+
// TypeScript/JavaScript
|
|
750
|
+
".ts": "typescript",
|
|
751
|
+
".tsx": "typescriptreact",
|
|
752
|
+
".js": "javascript",
|
|
753
|
+
".jsx": "javascriptreact",
|
|
754
|
+
".mjs": "javascript",
|
|
755
|
+
".cjs": "javascript",
|
|
756
|
+
// Python
|
|
757
|
+
".py": "python",
|
|
758
|
+
".pyi": "python",
|
|
759
|
+
// Go
|
|
760
|
+
".go": "go",
|
|
761
|
+
// Rust
|
|
762
|
+
".rs": "rust",
|
|
763
|
+
// C/C++
|
|
764
|
+
".c": "c",
|
|
765
|
+
".cpp": "cpp",
|
|
766
|
+
".cc": "cpp",
|
|
767
|
+
".cxx": "cpp",
|
|
768
|
+
".h": "c",
|
|
769
|
+
".hpp": "cpp",
|
|
770
|
+
// Java
|
|
771
|
+
".java": "java",
|
|
772
|
+
// JSON
|
|
773
|
+
".json": "json",
|
|
774
|
+
".jsonc": "jsonc",
|
|
775
|
+
// YAML
|
|
776
|
+
".yaml": "yaml",
|
|
777
|
+
".yml": "yaml",
|
|
778
|
+
// Markdown
|
|
779
|
+
".md": "markdown",
|
|
780
|
+
// HTML/CSS
|
|
781
|
+
".html": "html",
|
|
782
|
+
".css": "css",
|
|
783
|
+
".scss": "scss",
|
|
784
|
+
".sass": "sass",
|
|
785
|
+
".less": "less"
|
|
786
|
+
};
|
|
787
|
+
function getLanguageId(filePath) {
|
|
788
|
+
const ext = filePath.substring(filePath.lastIndexOf("."));
|
|
789
|
+
return LANGUAGE_EXTENSIONS[ext];
|
|
790
|
+
}
|
|
1968
791
|
var LSPClient = class {
|
|
1969
792
|
connection = null;
|
|
1970
793
|
process = null;
|
|
@@ -2263,53 +1086,6 @@ var LSPClient = class {
|
|
|
2263
1086
|
this.diagnostics = /* @__PURE__ */ new Map();
|
|
2264
1087
|
}
|
|
2265
1088
|
};
|
|
2266
|
-
|
|
2267
|
-
// src/lsp/language.ts
|
|
2268
|
-
var LANGUAGE_EXTENSIONS = {
|
|
2269
|
-
// TypeScript/JavaScript
|
|
2270
|
-
".ts": "typescript",
|
|
2271
|
-
".tsx": "typescriptreact",
|
|
2272
|
-
".js": "javascript",
|
|
2273
|
-
".jsx": "javascriptreact",
|
|
2274
|
-
".mjs": "javascript",
|
|
2275
|
-
".cjs": "javascript",
|
|
2276
|
-
// Python
|
|
2277
|
-
".py": "python",
|
|
2278
|
-
".pyi": "python",
|
|
2279
|
-
// Go
|
|
2280
|
-
".go": "go",
|
|
2281
|
-
// Rust
|
|
2282
|
-
".rs": "rust",
|
|
2283
|
-
// C/C++
|
|
2284
|
-
".c": "c",
|
|
2285
|
-
".cpp": "cpp",
|
|
2286
|
-
".cc": "cpp",
|
|
2287
|
-
".cxx": "cpp",
|
|
2288
|
-
".h": "c",
|
|
2289
|
-
".hpp": "cpp",
|
|
2290
|
-
// Java
|
|
2291
|
-
".java": "java",
|
|
2292
|
-
// JSON
|
|
2293
|
-
".json": "json",
|
|
2294
|
-
".jsonc": "jsonc",
|
|
2295
|
-
// YAML
|
|
2296
|
-
".yaml": "yaml",
|
|
2297
|
-
".yml": "yaml",
|
|
2298
|
-
// Markdown
|
|
2299
|
-
".md": "markdown",
|
|
2300
|
-
// HTML/CSS
|
|
2301
|
-
".html": "html",
|
|
2302
|
-
".css": "css",
|
|
2303
|
-
".scss": "scss",
|
|
2304
|
-
".sass": "sass",
|
|
2305
|
-
".less": "less"
|
|
2306
|
-
};
|
|
2307
|
-
function getLanguageId(filePath) {
|
|
2308
|
-
const ext = filePath.substring(filePath.lastIndexOf("."));
|
|
2309
|
-
return LANGUAGE_EXTENSIONS[ext];
|
|
2310
|
-
}
|
|
2311
|
-
|
|
2312
|
-
// src/lsp/server.ts
|
|
2313
1089
|
function findNearestRoot(cwd, markers) {
|
|
2314
1090
|
let current = cwd;
|
|
2315
1091
|
while (current !== "/") {
|
|
@@ -2534,8 +1310,8 @@ function findWorkspaceRoot(filePath) {
|
|
|
2534
1310
|
let closestProjectRoot = null;
|
|
2535
1311
|
let searchDir = currentDir;
|
|
2536
1312
|
while (searchDir !== root) {
|
|
2537
|
-
const hasTsConfig =
|
|
2538
|
-
const hasPackageJson =
|
|
1313
|
+
const hasTsConfig = fs8.existsSync(path.join(searchDir, "tsconfig.json"));
|
|
1314
|
+
const hasPackageJson = fs8.existsSync(path.join(searchDir, "package.json"));
|
|
2539
1315
|
if (hasTsConfig || hasPackageJson) {
|
|
2540
1316
|
closestProjectRoot = searchDir;
|
|
2541
1317
|
break;
|
|
@@ -2553,7 +1329,7 @@ function findWorkspaceRoot(filePath) {
|
|
|
2553
1329
|
while (currentDir !== root) {
|
|
2554
1330
|
for (const marker of WORKSPACE_MARKERS) {
|
|
2555
1331
|
const markerPath = path.join(currentDir, marker);
|
|
2556
|
-
if (
|
|
1332
|
+
if (fs8.existsSync(markerPath)) {
|
|
2557
1333
|
return currentDir;
|
|
2558
1334
|
}
|
|
2559
1335
|
}
|
|
@@ -2581,7 +1357,7 @@ var FileEditor = class {
|
|
|
2581
1357
|
async view(args) {
|
|
2582
1358
|
await validatePath("view", args.path);
|
|
2583
1359
|
if (await this.isDirectory(args.path)) {
|
|
2584
|
-
const { stdout, stderr } = await this.execAsync(`find "${args.path}" -maxdepth 2 -not -path '
|
|
1360
|
+
const { stdout, stderr } = await this.execAsync(`find "${args.path}" -maxdepth 2 -not -path '*/.*'`);
|
|
2585
1361
|
if (stderr) return stderr;
|
|
2586
1362
|
return `Here's the files and directories up to 2 levels deep in ${args.path}, excluding hidden items:
|
|
2587
1363
|
${stdout}
|
|
@@ -2940,8 +1716,8 @@ Usage notes:
|
|
|
2940
1716
|
const workspaceRoot = findWorkspaceRoot(absoluteFilePath);
|
|
2941
1717
|
const client = await lspManager.getClient(absoluteFilePath, workspaceRoot);
|
|
2942
1718
|
if (client) {
|
|
2943
|
-
const contentNew =
|
|
2944
|
-
const languageId = path.extname(absoluteFilePath).slice(1);
|
|
1719
|
+
const contentNew = fs8.readFileSync(absoluteFilePath, "utf-8");
|
|
1720
|
+
const languageId = getLanguageId(absoluteFilePath) || path.extname(absoluteFilePath).slice(1);
|
|
2945
1721
|
client.notifyOpen(absoluteFilePath, contentNew, languageId);
|
|
2946
1722
|
client.notifyChange(absoluteFilePath, contentNew, 1);
|
|
2947
1723
|
const diagnostics = await client.waitForDiagnostics(absoluteFilePath, 3e3).catch(() => []);
|
|
@@ -3250,7 +2026,7 @@ function escapeRegex(s) {
|
|
|
3250
2026
|
}
|
|
3251
2027
|
async function getModTime(filePath) {
|
|
3252
2028
|
try {
|
|
3253
|
-
const stat = await
|
|
2029
|
+
const stat = await fs8.promises.stat(filePath);
|
|
3254
2030
|
return stat.mtimeMs;
|
|
3255
2031
|
} catch {
|
|
3256
2032
|
return 0;
|
|
@@ -3338,12 +2114,12 @@ Usage notes:
|
|
|
3338
2114
|
const absolutePath = path.resolve(root, filePath);
|
|
3339
2115
|
const allowedPaths = getAllowedPathsFromContext(toolContext);
|
|
3340
2116
|
assertPathAllowed(absolutePath, root, allowedPaths);
|
|
3341
|
-
const exists =
|
|
2117
|
+
const exists = fs8.existsSync(absolutePath);
|
|
3342
2118
|
const dir = path.dirname(absolutePath);
|
|
3343
|
-
if (!
|
|
3344
|
-
|
|
2119
|
+
if (!fs8.existsSync(dir)) {
|
|
2120
|
+
fs8.mkdirSync(dir, { recursive: true });
|
|
3345
2121
|
}
|
|
3346
|
-
|
|
2122
|
+
fs8.writeFileSync(absolutePath, context.content, "utf-8");
|
|
3347
2123
|
const lineCount = context.content.split("\n").length;
|
|
3348
2124
|
const relPath = path.relative(root, absolutePath);
|
|
3349
2125
|
if (exists) {
|
|
@@ -3357,16 +2133,384 @@ Usage notes:
|
|
|
3357
2133
|
isError: false
|
|
3358
2134
|
};
|
|
3359
2135
|
}
|
|
3360
|
-
} catch (error) {
|
|
3361
|
-
const msg = error instanceof Error ? error.message : "Unknown error";
|
|
3362
|
-
return {
|
|
3363
|
-
content: `write_file failed: ${msg}`,
|
|
3364
|
-
isError: true
|
|
3365
|
-
};
|
|
2136
|
+
} catch (error) {
|
|
2137
|
+
const msg = error instanceof Error ? error.message : "Unknown error";
|
|
2138
|
+
return {
|
|
2139
|
+
content: `write_file failed: ${msg}`,
|
|
2140
|
+
isError: true
|
|
2141
|
+
};
|
|
2142
|
+
}
|
|
2143
|
+
}
|
|
2144
|
+
});
|
|
2145
|
+
}
|
|
2146
|
+
var claudeCodeIdentity = "You are Claude Code, Anthropic's official CLI for Claude.";
|
|
2147
|
+
var authStorageInstance = null;
|
|
2148
|
+
function getAuthStorage() {
|
|
2149
|
+
if (!authStorageInstance) {
|
|
2150
|
+
authStorageInstance = new AuthStorage();
|
|
2151
|
+
}
|
|
2152
|
+
return authStorageInstance;
|
|
2153
|
+
}
|
|
2154
|
+
function setAuthStorage(storage) {
|
|
2155
|
+
authStorageInstance = storage;
|
|
2156
|
+
}
|
|
2157
|
+
var claudeCodeMiddleware = {
|
|
2158
|
+
specificationVersion: "v3",
|
|
2159
|
+
transformParams: async ({ params }) => {
|
|
2160
|
+
const systemMessage = {
|
|
2161
|
+
role: "system",
|
|
2162
|
+
content: claudeCodeIdentity
|
|
2163
|
+
};
|
|
2164
|
+
if (params.temperature) {
|
|
2165
|
+
delete params.topP;
|
|
2166
|
+
}
|
|
2167
|
+
return {
|
|
2168
|
+
...params,
|
|
2169
|
+
prompt: [systemMessage, ...params.prompt]
|
|
2170
|
+
};
|
|
2171
|
+
}
|
|
2172
|
+
};
|
|
2173
|
+
var promptCacheMiddleware = {
|
|
2174
|
+
specificationVersion: "v3",
|
|
2175
|
+
transformParams: async ({ params }) => {
|
|
2176
|
+
const prompt = [...params.prompt];
|
|
2177
|
+
const cacheControl = { type: "ephemeral", ttl: "5m" };
|
|
2178
|
+
const addCacheToMessage = (msg) => {
|
|
2179
|
+
if (typeof msg.content === "string") {
|
|
2180
|
+
return {
|
|
2181
|
+
...msg,
|
|
2182
|
+
providerOptions: {
|
|
2183
|
+
...msg.providerOptions,
|
|
2184
|
+
anthropic: { ...msg.providerOptions?.anthropic, cacheControl }
|
|
2185
|
+
}
|
|
2186
|
+
};
|
|
2187
|
+
}
|
|
2188
|
+
if (Array.isArray(msg.content) && msg.content.length > 0) {
|
|
2189
|
+
const content = [...msg.content];
|
|
2190
|
+
const lastPart = content[content.length - 1];
|
|
2191
|
+
content[content.length - 1] = {
|
|
2192
|
+
...lastPart,
|
|
2193
|
+
providerOptions: {
|
|
2194
|
+
...lastPart.providerOptions,
|
|
2195
|
+
anthropic: { ...lastPart.providerOptions?.anthropic, cacheControl }
|
|
2196
|
+
}
|
|
2197
|
+
};
|
|
2198
|
+
return { ...msg, content };
|
|
2199
|
+
}
|
|
2200
|
+
return msg;
|
|
2201
|
+
};
|
|
2202
|
+
let lastSystemIdx = -1;
|
|
2203
|
+
for (let i = prompt.length - 1; i >= 0; i--) {
|
|
2204
|
+
if (prompt[i].role === "system") {
|
|
2205
|
+
lastSystemIdx = i;
|
|
2206
|
+
break;
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
if (lastSystemIdx >= 0) {
|
|
2210
|
+
prompt[lastSystemIdx] = addCacheToMessage(prompt[lastSystemIdx]);
|
|
2211
|
+
}
|
|
2212
|
+
const lastIdx = prompt.length - 1;
|
|
2213
|
+
if (lastIdx >= 0 && lastIdx !== lastSystemIdx) {
|
|
2214
|
+
prompt[lastIdx] = addCacheToMessage(prompt[lastIdx]);
|
|
2215
|
+
}
|
|
2216
|
+
return { ...params, prompt };
|
|
2217
|
+
}
|
|
2218
|
+
};
|
|
2219
|
+
function opencodeClaudeMaxProvider(modelId = "claude-sonnet-4-20250514") {
|
|
2220
|
+
if (process.env.NODE_ENV === "test" || process.env.VITEST) {
|
|
2221
|
+
const anthropic2 = createAnthropic({
|
|
2222
|
+
apiKey: process.env.ANTHROPIC_API_KEY || "test-api-key"
|
|
2223
|
+
});
|
|
2224
|
+
return wrapLanguageModel({
|
|
2225
|
+
model: anthropic2(modelId),
|
|
2226
|
+
middleware: [claudeCodeMiddleware, promptCacheMiddleware]
|
|
2227
|
+
});
|
|
2228
|
+
}
|
|
2229
|
+
const oauthFetch = async (url, init) => {
|
|
2230
|
+
const authStorage2 = getAuthStorage();
|
|
2231
|
+
authStorage2.reload();
|
|
2232
|
+
const accessToken = await authStorage2.getApiKey("anthropic");
|
|
2233
|
+
if (!accessToken) {
|
|
2234
|
+
throw new Error("Not logged in to Anthropic. Run /login first.");
|
|
2235
|
+
}
|
|
2236
|
+
return fetch(url, {
|
|
2237
|
+
...init,
|
|
2238
|
+
headers: {
|
|
2239
|
+
Authorization: `Bearer ${accessToken}`,
|
|
2240
|
+
"anthropic-beta": "oauth-2025-04-20,claude-code-20250219,interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14",
|
|
2241
|
+
"anthropic-version": "2023-06-01"
|
|
2242
|
+
}
|
|
2243
|
+
});
|
|
2244
|
+
};
|
|
2245
|
+
const anthropic = createAnthropic({
|
|
2246
|
+
// Provide a dummy API key - the actual auth is handled via OAuth in oauthFetch
|
|
2247
|
+
// This prevents the SDK from throwing "API key is missing" at model creation time
|
|
2248
|
+
apiKey: "oauth-placeholder",
|
|
2249
|
+
fetch: oauthFetch
|
|
2250
|
+
});
|
|
2251
|
+
return wrapLanguageModel({
|
|
2252
|
+
model: anthropic(modelId),
|
|
2253
|
+
middleware: [claudeCodeMiddleware, promptCacheMiddleware]
|
|
2254
|
+
});
|
|
2255
|
+
}
|
|
2256
|
+
var CODEX_API_ENDPOINT = "https://chatgpt.com/backend-api/codex/responses";
|
|
2257
|
+
var authStorageInstance2 = null;
|
|
2258
|
+
function getAuthStorage2() {
|
|
2259
|
+
if (!authStorageInstance2) {
|
|
2260
|
+
authStorageInstance2 = new AuthStorage();
|
|
2261
|
+
}
|
|
2262
|
+
return authStorageInstance2;
|
|
2263
|
+
}
|
|
2264
|
+
function setAuthStorage2(storage) {
|
|
2265
|
+
authStorageInstance2 = storage;
|
|
2266
|
+
}
|
|
2267
|
+
var CODEX_INSTRUCTIONS = `You are an interactive CLI tool that helps users with software engineering tasks. Use the instructions below and the tools available to you to assist the user.
|
|
2268
|
+
|
|
2269
|
+
IMPORTANT: You should be concise, direct, and helpful. Focus on solving the user's problem efficiently.`;
|
|
2270
|
+
var openaiCodexMiddleware = {
|
|
2271
|
+
specificationVersion: "v3",
|
|
2272
|
+
transformParams: async ({ params }) => {
|
|
2273
|
+
if (params.temperature) {
|
|
2274
|
+
delete params.topP;
|
|
2275
|
+
}
|
|
2276
|
+
params.providerOptions = {
|
|
2277
|
+
...params.providerOptions,
|
|
2278
|
+
openai: {
|
|
2279
|
+
...params.providerOptions?.openai ?? {},
|
|
2280
|
+
// Codex API requires instructions
|
|
2281
|
+
instructions: CODEX_INSTRUCTIONS,
|
|
2282
|
+
// Codex API requires store to be false
|
|
2283
|
+
store: false
|
|
2284
|
+
}
|
|
2285
|
+
};
|
|
2286
|
+
return params;
|
|
2287
|
+
}
|
|
2288
|
+
};
|
|
2289
|
+
function openaiCodexProvider(modelId = "codex-mini-latest") {
|
|
2290
|
+
if (process.env.NODE_ENV === "test" || process.env.VITEST) {
|
|
2291
|
+
const openai2 = createOpenAI({
|
|
2292
|
+
apiKey: process.env.OPENAI_API_KEY || "test-api-key"
|
|
2293
|
+
});
|
|
2294
|
+
return wrapLanguageModel({
|
|
2295
|
+
model: openai2.responses(modelId),
|
|
2296
|
+
middleware: [openaiCodexMiddleware]
|
|
2297
|
+
});
|
|
2298
|
+
}
|
|
2299
|
+
const oauthFetch = async (url, init) => {
|
|
2300
|
+
const authStorage2 = getAuthStorage2();
|
|
2301
|
+
authStorage2.reload();
|
|
2302
|
+
const cred = authStorage2.get("openai-codex");
|
|
2303
|
+
if (!cred || cred.type !== "oauth") {
|
|
2304
|
+
throw new Error("Not logged in to OpenAI Codex. Run /login first.");
|
|
2305
|
+
}
|
|
2306
|
+
let accessToken = cred.access;
|
|
2307
|
+
if (Date.now() >= cred.expires) {
|
|
2308
|
+
const refreshedToken = await authStorage2.getApiKey("openai-codex");
|
|
2309
|
+
if (!refreshedToken) {
|
|
2310
|
+
throw new Error("Failed to refresh OpenAI Codex token. Please /login again.");
|
|
2311
|
+
}
|
|
2312
|
+
accessToken = refreshedToken;
|
|
2313
|
+
authStorage2.reload();
|
|
2314
|
+
}
|
|
2315
|
+
const accountId = cred.accountId;
|
|
2316
|
+
const headers = new Headers();
|
|
2317
|
+
if (init?.headers) {
|
|
2318
|
+
if (init.headers instanceof Headers) {
|
|
2319
|
+
init.headers.forEach((value, key) => {
|
|
2320
|
+
if (key.toLowerCase() !== "authorization") {
|
|
2321
|
+
headers.set(key, value);
|
|
2322
|
+
}
|
|
2323
|
+
});
|
|
2324
|
+
} else if (Array.isArray(init.headers)) {
|
|
2325
|
+
for (const [key, value] of init.headers) {
|
|
2326
|
+
if (key.toLowerCase() !== "authorization" && value !== void 0) {
|
|
2327
|
+
headers.set(key, String(value));
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2330
|
+
} else {
|
|
2331
|
+
for (const [key, value] of Object.entries(init.headers)) {
|
|
2332
|
+
if (key.toLowerCase() !== "authorization" && value !== void 0) {
|
|
2333
|
+
headers.set(key, String(value));
|
|
2334
|
+
}
|
|
2335
|
+
}
|
|
3366
2336
|
}
|
|
3367
2337
|
}
|
|
2338
|
+
headers.set("Authorization", `Bearer ${accessToken}`);
|
|
2339
|
+
if (accountId) {
|
|
2340
|
+
headers.set("ChatGPT-Account-Id", accountId);
|
|
2341
|
+
}
|
|
2342
|
+
const parsed = url instanceof URL ? url : new URL(typeof url === "string" ? url : url.url);
|
|
2343
|
+
const shouldRewrite = parsed.pathname.includes("/v1/responses") || parsed.pathname.includes("/chat/completions");
|
|
2344
|
+
const finalUrl = shouldRewrite ? new URL(CODEX_API_ENDPOINT) : parsed;
|
|
2345
|
+
return fetch(finalUrl, {
|
|
2346
|
+
...init,
|
|
2347
|
+
headers
|
|
2348
|
+
});
|
|
2349
|
+
};
|
|
2350
|
+
const openai = createOpenAI({
|
|
2351
|
+
// Use a dummy API key since we're using OAuth
|
|
2352
|
+
apiKey: "oauth-dummy-key",
|
|
2353
|
+
fetch: oauthFetch
|
|
2354
|
+
});
|
|
2355
|
+
return wrapLanguageModel({
|
|
2356
|
+
model: openai.responses(modelId),
|
|
2357
|
+
middleware: [openaiCodexMiddleware]
|
|
3368
2358
|
});
|
|
3369
2359
|
}
|
|
2360
|
+
var authStorage = new AuthStorage();
|
|
2361
|
+
function resolveModel(modelId) {
|
|
2362
|
+
authStorage.reload();
|
|
2363
|
+
const isAnthropicModel = modelId.startsWith("anthropic/");
|
|
2364
|
+
const isOpenAIModel = modelId.startsWith("openai/");
|
|
2365
|
+
const isMoonshotModel = modelId.startsWith("moonshotai/");
|
|
2366
|
+
if (isMoonshotModel) {
|
|
2367
|
+
if (!process.env.MOONSHOT_AI_API_KEY) {
|
|
2368
|
+
throw new Error(`Need MOONSHOT_AI_API_KEY`);
|
|
2369
|
+
}
|
|
2370
|
+
return createAnthropic({
|
|
2371
|
+
apiKey: process.env.MOONSHOT_AI_API_KEY,
|
|
2372
|
+
baseURL: "https://api.moonshot.ai/anthropic/v1",
|
|
2373
|
+
name: "moonshotai.anthropicv1"
|
|
2374
|
+
})(modelId.substring("moonshotai/".length));
|
|
2375
|
+
} else if (isAnthropicModel) {
|
|
2376
|
+
return opencodeClaudeMaxProvider(modelId.substring(`anthropic/`.length));
|
|
2377
|
+
} else if (isOpenAIModel && authStorage.isLoggedIn("openai-codex")) {
|
|
2378
|
+
return openaiCodexProvider(modelId.substring(`openai/`.length));
|
|
2379
|
+
} else {
|
|
2380
|
+
return new ModelRouterLanguageModel(modelId);
|
|
2381
|
+
}
|
|
2382
|
+
}
|
|
2383
|
+
function getDynamicModel({
|
|
2384
|
+
requestContext
|
|
2385
|
+
}) {
|
|
2386
|
+
const harnessContext = requestContext.get("harness");
|
|
2387
|
+
const modelId = harnessContext?.state?.currentModelId;
|
|
2388
|
+
if (!modelId) {
|
|
2389
|
+
throw new Error("No model selected. Use /models to select a model first.");
|
|
2390
|
+
}
|
|
2391
|
+
return resolveModel(modelId);
|
|
2392
|
+
}
|
|
2393
|
+
|
|
2394
|
+
// src/agents/subagents/execute.ts
|
|
2395
|
+
var executeSubagent = {
|
|
2396
|
+
id: "execute",
|
|
2397
|
+
name: "Execute",
|
|
2398
|
+
instructions: `You are a focused execution agent. Your job is to complete a specific, well-defined task by making the necessary changes to the codebase.
|
|
2399
|
+
|
|
2400
|
+
## Rules
|
|
2401
|
+
- You have FULL ACCESS to read, write, and execute within your task scope.
|
|
2402
|
+
- Stay focused on the specific task given. Do not make unrelated changes.
|
|
2403
|
+
- Read files before modifying them \u2014 use view first, then string_replace_lsp or write_file.
|
|
2404
|
+
- Verify your changes work by running relevant tests or checking for errors.
|
|
2405
|
+
|
|
2406
|
+
## Tool Strategy
|
|
2407
|
+
- **Read first**: Always view a file before editing it
|
|
2408
|
+
- **Edit precisely**: Use string_replace_lsp with enough context to match uniquely
|
|
2409
|
+
- **Use specialized tools**: Prefer view/search_content/find_files over shell commands for reading
|
|
2410
|
+
- **Parallelize**: Make independent tool calls together (e.g., view multiple files at once)
|
|
2411
|
+
|
|
2412
|
+
## Workflow
|
|
2413
|
+
. Understand the task and explore relevant code
|
|
2414
|
+
. For complex tasks (3+ steps): use task_write to track progress
|
|
2415
|
+
. Make changes incrementally \u2014 verify each change before moving on
|
|
2416
|
+
. Run tests or type-check to verify
|
|
2417
|
+
. If you created tasks: ALWAYS call task_check before finishing
|
|
2418
|
+
|
|
2419
|
+
## Efficiency
|
|
2420
|
+
Your output returns to the parent agent. Be concise:
|
|
2421
|
+
- Don't repeat file contents in your response
|
|
2422
|
+
- Summarize what changed, don't narrate each step
|
|
2423
|
+
- Keep your final summary under 300 words
|
|
2424
|
+
|
|
2425
|
+
## Output Format
|
|
2426
|
+
End with a structured summary:
|
|
2427
|
+
. **Completed**: What you implemented (1-2 sentences)
|
|
2428
|
+
. **Changes**: Files modified/created
|
|
2429
|
+
. **Verification**: How you verified it works
|
|
2430
|
+
. **Notes**: Follow-up needed (if any)`,
|
|
2431
|
+
allowedTools: [
|
|
2432
|
+
// Read tools
|
|
2433
|
+
"view",
|
|
2434
|
+
"search_content",
|
|
2435
|
+
"find_files",
|
|
2436
|
+
// Write tools
|
|
2437
|
+
"string_replace_lsp",
|
|
2438
|
+
"write_file",
|
|
2439
|
+
// Execution tool
|
|
2440
|
+
"execute_command",
|
|
2441
|
+
// Task tracking (built-in harness tools)
|
|
2442
|
+
"task_write",
|
|
2443
|
+
"task_check"
|
|
2444
|
+
]
|
|
2445
|
+
};
|
|
2446
|
+
|
|
2447
|
+
// src/agents/subagents/explore.ts
|
|
2448
|
+
var exploreSubagent = {
|
|
2449
|
+
id: "explore",
|
|
2450
|
+
name: "Explore",
|
|
2451
|
+
instructions: `You are an expert code explorer. Your job is to investigate a codebase and answer a specific question or gather specific information.
|
|
2452
|
+
|
|
2453
|
+
## Rules
|
|
2454
|
+
- You have READ-ONLY access. You cannot modify files or run commands.
|
|
2455
|
+
- Be thorough \u2014 search broadly first, then drill into relevant files.
|
|
2456
|
+
- After gathering enough information, produce a clear, concise summary of your findings.
|
|
2457
|
+
|
|
2458
|
+
## Tool Strategy
|
|
2459
|
+
- **Start broad**: Use find_files (glob) to understand project structure
|
|
2460
|
+
- **Search smart**: Use search_content (grep) with specific patterns \u2014 avoid overly broad searches
|
|
2461
|
+
- **Read efficiently**: Use view with view_range for large files \u2014 don't read entire files if you only need a section
|
|
2462
|
+
- **Parallelize**: Make multiple independent tool calls in one round when exploring different areas
|
|
2463
|
+
|
|
2464
|
+
## Efficiency
|
|
2465
|
+
Your output returns to the parent agent. Be concise:
|
|
2466
|
+
- Don't include raw file contents in your response \u2014 summarize what you found
|
|
2467
|
+
- Reference files by path and line number, not by copying code
|
|
2468
|
+
- If a search returns many results, report the count and key examples, not every match
|
|
2469
|
+
|
|
2470
|
+
## Output Format
|
|
2471
|
+
End with a structured summary:
|
|
2472
|
+
. **Answer**: Direct answer to the question (1-2 sentences)
|
|
2473
|
+
. **Key Files**: Most relevant files with line numbers
|
|
2474
|
+
. **Details**: Additional context if needed
|
|
2475
|
+
|
|
2476
|
+
Keep your summary under 300 words.`,
|
|
2477
|
+
allowedTools: ["view", "search_content", "find_files"]
|
|
2478
|
+
};
|
|
2479
|
+
|
|
2480
|
+
// src/agents/subagents/plan.ts
|
|
2481
|
+
var planSubagent = {
|
|
2482
|
+
id: "plan",
|
|
2483
|
+
name: "Plan",
|
|
2484
|
+
instructions: `You are an expert software architect and planner. Your job is to analyze a codebase and produce a detailed implementation plan for a given task.
|
|
2485
|
+
|
|
2486
|
+
## Rules
|
|
2487
|
+
- You have READ-ONLY access. You cannot modify files or run commands.
|
|
2488
|
+
- First, explore the codebase to understand existing patterns, architecture, and conventions.
|
|
2489
|
+
- Produce a concrete, actionable plan \u2014 not vague suggestions.
|
|
2490
|
+
|
|
2491
|
+
## Tool Strategy
|
|
2492
|
+
- **Discover structure**: Use find_files (glob) to understand project layout and find relevant files
|
|
2493
|
+
- **Find patterns**: Use search_content (grep) to locate existing implementations, imports, and conventions
|
|
2494
|
+
- **Understand deeply**: Use view with view_range to read specific sections of key files
|
|
2495
|
+
- **Parallelize**: Make multiple independent tool calls when exploring different areas
|
|
2496
|
+
|
|
2497
|
+
## Efficiency
|
|
2498
|
+
Your output returns to the parent agent. Be concise:
|
|
2499
|
+
- Don't include raw file contents \u2014 reference by path and line number
|
|
2500
|
+
- Focus on actionable details, not general observations
|
|
2501
|
+
- If you find many similar patterns, describe the pattern once with examples
|
|
2502
|
+
|
|
2503
|
+
## Output Format
|
|
2504
|
+
Structure your plan as:
|
|
2505
|
+
|
|
2506
|
+
. **Summary**: One-paragraph overview (2-3 sentences)
|
|
2507
|
+
. **Files to Change**: List each file with specific changes needed
|
|
2508
|
+
. **Implementation Order**: Numbered steps in dependency order
|
|
2509
|
+
. **Risks**: Potential issues or edge cases (if any)
|
|
2510
|
+
|
|
2511
|
+
Be specific about code locations (file paths, function names, line numbers). Keep the plan actionable and under 500 words.`,
|
|
2512
|
+
allowedTools: ["view", "search_content", "find_files"]
|
|
2513
|
+
};
|
|
3370
2514
|
var MAX_WEB_SEARCH_TOKENS = 2e3;
|
|
3371
2515
|
var MAX_WEB_EXTRACT_TOKENS = 2e3;
|
|
3372
2516
|
var MIN_RELEVANCE_SCORE = 0.25;
|
|
@@ -3508,14 +2652,14 @@ Examples:
|
|
|
3508
2652
|
- Pattern replace: { pattern: 'console.log($ARG)', replacement: 'logger.debug($ARG)' }`,
|
|
3509
2653
|
// requireApproval: true,
|
|
3510
2654
|
inputSchema: astSmartEditSchema,
|
|
3511
|
-
execute: async ({ path:
|
|
2655
|
+
execute: async ({ path: path12, pattern, replacement, selector, transform, targetName, newName, importSpec }, toolContext) => {
|
|
3512
2656
|
try {
|
|
3513
2657
|
const projectRoot = getProjectRoot();
|
|
3514
|
-
const filePath = resolve(projectRoot,
|
|
2658
|
+
const filePath = resolve(projectRoot, path12);
|
|
3515
2659
|
const allowedPaths = getAllowedPathsFromContext(toolContext);
|
|
3516
2660
|
assertPathAllowed(filePath, projectRoot, allowedPaths);
|
|
3517
2661
|
const content = readFileSync(filePath, "utf-8");
|
|
3518
|
-
const lang = getLanguageFromPath(
|
|
2662
|
+
const lang = getLanguageFromPath(path12);
|
|
3519
2663
|
const ast = parse(lang, content);
|
|
3520
2664
|
const root = ast.root();
|
|
3521
2665
|
let modifiedContent = content;
|
|
@@ -3590,8 +2734,8 @@ Examples:
|
|
|
3590
2734
|
}
|
|
3591
2735
|
}
|
|
3592
2736
|
});
|
|
3593
|
-
function getLanguageFromPath(
|
|
3594
|
-
const ext =
|
|
2737
|
+
function getLanguageFromPath(path12) {
|
|
2738
|
+
const ext = path12.split(".").pop()?.toLowerCase();
|
|
3595
2739
|
switch (ext) {
|
|
3596
2740
|
case "ts":
|
|
3597
2741
|
case "tsx":
|
|
@@ -3853,6 +2997,259 @@ var requestSandboxAccessTool = createTool({
|
|
|
3853
2997
|
}
|
|
3854
2998
|
}
|
|
3855
2999
|
});
|
|
3000
|
+
|
|
3001
|
+
// src/onboarding/packs.ts
|
|
3002
|
+
function getAvailableModePacks(access, savedCustomPacks = []) {
|
|
3003
|
+
const packs = [];
|
|
3004
|
+
const openaiCodex = "openai/gpt-5.2-codex";
|
|
3005
|
+
const anthropicBuild = access.anthropic === "oauth" ? "anthropic/claude-opus-4-6" : "anthropic/claude-sonnet-4-5";
|
|
3006
|
+
if (access.anthropic && access.openai) {
|
|
3007
|
+
packs.push({
|
|
3008
|
+
id: "varied",
|
|
3009
|
+
name: "Varied",
|
|
3010
|
+
description: "Models from multiple providers",
|
|
3011
|
+
models: {
|
|
3012
|
+
build: anthropicBuild,
|
|
3013
|
+
plan: openaiCodex,
|
|
3014
|
+
fast: access.cerebras ? "cerebras/zai-glm-4.7" : "anthropic/claude-haiku-4-5"
|
|
3015
|
+
}
|
|
3016
|
+
});
|
|
3017
|
+
}
|
|
3018
|
+
if (access.anthropic) {
|
|
3019
|
+
packs.push({
|
|
3020
|
+
id: "anthropic",
|
|
3021
|
+
name: "Anthropic",
|
|
3022
|
+
description: access.anthropic === "oauth" ? "All Anthropic models via Max subscription" : "All Anthropic models via API key",
|
|
3023
|
+
models: {
|
|
3024
|
+
build: anthropicBuild,
|
|
3025
|
+
plan: anthropicBuild,
|
|
3026
|
+
fast: "anthropic/claude-haiku-4-5"
|
|
3027
|
+
}
|
|
3028
|
+
});
|
|
3029
|
+
}
|
|
3030
|
+
if (access.openai) {
|
|
3031
|
+
packs.push({
|
|
3032
|
+
id: "openai",
|
|
3033
|
+
name: "OpenAI",
|
|
3034
|
+
description: access.openai === "oauth" ? "All OpenAI models via Codex subscription" : "All OpenAI models via API key",
|
|
3035
|
+
models: {
|
|
3036
|
+
build: openaiCodex,
|
|
3037
|
+
plan: openaiCodex,
|
|
3038
|
+
fast: "openai/gpt-5.1-codex-mini"
|
|
3039
|
+
}
|
|
3040
|
+
});
|
|
3041
|
+
}
|
|
3042
|
+
for (const cp of savedCustomPacks) {
|
|
3043
|
+
packs.push({
|
|
3044
|
+
id: `custom:${cp.name}`,
|
|
3045
|
+
name: cp.name,
|
|
3046
|
+
description: "Saved custom pack",
|
|
3047
|
+
models: {
|
|
3048
|
+
build: cp.models.build ?? "",
|
|
3049
|
+
plan: cp.models.plan ?? "",
|
|
3050
|
+
fast: cp.models.fast ?? ""
|
|
3051
|
+
}
|
|
3052
|
+
});
|
|
3053
|
+
}
|
|
3054
|
+
const hasCustom = savedCustomPacks.length > 0;
|
|
3055
|
+
packs.push({
|
|
3056
|
+
id: "custom",
|
|
3057
|
+
name: hasCustom ? "New Custom" : "Custom",
|
|
3058
|
+
description: "Choose a model for each mode",
|
|
3059
|
+
models: { build: "", plan: "", fast: "" }
|
|
3060
|
+
});
|
|
3061
|
+
return packs;
|
|
3062
|
+
}
|
|
3063
|
+
function getAvailableOmPacks(access) {
|
|
3064
|
+
const packs = [];
|
|
3065
|
+
if (access.google) {
|
|
3066
|
+
packs.push({
|
|
3067
|
+
id: "gemini",
|
|
3068
|
+
name: "Gemini Flash",
|
|
3069
|
+
description: access.google === "oauth" ? "Via Google OAuth" : "Via Google API key",
|
|
3070
|
+
modelId: "google/gemini-2.5-flash"
|
|
3071
|
+
});
|
|
3072
|
+
}
|
|
3073
|
+
if (access.anthropic) {
|
|
3074
|
+
packs.push({
|
|
3075
|
+
id: "anthropic",
|
|
3076
|
+
name: "Claude Haiku",
|
|
3077
|
+
description: access.anthropic === "oauth" ? "Via Max subscription" : "Via Anthropic API key",
|
|
3078
|
+
modelId: "anthropic/claude-haiku-4-5"
|
|
3079
|
+
});
|
|
3080
|
+
}
|
|
3081
|
+
if (access.openai) {
|
|
3082
|
+
packs.push({
|
|
3083
|
+
id: "openai",
|
|
3084
|
+
name: "Codex Mini",
|
|
3085
|
+
description: access.openai === "oauth" ? "Via Codex subscription" : "Via OpenAI API key",
|
|
3086
|
+
modelId: "openai/gpt-5.1-codex-mini"
|
|
3087
|
+
});
|
|
3088
|
+
}
|
|
3089
|
+
if (access.deepseek) {
|
|
3090
|
+
packs.push({
|
|
3091
|
+
id: "deepseek",
|
|
3092
|
+
name: "DeepSeek",
|
|
3093
|
+
description: "Via DeepSeek API key",
|
|
3094
|
+
modelId: "deepseek/deepseek-chat"
|
|
3095
|
+
});
|
|
3096
|
+
}
|
|
3097
|
+
packs.push({
|
|
3098
|
+
id: "custom",
|
|
3099
|
+
name: "Custom",
|
|
3100
|
+
description: "Choose any available model",
|
|
3101
|
+
modelId: ""
|
|
3102
|
+
});
|
|
3103
|
+
return packs;
|
|
3104
|
+
}
|
|
3105
|
+
var ONBOARDING_VERSION = 1;
|
|
3106
|
+
var STORAGE_DEFAULTS = {
|
|
3107
|
+
backend: "libsql",
|
|
3108
|
+
libsql: {},
|
|
3109
|
+
pg: {}
|
|
3110
|
+
};
|
|
3111
|
+
var DEFAULTS = {
|
|
3112
|
+
onboarding: {
|
|
3113
|
+
completedAt: null,
|
|
3114
|
+
skippedAt: null,
|
|
3115
|
+
version: 0,
|
|
3116
|
+
modePackId: null,
|
|
3117
|
+
omPackId: null
|
|
3118
|
+
},
|
|
3119
|
+
models: {
|
|
3120
|
+
activeModelPackId: null,
|
|
3121
|
+
modeDefaults: {},
|
|
3122
|
+
activeOmPackId: null,
|
|
3123
|
+
omModelOverride: null,
|
|
3124
|
+
subagentModels: {}
|
|
3125
|
+
},
|
|
3126
|
+
preferences: {
|
|
3127
|
+
yolo: null
|
|
3128
|
+
},
|
|
3129
|
+
storage: { ...STORAGE_DEFAULTS },
|
|
3130
|
+
customModelPacks: [],
|
|
3131
|
+
modelUseCounts: {}
|
|
3132
|
+
};
|
|
3133
|
+
function getSettingsPath() {
|
|
3134
|
+
return join(getAppDataDir(), "settings.json");
|
|
3135
|
+
}
|
|
3136
|
+
function migrateFromAuth(settingsPath) {
|
|
3137
|
+
const authPath = join(getAppDataDir(), "auth.json");
|
|
3138
|
+
if (!existsSync(authPath)) return false;
|
|
3139
|
+
let authData;
|
|
3140
|
+
try {
|
|
3141
|
+
authData = JSON.parse(readFileSync(authPath, "utf-8"));
|
|
3142
|
+
} catch {
|
|
3143
|
+
return false;
|
|
3144
|
+
}
|
|
3145
|
+
const modelKeys = Object.keys(authData).filter((k) => k.startsWith("_"));
|
|
3146
|
+
if (modelKeys.length === 0) return false;
|
|
3147
|
+
let settings;
|
|
3148
|
+
if (existsSync(settingsPath)) {
|
|
3149
|
+
try {
|
|
3150
|
+
const raw = JSON.parse(readFileSync(settingsPath, "utf-8"));
|
|
3151
|
+
settings = {
|
|
3152
|
+
onboarding: { ...DEFAULTS.onboarding, ...raw.onboarding },
|
|
3153
|
+
models: { ...DEFAULTS.models, ...raw.models },
|
|
3154
|
+
preferences: { ...DEFAULTS.preferences, ...raw.preferences },
|
|
3155
|
+
storage: {
|
|
3156
|
+
...STORAGE_DEFAULTS,
|
|
3157
|
+
...raw.storage,
|
|
3158
|
+
libsql: { ...STORAGE_DEFAULTS.libsql, ...raw.storage?.libsql },
|
|
3159
|
+
pg: { ...STORAGE_DEFAULTS.pg, ...raw.storage?.pg }
|
|
3160
|
+
},
|
|
3161
|
+
customModelPacks: Array.isArray(raw.customModelPacks) ? raw.customModelPacks : [],
|
|
3162
|
+
modelUseCounts: raw.modelUseCounts && typeof raw.modelUseCounts === "object" ? raw.modelUseCounts : {}
|
|
3163
|
+
};
|
|
3164
|
+
} catch {
|
|
3165
|
+
settings = structuredClone(DEFAULTS);
|
|
3166
|
+
}
|
|
3167
|
+
} else {
|
|
3168
|
+
settings = structuredClone(DEFAULTS);
|
|
3169
|
+
}
|
|
3170
|
+
if (authData._modelRanks && typeof authData._modelRanks === "object") {
|
|
3171
|
+
settings.modelUseCounts = { ...authData._modelRanks, ...settings.modelUseCounts };
|
|
3172
|
+
}
|
|
3173
|
+
for (const key of modelKeys) {
|
|
3174
|
+
const modeMatch = key.match(/^_modeModelId_(.+)$/);
|
|
3175
|
+
if (modeMatch?.[1] && typeof authData[key] === "string" && !settings.models.modeDefaults[modeMatch[1]]) {
|
|
3176
|
+
settings.models.modeDefaults[modeMatch[1]] = authData[key];
|
|
3177
|
+
}
|
|
3178
|
+
}
|
|
3179
|
+
for (const key of modelKeys) {
|
|
3180
|
+
if (key === "_subagentModelId" && typeof authData[key] === "string" && !settings.models.subagentModels["default"]) {
|
|
3181
|
+
settings.models.subagentModels["default"] = authData[key];
|
|
3182
|
+
}
|
|
3183
|
+
const saMatch = key.match(/^_subagentModelId_(.+)$/);
|
|
3184
|
+
if (saMatch?.[1] && typeof authData[key] === "string" && !settings.models.subagentModels[saMatch[1]]) {
|
|
3185
|
+
settings.models.subagentModels[saMatch[1]] = authData[key];
|
|
3186
|
+
}
|
|
3187
|
+
}
|
|
3188
|
+
saveSettings(settings, settingsPath);
|
|
3189
|
+
for (const key of modelKeys) {
|
|
3190
|
+
delete authData[key];
|
|
3191
|
+
}
|
|
3192
|
+
try {
|
|
3193
|
+
writeFileSync(authPath, JSON.stringify(authData, null, 2), "utf-8");
|
|
3194
|
+
} catch {
|
|
3195
|
+
}
|
|
3196
|
+
return true;
|
|
3197
|
+
}
|
|
3198
|
+
function loadSettings(filePath = getSettingsPath()) {
|
|
3199
|
+
migrateFromAuth(filePath);
|
|
3200
|
+
if (!existsSync(filePath)) return structuredClone(DEFAULTS);
|
|
3201
|
+
try {
|
|
3202
|
+
const raw = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
3203
|
+
const settings = {
|
|
3204
|
+
onboarding: { ...DEFAULTS.onboarding, ...raw.onboarding },
|
|
3205
|
+
models: { ...DEFAULTS.models, ...raw.models },
|
|
3206
|
+
preferences: { ...DEFAULTS.preferences, ...raw.preferences },
|
|
3207
|
+
storage: {
|
|
3208
|
+
...STORAGE_DEFAULTS,
|
|
3209
|
+
...raw.storage,
|
|
3210
|
+
libsql: { ...STORAGE_DEFAULTS.libsql, ...raw.storage?.libsql },
|
|
3211
|
+
pg: { ...STORAGE_DEFAULTS.pg, ...raw.storage?.pg }
|
|
3212
|
+
},
|
|
3213
|
+
customModelPacks: Array.isArray(raw.customModelPacks) ? raw.customModelPacks : [],
|
|
3214
|
+
modelUseCounts: raw.modelUseCounts && typeof raw.modelUseCounts === "object" ? raw.modelUseCounts : {}
|
|
3215
|
+
};
|
|
3216
|
+
if (raw.models?.omModelId && !settings.models.omModelOverride) {
|
|
3217
|
+
settings.models.omModelOverride = raw.models.omModelId;
|
|
3218
|
+
saveSettings(settings, filePath);
|
|
3219
|
+
}
|
|
3220
|
+
return settings;
|
|
3221
|
+
} catch {
|
|
3222
|
+
return structuredClone(DEFAULTS);
|
|
3223
|
+
}
|
|
3224
|
+
}
|
|
3225
|
+
function resolveModelDefaults(settings, builtinPacks) {
|
|
3226
|
+
const { activeModelPackId, modeDefaults } = settings.models;
|
|
3227
|
+
if (!activeModelPackId) return modeDefaults;
|
|
3228
|
+
if (activeModelPackId.startsWith("custom:")) {
|
|
3229
|
+
const name = activeModelPackId.slice("custom:".length);
|
|
3230
|
+
const pack = settings.customModelPacks.find((p) => p.name === name);
|
|
3231
|
+
if (pack) return pack.models;
|
|
3232
|
+
return modeDefaults;
|
|
3233
|
+
}
|
|
3234
|
+
const builtin = builtinPacks.find((p) => p.id === activeModelPackId);
|
|
3235
|
+
if (builtin) return builtin.models;
|
|
3236
|
+
return modeDefaults;
|
|
3237
|
+
}
|
|
3238
|
+
function resolveOmModel(settings, builtinOmPacks) {
|
|
3239
|
+
const { activeOmPackId, omModelOverride } = settings.models;
|
|
3240
|
+
if (!activeOmPackId) return omModelOverride;
|
|
3241
|
+
if (activeOmPackId === "custom") return omModelOverride;
|
|
3242
|
+
const pack = builtinOmPacks.find((p) => p.id === activeOmPackId);
|
|
3243
|
+
if (pack) return pack.modelId;
|
|
3244
|
+
return omModelOverride;
|
|
3245
|
+
}
|
|
3246
|
+
function saveSettings(settings, filePath = getSettingsPath()) {
|
|
3247
|
+
const dir = dirname(filePath);
|
|
3248
|
+
if (!existsSync(dir)) {
|
|
3249
|
+
mkdirSync(dir, { recursive: true });
|
|
3250
|
+
}
|
|
3251
|
+
writeFileSync(filePath, JSON.stringify(settings, null, 2), "utf-8");
|
|
3252
|
+
}
|
|
3856
3253
|
var mastra = {
|
|
3857
3254
|
purple: "#7f45e0",
|
|
3858
3255
|
// #b588fe brand is too washed out for terminal
|
|
@@ -4034,8 +3431,8 @@ var ThreadLockError = class extends Error {
|
|
|
4034
3431
|
};
|
|
4035
3432
|
function getLocksDir() {
|
|
4036
3433
|
const dir = path.join(getAppDataDir(), "locks");
|
|
4037
|
-
if (!
|
|
4038
|
-
|
|
3434
|
+
if (!fs8.existsSync(dir)) {
|
|
3435
|
+
fs8.mkdirSync(dir, { recursive: true });
|
|
4039
3436
|
}
|
|
4040
3437
|
return dir;
|
|
4041
3438
|
}
|
|
@@ -4054,9 +3451,9 @@ function isProcessAlive(pid) {
|
|
|
4054
3451
|
function acquireThreadLock(threadId) {
|
|
4055
3452
|
const lockPath = getLockPath(threadId);
|
|
4056
3453
|
const myPid = process.pid;
|
|
4057
|
-
if (
|
|
3454
|
+
if (fs8.existsSync(lockPath)) {
|
|
4058
3455
|
try {
|
|
4059
|
-
const content =
|
|
3456
|
+
const content = fs8.readFileSync(lockPath, "utf-8").trim();
|
|
4060
3457
|
const ownerPid = parseInt(content, 10);
|
|
4061
3458
|
if (!isNaN(ownerPid) && ownerPid !== myPid && isProcessAlive(ownerPid)) {
|
|
4062
3459
|
throw new ThreadLockError(threadId, ownerPid);
|
|
@@ -4065,17 +3462,17 @@ function acquireThreadLock(threadId) {
|
|
|
4065
3462
|
if (error instanceof ThreadLockError) throw error;
|
|
4066
3463
|
}
|
|
4067
3464
|
}
|
|
4068
|
-
|
|
3465
|
+
fs8.writeFileSync(lockPath, String(myPid), { mode: 420 });
|
|
4069
3466
|
}
|
|
4070
3467
|
function releaseThreadLock(threadId) {
|
|
4071
3468
|
const lockPath = getLockPath(threadId);
|
|
4072
3469
|
const myPid = process.pid;
|
|
4073
3470
|
try {
|
|
4074
|
-
if (!
|
|
4075
|
-
const content =
|
|
3471
|
+
if (!fs8.existsSync(lockPath)) return;
|
|
3472
|
+
const content = fs8.readFileSync(lockPath, "utf-8").trim();
|
|
4076
3473
|
const ownerPid = parseInt(content, 10);
|
|
4077
3474
|
if (ownerPid === myPid) {
|
|
4078
|
-
|
|
3475
|
+
fs8.unlinkSync(lockPath);
|
|
4079
3476
|
}
|
|
4080
3477
|
} catch {
|
|
4081
3478
|
}
|
|
@@ -4083,15 +3480,15 @@ function releaseThreadLock(threadId) {
|
|
|
4083
3480
|
function releaseAllThreadLocks() {
|
|
4084
3481
|
try {
|
|
4085
3482
|
const locksDir = getLocksDir();
|
|
4086
|
-
const files =
|
|
3483
|
+
const files = fs8.readdirSync(locksDir);
|
|
4087
3484
|
const myPid = String(process.pid);
|
|
4088
3485
|
for (const file of files) {
|
|
4089
3486
|
if (!file.endsWith(".lock")) continue;
|
|
4090
3487
|
const lockPath = path.join(locksDir, file);
|
|
4091
3488
|
try {
|
|
4092
|
-
const content =
|
|
3489
|
+
const content = fs8.readFileSync(lockPath, "utf-8").trim();
|
|
4093
3490
|
if (content === myPid) {
|
|
4094
|
-
|
|
3491
|
+
fs8.unlinkSync(lockPath);
|
|
4095
3492
|
}
|
|
4096
3493
|
} catch {
|
|
4097
3494
|
}
|
|
@@ -4100,6 +3497,6 @@ function releaseAllThreadLocks() {
|
|
|
4100
3497
|
}
|
|
4101
3498
|
}
|
|
4102
3499
|
|
|
4103
|
-
export {
|
|
4104
|
-
//# sourceMappingURL=chunk-
|
|
4105
|
-
//# sourceMappingURL=chunk-
|
|
3500
|
+
export { ONBOARDING_VERSION, ThreadLockError, acquireThreadLock, astSmartEditTool, bg, bold, createExecuteCommandTool, createGlobTool, createGrepTool, createViewTool, createWebExtractTool, createWebSearchTool, createWriteFileTool, executeSubagent, exploreSubagent, fg, getAvailableModePacks, getAvailableOmPacks, getDynamicModel, getEditorTheme, getMarkdownTheme, getSelectListTheme, getSettingsListTheme, getTheme, hasTavilyKey, loadSettings, mastra, parseSubagentMeta, planSubagent, releaseAllThreadLocks, releaseThreadLock, requestSandboxAccessTool, resolveModel, resolveModelDefaults, resolveOmModel, saveSettings, setAuthStorage, setAuthStorage2, setTheme, stringReplaceLspTool, theme, tintHex };
|
|
3501
|
+
//# sourceMappingURL=chunk-V4HZ2AVV.js.map
|
|
3502
|
+
//# sourceMappingURL=chunk-V4HZ2AVV.js.map
|