tarsk 0.4.27 → 0.4.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +467 -173
- package/dist/public/assets/account-view-YvC0mymF.js +1 -0
- package/dist/public/assets/alert-dialog-BFhUZsPe.js +1 -0
- package/dist/public/assets/api-DSvSyBH5.js +1 -0
- package/dist/public/assets/{browser-tab-DlzO5cMP.js → browser-tab-CCXMlmPO.js} +1 -1
- package/dist/public/assets/chat-input-container-VwmGKJFJ.js +21 -0
- package/dist/public/assets/codicon-ngg6Pgfi.ttf +0 -0
- package/dist/public/assets/context-menu-B6K8j1ha.js +1 -0
- package/dist/public/assets/conversation-history-view-DrrLyMbg.js +1 -0
- package/dist/public/assets/css.worker-DyDu5ynT.js +89 -0
- package/dist/public/assets/{dialogs-config-DZFG2IQM.js → dialogs-config-B91VQpxo.js} +3 -3
- package/dist/public/assets/diff-view-C09yOc5d.js +3 -0
- package/dist/public/assets/explorer-tab-view-B1DukGdo.js +2 -0
- package/dist/public/assets/{explorer-tree-8TyBYuC7.js → explorer-tree-DCeud6YI.js} +1 -1
- package/dist/public/assets/explorer-view-BCJqE1-z.js +1 -0
- package/dist/public/assets/history-view-Dj4NiScy.js +1 -0
- package/dist/public/assets/html.worker-IWNXhnIC.js +502 -0
- package/dist/public/assets/index-7TFkb0ll.css +1 -0
- package/dist/public/assets/index-BLCecayZ.js +29 -0
- package/dist/public/assets/json.worker-c4h5s80L.js +58 -0
- package/dist/public/assets/markdown-renderer-9nzN0Pk3.js +10 -0
- package/dist/public/assets/{mcp-server-card-DupwHcKT.js → mcp-server-card-BSw_FlEC.js} +1 -1
- package/dist/public/assets/monaco-BGyhgQtx.js +1209 -0
- package/dist/public/assets/monaco-Br_kD0ds.css +1 -0
- package/dist/public/assets/onboarding-ClUE_I5q.js +1 -0
- package/dist/public/assets/onboarding-dialog-C2jig8mT.js +1 -0
- package/dist/public/assets/page-toolbar-DnVFnCgq.js +1 -0
- package/dist/public/assets/{project-settings-view-C7GW_R0m.js → project-settings-view-DWn0ty7b.js} +1 -1
- package/dist/public/assets/providers-list-view-B0DqNJYK.js +1 -0
- package/dist/public/assets/radio-group-CM_Wwam0.js +1 -0
- package/dist/public/assets/react-vendor-nPkCWaAR.js +22 -0
- package/dist/public/assets/resizable-CEv7JjTI.js +1 -0
- package/dist/public/assets/{run-stop-button-k1LeM0M-.js → run-stop-button-CrjPMC0m.js} +2 -2
- package/dist/public/assets/settings-general-view-CwQYmnI2.js +1 -0
- package/dist/public/assets/settings-instructions-view-C23spY9Y.js +1 -0
- package/dist/public/assets/settings-mcp-servers-view-ZHar6YbE.js +5 -0
- package/dist/public/assets/settings-models-view-Cg6WQ4cy.js +1 -0
- package/dist/public/assets/settings-rules-view-zAx5JL4w.js +8 -0
- package/dist/public/assets/{settings-skills-view-aPv03-bO.js → settings-skills-view-D60tD-OE.js} +2 -2
- package/dist/public/assets/{settings-slash-commands-view-DLMWXQXA.js → settings-slash-commands-view-DR-pzulT.js} +1 -1
- package/dist/public/assets/{settings-subagents-view-DBYzNE4W.js → settings-subagents-view-InXmwmw9.js} +2 -2
- package/dist/public/assets/settings-view-DLRDBuwH.js +2 -0
- package/dist/public/assets/side-panel-container-CO3qgRvc.js +2 -0
- package/dist/public/assets/skeleton-CXoVAzC1.js +1 -0
- package/dist/public/assets/{standard-list-item-D2lbPXa1.js → standard-list-item-DyALhVYe.js} +1 -1
- package/dist/public/assets/store-D28g9VUj.js +4 -0
- package/dist/public/assets/{tab-context-CCvlrC0o.js → tab-context-C3z7YU59.js} +1 -1
- package/dist/public/assets/tabs-sXgL2DZz.js +1 -0
- package/dist/public/assets/{terminal-panel-GuO2XygM.js → terminal-panel-EifoA6G0.js} +2 -2
- package/dist/public/assets/textarea-Bp3p1-25.js +1 -0
- package/dist/public/assets/todos-view-DRL4px6A.js +1 -0
- package/dist/public/assets/ts.worker-BwM5Ha3L.js +67719 -0
- package/dist/public/assets/use-font-size-BCtvz0bm.js +1 -0
- package/dist/public/assets/{use-toast-DqmC-mB5.js → use-toast-BZ79E_dX.js} +1 -1
- package/dist/public/assets/{utils-Cy2s2TEb.js → utils-Bhu11Tpg.js} +1 -1
- package/dist/public/assets/{whisper-wasm-Clnf-l-9.js → whisper-wasm-C_Ot631g.js} +1 -1
- package/dist/public/ide/antigravity.svg +1 -0
- package/dist/public/ide/devin.svg +1 -0
- package/dist/public/index.html +24 -21
- package/package.json +1 -1
- package/dist/public/assets/account-view-BFhjAYmz.js +0 -1
- package/dist/public/assets/alert-dialog--tUzAWFm.js +0 -1
- package/dist/public/assets/api-BBbysRIh.js +0 -1
- package/dist/public/assets/chat-input-container-B_qVOGzW.js +0 -21
- package/dist/public/assets/context-menu-D5slxFcc.js +0 -1
- package/dist/public/assets/conversation-history-view-B4zHwyrw.js +0 -1
- package/dist/public/assets/diff-view-fOVqrHQW.js +0 -3
- package/dist/public/assets/explorer-tab-view-MLrWJ1Y0.js +0 -2
- package/dist/public/assets/explorer-view-5U70mfAJ.js +0 -1
- package/dist/public/assets/history-view-BR8scGnM.js +0 -1
- package/dist/public/assets/index-0BuHfewk.css +0 -1
- package/dist/public/assets/index-BTbiyooe.js +0 -37
- package/dist/public/assets/monaco-DDfTQM3i.js +0 -11
- package/dist/public/assets/onboarding-Cn5RqOCa.js +0 -1
- package/dist/public/assets/onboarding-dialog-BXsvjU1t.js +0 -1
- package/dist/public/assets/page-toolbar-TvYsDwUY.js +0 -1
- package/dist/public/assets/providers-list-view-AVt5I1WB.js +0 -1
- package/dist/public/assets/radio-group-Mifbn8sU.js +0 -1
- package/dist/public/assets/react-vendor-Bao-KYKs.js +0 -22
- package/dist/public/assets/resizable-DzrIos_Y.js +0 -1
- package/dist/public/assets/settings-instructions-view-CgfWSPmK.js +0 -1
- package/dist/public/assets/settings-mcp-servers-view-Becs_RxN.js +0 -5
- package/dist/public/assets/settings-models-view-BqdD_7GR.js +0 -1
- package/dist/public/assets/settings-rules-view-D7j5OHUi.js +0 -8
- package/dist/public/assets/settings-view-CccwUKkf.js +0 -2
- package/dist/public/assets/side-panel-container-oMcXib2A.js +0 -2
- package/dist/public/assets/skeleton-Bc0U0oGc.js +0 -1
- package/dist/public/assets/store-CwvBC-YP.js +0 -2
- package/dist/public/assets/tabs-Mb_cjIfE.js +0 -1
- package/dist/public/assets/textarea-DpXOZLt8.js +0 -1
- package/dist/public/assets/todos-view-C4GMi1Np.js +0 -1
- /package/dist/public/assets/{dist-BI23Ei33.js → dist-D5DgwfYL.js} +0 -0
package/dist/index.js
CHANGED
|
@@ -769,6 +769,14 @@ var PROGRAMS_CONFIG = {
|
|
|
769
769
|
Terminal: {
|
|
770
770
|
name: "Terminal",
|
|
771
771
|
appName: "Terminal"
|
|
772
|
+
},
|
|
773
|
+
"Devin Desktop": {
|
|
774
|
+
name: "Devin Desktop",
|
|
775
|
+
appName: ["Devin", "Devin Desktop"]
|
|
776
|
+
},
|
|
777
|
+
Antigravity: {
|
|
778
|
+
name: "Antigravity",
|
|
779
|
+
appName: ["com.google.antigravity-ide", "Antigravity IDE"]
|
|
772
780
|
}
|
|
773
781
|
};
|
|
774
782
|
var AVAILABLE_PROGRAMS = Object.keys(PROGRAMS_CONFIG);
|
|
@@ -1820,9 +1828,9 @@ function createGrepTool(cwd, options) {
|
|
|
1820
1828
|
const effectiveLimit = Math.max(1, limit ?? DEFAULT_LIMIT2);
|
|
1821
1829
|
const formatPath = (filePath) => {
|
|
1822
1830
|
if (isDirectory) {
|
|
1823
|
-
const
|
|
1824
|
-
if (
|
|
1825
|
-
return
|
|
1831
|
+
const relative8 = path2.relative(searchPath, filePath);
|
|
1832
|
+
if (relative8 && !relative8.startsWith("..")) {
|
|
1833
|
+
return relative8.replace(/\\/g, "/");
|
|
1826
1834
|
}
|
|
1827
1835
|
}
|
|
1828
1836
|
return path2.basename(filePath);
|
|
@@ -2175,7 +2183,7 @@ async function invalidateThreadIndex(threadId) {
|
|
|
2175
2183
|
}
|
|
2176
2184
|
|
|
2177
2185
|
// src/tools/code-search.ts
|
|
2178
|
-
var DEFAULT_LIMIT3 =
|
|
2186
|
+
var DEFAULT_LIMIT3 = 30;
|
|
2179
2187
|
var codeSearchSchema = Type5.Object({
|
|
2180
2188
|
query: Type5.Optional(
|
|
2181
2189
|
Type5.String({
|
|
@@ -2189,7 +2197,7 @@ var codeSearchSchema = Type5.Object({
|
|
|
2189
2197
|
),
|
|
2190
2198
|
filePath: Type5.Optional(
|
|
2191
2199
|
Type5.String({
|
|
2192
|
-
description: "Search by file path pattern, e.g. 'camp.page.ts', 'src/app/camp', or 'src/**/camp/*.ts'. Supports glob wildcards (* and **). Returns
|
|
2200
|
+
description: "Search by file path pattern, e.g. 'camp.page.ts', 'src/app/camp', or 'src/**/camp/*.ts'. Supports glob wildcards (* and **). Returns file paths only, no content."
|
|
2193
2201
|
})
|
|
2194
2202
|
),
|
|
2195
2203
|
fileGlob: Type5.Optional(
|
|
@@ -2324,10 +2332,12 @@ function formatOutput(rows, tokens, regex, limit, multiline = false) {
|
|
|
2324
2332
|
const matchLimitReached = rows.length >= limit ? rows.length : void 0;
|
|
2325
2333
|
for (const { file_path, content } of rows) {
|
|
2326
2334
|
const matchingLines = regex ? findLinesByRegex(content, regex, multiline) : findLinesByTokens(content, tokens);
|
|
2335
|
+
if (matchingLines.length === 0) continue;
|
|
2336
|
+
outputLines.push(`${file_path}:`);
|
|
2327
2337
|
for (const { line, text } of matchingLines) {
|
|
2328
2338
|
const { text: truncated, wasTruncated } = truncateLine(text.replace(/\r/g, ""));
|
|
2329
2339
|
if (wasTruncated) linesTruncated = true;
|
|
2330
|
-
outputLines.push(
|
|
2340
|
+
outputLines.push(` ${line}: ${truncated}`);
|
|
2331
2341
|
}
|
|
2332
2342
|
}
|
|
2333
2343
|
return { outputLines, linesTruncated, matchLimitReached };
|
|
@@ -2435,24 +2445,15 @@ function createCodeSearchTool(cwd, threadId) {
|
|
|
2435
2445
|
if (filePath && !query && !pattern) {
|
|
2436
2446
|
const likePattern = filePathToLike(filePath);
|
|
2437
2447
|
const result = await db.execute(
|
|
2438
|
-
`SELECT file_path
|
|
2448
|
+
`SELECT file_path FROM code_files
|
|
2439
2449
|
WHERE thread_id = ?
|
|
2440
2450
|
AND file_path LIKE ? ESCAPE '\\'
|
|
2441
2451
|
LIMIT ? OFFSET ?`,
|
|
2442
2452
|
[threadId, likePattern, limit, offset]
|
|
2443
2453
|
);
|
|
2444
2454
|
const rows2 = result.rows;
|
|
2445
|
-
const
|
|
2446
|
-
|
|
2447
|
-
[],
|
|
2448
|
-
null,
|
|
2449
|
-
limit
|
|
2450
|
-
);
|
|
2451
|
-
if (outputLines2.length === 0) {
|
|
2452
|
-
const names = rows2.map((r) => r.file_path).join("\n");
|
|
2453
|
-
return buildResult(names ? names.split("\n") : [], false, void 0, limit, offset);
|
|
2454
|
-
}
|
|
2455
|
-
return buildResult(outputLines2, linesTruncated2, matchLimitReached2, limit, offset);
|
|
2455
|
+
const names = rows2.map((r) => r.file_path);
|
|
2456
|
+
return buildResult(names, false, void 0, limit, offset);
|
|
2456
2457
|
}
|
|
2457
2458
|
if (filePath && (query || pattern)) {
|
|
2458
2459
|
const likePattern = filePathToLike(filePath);
|
|
@@ -2482,7 +2483,7 @@ function createCodeSearchTool(cwd, threadId) {
|
|
|
2482
2483
|
const args2 = likeFilter ? [threadId, likeFilter] : [threadId];
|
|
2483
2484
|
const result = await db.execute(sql, args2);
|
|
2484
2485
|
const allRows = result.rows;
|
|
2485
|
-
const
|
|
2486
|
+
const fileGroups = [];
|
|
2486
2487
|
let linesTruncated2 = false;
|
|
2487
2488
|
let matchedFiles = 0;
|
|
2488
2489
|
for (const { file_path, content } of allRows) {
|
|
@@ -2491,10 +2492,18 @@ function createCodeSearchTool(cwd, threadId) {
|
|
|
2491
2492
|
matchedFiles++;
|
|
2492
2493
|
if (matchedFiles <= offset) continue;
|
|
2493
2494
|
if (matchedFiles > offset + limit) break;
|
|
2494
|
-
|
|
2495
|
-
|
|
2495
|
+
fileGroups.push({ filePath: file_path, lines: matchingLines });
|
|
2496
|
+
for (const { text } of matchingLines) {
|
|
2497
|
+
const { wasTruncated } = truncateLine(text.replace(/\r/g, ""));
|
|
2496
2498
|
if (wasTruncated) linesTruncated2 = true;
|
|
2497
|
-
|
|
2499
|
+
}
|
|
2500
|
+
}
|
|
2501
|
+
const outputLines2 = [];
|
|
2502
|
+
for (const group of fileGroups) {
|
|
2503
|
+
outputLines2.push(`${group.filePath}:`);
|
|
2504
|
+
for (const { line, text } of group.lines) {
|
|
2505
|
+
const { text: truncated } = truncateLine(text.replace(/\r/g, ""));
|
|
2506
|
+
outputLines2.push(` ${line}: ${truncated}`);
|
|
2498
2507
|
}
|
|
2499
2508
|
}
|
|
2500
2509
|
const matchLimitReached2 = matchedFiles > offset + limit ? matchedFiles : void 0;
|
|
@@ -4140,16 +4149,56 @@ var ModelManager = class {
|
|
|
4140
4149
|
};
|
|
4141
4150
|
|
|
4142
4151
|
// src/features/providers/provider-resolver.ts
|
|
4152
|
+
function getProviderApiKeyEnvName(providerEnv) {
|
|
4153
|
+
if (providerEnv.length === 0) {
|
|
4154
|
+
return "";
|
|
4155
|
+
}
|
|
4156
|
+
const explicitApiKeyEnv = providerEnv.find((envName) => /api_key$/i.test(envName));
|
|
4157
|
+
return explicitApiKeyEnv ?? providerEnv[providerEnv.length - 1];
|
|
4158
|
+
}
|
|
4159
|
+
function getProviderApiUrlEnvName(providerName) {
|
|
4160
|
+
return `${providerName.toUpperCase().replace(/-/g, "_")}_API_URL`;
|
|
4161
|
+
}
|
|
4162
|
+
function getProviderResourceName(providerEnv) {
|
|
4163
|
+
const explicitResourceEnv = providerEnv.find((envName) => /resource_name$/i.test(envName));
|
|
4164
|
+
return explicitResourceEnv ?? providerEnv[0] ?? "";
|
|
4165
|
+
}
|
|
4166
|
+
function resolveAzureApiUrl(providerEnv) {
|
|
4167
|
+
const resourceEnvName = getProviderResourceName(providerEnv);
|
|
4168
|
+
const resourceName = process.env[resourceEnvName];
|
|
4169
|
+
if (!resourceName) {
|
|
4170
|
+
return "";
|
|
4171
|
+
}
|
|
4172
|
+
return `https://${resourceName}.openai.azure.com/openai/v1`;
|
|
4173
|
+
}
|
|
4174
|
+
function resolveTemplate(template, envValues) {
|
|
4175
|
+
let hasMissingValue = false;
|
|
4176
|
+
const resolved = template.replace(/\$\{([A-Z0-9_]+)\}/gi, (_match, envName) => {
|
|
4177
|
+
const value = envValues[envName] ?? process.env[envName] ?? "";
|
|
4178
|
+
if (!value) {
|
|
4179
|
+
hasMissingValue = true;
|
|
4180
|
+
}
|
|
4181
|
+
return value;
|
|
4182
|
+
});
|
|
4183
|
+
return hasMissingValue ? "" : resolved;
|
|
4184
|
+
}
|
|
4143
4185
|
async function resolveProviderConfig(providerName) {
|
|
4144
4186
|
const slug = providerName.toLowerCase();
|
|
4145
4187
|
const catalogProvider = await getCatalogProvider(slug);
|
|
4146
4188
|
if (!catalogProvider) return null;
|
|
4147
|
-
|
|
4189
|
+
const apiKeyEnv = getProviderApiKeyEnvName(catalogProvider.env ?? []);
|
|
4190
|
+
const apiUrlEnvName = getProviderApiUrlEnvName(slug);
|
|
4191
|
+
const api = slug === "azure" ? resolveAzureApiUrl(catalogProvider.env ?? []) : process.env[apiUrlEnvName] || (catalogProvider.api ? resolveTemplate(catalogProvider.api, process.env) : "");
|
|
4192
|
+
return {
|
|
4193
|
+
...catalogProvider,
|
|
4194
|
+
api,
|
|
4195
|
+
apiKeyEnv
|
|
4196
|
+
};
|
|
4148
4197
|
}
|
|
4149
4198
|
async function resolveProviderKeyName(providerName) {
|
|
4150
4199
|
const slug = providerName.toLowerCase();
|
|
4151
4200
|
const catalogProvider = await getCatalogProvider(slug);
|
|
4152
|
-
if (catalogProvider?.env?.
|
|
4201
|
+
if (catalogProvider?.env?.length) return getProviderApiKeyEnvName(catalogProvider.env);
|
|
4153
4202
|
return "";
|
|
4154
4203
|
}
|
|
4155
4204
|
|
|
@@ -4346,7 +4395,7 @@ function createGenerateImageTool(options) {
|
|
|
4346
4395
|
details: void 0
|
|
4347
4396
|
};
|
|
4348
4397
|
}
|
|
4349
|
-
let apiKey = providerConfig.
|
|
4398
|
+
let apiKey = providerConfig.apiKeyEnv ? process.env[providerConfig.apiKeyEnv] : void 0;
|
|
4350
4399
|
if (!apiKey) {
|
|
4351
4400
|
const providerKeys = await metadataManager.getProviderKeys();
|
|
4352
4401
|
apiKey = providerKeys[providerConfig.id];
|
|
@@ -4784,8 +4833,12 @@ function createToolSearchTool(options) {
|
|
|
4784
4833
|
let matched = [];
|
|
4785
4834
|
if (parsed.mode === "select") {
|
|
4786
4835
|
for (const name of parsed.names) {
|
|
4787
|
-
const tool
|
|
4788
|
-
|
|
4836
|
+
for (const [toolName, tool] of deferredTools.entries()) {
|
|
4837
|
+
if (toolName.toLowerCase() === name) {
|
|
4838
|
+
matched.push(tool);
|
|
4839
|
+
break;
|
|
4840
|
+
}
|
|
4841
|
+
}
|
|
4789
4842
|
}
|
|
4790
4843
|
} else {
|
|
4791
4844
|
const candidates = [];
|
|
@@ -5039,6 +5092,7 @@ var EventQueue = class {
|
|
|
5039
5092
|
finalContent = "";
|
|
5040
5093
|
errorOccurred = false;
|
|
5041
5094
|
toolCallCount = 0;
|
|
5095
|
+
lastErrorMessage;
|
|
5042
5096
|
/**
|
|
5043
5097
|
* Processes Pi Agent events and transforms them into AgentEvent format
|
|
5044
5098
|
*/
|
|
@@ -5107,6 +5161,9 @@ var EventQueue = class {
|
|
|
5107
5161
|
} else if (event.type === "agent_end") {
|
|
5108
5162
|
const messages = event.messages;
|
|
5109
5163
|
const lastAssistant = [...messages].reverse().find((m) => "role" in m && m.role === "assistant");
|
|
5164
|
+
if (lastAssistant && "errorMessage" in lastAssistant && typeof lastAssistant.errorMessage === "string") {
|
|
5165
|
+
this.lastErrorMessage = lastAssistant.errorMessage;
|
|
5166
|
+
}
|
|
5110
5167
|
if (lastAssistant) {
|
|
5111
5168
|
const extracted = extractTextFromAssistantMessage(lastAssistant);
|
|
5112
5169
|
if (extracted && extracted !== this.finalContent) {
|
|
@@ -5220,14 +5277,14 @@ async function executeSubagent(options) {
|
|
|
5220
5277
|
if (!providerConfig) {
|
|
5221
5278
|
throw new Error(`Unknown provider for subagent: ${providerName}`);
|
|
5222
5279
|
}
|
|
5223
|
-
let apiKey = providerConfig.
|
|
5280
|
+
let apiKey = providerConfig.apiKeyEnv ? process.env[providerConfig.apiKeyEnv] : void 0;
|
|
5224
5281
|
if (!apiKey) {
|
|
5225
5282
|
const providerKeys = await metadataManager.getProviderKeys();
|
|
5226
5283
|
apiKey = providerKeys[providerConfig.id];
|
|
5227
5284
|
}
|
|
5228
5285
|
if (!apiKey) {
|
|
5229
5286
|
throw new Error(
|
|
5230
|
-
`No API key found for subagent provider: ${providerName}. Set ${providerConfig.
|
|
5287
|
+
`No API key found for subagent provider: ${providerName}. Set ${providerConfig.apiKeyEnv ?? "an API key"} in environment variables or configuration.`
|
|
5231
5288
|
);
|
|
5232
5289
|
}
|
|
5233
5290
|
const resolvedModel = resolveModel(providerName, model, providerConfig);
|
|
@@ -6115,10 +6172,94 @@ async function loadAgentSystemPrompt(threadPath, tools, skills, planMode, agents
|
|
|
6115
6172
|
var cachedStreamFn = (model, context, options) => {
|
|
6116
6173
|
return streamSimple(model, context, { ...options, cacheRetention: "long" });
|
|
6117
6174
|
};
|
|
6175
|
+
function summarizeAssistantResponse(response) {
|
|
6176
|
+
const lines = response.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
|
|
6177
|
+
const salient = lines.filter(
|
|
6178
|
+
(line) => /^(#+\s|[-*]\s|\d+\.\s|```|changed|created|updated|deleted|fixed|implemented|added|removed|renamed|moved|tested|next|summary|result)/i.test(
|
|
6179
|
+
line
|
|
6180
|
+
) || line.length <= 180
|
|
6181
|
+
);
|
|
6182
|
+
const summary = (salient.length > 0 ? salient : lines).slice(0, 12).join("\n");
|
|
6183
|
+
return summary.length > 3e3 ? `${summary.slice(0, 3e3)}\u2026` : summary;
|
|
6184
|
+
}
|
|
6185
|
+
function getAssistantHistoryContent(exchange, isMostRecent) {
|
|
6186
|
+
if (isMostRecent) {
|
|
6187
|
+
return exchange.assistantResponse;
|
|
6188
|
+
}
|
|
6189
|
+
return exchange.assistantSummary ?? summarizeAssistantResponse(exchange.assistantResponse);
|
|
6190
|
+
}
|
|
6191
|
+
function buildAssistantHistoryText(content, isMostRecent) {
|
|
6192
|
+
if (isMostRecent) {
|
|
6193
|
+
return content;
|
|
6194
|
+
}
|
|
6195
|
+
return `[Earlier assistant response summarized to reduce context tokens]
|
|
6196
|
+
${content}`;
|
|
6197
|
+
}
|
|
6198
|
+
function serializeLogValue(value, depth = 0, seen = /* @__PURE__ */ new WeakSet()) {
|
|
6199
|
+
if (value === null || value === void 0) {
|
|
6200
|
+
return value;
|
|
6201
|
+
}
|
|
6202
|
+
const valueType = typeof value;
|
|
6203
|
+
if (valueType === "string" || valueType === "number" || valueType === "boolean") {
|
|
6204
|
+
return value;
|
|
6205
|
+
}
|
|
6206
|
+
if (valueType === "bigint" || valueType === "symbol") {
|
|
6207
|
+
return "[Primitive]";
|
|
6208
|
+
}
|
|
6209
|
+
if (valueType === "function") {
|
|
6210
|
+
return "[Function]";
|
|
6211
|
+
}
|
|
6212
|
+
if (value instanceof Error) {
|
|
6213
|
+
const serializedError = {
|
|
6214
|
+
name: value.name,
|
|
6215
|
+
message: value.message
|
|
6216
|
+
};
|
|
6217
|
+
if (value.stack) {
|
|
6218
|
+
serializedError.stack = value.stack;
|
|
6219
|
+
}
|
|
6220
|
+
if ("cause" in value) {
|
|
6221
|
+
serializedError.cause = serializeLogValue(
|
|
6222
|
+
value.cause,
|
|
6223
|
+
depth + 1,
|
|
6224
|
+
seen
|
|
6225
|
+
);
|
|
6226
|
+
}
|
|
6227
|
+
const errorObject = value;
|
|
6228
|
+
for (const [key, nestedValue] of Object.entries(errorObject)) {
|
|
6229
|
+
if (key in serializedError) continue;
|
|
6230
|
+
serializedError[key] = serializeLogValue(nestedValue, depth + 1, seen);
|
|
6231
|
+
}
|
|
6232
|
+
return serializedError;
|
|
6233
|
+
}
|
|
6234
|
+
if (Array.isArray(value)) {
|
|
6235
|
+
if (depth >= 4) {
|
|
6236
|
+
return "[Truncated]";
|
|
6237
|
+
}
|
|
6238
|
+
return value.slice(0, 50).map((item) => serializeLogValue(item, depth + 1, seen));
|
|
6239
|
+
}
|
|
6240
|
+
if (valueType === "object") {
|
|
6241
|
+
if (seen.has(value)) {
|
|
6242
|
+
return "[Circular]";
|
|
6243
|
+
}
|
|
6244
|
+
if (depth >= 4) {
|
|
6245
|
+
return "[Truncated]";
|
|
6246
|
+
}
|
|
6247
|
+
seen.add(value);
|
|
6248
|
+
const serializedObject = {};
|
|
6249
|
+
const plainObject = value;
|
|
6250
|
+
for (const [key, nestedValue] of Object.entries(plainObject).slice(0, 50)) {
|
|
6251
|
+
serializedObject[key] = serializeLogValue(nestedValue, depth + 1, seen);
|
|
6252
|
+
}
|
|
6253
|
+
return serializedObject;
|
|
6254
|
+
}
|
|
6255
|
+
return "[Unsupported value]";
|
|
6256
|
+
}
|
|
6118
6257
|
function buildHistoryMessages(history) {
|
|
6119
6258
|
const messages = [];
|
|
6120
6259
|
const now = Date.now();
|
|
6121
|
-
|
|
6260
|
+
history.forEach((exchange, index) => {
|
|
6261
|
+
const isMostRecent = index === history.length - 1;
|
|
6262
|
+
const assistantContent = getAssistantHistoryContent(exchange, isMostRecent);
|
|
6122
6263
|
messages.push({
|
|
6123
6264
|
role: "user",
|
|
6124
6265
|
content: exchange.userMessage,
|
|
@@ -6126,7 +6267,9 @@ function buildHistoryMessages(history) {
|
|
|
6126
6267
|
});
|
|
6127
6268
|
messages.push({
|
|
6128
6269
|
role: "assistant",
|
|
6129
|
-
content: [
|
|
6270
|
+
content: [
|
|
6271
|
+
{ type: "text", text: buildAssistantHistoryText(assistantContent, isMostRecent) }
|
|
6272
|
+
],
|
|
6130
6273
|
api: "",
|
|
6131
6274
|
provider: "",
|
|
6132
6275
|
model: "",
|
|
@@ -6141,7 +6284,7 @@ function buildHistoryMessages(history) {
|
|
|
6141
6284
|
stopReason: "stop",
|
|
6142
6285
|
timestamp: now
|
|
6143
6286
|
});
|
|
6144
|
-
}
|
|
6287
|
+
});
|
|
6145
6288
|
return messages;
|
|
6146
6289
|
}
|
|
6147
6290
|
var PiExecutorImpl = class {
|
|
@@ -6171,7 +6314,7 @@ var PiExecutorImpl = class {
|
|
|
6171
6314
|
if (!providerConfig) {
|
|
6172
6315
|
throw new Error(`Unknown provider: ${providerName}`);
|
|
6173
6316
|
}
|
|
6174
|
-
let apiKey = providerConfig.
|
|
6317
|
+
let apiKey = providerConfig.apiKeyEnv ? process.env[providerConfig.apiKeyEnv] : void 0;
|
|
6175
6318
|
let apiKeySource = "environment";
|
|
6176
6319
|
if (!apiKey) {
|
|
6177
6320
|
try {
|
|
@@ -6187,7 +6330,7 @@ var PiExecutorImpl = class {
|
|
|
6187
6330
|
}
|
|
6188
6331
|
if (!apiKey) {
|
|
6189
6332
|
throw new Error(
|
|
6190
|
-
`No API key found for provider: ${providerName}. Set ${providerConfig.
|
|
6333
|
+
`No API key found for provider: ${providerName}. Set ${providerConfig.apiKeyEnv ?? "an API key"} in environment variables or configuration.`
|
|
6191
6334
|
);
|
|
6192
6335
|
}
|
|
6193
6336
|
if (apiKey.includes(":") && apiKey.length > 100) {
|
|
@@ -6302,15 +6445,27 @@ ${decoded}
|
|
|
6302
6445
|
|
|
6303
6446
|
${userPrompt}`;
|
|
6304
6447
|
}
|
|
6305
|
-
|
|
6306
|
-
|
|
6307
|
-
|
|
6448
|
+
const promptRequest = {
|
|
6449
|
+
provider: providerName,
|
|
6450
|
+
model,
|
|
6451
|
+
cwd,
|
|
6452
|
+
prompt: finalPrompt,
|
|
6453
|
+
promptLength: finalPrompt.length,
|
|
6454
|
+
promptPreview: finalPrompt.substring(0, 200) + (finalPrompt.length > 200 ? "..." : ""),
|
|
6455
|
+
imageCount: images.length,
|
|
6456
|
+
images: images.map((img) => ({
|
|
6457
|
+
mimeType: img.mimeType,
|
|
6458
|
+
dataLength: img.data?.length ?? 0
|
|
6459
|
+
})),
|
|
6460
|
+
attachments: (context.attachments ?? []).map((a) => ({
|
|
6308
6461
|
name: a.name,
|
|
6309
6462
|
mimeType: a.mimeType,
|
|
6310
6463
|
contentLength: a.content?.length ?? 0
|
|
6311
|
-
}))
|
|
6312
|
-
|
|
6313
|
-
|
|
6464
|
+
}))
|
|
6465
|
+
};
|
|
6466
|
+
console.log(
|
|
6467
|
+
`[ai] Attachments: ${context.attachments?.length ?? 0} total, ${images.length} image(s), ${textAttachments.length} text file(s)`,
|
|
6468
|
+
promptRequest
|
|
6314
6469
|
);
|
|
6315
6470
|
const promptDone = agent.prompt(finalPrompt, images.length > 0 ? images : void 0).then(() => {
|
|
6316
6471
|
console.log(`[ai] Agent prompt completed successfully`);
|
|
@@ -6319,7 +6474,8 @@ ${userPrompt}`;
|
|
|
6319
6474
|
}).catch((err) => {
|
|
6320
6475
|
const errMessage = err instanceof Error ? err.message : String(err);
|
|
6321
6476
|
console.error(`[ai] Agent prompt failed:`, {
|
|
6322
|
-
|
|
6477
|
+
request: promptRequest,
|
|
6478
|
+
error: serializeLogValue(err),
|
|
6323
6479
|
errorType: err instanceof Error ? err.constructor.name : typeof err
|
|
6324
6480
|
});
|
|
6325
6481
|
if (!eventQueue.errorOccurred) {
|
|
@@ -6373,12 +6529,20 @@ ${userPrompt}`;
|
|
|
6373
6529
|
};
|
|
6374
6530
|
} else {
|
|
6375
6531
|
console.log("[ai] \u26A0\uFE0F Agent ended with no text content and no tool calls.");
|
|
6532
|
+
const upstreamError = eventQueue.lastErrorMessage ? { message: eventQueue.lastErrorMessage } : void 0;
|
|
6533
|
+
console.error("[ai] Agent ended without text content:", {
|
|
6534
|
+
request: promptRequest,
|
|
6535
|
+
upstreamError,
|
|
6536
|
+
finalContentLength: eventQueue.finalContent.length,
|
|
6537
|
+
toolCallCount: eventQueue.toolCallCount
|
|
6538
|
+
});
|
|
6376
6539
|
yield {
|
|
6377
6540
|
type: "error",
|
|
6378
6541
|
content: `The model ${model} from ${providerName} did not provide a response to your prompt. Check your API key and balance and try again.`,
|
|
6379
6542
|
error: {
|
|
6380
6543
|
code: "NO_CONTENT_NO_TOOLS",
|
|
6381
|
-
message: `The model ${model} from ${providerName} did not provide a response to your prompt
|
|
6544
|
+
message: `The model ${model} from ${providerName} did not provide a response to your prompt.`,
|
|
6545
|
+
details: upstreamError
|
|
6382
6546
|
}
|
|
6383
6547
|
};
|
|
6384
6548
|
}
|
|
@@ -6485,6 +6649,63 @@ var ProcessingStateManagerImpl = class {
|
|
|
6485
6649
|
}
|
|
6486
6650
|
};
|
|
6487
6651
|
|
|
6652
|
+
// src/core/env-manager.ts
|
|
6653
|
+
import { promises as fs } from "fs";
|
|
6654
|
+
import { join as join11 } from "path";
|
|
6655
|
+
function updateRuntimeEnv(values) {
|
|
6656
|
+
for (const [key, value] of Object.entries(values)) {
|
|
6657
|
+
process.env[key] = value;
|
|
6658
|
+
}
|
|
6659
|
+
}
|
|
6660
|
+
async function updateEnvFile(keyNames) {
|
|
6661
|
+
const envPath = join11(process.cwd(), ".env");
|
|
6662
|
+
let content = "";
|
|
6663
|
+
try {
|
|
6664
|
+
content = await fs.readFile(envPath, "utf-8");
|
|
6665
|
+
} catch {
|
|
6666
|
+
}
|
|
6667
|
+
const lines = content.split("\n");
|
|
6668
|
+
const envMap = {};
|
|
6669
|
+
for (const line of lines) {
|
|
6670
|
+
const trimmed = line.trim();
|
|
6671
|
+
if (trimmed && !trimmed.startsWith("#") && trimmed.includes("=")) {
|
|
6672
|
+
const index = trimmed.indexOf("=");
|
|
6673
|
+
const key = trimmed.substring(0, index).trim();
|
|
6674
|
+
const value = trimmed.substring(index + 1).trim();
|
|
6675
|
+
envMap[key] = value;
|
|
6676
|
+
}
|
|
6677
|
+
}
|
|
6678
|
+
for (const [key, value] of Object.entries(keyNames)) {
|
|
6679
|
+
if (key) {
|
|
6680
|
+
envMap[key] = value;
|
|
6681
|
+
}
|
|
6682
|
+
}
|
|
6683
|
+
const newLines = [];
|
|
6684
|
+
for (const [key, value] of Object.entries(envMap)) {
|
|
6685
|
+
newLines.push(`${key}=${value}`);
|
|
6686
|
+
}
|
|
6687
|
+
await fs.writeFile(envPath, newLines.join("\n") + "\n", "utf-8");
|
|
6688
|
+
}
|
|
6689
|
+
async function readEnvFile() {
|
|
6690
|
+
const envPath = join11(process.cwd(), ".env");
|
|
6691
|
+
const envMap = {};
|
|
6692
|
+
try {
|
|
6693
|
+
const content = await fs.readFile(envPath, "utf-8");
|
|
6694
|
+
const lines = content.split("\n");
|
|
6695
|
+
for (const line of lines) {
|
|
6696
|
+
const trimmed = line.trim();
|
|
6697
|
+
if (trimmed && !trimmed.startsWith("#") && trimmed.includes("=")) {
|
|
6698
|
+
const index = trimmed.indexOf("=");
|
|
6699
|
+
const key = trimmed.substring(0, index).trim();
|
|
6700
|
+
const value = trimmed.substring(index + 1).trim();
|
|
6701
|
+
envMap[key] = value;
|
|
6702
|
+
}
|
|
6703
|
+
}
|
|
6704
|
+
} catch {
|
|
6705
|
+
}
|
|
6706
|
+
return envMap;
|
|
6707
|
+
}
|
|
6708
|
+
|
|
6488
6709
|
// src/features/ask-user/ask-user.routes.ts
|
|
6489
6710
|
import { Hono } from "hono";
|
|
6490
6711
|
|
|
@@ -6659,7 +6880,7 @@ import { randomUUID as randomUUID4 } from "crypto";
|
|
|
6659
6880
|
|
|
6660
6881
|
// src/features/skills/skills.manager.ts
|
|
6661
6882
|
import { readdir as readdir4, readFile as readFile6 } from "fs/promises";
|
|
6662
|
-
import { join as
|
|
6883
|
+
import { join as join12 } from "path";
|
|
6663
6884
|
import { existsSync as existsSync9 } from "fs";
|
|
6664
6885
|
import { homedir as homedir5 } from "os";
|
|
6665
6886
|
function parseFrontmatter(markdown) {
|
|
@@ -6707,10 +6928,10 @@ function parseFrontmatter(markdown) {
|
|
|
6707
6928
|
return { metadata, content };
|
|
6708
6929
|
}
|
|
6709
6930
|
function getGlobalSkillsDir() {
|
|
6710
|
-
return
|
|
6931
|
+
return join12(homedir5(), ".agents", "skills");
|
|
6711
6932
|
}
|
|
6712
6933
|
function getProjectSkillsDir(threadPath) {
|
|
6713
|
-
return
|
|
6934
|
+
return join12(threadPath, ".agents", "skills");
|
|
6714
6935
|
}
|
|
6715
6936
|
function validateSkillName(name) {
|
|
6716
6937
|
if (!name || name.length === 0 || name.length > 64) {
|
|
@@ -6763,8 +6984,8 @@ var SkillManager = class {
|
|
|
6763
6984
|
for (const entry of entries) {
|
|
6764
6985
|
if (!entry.isDirectory()) continue;
|
|
6765
6986
|
const skillDirName = entry.name;
|
|
6766
|
-
const skillPath =
|
|
6767
|
-
const skillFilePath =
|
|
6987
|
+
const skillPath = join12(dir, skillDirName);
|
|
6988
|
+
const skillFilePath = join12(skillPath, "SKILL.md");
|
|
6768
6989
|
if (!existsSync9(skillFilePath)) {
|
|
6769
6990
|
console.warn(`Skipping skill directory ${skillDirName}: SKILL.md not found`);
|
|
6770
6991
|
continue;
|
|
@@ -6944,7 +7165,7 @@ async function activateSkills(allSkills, taskDescription, thread, options) {
|
|
|
6944
7165
|
|
|
6945
7166
|
// src/features/agents/agents.manager.ts
|
|
6946
7167
|
import { readdir as readdir5, readFile as readFile7 } from "fs/promises";
|
|
6947
|
-
import { join as
|
|
7168
|
+
import { join as join13 } from "path";
|
|
6948
7169
|
import { existsSync as existsSync10 } from "fs";
|
|
6949
7170
|
import { homedir as homedir6 } from "os";
|
|
6950
7171
|
function parseFrontmatter2(markdown) {
|
|
@@ -7002,10 +7223,10 @@ function validateDescription2(description) {
|
|
|
7002
7223
|
return !!description && description.length > 0 && description.length <= 1024;
|
|
7003
7224
|
}
|
|
7004
7225
|
function getGlobalAgentsDir() {
|
|
7005
|
-
return
|
|
7226
|
+
return join13(homedir6(), ".agents", "agents");
|
|
7006
7227
|
}
|
|
7007
7228
|
function getProjectAgentsDir(threadPath) {
|
|
7008
|
-
return
|
|
7229
|
+
return join13(threadPath, ".agents", "agents");
|
|
7009
7230
|
}
|
|
7010
7231
|
var AgentsManager = class {
|
|
7011
7232
|
/**
|
|
@@ -7037,8 +7258,8 @@ var AgentsManager = class {
|
|
|
7037
7258
|
for (const entry of entries) {
|
|
7038
7259
|
if (!entry.isDirectory()) continue;
|
|
7039
7260
|
const agentDirName = entry.name;
|
|
7040
|
-
const agentPath =
|
|
7041
|
-
const agentFilePath =
|
|
7261
|
+
const agentPath = join13(dir, agentDirName);
|
|
7262
|
+
const agentFilePath = join13(agentPath, "AGENT.md");
|
|
7042
7263
|
if (!existsSync10(agentFilePath)) {
|
|
7043
7264
|
console.warn(`[agents] Skipping agent directory ${agentDirName}: AGENT.md not found`);
|
|
7044
7265
|
continue;
|
|
@@ -7225,7 +7446,7 @@ function extractAssistantContent(events, fallback) {
|
|
|
7225
7446
|
// src/features/chat/chat-post.route.ts
|
|
7226
7447
|
init_database();
|
|
7227
7448
|
import { readFile as readFile8, unlink } from "fs/promises";
|
|
7228
|
-
import { join as
|
|
7449
|
+
import { join as join14 } from "path";
|
|
7229
7450
|
|
|
7230
7451
|
// src/features/project-todos/project-todos.database.ts
|
|
7231
7452
|
init_database();
|
|
@@ -8218,7 +8439,7 @@ ${result.output}`;
|
|
|
8218
8439
|
try {
|
|
8219
8440
|
const todo = await getTodoByThreadId(db, threadId);
|
|
8220
8441
|
if (todo && todo.status === "Plan") {
|
|
8221
|
-
const planFilePath =
|
|
8442
|
+
const planFilePath = join14(threadPath, `${todo.id}-plan.md`);
|
|
8222
8443
|
try {
|
|
8223
8444
|
const planContent = await readFile8(planFilePath, "utf-8");
|
|
8224
8445
|
await updateTodo(db, todo.id, { description: planContent.trim() });
|
|
@@ -8430,13 +8651,15 @@ async function updateMessage(db, messageId, content, events, imageUrl) {
|
|
|
8430
8651
|
if (hasMessageUsage) {
|
|
8431
8652
|
if (event.type === "message" && event.message?.usage) {
|
|
8432
8653
|
const usage = event.message.usage;
|
|
8433
|
-
|
|
8434
|
-
|
|
8654
|
+
const tokens = calculateCostEquivalentTokens(usage);
|
|
8655
|
+
inputTokens += tokens.inputTokens;
|
|
8656
|
+
outputTokens += tokens.outputTokens;
|
|
8435
8657
|
}
|
|
8436
8658
|
} else {
|
|
8437
8659
|
if (event.type === "toolcall_delta" && event.partial?.usage) {
|
|
8438
|
-
|
|
8439
|
-
|
|
8660
|
+
const tokens = calculateCostEquivalentTokens(event.partial.usage);
|
|
8661
|
+
inputTokens += tokens.inputTokens;
|
|
8662
|
+
outputTokens += tokens.outputTokens;
|
|
8440
8663
|
}
|
|
8441
8664
|
}
|
|
8442
8665
|
}
|
|
@@ -8467,6 +8690,34 @@ async function updateMessage(db, messageId, content, events, imageUrl) {
|
|
|
8467
8690
|
throw error;
|
|
8468
8691
|
}
|
|
8469
8692
|
}
|
|
8693
|
+
function calculateCostEquivalentTokens(usage) {
|
|
8694
|
+
const input = usage.input ?? 0;
|
|
8695
|
+
const output = usage.output ?? 0;
|
|
8696
|
+
const cacheRead = usage.cacheRead ?? 0;
|
|
8697
|
+
const cacheWrite = usage.cacheWrite ?? 0;
|
|
8698
|
+
const cachedInput = cacheRead + cacheWrite;
|
|
8699
|
+
const uncachedInput = input >= cachedInput ? input - cachedInput : input;
|
|
8700
|
+
const inputCost = usage.cost?.input ?? 0;
|
|
8701
|
+
const cacheCost = (usage.cost?.cacheRead ?? 0) + (usage.cost?.cacheWrite ?? 0);
|
|
8702
|
+
if (uncachedInput > 0 && inputCost > 0 && cacheCost > 0) {
|
|
8703
|
+
const inputCostPerToken = inputCost / uncachedInput;
|
|
8704
|
+
const costEquivalentCachedInput = cacheCost / inputCostPerToken;
|
|
8705
|
+
return {
|
|
8706
|
+
inputTokens: Math.round(uncachedInput + costEquivalentCachedInput),
|
|
8707
|
+
outputTokens: output
|
|
8708
|
+
};
|
|
8709
|
+
}
|
|
8710
|
+
if (input >= cachedInput) {
|
|
8711
|
+
return {
|
|
8712
|
+
inputTokens: uncachedInput + cachedInput,
|
|
8713
|
+
outputTokens: output
|
|
8714
|
+
};
|
|
8715
|
+
}
|
|
8716
|
+
return {
|
|
8717
|
+
inputTokens: input,
|
|
8718
|
+
outputTokens: output
|
|
8719
|
+
};
|
|
8720
|
+
}
|
|
8470
8721
|
async function getConversationHistory(db, threadId) {
|
|
8471
8722
|
try {
|
|
8472
8723
|
const result = await db.execute(
|
|
@@ -9760,6 +10011,7 @@ var MetadataManager = class {
|
|
|
9760
10011
|
await setState(this.db, "selectedThreadId", state.selectedThreadId);
|
|
9761
10012
|
await setState(this.db, "enabledModels", state.enabledModels);
|
|
9762
10013
|
await setState(this.db, "enabledImageModels", state.enabledImageModels);
|
|
10014
|
+
await setState(this.db, "providerEnvValues", state.providerEnvValues);
|
|
9763
10015
|
await setState(this.db, "onboardingCompleted", state.onboardingCompleted);
|
|
9764
10016
|
const encryptedKeys = {};
|
|
9765
10017
|
for (const [provider, key] of Object.entries(state.providerKeys)) {
|
|
@@ -9792,6 +10044,7 @@ var MetadataManager = class {
|
|
|
9792
10044
|
const onboardingCompleted = allState.onboardingCompleted || false;
|
|
9793
10045
|
const enabledModels = allState.enabledModels || {};
|
|
9794
10046
|
const enabledImageModels = allState.enabledImageModels || {};
|
|
10047
|
+
const providerEnvValues = allState.providerEnvValues || {};
|
|
9795
10048
|
const encryptedKeys = allState.providerKeys || {};
|
|
9796
10049
|
const providerKeys = {};
|
|
9797
10050
|
let needsMigration = false;
|
|
@@ -9824,6 +10077,7 @@ var MetadataManager = class {
|
|
|
9824
10077
|
selectedThreadId,
|
|
9825
10078
|
onboardingCompleted,
|
|
9826
10079
|
providerKeys,
|
|
10080
|
+
providerEnvValues,
|
|
9827
10081
|
enabledModels,
|
|
9828
10082
|
enabledImageModels
|
|
9829
10083
|
};
|
|
@@ -9902,6 +10156,36 @@ var MetadataManager = class {
|
|
|
9902
10156
|
throw new Error(`Failed to get provider keys: ${errorMessage}`);
|
|
9903
10157
|
}
|
|
9904
10158
|
}
|
|
10159
|
+
/**
|
|
10160
|
+
* Save provider environment values (non-secret values such as Azure resource name)
|
|
10161
|
+
* @param values - Environment values to merge into metadata state
|
|
10162
|
+
*/
|
|
10163
|
+
async saveProviderEnvValues(values) {
|
|
10164
|
+
try {
|
|
10165
|
+
const state = await this.loadState();
|
|
10166
|
+
state.providerEnvValues = {
|
|
10167
|
+
...state.providerEnvValues,
|
|
10168
|
+
...values
|
|
10169
|
+
};
|
|
10170
|
+
await this.saveState(state);
|
|
10171
|
+
} catch (error) {
|
|
10172
|
+
throw new Error(`Failed to save provider environment values: ${String(error)}`);
|
|
10173
|
+
}
|
|
10174
|
+
}
|
|
10175
|
+
/**
|
|
10176
|
+
* Get provider environment values stored in metadata state
|
|
10177
|
+
* @returns Record of environment variable names to values
|
|
10178
|
+
*/
|
|
10179
|
+
async getProviderEnvValues() {
|
|
10180
|
+
try {
|
|
10181
|
+
const state = await this.loadState();
|
|
10182
|
+
return state.providerEnvValues;
|
|
10183
|
+
} catch (error) {
|
|
10184
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
10185
|
+
console.error("[MetadataManager] Failed to get provider environment values:", errorMessage);
|
|
10186
|
+
throw new Error(`Failed to get provider environment values: ${errorMessage}`);
|
|
10187
|
+
}
|
|
10188
|
+
}
|
|
9905
10189
|
/**
|
|
9906
10190
|
* Set the selected thread ID
|
|
9907
10191
|
* @param threadId - Thread ID to select (or null to deselect)
|
|
@@ -10086,7 +10370,7 @@ async function handleGetAvailableModels(c, modelManager) {
|
|
|
10086
10370
|
const bEnabled = enabledModelIds.has(b.id) ? 0 : 1;
|
|
10087
10371
|
return aEnabled - bEnabled;
|
|
10088
10372
|
});
|
|
10089
|
-
return c.json({ provider, models });
|
|
10373
|
+
return c.json({ provider, models, enabledModelIds: [...enabledModelIds] });
|
|
10090
10374
|
} catch (error) {
|
|
10091
10375
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
10092
10376
|
return c.json({ error: message }, 500);
|
|
@@ -10542,6 +10826,9 @@ async function handleDeleteProject(c, projectManager) {
|
|
|
10542
10826
|
// src/features/projects/projects.open-with.ts
|
|
10543
10827
|
init_utils();
|
|
10544
10828
|
import open from "open";
|
|
10829
|
+
function formatOpenError(program, _rawError) {
|
|
10830
|
+
return `${program} could not be started. Maybe it is not installed.`;
|
|
10831
|
+
}
|
|
10545
10832
|
function validateProgram(program) {
|
|
10546
10833
|
if (!program) {
|
|
10547
10834
|
return "Program is required";
|
|
@@ -10593,7 +10880,7 @@ var OpenWithHandler = class {
|
|
|
10593
10880
|
} catch (error) {
|
|
10594
10881
|
const message = error instanceof Error ? error.message : String(error);
|
|
10595
10882
|
console.error("[openWith] open() error:", message);
|
|
10596
|
-
throw new Error(
|
|
10883
|
+
throw new Error(formatOpenError(program, error));
|
|
10597
10884
|
}
|
|
10598
10885
|
}
|
|
10599
10886
|
/**
|
|
@@ -10720,7 +11007,7 @@ async function handleOpenProject(c, projectManager) {
|
|
|
10720
11007
|
});
|
|
10721
11008
|
} catch (error) {
|
|
10722
11009
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
10723
|
-
return errorResponse(c, ErrorCodes.OPEN_PROGRAM_ERROR, errorMessage,
|
|
11010
|
+
return errorResponse(c, ErrorCodes.OPEN_PROGRAM_ERROR, errorMessage, 422);
|
|
10724
11011
|
}
|
|
10725
11012
|
}
|
|
10726
11013
|
|
|
@@ -10958,10 +11245,10 @@ async function handleCheckRunning(c, projectManager) {
|
|
|
10958
11245
|
|
|
10959
11246
|
// src/core/run-command-detector.ts
|
|
10960
11247
|
import { readFile as readFile9 } from "fs/promises";
|
|
10961
|
-
import { join as
|
|
11248
|
+
import { join as join15 } from "path";
|
|
10962
11249
|
async function detectPackageManager(projectPath) {
|
|
10963
11250
|
try {
|
|
10964
|
-
const packageJsonPath =
|
|
11251
|
+
const packageJsonPath = join15(projectPath, "package.json");
|
|
10965
11252
|
const content = await readFile9(packageJsonPath, "utf-8");
|
|
10966
11253
|
const packageJson = JSON.parse(content);
|
|
10967
11254
|
if (packageJson.packageManager) {
|
|
@@ -10977,7 +11264,7 @@ async function detectPackageManager(projectPath) {
|
|
|
10977
11264
|
}
|
|
10978
11265
|
async function detectRunScripts(projectPath) {
|
|
10979
11266
|
try {
|
|
10980
|
-
const packageJsonPath =
|
|
11267
|
+
const packageJsonPath = join15(projectPath, "package.json");
|
|
10981
11268
|
const content = await readFile9(packageJsonPath, "utf-8");
|
|
10982
11269
|
const packageJson = JSON.parse(content);
|
|
10983
11270
|
const scripts = packageJson.scripts ?? {};
|
|
@@ -11024,7 +11311,7 @@ async function suggestRunCommand(projectPath) {
|
|
|
11024
11311
|
|
|
11025
11312
|
// src/features/projects/projects-package-scripts.route.ts
|
|
11026
11313
|
import { readFile as readFile10 } from "fs/promises";
|
|
11027
|
-
import { join as
|
|
11314
|
+
import { join as join16 } from "path";
|
|
11028
11315
|
import { glob } from "glob";
|
|
11029
11316
|
function formatScriptName(scriptName) {
|
|
11030
11317
|
return scriptName.replace(/[-:_]/g, " ").replace(/\b\w/g, (l) => l.toUpperCase());
|
|
@@ -11052,7 +11339,7 @@ async function findPackageScripts(projectPath) {
|
|
|
11052
11339
|
const packageManager = await detectPackageManager(projectPath);
|
|
11053
11340
|
for (const filePath of packageJsonFiles) {
|
|
11054
11341
|
try {
|
|
11055
|
-
const fullPath =
|
|
11342
|
+
const fullPath = join16(projectPath, filePath);
|
|
11056
11343
|
const content = await readFile10(fullPath, "utf-8");
|
|
11057
11344
|
const packageJson = JSON.parse(content);
|
|
11058
11345
|
if (packageJson.scripts) {
|
|
@@ -11101,7 +11388,7 @@ async function handleGetPackageScripts(c, projectManager) {
|
|
|
11101
11388
|
|
|
11102
11389
|
// src/features/projects/projects-ai-files.route.ts
|
|
11103
11390
|
import { readdir as readdir6, readFile as readFile11, writeFile as writeFile2, mkdir, access as access2, rm } from "fs/promises";
|
|
11104
|
-
import { join as
|
|
11391
|
+
import { join as join17, extname as extname3, basename } from "path";
|
|
11105
11392
|
import { existsSync as existsSync11 } from "fs";
|
|
11106
11393
|
var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
11107
11394
|
".git",
|
|
@@ -11120,8 +11407,8 @@ async function buildFullTree(dirPath, relativeDirPath) {
|
|
|
11120
11407
|
try {
|
|
11121
11408
|
const entries = await readdir6(dirPath, { withFileTypes: true });
|
|
11122
11409
|
for (const entry of entries) {
|
|
11123
|
-
const entryRelPath =
|
|
11124
|
-
const entryAbsPath =
|
|
11410
|
+
const entryRelPath = join17(relativeDirPath, entry.name);
|
|
11411
|
+
const entryAbsPath = join17(dirPath, entry.name);
|
|
11125
11412
|
if (entry.isDirectory()) {
|
|
11126
11413
|
const children = await buildFullTree(entryAbsPath, entryRelPath);
|
|
11127
11414
|
nodes.push({
|
|
@@ -11149,8 +11436,8 @@ async function buildMarkdownFilteredTree(dirPath, relativeDirPath) {
|
|
|
11149
11436
|
try {
|
|
11150
11437
|
const entries = await readdir6(dirPath, { withFileTypes: true });
|
|
11151
11438
|
for (const entry of entries) {
|
|
11152
|
-
const entryRelPath =
|
|
11153
|
-
const entryAbsPath =
|
|
11439
|
+
const entryRelPath = join17(relativeDirPath, entry.name);
|
|
11440
|
+
const entryAbsPath = join17(dirPath, entry.name);
|
|
11154
11441
|
if (entry.isDirectory()) {
|
|
11155
11442
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
11156
11443
|
const children = await buildMarkdownFilteredTree(entryAbsPath, entryRelPath);
|
|
@@ -11185,8 +11472,8 @@ async function buildAIFileTree(projectPath) {
|
|
|
11185
11472
|
{ key: "agents", label: "Agents" }
|
|
11186
11473
|
];
|
|
11187
11474
|
for (const { key, label } of agentsFolders) {
|
|
11188
|
-
const relPath =
|
|
11189
|
-
const absPath =
|
|
11475
|
+
const relPath = join17(".agents", key);
|
|
11476
|
+
const absPath = join17(projectPath, relPath);
|
|
11190
11477
|
const exists = existsSync11(absPath);
|
|
11191
11478
|
if (exists) {
|
|
11192
11479
|
const children = await buildFullTree(absPath, relPath);
|
|
@@ -11221,7 +11508,7 @@ async function buildAIFileTree(projectPath) {
|
|
|
11221
11508
|
if (entry.name === ".agents" || entry.name === "AGENTS.md" || IGNORED_DIRS.has(entry.name))
|
|
11222
11509
|
continue;
|
|
11223
11510
|
const entryRelPath = entry.name;
|
|
11224
|
-
const entryAbsPath =
|
|
11511
|
+
const entryAbsPath = join17(projectPath, entry.name);
|
|
11225
11512
|
if (entry.isFile() && MARKDOWN_EXTS.has(extname3(entry.name))) {
|
|
11226
11513
|
nodes.push({
|
|
11227
11514
|
id: entryRelPath,
|
|
@@ -11248,7 +11535,7 @@ async function buildAIFileTree(projectPath) {
|
|
|
11248
11535
|
}
|
|
11249
11536
|
function validateFilePath(projectPath, filePath) {
|
|
11250
11537
|
if (!filePath) return null;
|
|
11251
|
-
const abs =
|
|
11538
|
+
const abs = join17(projectPath, filePath);
|
|
11252
11539
|
if (!abs.startsWith(projectPath + "/") && abs !== projectPath) return null;
|
|
11253
11540
|
return abs;
|
|
11254
11541
|
}
|
|
@@ -11374,7 +11661,7 @@ async function handleSaveAIFile(c, projectManager) {
|
|
|
11374
11661
|
if (!absPath) {
|
|
11375
11662
|
return c.json({ error: { code: "BAD_REQUEST", message: "Invalid file path" } }, 400);
|
|
11376
11663
|
}
|
|
11377
|
-
const parentDir =
|
|
11664
|
+
const parentDir = join17(absPath, "..");
|
|
11378
11665
|
await mkdir(parentDir, { recursive: true });
|
|
11379
11666
|
await writeFile2(absPath, content, "utf-8");
|
|
11380
11667
|
return c.json({ success: true, path: filePath });
|
|
@@ -11474,10 +11761,10 @@ async function handleCreateSkill(c, projectManager) {
|
|
|
11474
11761
|
if (!project) {
|
|
11475
11762
|
return errorResponse(c, ErrorCodes.PROJECT_NOT_FOUND, `Project not found: ${projectId}`, 404);
|
|
11476
11763
|
}
|
|
11477
|
-
const skillRelPath =
|
|
11478
|
-
const skillAbsPath =
|
|
11479
|
-
const skillFileRelPath =
|
|
11480
|
-
const skillFileAbsPath =
|
|
11764
|
+
const skillRelPath = join17(".agents", "skills", name);
|
|
11765
|
+
const skillAbsPath = join17(project.path, skillRelPath);
|
|
11766
|
+
const skillFileRelPath = join17(skillRelPath, "SKILL.md");
|
|
11767
|
+
const skillFileAbsPath = join17(skillAbsPath, "SKILL.md");
|
|
11481
11768
|
if (existsSync11(skillAbsPath)) {
|
|
11482
11769
|
return c.json(
|
|
11483
11770
|
{ error: { code: "CONFLICT", message: `Skill '${name}' already exists` } },
|
|
@@ -11579,7 +11866,7 @@ function createProjectRoutes(projectManager, threadManager) {
|
|
|
11579
11866
|
|
|
11580
11867
|
// src/features/projects/projects.manager.ts
|
|
11581
11868
|
init_utils();
|
|
11582
|
-
import { join as
|
|
11869
|
+
import { join as join20 } from "path";
|
|
11583
11870
|
import { realpathSync as realpathSync2 } from "fs";
|
|
11584
11871
|
import { rm as rm3 } from "fs/promises";
|
|
11585
11872
|
|
|
@@ -11800,13 +12087,13 @@ var ProcessManager = class extends EventEmitter {
|
|
|
11800
12087
|
// src/features/projects/projects.creator.ts
|
|
11801
12088
|
init_utils();
|
|
11802
12089
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
11803
|
-
import { basename as basename2, join as
|
|
12090
|
+
import { basename as basename2, join as join19, relative as relative5 } from "path";
|
|
11804
12091
|
import { access as access3, stat as stat3, readdir as readdir8 } from "fs/promises";
|
|
11805
12092
|
|
|
11806
12093
|
// src/features/scaffold/scaffold.runner.ts
|
|
11807
12094
|
init_utils();
|
|
11808
12095
|
import { existsSync as existsSync12 } from "fs";
|
|
11809
|
-
import { join as
|
|
12096
|
+
import { join as join18 } from "path";
|
|
11810
12097
|
import { mkdir as mkdir2, readdir as readdir7, stat as stat2, rename, rm as rm2, writeFile as writeFile3 } from "fs/promises";
|
|
11811
12098
|
import { createInterface as createInterface2 } from "readline";
|
|
11812
12099
|
|
|
@@ -12528,7 +12815,7 @@ function loadCatalog() {
|
|
|
12528
12815
|
}
|
|
12529
12816
|
async function writeAgentsMd(projectPath, agentsMd) {
|
|
12530
12817
|
if (!agentsMd) return;
|
|
12531
|
-
const agentsPath =
|
|
12818
|
+
const agentsPath = join18(projectPath, "AGENTS.md");
|
|
12532
12819
|
if (existsSync12(agentsPath)) return;
|
|
12533
12820
|
await writeFile3(agentsPath, agentsMd, "utf-8");
|
|
12534
12821
|
}
|
|
@@ -12686,7 +12973,7 @@ async function* runCommandStreaming(cwd, command) {
|
|
|
12686
12973
|
}
|
|
12687
12974
|
}
|
|
12688
12975
|
async function* createScaffoldedProjectStreaming(options) {
|
|
12689
|
-
const projectPath =
|
|
12976
|
+
const projectPath = join18(options.parentDir, options.threadId);
|
|
12690
12977
|
if (!existsSync12(options.parentDir)) {
|
|
12691
12978
|
yield {
|
|
12692
12979
|
type: "result",
|
|
@@ -12776,7 +13063,7 @@ async function* createScaffoldedProjectStreaming(options) {
|
|
|
12776
13063
|
}
|
|
12777
13064
|
try {
|
|
12778
13065
|
const projectName2 = getProjectName(options.projectName);
|
|
12779
|
-
const projectSubDir =
|
|
13066
|
+
const projectSubDir = join18(projectPath, projectName2);
|
|
12780
13067
|
try {
|
|
12781
13068
|
const subDirStat = await stat2(projectSubDir);
|
|
12782
13069
|
if (subDirStat.isDirectory()) {
|
|
@@ -12786,8 +13073,8 @@ async function* createScaffoldedProjectStreaming(options) {
|
|
|
12786
13073
|
};
|
|
12787
13074
|
const items = await readdir7(projectSubDir);
|
|
12788
13075
|
for (const item of items) {
|
|
12789
|
-
const oldPath =
|
|
12790
|
-
const newPath =
|
|
13076
|
+
const oldPath = join18(projectSubDir, item);
|
|
13077
|
+
const newPath = join18(projectPath, item);
|
|
12791
13078
|
await rename(oldPath, newPath);
|
|
12792
13079
|
}
|
|
12793
13080
|
await rm2(projectSubDir, { recursive: true, force: true });
|
|
@@ -13255,7 +13542,7 @@ var ProjectCreator = class {
|
|
|
13255
13542
|
return name;
|
|
13256
13543
|
}
|
|
13257
13544
|
generateThreadPath(_projectId, threadId) {
|
|
13258
|
-
return
|
|
13545
|
+
return join19(this.rootFolder, threadId);
|
|
13259
13546
|
}
|
|
13260
13547
|
async *createProjectFromFolder() {
|
|
13261
13548
|
await loadUtils();
|
|
@@ -13405,19 +13692,19 @@ var ProjectCreator = class {
|
|
|
13405
13692
|
}
|
|
13406
13693
|
async findAllPackageJsonFiles(rootPath, currentPath, setupScripts) {
|
|
13407
13694
|
try {
|
|
13408
|
-
await access3(
|
|
13695
|
+
await access3(join19(currentPath, "package.json"));
|
|
13409
13696
|
const relativePath = relative5(rootPath, currentPath);
|
|
13410
13697
|
let installCommand = "";
|
|
13411
13698
|
try {
|
|
13412
|
-
await access3(
|
|
13699
|
+
await access3(join19(currentPath, "yarn.lock"));
|
|
13413
13700
|
installCommand = "yarn install";
|
|
13414
13701
|
} catch {
|
|
13415
13702
|
try {
|
|
13416
|
-
await access3(
|
|
13703
|
+
await access3(join19(currentPath, "bun.lock"));
|
|
13417
13704
|
installCommand = "bun install";
|
|
13418
13705
|
} catch {
|
|
13419
13706
|
try {
|
|
13420
|
-
await access3(
|
|
13707
|
+
await access3(join19(currentPath, "pnpm-lock.yaml"));
|
|
13421
13708
|
installCommand = "pnpm install";
|
|
13422
13709
|
} catch {
|
|
13423
13710
|
installCommand = "npm install";
|
|
@@ -13434,7 +13721,7 @@ var ProjectCreator = class {
|
|
|
13434
13721
|
try {
|
|
13435
13722
|
const entries = await readdir8(currentPath);
|
|
13436
13723
|
for (const entry of entries) {
|
|
13437
|
-
const entryPath =
|
|
13724
|
+
const entryPath = join19(currentPath, entry);
|
|
13438
13725
|
try {
|
|
13439
13726
|
const stats = await stat3(entryPath);
|
|
13440
13727
|
if (stats.isDirectory() && !entry.startsWith(".") && entry !== "node_modules") {
|
|
@@ -13774,7 +14061,7 @@ var ProjectManagerImpl = class {
|
|
|
13774
14061
|
const session = this.terminalSessionManager.getOrCreateSession(threadId, thread.path);
|
|
13775
14062
|
let workingDir;
|
|
13776
14063
|
if (cwd) {
|
|
13777
|
-
workingDir = cwd.startsWith("/") ? cwd :
|
|
14064
|
+
workingDir = cwd.startsWith("/") ? cwd : join20(thread.path, cwd);
|
|
13778
14065
|
this.terminalSessionManager.updateWorkingDirectory(threadId, workingDir);
|
|
13779
14066
|
} else {
|
|
13780
14067
|
workingDir = session.currentWorkingDirectory;
|
|
@@ -13977,80 +14264,59 @@ ___CWD___%s
|
|
|
13977
14264
|
// src/features/providers/providers.routes.ts
|
|
13978
14265
|
import { Hono as Hono7 } from "hono";
|
|
13979
14266
|
|
|
13980
|
-
// src/
|
|
13981
|
-
|
|
13982
|
-
|
|
13983
|
-
|
|
13984
|
-
const envPath = join20(process.cwd(), ".env");
|
|
13985
|
-
let content = "";
|
|
13986
|
-
try {
|
|
13987
|
-
content = await fs.readFile(envPath, "utf-8");
|
|
13988
|
-
} catch {
|
|
13989
|
-
}
|
|
13990
|
-
const lines = content.split("\n");
|
|
13991
|
-
const envMap = {};
|
|
13992
|
-
for (const line of lines) {
|
|
13993
|
-
const trimmed = line.trim();
|
|
13994
|
-
if (trimmed && !trimmed.startsWith("#") && trimmed.includes("=")) {
|
|
13995
|
-
const index = trimmed.indexOf("=");
|
|
13996
|
-
const key = trimmed.substring(0, index).trim();
|
|
13997
|
-
const value = trimmed.substring(index + 1).trim();
|
|
13998
|
-
envMap[key] = value;
|
|
13999
|
-
}
|
|
14000
|
-
}
|
|
14001
|
-
for (const [key, value] of Object.entries(keyNames)) {
|
|
14002
|
-
if (key) {
|
|
14003
|
-
envMap[key] = value;
|
|
14004
|
-
}
|
|
14005
|
-
}
|
|
14006
|
-
const newLines = [];
|
|
14007
|
-
for (const [key, value] of Object.entries(envMap)) {
|
|
14008
|
-
newLines.push(`${key}=${value}`);
|
|
14267
|
+
// src/features/providers/providers-get.route.ts
|
|
14268
|
+
function getProviderApiKeyEnvName2(providerEnv) {
|
|
14269
|
+
if (providerEnv.length === 0) {
|
|
14270
|
+
return "";
|
|
14009
14271
|
}
|
|
14010
|
-
|
|
14272
|
+
const explicitApiKeyEnv = providerEnv.find((envName) => /api_key$/i.test(envName));
|
|
14273
|
+
return explicitApiKeyEnv ?? providerEnv[providerEnv.length - 1];
|
|
14011
14274
|
}
|
|
14012
|
-
|
|
14013
|
-
const
|
|
14014
|
-
|
|
14015
|
-
|
|
14016
|
-
|
|
14017
|
-
|
|
14018
|
-
|
|
14019
|
-
|
|
14020
|
-
|
|
14021
|
-
const index = trimmed.indexOf("=");
|
|
14022
|
-
const key = trimmed.substring(0, index).trim();
|
|
14023
|
-
const value = trimmed.substring(index + 1).trim();
|
|
14024
|
-
envMap[key] = value;
|
|
14025
|
-
}
|
|
14026
|
-
}
|
|
14027
|
-
} catch {
|
|
14275
|
+
function getProviderResourceName2(providerEnv) {
|
|
14276
|
+
const explicitResourceEnv = providerEnv.find((envName) => /resource_name$/i.test(envName));
|
|
14277
|
+
return explicitResourceEnv ?? providerEnv[0] ?? "";
|
|
14278
|
+
}
|
|
14279
|
+
function resolveAzureApiUrl2(providerEnv, envValues) {
|
|
14280
|
+
const resourceEnvName = getProviderResourceName2(providerEnv);
|
|
14281
|
+
const resourceName = envValues[resourceEnvName] ?? "";
|
|
14282
|
+
if (!resourceName) {
|
|
14283
|
+
return "";
|
|
14028
14284
|
}
|
|
14029
|
-
return
|
|
14285
|
+
return `https://${resourceName}.openai.azure.com/openai/v1`;
|
|
14030
14286
|
}
|
|
14031
|
-
|
|
14032
|
-
// src/features/providers/providers-get.route.ts
|
|
14033
14287
|
async function getProviders(c, metadataManager) {
|
|
14034
14288
|
const keys = await metadataManager.getProviderKeys();
|
|
14289
|
+
const persistedEnvValues = await metadataManager.getProviderEnvValues();
|
|
14035
14290
|
const envFileKeys = await readEnvFile();
|
|
14036
14291
|
const catalog = await getModelsCatalog();
|
|
14037
14292
|
const providersWithKeys = [];
|
|
14038
14293
|
if (catalog) {
|
|
14039
14294
|
for (const [catalogId, catalogProvider] of Object.entries(catalog)) {
|
|
14040
|
-
const
|
|
14295
|
+
const envNames = catalogProvider.env ?? [];
|
|
14296
|
+
const keyName = getProviderApiKeyEnvName2(envNames);
|
|
14297
|
+
const envValues = {};
|
|
14298
|
+
for (const envName of envNames) {
|
|
14299
|
+
envValues[envName] = envFileKeys[envName] ?? process.env[envName] ?? persistedEnvValues[envName] ?? "";
|
|
14300
|
+
}
|
|
14041
14301
|
const envFileKey = keyName ? envFileKeys[keyName] : "";
|
|
14042
14302
|
const processEnvKey = keyName ? process.env[keyName] : "";
|
|
14043
14303
|
const storedKey = keys[catalogProvider.name] || keys[catalogId];
|
|
14044
14304
|
const hasEnvKey = Boolean(envFileKey || processEnvKey);
|
|
14045
14305
|
const resolvedKey = storedKey ?? envFileKey ?? processEnvKey ?? "";
|
|
14046
14306
|
const isEnv = !storedKey && hasEnvKey && resolvedKey !== "";
|
|
14307
|
+
const api = catalogId === "azure" ? resolveAzureApiUrl2(envNames, envValues) : catalogProvider.api ?? "";
|
|
14308
|
+
if (keyName && !envValues[keyName]) {
|
|
14309
|
+
envValues[keyName] = resolvedKey;
|
|
14310
|
+
}
|
|
14047
14311
|
providersWithKeys.push({
|
|
14048
14312
|
name: catalogProvider.name ?? catalogId,
|
|
14049
14313
|
slug: catalogId,
|
|
14050
14314
|
url: catalogProvider.doc ?? "",
|
|
14051
14315
|
keyName,
|
|
14052
14316
|
authType: "APIKey",
|
|
14053
|
-
api
|
|
14317
|
+
api,
|
|
14318
|
+
env: envNames,
|
|
14319
|
+
envValues,
|
|
14054
14320
|
apiKey: resolvedKey,
|
|
14055
14321
|
isEnv
|
|
14056
14322
|
});
|
|
@@ -14069,6 +14335,7 @@ async function postProviderKeys(c, metadataManager) {
|
|
|
14069
14335
|
await metadataManager.saveProviderKey(providerName, apiKey);
|
|
14070
14336
|
const keyName = await resolveProviderKeyName(providerName);
|
|
14071
14337
|
if (keyName) {
|
|
14338
|
+
updateRuntimeEnv({ [keyName]: apiKey });
|
|
14072
14339
|
await updateEnvFile({ [keyName]: apiKey });
|
|
14073
14340
|
}
|
|
14074
14341
|
return c.json({ success: true });
|
|
@@ -14093,6 +14360,7 @@ async function postProviderBulkKeys(c, metadataManager) {
|
|
|
14093
14360
|
}
|
|
14094
14361
|
}
|
|
14095
14362
|
if (Object.keys(envUpdates).length > 0) {
|
|
14363
|
+
updateRuntimeEnv(envUpdates);
|
|
14096
14364
|
await updateEnvFile(envUpdates);
|
|
14097
14365
|
}
|
|
14098
14366
|
return c.json({ success: true });
|
|
@@ -14101,6 +14369,28 @@ async function postProviderBulkKeys(c, metadataManager) {
|
|
|
14101
14369
|
}
|
|
14102
14370
|
}
|
|
14103
14371
|
|
|
14372
|
+
// src/features/providers/providers-post-env.route.ts
|
|
14373
|
+
async function postProviderEnv(c, metadataManager) {
|
|
14374
|
+
try {
|
|
14375
|
+
const { values } = await c.req.json();
|
|
14376
|
+
if (!values || typeof values !== "object") {
|
|
14377
|
+
return c.json({ error: "values object is required" }, 400);
|
|
14378
|
+
}
|
|
14379
|
+
const envValues = {};
|
|
14380
|
+
for (const [key, value] of Object.entries(values)) {
|
|
14381
|
+
if (typeof value === "string") {
|
|
14382
|
+
envValues[key] = value;
|
|
14383
|
+
}
|
|
14384
|
+
}
|
|
14385
|
+
await metadataManager.saveProviderEnvValues(envValues);
|
|
14386
|
+
updateRuntimeEnv(envValues);
|
|
14387
|
+
await updateEnvFile(envValues);
|
|
14388
|
+
return c.json({ success: true });
|
|
14389
|
+
} catch (error) {
|
|
14390
|
+
return c.json({ error: `Failed to save provider environment: ${String(error)}` }, 500);
|
|
14391
|
+
}
|
|
14392
|
+
}
|
|
14393
|
+
|
|
14104
14394
|
// src/features/models/model-info-openrouter.ts
|
|
14105
14395
|
async function getOpenRouterCredits(apiKey) {
|
|
14106
14396
|
try {
|
|
@@ -14196,13 +14486,24 @@ async function getProviderCredits(c, metadataManager) {
|
|
|
14196
14486
|
// src/features/providers/providers-get-logo.route.ts
|
|
14197
14487
|
var LOGO_BASE_URL = "https://models.tarsk.io/svg";
|
|
14198
14488
|
var SLUG_PATTERN = /^[a-z0-9._-]+$/i;
|
|
14489
|
+
var logoCache = /* @__PURE__ */ new Map();
|
|
14490
|
+
var LOGO_CACHE_TTL_MS = 15 * 60 * 1e3;
|
|
14199
14491
|
async function getProviderLogo(c) {
|
|
14200
14492
|
const slug = c.req.param("slug");
|
|
14201
14493
|
if (!slug || !SLUG_PATTERN.test(slug)) {
|
|
14202
14494
|
return c.body(null, 404);
|
|
14203
14495
|
}
|
|
14496
|
+
const cached = logoCache.get(slug);
|
|
14497
|
+
if (cached && Date.now() < cached.expires) {
|
|
14498
|
+
return c.body(cached.svg, 200, {
|
|
14499
|
+
"Content-Type": "image/svg+xml",
|
|
14500
|
+
"Cache-Control": "public, max-age=86400"
|
|
14501
|
+
});
|
|
14502
|
+
}
|
|
14204
14503
|
try {
|
|
14205
|
-
const response = await fetch(`${LOGO_BASE_URL}/${slug}.svg`, {
|
|
14504
|
+
const response = await fetch(`${LOGO_BASE_URL}/${slug}.svg`, {
|
|
14505
|
+
signal: AbortSignal.timeout(5e3)
|
|
14506
|
+
});
|
|
14206
14507
|
if (!response.ok) {
|
|
14207
14508
|
return c.body(null, 404);
|
|
14208
14509
|
}
|
|
@@ -14210,6 +14511,7 @@ async function getProviderLogo(c) {
|
|
|
14210
14511
|
if (!svg.includes("<svg")) {
|
|
14211
14512
|
return c.body(null, 404);
|
|
14212
14513
|
}
|
|
14514
|
+
logoCache.set(slug, { svg, expires: Date.now() + LOGO_CACHE_TTL_MS });
|
|
14213
14515
|
return c.body(svg, 200, {
|
|
14214
14516
|
"Content-Type": "image/svg+xml",
|
|
14215
14517
|
"Cache-Control": "public, max-age=86400"
|
|
@@ -14262,6 +14564,7 @@ function createProviderRoutes(metadataManager) {
|
|
|
14262
14564
|
router.get("/", (c) => getProviders(c, metadataManager));
|
|
14263
14565
|
router.post("/keys", (c) => postProviderKeys(c, metadataManager));
|
|
14264
14566
|
router.post("/bulk-keys", (c) => postProviderBulkKeys(c, metadataManager));
|
|
14567
|
+
router.post("/env", (c) => postProviderEnv(c, metadataManager));
|
|
14265
14568
|
router.get("/:name/credits", (c) => getProviderCredits(c, metadataManager));
|
|
14266
14569
|
router.get("/:slug/logo", (c) => getProviderLogo(c));
|
|
14267
14570
|
router.post("/open-external", (c) => openExternalUrl(c));
|
|
@@ -14512,7 +14815,7 @@ function createScaffoldRoutes(projectManager) {
|
|
|
14512
14815
|
|
|
14513
14816
|
// src/features/slash-commands/slash-commands.manager.ts
|
|
14514
14817
|
import { readdir as readdir9, readFile as readFile12, mkdir as mkdir3, writeFile as writeFile4, unlink as unlink2 } from "fs/promises";
|
|
14515
|
-
import { join as join21, basename as basename3, extname as extname4 } from "path";
|
|
14818
|
+
import { join as join21, basename as basename3, extname as extname4, relative as relative6 } from "path";
|
|
14516
14819
|
import { existsSync as existsSync13 } from "fs";
|
|
14517
14820
|
import { homedir as homedir7 } from "os";
|
|
14518
14821
|
function slugify(filename) {
|
|
@@ -14593,14 +14896,14 @@ var SlashCommandManager = class _SlashCommandManager {
|
|
|
14593
14896
|
const commands = /* @__PURE__ */ new Map();
|
|
14594
14897
|
const globalDir = getGlobalCommandsDir();
|
|
14595
14898
|
if (existsSync13(globalDir)) {
|
|
14596
|
-
const globalCommands = await this.loadCommandsFromDir(globalDir, "global");
|
|
14899
|
+
const globalCommands = await this.loadCommandsFromDir(globalDir, threadPath, "global");
|
|
14597
14900
|
for (const cmd of globalCommands) {
|
|
14598
14901
|
commands.set(cmd.name, cmd);
|
|
14599
14902
|
}
|
|
14600
14903
|
}
|
|
14601
14904
|
const projectDir = getProjectCommandsDir(threadPath);
|
|
14602
14905
|
if (existsSync13(projectDir)) {
|
|
14603
|
-
const projectCommands = await this.loadCommandsFromDir(projectDir, "project");
|
|
14906
|
+
const projectCommands = await this.loadCommandsFromDir(projectDir, threadPath, "project");
|
|
14604
14907
|
for (const cmd of projectCommands) {
|
|
14605
14908
|
if (!_SlashCommandManager.BUILT_IN_COMMANDS.has(cmd.name)) {
|
|
14606
14909
|
commands.set(cmd.name, cmd);
|
|
@@ -14617,7 +14920,7 @@ var SlashCommandManager = class _SlashCommandManager {
|
|
|
14617
14920
|
/**
|
|
14618
14921
|
* Load commands from a specific directory
|
|
14619
14922
|
*/
|
|
14620
|
-
async loadCommandsFromDir(dir, scope) {
|
|
14923
|
+
async loadCommandsFromDir(dir, threadPath, scope) {
|
|
14621
14924
|
const commands = [];
|
|
14622
14925
|
try {
|
|
14623
14926
|
const files = await readdir9(dir);
|
|
@@ -14634,7 +14937,7 @@ var SlashCommandManager = class _SlashCommandManager {
|
|
|
14634
14937
|
content,
|
|
14635
14938
|
metadata,
|
|
14636
14939
|
scope,
|
|
14637
|
-
filePath
|
|
14940
|
+
filePath: scope === "project" ? relative6(threadPath, filePath) : filePath
|
|
14638
14941
|
});
|
|
14639
14942
|
}
|
|
14640
14943
|
} catch (error) {
|
|
@@ -15855,7 +16158,7 @@ async function handleDeleteThread(c, threadManager) {
|
|
|
15855
16158
|
// src/core/project-inspector.ts
|
|
15856
16159
|
import { readFile as readFile13, readdir as readdir10 } from "fs/promises";
|
|
15857
16160
|
import { existsSync as existsSync14 } from "fs";
|
|
15858
|
-
import { join as join23, basename as basename4, relative as
|
|
16161
|
+
import { join as join23, basename as basename4, relative as relative7 } from "path";
|
|
15859
16162
|
import { glob as glob2 } from "glob";
|
|
15860
16163
|
|
|
15861
16164
|
// src/features/project-scripts/project-scripts.database.ts
|
|
@@ -16205,7 +16508,7 @@ function buildRunCommand(scriptName, workspace, packageManager, repoType, projec
|
|
|
16205
16508
|
return `npm run ${scriptName} --workspace=${workspace.relativePath}`;
|
|
16206
16509
|
}
|
|
16207
16510
|
if (!isRoot) {
|
|
16208
|
-
const relPath =
|
|
16511
|
+
const relPath = relative7(projectPath, workspace.folder);
|
|
16209
16512
|
return `cd ${relPath} && ${runCmd}`;
|
|
16210
16513
|
}
|
|
16211
16514
|
return runCmd;
|
|
@@ -16828,7 +17131,7 @@ async function handleOpenThread(c, threadManager) {
|
|
|
16828
17131
|
c,
|
|
16829
17132
|
ErrorCodes.OPEN_PROGRAM_ERROR,
|
|
16830
17133
|
`Unsupported program: ${program}`,
|
|
16831
|
-
|
|
17134
|
+
422
|
|
16832
17135
|
);
|
|
16833
17136
|
}
|
|
16834
17137
|
console.log(
|
|
@@ -16848,8 +17151,8 @@ async function handleOpenThread(c, threadManager) {
|
|
|
16848
17151
|
return errorResponse(
|
|
16849
17152
|
c,
|
|
16850
17153
|
ErrorCodes.OPEN_PROGRAM_ERROR,
|
|
16851
|
-
|
|
16852
|
-
|
|
17154
|
+
formatOpenError(program, openError),
|
|
17155
|
+
422
|
|
16853
17156
|
);
|
|
16854
17157
|
}
|
|
16855
17158
|
return successResponse(c, { success: true, message: `Thread opened in ${program}` });
|
|
@@ -16859,7 +17162,7 @@ async function handleOpenThread(c, threadManager) {
|
|
|
16859
17162
|
c,
|
|
16860
17163
|
ErrorCodes.OPEN_PROGRAM_ERROR,
|
|
16861
17164
|
"Failed to open thread",
|
|
16862
|
-
|
|
17165
|
+
422,
|
|
16863
17166
|
errorMessage
|
|
16864
17167
|
);
|
|
16865
17168
|
}
|
|
@@ -17964,7 +18267,7 @@ import { completeSimple } from "@mariozechner/pi-ai";
|
|
|
17964
18267
|
async function resolveModelAndKey(provider, modelId, metadataManager) {
|
|
17965
18268
|
const providerConfig = await resolveProviderConfig(provider);
|
|
17966
18269
|
if (!providerConfig) return null;
|
|
17967
|
-
let apiKey = providerConfig.
|
|
18270
|
+
let apiKey = providerConfig.apiKeyEnv ? process.env[providerConfig.apiKeyEnv] : void 0;
|
|
17968
18271
|
if (!apiKey) {
|
|
17969
18272
|
const providerKeys = await metadataManager.getProviderKeys();
|
|
17970
18273
|
apiKey = providerKeys[providerConfig.id];
|
|
@@ -19894,15 +20197,6 @@ async function gitSyncBranchHandler(c, metadataManager) {
|
|
|
19894
20197
|
400
|
|
19895
20198
|
);
|
|
19896
20199
|
}
|
|
19897
|
-
if (currentBranch === defaultBranch) {
|
|
19898
|
-
console.log(`[sync-branch] Already on default branch: ${defaultBranch}`);
|
|
19899
|
-
return c.json(
|
|
19900
|
-
{
|
|
19901
|
-
error: `Already on default branch (${defaultBranch})`
|
|
19902
|
-
},
|
|
19903
|
-
400
|
|
19904
|
-
);
|
|
19905
|
-
}
|
|
19906
20200
|
const { spawnProcess: spawnProcess2 } = await Promise.resolve().then(() => (init_utils(), utils_exports));
|
|
19907
20201
|
return new Promise((resolve6) => {
|
|
19908
20202
|
console.log(`[sync-branch] Checking branch status...`);
|
|
@@ -20038,13 +20332,12 @@ async function gitSyncBranchHandler(c, metadataManager) {
|
|
|
20038
20332
|
}
|
|
20039
20333
|
pullProc.on("close", (code) => {
|
|
20040
20334
|
if (code === 0) {
|
|
20041
|
-
|
|
20042
|
-
|
|
20043
|
-
);
|
|
20335
|
+
const message = currentBranch === defaultBranch ? `Successfully pulled latest changes from origin/${defaultBranch}` : `Successfully synced ${currentBranch} with ${defaultBranch}`;
|
|
20336
|
+
console.log(`[sync-branch] \u2713 ${message}`);
|
|
20044
20337
|
resolve6(
|
|
20045
20338
|
c.json({
|
|
20046
20339
|
success: true,
|
|
20047
|
-
message
|
|
20340
|
+
message,
|
|
20048
20341
|
branch: currentBranch,
|
|
20049
20342
|
defaultBranch
|
|
20050
20343
|
})
|
|
@@ -21204,7 +21497,7 @@ async function generateImage(c, metadataManager, conversationManager, threadMana
|
|
|
21204
21497
|
if (!providerConfig) {
|
|
21205
21498
|
return c.json({ error: `Unknown provider: ${provider}` }, 400);
|
|
21206
21499
|
}
|
|
21207
|
-
let apiKey = providerConfig.
|
|
21500
|
+
let apiKey = providerConfig.apiKeyEnv ? process.env[providerConfig.apiKeyEnv] : void 0;
|
|
21208
21501
|
if (!apiKey) {
|
|
21209
21502
|
const providerKeys = await metadataManager.getProviderKeys();
|
|
21210
21503
|
apiKey = providerKeys[providerConfig.id];
|
|
@@ -21631,6 +21924,7 @@ async function startTarskServer(options) {
|
|
|
21631
21924
|
const conversationManager = new ConversationManagerImpl(dataDir);
|
|
21632
21925
|
const agentExecutor = new PiExecutorImpl(metadataManager);
|
|
21633
21926
|
await metadataManager.initialize();
|
|
21927
|
+
updateRuntimeEnv(await metadataManager.getProviderEnvValues());
|
|
21634
21928
|
await conversationManager.initialize();
|
|
21635
21929
|
app.get("/health", (c) => {
|
|
21636
21930
|
return c.json({
|