tarsk 0.4.26 → 0.4.28
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 +634 -198
- package/dist/public/assets/account-view-DcpSNB3N.js +1 -0
- package/dist/public/assets/alert-dialog-CQRWdGnn.js +1 -0
- package/dist/public/assets/api-CH5gAPs5.js +1 -0
- package/dist/public/assets/{browser-tab-BNkQAd5u.js → browser-tab-CiAz6nc1.js} +1 -1
- package/dist/public/assets/chat-input-container-C6U_WFFu.js +21 -0
- package/dist/public/assets/context-menu-A9p22S7d.js +1 -0
- package/dist/public/assets/conversation-history-view-CD_uVRNY.js +1 -0
- package/dist/public/assets/{dialogs-config-BEsTbY1C.js → dialogs-config-vkfELnGl.js} +3 -3
- package/dist/public/assets/diff-view-CjbcDB6m.js +3 -0
- package/dist/public/assets/explorer-tab-view-BfycKUUW.js +2 -0
- package/dist/public/assets/explorer-tree-DTFYhczJ.js +1 -0
- package/dist/public/assets/explorer-view-C0KcZ8NQ.js +1 -0
- package/dist/public/assets/history-view-Dn0hQIKU.js +1 -0
- package/dist/public/assets/index-Bdde81Uo.css +1 -0
- package/dist/public/assets/index-DKQVo4Vz.js +29 -0
- package/dist/public/assets/markdown-renderer-CNuyXHcy.js +10 -0
- package/dist/public/assets/mcp-server-card-DBEBe3Qh.js +1 -0
- package/dist/public/assets/onboarding-DslO5AS6.js +1 -0
- package/dist/public/assets/onboarding-dialog-CE-GhoDt.js +1 -0
- package/dist/public/assets/{page-toolbar-DM-x5q1_.js → page-toolbar-CloU_Kvo.js} +1 -1
- package/dist/public/assets/{project-settings-view-wkrbmymL.js → project-settings-view-CxD9JZoB.js} +1 -1
- package/dist/public/assets/providers-list-view-CNxVBJlM.js +1 -0
- package/dist/public/assets/radio-group-Co0Lg6se.js +1 -0
- package/dist/public/assets/react-vendor-DyE9sO8V.js +22 -0
- package/dist/public/assets/resizable-dToeUeVD.js +1 -0
- package/dist/public/assets/{run-stop-button-CZ62jJDn.js → run-stop-button-DZ4JFwdL.js} +2 -2
- package/dist/public/assets/settings-instructions-view-DWzAH2yN.js +1 -0
- package/dist/public/assets/settings-mcp-servers-view-q6gGMMVX.js +5 -0
- package/dist/public/assets/settings-models-view-BDDFQWWC.js +1 -0
- package/dist/public/assets/settings-rules-view-DsEwgDFr.js +8 -0
- package/dist/public/assets/settings-skills-view-DV40_L1o.js +2 -0
- package/dist/public/assets/settings-slash-commands-view-BEk3BgOY.js +1 -0
- package/dist/public/assets/settings-subagents-view-d3P4ylDX.js +2 -0
- package/dist/public/assets/settings-view-ClTMPafQ.js +2 -0
- package/dist/public/assets/{side-panel-container-DVnN8vFb.js → side-panel-container-BAYoMt7B.js} +2 -2
- package/dist/public/assets/skeleton-DveUQR4w.js +1 -0
- package/dist/public/assets/standard-list-item-BLlgm1qW.js +1 -0
- package/dist/public/assets/store-BcwWHqYr.js +4 -0
- package/dist/public/assets/{tab-context-DBrXMyNd.js → tab-context-DWD20a4z.js} +1 -1
- package/dist/public/assets/tabs-CgvpwOBj.js +1 -0
- package/dist/public/assets/{terminal-panel-COYGYBV5.js → terminal-panel-CkIe2d2K.js} +2 -2
- package/dist/public/assets/textarea-C2UIAa7y.js +1 -0
- package/dist/public/assets/todos-view-C-awG1dZ.js +1 -0
- package/dist/public/assets/{use-toast-BqYP6xbg.js → use-toast-yY7qQuQP.js} +1 -1
- package/dist/public/assets/{utils-BavofGRh.js → utils-BKss67Vg.js} +1 -1
- package/dist/public/assets/whisper-wasm-B7iuRPFr.js +2 -0
- package/dist/public/ide/antigravity.svg +1 -0
- package/dist/public/ide/devin.svg +1 -0
- package/dist/public/index.html +22 -20
- package/dist/public/wasm/libmain-CWYJvMY5.js +3318 -0
- package/dist/public/wasm/libmain-D9-QM3iM.mjs +3301 -0
- package/package.json +4 -3
- package/dist/public/assets/account-view-g3bIVa-z.js +0 -1
- package/dist/public/assets/alert-dialog-DXEB8gMn.js +0 -1
- package/dist/public/assets/api-DQLZE-zX.js +0 -1
- package/dist/public/assets/chat-input-container-C8o4YcJZ.js +0 -1
- package/dist/public/assets/context-menu-CThafUml.js +0 -1
- package/dist/public/assets/conversation-history-view-64T6FDPb.js +0 -1
- package/dist/public/assets/diff-view-DgR-WEkB.js +0 -3
- package/dist/public/assets/explorer-tab-view-B53xe5xM.js +0 -2
- package/dist/public/assets/explorer-tree-KuYjR6r9.js +0 -1
- package/dist/public/assets/explorer-view-Cj6fyxMP.js +0 -1
- package/dist/public/assets/history-view-_L50vkTM.js +0 -1
- package/dist/public/assets/index-CAoVFXj2.css +0 -1
- package/dist/public/assets/index-Q5OqmtNF.js +0 -50
- package/dist/public/assets/onboarding-C_q_uRK0.js +0 -1
- package/dist/public/assets/onboarding-dialog-Cum5S2wE.js +0 -1
- package/dist/public/assets/provider-details-view-jc3yYdzr.js +0 -1
- package/dist/public/assets/providers-sidebar-B796hXzU.js +0 -1
- package/dist/public/assets/react-vendor-QkuITxN7.js +0 -17
- package/dist/public/assets/resizable-GT3fWOfA.js +0 -1
- package/dist/public/assets/settings-view-BbL7i34u.js +0 -2
- package/dist/public/assets/standard-list-item-ByRgCvKd.js +0 -1
- package/dist/public/assets/store-Dyy2UFdD.js +0 -2
- package/dist/public/assets/tabs-DxQZhlZp.js +0 -1
- package/dist/public/assets/textarea-CUYwgm5q.js +0 -1
- package/dist/public/assets/todos-view-DB99PEKH.js +0 -1
- /package/dist/public/assets/{dist-CIGnaqp2.js → dist-Bjt_i1zi.js} +0 -0
- /package/dist/public/assets/{monaco-c-HyCuEi.js → monaco-SWcefg0q.js} +0 -0
package/dist/index.js
CHANGED
|
@@ -249,7 +249,7 @@ async function initializeSchema(db) {
|
|
|
249
249
|
friendlyName TEXT NOT NULL,
|
|
250
250
|
updatedAt TEXT NOT NULL,
|
|
251
251
|
FOREIGN KEY (projectId) REFERENCES projects(id) ON DELETE CASCADE,
|
|
252
|
-
UNIQUE(projectId, name)
|
|
252
|
+
UNIQUE(projectId, workspace, name)
|
|
253
253
|
)
|
|
254
254
|
`);
|
|
255
255
|
await db.execute(`
|
|
@@ -364,8 +364,8 @@ async function runMigrations(db) {
|
|
|
364
364
|
WHERE type='table' AND name='conversation_history'
|
|
365
365
|
`);
|
|
366
366
|
if (tableInfo.rows.length > 0) {
|
|
367
|
-
const
|
|
368
|
-
if (!
|
|
367
|
+
const tableSql2 = tableInfo.rows[0].sql;
|
|
368
|
+
if (!tableSql2.includes("ON DELETE CASCADE")) {
|
|
369
369
|
console.log(
|
|
370
370
|
"[db] Running migration: Adding ON DELETE CASCADE to conversation_history.threadId foreign key"
|
|
371
371
|
);
|
|
@@ -444,8 +444,8 @@ async function runMigrations(db) {
|
|
|
444
444
|
WHERE type='table' AND name='threads'
|
|
445
445
|
`);
|
|
446
446
|
if (threadsTableInfo.rows.length > 0) {
|
|
447
|
-
const
|
|
448
|
-
if (!
|
|
447
|
+
const tableSql2 = threadsTableInfo.rows[0].sql;
|
|
448
|
+
if (!tableSql2.includes("ON DELETE CASCADE")) {
|
|
449
449
|
console.log(
|
|
450
450
|
"[db] Running migration: Adding ON DELETE CASCADE to threads.projectId foreign key"
|
|
451
451
|
);
|
|
@@ -680,7 +680,31 @@ async function runMigrations(db) {
|
|
|
680
680
|
friendlyName TEXT NOT NULL,
|
|
681
681
|
updatedAt TEXT NOT NULL,
|
|
682
682
|
FOREIGN KEY (projectId) REFERENCES projects(id) ON DELETE CASCADE,
|
|
683
|
-
UNIQUE(projectId, name)
|
|
683
|
+
UNIQUE(projectId, workspace, name)
|
|
684
|
+
)
|
|
685
|
+
`);
|
|
686
|
+
await db.execute(`
|
|
687
|
+
CREATE INDEX idx_project_scripts_projectId ON project_scripts(projectId)
|
|
688
|
+
`);
|
|
689
|
+
}
|
|
690
|
+
const projectScriptsInfo = await db.execute(
|
|
691
|
+
`SELECT sql FROM sqlite_master WHERE type='table' AND name='project_scripts'`
|
|
692
|
+
);
|
|
693
|
+
const tableSql = projectScriptsInfo.rows[0];
|
|
694
|
+
if (projectScriptsInfo.rows.length > 0 && typeof tableSql["sql"] === "string" && tableSql["sql"].includes('UNIQUE("projectId", "name")')) {
|
|
695
|
+
console.log("[db] Running migration: Fixing project_scripts UNIQUE constraint");
|
|
696
|
+
await db.execute(`DROP TABLE IF EXISTS project_scripts`);
|
|
697
|
+
await db.execute(`
|
|
698
|
+
CREATE TABLE project_scripts (
|
|
699
|
+
id TEXT PRIMARY KEY,
|
|
700
|
+
projectId TEXT NOT NULL,
|
|
701
|
+
workspace TEXT NOT NULL,
|
|
702
|
+
name TEXT NOT NULL,
|
|
703
|
+
command TEXT NOT NULL,
|
|
704
|
+
friendlyName TEXT NOT NULL,
|
|
705
|
+
updatedAt TEXT NOT NULL,
|
|
706
|
+
FOREIGN KEY (projectId) REFERENCES projects(id) ON DELETE CASCADE,
|
|
707
|
+
UNIQUE(projectId, workspace, name)
|
|
684
708
|
)
|
|
685
709
|
`);
|
|
686
710
|
await db.execute(`
|
|
@@ -745,6 +769,14 @@ var PROGRAMS_CONFIG = {
|
|
|
745
769
|
Terminal: {
|
|
746
770
|
name: "Terminal",
|
|
747
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"]
|
|
748
780
|
}
|
|
749
781
|
};
|
|
750
782
|
var AVAILABLE_PROGRAMS = Object.keys(PROGRAMS_CONFIG);
|
|
@@ -792,7 +824,7 @@ function isValidPackageManager(value) {
|
|
|
792
824
|
|
|
793
825
|
// src/server.ts
|
|
794
826
|
import fs3 from "fs";
|
|
795
|
-
import { Hono as
|
|
827
|
+
import { Hono as Hono23 } from "hono";
|
|
796
828
|
import { cors } from "hono/cors";
|
|
797
829
|
import open3 from "open";
|
|
798
830
|
import path4 from "path";
|
|
@@ -1796,9 +1828,9 @@ function createGrepTool(cwd, options) {
|
|
|
1796
1828
|
const effectiveLimit = Math.max(1, limit ?? DEFAULT_LIMIT2);
|
|
1797
1829
|
const formatPath = (filePath) => {
|
|
1798
1830
|
if (isDirectory) {
|
|
1799
|
-
const
|
|
1800
|
-
if (
|
|
1801
|
-
return
|
|
1831
|
+
const relative8 = path2.relative(searchPath, filePath);
|
|
1832
|
+
if (relative8 && !relative8.startsWith("..")) {
|
|
1833
|
+
return relative8.replace(/\\/g, "/");
|
|
1802
1834
|
}
|
|
1803
1835
|
}
|
|
1804
1836
|
return path2.basename(filePath);
|
|
@@ -4116,16 +4148,56 @@ var ModelManager = class {
|
|
|
4116
4148
|
};
|
|
4117
4149
|
|
|
4118
4150
|
// src/features/providers/provider-resolver.ts
|
|
4151
|
+
function getProviderApiKeyEnvName(providerEnv) {
|
|
4152
|
+
if (providerEnv.length === 0) {
|
|
4153
|
+
return "";
|
|
4154
|
+
}
|
|
4155
|
+
const explicitApiKeyEnv = providerEnv.find((envName) => /api_key$/i.test(envName));
|
|
4156
|
+
return explicitApiKeyEnv ?? providerEnv[providerEnv.length - 1];
|
|
4157
|
+
}
|
|
4158
|
+
function getProviderApiUrlEnvName(providerName) {
|
|
4159
|
+
return `${providerName.toUpperCase().replace(/-/g, "_")}_API_URL`;
|
|
4160
|
+
}
|
|
4161
|
+
function getProviderResourceName(providerEnv) {
|
|
4162
|
+
const explicitResourceEnv = providerEnv.find((envName) => /resource_name$/i.test(envName));
|
|
4163
|
+
return explicitResourceEnv ?? providerEnv[0] ?? "";
|
|
4164
|
+
}
|
|
4165
|
+
function resolveAzureApiUrl(providerEnv) {
|
|
4166
|
+
const resourceEnvName = getProviderResourceName(providerEnv);
|
|
4167
|
+
const resourceName = process.env[resourceEnvName];
|
|
4168
|
+
if (!resourceName) {
|
|
4169
|
+
return "";
|
|
4170
|
+
}
|
|
4171
|
+
return `https://${resourceName}.openai.azure.com/openai/v1`;
|
|
4172
|
+
}
|
|
4173
|
+
function resolveTemplate(template, envValues) {
|
|
4174
|
+
let hasMissingValue = false;
|
|
4175
|
+
const resolved = template.replace(/\$\{([A-Z0-9_]+)\}/gi, (_match, envName) => {
|
|
4176
|
+
const value = envValues[envName] ?? process.env[envName] ?? "";
|
|
4177
|
+
if (!value) {
|
|
4178
|
+
hasMissingValue = true;
|
|
4179
|
+
}
|
|
4180
|
+
return value;
|
|
4181
|
+
});
|
|
4182
|
+
return hasMissingValue ? "" : resolved;
|
|
4183
|
+
}
|
|
4119
4184
|
async function resolveProviderConfig(providerName) {
|
|
4120
4185
|
const slug = providerName.toLowerCase();
|
|
4121
4186
|
const catalogProvider = await getCatalogProvider(slug);
|
|
4122
4187
|
if (!catalogProvider) return null;
|
|
4123
|
-
|
|
4188
|
+
const apiKeyEnv = getProviderApiKeyEnvName(catalogProvider.env ?? []);
|
|
4189
|
+
const apiUrlEnvName = getProviderApiUrlEnvName(slug);
|
|
4190
|
+
const api = slug === "azure" ? resolveAzureApiUrl(catalogProvider.env ?? []) : process.env[apiUrlEnvName] || (catalogProvider.api ? resolveTemplate(catalogProvider.api, process.env) : "");
|
|
4191
|
+
return {
|
|
4192
|
+
...catalogProvider,
|
|
4193
|
+
api,
|
|
4194
|
+
apiKeyEnv
|
|
4195
|
+
};
|
|
4124
4196
|
}
|
|
4125
4197
|
async function resolveProviderKeyName(providerName) {
|
|
4126
4198
|
const slug = providerName.toLowerCase();
|
|
4127
4199
|
const catalogProvider = await getCatalogProvider(slug);
|
|
4128
|
-
if (catalogProvider?.env?.
|
|
4200
|
+
if (catalogProvider?.env?.length) return getProviderApiKeyEnvName(catalogProvider.env);
|
|
4129
4201
|
return "";
|
|
4130
4202
|
}
|
|
4131
4203
|
|
|
@@ -4322,7 +4394,7 @@ function createGenerateImageTool(options) {
|
|
|
4322
4394
|
details: void 0
|
|
4323
4395
|
};
|
|
4324
4396
|
}
|
|
4325
|
-
let apiKey = providerConfig.
|
|
4397
|
+
let apiKey = providerConfig.apiKeyEnv ? process.env[providerConfig.apiKeyEnv] : void 0;
|
|
4326
4398
|
if (!apiKey) {
|
|
4327
4399
|
const providerKeys = await metadataManager.getProviderKeys();
|
|
4328
4400
|
apiKey = providerKeys[providerConfig.id];
|
|
@@ -4760,8 +4832,12 @@ function createToolSearchTool(options) {
|
|
|
4760
4832
|
let matched = [];
|
|
4761
4833
|
if (parsed.mode === "select") {
|
|
4762
4834
|
for (const name of parsed.names) {
|
|
4763
|
-
const tool
|
|
4764
|
-
|
|
4835
|
+
for (const [toolName, tool] of deferredTools.entries()) {
|
|
4836
|
+
if (toolName.toLowerCase() === name) {
|
|
4837
|
+
matched.push(tool);
|
|
4838
|
+
break;
|
|
4839
|
+
}
|
|
4840
|
+
}
|
|
4765
4841
|
}
|
|
4766
4842
|
} else {
|
|
4767
4843
|
const candidates = [];
|
|
@@ -5015,6 +5091,7 @@ var EventQueue = class {
|
|
|
5015
5091
|
finalContent = "";
|
|
5016
5092
|
errorOccurred = false;
|
|
5017
5093
|
toolCallCount = 0;
|
|
5094
|
+
lastErrorMessage;
|
|
5018
5095
|
/**
|
|
5019
5096
|
* Processes Pi Agent events and transforms them into AgentEvent format
|
|
5020
5097
|
*/
|
|
@@ -5083,6 +5160,9 @@ var EventQueue = class {
|
|
|
5083
5160
|
} else if (event.type === "agent_end") {
|
|
5084
5161
|
const messages = event.messages;
|
|
5085
5162
|
const lastAssistant = [...messages].reverse().find((m) => "role" in m && m.role === "assistant");
|
|
5163
|
+
if (lastAssistant && "errorMessage" in lastAssistant && typeof lastAssistant.errorMessage === "string") {
|
|
5164
|
+
this.lastErrorMessage = lastAssistant.errorMessage;
|
|
5165
|
+
}
|
|
5086
5166
|
if (lastAssistant) {
|
|
5087
5167
|
const extracted = extractTextFromAssistantMessage(lastAssistant);
|
|
5088
5168
|
if (extracted && extracted !== this.finalContent) {
|
|
@@ -5196,14 +5276,14 @@ async function executeSubagent(options) {
|
|
|
5196
5276
|
if (!providerConfig) {
|
|
5197
5277
|
throw new Error(`Unknown provider for subagent: ${providerName}`);
|
|
5198
5278
|
}
|
|
5199
|
-
let apiKey = providerConfig.
|
|
5279
|
+
let apiKey = providerConfig.apiKeyEnv ? process.env[providerConfig.apiKeyEnv] : void 0;
|
|
5200
5280
|
if (!apiKey) {
|
|
5201
5281
|
const providerKeys = await metadataManager.getProviderKeys();
|
|
5202
5282
|
apiKey = providerKeys[providerConfig.id];
|
|
5203
5283
|
}
|
|
5204
5284
|
if (!apiKey) {
|
|
5205
5285
|
throw new Error(
|
|
5206
|
-
`No API key found for subagent provider: ${providerName}. Set ${providerConfig.
|
|
5286
|
+
`No API key found for subagent provider: ${providerName}. Set ${providerConfig.apiKeyEnv ?? "an API key"} in environment variables or configuration.`
|
|
5207
5287
|
);
|
|
5208
5288
|
}
|
|
5209
5289
|
const resolvedModel = resolveModel(providerName, model, providerConfig);
|
|
@@ -6091,10 +6171,94 @@ async function loadAgentSystemPrompt(threadPath, tools, skills, planMode, agents
|
|
|
6091
6171
|
var cachedStreamFn = (model, context, options) => {
|
|
6092
6172
|
return streamSimple(model, context, { ...options, cacheRetention: "long" });
|
|
6093
6173
|
};
|
|
6174
|
+
function summarizeAssistantResponse(response) {
|
|
6175
|
+
const lines = response.split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
|
|
6176
|
+
const salient = lines.filter(
|
|
6177
|
+
(line) => /^(#+\s|[-*]\s|\d+\.\s|```|changed|created|updated|deleted|fixed|implemented|added|removed|renamed|moved|tested|next|summary|result)/i.test(
|
|
6178
|
+
line
|
|
6179
|
+
) || line.length <= 180
|
|
6180
|
+
);
|
|
6181
|
+
const summary = (salient.length > 0 ? salient : lines).slice(0, 12).join("\n");
|
|
6182
|
+
return summary.length > 3e3 ? `${summary.slice(0, 3e3)}\u2026` : summary;
|
|
6183
|
+
}
|
|
6184
|
+
function getAssistantHistoryContent(exchange, isMostRecent) {
|
|
6185
|
+
if (isMostRecent) {
|
|
6186
|
+
return exchange.assistantResponse;
|
|
6187
|
+
}
|
|
6188
|
+
return exchange.assistantSummary ?? summarizeAssistantResponse(exchange.assistantResponse);
|
|
6189
|
+
}
|
|
6190
|
+
function buildAssistantHistoryText(content, isMostRecent) {
|
|
6191
|
+
if (isMostRecent) {
|
|
6192
|
+
return content;
|
|
6193
|
+
}
|
|
6194
|
+
return `[Earlier assistant response summarized to reduce context tokens]
|
|
6195
|
+
${content}`;
|
|
6196
|
+
}
|
|
6197
|
+
function serializeLogValue(value, depth = 0, seen = /* @__PURE__ */ new WeakSet()) {
|
|
6198
|
+
if (value === null || value === void 0) {
|
|
6199
|
+
return value;
|
|
6200
|
+
}
|
|
6201
|
+
const valueType = typeof value;
|
|
6202
|
+
if (valueType === "string" || valueType === "number" || valueType === "boolean") {
|
|
6203
|
+
return value;
|
|
6204
|
+
}
|
|
6205
|
+
if (valueType === "bigint" || valueType === "symbol") {
|
|
6206
|
+
return "[Primitive]";
|
|
6207
|
+
}
|
|
6208
|
+
if (valueType === "function") {
|
|
6209
|
+
return "[Function]";
|
|
6210
|
+
}
|
|
6211
|
+
if (value instanceof Error) {
|
|
6212
|
+
const serializedError = {
|
|
6213
|
+
name: value.name,
|
|
6214
|
+
message: value.message
|
|
6215
|
+
};
|
|
6216
|
+
if (value.stack) {
|
|
6217
|
+
serializedError.stack = value.stack;
|
|
6218
|
+
}
|
|
6219
|
+
if ("cause" in value) {
|
|
6220
|
+
serializedError.cause = serializeLogValue(
|
|
6221
|
+
value.cause,
|
|
6222
|
+
depth + 1,
|
|
6223
|
+
seen
|
|
6224
|
+
);
|
|
6225
|
+
}
|
|
6226
|
+
const errorObject = value;
|
|
6227
|
+
for (const [key, nestedValue] of Object.entries(errorObject)) {
|
|
6228
|
+
if (key in serializedError) continue;
|
|
6229
|
+
serializedError[key] = serializeLogValue(nestedValue, depth + 1, seen);
|
|
6230
|
+
}
|
|
6231
|
+
return serializedError;
|
|
6232
|
+
}
|
|
6233
|
+
if (Array.isArray(value)) {
|
|
6234
|
+
if (depth >= 4) {
|
|
6235
|
+
return "[Truncated]";
|
|
6236
|
+
}
|
|
6237
|
+
return value.slice(0, 50).map((item) => serializeLogValue(item, depth + 1, seen));
|
|
6238
|
+
}
|
|
6239
|
+
if (valueType === "object") {
|
|
6240
|
+
if (seen.has(value)) {
|
|
6241
|
+
return "[Circular]";
|
|
6242
|
+
}
|
|
6243
|
+
if (depth >= 4) {
|
|
6244
|
+
return "[Truncated]";
|
|
6245
|
+
}
|
|
6246
|
+
seen.add(value);
|
|
6247
|
+
const serializedObject = {};
|
|
6248
|
+
const plainObject = value;
|
|
6249
|
+
for (const [key, nestedValue] of Object.entries(plainObject).slice(0, 50)) {
|
|
6250
|
+
serializedObject[key] = serializeLogValue(nestedValue, depth + 1, seen);
|
|
6251
|
+
}
|
|
6252
|
+
return serializedObject;
|
|
6253
|
+
}
|
|
6254
|
+
return "[Unsupported value]";
|
|
6255
|
+
}
|
|
6094
6256
|
function buildHistoryMessages(history) {
|
|
6095
6257
|
const messages = [];
|
|
6096
6258
|
const now = Date.now();
|
|
6097
|
-
|
|
6259
|
+
history.forEach((exchange, index) => {
|
|
6260
|
+
const isMostRecent = index === history.length - 1;
|
|
6261
|
+
const assistantContent = getAssistantHistoryContent(exchange, isMostRecent);
|
|
6098
6262
|
messages.push({
|
|
6099
6263
|
role: "user",
|
|
6100
6264
|
content: exchange.userMessage,
|
|
@@ -6102,7 +6266,9 @@ function buildHistoryMessages(history) {
|
|
|
6102
6266
|
});
|
|
6103
6267
|
messages.push({
|
|
6104
6268
|
role: "assistant",
|
|
6105
|
-
content: [
|
|
6269
|
+
content: [
|
|
6270
|
+
{ type: "text", text: buildAssistantHistoryText(assistantContent, isMostRecent) }
|
|
6271
|
+
],
|
|
6106
6272
|
api: "",
|
|
6107
6273
|
provider: "",
|
|
6108
6274
|
model: "",
|
|
@@ -6117,7 +6283,7 @@ function buildHistoryMessages(history) {
|
|
|
6117
6283
|
stopReason: "stop",
|
|
6118
6284
|
timestamp: now
|
|
6119
6285
|
});
|
|
6120
|
-
}
|
|
6286
|
+
});
|
|
6121
6287
|
return messages;
|
|
6122
6288
|
}
|
|
6123
6289
|
var PiExecutorImpl = class {
|
|
@@ -6147,7 +6313,7 @@ var PiExecutorImpl = class {
|
|
|
6147
6313
|
if (!providerConfig) {
|
|
6148
6314
|
throw new Error(`Unknown provider: ${providerName}`);
|
|
6149
6315
|
}
|
|
6150
|
-
let apiKey = providerConfig.
|
|
6316
|
+
let apiKey = providerConfig.apiKeyEnv ? process.env[providerConfig.apiKeyEnv] : void 0;
|
|
6151
6317
|
let apiKeySource = "environment";
|
|
6152
6318
|
if (!apiKey) {
|
|
6153
6319
|
try {
|
|
@@ -6163,7 +6329,7 @@ var PiExecutorImpl = class {
|
|
|
6163
6329
|
}
|
|
6164
6330
|
if (!apiKey) {
|
|
6165
6331
|
throw new Error(
|
|
6166
|
-
`No API key found for provider: ${providerName}. Set ${providerConfig.
|
|
6332
|
+
`No API key found for provider: ${providerName}. Set ${providerConfig.apiKeyEnv ?? "an API key"} in environment variables or configuration.`
|
|
6167
6333
|
);
|
|
6168
6334
|
}
|
|
6169
6335
|
if (apiKey.includes(":") && apiKey.length > 100) {
|
|
@@ -6278,15 +6444,27 @@ ${decoded}
|
|
|
6278
6444
|
|
|
6279
6445
|
${userPrompt}`;
|
|
6280
6446
|
}
|
|
6281
|
-
|
|
6282
|
-
|
|
6283
|
-
|
|
6447
|
+
const promptRequest = {
|
|
6448
|
+
provider: providerName,
|
|
6449
|
+
model,
|
|
6450
|
+
cwd,
|
|
6451
|
+
prompt: finalPrompt,
|
|
6452
|
+
promptLength: finalPrompt.length,
|
|
6453
|
+
promptPreview: finalPrompt.substring(0, 200) + (finalPrompt.length > 200 ? "..." : ""),
|
|
6454
|
+
imageCount: images.length,
|
|
6455
|
+
images: images.map((img) => ({
|
|
6456
|
+
mimeType: img.mimeType,
|
|
6457
|
+
dataLength: img.data?.length ?? 0
|
|
6458
|
+
})),
|
|
6459
|
+
attachments: (context.attachments ?? []).map((a) => ({
|
|
6284
6460
|
name: a.name,
|
|
6285
6461
|
mimeType: a.mimeType,
|
|
6286
6462
|
contentLength: a.content?.length ?? 0
|
|
6287
|
-
}))
|
|
6288
|
-
|
|
6289
|
-
|
|
6463
|
+
}))
|
|
6464
|
+
};
|
|
6465
|
+
console.log(
|
|
6466
|
+
`[ai] Attachments: ${context.attachments?.length ?? 0} total, ${images.length} image(s), ${textAttachments.length} text file(s)`,
|
|
6467
|
+
promptRequest
|
|
6290
6468
|
);
|
|
6291
6469
|
const promptDone = agent.prompt(finalPrompt, images.length > 0 ? images : void 0).then(() => {
|
|
6292
6470
|
console.log(`[ai] Agent prompt completed successfully`);
|
|
@@ -6295,7 +6473,8 @@ ${userPrompt}`;
|
|
|
6295
6473
|
}).catch((err) => {
|
|
6296
6474
|
const errMessage = err instanceof Error ? err.message : String(err);
|
|
6297
6475
|
console.error(`[ai] Agent prompt failed:`, {
|
|
6298
|
-
|
|
6476
|
+
request: promptRequest,
|
|
6477
|
+
error: serializeLogValue(err),
|
|
6299
6478
|
errorType: err instanceof Error ? err.constructor.name : typeof err
|
|
6300
6479
|
});
|
|
6301
6480
|
if (!eventQueue.errorOccurred) {
|
|
@@ -6349,12 +6528,20 @@ ${userPrompt}`;
|
|
|
6349
6528
|
};
|
|
6350
6529
|
} else {
|
|
6351
6530
|
console.log("[ai] \u26A0\uFE0F Agent ended with no text content and no tool calls.");
|
|
6531
|
+
const upstreamError = eventQueue.lastErrorMessage ? { message: eventQueue.lastErrorMessage } : void 0;
|
|
6532
|
+
console.error("[ai] Agent ended without text content:", {
|
|
6533
|
+
request: promptRequest,
|
|
6534
|
+
upstreamError,
|
|
6535
|
+
finalContentLength: eventQueue.finalContent.length,
|
|
6536
|
+
toolCallCount: eventQueue.toolCallCount
|
|
6537
|
+
});
|
|
6352
6538
|
yield {
|
|
6353
6539
|
type: "error",
|
|
6354
6540
|
content: `The model ${model} from ${providerName} did not provide a response to your prompt. Check your API key and balance and try again.`,
|
|
6355
6541
|
error: {
|
|
6356
6542
|
code: "NO_CONTENT_NO_TOOLS",
|
|
6357
|
-
message: `The model ${model} from ${providerName} did not provide a response to your prompt
|
|
6543
|
+
message: `The model ${model} from ${providerName} did not provide a response to your prompt.`,
|
|
6544
|
+
details: upstreamError
|
|
6358
6545
|
}
|
|
6359
6546
|
};
|
|
6360
6547
|
}
|
|
@@ -6461,6 +6648,63 @@ var ProcessingStateManagerImpl = class {
|
|
|
6461
6648
|
}
|
|
6462
6649
|
};
|
|
6463
6650
|
|
|
6651
|
+
// src/core/env-manager.ts
|
|
6652
|
+
import { promises as fs } from "fs";
|
|
6653
|
+
import { join as join11 } from "path";
|
|
6654
|
+
function updateRuntimeEnv(values) {
|
|
6655
|
+
for (const [key, value] of Object.entries(values)) {
|
|
6656
|
+
process.env[key] = value;
|
|
6657
|
+
}
|
|
6658
|
+
}
|
|
6659
|
+
async function updateEnvFile(keyNames) {
|
|
6660
|
+
const envPath = join11(process.cwd(), ".env");
|
|
6661
|
+
let content = "";
|
|
6662
|
+
try {
|
|
6663
|
+
content = await fs.readFile(envPath, "utf-8");
|
|
6664
|
+
} catch {
|
|
6665
|
+
}
|
|
6666
|
+
const lines = content.split("\n");
|
|
6667
|
+
const envMap = {};
|
|
6668
|
+
for (const line of lines) {
|
|
6669
|
+
const trimmed = line.trim();
|
|
6670
|
+
if (trimmed && !trimmed.startsWith("#") && trimmed.includes("=")) {
|
|
6671
|
+
const index = trimmed.indexOf("=");
|
|
6672
|
+
const key = trimmed.substring(0, index).trim();
|
|
6673
|
+
const value = trimmed.substring(index + 1).trim();
|
|
6674
|
+
envMap[key] = value;
|
|
6675
|
+
}
|
|
6676
|
+
}
|
|
6677
|
+
for (const [key, value] of Object.entries(keyNames)) {
|
|
6678
|
+
if (key) {
|
|
6679
|
+
envMap[key] = value;
|
|
6680
|
+
}
|
|
6681
|
+
}
|
|
6682
|
+
const newLines = [];
|
|
6683
|
+
for (const [key, value] of Object.entries(envMap)) {
|
|
6684
|
+
newLines.push(`${key}=${value}`);
|
|
6685
|
+
}
|
|
6686
|
+
await fs.writeFile(envPath, newLines.join("\n") + "\n", "utf-8");
|
|
6687
|
+
}
|
|
6688
|
+
async function readEnvFile() {
|
|
6689
|
+
const envPath = join11(process.cwd(), ".env");
|
|
6690
|
+
const envMap = {};
|
|
6691
|
+
try {
|
|
6692
|
+
const content = await fs.readFile(envPath, "utf-8");
|
|
6693
|
+
const lines = content.split("\n");
|
|
6694
|
+
for (const line of lines) {
|
|
6695
|
+
const trimmed = line.trim();
|
|
6696
|
+
if (trimmed && !trimmed.startsWith("#") && trimmed.includes("=")) {
|
|
6697
|
+
const index = trimmed.indexOf("=");
|
|
6698
|
+
const key = trimmed.substring(0, index).trim();
|
|
6699
|
+
const value = trimmed.substring(index + 1).trim();
|
|
6700
|
+
envMap[key] = value;
|
|
6701
|
+
}
|
|
6702
|
+
}
|
|
6703
|
+
} catch {
|
|
6704
|
+
}
|
|
6705
|
+
return envMap;
|
|
6706
|
+
}
|
|
6707
|
+
|
|
6464
6708
|
// src/features/ask-user/ask-user.routes.ts
|
|
6465
6709
|
import { Hono } from "hono";
|
|
6466
6710
|
|
|
@@ -6577,7 +6821,8 @@ var ErrorCodes = {
|
|
|
6577
6821
|
// Git operation errors
|
|
6578
6822
|
DUPLICATE_BRANCH: "DUPLICATE_BRANCH",
|
|
6579
6823
|
// General server errors
|
|
6580
|
-
INTERNAL_ERROR: "INTERNAL_ERROR"
|
|
6824
|
+
INTERNAL_ERROR: "INTERNAL_ERROR",
|
|
6825
|
+
EXTERNAL_API_ERROR: "EXTERNAL_API_ERROR"
|
|
6581
6826
|
};
|
|
6582
6827
|
|
|
6583
6828
|
// src/features/ask-user/ask-user.routes.ts
|
|
@@ -6634,7 +6879,7 @@ import { randomUUID as randomUUID4 } from "crypto";
|
|
|
6634
6879
|
|
|
6635
6880
|
// src/features/skills/skills.manager.ts
|
|
6636
6881
|
import { readdir as readdir4, readFile as readFile6 } from "fs/promises";
|
|
6637
|
-
import { join as
|
|
6882
|
+
import { join as join12 } from "path";
|
|
6638
6883
|
import { existsSync as existsSync9 } from "fs";
|
|
6639
6884
|
import { homedir as homedir5 } from "os";
|
|
6640
6885
|
function parseFrontmatter(markdown) {
|
|
@@ -6682,10 +6927,10 @@ function parseFrontmatter(markdown) {
|
|
|
6682
6927
|
return { metadata, content };
|
|
6683
6928
|
}
|
|
6684
6929
|
function getGlobalSkillsDir() {
|
|
6685
|
-
return
|
|
6930
|
+
return join12(homedir5(), ".agents", "skills");
|
|
6686
6931
|
}
|
|
6687
6932
|
function getProjectSkillsDir(threadPath) {
|
|
6688
|
-
return
|
|
6933
|
+
return join12(threadPath, ".agents", "skills");
|
|
6689
6934
|
}
|
|
6690
6935
|
function validateSkillName(name) {
|
|
6691
6936
|
if (!name || name.length === 0 || name.length > 64) {
|
|
@@ -6738,8 +6983,8 @@ var SkillManager = class {
|
|
|
6738
6983
|
for (const entry of entries) {
|
|
6739
6984
|
if (!entry.isDirectory()) continue;
|
|
6740
6985
|
const skillDirName = entry.name;
|
|
6741
|
-
const skillPath =
|
|
6742
|
-
const skillFilePath =
|
|
6986
|
+
const skillPath = join12(dir, skillDirName);
|
|
6987
|
+
const skillFilePath = join12(skillPath, "SKILL.md");
|
|
6743
6988
|
if (!existsSync9(skillFilePath)) {
|
|
6744
6989
|
console.warn(`Skipping skill directory ${skillDirName}: SKILL.md not found`);
|
|
6745
6990
|
continue;
|
|
@@ -6919,7 +7164,7 @@ async function activateSkills(allSkills, taskDescription, thread, options) {
|
|
|
6919
7164
|
|
|
6920
7165
|
// src/features/agents/agents.manager.ts
|
|
6921
7166
|
import { readdir as readdir5, readFile as readFile7 } from "fs/promises";
|
|
6922
|
-
import { join as
|
|
7167
|
+
import { join as join13 } from "path";
|
|
6923
7168
|
import { existsSync as existsSync10 } from "fs";
|
|
6924
7169
|
import { homedir as homedir6 } from "os";
|
|
6925
7170
|
function parseFrontmatter2(markdown) {
|
|
@@ -6977,10 +7222,10 @@ function validateDescription2(description) {
|
|
|
6977
7222
|
return !!description && description.length > 0 && description.length <= 1024;
|
|
6978
7223
|
}
|
|
6979
7224
|
function getGlobalAgentsDir() {
|
|
6980
|
-
return
|
|
7225
|
+
return join13(homedir6(), ".agents", "agents");
|
|
6981
7226
|
}
|
|
6982
7227
|
function getProjectAgentsDir(threadPath) {
|
|
6983
|
-
return
|
|
7228
|
+
return join13(threadPath, ".agents", "agents");
|
|
6984
7229
|
}
|
|
6985
7230
|
var AgentsManager = class {
|
|
6986
7231
|
/**
|
|
@@ -7012,8 +7257,8 @@ var AgentsManager = class {
|
|
|
7012
7257
|
for (const entry of entries) {
|
|
7013
7258
|
if (!entry.isDirectory()) continue;
|
|
7014
7259
|
const agentDirName = entry.name;
|
|
7015
|
-
const agentPath =
|
|
7016
|
-
const agentFilePath =
|
|
7260
|
+
const agentPath = join13(dir, agentDirName);
|
|
7261
|
+
const agentFilePath = join13(agentPath, "AGENT.md");
|
|
7017
7262
|
if (!existsSync10(agentFilePath)) {
|
|
7018
7263
|
console.warn(`[agents] Skipping agent directory ${agentDirName}: AGENT.md not found`);
|
|
7019
7264
|
continue;
|
|
@@ -7200,7 +7445,7 @@ function extractAssistantContent(events, fallback) {
|
|
|
7200
7445
|
// src/features/chat/chat-post.route.ts
|
|
7201
7446
|
init_database();
|
|
7202
7447
|
import { readFile as readFile8, unlink } from "fs/promises";
|
|
7203
|
-
import { join as
|
|
7448
|
+
import { join as join14 } from "path";
|
|
7204
7449
|
|
|
7205
7450
|
// src/features/project-todos/project-todos.database.ts
|
|
7206
7451
|
init_database();
|
|
@@ -8193,7 +8438,7 @@ ${result.output}`;
|
|
|
8193
8438
|
try {
|
|
8194
8439
|
const todo = await getTodoByThreadId(db, threadId);
|
|
8195
8440
|
if (todo && todo.status === "Plan") {
|
|
8196
|
-
const planFilePath =
|
|
8441
|
+
const planFilePath = join14(threadPath, `${todo.id}-plan.md`);
|
|
8197
8442
|
try {
|
|
8198
8443
|
const planContent = await readFile8(planFilePath, "utf-8");
|
|
8199
8444
|
await updateTodo(db, todo.id, { description: planContent.trim() });
|
|
@@ -8405,13 +8650,15 @@ async function updateMessage(db, messageId, content, events, imageUrl) {
|
|
|
8405
8650
|
if (hasMessageUsage) {
|
|
8406
8651
|
if (event.type === "message" && event.message?.usage) {
|
|
8407
8652
|
const usage = event.message.usage;
|
|
8408
|
-
|
|
8409
|
-
|
|
8653
|
+
const tokens = calculateCostEquivalentTokens(usage);
|
|
8654
|
+
inputTokens += tokens.inputTokens;
|
|
8655
|
+
outputTokens += tokens.outputTokens;
|
|
8410
8656
|
}
|
|
8411
8657
|
} else {
|
|
8412
8658
|
if (event.type === "toolcall_delta" && event.partial?.usage) {
|
|
8413
|
-
|
|
8414
|
-
|
|
8659
|
+
const tokens = calculateCostEquivalentTokens(event.partial.usage);
|
|
8660
|
+
inputTokens += tokens.inputTokens;
|
|
8661
|
+
outputTokens += tokens.outputTokens;
|
|
8415
8662
|
}
|
|
8416
8663
|
}
|
|
8417
8664
|
}
|
|
@@ -8442,6 +8689,34 @@ async function updateMessage(db, messageId, content, events, imageUrl) {
|
|
|
8442
8689
|
throw error;
|
|
8443
8690
|
}
|
|
8444
8691
|
}
|
|
8692
|
+
function calculateCostEquivalentTokens(usage) {
|
|
8693
|
+
const input = usage.input ?? 0;
|
|
8694
|
+
const output = usage.output ?? 0;
|
|
8695
|
+
const cacheRead = usage.cacheRead ?? 0;
|
|
8696
|
+
const cacheWrite = usage.cacheWrite ?? 0;
|
|
8697
|
+
const cachedInput = cacheRead + cacheWrite;
|
|
8698
|
+
const uncachedInput = input >= cachedInput ? input - cachedInput : input;
|
|
8699
|
+
const inputCost = usage.cost?.input ?? 0;
|
|
8700
|
+
const cacheCost = (usage.cost?.cacheRead ?? 0) + (usage.cost?.cacheWrite ?? 0);
|
|
8701
|
+
if (uncachedInput > 0 && inputCost > 0 && cacheCost > 0) {
|
|
8702
|
+
const inputCostPerToken = inputCost / uncachedInput;
|
|
8703
|
+
const costEquivalentCachedInput = cacheCost / inputCostPerToken;
|
|
8704
|
+
return {
|
|
8705
|
+
inputTokens: Math.round(uncachedInput + costEquivalentCachedInput),
|
|
8706
|
+
outputTokens: output
|
|
8707
|
+
};
|
|
8708
|
+
}
|
|
8709
|
+
if (input >= cachedInput) {
|
|
8710
|
+
return {
|
|
8711
|
+
inputTokens: uncachedInput + cachedInput,
|
|
8712
|
+
outputTokens: output
|
|
8713
|
+
};
|
|
8714
|
+
}
|
|
8715
|
+
return {
|
|
8716
|
+
inputTokens: input,
|
|
8717
|
+
outputTokens: output
|
|
8718
|
+
};
|
|
8719
|
+
}
|
|
8445
8720
|
async function getConversationHistory(db, threadId) {
|
|
8446
8721
|
try {
|
|
8447
8722
|
const result = await db.execute(
|
|
@@ -9735,6 +10010,7 @@ var MetadataManager = class {
|
|
|
9735
10010
|
await setState(this.db, "selectedThreadId", state.selectedThreadId);
|
|
9736
10011
|
await setState(this.db, "enabledModels", state.enabledModels);
|
|
9737
10012
|
await setState(this.db, "enabledImageModels", state.enabledImageModels);
|
|
10013
|
+
await setState(this.db, "providerEnvValues", state.providerEnvValues);
|
|
9738
10014
|
await setState(this.db, "onboardingCompleted", state.onboardingCompleted);
|
|
9739
10015
|
const encryptedKeys = {};
|
|
9740
10016
|
for (const [provider, key] of Object.entries(state.providerKeys)) {
|
|
@@ -9767,6 +10043,7 @@ var MetadataManager = class {
|
|
|
9767
10043
|
const onboardingCompleted = allState.onboardingCompleted || false;
|
|
9768
10044
|
const enabledModels = allState.enabledModels || {};
|
|
9769
10045
|
const enabledImageModels = allState.enabledImageModels || {};
|
|
10046
|
+
const providerEnvValues = allState.providerEnvValues || {};
|
|
9770
10047
|
const encryptedKeys = allState.providerKeys || {};
|
|
9771
10048
|
const providerKeys = {};
|
|
9772
10049
|
let needsMigration = false;
|
|
@@ -9799,6 +10076,7 @@ var MetadataManager = class {
|
|
|
9799
10076
|
selectedThreadId,
|
|
9800
10077
|
onboardingCompleted,
|
|
9801
10078
|
providerKeys,
|
|
10079
|
+
providerEnvValues,
|
|
9802
10080
|
enabledModels,
|
|
9803
10081
|
enabledImageModels
|
|
9804
10082
|
};
|
|
@@ -9877,6 +10155,36 @@ var MetadataManager = class {
|
|
|
9877
10155
|
throw new Error(`Failed to get provider keys: ${errorMessage}`);
|
|
9878
10156
|
}
|
|
9879
10157
|
}
|
|
10158
|
+
/**
|
|
10159
|
+
* Save provider environment values (non-secret values such as Azure resource name)
|
|
10160
|
+
* @param values - Environment values to merge into metadata state
|
|
10161
|
+
*/
|
|
10162
|
+
async saveProviderEnvValues(values) {
|
|
10163
|
+
try {
|
|
10164
|
+
const state = await this.loadState();
|
|
10165
|
+
state.providerEnvValues = {
|
|
10166
|
+
...state.providerEnvValues,
|
|
10167
|
+
...values
|
|
10168
|
+
};
|
|
10169
|
+
await this.saveState(state);
|
|
10170
|
+
} catch (error) {
|
|
10171
|
+
throw new Error(`Failed to save provider environment values: ${String(error)}`);
|
|
10172
|
+
}
|
|
10173
|
+
}
|
|
10174
|
+
/**
|
|
10175
|
+
* Get provider environment values stored in metadata state
|
|
10176
|
+
* @returns Record of environment variable names to values
|
|
10177
|
+
*/
|
|
10178
|
+
async getProviderEnvValues() {
|
|
10179
|
+
try {
|
|
10180
|
+
const state = await this.loadState();
|
|
10181
|
+
return state.providerEnvValues;
|
|
10182
|
+
} catch (error) {
|
|
10183
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
10184
|
+
console.error("[MetadataManager] Failed to get provider environment values:", errorMessage);
|
|
10185
|
+
throw new Error(`Failed to get provider environment values: ${errorMessage}`);
|
|
10186
|
+
}
|
|
10187
|
+
}
|
|
9880
10188
|
/**
|
|
9881
10189
|
* Set the selected thread ID
|
|
9882
10190
|
* @param threadId - Thread ID to select (or null to deselect)
|
|
@@ -10061,7 +10369,7 @@ async function handleGetAvailableModels(c, modelManager) {
|
|
|
10061
10369
|
const bEnabled = enabledModelIds.has(b.id) ? 0 : 1;
|
|
10062
10370
|
return aEnabled - bEnabled;
|
|
10063
10371
|
});
|
|
10064
|
-
return c.json({ provider, models });
|
|
10372
|
+
return c.json({ provider, models, enabledModelIds: [...enabledModelIds] });
|
|
10065
10373
|
} catch (error) {
|
|
10066
10374
|
const message = error instanceof Error ? error.message : "Unknown error";
|
|
10067
10375
|
return c.json({ error: message }, 500);
|
|
@@ -10933,10 +11241,10 @@ async function handleCheckRunning(c, projectManager) {
|
|
|
10933
11241
|
|
|
10934
11242
|
// src/core/run-command-detector.ts
|
|
10935
11243
|
import { readFile as readFile9 } from "fs/promises";
|
|
10936
|
-
import { join as
|
|
11244
|
+
import { join as join15 } from "path";
|
|
10937
11245
|
async function detectPackageManager(projectPath) {
|
|
10938
11246
|
try {
|
|
10939
|
-
const packageJsonPath =
|
|
11247
|
+
const packageJsonPath = join15(projectPath, "package.json");
|
|
10940
11248
|
const content = await readFile9(packageJsonPath, "utf-8");
|
|
10941
11249
|
const packageJson = JSON.parse(content);
|
|
10942
11250
|
if (packageJson.packageManager) {
|
|
@@ -10952,7 +11260,7 @@ async function detectPackageManager(projectPath) {
|
|
|
10952
11260
|
}
|
|
10953
11261
|
async function detectRunScripts(projectPath) {
|
|
10954
11262
|
try {
|
|
10955
|
-
const packageJsonPath =
|
|
11263
|
+
const packageJsonPath = join15(projectPath, "package.json");
|
|
10956
11264
|
const content = await readFile9(packageJsonPath, "utf-8");
|
|
10957
11265
|
const packageJson = JSON.parse(content);
|
|
10958
11266
|
const scripts = packageJson.scripts ?? {};
|
|
@@ -10999,7 +11307,7 @@ async function suggestRunCommand(projectPath) {
|
|
|
10999
11307
|
|
|
11000
11308
|
// src/features/projects/projects-package-scripts.route.ts
|
|
11001
11309
|
import { readFile as readFile10 } from "fs/promises";
|
|
11002
|
-
import { join as
|
|
11310
|
+
import { join as join16 } from "path";
|
|
11003
11311
|
import { glob } from "glob";
|
|
11004
11312
|
function formatScriptName(scriptName) {
|
|
11005
11313
|
return scriptName.replace(/[-:_]/g, " ").replace(/\b\w/g, (l) => l.toUpperCase());
|
|
@@ -11027,7 +11335,7 @@ async function findPackageScripts(projectPath) {
|
|
|
11027
11335
|
const packageManager = await detectPackageManager(projectPath);
|
|
11028
11336
|
for (const filePath of packageJsonFiles) {
|
|
11029
11337
|
try {
|
|
11030
|
-
const fullPath =
|
|
11338
|
+
const fullPath = join16(projectPath, filePath);
|
|
11031
11339
|
const content = await readFile10(fullPath, "utf-8");
|
|
11032
11340
|
const packageJson = JSON.parse(content);
|
|
11033
11341
|
if (packageJson.scripts) {
|
|
@@ -11076,7 +11384,7 @@ async function handleGetPackageScripts(c, projectManager) {
|
|
|
11076
11384
|
|
|
11077
11385
|
// src/features/projects/projects-ai-files.route.ts
|
|
11078
11386
|
import { readdir as readdir6, readFile as readFile11, writeFile as writeFile2, mkdir, access as access2, rm } from "fs/promises";
|
|
11079
|
-
import { join as
|
|
11387
|
+
import { join as join17, extname as extname3, basename } from "path";
|
|
11080
11388
|
import { existsSync as existsSync11 } from "fs";
|
|
11081
11389
|
var IGNORED_DIRS = /* @__PURE__ */ new Set([
|
|
11082
11390
|
".git",
|
|
@@ -11095,8 +11403,8 @@ async function buildFullTree(dirPath, relativeDirPath) {
|
|
|
11095
11403
|
try {
|
|
11096
11404
|
const entries = await readdir6(dirPath, { withFileTypes: true });
|
|
11097
11405
|
for (const entry of entries) {
|
|
11098
|
-
const entryRelPath =
|
|
11099
|
-
const entryAbsPath =
|
|
11406
|
+
const entryRelPath = join17(relativeDirPath, entry.name);
|
|
11407
|
+
const entryAbsPath = join17(dirPath, entry.name);
|
|
11100
11408
|
if (entry.isDirectory()) {
|
|
11101
11409
|
const children = await buildFullTree(entryAbsPath, entryRelPath);
|
|
11102
11410
|
nodes.push({
|
|
@@ -11124,8 +11432,8 @@ async function buildMarkdownFilteredTree(dirPath, relativeDirPath) {
|
|
|
11124
11432
|
try {
|
|
11125
11433
|
const entries = await readdir6(dirPath, { withFileTypes: true });
|
|
11126
11434
|
for (const entry of entries) {
|
|
11127
|
-
const entryRelPath =
|
|
11128
|
-
const entryAbsPath =
|
|
11435
|
+
const entryRelPath = join17(relativeDirPath, entry.name);
|
|
11436
|
+
const entryAbsPath = join17(dirPath, entry.name);
|
|
11129
11437
|
if (entry.isDirectory()) {
|
|
11130
11438
|
if (IGNORED_DIRS.has(entry.name)) continue;
|
|
11131
11439
|
const children = await buildMarkdownFilteredTree(entryAbsPath, entryRelPath);
|
|
@@ -11160,8 +11468,8 @@ async function buildAIFileTree(projectPath) {
|
|
|
11160
11468
|
{ key: "agents", label: "Agents" }
|
|
11161
11469
|
];
|
|
11162
11470
|
for (const { key, label } of agentsFolders) {
|
|
11163
|
-
const relPath =
|
|
11164
|
-
const absPath =
|
|
11471
|
+
const relPath = join17(".agents", key);
|
|
11472
|
+
const absPath = join17(projectPath, relPath);
|
|
11165
11473
|
const exists = existsSync11(absPath);
|
|
11166
11474
|
if (exists) {
|
|
11167
11475
|
const children = await buildFullTree(absPath, relPath);
|
|
@@ -11196,7 +11504,7 @@ async function buildAIFileTree(projectPath) {
|
|
|
11196
11504
|
if (entry.name === ".agents" || entry.name === "AGENTS.md" || IGNORED_DIRS.has(entry.name))
|
|
11197
11505
|
continue;
|
|
11198
11506
|
const entryRelPath = entry.name;
|
|
11199
|
-
const entryAbsPath =
|
|
11507
|
+
const entryAbsPath = join17(projectPath, entry.name);
|
|
11200
11508
|
if (entry.isFile() && MARKDOWN_EXTS.has(extname3(entry.name))) {
|
|
11201
11509
|
nodes.push({
|
|
11202
11510
|
id: entryRelPath,
|
|
@@ -11223,7 +11531,7 @@ async function buildAIFileTree(projectPath) {
|
|
|
11223
11531
|
}
|
|
11224
11532
|
function validateFilePath(projectPath, filePath) {
|
|
11225
11533
|
if (!filePath) return null;
|
|
11226
|
-
const abs =
|
|
11534
|
+
const abs = join17(projectPath, filePath);
|
|
11227
11535
|
if (!abs.startsWith(projectPath + "/") && abs !== projectPath) return null;
|
|
11228
11536
|
return abs;
|
|
11229
11537
|
}
|
|
@@ -11349,7 +11657,7 @@ async function handleSaveAIFile(c, projectManager) {
|
|
|
11349
11657
|
if (!absPath) {
|
|
11350
11658
|
return c.json({ error: { code: "BAD_REQUEST", message: "Invalid file path" } }, 400);
|
|
11351
11659
|
}
|
|
11352
|
-
const parentDir =
|
|
11660
|
+
const parentDir = join17(absPath, "..");
|
|
11353
11661
|
await mkdir(parentDir, { recursive: true });
|
|
11354
11662
|
await writeFile2(absPath, content, "utf-8");
|
|
11355
11663
|
return c.json({ success: true, path: filePath });
|
|
@@ -11449,10 +11757,10 @@ async function handleCreateSkill(c, projectManager) {
|
|
|
11449
11757
|
if (!project) {
|
|
11450
11758
|
return errorResponse(c, ErrorCodes.PROJECT_NOT_FOUND, `Project not found: ${projectId}`, 404);
|
|
11451
11759
|
}
|
|
11452
|
-
const skillRelPath =
|
|
11453
|
-
const skillAbsPath =
|
|
11454
|
-
const skillFileRelPath =
|
|
11455
|
-
const skillFileAbsPath =
|
|
11760
|
+
const skillRelPath = join17(".agents", "skills", name);
|
|
11761
|
+
const skillAbsPath = join17(project.path, skillRelPath);
|
|
11762
|
+
const skillFileRelPath = join17(skillRelPath, "SKILL.md");
|
|
11763
|
+
const skillFileAbsPath = join17(skillAbsPath, "SKILL.md");
|
|
11456
11764
|
if (existsSync11(skillAbsPath)) {
|
|
11457
11765
|
return c.json(
|
|
11458
11766
|
{ error: { code: "CONFLICT", message: `Skill '${name}' already exists` } },
|
|
@@ -11554,7 +11862,7 @@ function createProjectRoutes(projectManager, threadManager) {
|
|
|
11554
11862
|
|
|
11555
11863
|
// src/features/projects/projects.manager.ts
|
|
11556
11864
|
init_utils();
|
|
11557
|
-
import { join as
|
|
11865
|
+
import { join as join20 } from "path";
|
|
11558
11866
|
import { realpathSync as realpathSync2 } from "fs";
|
|
11559
11867
|
import { rm as rm3 } from "fs/promises";
|
|
11560
11868
|
|
|
@@ -11775,13 +12083,13 @@ var ProcessManager = class extends EventEmitter {
|
|
|
11775
12083
|
// src/features/projects/projects.creator.ts
|
|
11776
12084
|
init_utils();
|
|
11777
12085
|
import { randomUUID as randomUUID7 } from "crypto";
|
|
11778
|
-
import { basename as basename2, join as
|
|
12086
|
+
import { basename as basename2, join as join19, relative as relative5 } from "path";
|
|
11779
12087
|
import { access as access3, stat as stat3, readdir as readdir8 } from "fs/promises";
|
|
11780
12088
|
|
|
11781
12089
|
// src/features/scaffold/scaffold.runner.ts
|
|
11782
12090
|
init_utils();
|
|
11783
12091
|
import { existsSync as existsSync12 } from "fs";
|
|
11784
|
-
import { join as
|
|
12092
|
+
import { join as join18 } from "path";
|
|
11785
12093
|
import { mkdir as mkdir2, readdir as readdir7, stat as stat2, rename, rm as rm2, writeFile as writeFile3 } from "fs/promises";
|
|
11786
12094
|
import { createInterface as createInterface2 } from "readline";
|
|
11787
12095
|
|
|
@@ -12503,7 +12811,7 @@ function loadCatalog() {
|
|
|
12503
12811
|
}
|
|
12504
12812
|
async function writeAgentsMd(projectPath, agentsMd) {
|
|
12505
12813
|
if (!agentsMd) return;
|
|
12506
|
-
const agentsPath =
|
|
12814
|
+
const agentsPath = join18(projectPath, "AGENTS.md");
|
|
12507
12815
|
if (existsSync12(agentsPath)) return;
|
|
12508
12816
|
await writeFile3(agentsPath, agentsMd, "utf-8");
|
|
12509
12817
|
}
|
|
@@ -12661,7 +12969,7 @@ async function* runCommandStreaming(cwd, command) {
|
|
|
12661
12969
|
}
|
|
12662
12970
|
}
|
|
12663
12971
|
async function* createScaffoldedProjectStreaming(options) {
|
|
12664
|
-
const projectPath =
|
|
12972
|
+
const projectPath = join18(options.parentDir, options.threadId);
|
|
12665
12973
|
if (!existsSync12(options.parentDir)) {
|
|
12666
12974
|
yield {
|
|
12667
12975
|
type: "result",
|
|
@@ -12751,7 +13059,7 @@ async function* createScaffoldedProjectStreaming(options) {
|
|
|
12751
13059
|
}
|
|
12752
13060
|
try {
|
|
12753
13061
|
const projectName2 = getProjectName(options.projectName);
|
|
12754
|
-
const projectSubDir =
|
|
13062
|
+
const projectSubDir = join18(projectPath, projectName2);
|
|
12755
13063
|
try {
|
|
12756
13064
|
const subDirStat = await stat2(projectSubDir);
|
|
12757
13065
|
if (subDirStat.isDirectory()) {
|
|
@@ -12761,8 +13069,8 @@ async function* createScaffoldedProjectStreaming(options) {
|
|
|
12761
13069
|
};
|
|
12762
13070
|
const items = await readdir7(projectSubDir);
|
|
12763
13071
|
for (const item of items) {
|
|
12764
|
-
const oldPath =
|
|
12765
|
-
const newPath =
|
|
13072
|
+
const oldPath = join18(projectSubDir, item);
|
|
13073
|
+
const newPath = join18(projectPath, item);
|
|
12766
13074
|
await rename(oldPath, newPath);
|
|
12767
13075
|
}
|
|
12768
13076
|
await rm2(projectSubDir, { recursive: true, force: true });
|
|
@@ -13230,7 +13538,7 @@ var ProjectCreator = class {
|
|
|
13230
13538
|
return name;
|
|
13231
13539
|
}
|
|
13232
13540
|
generateThreadPath(_projectId, threadId) {
|
|
13233
|
-
return
|
|
13541
|
+
return join19(this.rootFolder, threadId);
|
|
13234
13542
|
}
|
|
13235
13543
|
async *createProjectFromFolder() {
|
|
13236
13544
|
await loadUtils();
|
|
@@ -13380,19 +13688,19 @@ var ProjectCreator = class {
|
|
|
13380
13688
|
}
|
|
13381
13689
|
async findAllPackageJsonFiles(rootPath, currentPath, setupScripts) {
|
|
13382
13690
|
try {
|
|
13383
|
-
await access3(
|
|
13691
|
+
await access3(join19(currentPath, "package.json"));
|
|
13384
13692
|
const relativePath = relative5(rootPath, currentPath);
|
|
13385
13693
|
let installCommand = "";
|
|
13386
13694
|
try {
|
|
13387
|
-
await access3(
|
|
13695
|
+
await access3(join19(currentPath, "yarn.lock"));
|
|
13388
13696
|
installCommand = "yarn install";
|
|
13389
13697
|
} catch {
|
|
13390
13698
|
try {
|
|
13391
|
-
await access3(
|
|
13699
|
+
await access3(join19(currentPath, "bun.lock"));
|
|
13392
13700
|
installCommand = "bun install";
|
|
13393
13701
|
} catch {
|
|
13394
13702
|
try {
|
|
13395
|
-
await access3(
|
|
13703
|
+
await access3(join19(currentPath, "pnpm-lock.yaml"));
|
|
13396
13704
|
installCommand = "pnpm install";
|
|
13397
13705
|
} catch {
|
|
13398
13706
|
installCommand = "npm install";
|
|
@@ -13409,7 +13717,7 @@ var ProjectCreator = class {
|
|
|
13409
13717
|
try {
|
|
13410
13718
|
const entries = await readdir8(currentPath);
|
|
13411
13719
|
for (const entry of entries) {
|
|
13412
|
-
const entryPath =
|
|
13720
|
+
const entryPath = join19(currentPath, entry);
|
|
13413
13721
|
try {
|
|
13414
13722
|
const stats = await stat3(entryPath);
|
|
13415
13723
|
if (stats.isDirectory() && !entry.startsWith(".") && entry !== "node_modules") {
|
|
@@ -13617,12 +13925,15 @@ var ProjectManagerImpl = class {
|
|
|
13617
13925
|
const threads = await this.metadataManager.loadThreads();
|
|
13618
13926
|
const remainingThreads = threads.filter((t) => t.projectId !== projectId);
|
|
13619
13927
|
await this.metadataManager.saveThreads(remainingThreads);
|
|
13620
|
-
|
|
13621
|
-
|
|
13622
|
-
|
|
13623
|
-
|
|
13624
|
-
|
|
13625
|
-
|
|
13928
|
+
const isInsideDataDir = project.path.startsWith(this.rootFolder + "/");
|
|
13929
|
+
if (isInsideDataDir) {
|
|
13930
|
+
try {
|
|
13931
|
+
await rm3(project.path, { recursive: true, force: true });
|
|
13932
|
+
} catch (error) {
|
|
13933
|
+
throw new Error(
|
|
13934
|
+
`Failed to remove project directory: ${error instanceof Error ? error.message : String(error)}`
|
|
13935
|
+
);
|
|
13936
|
+
}
|
|
13626
13937
|
}
|
|
13627
13938
|
projects.splice(projectIndex, 1);
|
|
13628
13939
|
await this.metadataManager.saveProjects(projects);
|
|
@@ -13746,7 +14057,7 @@ var ProjectManagerImpl = class {
|
|
|
13746
14057
|
const session = this.terminalSessionManager.getOrCreateSession(threadId, thread.path);
|
|
13747
14058
|
let workingDir;
|
|
13748
14059
|
if (cwd) {
|
|
13749
|
-
workingDir = cwd.startsWith("/") ? cwd :
|
|
14060
|
+
workingDir = cwd.startsWith("/") ? cwd : join20(thread.path, cwd);
|
|
13750
14061
|
this.terminalSessionManager.updateWorkingDirectory(threadId, workingDir);
|
|
13751
14062
|
} else {
|
|
13752
14063
|
workingDir = session.currentWorkingDirectory;
|
|
@@ -13949,78 +14260,61 @@ ___CWD___%s
|
|
|
13949
14260
|
// src/features/providers/providers.routes.ts
|
|
13950
14261
|
import { Hono as Hono7 } from "hono";
|
|
13951
14262
|
|
|
13952
|
-
// src/
|
|
13953
|
-
|
|
13954
|
-
|
|
13955
|
-
|
|
13956
|
-
const envPath = join20(process.cwd(), ".env");
|
|
13957
|
-
let content = "";
|
|
13958
|
-
try {
|
|
13959
|
-
content = await fs.readFile(envPath, "utf-8");
|
|
13960
|
-
} catch {
|
|
13961
|
-
}
|
|
13962
|
-
const lines = content.split("\n");
|
|
13963
|
-
const envMap = {};
|
|
13964
|
-
for (const line of lines) {
|
|
13965
|
-
const trimmed = line.trim();
|
|
13966
|
-
if (trimmed && !trimmed.startsWith("#") && trimmed.includes("=")) {
|
|
13967
|
-
const index = trimmed.indexOf("=");
|
|
13968
|
-
const key = trimmed.substring(0, index).trim();
|
|
13969
|
-
const value = trimmed.substring(index + 1).trim();
|
|
13970
|
-
envMap[key] = value;
|
|
13971
|
-
}
|
|
13972
|
-
}
|
|
13973
|
-
for (const [key, value] of Object.entries(keyNames)) {
|
|
13974
|
-
if (key) {
|
|
13975
|
-
envMap[key] = value;
|
|
13976
|
-
}
|
|
13977
|
-
}
|
|
13978
|
-
const newLines = [];
|
|
13979
|
-
for (const [key, value] of Object.entries(envMap)) {
|
|
13980
|
-
newLines.push(`${key}=${value}`);
|
|
14263
|
+
// src/features/providers/providers-get.route.ts
|
|
14264
|
+
function getProviderApiKeyEnvName2(providerEnv) {
|
|
14265
|
+
if (providerEnv.length === 0) {
|
|
14266
|
+
return "";
|
|
13981
14267
|
}
|
|
13982
|
-
|
|
14268
|
+
const explicitApiKeyEnv = providerEnv.find((envName) => /api_key$/i.test(envName));
|
|
14269
|
+
return explicitApiKeyEnv ?? providerEnv[providerEnv.length - 1];
|
|
13983
14270
|
}
|
|
13984
|
-
|
|
13985
|
-
const
|
|
13986
|
-
|
|
13987
|
-
|
|
13988
|
-
|
|
13989
|
-
|
|
13990
|
-
|
|
13991
|
-
|
|
13992
|
-
|
|
13993
|
-
const index = trimmed.indexOf("=");
|
|
13994
|
-
const key = trimmed.substring(0, index).trim();
|
|
13995
|
-
const value = trimmed.substring(index + 1).trim();
|
|
13996
|
-
envMap[key] = value;
|
|
13997
|
-
}
|
|
13998
|
-
}
|
|
13999
|
-
} catch {
|
|
14271
|
+
function getProviderResourceName2(providerEnv) {
|
|
14272
|
+
const explicitResourceEnv = providerEnv.find((envName) => /resource_name$/i.test(envName));
|
|
14273
|
+
return explicitResourceEnv ?? providerEnv[0] ?? "";
|
|
14274
|
+
}
|
|
14275
|
+
function resolveAzureApiUrl2(providerEnv, envValues) {
|
|
14276
|
+
const resourceEnvName = getProviderResourceName2(providerEnv);
|
|
14277
|
+
const resourceName = envValues[resourceEnvName] ?? "";
|
|
14278
|
+
if (!resourceName) {
|
|
14279
|
+
return "";
|
|
14000
14280
|
}
|
|
14001
|
-
return
|
|
14281
|
+
return `https://${resourceName}.openai.azure.com/openai/v1`;
|
|
14002
14282
|
}
|
|
14003
|
-
|
|
14004
|
-
// src/features/providers/providers-get.route.ts
|
|
14005
14283
|
async function getProviders(c, metadataManager) {
|
|
14006
14284
|
const keys = await metadataManager.getProviderKeys();
|
|
14285
|
+
const persistedEnvValues = await metadataManager.getProviderEnvValues();
|
|
14007
14286
|
const envFileKeys = await readEnvFile();
|
|
14008
14287
|
const catalog = await getModelsCatalog();
|
|
14009
14288
|
const providersWithKeys = [];
|
|
14010
14289
|
if (catalog) {
|
|
14011
14290
|
for (const [catalogId, catalogProvider] of Object.entries(catalog)) {
|
|
14012
|
-
const
|
|
14291
|
+
const envNames = catalogProvider.env ?? [];
|
|
14292
|
+
const keyName = getProviderApiKeyEnvName2(envNames);
|
|
14293
|
+
const envValues = {};
|
|
14294
|
+
for (const envName of envNames) {
|
|
14295
|
+
envValues[envName] = envFileKeys[envName] ?? process.env[envName] ?? persistedEnvValues[envName] ?? "";
|
|
14296
|
+
}
|
|
14013
14297
|
const envFileKey = keyName ? envFileKeys[keyName] : "";
|
|
14014
14298
|
const processEnvKey = keyName ? process.env[keyName] : "";
|
|
14015
14299
|
const storedKey = keys[catalogProvider.name] || keys[catalogId];
|
|
14300
|
+
const hasEnvKey = Boolean(envFileKey || processEnvKey);
|
|
14301
|
+
const resolvedKey = storedKey ?? envFileKey ?? processEnvKey ?? "";
|
|
14302
|
+
const isEnv = !storedKey && hasEnvKey && resolvedKey !== "";
|
|
14303
|
+
const api = catalogId === "azure" ? resolveAzureApiUrl2(envNames, envValues) : catalogProvider.api ?? "";
|
|
14304
|
+
if (keyName && !envValues[keyName]) {
|
|
14305
|
+
envValues[keyName] = resolvedKey;
|
|
14306
|
+
}
|
|
14016
14307
|
providersWithKeys.push({
|
|
14017
14308
|
name: catalogProvider.name ?? catalogId,
|
|
14018
14309
|
slug: catalogId,
|
|
14019
14310
|
url: catalogProvider.doc ?? "",
|
|
14020
14311
|
keyName,
|
|
14021
14312
|
authType: "APIKey",
|
|
14022
|
-
api
|
|
14023
|
-
|
|
14313
|
+
api,
|
|
14314
|
+
env: envNames,
|
|
14315
|
+
envValues,
|
|
14316
|
+
apiKey: resolvedKey,
|
|
14317
|
+
isEnv
|
|
14024
14318
|
});
|
|
14025
14319
|
}
|
|
14026
14320
|
}
|
|
@@ -14037,6 +14331,7 @@ async function postProviderKeys(c, metadataManager) {
|
|
|
14037
14331
|
await metadataManager.saveProviderKey(providerName, apiKey);
|
|
14038
14332
|
const keyName = await resolveProviderKeyName(providerName);
|
|
14039
14333
|
if (keyName) {
|
|
14334
|
+
updateRuntimeEnv({ [keyName]: apiKey });
|
|
14040
14335
|
await updateEnvFile({ [keyName]: apiKey });
|
|
14041
14336
|
}
|
|
14042
14337
|
return c.json({ success: true });
|
|
@@ -14061,6 +14356,7 @@ async function postProviderBulkKeys(c, metadataManager) {
|
|
|
14061
14356
|
}
|
|
14062
14357
|
}
|
|
14063
14358
|
if (Object.keys(envUpdates).length > 0) {
|
|
14359
|
+
updateRuntimeEnv(envUpdates);
|
|
14064
14360
|
await updateEnvFile(envUpdates);
|
|
14065
14361
|
}
|
|
14066
14362
|
return c.json({ success: true });
|
|
@@ -14069,6 +14365,28 @@ async function postProviderBulkKeys(c, metadataManager) {
|
|
|
14069
14365
|
}
|
|
14070
14366
|
}
|
|
14071
14367
|
|
|
14368
|
+
// src/features/providers/providers-post-env.route.ts
|
|
14369
|
+
async function postProviderEnv(c, metadataManager) {
|
|
14370
|
+
try {
|
|
14371
|
+
const { values } = await c.req.json();
|
|
14372
|
+
if (!values || typeof values !== "object") {
|
|
14373
|
+
return c.json({ error: "values object is required" }, 400);
|
|
14374
|
+
}
|
|
14375
|
+
const envValues = {};
|
|
14376
|
+
for (const [key, value] of Object.entries(values)) {
|
|
14377
|
+
if (typeof value === "string") {
|
|
14378
|
+
envValues[key] = value;
|
|
14379
|
+
}
|
|
14380
|
+
}
|
|
14381
|
+
await metadataManager.saveProviderEnvValues(envValues);
|
|
14382
|
+
updateRuntimeEnv(envValues);
|
|
14383
|
+
await updateEnvFile(envValues);
|
|
14384
|
+
return c.json({ success: true });
|
|
14385
|
+
} catch (error) {
|
|
14386
|
+
return c.json({ error: `Failed to save provider environment: ${String(error)}` }, 500);
|
|
14387
|
+
}
|
|
14388
|
+
}
|
|
14389
|
+
|
|
14072
14390
|
// src/features/models/model-info-openrouter.ts
|
|
14073
14391
|
async function getOpenRouterCredits(apiKey) {
|
|
14074
14392
|
try {
|
|
@@ -14161,6 +14479,44 @@ async function getProviderCredits(c, metadataManager) {
|
|
|
14161
14479
|
return c.json({ balance: null, status: "unknown" });
|
|
14162
14480
|
}
|
|
14163
14481
|
|
|
14482
|
+
// src/features/providers/providers-get-logo.route.ts
|
|
14483
|
+
var LOGO_BASE_URL = "https://models.tarsk.io/svg";
|
|
14484
|
+
var SLUG_PATTERN = /^[a-z0-9._-]+$/i;
|
|
14485
|
+
var logoCache = /* @__PURE__ */ new Map();
|
|
14486
|
+
var LOGO_CACHE_TTL_MS = 15 * 60 * 1e3;
|
|
14487
|
+
async function getProviderLogo(c) {
|
|
14488
|
+
const slug = c.req.param("slug");
|
|
14489
|
+
if (!slug || !SLUG_PATTERN.test(slug)) {
|
|
14490
|
+
return c.body(null, 404);
|
|
14491
|
+
}
|
|
14492
|
+
const cached = logoCache.get(slug);
|
|
14493
|
+
if (cached && Date.now() < cached.expires) {
|
|
14494
|
+
return c.body(cached.svg, 200, {
|
|
14495
|
+
"Content-Type": "image/svg+xml",
|
|
14496
|
+
"Cache-Control": "public, max-age=86400"
|
|
14497
|
+
});
|
|
14498
|
+
}
|
|
14499
|
+
try {
|
|
14500
|
+
const response = await fetch(`${LOGO_BASE_URL}/${slug}.svg`, {
|
|
14501
|
+
signal: AbortSignal.timeout(5e3)
|
|
14502
|
+
});
|
|
14503
|
+
if (!response.ok) {
|
|
14504
|
+
return c.body(null, 404);
|
|
14505
|
+
}
|
|
14506
|
+
const svg = await response.text();
|
|
14507
|
+
if (!svg.includes("<svg")) {
|
|
14508
|
+
return c.body(null, 404);
|
|
14509
|
+
}
|
|
14510
|
+
logoCache.set(slug, { svg, expires: Date.now() + LOGO_CACHE_TTL_MS });
|
|
14511
|
+
return c.body(svg, 200, {
|
|
14512
|
+
"Content-Type": "image/svg+xml",
|
|
14513
|
+
"Cache-Control": "public, max-age=86400"
|
|
14514
|
+
});
|
|
14515
|
+
} catch {
|
|
14516
|
+
return c.body(null, 404);
|
|
14517
|
+
}
|
|
14518
|
+
}
|
|
14519
|
+
|
|
14164
14520
|
// src/features/providers/providers-open-external.route.ts
|
|
14165
14521
|
var Utils2 = null;
|
|
14166
14522
|
var utilsLoaded2 = false;
|
|
@@ -14204,7 +14560,9 @@ function createProviderRoutes(metadataManager) {
|
|
|
14204
14560
|
router.get("/", (c) => getProviders(c, metadataManager));
|
|
14205
14561
|
router.post("/keys", (c) => postProviderKeys(c, metadataManager));
|
|
14206
14562
|
router.post("/bulk-keys", (c) => postProviderBulkKeys(c, metadataManager));
|
|
14563
|
+
router.post("/env", (c) => postProviderEnv(c, metadataManager));
|
|
14207
14564
|
router.get("/:name/credits", (c) => getProviderCredits(c, metadataManager));
|
|
14565
|
+
router.get("/:slug/logo", (c) => getProviderLogo(c));
|
|
14208
14566
|
router.post("/open-external", (c) => openExternalUrl(c));
|
|
14209
14567
|
return router;
|
|
14210
14568
|
}
|
|
@@ -14453,7 +14811,7 @@ function createScaffoldRoutes(projectManager) {
|
|
|
14453
14811
|
|
|
14454
14812
|
// src/features/slash-commands/slash-commands.manager.ts
|
|
14455
14813
|
import { readdir as readdir9, readFile as readFile12, mkdir as mkdir3, writeFile as writeFile4, unlink as unlink2 } from "fs/promises";
|
|
14456
|
-
import { join as join21, basename as basename3, extname as extname4 } from "path";
|
|
14814
|
+
import { join as join21, basename as basename3, extname as extname4, relative as relative6 } from "path";
|
|
14457
14815
|
import { existsSync as existsSync13 } from "fs";
|
|
14458
14816
|
import { homedir as homedir7 } from "os";
|
|
14459
14817
|
function slugify(filename) {
|
|
@@ -14534,14 +14892,14 @@ var SlashCommandManager = class _SlashCommandManager {
|
|
|
14534
14892
|
const commands = /* @__PURE__ */ new Map();
|
|
14535
14893
|
const globalDir = getGlobalCommandsDir();
|
|
14536
14894
|
if (existsSync13(globalDir)) {
|
|
14537
|
-
const globalCommands = await this.loadCommandsFromDir(globalDir, "global");
|
|
14895
|
+
const globalCommands = await this.loadCommandsFromDir(globalDir, threadPath, "global");
|
|
14538
14896
|
for (const cmd of globalCommands) {
|
|
14539
14897
|
commands.set(cmd.name, cmd);
|
|
14540
14898
|
}
|
|
14541
14899
|
}
|
|
14542
14900
|
const projectDir = getProjectCommandsDir(threadPath);
|
|
14543
14901
|
if (existsSync13(projectDir)) {
|
|
14544
|
-
const projectCommands = await this.loadCommandsFromDir(projectDir, "project");
|
|
14902
|
+
const projectCommands = await this.loadCommandsFromDir(projectDir, threadPath, "project");
|
|
14545
14903
|
for (const cmd of projectCommands) {
|
|
14546
14904
|
if (!_SlashCommandManager.BUILT_IN_COMMANDS.has(cmd.name)) {
|
|
14547
14905
|
commands.set(cmd.name, cmd);
|
|
@@ -14558,7 +14916,7 @@ var SlashCommandManager = class _SlashCommandManager {
|
|
|
14558
14916
|
/**
|
|
14559
14917
|
* Load commands from a specific directory
|
|
14560
14918
|
*/
|
|
14561
|
-
async loadCommandsFromDir(dir, scope) {
|
|
14919
|
+
async loadCommandsFromDir(dir, threadPath, scope) {
|
|
14562
14920
|
const commands = [];
|
|
14563
14921
|
try {
|
|
14564
14922
|
const files = await readdir9(dir);
|
|
@@ -14575,7 +14933,7 @@ var SlashCommandManager = class _SlashCommandManager {
|
|
|
14575
14933
|
content,
|
|
14576
14934
|
metadata,
|
|
14577
14935
|
scope,
|
|
14578
|
-
filePath
|
|
14936
|
+
filePath: scope === "project" ? relative6(threadPath, filePath) : filePath
|
|
14579
14937
|
});
|
|
14580
14938
|
}
|
|
14581
14939
|
} catch (error) {
|
|
@@ -15462,12 +15820,15 @@ var ThreadManagerImpl = class {
|
|
|
15462
15820
|
throw new Error(`Thread not found: ${threadId}`);
|
|
15463
15821
|
}
|
|
15464
15822
|
const thread = threads[threadIndex];
|
|
15465
|
-
|
|
15466
|
-
|
|
15467
|
-
|
|
15468
|
-
|
|
15469
|
-
|
|
15470
|
-
|
|
15823
|
+
const isInsideDataDir = thread.path.startsWith(this.rootFolder + "/");
|
|
15824
|
+
if (isInsideDataDir) {
|
|
15825
|
+
try {
|
|
15826
|
+
await rm4(thread.path, { recursive: true, force: true });
|
|
15827
|
+
} catch (error) {
|
|
15828
|
+
throw new Error(
|
|
15829
|
+
`Failed to remove thread directory: ${error instanceof Error ? error.message : String(error)}`
|
|
15830
|
+
);
|
|
15831
|
+
}
|
|
15471
15832
|
}
|
|
15472
15833
|
threads.splice(threadIndex, 1);
|
|
15473
15834
|
await this.metadataManager.saveThreads(threads);
|
|
@@ -15793,7 +16154,7 @@ async function handleDeleteThread(c, threadManager) {
|
|
|
15793
16154
|
// src/core/project-inspector.ts
|
|
15794
16155
|
import { readFile as readFile13, readdir as readdir10 } from "fs/promises";
|
|
15795
16156
|
import { existsSync as existsSync14 } from "fs";
|
|
15796
|
-
import { join as join23, basename as basename4, relative as
|
|
16157
|
+
import { join as join23, basename as basename4, relative as relative7 } from "path";
|
|
15797
16158
|
import { glob as glob2 } from "glob";
|
|
15798
16159
|
|
|
15799
16160
|
// src/features/project-scripts/project-scripts.database.ts
|
|
@@ -16143,7 +16504,7 @@ function buildRunCommand(scriptName, workspace, packageManager, repoType, projec
|
|
|
16143
16504
|
return `npm run ${scriptName} --workspace=${workspace.relativePath}`;
|
|
16144
16505
|
}
|
|
16145
16506
|
if (!isRoot) {
|
|
16146
|
-
const relPath =
|
|
16507
|
+
const relPath = relative7(projectPath, workspace.folder);
|
|
16147
16508
|
return `cd ${relPath} && ${runCmd}`;
|
|
16148
16509
|
}
|
|
16149
16510
|
return runCmd;
|
|
@@ -16804,15 +17165,17 @@ async function handleOpenThread(c, threadManager) {
|
|
|
16804
17165
|
}
|
|
16805
17166
|
|
|
16806
17167
|
// src/features/threads/threads-conversation-folder-path.route.ts
|
|
16807
|
-
|
|
16808
|
-
async function handleGetConversationFolderPath(c) {
|
|
17168
|
+
async function handleGetConversationFolderPath(c, threadManager) {
|
|
16809
17169
|
try {
|
|
16810
17170
|
const threadId = c.req.param("id");
|
|
16811
17171
|
if (!threadId) {
|
|
16812
17172
|
return errorResponse(c, ErrorCodes.INVALID_REQUEST, "Thread ID is required", 400);
|
|
16813
17173
|
}
|
|
16814
|
-
const
|
|
16815
|
-
|
|
17174
|
+
const thread = await threadManager.getThread(threadId);
|
|
17175
|
+
if (!thread) {
|
|
17176
|
+
return errorResponse(c, ErrorCodes.THREAD_NOT_FOUND, `Thread not found: ${threadId}`, 404);
|
|
17177
|
+
}
|
|
17178
|
+
return successResponse(c, { path: thread.path });
|
|
16816
17179
|
} catch (error) {
|
|
16817
17180
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
16818
17181
|
return errorResponse(
|
|
@@ -16866,12 +17229,12 @@ async function handleFixComments(c, threadManager) {
|
|
|
16866
17229
|
|
|
16867
17230
|
// src/features/threads/threads-ai-files.route.ts
|
|
16868
17231
|
import { readFile as readFile15, writeFile as writeFile7, mkdir as mkdir7, access as access5, rm as rm6 } from "fs/promises";
|
|
16869
|
-
import { join as
|
|
17232
|
+
import { join as join26 } from "path";
|
|
16870
17233
|
import { existsSync as existsSync15 } from "fs";
|
|
16871
17234
|
|
|
16872
17235
|
// src/features/git/git-download-folder.ts
|
|
16873
17236
|
import { mkdir as mkdir6, writeFile as writeFile6 } from "fs/promises";
|
|
16874
|
-
import { join as
|
|
17237
|
+
import { join as join25, dirname as dirname4 } from "path";
|
|
16875
17238
|
async function downloadGithubFolder(repoUrl, srcPath, destPath, options = {}) {
|
|
16876
17239
|
const { token, ref } = options;
|
|
16877
17240
|
const match = repoUrl.replace(/\.git$/, "").match(/github\.com\/([^/]+)\/([^/]+)/);
|
|
@@ -16907,7 +17270,7 @@ async function downloadGithubFolder(repoUrl, srcPath, destPath, options = {}) {
|
|
|
16907
17270
|
await Promise.all(
|
|
16908
17271
|
files.map(async (file) => {
|
|
16909
17272
|
const relativePath = file.path.slice(prefix.length);
|
|
16910
|
-
const localPath =
|
|
17273
|
+
const localPath = join25(destPath, relativePath);
|
|
16911
17274
|
await mkdir6(dirname4(localPath), { recursive: true });
|
|
16912
17275
|
const rawUrl = `https://raw.githubusercontent.com/${owner}/${repo}/${refParam}/${file.path}`;
|
|
16913
17276
|
const fileRes = await fetch(rawUrl, { headers });
|
|
@@ -17049,7 +17412,7 @@ async function handleSaveThreadAIFile(c, threadManager) {
|
|
|
17049
17412
|
if (!absPath) {
|
|
17050
17413
|
return c.json({ error: { code: "BAD_REQUEST", message: "Invalid file path" } }, 400);
|
|
17051
17414
|
}
|
|
17052
|
-
const parentDir =
|
|
17415
|
+
const parentDir = join26(absPath, "..");
|
|
17053
17416
|
await mkdir7(parentDir, { recursive: true });
|
|
17054
17417
|
await writeFile7(absPath, content, "utf-8");
|
|
17055
17418
|
return c.json({ success: true, path: filePath });
|
|
@@ -17138,10 +17501,10 @@ async function handleCreateThreadAgent(c, threadManager) {
|
|
|
17138
17501
|
if (!thread) {
|
|
17139
17502
|
return errorResponse(c, ErrorCodes.THREAD_NOT_FOUND, `Thread not found: ${threadId}`, 404);
|
|
17140
17503
|
}
|
|
17141
|
-
const agentRelPath =
|
|
17142
|
-
const agentAbsPath =
|
|
17143
|
-
const agentFileRelPath =
|
|
17144
|
-
const agentFileAbsPath =
|
|
17504
|
+
const agentRelPath = join26(".agents", "agents", name);
|
|
17505
|
+
const agentAbsPath = join26(thread.path, agentRelPath);
|
|
17506
|
+
const agentFileRelPath = join26(agentRelPath, "AGENT.md");
|
|
17507
|
+
const agentFileAbsPath = join26(agentAbsPath, "AGENT.md");
|
|
17145
17508
|
if (existsSync15(agentAbsPath)) {
|
|
17146
17509
|
return c.json(
|
|
17147
17510
|
{ error: { code: "CONFLICT", message: `Agent '${name}' already exists` } },
|
|
@@ -17208,10 +17571,10 @@ async function handleCreateThreadSkill(c, threadManager) {
|
|
|
17208
17571
|
if (!thread) {
|
|
17209
17572
|
return errorResponse(c, ErrorCodes.THREAD_NOT_FOUND, `Thread not found: ${threadId}`, 404);
|
|
17210
17573
|
}
|
|
17211
|
-
const skillRelPath =
|
|
17212
|
-
const skillAbsPath =
|
|
17213
|
-
const skillFileRelPath =
|
|
17214
|
-
const skillFileAbsPath =
|
|
17574
|
+
const skillRelPath = join26(".agents", "skills", name);
|
|
17575
|
+
const skillAbsPath = join26(thread.path, skillRelPath);
|
|
17576
|
+
const skillFileRelPath = join26(skillRelPath, "SKILL.md");
|
|
17577
|
+
const skillFileAbsPath = join26(skillAbsPath, "SKILL.md");
|
|
17215
17578
|
if (existsSync15(skillAbsPath)) {
|
|
17216
17579
|
return c.json(
|
|
17217
17580
|
{ error: { code: "CONFLICT", message: `Skill '${name}' already exists` } },
|
|
@@ -17525,7 +17888,7 @@ function createThreadRoutes(threadManager, gitManager, conversationManager) {
|
|
|
17525
17888
|
return handleOpenThread(c, threadManager);
|
|
17526
17889
|
});
|
|
17527
17890
|
router.get("/:id/conversation-folder-path", async (c) => {
|
|
17528
|
-
return handleGetConversationFolderPath(c);
|
|
17891
|
+
return handleGetConversationFolderPath(c, threadManager);
|
|
17529
17892
|
});
|
|
17530
17893
|
router.post("/:id/fix-comments", async (c) => {
|
|
17531
17894
|
return handleFixComments(c, threadManager);
|
|
@@ -17895,12 +18258,12 @@ import { existsSync as existsSync17 } from "fs";
|
|
|
17895
18258
|
// src/features/git/git.utils.ts
|
|
17896
18259
|
init_utils();
|
|
17897
18260
|
import { existsSync as existsSync16, readFileSync as readFileSync5, statSync as statSync4 } from "fs";
|
|
17898
|
-
import { isAbsolute as isAbsolute3, normalize as normalize2, resolve as resolve3, join as
|
|
18261
|
+
import { isAbsolute as isAbsolute3, normalize as normalize2, resolve as resolve3, join as join27 } from "path";
|
|
17899
18262
|
import { completeSimple } from "@mariozechner/pi-ai";
|
|
17900
18263
|
async function resolveModelAndKey(provider, modelId, metadataManager) {
|
|
17901
18264
|
const providerConfig = await resolveProviderConfig(provider);
|
|
17902
18265
|
if (!providerConfig) return null;
|
|
17903
|
-
let apiKey = providerConfig.
|
|
18266
|
+
let apiKey = providerConfig.apiKeyEnv ? process.env[providerConfig.apiKeyEnv] : void 0;
|
|
17904
18267
|
if (!apiKey) {
|
|
17905
18268
|
const providerKeys = await metadataManager.getProviderKeys();
|
|
17906
18269
|
apiKey = providerKeys[providerConfig.id];
|
|
@@ -18152,7 +18515,7 @@ async function getUntrackedFilesDiff(gitRoot) {
|
|
|
18152
18515
|
const parts = [];
|
|
18153
18516
|
const maxFileSize = 1e5;
|
|
18154
18517
|
for (const relPath of untrackedPaths) {
|
|
18155
|
-
const fullPath =
|
|
18518
|
+
const fullPath = join27(gitRoot, relPath);
|
|
18156
18519
|
if (!existsSync16(fullPath)) continue;
|
|
18157
18520
|
try {
|
|
18158
18521
|
if (statSync4(fullPath).isDirectory()) continue;
|
|
@@ -19830,15 +20193,6 @@ async function gitSyncBranchHandler(c, metadataManager) {
|
|
|
19830
20193
|
400
|
|
19831
20194
|
);
|
|
19832
20195
|
}
|
|
19833
|
-
if (currentBranch === defaultBranch) {
|
|
19834
|
-
console.log(`[sync-branch] Already on default branch: ${defaultBranch}`);
|
|
19835
|
-
return c.json(
|
|
19836
|
-
{
|
|
19837
|
-
error: `Already on default branch (${defaultBranch})`
|
|
19838
|
-
},
|
|
19839
|
-
400
|
|
19840
|
-
);
|
|
19841
|
-
}
|
|
19842
20196
|
const { spawnProcess: spawnProcess2 } = await Promise.resolve().then(() => (init_utils(), utils_exports));
|
|
19843
20197
|
return new Promise((resolve6) => {
|
|
19844
20198
|
console.log(`[sync-branch] Checking branch status...`);
|
|
@@ -19974,13 +20328,12 @@ async function gitSyncBranchHandler(c, metadataManager) {
|
|
|
19974
20328
|
}
|
|
19975
20329
|
pullProc.on("close", (code) => {
|
|
19976
20330
|
if (code === 0) {
|
|
19977
|
-
|
|
19978
|
-
|
|
19979
|
-
);
|
|
20331
|
+
const message = currentBranch === defaultBranch ? `Successfully pulled latest changes from origin/${defaultBranch}` : `Successfully synced ${currentBranch} with ${defaultBranch}`;
|
|
20332
|
+
console.log(`[sync-branch] \u2713 ${message}`);
|
|
19980
20333
|
resolve6(
|
|
19981
20334
|
c.json({
|
|
19982
20335
|
success: true,
|
|
19983
|
-
message
|
|
20336
|
+
message,
|
|
19984
20337
|
branch: currentBranch,
|
|
19985
20338
|
defaultBranch
|
|
19986
20339
|
})
|
|
@@ -20522,7 +20875,7 @@ function createUpdateRoutes(updater) {
|
|
|
20522
20875
|
// src/features/mcp/mcp.routes.ts
|
|
20523
20876
|
import { Hono as Hono13 } from "hono";
|
|
20524
20877
|
import { readFile as readFile16, writeFile as writeFile8, mkdir as mkdir9, access as access6 } from "fs/promises";
|
|
20525
|
-
import { join as
|
|
20878
|
+
import { join as join28, dirname as dirname6 } from "path";
|
|
20526
20879
|
|
|
20527
20880
|
// src/features/mcp/mcp.popular.json
|
|
20528
20881
|
var mcp_popular_default = [
|
|
@@ -20633,7 +20986,7 @@ var mcp_popular_default = [
|
|
|
20633
20986
|
var MCP_CONFIG_PATHS2 = [".agents/mcp.json", "mcp.json"];
|
|
20634
20987
|
async function readMCPConfig(projectPath) {
|
|
20635
20988
|
for (const configPath of MCP_CONFIG_PATHS2) {
|
|
20636
|
-
const fullPath =
|
|
20989
|
+
const fullPath = join28(projectPath, configPath);
|
|
20637
20990
|
try {
|
|
20638
20991
|
await access6(fullPath);
|
|
20639
20992
|
const content = await readFile16(fullPath, "utf-8");
|
|
@@ -20647,7 +21000,7 @@ async function readMCPConfig(projectPath) {
|
|
|
20647
21000
|
}
|
|
20648
21001
|
async function writeMCPConfig(projectPath, config) {
|
|
20649
21002
|
const existing = await readMCPConfig(projectPath);
|
|
20650
|
-
const targetPath = existing?.filePath ??
|
|
21003
|
+
const targetPath = existing?.filePath ?? join28(projectPath, ".agents/mcp.json");
|
|
20651
21004
|
await mkdir9(dirname6(targetPath), { recursive: true });
|
|
20652
21005
|
await writeFile8(targetPath, JSON.stringify(config, null, 2), "utf-8");
|
|
20653
21006
|
}
|
|
@@ -21140,7 +21493,7 @@ async function generateImage(c, metadataManager, conversationManager, threadMana
|
|
|
21140
21493
|
if (!providerConfig) {
|
|
21141
21494
|
return c.json({ error: `Unknown provider: ${provider}` }, 400);
|
|
21142
21495
|
}
|
|
21143
|
-
let apiKey = providerConfig.
|
|
21496
|
+
let apiKey = providerConfig.apiKeyEnv ? process.env[providerConfig.apiKeyEnv] : void 0;
|
|
21144
21497
|
if (!apiKey) {
|
|
21145
21498
|
const providerKeys = await metadataManager.getProviderKeys();
|
|
21146
21499
|
apiKey = providerKeys[providerConfig.id];
|
|
@@ -21321,7 +21674,7 @@ async function clipboardWriteImage(c) {
|
|
|
21321
21674
|
}
|
|
21322
21675
|
|
|
21323
21676
|
// src/features/image/image-save.route.ts
|
|
21324
|
-
import { join as
|
|
21677
|
+
import { join as join29 } from "path";
|
|
21325
21678
|
var Utils5 = null;
|
|
21326
21679
|
var utilsLoaded5 = false;
|
|
21327
21680
|
async function loadUtils5() {
|
|
@@ -21358,7 +21711,7 @@ async function saveImage(c) {
|
|
|
21358
21711
|
return c.json({ error: "imageUrl or imageData is required" }, 400);
|
|
21359
21712
|
}
|
|
21360
21713
|
const name = filename ?? `generated-image-${Date.now()}.png`;
|
|
21361
|
-
const savePath =
|
|
21714
|
+
const savePath = join29(Utils5.paths.downloads, name);
|
|
21362
21715
|
await Bun.write(savePath, data);
|
|
21363
21716
|
return c.json({ success: true, path: savePath });
|
|
21364
21717
|
} catch (error) {
|
|
@@ -21447,20 +21800,102 @@ function createBrowserJsRoutes(threadManager) {
|
|
|
21447
21800
|
return router;
|
|
21448
21801
|
}
|
|
21449
21802
|
|
|
21803
|
+
// src/features/voice-model/voice-model.routes.ts
|
|
21804
|
+
import { Hono as Hono21 } from "hono";
|
|
21805
|
+
var MODEL_URL = "https://install.tarsk.io/voice-models/ggml-tiny.en.bin";
|
|
21806
|
+
function createVoiceModelRoutes() {
|
|
21807
|
+
const router = new Hono21();
|
|
21808
|
+
router.get("/download", async (c) => {
|
|
21809
|
+
try {
|
|
21810
|
+
const response = await fetch(MODEL_URL);
|
|
21811
|
+
if (!response.ok) {
|
|
21812
|
+
return errorResponse(
|
|
21813
|
+
c,
|
|
21814
|
+
ErrorCodes.EXTERNAL_API_ERROR,
|
|
21815
|
+
`Download server returned ${response.status} ${response.statusText}`,
|
|
21816
|
+
502
|
|
21817
|
+
);
|
|
21818
|
+
}
|
|
21819
|
+
const contentLength = response.headers.get("content-length");
|
|
21820
|
+
const headers = {
|
|
21821
|
+
"Content-Type": "application/octet-stream"
|
|
21822
|
+
};
|
|
21823
|
+
if (contentLength) {
|
|
21824
|
+
headers["Content-Length"] = contentLength;
|
|
21825
|
+
}
|
|
21826
|
+
return new Response(response.body, {
|
|
21827
|
+
status: 200,
|
|
21828
|
+
headers
|
|
21829
|
+
});
|
|
21830
|
+
} catch (error) {
|
|
21831
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
21832
|
+
return errorResponse(
|
|
21833
|
+
c,
|
|
21834
|
+
ErrorCodes.EXTERNAL_API_ERROR,
|
|
21835
|
+
`Failed to download voice model: ${message}`,
|
|
21836
|
+
502,
|
|
21837
|
+
message
|
|
21838
|
+
);
|
|
21839
|
+
}
|
|
21840
|
+
});
|
|
21841
|
+
return router;
|
|
21842
|
+
}
|
|
21843
|
+
|
|
21844
|
+
// src/features/logs/logs.routes.ts
|
|
21845
|
+
import { Hono as Hono22 } from "hono";
|
|
21846
|
+
function createLogsRoutes() {
|
|
21847
|
+
const router = new Hono22();
|
|
21848
|
+
router.post("/", async (c) => {
|
|
21849
|
+
let body = {};
|
|
21850
|
+
try {
|
|
21851
|
+
body = await c.req.json();
|
|
21852
|
+
} catch {
|
|
21853
|
+
return c.json({ ok: false, error: "invalid json" }, 400);
|
|
21854
|
+
}
|
|
21855
|
+
const { level = "INFO", context = "app", message = "", data } = body;
|
|
21856
|
+
let line = `[VOICE] [${level}] [${context}] ${message}`;
|
|
21857
|
+
if (data !== void 0 && data !== "") {
|
|
21858
|
+
try {
|
|
21859
|
+
line += ` | ${JSON.stringify(data)}`;
|
|
21860
|
+
} catch {
|
|
21861
|
+
line += ` | [non-serializable]`;
|
|
21862
|
+
}
|
|
21863
|
+
}
|
|
21864
|
+
if (level === "ERROR") {
|
|
21865
|
+
console.error(line);
|
|
21866
|
+
} else if (level === "WARN") {
|
|
21867
|
+
console.warn(line);
|
|
21868
|
+
} else {
|
|
21869
|
+
console.log(line);
|
|
21870
|
+
}
|
|
21871
|
+
return c.json({ ok: true });
|
|
21872
|
+
});
|
|
21873
|
+
router.delete("/", (c) => {
|
|
21874
|
+
console.log("[VOICE] Log cleared by client request");
|
|
21875
|
+
return c.json({ ok: true });
|
|
21876
|
+
});
|
|
21877
|
+
return router;
|
|
21878
|
+
}
|
|
21879
|
+
|
|
21450
21880
|
// src/server.ts
|
|
21451
21881
|
var __filename = fileURLToPath(import.meta.url);
|
|
21452
21882
|
var __dirname = path4.dirname(__filename);
|
|
21453
21883
|
async function startTarskServer(options) {
|
|
21454
21884
|
const { isDebug: isDebug2, publicDir: publicDirOverride } = options;
|
|
21455
21885
|
const port = isDebug2 ? 462 : process.env.PORT ? parseInt(process.env.PORT) : 641;
|
|
21456
|
-
const app = new
|
|
21886
|
+
const app = new Hono23();
|
|
21457
21887
|
app.use("/*", cors());
|
|
21888
|
+
app.use("/*", async (c, next) => {
|
|
21889
|
+
c.header("Cross-Origin-Opener-Policy", "same-origin");
|
|
21890
|
+
c.header("Cross-Origin-Embedder-Policy", "require-corp");
|
|
21891
|
+
return next();
|
|
21892
|
+
});
|
|
21458
21893
|
app.use("/*", async (c, next) => {
|
|
21459
21894
|
if (c.req.path.startsWith("/api/")) {
|
|
21460
21895
|
const method = c.req.method;
|
|
21461
21896
|
const reqPath = c.req.path;
|
|
21462
21897
|
const fullUrl = `http://localhost:${port}${reqPath}`;
|
|
21463
|
-
if (!fullUrl.includes("processing")) {
|
|
21898
|
+
if (!fullUrl.includes("processing") && !reqPath.startsWith("/api/logs")) {
|
|
21464
21899
|
console.log(`${method} ${fullUrl}`);
|
|
21465
21900
|
}
|
|
21466
21901
|
}
|
|
@@ -21485,6 +21920,7 @@ async function startTarskServer(options) {
|
|
|
21485
21920
|
const conversationManager = new ConversationManagerImpl(dataDir);
|
|
21486
21921
|
const agentExecutor = new PiExecutorImpl(metadataManager);
|
|
21487
21922
|
await metadataManager.initialize();
|
|
21923
|
+
updateRuntimeEnv(await metadataManager.getProviderEnvValues());
|
|
21488
21924
|
await conversationManager.initialize();
|
|
21489
21925
|
app.get("/health", (c) => {
|
|
21490
21926
|
return c.json({
|
|
@@ -21525,6 +21961,8 @@ async function startTarskServer(options) {
|
|
|
21525
21961
|
app.route("/api/scaffold", createScaffoldRoutes(projectManager));
|
|
21526
21962
|
app.route("/api/ask-user", createAskUserRoutes());
|
|
21527
21963
|
app.route("/api/browser-js", createBrowserJsRoutes(threadManager));
|
|
21964
|
+
app.route("/api/voice-model", createVoiceModelRoutes());
|
|
21965
|
+
app.route("/api/logs", createLogsRoutes());
|
|
21528
21966
|
app.route("/api/update", createUpdateRoutes(options.updater));
|
|
21529
21967
|
createSlashCommandRoutes(app, threadManager);
|
|
21530
21968
|
createRuleRoutes(app, projectManager);
|
|
@@ -21583,7 +22021,6 @@ async function startTarskServer(options) {
|
|
|
21583
22021
|
// src/index.ts
|
|
21584
22022
|
import fs4 from "fs";
|
|
21585
22023
|
import path5 from "path";
|
|
21586
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
21587
22024
|
var args = process.argv.slice(2);
|
|
21588
22025
|
var isDebug = args.includes("--debug");
|
|
21589
22026
|
var shouldOpenBrowser = args.includes("--open");
|
|
@@ -21591,10 +22028,9 @@ if (!isDebug) {
|
|
|
21591
22028
|
console.log = () => {
|
|
21592
22029
|
};
|
|
21593
22030
|
} else {
|
|
21594
|
-
const
|
|
21595
|
-
const cliDir = path5.resolve(path5.dirname(__filename2), "..");
|
|
21596
|
-
const logFilePath = path5.join(cliDir, "logs.txt");
|
|
22031
|
+
const logFilePath = path5.join(APP_SUPPORT_DIR, "debug-logs.txt");
|
|
21597
22032
|
try {
|
|
22033
|
+
fs4.mkdirSync(path5.dirname(logFilePath), { recursive: true });
|
|
21598
22034
|
fs4.writeFileSync(logFilePath, "");
|
|
21599
22035
|
} catch {
|
|
21600
22036
|
}
|