tarsk 0.5.44 → 0.5.45
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 +1444 -175
- package/dist/public/assets/{account-view-CTizS7Yb.js → account-view-CQetjhCu.js} +1 -1
- package/dist/public/assets/api-BiOks4gS.js +1 -0
- package/dist/public/assets/{browser-tab-DSpOIu84.js → browser-tab-CqyQXekj.js} +1 -1
- package/dist/public/assets/commit-dialog-CF1xviwe.js +1 -0
- package/dist/public/assets/context-menu-DcqiR-vK.js +1 -0
- package/dist/public/assets/create-repo-dialog-zVm7eDO3.js +1 -0
- package/dist/public/assets/{dialogs-config-CxRnlUcQ.js → dialogs-config-Cmwn8AP3.js} +14 -14
- package/dist/public/assets/diff-view-CKaqAShe.js +3 -0
- package/dist/public/assets/explorer-tab-view-CDb7lUWX.js +2 -0
- package/dist/public/assets/explorer-tree-C-9BuD8n.js +1 -0
- package/dist/public/assets/{explorer-view-BmKKHyrH.js → explorer-view-txWDc-Tg.js} +1 -1
- package/dist/public/assets/git-history-dialog-WU95COhj.js +1 -0
- package/dist/public/assets/git-ops-button-eOAENpwR.js +2 -0
- package/dist/public/assets/history-view-DTJug8Lv.js +9 -0
- package/dist/public/assets/index-CaswO76A.js +89 -0
- package/dist/public/assets/index-WIITae9I.css +1 -0
- package/dist/public/assets/mcp-server-card-BKJl2_RK.js +1 -0
- package/dist/public/assets/merged-pr-dialog-rSHpRiME.js +1 -0
- package/dist/public/assets/model-star-rating-DpF6_FVG.js +1 -0
- package/dist/public/assets/onboarding-ZCm02KNK.js +1 -0
- package/dist/public/assets/project-settings-view-COigmUzh.js +1 -0
- package/dist/public/assets/providers-list-view-BWQJtM6g.js +1 -0
- package/dist/public/assets/pull-request-dialog-DQVVlS8M.js +1 -0
- package/dist/public/assets/pull-with-changes-dialog-DtMvqvd8.js +1 -0
- package/dist/public/assets/push-before-pr-dialog-Bi6MHZiO.js +1 -0
- package/dist/public/assets/radio-group-s_ij8p3t.js +1 -0
- package/dist/public/assets/react-vendor-DMQDEfby.js +16 -0
- package/dist/public/assets/settings-general-view-Cgl5v7l-.js +1 -0
- package/dist/public/assets/settings-instructions-view-DrIwhrWJ.js +1 -0
- package/dist/public/assets/settings-list-B6nYThKU.js +1 -0
- package/dist/public/assets/settings-mcp-servers-view-QPaXg_C8.js +5 -0
- package/dist/public/assets/{settings-models-skeleton-DAEnT4kC.js → settings-models-skeleton-D44p69YI.js} +1 -1
- package/dist/public/assets/settings-models-view--0zYzSYj.js +1 -0
- package/dist/public/assets/settings-rules-view-DtTJGnIp.js +8 -0
- package/dist/public/assets/settings-skills-view-CHT2EsBm.js +3 -0
- package/dist/public/assets/settings-slash-commands-view-RamAmlbD.js +1 -0
- package/dist/public/assets/settings-subagents-view-BY1e9nax.js +2 -0
- package/dist/public/assets/{settings-system-prompt-view-0sXVxXh3.js → settings-system-prompt-view-DNR6lOSK.js} +1 -1
- package/dist/public/assets/settings-view-XnpSVwUl.js +2 -0
- package/dist/public/assets/skeleton-_77ZLAk1.js +1 -0
- package/dist/public/assets/slug-utils-C0Ke4-ko.js +1 -0
- package/dist/public/assets/{terminal-panel-PjkgqbY6.js → terminal-panel-Cp5-H4GS.js} +1 -1
- package/dist/public/assets/{ui-components-DaffuSG7.js → ui-components-BMQhWki4.js} +1 -1
- package/dist/public/assets/{use-deferred-search-b843Ztu9.js → use-deferred-search-D0WCS0gz.js} +1 -1
- package/dist/public/assets/{utils-BIt9KCVx.js → utils-CkwFiI9G.js} +1 -1
- package/dist/public/assets/{web-D6dBzWD1.js → web-CdlbRaqq.js} +1 -1
- package/dist/public/assets/{web-BBGaE-6B.js → web-zT0ibfrw.js} +1 -1
- package/dist/public/index.html +7 -7
- package/package.json +1 -1
- package/dist/public/assets/api-Cuj9iSVv.js +0 -1
- package/dist/public/assets/commit-dialog-kTaKOsNO.js +0 -1
- package/dist/public/assets/context-menu-DRXV-Suu.js +0 -1
- package/dist/public/assets/create-repo-dialog-gIZLk6LZ.js +0 -1
- package/dist/public/assets/diff-view-B2OdJMRe.js +0 -3
- package/dist/public/assets/explorer-tab-view-fejcb7N1.js +0 -2
- package/dist/public/assets/explorer-tree-C2kLAwI0.js +0 -1
- package/dist/public/assets/git-history-dialog-B2ABiCXZ.js +0 -1
- package/dist/public/assets/git-ops-button-Bxx_jyqR.js +0 -2
- package/dist/public/assets/history-view-DebwP9D7.js +0 -9
- package/dist/public/assets/index-C2_gO3dh.css +0 -1
- package/dist/public/assets/index-D9aY24hf.js +0 -89
- package/dist/public/assets/mcp-server-card-B6iwOybN.js +0 -1
- package/dist/public/assets/merged-pr-dialog-CObeSEaB.js +0 -1
- package/dist/public/assets/model-star-rating-BgdOyTTO.js +0 -1
- package/dist/public/assets/onboarding-BN_K08ma.js +0 -1
- package/dist/public/assets/project-settings-view-BDvHOmF8.js +0 -1
- package/dist/public/assets/providers-list-view-DydWz8z3.js +0 -1
- package/dist/public/assets/pull-request-dialog-LJhZpT8C.js +0 -1
- package/dist/public/assets/pull-with-changes-dialog-BPIj9wIk.js +0 -1
- package/dist/public/assets/push-before-pr-dialog-DzBp93KT.js +0 -1
- package/dist/public/assets/radio-group-B9xMCZ1X.js +0 -1
- package/dist/public/assets/react-vendor-CE5Dmjd0.js +0 -16
- package/dist/public/assets/settings-general-view-D_44BPVr.js +0 -1
- package/dist/public/assets/settings-instructions-view-Y6EK67-G.js +0 -1
- package/dist/public/assets/settings-list-BZDwK9bE.js +0 -1
- package/dist/public/assets/settings-mcp-servers-view-B8IqOOnN.js +0 -5
- package/dist/public/assets/settings-models-view-CluPgRkw.js +0 -1
- package/dist/public/assets/settings-rules-view-CxHrCDwn.js +0 -8
- package/dist/public/assets/settings-skills-view-BE9u6FvJ.js +0 -2
- package/dist/public/assets/settings-slash-commands-view-CVOZ3GH3.js +0 -1
- package/dist/public/assets/settings-subagents-view-mguSf6zE.js +0 -2
- package/dist/public/assets/settings-view-BE8EW7s7.js +0 -2
- package/dist/public/assets/skeleton-CVhaOA8k.js +0 -1
- package/dist/public/assets/slug-utils-DGOwJkKI.js +0 -1
package/dist/index.js
CHANGED
|
@@ -60,11 +60,127 @@ var init_programs = __esm({
|
|
|
60
60
|
}
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
+
// ../shared/dist/model-reasoning.js
|
|
64
|
+
function isReasoningEffortLevel(value) {
|
|
65
|
+
return REASONING_EFFORT_LEVEL_SET.has(value);
|
|
66
|
+
}
|
|
67
|
+
function isModelReasoningSetting(value) {
|
|
68
|
+
return value === REASONING_EFFORT_OFF || isReasoningEffortLevel(value);
|
|
69
|
+
}
|
|
70
|
+
function normalizeModelSettings(settings) {
|
|
71
|
+
if (!settings) {
|
|
72
|
+
return { reasoningEffort: REASONING_EFFORT_OFF };
|
|
73
|
+
}
|
|
74
|
+
if (settings.reasoningEffort !== void 0) {
|
|
75
|
+
return { reasoningEffort: settings.reasoningEffort };
|
|
76
|
+
}
|
|
77
|
+
if (settings.reasoning !== void 0) {
|
|
78
|
+
return {
|
|
79
|
+
reasoningEffort: settings.reasoning ? "medium" : REASONING_EFFORT_OFF
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return { reasoningEffort: REASONING_EFFORT_OFF };
|
|
83
|
+
}
|
|
84
|
+
function isReasoningEnabled(settings) {
|
|
85
|
+
return normalizeModelSettings(settings).reasoningEffort !== REASONING_EFFORT_OFF;
|
|
86
|
+
}
|
|
87
|
+
function getThinkingLevelFromSettings(settings) {
|
|
88
|
+
return normalizeModelSettings(settings).reasoningEffort ?? REASONING_EFFORT_OFF;
|
|
89
|
+
}
|
|
90
|
+
function clampReasoningEffort(effort, supportedEfforts) {
|
|
91
|
+
if (effort === REASONING_EFFORT_OFF || supportedEfforts.length === 0) {
|
|
92
|
+
return REASONING_EFFORT_OFF;
|
|
93
|
+
}
|
|
94
|
+
if (supportedEfforts.includes(effort)) {
|
|
95
|
+
return effort;
|
|
96
|
+
}
|
|
97
|
+
return supportedEfforts.includes("medium") ? "medium" : supportedEfforts[0] ?? REASONING_EFFORT_OFF;
|
|
98
|
+
}
|
|
99
|
+
var REASONING_EFFORT_OFF, REASONING_EFFORT_LEVELS, REASONING_EFFORT_LEVEL_SET;
|
|
100
|
+
var init_model_reasoning = __esm({
|
|
101
|
+
"../shared/dist/model-reasoning.js"() {
|
|
102
|
+
"use strict";
|
|
103
|
+
REASONING_EFFORT_OFF = "off";
|
|
104
|
+
REASONING_EFFORT_LEVELS = ["minimal", "low", "medium", "high", "xhigh"];
|
|
105
|
+
REASONING_EFFORT_LEVEL_SET = new Set(REASONING_EFFORT_LEVELS);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
63
109
|
// ../shared/dist/models.js
|
|
64
110
|
var init_models = __esm({
|
|
65
111
|
"../shared/dist/models.js"() {
|
|
66
112
|
"use strict";
|
|
67
113
|
init_programs();
|
|
114
|
+
init_model_reasoning();
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// ../shared/dist/model-aliases.js
|
|
119
|
+
function isModelAliasName(value) {
|
|
120
|
+
return MODEL_ALIAS_NAME_SET.has(value);
|
|
121
|
+
}
|
|
122
|
+
function normalizeProjectModelAliases(value) {
|
|
123
|
+
if (value == null) {
|
|
124
|
+
return void 0;
|
|
125
|
+
}
|
|
126
|
+
if (typeof value !== "object" || Array.isArray(value)) {
|
|
127
|
+
throw new Error("modelAliases must be an object");
|
|
128
|
+
}
|
|
129
|
+
const normalized = {};
|
|
130
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
131
|
+
if (!isModelAliasName(key)) {
|
|
132
|
+
throw new Error(`Invalid model alias: ${key}`);
|
|
133
|
+
}
|
|
134
|
+
if (entry == null) {
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
if (typeof entry !== "object" || Array.isArray(entry)) {
|
|
138
|
+
throw new Error(`Invalid model alias target for '${key}'`);
|
|
139
|
+
}
|
|
140
|
+
const model = typeof entry.model === "string" ? entry.model.trim() : "";
|
|
141
|
+
const provider = typeof entry.provider === "string" ? entry.provider.trim() : "";
|
|
142
|
+
if (!model || !provider) {
|
|
143
|
+
throw new Error(`Model alias '${key}' requires both model and provider`);
|
|
144
|
+
}
|
|
145
|
+
normalized[key] = { model, provider };
|
|
146
|
+
}
|
|
147
|
+
return Object.keys(normalized).length > 0 ? normalized : void 0;
|
|
148
|
+
}
|
|
149
|
+
function resolveModelAlias(model, aliases) {
|
|
150
|
+
if (!isModelAliasName(model)) {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
return aliases?.[model] ?? null;
|
|
154
|
+
}
|
|
155
|
+
function getEnabledModelIdsForProvider(provider, enabledModelsByProvider) {
|
|
156
|
+
const providerSlug = provider.toLowerCase();
|
|
157
|
+
if (enabledModelsByProvider[providerSlug]) {
|
|
158
|
+
return enabledModelsByProvider[providerSlug];
|
|
159
|
+
}
|
|
160
|
+
const matchedEntry = Object.entries(enabledModelsByProvider).find(([key]) => key.toLowerCase() === providerSlug);
|
|
161
|
+
return matchedEntry?.[1] ?? [];
|
|
162
|
+
}
|
|
163
|
+
function isModelAliasTargetEnabled(target, enabledModelsByProvider) {
|
|
164
|
+
const enabledModelIds = getEnabledModelIdsForProvider(target.provider, enabledModelsByProvider);
|
|
165
|
+
return enabledModelIds.includes(target.model);
|
|
166
|
+
}
|
|
167
|
+
function validateProjectModelAliasesAgainstEnabled(aliases, enabledModelsByProvider) {
|
|
168
|
+
for (const aliasName of MODEL_ALIAS_NAMES) {
|
|
169
|
+
const target = aliases[aliasName];
|
|
170
|
+
if (!target) {
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
if (!isModelAliasTargetEnabled(target, enabledModelsByProvider)) {
|
|
174
|
+
throw new Error(`Model alias '${aliasName}' must reference an enabled model. '${target.provider}/${target.model}' is not enabled.`);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
var MODEL_ALIAS_NAMES, MODEL_ALIAS_NAME_SET;
|
|
179
|
+
var init_model_aliases = __esm({
|
|
180
|
+
"../shared/dist/model-aliases.js"() {
|
|
181
|
+
"use strict";
|
|
182
|
+
MODEL_ALIAS_NAMES = ["fast", "local", "cheap", "smart"];
|
|
183
|
+
MODEL_ALIAS_NAME_SET = new Set(MODEL_ALIAS_NAMES);
|
|
68
184
|
}
|
|
69
185
|
});
|
|
70
186
|
|
|
@@ -695,13 +811,14 @@ function resolveLocalProviderApiUrl(envValues) {
|
|
|
695
811
|
function isLocalProvider(providerId) {
|
|
696
812
|
return providerId.toLowerCase() === LOCAL_PROVIDER_ID;
|
|
697
813
|
}
|
|
698
|
-
var LOCAL_PROVIDER_ID, LOCAL_PROVIDER_NAME, LOCAL_PROVIDER_DEFAULT_API_URL, LOCAL_API_URL_ENV, LOCAL_API_KEY_ENV;
|
|
814
|
+
var LOCAL_PROVIDER_ID, LOCAL_PROVIDER_NAME, LOCAL_PROVIDER_DEFAULT_API_URL, LOCAL_MODELS_FETCH_TIMEOUT_MS, LOCAL_API_URL_ENV, LOCAL_API_KEY_ENV;
|
|
699
815
|
var init_providers = __esm({
|
|
700
816
|
"../shared/dist/providers.js"() {
|
|
701
817
|
"use strict";
|
|
702
818
|
LOCAL_PROVIDER_ID = "local";
|
|
703
819
|
LOCAL_PROVIDER_NAME = "Local";
|
|
704
820
|
LOCAL_PROVIDER_DEFAULT_API_URL = "http://127.0.0.1:8000/v1";
|
|
821
|
+
LOCAL_MODELS_FETCH_TIMEOUT_MS = 5e3;
|
|
705
822
|
LOCAL_API_URL_ENV = "LOCAL_API_URL";
|
|
706
823
|
LOCAL_API_KEY_ENV = "LOCAL_API_KEY";
|
|
707
824
|
}
|
|
@@ -867,11 +984,58 @@ var init_tmp_images = __esm({
|
|
|
867
984
|
}
|
|
868
985
|
});
|
|
869
986
|
|
|
987
|
+
// ../shared/dist/git-status-dot.js
|
|
988
|
+
function computeGitStatusDot(status) {
|
|
989
|
+
if (status.hasChanges)
|
|
990
|
+
return "changes";
|
|
991
|
+
if (status.hasUnpushedCommits && status.prExists)
|
|
992
|
+
return "update-pr";
|
|
993
|
+
if (status.prExists && status.prState === "OPEN" && !status.hasUnpushedCommits) {
|
|
994
|
+
return "open-pr";
|
|
995
|
+
}
|
|
996
|
+
return false;
|
|
997
|
+
}
|
|
998
|
+
var init_git_status_dot = __esm({
|
|
999
|
+
"../shared/dist/git-status-dot.js"() {
|
|
1000
|
+
"use strict";
|
|
1001
|
+
}
|
|
1002
|
+
});
|
|
1003
|
+
|
|
1004
|
+
// ../shared/dist/skill-sources.js
|
|
1005
|
+
function isCustomSkillSourceId(sourceId) {
|
|
1006
|
+
return sourceId.startsWith(CUSTOM_SKILL_SOURCE_ID_PREFIX);
|
|
1007
|
+
}
|
|
1008
|
+
function slugifySkillSourceTitle(title) {
|
|
1009
|
+
return title.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64) || "source";
|
|
1010
|
+
}
|
|
1011
|
+
function createCustomSkillSourceId(title, existingIds) {
|
|
1012
|
+
const slug = slugifySkillSourceTitle(title);
|
|
1013
|
+
const base = `${CUSTOM_SKILL_SOURCE_ID_PREFIX}${slug}`;
|
|
1014
|
+
const taken = new Set(existingIds);
|
|
1015
|
+
if (!taken.has(base)) {
|
|
1016
|
+
return base;
|
|
1017
|
+
}
|
|
1018
|
+
let suffix = 2;
|
|
1019
|
+
while (taken.has(`${base}-${suffix}`)) {
|
|
1020
|
+
suffix += 1;
|
|
1021
|
+
}
|
|
1022
|
+
return `${base}-${suffix}`;
|
|
1023
|
+
}
|
|
1024
|
+
var CUSTOM_SKILL_SOURCE_ID_PREFIX;
|
|
1025
|
+
var init_skill_sources = __esm({
|
|
1026
|
+
"../shared/dist/skill-sources.js"() {
|
|
1027
|
+
"use strict";
|
|
1028
|
+
CUSTOM_SKILL_SOURCE_ID_PREFIX = "custom-";
|
|
1029
|
+
}
|
|
1030
|
+
});
|
|
1031
|
+
|
|
870
1032
|
// ../shared/dist/index.js
|
|
871
1033
|
var init_dist = __esm({
|
|
872
1034
|
"../shared/dist/index.js"() {
|
|
873
1035
|
"use strict";
|
|
874
1036
|
init_models();
|
|
1037
|
+
init_model_aliases();
|
|
1038
|
+
init_model_reasoning();
|
|
875
1039
|
init_commit_methods();
|
|
876
1040
|
init_package_managers();
|
|
877
1041
|
init_programs();
|
|
@@ -889,6 +1053,8 @@ var init_dist = __esm({
|
|
|
889
1053
|
init_project_env_vars();
|
|
890
1054
|
init_browser_tool();
|
|
891
1055
|
init_tmp_images();
|
|
1056
|
+
init_git_status_dot();
|
|
1057
|
+
init_skill_sources();
|
|
892
1058
|
}
|
|
893
1059
|
});
|
|
894
1060
|
|
|
@@ -1966,6 +2132,14 @@ async function runMigrations(db) {
|
|
|
1966
2132
|
tarskDebugLog("[db] Running migration: Adding allowedFetchDomains column to projects");
|
|
1967
2133
|
await db.execute(`ALTER TABLE projects ADD COLUMN allowedFetchDomains TEXT`);
|
|
1968
2134
|
}
|
|
2135
|
+
const modelAliasesProjectsInfo = await db.execute(`PRAGMA table_info(projects)`);
|
|
2136
|
+
const hasModelAliases = modelAliasesProjectsInfo.rows.some(
|
|
2137
|
+
(col) => col.name === "modelAliases"
|
|
2138
|
+
);
|
|
2139
|
+
if (!hasModelAliases) {
|
|
2140
|
+
tarskDebugLog("[db] Running migration: Adding modelAliases column to projects");
|
|
2141
|
+
await db.execute(`ALTER TABLE projects ADD COLUMN modelAliases TEXT`);
|
|
2142
|
+
}
|
|
1969
2143
|
const projectScriptsExists = await db.execute(
|
|
1970
2144
|
`SELECT name FROM sqlite_master WHERE type='table' AND name='project_scripts'`
|
|
1971
2145
|
);
|
|
@@ -3496,9 +3670,9 @@ function createSimpleGrepTool(cwd, options) {
|
|
|
3496
3670
|
const effectiveLimit = Math.max(1, limit ?? DEFAULT_LIMIT2);
|
|
3497
3671
|
const formatPath = (filePath) => {
|
|
3498
3672
|
if (isDirectory) {
|
|
3499
|
-
const
|
|
3500
|
-
if (
|
|
3501
|
-
return
|
|
3673
|
+
const relative9 = path2.relative(searchPath, filePath);
|
|
3674
|
+
if (relative9 && !relative9.startsWith("..")) {
|
|
3675
|
+
return relative9.replace(/\\/g, "/");
|
|
3502
3676
|
}
|
|
3503
3677
|
}
|
|
3504
3678
|
return path2.basename(filePath);
|
|
@@ -3838,9 +4012,9 @@ function parseRipgrepOutput(stdout) {
|
|
|
3838
4012
|
return stdout.trim().split("\n").map((line) => line.replace(/\r$/, "")).filter(Boolean);
|
|
3839
4013
|
}
|
|
3840
4014
|
function toRelativePath(cwd, filePath) {
|
|
3841
|
-
const
|
|
3842
|
-
if (
|
|
3843
|
-
return
|
|
4015
|
+
const relative9 = path3.relative(cwd, filePath);
|
|
4016
|
+
if (relative9 && !relative9.startsWith("..") && !path3.isAbsolute(relative9)) {
|
|
4017
|
+
return relative9.replace(/\\/g, "/");
|
|
3844
4018
|
}
|
|
3845
4019
|
return filePath.replace(/\\/g, "/");
|
|
3846
4020
|
}
|
|
@@ -6282,6 +6456,7 @@ async function getCatalogProvider(providerId) {
|
|
|
6282
6456
|
}
|
|
6283
6457
|
|
|
6284
6458
|
// src/features/models/local-models.ts
|
|
6459
|
+
init_dist();
|
|
6285
6460
|
function normalizeModelsBaseUrl(apiUrl) {
|
|
6286
6461
|
const trimmed = apiUrl.trim().replace(/\/+$/, "");
|
|
6287
6462
|
return trimmed.endsWith("/v1") ? trimmed : `${trimmed}/v1`;
|
|
@@ -6296,9 +6471,15 @@ async function fetchLocalModels(apiUrl, apiKey) {
|
|
|
6296
6471
|
try {
|
|
6297
6472
|
response = await fetch(`${baseUrl}/models`, {
|
|
6298
6473
|
method: "GET",
|
|
6299
|
-
headers
|
|
6474
|
+
headers,
|
|
6475
|
+
signal: AbortSignal.timeout(LOCAL_MODELS_FETCH_TIMEOUT_MS)
|
|
6300
6476
|
});
|
|
6301
6477
|
} catch (error) {
|
|
6478
|
+
if (error instanceof Error && error.name === "TimeoutError") {
|
|
6479
|
+
throw new Error(
|
|
6480
|
+
`Local API at ${baseUrl}/models did not respond within ${LOCAL_MODELS_FETCH_TIMEOUT_MS / 1e3} seconds`
|
|
6481
|
+
);
|
|
6482
|
+
}
|
|
6302
6483
|
const detail = error instanceof Error ? error.message : "Unknown error";
|
|
6303
6484
|
throw new Error(`Could not reach local API at ${baseUrl}/models: ${detail}`);
|
|
6304
6485
|
}
|
|
@@ -6327,6 +6508,119 @@ async function fetchLocalModels(apiUrl, apiKey) {
|
|
|
6327
6508
|
}));
|
|
6328
6509
|
}
|
|
6329
6510
|
|
|
6511
|
+
// src/agent/agent.model-resolver.ts
|
|
6512
|
+
init_dist();
|
|
6513
|
+
import {
|
|
6514
|
+
getModel,
|
|
6515
|
+
getSupportedThinkingLevels
|
|
6516
|
+
} from "@earendil-works/pi-ai";
|
|
6517
|
+
var DEFAULT_HEADERS = {
|
|
6518
|
+
"HTTP-Referer": "https://tarsk.io",
|
|
6519
|
+
"X-Title": "Tarsk.io"
|
|
6520
|
+
};
|
|
6521
|
+
var PROVIDER_NAME_TO_PI = {
|
|
6522
|
+
anthropic: "anthropic",
|
|
6523
|
+
openai: "openai",
|
|
6524
|
+
google: "google",
|
|
6525
|
+
groq: "groq",
|
|
6526
|
+
cerebras: "cerebras",
|
|
6527
|
+
xai: "xai",
|
|
6528
|
+
openrouter: "openrouter",
|
|
6529
|
+
"github copilot": "github-copilot",
|
|
6530
|
+
minimax: "minimax",
|
|
6531
|
+
"kimi coding plan": "kimi-coding",
|
|
6532
|
+
"hugging face": "huggingface",
|
|
6533
|
+
codex: "openai-codex",
|
|
6534
|
+
mistral: "mistral"
|
|
6535
|
+
};
|
|
6536
|
+
function determineApiType(providerName, apiUrl) {
|
|
6537
|
+
const slug = providerName.toLowerCase();
|
|
6538
|
+
if (isLocalProvider(slug)) {
|
|
6539
|
+
return "openai-completions";
|
|
6540
|
+
}
|
|
6541
|
+
if (slug === "azure" || apiUrl.includes("openai.azure.com")) {
|
|
6542
|
+
return "azure-openai-responses";
|
|
6543
|
+
}
|
|
6544
|
+
if (apiUrl.includes("/anthropic/")) return "anthropic-messages";
|
|
6545
|
+
if (apiUrl.includes("api.anthropic.com")) return "anthropic-messages";
|
|
6546
|
+
return "openai-completions";
|
|
6547
|
+
}
|
|
6548
|
+
var PI_REGISTRY_PROVIDERS_BY_API = {
|
|
6549
|
+
"azure-openai-responses": "azure-openai-responses"
|
|
6550
|
+
};
|
|
6551
|
+
function getLocalModelResolveOptions(providerName, settings) {
|
|
6552
|
+
if (!isLocalProvider(providerName)) {
|
|
6553
|
+
return void 0;
|
|
6554
|
+
}
|
|
6555
|
+
const normalized = normalizeModelSettings(settings);
|
|
6556
|
+
return {
|
|
6557
|
+
reasoning: isReasoningEnabled(normalized)
|
|
6558
|
+
};
|
|
6559
|
+
}
|
|
6560
|
+
function getSupportedReasoningEfforts(providerName, modelId, providerConfig) {
|
|
6561
|
+
const resolved = resolveModel(providerName, modelId, providerConfig, { reasoning: true });
|
|
6562
|
+
return getSupportedThinkingLevels(resolved).filter(
|
|
6563
|
+
(level) => level !== "off"
|
|
6564
|
+
);
|
|
6565
|
+
}
|
|
6566
|
+
function mergeModelHeaders(model) {
|
|
6567
|
+
return { ...model, headers: { ...model.headers, ...DEFAULT_HEADERS } };
|
|
6568
|
+
}
|
|
6569
|
+
function tryGetRegistryModel(provider, modelId) {
|
|
6570
|
+
const model = getModel(provider, modelId);
|
|
6571
|
+
return model ?? void 0;
|
|
6572
|
+
}
|
|
6573
|
+
function applyReasoningOption(model, options) {
|
|
6574
|
+
if (options?.reasoning === void 0) {
|
|
6575
|
+
return model;
|
|
6576
|
+
}
|
|
6577
|
+
return { ...model, reasoning: options.reasoning };
|
|
6578
|
+
}
|
|
6579
|
+
function resolveModel(providerName, modelId, providerConfig, options) {
|
|
6580
|
+
const piProvider = PROVIDER_NAME_TO_PI[providerName.toLowerCase()];
|
|
6581
|
+
if (piProvider) {
|
|
6582
|
+
const model = tryGetRegistryModel(piProvider, modelId);
|
|
6583
|
+
if (model) {
|
|
6584
|
+
return applyReasoningOption(mergeModelHeaders(model), options);
|
|
6585
|
+
}
|
|
6586
|
+
}
|
|
6587
|
+
const baseUrl = providerConfig.api;
|
|
6588
|
+
if (!baseUrl) {
|
|
6589
|
+
throw new Error(`No API URL configured for provider: ${providerName}`);
|
|
6590
|
+
}
|
|
6591
|
+
const apiType = determineApiType(providerName, baseUrl);
|
|
6592
|
+
const registryProvider = PI_REGISTRY_PROVIDERS_BY_API[apiType];
|
|
6593
|
+
if (registryProvider) {
|
|
6594
|
+
const model = tryGetRegistryModel(registryProvider, modelId);
|
|
6595
|
+
if (model) {
|
|
6596
|
+
return applyReasoningOption(
|
|
6597
|
+
mergeModelHeaders({
|
|
6598
|
+
...model,
|
|
6599
|
+
provider: providerName.toLowerCase(),
|
|
6600
|
+
baseUrl
|
|
6601
|
+
}),
|
|
6602
|
+
options
|
|
6603
|
+
);
|
|
6604
|
+
}
|
|
6605
|
+
}
|
|
6606
|
+
return applyReasoningOption(
|
|
6607
|
+
{
|
|
6608
|
+
id: modelId,
|
|
6609
|
+
name: modelId,
|
|
6610
|
+
api: apiType,
|
|
6611
|
+
provider: providerName.toLowerCase(),
|
|
6612
|
+
baseUrl,
|
|
6613
|
+
reasoning: false,
|
|
6614
|
+
input: ["text"],
|
|
6615
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
6616
|
+
contextWindow: 128e3,
|
|
6617
|
+
maxTokens: 16384,
|
|
6618
|
+
headers: DEFAULT_HEADERS
|
|
6619
|
+
},
|
|
6620
|
+
options
|
|
6621
|
+
);
|
|
6622
|
+
}
|
|
6623
|
+
|
|
6330
6624
|
// src/features/models/models.manager.ts
|
|
6331
6625
|
var ModelManager = class {
|
|
6332
6626
|
metadataManager;
|
|
@@ -6341,10 +6635,40 @@ var ModelManager = class {
|
|
|
6341
6635
|
* Get available models for a provider.
|
|
6342
6636
|
* Uses the remote catalog as the source for model lists, names, and pricing.
|
|
6343
6637
|
*/
|
|
6638
|
+
async applyModelSettings(models, providerId) {
|
|
6639
|
+
const settingsByModel = await this.metadataManager.getProviderModelSettings(providerId);
|
|
6640
|
+
if (!isLocalProvider(providerId)) {
|
|
6641
|
+
return models.map((model) => ({
|
|
6642
|
+
...model,
|
|
6643
|
+
settings: normalizeModelSettings(settingsByModel[model.id])
|
|
6644
|
+
}));
|
|
6645
|
+
}
|
|
6646
|
+
const envValues = await this.metadataManager.getProviderEnvValues();
|
|
6647
|
+
const apiUrl = resolveLocalProviderApiUrl({
|
|
6648
|
+
...envValues,
|
|
6649
|
+
[LOCAL_API_URL_ENV]: envValues[LOCAL_API_URL_ENV] ?? process.env[LOCAL_API_URL_ENV]
|
|
6650
|
+
});
|
|
6651
|
+
return models.map((model) => {
|
|
6652
|
+
const supportedReasoningEfforts = getSupportedReasoningEfforts(providerId, model.id, {
|
|
6653
|
+
api: apiUrl || LOCAL_PROVIDER_DEFAULT_API_URL
|
|
6654
|
+
});
|
|
6655
|
+
const normalized = normalizeModelSettings(settingsByModel[model.id]);
|
|
6656
|
+
const reasoningEffort = clampReasoningEffort(
|
|
6657
|
+
normalized.reasoningEffort ?? REASONING_EFFORT_OFF,
|
|
6658
|
+
supportedReasoningEfforts
|
|
6659
|
+
);
|
|
6660
|
+
return {
|
|
6661
|
+
...model,
|
|
6662
|
+
supportedReasoningEfforts,
|
|
6663
|
+
settings: { reasoningEffort }
|
|
6664
|
+
};
|
|
6665
|
+
});
|
|
6666
|
+
}
|
|
6344
6667
|
async getAvailableModels(provider) {
|
|
6345
6668
|
const providerId = provider.toLowerCase();
|
|
6346
6669
|
if (isLocalProvider(providerId)) {
|
|
6347
|
-
|
|
6670
|
+
const models = await this.getLocalAvailableModels();
|
|
6671
|
+
return this.applyModelSettings(models, providerId);
|
|
6348
6672
|
}
|
|
6349
6673
|
const catalogProvider = await getCatalogProvider(providerId);
|
|
6350
6674
|
if (!catalogProvider) {
|
|
@@ -6406,6 +6730,9 @@ var ModelManager = class {
|
|
|
6406
6730
|
return [];
|
|
6407
6731
|
}
|
|
6408
6732
|
}
|
|
6733
|
+
async getModelSettings(provider, modelId) {
|
|
6734
|
+
return this.metadataManager.getModelSettings(provider, modelId);
|
|
6735
|
+
}
|
|
6409
6736
|
/**
|
|
6410
6737
|
* Get a specific model by ID from a provider
|
|
6411
6738
|
* @param provider - Provider name
|
|
@@ -6454,17 +6781,24 @@ var ModelManager = class {
|
|
|
6454
6781
|
}
|
|
6455
6782
|
async getAllEnabledImageModels() {
|
|
6456
6783
|
const enabledByProvider = await this.metadataManager.getAllEnabledImageModels();
|
|
6457
|
-
const
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
|
|
6461
|
-
|
|
6462
|
-
|
|
6463
|
-
|
|
6464
|
-
|
|
6784
|
+
const providers = Object.keys(enabledByProvider);
|
|
6785
|
+
const entries = await Promise.all(
|
|
6786
|
+
providers.map(async (provider) => {
|
|
6787
|
+
try {
|
|
6788
|
+
const availableModels = await this.getAvailableModels(provider);
|
|
6789
|
+
const enabledIds = enabledByProvider[provider];
|
|
6790
|
+
const models = availableModels.filter((model) => enabledIds.includes(model.id));
|
|
6791
|
+
return [provider, models];
|
|
6792
|
+
} catch (error) {
|
|
6793
|
+
console.error(`Failed to get enabled image models for ${provider}:`, error);
|
|
6794
|
+
return [provider, []];
|
|
6465
6795
|
}
|
|
6466
|
-
}
|
|
6467
|
-
|
|
6796
|
+
})
|
|
6797
|
+
);
|
|
6798
|
+
const result = {};
|
|
6799
|
+
for (const [provider, models] of entries) {
|
|
6800
|
+
if (models.length > 0) {
|
|
6801
|
+
result[provider] = models;
|
|
6468
6802
|
}
|
|
6469
6803
|
}
|
|
6470
6804
|
return result;
|
|
@@ -6475,15 +6809,22 @@ var ModelManager = class {
|
|
|
6475
6809
|
*/
|
|
6476
6810
|
async getAllEnabledModels() {
|
|
6477
6811
|
const enabledByProvider = await this.metadataManager.getAllEnabledModels();
|
|
6478
|
-
const
|
|
6479
|
-
|
|
6480
|
-
|
|
6481
|
-
|
|
6482
|
-
|
|
6483
|
-
|
|
6812
|
+
const providers = Object.keys(enabledByProvider);
|
|
6813
|
+
const entries = await Promise.all(
|
|
6814
|
+
providers.map(async (provider) => {
|
|
6815
|
+
try {
|
|
6816
|
+
const models = await this.getEnabledModels(provider);
|
|
6817
|
+
return [provider, models];
|
|
6818
|
+
} catch (error) {
|
|
6819
|
+
console.error(`Failed to get enabled models for ${provider}:`, error);
|
|
6820
|
+
return [provider, []];
|
|
6484
6821
|
}
|
|
6485
|
-
}
|
|
6486
|
-
|
|
6822
|
+
})
|
|
6823
|
+
);
|
|
6824
|
+
const result = {};
|
|
6825
|
+
for (const [provider, models] of entries) {
|
|
6826
|
+
if (models.length > 0) {
|
|
6827
|
+
result[provider] = models;
|
|
6487
6828
|
}
|
|
6488
6829
|
}
|
|
6489
6830
|
return result;
|
|
@@ -8284,65 +8625,6 @@ function getDataDir() {
|
|
|
8284
8625
|
return DATA_DIR;
|
|
8285
8626
|
}
|
|
8286
8627
|
|
|
8287
|
-
// src/agent/agent.model-resolver.ts
|
|
8288
|
-
import { getModel } from "@earendil-works/pi-ai";
|
|
8289
|
-
var DEFAULT_HEADERS = {
|
|
8290
|
-
"HTTP-Referer": "https://tarsk.io",
|
|
8291
|
-
"X-Title": "Tarsk.io"
|
|
8292
|
-
};
|
|
8293
|
-
var PROVIDER_NAME_TO_PI = {
|
|
8294
|
-
anthropic: "anthropic",
|
|
8295
|
-
openai: "openai",
|
|
8296
|
-
google: "google",
|
|
8297
|
-
groq: "groq",
|
|
8298
|
-
cerebras: "cerebras",
|
|
8299
|
-
xai: "xai",
|
|
8300
|
-
openrouter: "openrouter",
|
|
8301
|
-
"github copilot": "github-copilot",
|
|
8302
|
-
minimax: "minimax",
|
|
8303
|
-
"kimi coding plan": "kimi-coding",
|
|
8304
|
-
"hugging face": "huggingface",
|
|
8305
|
-
codex: "openai-codex",
|
|
8306
|
-
mistral: "mistral"
|
|
8307
|
-
};
|
|
8308
|
-
function determineApiType(providerName, apiUrl) {
|
|
8309
|
-
const slug = providerName.toLowerCase();
|
|
8310
|
-
if (slug === "azure" || apiUrl.includes("openai.azure.com")) {
|
|
8311
|
-
return "azure-openai-responses";
|
|
8312
|
-
}
|
|
8313
|
-
if (apiUrl.includes("/anthropic/")) return "anthropic-messages";
|
|
8314
|
-
if (apiUrl.includes("api.anthropic.com")) return "anthropic-messages";
|
|
8315
|
-
return "openai-completions";
|
|
8316
|
-
}
|
|
8317
|
-
function resolveModel(providerName, modelId, providerConfig) {
|
|
8318
|
-
const piProvider = PROVIDER_NAME_TO_PI[providerName.toLowerCase()];
|
|
8319
|
-
if (piProvider) {
|
|
8320
|
-
try {
|
|
8321
|
-
const model = getModel(piProvider, modelId);
|
|
8322
|
-
return { ...model, headers: { ...model.headers, ...DEFAULT_HEADERS } };
|
|
8323
|
-
} catch {
|
|
8324
|
-
}
|
|
8325
|
-
}
|
|
8326
|
-
const baseUrl = providerConfig.api;
|
|
8327
|
-
if (!baseUrl) {
|
|
8328
|
-
throw new Error(`No API URL configured for provider: ${providerName}`);
|
|
8329
|
-
}
|
|
8330
|
-
const apiType = determineApiType(providerName, baseUrl);
|
|
8331
|
-
return {
|
|
8332
|
-
id: modelId,
|
|
8333
|
-
name: modelId,
|
|
8334
|
-
api: apiType,
|
|
8335
|
-
provider: providerName.toLowerCase(),
|
|
8336
|
-
baseUrl,
|
|
8337
|
-
reasoning: true,
|
|
8338
|
-
input: ["text"],
|
|
8339
|
-
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
8340
|
-
contextWindow: 128e3,
|
|
8341
|
-
maxTokens: 16384,
|
|
8342
|
-
headers: DEFAULT_HEADERS
|
|
8343
|
-
};
|
|
8344
|
-
}
|
|
8345
|
-
|
|
8346
8628
|
// src/features/git/git.utils.ts
|
|
8347
8629
|
init_dist();
|
|
8348
8630
|
import { completeSimple } from "@earendil-works/pi-ai";
|
|
@@ -10860,10 +11142,26 @@ async function* loadCodingToolsWithMcpStatus(cwd, options) {
|
|
|
10860
11142
|
}
|
|
10861
11143
|
|
|
10862
11144
|
// src/tools/agent-tool.ts
|
|
11145
|
+
init_dist();
|
|
10863
11146
|
import { Type as Type21 } from "@sinclair/typebox";
|
|
10864
11147
|
|
|
11148
|
+
// src/features/projects/project-model-aliases.ts
|
|
11149
|
+
init_dist();
|
|
11150
|
+
async function validateProjectModelAliasesForSave(metadataManager, aliases) {
|
|
11151
|
+
if (!aliases) {
|
|
11152
|
+
return;
|
|
11153
|
+
}
|
|
11154
|
+
const enabledModelsByProvider = await metadataManager.getAllEnabledModels();
|
|
11155
|
+
validateProjectModelAliasesAgainstEnabled(aliases, enabledModelsByProvider);
|
|
11156
|
+
}
|
|
11157
|
+
async function isProjectModelAliasTargetEnabled(metadataManager, target) {
|
|
11158
|
+
const enabledModelsByProvider = await metadataManager.getAllEnabledModels();
|
|
11159
|
+
return isModelAliasTargetEnabled(target, enabledModelsByProvider);
|
|
11160
|
+
}
|
|
11161
|
+
|
|
10865
11162
|
// src/agent/agent.subagent-executor.ts
|
|
10866
11163
|
import { Agent } from "@earendil-works/pi-agent-core";
|
|
11164
|
+
init_dist();
|
|
10867
11165
|
|
|
10868
11166
|
// src/agent/agent.event-transformer.ts
|
|
10869
11167
|
init_dist();
|
|
@@ -11124,7 +11422,14 @@ async function executeSubagent(options) {
|
|
|
11124
11422
|
`No API key found for subagent provider: ${providerName}. Set ${providerConfig.apiKeyEnv ?? "an API key"} in environment variables or configuration.`
|
|
11125
11423
|
);
|
|
11126
11424
|
}
|
|
11127
|
-
const
|
|
11425
|
+
const localModelSettings = isLocalProvider(providerName) ? await metadataManager.getModelSettings(providerName, model) : {};
|
|
11426
|
+
const thinkingLevel = getThinkingLevelFromSettings(localModelSettings);
|
|
11427
|
+
const resolvedModel = resolveModel(
|
|
11428
|
+
providerName,
|
|
11429
|
+
model,
|
|
11430
|
+
providerConfig,
|
|
11431
|
+
getLocalModelResolveOptions(providerName, localModelSettings)
|
|
11432
|
+
);
|
|
11128
11433
|
console.log(
|
|
11129
11434
|
`[subagent] Starting with model=${resolvedModel.id}, tools=${tools.map((t) => t.name).join(",")}`
|
|
11130
11435
|
);
|
|
@@ -11132,7 +11437,8 @@ async function executeSubagent(options) {
|
|
|
11132
11437
|
initialState: {
|
|
11133
11438
|
systemPrompt,
|
|
11134
11439
|
model: resolvedModel,
|
|
11135
|
-
tools
|
|
11440
|
+
tools,
|
|
11441
|
+
thinkingLevel
|
|
11136
11442
|
},
|
|
11137
11443
|
streamFn: cachedStreamFn,
|
|
11138
11444
|
getApiKey: async () => apiKey
|
|
@@ -11231,6 +11537,31 @@ function filterTools(baseTools, allowedToolNames) {
|
|
|
11231
11537
|
);
|
|
11232
11538
|
return baseTools.filter((t) => allowed.has(t.name));
|
|
11233
11539
|
}
|
|
11540
|
+
function resolveSubagentModelSelection(agentDef, parentModel, parentProvider) {
|
|
11541
|
+
if (agentDef?.model) {
|
|
11542
|
+
return {
|
|
11543
|
+
model: agentDef.model,
|
|
11544
|
+
provider: agentDef.provider ?? parentProvider
|
|
11545
|
+
};
|
|
11546
|
+
}
|
|
11547
|
+
return {
|
|
11548
|
+
model: parentModel,
|
|
11549
|
+
provider: parentProvider
|
|
11550
|
+
};
|
|
11551
|
+
}
|
|
11552
|
+
async function resolveSubagentModelSelectionWithAliases(agentDef, parentModel, parentProvider, modelAliases, metadataManager) {
|
|
11553
|
+
if (agentDef?.model && isModelAliasName(agentDef.model)) {
|
|
11554
|
+
const aliasTarget = resolveModelAlias(agentDef.model, modelAliases);
|
|
11555
|
+
if (aliasTarget && await isProjectModelAliasTargetEnabled(metadataManager, aliasTarget)) {
|
|
11556
|
+
return aliasTarget;
|
|
11557
|
+
}
|
|
11558
|
+
return {
|
|
11559
|
+
model: parentModel,
|
|
11560
|
+
provider: parentProvider
|
|
11561
|
+
};
|
|
11562
|
+
}
|
|
11563
|
+
return resolveSubagentModelSelection(agentDef, parentModel, parentProvider);
|
|
11564
|
+
}
|
|
11234
11565
|
function createAgentTool(options) {
|
|
11235
11566
|
const {
|
|
11236
11567
|
agents,
|
|
@@ -11240,7 +11571,8 @@ function createAgentTool(options) {
|
|
|
11240
11571
|
parentProvider,
|
|
11241
11572
|
defaultSystemPrompt,
|
|
11242
11573
|
onEvent,
|
|
11243
|
-
signal
|
|
11574
|
+
signal,
|
|
11575
|
+
modelAliases
|
|
11244
11576
|
} = options;
|
|
11245
11577
|
return {
|
|
11246
11578
|
name: "agent",
|
|
@@ -11263,8 +11595,13 @@ function createAgentTool(options) {
|
|
|
11263
11595
|
}
|
|
11264
11596
|
const label = description ?? agentName ?? "subagent";
|
|
11265
11597
|
const systemPrompt = agentDef?.systemPrompt ?? defaultSystemPrompt;
|
|
11266
|
-
const model =
|
|
11267
|
-
|
|
11598
|
+
const { model, provider } = await resolveSubagentModelSelectionWithAliases(
|
|
11599
|
+
agentDef,
|
|
11600
|
+
parentModel,
|
|
11601
|
+
parentProvider,
|
|
11602
|
+
modelAliases,
|
|
11603
|
+
metadataManager
|
|
11604
|
+
);
|
|
11268
11605
|
const subTools = filterTools(baseTools, agentDef?.tools);
|
|
11269
11606
|
console.log(
|
|
11270
11607
|
`[agent-tool] Spawning subagent '${label}' with model=${model}, tools=${subTools.map((t) => t.name).join(",")}`
|
|
@@ -12550,7 +12887,14 @@ var PiExecutorImpl = class {
|
|
|
12550
12887
|
);
|
|
12551
12888
|
}
|
|
12552
12889
|
console.log(`[ai] API key source for ${providerName}: ${apiKeySource}`);
|
|
12553
|
-
const
|
|
12890
|
+
const localModelSettings = isLocalProvider(providerName) ? await this.metadataManager.getModelSettings(providerName, model) : {};
|
|
12891
|
+
const thinkingLevel = getThinkingLevelFromSettings(localModelSettings);
|
|
12892
|
+
const resolvedModel = resolveModel(
|
|
12893
|
+
providerName,
|
|
12894
|
+
model,
|
|
12895
|
+
providerConfig,
|
|
12896
|
+
getLocalModelResolveOptions(providerName, localModelSettings)
|
|
12897
|
+
);
|
|
12554
12898
|
console.log("[ai] Resolved model:", {
|
|
12555
12899
|
id: resolvedModel.id,
|
|
12556
12900
|
api: resolvedModel.api,
|
|
@@ -12629,7 +12973,8 @@ var PiExecutorImpl = class {
|
|
|
12629
12973
|
parentProvider: providerName,
|
|
12630
12974
|
defaultSystemPrompt: systemPrompt,
|
|
12631
12975
|
onEvent: (event) => eventQueue.push(event),
|
|
12632
|
-
signal
|
|
12976
|
+
signal,
|
|
12977
|
+
modelAliases: context.modelAliases
|
|
12633
12978
|
});
|
|
12634
12979
|
tools.push(agentToolInstance);
|
|
12635
12980
|
}
|
|
@@ -12639,7 +12984,8 @@ var PiExecutorImpl = class {
|
|
|
12639
12984
|
systemPrompt,
|
|
12640
12985
|
model: resolvedModel,
|
|
12641
12986
|
tools,
|
|
12642
|
-
messages: historyMessages
|
|
12987
|
+
messages: historyMessages,
|
|
12988
|
+
thinkingLevel
|
|
12643
12989
|
},
|
|
12644
12990
|
streamFn: cachedStreamFn,
|
|
12645
12991
|
getApiKey: async () => apiKey,
|
|
@@ -14476,8 +14822,8 @@ async function insertProject(db, project) {
|
|
|
14476
14822
|
try {
|
|
14477
14823
|
await db.execute(
|
|
14478
14824
|
`
|
|
14479
|
-
INSERT INTO projects (id, name, gitUrl, path, createdAt, openWith, commands, setupScript, validationScript, runCommand, commitMethod, planPrompt, testPrompt, reviewPrompt, customSystemPrompt, useCustomSystemPrompt, allowAllCommands, allowedCommandPatterns, allowedFetchDomains)
|
|
14480
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
14825
|
+
INSERT INTO projects (id, name, gitUrl, path, createdAt, openWith, commands, setupScript, validationScript, runCommand, commitMethod, planPrompt, testPrompt, reviewPrompt, customSystemPrompt, useCustomSystemPrompt, allowAllCommands, allowedCommandPatterns, allowedFetchDomains, modelAliases)
|
|
14826
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
14481
14827
|
`,
|
|
14482
14828
|
[
|
|
14483
14829
|
project.id,
|
|
@@ -14498,7 +14844,8 @@ async function insertProject(db, project) {
|
|
|
14498
14844
|
project.useCustomSystemPrompt ? 1 : 0,
|
|
14499
14845
|
project.allowAllCommands === false ? 0 : 1,
|
|
14500
14846
|
project.allowedCommandPatterns ? JSON.stringify(project.allowedCommandPatterns) : null,
|
|
14501
|
-
project.allowedFetchDomains ? JSON.stringify(project.allowedFetchDomains) : null
|
|
14847
|
+
project.allowedFetchDomains ? JSON.stringify(project.allowedFetchDomains) : null,
|
|
14848
|
+
project.modelAliases ? JSON.stringify(project.modelAliases) : null
|
|
14502
14849
|
]
|
|
14503
14850
|
);
|
|
14504
14851
|
} catch (error) {
|
|
@@ -14582,6 +14929,10 @@ async function updateProject(db, id, updates) {
|
|
|
14582
14929
|
updates.allowedFetchDomains.length > 0 ? JSON.stringify(updates.allowedFetchDomains) : null
|
|
14583
14930
|
);
|
|
14584
14931
|
}
|
|
14932
|
+
if (updates.modelAliases !== void 0) {
|
|
14933
|
+
fields.push("modelAliases = ?");
|
|
14934
|
+
values.push(updates.modelAliases ? JSON.stringify(updates.modelAliases) : null);
|
|
14935
|
+
}
|
|
14585
14936
|
if (fields.length === 0) {
|
|
14586
14937
|
return;
|
|
14587
14938
|
}
|
|
@@ -14689,7 +15040,8 @@ function deserializeProjectWithThreads(row, threadIds) {
|
|
|
14689
15040
|
useCustomSystemPrompt: Boolean(row.useCustomSystemPrompt),
|
|
14690
15041
|
allowAllCommands: row.allowAllCommands === null ? true : Boolean(row.allowAllCommands),
|
|
14691
15042
|
allowedCommandPatterns: row.allowedCommandPatterns ? JSON.parse(row.allowedCommandPatterns) : void 0,
|
|
14692
|
-
allowedFetchDomains: row.allowedFetchDomains ? JSON.parse(row.allowedFetchDomains) : void 0
|
|
15043
|
+
allowedFetchDomains: row.allowedFetchDomains ? JSON.parse(row.allowedFetchDomains) : void 0,
|
|
15044
|
+
modelAliases: row.modelAliases ? JSON.parse(row.modelAliases) : void 0
|
|
14693
15045
|
};
|
|
14694
15046
|
}
|
|
14695
15047
|
async function deserializeProject(db, row) {
|
|
@@ -14885,7 +15237,8 @@ async function postChatMessage(c, threadManager, agentExecutor, conversationMana
|
|
|
14885
15237
|
await updateProject(dbInstance2, thread.projectId, {
|
|
14886
15238
|
allowedFetchDomains: domains
|
|
14887
15239
|
});
|
|
14888
|
-
}
|
|
15240
|
+
},
|
|
15241
|
+
modelAliases: project?.modelAliases
|
|
14889
15242
|
};
|
|
14890
15243
|
console.log("[ChatRoute] Execution context:", {
|
|
14891
15244
|
threadId,
|
|
@@ -16992,7 +17345,8 @@ async function computeContextBreakdown(threadId, conversationId, conversationMan
|
|
|
16992
17345
|
parentProvider: thread.modelProvider ?? "",
|
|
16993
17346
|
defaultSystemPrompt: promptSections.systemPrompt,
|
|
16994
17347
|
onEvent: () => {
|
|
16995
|
-
}
|
|
17348
|
+
},
|
|
17349
|
+
modelAliases: project?.modelAliases
|
|
16996
17350
|
});
|
|
16997
17351
|
tools.push(agentToolInstance);
|
|
16998
17352
|
}
|
|
@@ -17404,6 +17758,7 @@ var MetadataManager = class {
|
|
|
17404
17758
|
await setState(this.db, "selectedThreadId", state.selectedThreadId);
|
|
17405
17759
|
await setState(this.db, "enabledModels", state.enabledModels);
|
|
17406
17760
|
await setState(this.db, "enabledImageModels", state.enabledImageModels);
|
|
17761
|
+
await setState(this.db, "modelSettings", state.modelSettings);
|
|
17407
17762
|
await setState(this.db, "providerEnvValues", state.providerEnvValues);
|
|
17408
17763
|
await setState(this.db, "onboardingCompleted", state.onboardingCompleted);
|
|
17409
17764
|
const encryptedKeys = {};
|
|
@@ -17441,6 +17796,7 @@ var MetadataManager = class {
|
|
|
17441
17796
|
const enabledImageModels = mergeEnabledModelsByProviderSlug(rawEnabledImageModels);
|
|
17442
17797
|
const needsEnabledModelsMigration = enabledModelsNeedMigration(rawEnabledModels, enabledModels) || enabledModelsNeedMigration(rawEnabledImageModels, enabledImageModels);
|
|
17443
17798
|
const providerEnvValues = allState.providerEnvValues || {};
|
|
17799
|
+
const modelSettings = allState.modelSettings || {};
|
|
17444
17800
|
const encryptedKeys = allState.providerKeys || {};
|
|
17445
17801
|
const providerKeys = {};
|
|
17446
17802
|
let needsMigration = false;
|
|
@@ -17482,7 +17838,8 @@ var MetadataManager = class {
|
|
|
17482
17838
|
providerKeys,
|
|
17483
17839
|
providerEnvValues,
|
|
17484
17840
|
enabledModels,
|
|
17485
|
-
enabledImageModels
|
|
17841
|
+
enabledImageModels,
|
|
17842
|
+
modelSettings
|
|
17486
17843
|
};
|
|
17487
17844
|
} catch (error) {
|
|
17488
17845
|
throw new Error(`Failed to load state: ${String(error)}`);
|
|
@@ -17673,6 +18030,34 @@ var MetadataManager = class {
|
|
|
17673
18030
|
}
|
|
17674
18031
|
await this.saveState(state);
|
|
17675
18032
|
}
|
|
18033
|
+
async getModelSettings(provider, modelId) {
|
|
18034
|
+
const providerSlug = normalizeProviderSlug(provider);
|
|
18035
|
+
const state = await this.loadState();
|
|
18036
|
+
return state.modelSettings[providerSlug]?.[modelId] ?? {};
|
|
18037
|
+
}
|
|
18038
|
+
async getProviderModelSettings(provider) {
|
|
18039
|
+
const providerSlug = normalizeProviderSlug(provider);
|
|
18040
|
+
const state = await this.loadState();
|
|
18041
|
+
return state.modelSettings[providerSlug] ?? {};
|
|
18042
|
+
}
|
|
18043
|
+
async setModelSettings(provider, modelId, settings) {
|
|
18044
|
+
const providerSlug = normalizeProviderSlug(provider);
|
|
18045
|
+
const state = await this.loadState();
|
|
18046
|
+
if (!state.modelSettings[providerSlug]) {
|
|
18047
|
+
state.modelSettings[providerSlug] = {};
|
|
18048
|
+
}
|
|
18049
|
+
const nextSettings = { ...state.modelSettings[providerSlug][modelId], ...settings };
|
|
18050
|
+
const hasSettings = Object.values(nextSettings).some((value) => value !== void 0);
|
|
18051
|
+
if (hasSettings) {
|
|
18052
|
+
state.modelSettings[providerSlug][modelId] = nextSettings;
|
|
18053
|
+
} else {
|
|
18054
|
+
delete state.modelSettings[providerSlug][modelId];
|
|
18055
|
+
if (Object.keys(state.modelSettings[providerSlug]).length === 0) {
|
|
18056
|
+
delete state.modelSettings[providerSlug];
|
|
18057
|
+
}
|
|
18058
|
+
}
|
|
18059
|
+
await this.saveState(state);
|
|
18060
|
+
}
|
|
17676
18061
|
// ── Image model methods ─────────────────────────────────────────────────────
|
|
17677
18062
|
async enableImageModel(provider, modelId) {
|
|
17678
18063
|
const providerSlug = normalizeProviderSlug(provider);
|
|
@@ -17993,11 +18378,43 @@ async function handleGetModelInfo(c, modelManager) {
|
|
|
17993
18378
|
}
|
|
17994
18379
|
}
|
|
17995
18380
|
|
|
17996
|
-
// src/features/models/models.
|
|
17997
|
-
|
|
17998
|
-
|
|
17999
|
-
|
|
18000
|
-
|
|
18381
|
+
// src/features/models/models-model-settings.route.ts
|
|
18382
|
+
init_dist();
|
|
18383
|
+
async function handleModelSettings(c, metadataManager, modelManager) {
|
|
18384
|
+
try {
|
|
18385
|
+
const provider = c.req.param("provider");
|
|
18386
|
+
const modelId = c.req.param("modelId");
|
|
18387
|
+
if (!provider || !modelId) {
|
|
18388
|
+
return c.json({ error: "provider and modelId are required" }, 400);
|
|
18389
|
+
}
|
|
18390
|
+
if (!isLocalProvider(provider)) {
|
|
18391
|
+
return c.json({ error: "Model settings are only supported for the local provider" }, 400);
|
|
18392
|
+
}
|
|
18393
|
+
const body = await c.req.json();
|
|
18394
|
+
if (body.reasoning !== void 0 && typeof body.reasoning !== "boolean") {
|
|
18395
|
+
return c.json({ error: "reasoning must be a boolean" }, 400);
|
|
18396
|
+
}
|
|
18397
|
+
if (body.reasoningEffort !== void 0 && (typeof body.reasoningEffort !== "string" || !isModelReasoningSetting(body.reasoningEffort))) {
|
|
18398
|
+
return c.json({ error: "reasoningEffort must be a supported reasoning level" }, 400);
|
|
18399
|
+
}
|
|
18400
|
+
const model = await modelManager.getModel(provider, modelId);
|
|
18401
|
+
if (!model) {
|
|
18402
|
+
return c.json({ error: `Model "${modelId}" not found in provider "${provider}"` }, 404);
|
|
18403
|
+
}
|
|
18404
|
+
await metadataManager.setModelSettings(provider, modelId, body);
|
|
18405
|
+
const settings = await metadataManager.getModelSettings(provider, modelId);
|
|
18406
|
+
return c.json({ provider, modelId, settings });
|
|
18407
|
+
} catch (error) {
|
|
18408
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
18409
|
+
return c.json({ error: message }, 500);
|
|
18410
|
+
}
|
|
18411
|
+
}
|
|
18412
|
+
|
|
18413
|
+
// src/features/models/models.routes.ts
|
|
18414
|
+
function createModelRoutes(metadataManager) {
|
|
18415
|
+
const router = new Hono6();
|
|
18416
|
+
const modelManager = new ModelManager(metadataManager);
|
|
18417
|
+
router.get("/", async (c) => {
|
|
18001
18418
|
return handleGetAvailableModels(c, modelManager);
|
|
18002
18419
|
});
|
|
18003
18420
|
router.get("/info", async (c) => {
|
|
@@ -18015,6 +18432,9 @@ function createModelRoutes(metadataManager) {
|
|
|
18015
18432
|
router.post("/:provider/:modelId/disable", async (c) => {
|
|
18016
18433
|
return handleModelDisable(c, modelManager);
|
|
18017
18434
|
});
|
|
18435
|
+
router.post("/:provider/:modelId/settings", async (c) => {
|
|
18436
|
+
return handleModelSettings(c, metadataManager, modelManager);
|
|
18437
|
+
});
|
|
18018
18438
|
router.post("/:provider/refresh", async (c) => {
|
|
18019
18439
|
return handleProviderRefresh(c, modelManager);
|
|
18020
18440
|
});
|
|
@@ -18192,12 +18612,7 @@ async function handleCreateFolderProject(c, projectManager) {
|
|
|
18192
18612
|
}
|
|
18193
18613
|
|
|
18194
18614
|
// src/features/projects/projects-list.route.ts
|
|
18195
|
-
|
|
18196
|
-
if (status.hasChanges) return true;
|
|
18197
|
-
if (status.commitsAheadOfDefault && !status.prExists) return true;
|
|
18198
|
-
if (status.status === "Behind" || status.status === "Diverged") return true;
|
|
18199
|
-
return false;
|
|
18200
|
-
}
|
|
18615
|
+
init_dist();
|
|
18201
18616
|
async function handleListProjects(c, projectManager, threadManager, db) {
|
|
18202
18617
|
try {
|
|
18203
18618
|
const projects = await projectManager.listProjects();
|
|
@@ -18234,6 +18649,7 @@ async function handleListProjects(c, projectManager, threadManager, db) {
|
|
|
18234
18649
|
useCustomSystemPrompt: project.useCustomSystemPrompt ?? false,
|
|
18235
18650
|
allowAllCommands: project.allowAllCommands !== false,
|
|
18236
18651
|
allowedCommandPatterns: project.allowedCommandPatterns ?? [],
|
|
18652
|
+
modelAliases: project.modelAliases,
|
|
18237
18653
|
threads: threads.map((thread) => {
|
|
18238
18654
|
const cached = statusCache.get(thread.id);
|
|
18239
18655
|
const gitStatusDot = cached ? computeGitStatusDot(cached) : false;
|
|
@@ -18499,7 +18915,7 @@ async function handleOpenProject(c, projectManager) {
|
|
|
18499
18915
|
|
|
18500
18916
|
// src/features/projects/projects-update.route.ts
|
|
18501
18917
|
init_dist();
|
|
18502
|
-
async function handleUpdateProject(c, projectManager) {
|
|
18918
|
+
async function handleUpdateProject(c, projectManager, metadataManager) {
|
|
18503
18919
|
try {
|
|
18504
18920
|
const projectId = c.req.param("id");
|
|
18505
18921
|
const body = await c.req.json();
|
|
@@ -18517,7 +18933,8 @@ async function handleUpdateProject(c, projectManager) {
|
|
|
18517
18933
|
customSystemPrompt,
|
|
18518
18934
|
useCustomSystemPrompt,
|
|
18519
18935
|
allowAllCommands,
|
|
18520
|
-
allowedCommandPatterns
|
|
18936
|
+
allowedCommandPatterns,
|
|
18937
|
+
modelAliases
|
|
18521
18938
|
} = body;
|
|
18522
18939
|
if (!projectId) {
|
|
18523
18940
|
return errorResponse(c, ErrorCodes.INVALID_REQUEST, "Project ID is required", 400);
|
|
@@ -18581,6 +18998,17 @@ async function handleUpdateProject(c, projectManager) {
|
|
|
18581
18998
|
allowedCommandPatterns
|
|
18582
18999
|
});
|
|
18583
19000
|
}
|
|
19001
|
+
if (modelAliases !== void 0) {
|
|
19002
|
+
let normalizedAliases;
|
|
19003
|
+
try {
|
|
19004
|
+
normalizedAliases = normalizeProjectModelAliases(modelAliases);
|
|
19005
|
+
await validateProjectModelAliasesForSave(metadataManager, normalizedAliases);
|
|
19006
|
+
} catch (error) {
|
|
19007
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
19008
|
+
return errorResponse(c, ErrorCodes.INVALID_REQUEST, message, 400);
|
|
19009
|
+
}
|
|
19010
|
+
await projectManager.updateModelAliases(projectId, normalizedAliases);
|
|
19011
|
+
}
|
|
18584
19012
|
if (name !== void 0) {
|
|
18585
19013
|
if (!name?.trim()) {
|
|
18586
19014
|
return errorResponse(c, ErrorCodes.INVALID_REQUEST, "Project name is required", 400);
|
|
@@ -19436,7 +19864,7 @@ async function handleSyncProjectEnvVars(c, projectManager) {
|
|
|
19436
19864
|
}
|
|
19437
19865
|
|
|
19438
19866
|
// src/features/projects/projects.routes.ts
|
|
19439
|
-
function createProjectRoutes(projectManager, threadManager) {
|
|
19867
|
+
function createProjectRoutes(projectManager, threadManager, metadataManager) {
|
|
19440
19868
|
const router = new Hono8();
|
|
19441
19869
|
router.post("/", async (c) => {
|
|
19442
19870
|
return handleCreateProject(c, projectManager);
|
|
@@ -19462,7 +19890,7 @@ function createProjectRoutes(projectManager, threadManager) {
|
|
|
19462
19890
|
return handleOpenProject(c, projectManager);
|
|
19463
19891
|
});
|
|
19464
19892
|
router.put("/:id", async (c) => {
|
|
19465
|
-
return handleUpdateProject(c, projectManager);
|
|
19893
|
+
return handleUpdateProject(c, projectManager, metadataManager);
|
|
19466
19894
|
});
|
|
19467
19895
|
router.post("/:id/commands", async (c) => {
|
|
19468
19896
|
return handleSaveCommand(c, projectManager);
|
|
@@ -21993,6 +22421,15 @@ ___CWD___%s
|
|
|
21993
22421
|
}
|
|
21994
22422
|
await this.metadataManager.saveProjects(projects);
|
|
21995
22423
|
}
|
|
22424
|
+
async updateModelAliases(projectId, modelAliases) {
|
|
22425
|
+
const projects = await this.metadataManager.loadProjects();
|
|
22426
|
+
const project = projects.find((p) => p.id === projectId);
|
|
22427
|
+
if (!project) {
|
|
22428
|
+
throw new Error(`Project not found: ${projectId}`);
|
|
22429
|
+
}
|
|
22430
|
+
project.modelAliases = modelAliases;
|
|
22431
|
+
await this.metadataManager.saveProjects(projects);
|
|
22432
|
+
}
|
|
21996
22433
|
/**
|
|
21997
22434
|
* Starts running a dev server process for a project
|
|
21998
22435
|
* @param projectId - The project ID
|
|
@@ -23934,12 +24371,7 @@ async function handleCreateThread(c, threadManager, gitManager2) {
|
|
|
23934
24371
|
}
|
|
23935
24372
|
|
|
23936
24373
|
// src/features/threads/threads-list.route.ts
|
|
23937
|
-
|
|
23938
|
-
if (status.hasChanges) return true;
|
|
23939
|
-
if (status.commitsAheadOfDefault && !status.prExists) return true;
|
|
23940
|
-
if (status.status === "Behind" || status.status === "Diverged") return true;
|
|
23941
|
-
return false;
|
|
23942
|
-
}
|
|
24374
|
+
init_dist();
|
|
23943
24375
|
async function handleListThreads(c, threadManager, db) {
|
|
23944
24376
|
try {
|
|
23945
24377
|
const projectId = c.req.query("projectId");
|
|
@@ -23961,7 +24393,7 @@ async function handleListThreads(c, threadManager, db) {
|
|
|
23961
24393
|
);
|
|
23962
24394
|
const threadsWithStatus = threads.map((thread) => {
|
|
23963
24395
|
const cached = statusCache.get(thread.id);
|
|
23964
|
-
const gitStatusDot = cached ?
|
|
24396
|
+
const gitStatusDot = cached ? computeGitStatusDot(cached) : false;
|
|
23965
24397
|
return { ...thread, gitStatusDot };
|
|
23966
24398
|
});
|
|
23967
24399
|
return c.json(threadsWithStatus);
|
|
@@ -25197,20 +25629,43 @@ async function handleFixComments(c, threadManager) {
|
|
|
25197
25629
|
|
|
25198
25630
|
// src/features/threads/threads-ai-files.route.ts
|
|
25199
25631
|
init_dist();
|
|
25200
|
-
import { readFile as
|
|
25201
|
-
import { join as
|
|
25632
|
+
import { readFile as readFile17, writeFile as writeFile8, mkdir as mkdir9, access as access5, rm as rm8, stat as stat6 } from "fs/promises";
|
|
25633
|
+
import { join as join34 } from "path";
|
|
25202
25634
|
import { existsSync as existsSync19 } from "fs";
|
|
25203
25635
|
|
|
25204
25636
|
// src/features/git/git-download-folder.ts
|
|
25205
25637
|
import { mkdir as mkdir8, writeFile as writeFile7 } from "fs/promises";
|
|
25206
25638
|
import { join as join32, dirname as dirname6 } from "path";
|
|
25207
|
-
|
|
25208
|
-
|
|
25639
|
+
|
|
25640
|
+
// src/features/git/github-folder.ts
|
|
25641
|
+
function parseGithubRepoUrl(repoUrl) {
|
|
25209
25642
|
const match = repoUrl.replace(/\.git$/, "").match(/github\.com\/([^/]+)\/([^/]+)/);
|
|
25210
25643
|
if (!match) {
|
|
25211
25644
|
throw new Error(`Invalid GitHub URL: ${repoUrl}`);
|
|
25212
25645
|
}
|
|
25213
25646
|
const [, owner, repo] = match;
|
|
25647
|
+
return { owner, repo };
|
|
25648
|
+
}
|
|
25649
|
+
function normalizeFolderPrefix(srcPath) {
|
|
25650
|
+
const trimmed = srcPath.replace(/^\/|\/$/g, "");
|
|
25651
|
+
return trimmed.length > 0 ? `${trimmed}/` : "";
|
|
25652
|
+
}
|
|
25653
|
+
function filterFolderBlobPaths(tree, srcPath, excludeRelativePrefixes = []) {
|
|
25654
|
+
const prefix = normalizeFolderPrefix(srcPath);
|
|
25655
|
+
return tree.filter((entry) => {
|
|
25656
|
+
if (entry.type !== "blob") {
|
|
25657
|
+
return false;
|
|
25658
|
+
}
|
|
25659
|
+
if (prefix.length > 0) {
|
|
25660
|
+
if (!entry.path.startsWith(prefix)) {
|
|
25661
|
+
return false;
|
|
25662
|
+
}
|
|
25663
|
+
}
|
|
25664
|
+
const relativePath = prefix.length > 0 ? entry.path.slice(prefix.length) : entry.path;
|
|
25665
|
+
return !excludeRelativePrefixes.some((skipPrefix) => relativePath.startsWith(skipPrefix));
|
|
25666
|
+
}).map((entry) => prefix.length > 0 ? entry.path.slice(prefix.length) : entry.path).sort((a, b) => a.localeCompare(b));
|
|
25667
|
+
}
|
|
25668
|
+
function buildGithubHeaders(token) {
|
|
25214
25669
|
const headers = {
|
|
25215
25670
|
Accept: "application/vnd.github+json",
|
|
25216
25671
|
"X-GitHub-Api-Version": "2022-11-28"
|
|
@@ -25218,42 +25673,63 @@ async function downloadGithubFolder(repoUrl, srcPath, destPath, options = {}) {
|
|
|
25218
25673
|
if (token) {
|
|
25219
25674
|
headers["Authorization"] = `Bearer ${token}`;
|
|
25220
25675
|
}
|
|
25221
|
-
|
|
25676
|
+
return headers;
|
|
25677
|
+
}
|
|
25678
|
+
async function fetchGithubTree(owner, repo, refParam, token) {
|
|
25222
25679
|
const treeUrl = `https://api.github.com/repos/${owner}/${repo}/git/trees/${refParam}?recursive=1`;
|
|
25223
|
-
const treeRes = await fetch(treeUrl, { headers });
|
|
25680
|
+
const treeRes = await fetch(treeUrl, { headers: buildGithubHeaders(token) });
|
|
25224
25681
|
if (!treeRes.ok) {
|
|
25225
25682
|
throw new Error(`GitHub API error ${treeRes.status}: ${await treeRes.text()}`);
|
|
25226
25683
|
}
|
|
25227
|
-
|
|
25684
|
+
return await treeRes.json();
|
|
25685
|
+
}
|
|
25686
|
+
async function listGithubFolderFiles(repoUrl, srcPath, options = {}) {
|
|
25687
|
+
const { token, ref, excludeRelativePrefixes = [] } = options;
|
|
25688
|
+
const { owner, repo } = parseGithubRepoUrl(repoUrl);
|
|
25689
|
+
const refParam = ref ?? "HEAD";
|
|
25690
|
+
const tree = await fetchGithubTree(owner, repo, refParam, token);
|
|
25228
25691
|
if (tree.truncated) {
|
|
25229
25692
|
console.warn(
|
|
25230
25693
|
"Warning: the repository tree was truncated by GitHub. Large repos may be incomplete."
|
|
25231
25694
|
);
|
|
25232
25695
|
}
|
|
25233
|
-
const
|
|
25234
|
-
const files = tree.tree.filter((entry) => {
|
|
25235
|
-
if (entry.type !== "blob" || !entry.path.startsWith(prefix)) {
|
|
25236
|
-
return false;
|
|
25237
|
-
}
|
|
25238
|
-
const relativePath = entry.path.slice(prefix.length);
|
|
25239
|
-
return !excludeRelativePrefixes.some((skipPrefix) => relativePath.startsWith(skipPrefix));
|
|
25240
|
-
});
|
|
25696
|
+
const files = filterFolderBlobPaths(tree.tree, srcPath, excludeRelativePrefixes);
|
|
25241
25697
|
if (files.length === 0) {
|
|
25242
25698
|
throw new Error(`No files found at path "${srcPath}" in ${owner}/${repo}@${refParam}`);
|
|
25243
25699
|
}
|
|
25700
|
+
return { files, ref: refParam };
|
|
25701
|
+
}
|
|
25702
|
+
async function fetchGithubFolderFile(repoUrl, srcPath, relativePath, options = {}) {
|
|
25703
|
+
const { token, ref } = options;
|
|
25704
|
+
const { owner, repo } = parseGithubRepoUrl(repoUrl);
|
|
25705
|
+
const refParam = ref ?? "HEAD";
|
|
25706
|
+
const folderPrefix = normalizeFolderPrefix(srcPath);
|
|
25707
|
+
const fullPath = folderPrefix.length > 0 ? `${folderPrefix}${relativePath}` : relativePath;
|
|
25708
|
+
const rawUrl = `https://raw.githubusercontent.com/${owner}/${repo}/${refParam}/${fullPath}`;
|
|
25709
|
+
const fileRes = await fetch(rawUrl, { headers: buildGithubHeaders(token) });
|
|
25710
|
+
if (!fileRes.ok) {
|
|
25711
|
+
throw new Error(`Failed to download ${fullPath}: HTTP ${fileRes.status}`);
|
|
25712
|
+
}
|
|
25713
|
+
let content = Buffer.from(await fileRes.arrayBuffer()).toString("utf8");
|
|
25714
|
+
content = content.replace(/Claude/gi, "Tarsk");
|
|
25715
|
+
return content;
|
|
25716
|
+
}
|
|
25717
|
+
|
|
25718
|
+
// src/features/git/git-download-folder.ts
|
|
25719
|
+
async function downloadGithubFolder(repoUrl, srcPath, destPath, options = {}) {
|
|
25720
|
+
const { ref, excludeRelativePrefixes = [] } = options;
|
|
25721
|
+
const { files, ref: refParam } = await listGithubFolderFiles(repoUrl, srcPath, options);
|
|
25722
|
+
const { owner, repo } = parseGithubRepoUrl(repoUrl);
|
|
25244
25723
|
console.log(`Downloading ${files.length} file(s) from ${owner}/${repo}/${srcPath} \u2192 ${destPath}`);
|
|
25245
25724
|
await Promise.all(
|
|
25246
|
-
files.map(async (
|
|
25247
|
-
const relativePath = file.path.slice(prefix.length);
|
|
25725
|
+
files.map(async (relativePath) => {
|
|
25248
25726
|
const localPath = join32(destPath, relativePath);
|
|
25249
25727
|
await mkdir8(dirname6(localPath), { recursive: true });
|
|
25250
|
-
const
|
|
25251
|
-
|
|
25252
|
-
|
|
25253
|
-
|
|
25254
|
-
}
|
|
25255
|
-
let content = Buffer.from(await fileRes.arrayBuffer()).toString("utf8");
|
|
25256
|
-
content = content.replace(/Claude/gi, "Tarsk");
|
|
25728
|
+
const content = await fetchGithubFolderFile(repoUrl, srcPath, relativePath, {
|
|
25729
|
+
...options,
|
|
25730
|
+
ref: ref ?? refParam,
|
|
25731
|
+
excludeRelativePrefixes
|
|
25732
|
+
});
|
|
25257
25733
|
await writeFile7(localPath, content, "utf8");
|
|
25258
25734
|
console.log(` \u2713 ${relativePath}`);
|
|
25259
25735
|
})
|
|
@@ -25261,6 +25737,433 @@ async function downloadGithubFolder(repoUrl, srcPath, destPath, options = {}) {
|
|
|
25261
25737
|
console.log("Done.");
|
|
25262
25738
|
}
|
|
25263
25739
|
|
|
25740
|
+
// src/features/skills/skills.catalog-cache.ts
|
|
25741
|
+
import { cp, mkdtemp, readdir as readdir12, readFile as readFile16, rm as rm7 } from "fs/promises";
|
|
25742
|
+
import { join as join33, relative as relative8, sep } from "path";
|
|
25743
|
+
import { tmpdir as tmpdir3 } from "os";
|
|
25744
|
+
|
|
25745
|
+
// src/features/git/git-clone-folder.ts
|
|
25746
|
+
init_utils();
|
|
25747
|
+
function normalizeGitCloneUrl(repoUrl) {
|
|
25748
|
+
const trimmed = repoUrl.trim().replace(/\/$/, "");
|
|
25749
|
+
return trimmed.endsWith(".git") ? trimmed : `${trimmed}.git`;
|
|
25750
|
+
}
|
|
25751
|
+
function runGit2(cwd, args2) {
|
|
25752
|
+
const result = spawnSyncProcess("git", args2, {
|
|
25753
|
+
cwd,
|
|
25754
|
+
encoding: "utf-8",
|
|
25755
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
25756
|
+
});
|
|
25757
|
+
return {
|
|
25758
|
+
ok: result.status === 0,
|
|
25759
|
+
stderr: (typeof result.stderr === "string" ? result.stderr : result.stderr?.toString() || "").trim()
|
|
25760
|
+
};
|
|
25761
|
+
}
|
|
25762
|
+
async function cloneGithubRepoFolder(destPath, repoUrl, folderPath, options = {}) {
|
|
25763
|
+
const ref = options.ref?.trim();
|
|
25764
|
+
const cloneUrl = normalizeGitCloneUrl(repoUrl);
|
|
25765
|
+
const trimmedFolderPath = folderPath.replace(/^\/|\/$/g, "");
|
|
25766
|
+
const useSparseCheckout = trimmedFolderPath.length > 0;
|
|
25767
|
+
const cloneArgs = [
|
|
25768
|
+
"clone",
|
|
25769
|
+
"--depth",
|
|
25770
|
+
"1",
|
|
25771
|
+
...ref ? ["--branch", ref, "--single-branch"] : [],
|
|
25772
|
+
...useSparseCheckout ? ["--filter=blob:none", "--sparse"] : [],
|
|
25773
|
+
cloneUrl,
|
|
25774
|
+
destPath
|
|
25775
|
+
];
|
|
25776
|
+
const cloneResult = runGit2(process.cwd(), cloneArgs);
|
|
25777
|
+
if (!cloneResult.ok) {
|
|
25778
|
+
const refLabel = ref ?? "default branch";
|
|
25779
|
+
throw new Error(`Failed to clone ${repoUrl}@${refLabel}: ${cloneResult.stderr}`);
|
|
25780
|
+
}
|
|
25781
|
+
if (!useSparseCheckout) {
|
|
25782
|
+
return;
|
|
25783
|
+
}
|
|
25784
|
+
const sparseResult = runGit2(destPath, ["sparse-checkout", "set", trimmedFolderPath]);
|
|
25785
|
+
if (!sparseResult.ok) {
|
|
25786
|
+
throw new Error(
|
|
25787
|
+
`Failed to sparse-checkout "${trimmedFolderPath}" from ${repoUrl}: ${sparseResult.stderr}`
|
|
25788
|
+
);
|
|
25789
|
+
}
|
|
25790
|
+
}
|
|
25791
|
+
|
|
25792
|
+
// src/features/git/github-tree-url.ts
|
|
25793
|
+
function parseGithubTreeUrl(url) {
|
|
25794
|
+
const trimmed = url.trim().replace(/\/$/, "");
|
|
25795
|
+
const treeMatch = trimmed.match(
|
|
25796
|
+
/^https?:\/\/github\.com\/([^/]+)\/([^/]+)\/tree\/([^/]+)(?:\/(.*))?$/
|
|
25797
|
+
);
|
|
25798
|
+
if (treeMatch) {
|
|
25799
|
+
const [, owner, repoWithSuffix, ref, folderPath = ""] = treeMatch;
|
|
25800
|
+
const repo = repoWithSuffix.replace(/\.git$/, "");
|
|
25801
|
+
return {
|
|
25802
|
+
owner,
|
|
25803
|
+
repo,
|
|
25804
|
+
repoUrl: `https://github.com/${owner}/${repo}`,
|
|
25805
|
+
ref,
|
|
25806
|
+
folderPath: folderPath.replace(/^\/|\/$/g, "")
|
|
25807
|
+
};
|
|
25808
|
+
}
|
|
25809
|
+
const repoMatch = trimmed.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/);
|
|
25810
|
+
if (repoMatch) {
|
|
25811
|
+
const [, owner, repoWithSuffix] = repoMatch;
|
|
25812
|
+
const repo = repoWithSuffix.replace(/\.git$/, "");
|
|
25813
|
+
return {
|
|
25814
|
+
owner,
|
|
25815
|
+
repo,
|
|
25816
|
+
repoUrl: `https://github.com/${owner}/${repo}`,
|
|
25817
|
+
ref: "",
|
|
25818
|
+
folderPath: ""
|
|
25819
|
+
};
|
|
25820
|
+
}
|
|
25821
|
+
throw new Error(`Invalid GitHub tree URL: ${url}`);
|
|
25822
|
+
}
|
|
25823
|
+
|
|
25824
|
+
// src/features/skills/skills.catalog-cache.ts
|
|
25825
|
+
init_dist();
|
|
25826
|
+
|
|
25827
|
+
// src/features/skills/skills.custom-sources.ts
|
|
25828
|
+
init_dist();
|
|
25829
|
+
init_database();
|
|
25830
|
+
init_database_state();
|
|
25831
|
+
var STATE_KEY = "customSkillSources";
|
|
25832
|
+
function isValidSkillSource(value) {
|
|
25833
|
+
return typeof value === "object" && value !== null && typeof value.id === "string" && typeof value.title === "string" && typeof value.url === "string";
|
|
25834
|
+
}
|
|
25835
|
+
function parseCustomSkillSources(raw) {
|
|
25836
|
+
if (!Array.isArray(raw)) {
|
|
25837
|
+
return [];
|
|
25838
|
+
}
|
|
25839
|
+
return raw.filter(isValidSkillSource).filter((source) => isCustomSkillSourceId(source.id)).sort((a, b) => a.title.localeCompare(b.title));
|
|
25840
|
+
}
|
|
25841
|
+
async function getCustomSkillSources() {
|
|
25842
|
+
const db = await getDatabase();
|
|
25843
|
+
const raw = await getState(db, STATE_KEY);
|
|
25844
|
+
return parseCustomSkillSources(raw);
|
|
25845
|
+
}
|
|
25846
|
+
async function getCustomSkillSourceById(sourceId) {
|
|
25847
|
+
const sources = await getCustomSkillSources();
|
|
25848
|
+
return sources.find((source) => source.id === sourceId);
|
|
25849
|
+
}
|
|
25850
|
+
async function saveCustomSkillSource(source) {
|
|
25851
|
+
if (!isCustomSkillSourceId(source.id)) {
|
|
25852
|
+
throw new Error("Custom skill sources must use the custom- id prefix");
|
|
25853
|
+
}
|
|
25854
|
+
const db = await getDatabase();
|
|
25855
|
+
const existing = await getCustomSkillSources();
|
|
25856
|
+
const next = [...existing.filter((entry) => entry.id !== source.id), source].sort(
|
|
25857
|
+
(a, b) => a.title.localeCompare(b.title)
|
|
25858
|
+
);
|
|
25859
|
+
await setState(db, STATE_KEY, next);
|
|
25860
|
+
return source;
|
|
25861
|
+
}
|
|
25862
|
+
async function importCustomSkillSources(sources) {
|
|
25863
|
+
const valid = sources.filter(isValidSkillSource).filter((source) => isCustomSkillSourceId(source.id));
|
|
25864
|
+
if (valid.length === 0) {
|
|
25865
|
+
return getCustomSkillSources();
|
|
25866
|
+
}
|
|
25867
|
+
const db = await getDatabase();
|
|
25868
|
+
const existing = await getCustomSkillSources();
|
|
25869
|
+
const byId = new Map(existing.map((source) => [source.id, source]));
|
|
25870
|
+
for (const source of valid) {
|
|
25871
|
+
byId.set(source.id, source);
|
|
25872
|
+
}
|
|
25873
|
+
const next = [...byId.values()].sort((a, b) => a.title.localeCompare(b.title));
|
|
25874
|
+
await setState(db, STATE_KEY, next);
|
|
25875
|
+
return next;
|
|
25876
|
+
}
|
|
25877
|
+
async function deleteCustomSkillSource(sourceId) {
|
|
25878
|
+
if (!isCustomSkillSourceId(sourceId)) {
|
|
25879
|
+
throw new Error("Only custom skill sources can be deleted");
|
|
25880
|
+
}
|
|
25881
|
+
const existing = await getCustomSkillSourceById(sourceId);
|
|
25882
|
+
if (!existing) {
|
|
25883
|
+
throw new Error("Unknown custom skill source");
|
|
25884
|
+
}
|
|
25885
|
+
const db = await getDatabase();
|
|
25886
|
+
const sources = await getCustomSkillSources();
|
|
25887
|
+
const next = sources.filter((entry) => entry.id !== sourceId);
|
|
25888
|
+
await setState(db, STATE_KEY, next);
|
|
25889
|
+
await clearCatalogClone(sourceId);
|
|
25890
|
+
}
|
|
25891
|
+
|
|
25892
|
+
// src/features/skills/skills.sources.json
|
|
25893
|
+
var skills_sources_default = [
|
|
25894
|
+
{
|
|
25895
|
+
id: "openai-curated",
|
|
25896
|
+
title: "OpenAI Curated",
|
|
25897
|
+
url: "https://github.com/openai/skills/tree/main/skills/.curated"
|
|
25898
|
+
},
|
|
25899
|
+
{
|
|
25900
|
+
id: "anthropic",
|
|
25901
|
+
title: "Anthropic",
|
|
25902
|
+
url: "https://github.com/anthropics/skills/tree/main/skills"
|
|
25903
|
+
},
|
|
25904
|
+
{
|
|
25905
|
+
id: "matt-pocock-engineering",
|
|
25906
|
+
title: "Engineering - Matt Pocock",
|
|
25907
|
+
url: "https://github.com/mattpocock/skills/tree/main/skills/engineering"
|
|
25908
|
+
},
|
|
25909
|
+
{
|
|
25910
|
+
id: "matt-pocock-productivity",
|
|
25911
|
+
title: "Productivity -Matt Pocock",
|
|
25912
|
+
url: "https://github.com/mattpocock/skills/tree/main/skills/productivity"
|
|
25913
|
+
},
|
|
25914
|
+
{
|
|
25915
|
+
id: "cloudflare",
|
|
25916
|
+
title: "Cloudflare",
|
|
25917
|
+
url: "https://github.com/cloudflare/skills/tree/main/skills"
|
|
25918
|
+
},
|
|
25919
|
+
{
|
|
25920
|
+
id: "vercel",
|
|
25921
|
+
title: "Vercel",
|
|
25922
|
+
url: "https://github.com/vercel-labs/agent-skills/tree/main/skills"
|
|
25923
|
+
},
|
|
25924
|
+
{
|
|
25925
|
+
id: "microsoft",
|
|
25926
|
+
title: "Microsoft",
|
|
25927
|
+
url: "https://github.com/MicrosoftDocs/Agent-Skills/tree/main/skills"
|
|
25928
|
+
},
|
|
25929
|
+
{
|
|
25930
|
+
id: "sentry",
|
|
25931
|
+
title: "Sentry",
|
|
25932
|
+
url: "https://github.com/getsentry/skills/tree/main/skills"
|
|
25933
|
+
},
|
|
25934
|
+
{
|
|
25935
|
+
id: "firebase",
|
|
25936
|
+
title: "Firebase",
|
|
25937
|
+
url: "https://github.com/firebase/skills/tree/main/skills"
|
|
25938
|
+
},
|
|
25939
|
+
{
|
|
25940
|
+
id: "flutter",
|
|
25941
|
+
title: "Flutter",
|
|
25942
|
+
url: "https://github.com/flutter/skills/tree/main/skills"
|
|
25943
|
+
},
|
|
25944
|
+
{
|
|
25945
|
+
id: "angular",
|
|
25946
|
+
title: "Angular",
|
|
25947
|
+
url: "https://github.com/angular/skills/tree/main"
|
|
25948
|
+
},
|
|
25949
|
+
{
|
|
25950
|
+
id: "firecrawl",
|
|
25951
|
+
title: "Firecrawl",
|
|
25952
|
+
url: "https://github.com/firecrawl/skills/tree/main/skills"
|
|
25953
|
+
},
|
|
25954
|
+
{
|
|
25955
|
+
id: "redis",
|
|
25956
|
+
title: "Redis",
|
|
25957
|
+
url: "https://github.com/redis/agent-skills/tree/main/skills"
|
|
25958
|
+
},
|
|
25959
|
+
{
|
|
25960
|
+
id: "corey-haines-marketing",
|
|
25961
|
+
title: "Marketing - Corey Haines",
|
|
25962
|
+
url: "https://github.com/coreyhaines31/marketingskills/tree/main/skills"
|
|
25963
|
+
},
|
|
25964
|
+
{
|
|
25965
|
+
id: "dean-peters-product-management",
|
|
25966
|
+
title: "Product Management - Dean Peters",
|
|
25967
|
+
url: "https://github.com/deanpeters/Product-Manager-Skills/tree/main/skills"
|
|
25968
|
+
},
|
|
25969
|
+
{
|
|
25970
|
+
id: "venice-ai",
|
|
25971
|
+
title: "AI - Venice",
|
|
25972
|
+
url: "https://github.com/veniceai/skills/tree/main/skills"
|
|
25973
|
+
},
|
|
25974
|
+
{
|
|
25975
|
+
id: "superpowers",
|
|
25976
|
+
title: "Development - Superpowers",
|
|
25977
|
+
url: "https://github.com/obra/superpowers/tree/main/skills"
|
|
25978
|
+
},
|
|
25979
|
+
{
|
|
25980
|
+
id: "cypress",
|
|
25981
|
+
title: "Testing - Cypress",
|
|
25982
|
+
url: "https://github.com/cypress-io/ai-toolkit/tree/main/skills"
|
|
25983
|
+
},
|
|
25984
|
+
{
|
|
25985
|
+
id: "resend",
|
|
25986
|
+
title: "Email - Resend",
|
|
25987
|
+
url: "https://github.com/resend/resend-skills/tree/main/skills"
|
|
25988
|
+
},
|
|
25989
|
+
{
|
|
25990
|
+
id: "n8n",
|
|
25991
|
+
title: "Automation - n8n",
|
|
25992
|
+
url: "https://github.com/czlonkowski/n8n-skills/tree/main/skills"
|
|
25993
|
+
}
|
|
25994
|
+
];
|
|
25995
|
+
|
|
25996
|
+
// src/features/skills/skills.catalog-cache.ts
|
|
25997
|
+
var catalogCloneCache = /* @__PURE__ */ new Map();
|
|
25998
|
+
var inFlightRefreshes = /* @__PURE__ */ new Map();
|
|
25999
|
+
function getBundledSkillSourceById(sourceId) {
|
|
26000
|
+
return skills_sources_default.find((source) => source.id === sourceId);
|
|
26001
|
+
}
|
|
26002
|
+
async function resolveSkillSource(sourceId) {
|
|
26003
|
+
const bundled = getBundledSkillSourceById(sourceId);
|
|
26004
|
+
if (bundled) {
|
|
26005
|
+
return bundled;
|
|
26006
|
+
}
|
|
26007
|
+
const custom = await getCustomSkillSourceById(sourceId);
|
|
26008
|
+
if (custom) {
|
|
26009
|
+
return custom;
|
|
26010
|
+
}
|
|
26011
|
+
throw new Error(`Unknown skill source: ${sourceId}`);
|
|
26012
|
+
}
|
|
26013
|
+
function normalizeSkillContent(content) {
|
|
26014
|
+
return content.replace(/Claude/gi, "Tarsk");
|
|
26015
|
+
}
|
|
26016
|
+
async function listFilesRecursive(dir, rootDir = dir) {
|
|
26017
|
+
const entries = await readdir12(dir, { withFileTypes: true });
|
|
26018
|
+
const files = [];
|
|
26019
|
+
for (const entry of entries) {
|
|
26020
|
+
const fullPath = join33(dir, entry.name);
|
|
26021
|
+
if (entry.isDirectory()) {
|
|
26022
|
+
files.push(...await listFilesRecursive(fullPath, rootDir));
|
|
26023
|
+
continue;
|
|
26024
|
+
}
|
|
26025
|
+
if (entry.isFile()) {
|
|
26026
|
+
files.push(relative8(rootDir, fullPath));
|
|
26027
|
+
}
|
|
26028
|
+
}
|
|
26029
|
+
return files.sort((a, b) => a.localeCompare(b));
|
|
26030
|
+
}
|
|
26031
|
+
function resolveSkillRelativePath(skillDir, relativePath) {
|
|
26032
|
+
const resolved = join33(skillDir, relativePath);
|
|
26033
|
+
const normalizedSkillDir = skillDir.endsWith(sep) ? skillDir : `${skillDir}${sep}`;
|
|
26034
|
+
if (resolved === skillDir || resolved.startsWith(normalizedSkillDir)) {
|
|
26035
|
+
return resolved;
|
|
26036
|
+
}
|
|
26037
|
+
return null;
|
|
26038
|
+
}
|
|
26039
|
+
async function ensureCatalogClone(sourceId) {
|
|
26040
|
+
const cached = catalogCloneCache.get(sourceId);
|
|
26041
|
+
if (cached) {
|
|
26042
|
+
return cached;
|
|
26043
|
+
}
|
|
26044
|
+
return refreshCatalogClone(sourceId);
|
|
26045
|
+
}
|
|
26046
|
+
async function getSkillDirEnsured(sourceId, skillId) {
|
|
26047
|
+
const entry = await ensureCatalogClone(sourceId);
|
|
26048
|
+
return join33(entry.scanDir, skillId);
|
|
26049
|
+
}
|
|
26050
|
+
async function cloneSourceToCache(sourceId, trustedUrl) {
|
|
26051
|
+
let url;
|
|
26052
|
+
if (trustedUrl !== void 0) {
|
|
26053
|
+
if (!isCustomSkillSourceId(sourceId)) {
|
|
26054
|
+
throw new Error(`Cannot override URL for skill source: ${sourceId}`);
|
|
26055
|
+
}
|
|
26056
|
+
url = trustedUrl;
|
|
26057
|
+
} else {
|
|
26058
|
+
url = (await resolveSkillSource(sourceId)).url;
|
|
26059
|
+
}
|
|
26060
|
+
const parsed = parseGithubTreeUrl(url);
|
|
26061
|
+
const cloneDir = await mkdtemp(join33(tmpdir3(), "tarsk-skills-catalog-clone-"));
|
|
26062
|
+
await cloneGithubRepoFolder(cloneDir, parsed.repoUrl, parsed.folderPath, {
|
|
26063
|
+
ref: parsed.ref
|
|
26064
|
+
});
|
|
26065
|
+
const scanDir = parsed.folderPath ? join33(cloneDir, parsed.folderPath) : cloneDir;
|
|
26066
|
+
const entry = {
|
|
26067
|
+
sourceId,
|
|
26068
|
+
cloneDir,
|
|
26069
|
+
scanDir,
|
|
26070
|
+
parsed,
|
|
26071
|
+
fetchedAt: Date.now()
|
|
26072
|
+
};
|
|
26073
|
+
const previous = catalogCloneCache.get(sourceId);
|
|
26074
|
+
catalogCloneCache.set(sourceId, entry);
|
|
26075
|
+
if (previous) {
|
|
26076
|
+
await rm7(previous.cloneDir, { recursive: true, force: true }).catch(() => {
|
|
26077
|
+
});
|
|
26078
|
+
}
|
|
26079
|
+
return entry;
|
|
26080
|
+
}
|
|
26081
|
+
async function refreshCatalogClone(sourceId, trustedUrl) {
|
|
26082
|
+
const existing = inFlightRefreshes.get(sourceId);
|
|
26083
|
+
if (existing) {
|
|
26084
|
+
return existing;
|
|
26085
|
+
}
|
|
26086
|
+
const refreshPromise = cloneSourceToCache(sourceId, trustedUrl).finally(() => {
|
|
26087
|
+
inFlightRefreshes.delete(sourceId);
|
|
26088
|
+
});
|
|
26089
|
+
inFlightRefreshes.set(sourceId, refreshPromise);
|
|
26090
|
+
return refreshPromise;
|
|
26091
|
+
}
|
|
26092
|
+
function normalizeGithubRepoUrl(repoUrl) {
|
|
26093
|
+
return repoUrl.trim().replace(/\/$/, "").replace(/\.git$/, "");
|
|
26094
|
+
}
|
|
26095
|
+
function resolveCatalogSkillFromGithubFolder(gitHubRepo, gitHubFolder) {
|
|
26096
|
+
const normalizedRepo = normalizeGithubRepoUrl(gitHubRepo);
|
|
26097
|
+
const normalizedFolder = gitHubFolder.replace(/^\/|\/$/g, "");
|
|
26098
|
+
for (const source of skills_sources_default) {
|
|
26099
|
+
const parsed = parseGithubTreeUrl(source.url);
|
|
26100
|
+
if (normalizeGithubRepoUrl(parsed.repoUrl) !== normalizedRepo) {
|
|
26101
|
+
continue;
|
|
26102
|
+
}
|
|
26103
|
+
const basePath = parsed.folderPath.replace(/^\/|\/$/g, "");
|
|
26104
|
+
if (basePath.length > 0) {
|
|
26105
|
+
const prefix = `${basePath}/`;
|
|
26106
|
+
if (!normalizedFolder.startsWith(prefix)) {
|
|
26107
|
+
continue;
|
|
26108
|
+
}
|
|
26109
|
+
const skillId2 = normalizedFolder.slice(prefix.length);
|
|
26110
|
+
if (!skillId2 || skillId2.includes("/")) {
|
|
26111
|
+
continue;
|
|
26112
|
+
}
|
|
26113
|
+
return { sourceId: source.id, skillId: skillId2, ref: parsed.ref };
|
|
26114
|
+
}
|
|
26115
|
+
const skillId = normalizedFolder.split("/")[0];
|
|
26116
|
+
if (!skillId) {
|
|
26117
|
+
continue;
|
|
26118
|
+
}
|
|
26119
|
+
return { sourceId: source.id, skillId, ref: parsed.ref };
|
|
26120
|
+
}
|
|
26121
|
+
return null;
|
|
26122
|
+
}
|
|
26123
|
+
async function resolveCatalogSkillFromGithubFolderEnsured(gitHubRepo, gitHubFolder) {
|
|
26124
|
+
const resolved = resolveCatalogSkillFromGithubFolder(gitHubRepo, gitHubFolder);
|
|
26125
|
+
if (!resolved) {
|
|
26126
|
+
throw new Error(`No catalog source found for ${gitHubRepo}/${gitHubFolder}`);
|
|
26127
|
+
}
|
|
26128
|
+
await ensureCatalogClone(resolved.sourceId);
|
|
26129
|
+
return resolved;
|
|
26130
|
+
}
|
|
26131
|
+
async function listCachedCatalogSkillFilesByGithubFolder(gitHubRepo, gitHubFolder) {
|
|
26132
|
+
const resolved = await resolveCatalogSkillFromGithubFolderEnsured(gitHubRepo, gitHubFolder);
|
|
26133
|
+
const files = await listCachedCatalogSkillFiles(resolved.sourceId, resolved.skillId);
|
|
26134
|
+
return { files, ref: resolved.ref };
|
|
26135
|
+
}
|
|
26136
|
+
async function readCachedCatalogSkillFileByGithubFolder(gitHubRepo, gitHubFolder, relativePath) {
|
|
26137
|
+
const resolved = await resolveCatalogSkillFromGithubFolderEnsured(gitHubRepo, gitHubFolder);
|
|
26138
|
+
return readCachedCatalogSkillFile(resolved.sourceId, resolved.skillId, relativePath);
|
|
26139
|
+
}
|
|
26140
|
+
async function listCachedCatalogSkillFiles(sourceId, skillId) {
|
|
26141
|
+
const skillDir = await getSkillDirEnsured(sourceId, skillId);
|
|
26142
|
+
return listFilesRecursive(skillDir);
|
|
26143
|
+
}
|
|
26144
|
+
async function readCachedCatalogSkillFile(sourceId, skillId, relativePath) {
|
|
26145
|
+
const skillDir = await getSkillDirEnsured(sourceId, skillId);
|
|
26146
|
+
const absPath = resolveSkillRelativePath(skillDir, relativePath);
|
|
26147
|
+
if (!absPath) {
|
|
26148
|
+
throw new Error(`Invalid file path: ${relativePath}`);
|
|
26149
|
+
}
|
|
26150
|
+
const content = await readFile16(absPath, "utf8");
|
|
26151
|
+
return normalizeSkillContent(content);
|
|
26152
|
+
}
|
|
26153
|
+
async function copyCachedCatalogSkillToDirectory(sourceId, skillId, destPath) {
|
|
26154
|
+
const skillDir = await getSkillDirEnsured(sourceId, skillId);
|
|
26155
|
+
await cp(skillDir, destPath, { recursive: true });
|
|
26156
|
+
}
|
|
26157
|
+
async function clearCatalogClone(sourceId) {
|
|
26158
|
+
const entry = catalogCloneCache.get(sourceId);
|
|
26159
|
+
if (!entry) {
|
|
26160
|
+
return;
|
|
26161
|
+
}
|
|
26162
|
+
catalogCloneCache.delete(sourceId);
|
|
26163
|
+
await rm7(entry.cloneDir, { recursive: true, force: true }).catch(() => {
|
|
26164
|
+
});
|
|
26165
|
+
}
|
|
26166
|
+
|
|
25264
26167
|
// src/features/threads/threads-ai-files.route.ts
|
|
25265
26168
|
async function handleGetThreadAIFiles(c, threadManager) {
|
|
25266
26169
|
try {
|
|
@@ -25363,7 +26266,7 @@ Links to important documentation, tools, or references.
|
|
|
25363
26266
|
400
|
|
25364
26267
|
);
|
|
25365
26268
|
}
|
|
25366
|
-
const content = await
|
|
26269
|
+
const content = await readFile17(absPath, "utf-8");
|
|
25367
26270
|
return c.json({ content, path: filePath });
|
|
25368
26271
|
} catch (error) {
|
|
25369
26272
|
return errorResponse(
|
|
@@ -25402,7 +26305,7 @@ async function handleSaveThreadAIFile(c, threadManager) {
|
|
|
25402
26305
|
if (!absPath) {
|
|
25403
26306
|
return c.json({ error: { code: "BAD_REQUEST", message: "Invalid file path" } }, 400);
|
|
25404
26307
|
}
|
|
25405
|
-
const parentDir =
|
|
26308
|
+
const parentDir = join34(absPath, "..");
|
|
25406
26309
|
await mkdir9(parentDir, { recursive: true });
|
|
25407
26310
|
await writeFile8(absPath, content, "utf-8");
|
|
25408
26311
|
return c.json({ success: true, path: filePath });
|
|
@@ -25445,7 +26348,7 @@ async function handleDeleteThreadAIFile(c, threadManager) {
|
|
|
25445
26348
|
404
|
|
25446
26349
|
);
|
|
25447
26350
|
}
|
|
25448
|
-
await
|
|
26351
|
+
await rm8(absPath, { recursive: true, force: true });
|
|
25449
26352
|
return c.json({ success: true, path: filePath });
|
|
25450
26353
|
} catch (error) {
|
|
25451
26354
|
return errorResponse(
|
|
@@ -25491,10 +26394,10 @@ async function handleCreateThreadAgent(c, threadManager) {
|
|
|
25491
26394
|
if (!thread) {
|
|
25492
26395
|
return errorResponse(c, ErrorCodes.THREAD_NOT_FOUND, `Thread not found: ${threadId}`, 404);
|
|
25493
26396
|
}
|
|
25494
|
-
const agentRelPath =
|
|
25495
|
-
const agentAbsPath =
|
|
25496
|
-
const agentFileRelPath =
|
|
25497
|
-
const agentFileAbsPath =
|
|
26397
|
+
const agentRelPath = join34(".agents", "agents", name);
|
|
26398
|
+
const agentAbsPath = join34(thread.path, agentRelPath);
|
|
26399
|
+
const agentFileRelPath = join34(agentRelPath, "AGENT.md");
|
|
26400
|
+
const agentFileAbsPath = join34(agentAbsPath, "AGENT.md");
|
|
25498
26401
|
if (existsSync19(agentAbsPath)) {
|
|
25499
26402
|
return c.json(
|
|
25500
26403
|
{ error: { code: "CONFLICT", message: `Agent '${name}' already exists` } },
|
|
@@ -25561,10 +26464,10 @@ async function handleCreateThreadSkill(c, threadManager) {
|
|
|
25561
26464
|
if (!thread) {
|
|
25562
26465
|
return errorResponse(c, ErrorCodes.THREAD_NOT_FOUND, `Thread not found: ${threadId}`, 404);
|
|
25563
26466
|
}
|
|
25564
|
-
const skillRelPath =
|
|
25565
|
-
const skillAbsPath =
|
|
25566
|
-
const skillFileRelPath =
|
|
25567
|
-
const skillFileAbsPath =
|
|
26467
|
+
const skillRelPath = join34(".agents", "skills", name);
|
|
26468
|
+
const skillAbsPath = join34(thread.path, skillRelPath);
|
|
26469
|
+
const skillFileRelPath = join34(skillRelPath, "SKILL.md");
|
|
26470
|
+
const skillFileAbsPath = join34(skillAbsPath, "SKILL.md");
|
|
25568
26471
|
if (existsSync19(skillAbsPath)) {
|
|
25569
26472
|
return c.json(
|
|
25570
26473
|
{ error: { code: "CONFLICT", message: `Skill '${name}' already exists` } },
|
|
@@ -25607,7 +26510,7 @@ async function handleInstallGithubFolder(c, threadManager) {
|
|
|
25607
26510
|
} catch {
|
|
25608
26511
|
return c.json({ error: { code: "BAD_REQUEST", message: "Invalid JSON body" } }, 400);
|
|
25609
26512
|
}
|
|
25610
|
-
const { gitHubRepo, gitHubFolder, destPath } = body;
|
|
26513
|
+
const { gitHubRepo, gitHubFolder, destPath, gitHubRef } = body;
|
|
25611
26514
|
if (!gitHubRepo || !gitHubFolder || !destPath) {
|
|
25612
26515
|
return c.json(
|
|
25613
26516
|
{
|
|
@@ -25627,7 +26530,9 @@ async function handleInstallGithubFolder(c, threadManager) {
|
|
|
25627
26530
|
if (!absDestPath) {
|
|
25628
26531
|
return c.json({ error: { code: "BAD_REQUEST", message: "Invalid destPath" } }, 400);
|
|
25629
26532
|
}
|
|
25630
|
-
await downloadGithubFolder(gitHubRepo, gitHubFolder, absDestPath
|
|
26533
|
+
await downloadGithubFolder(gitHubRepo, gitHubFolder, absDestPath, {
|
|
26534
|
+
ref: gitHubRef
|
|
26535
|
+
});
|
|
25631
26536
|
return c.json({ success: true, path: destPath });
|
|
25632
26537
|
} catch (error) {
|
|
25633
26538
|
return errorResponse(
|
|
@@ -25639,6 +26544,50 @@ async function handleInstallGithubFolder(c, threadManager) {
|
|
|
25639
26544
|
);
|
|
25640
26545
|
}
|
|
25641
26546
|
}
|
|
26547
|
+
async function handleInstallCatalogSkill(c, threadManager) {
|
|
26548
|
+
try {
|
|
26549
|
+
const threadId = c.req.param("id");
|
|
26550
|
+
if (!threadId) {
|
|
26551
|
+
return errorResponse(c, ErrorCodes.INVALID_REQUEST, "Thread ID is required", 400);
|
|
26552
|
+
}
|
|
26553
|
+
let body;
|
|
26554
|
+
try {
|
|
26555
|
+
body = await c.req.json();
|
|
26556
|
+
} catch {
|
|
26557
|
+
return c.json({ error: { code: "BAD_REQUEST", message: "Invalid JSON body" } }, 400);
|
|
26558
|
+
}
|
|
26559
|
+
const { sourceId, skillId, destPath } = body;
|
|
26560
|
+
if (!sourceId || !skillId || !destPath) {
|
|
26561
|
+
return c.json(
|
|
26562
|
+
{
|
|
26563
|
+
error: {
|
|
26564
|
+
code: "BAD_REQUEST",
|
|
26565
|
+
message: "sourceId, skillId, and destPath are required"
|
|
26566
|
+
}
|
|
26567
|
+
},
|
|
26568
|
+
400
|
|
26569
|
+
);
|
|
26570
|
+
}
|
|
26571
|
+
const thread = await threadManager.getThread(threadId);
|
|
26572
|
+
if (!thread) {
|
|
26573
|
+
return errorResponse(c, ErrorCodes.THREAD_NOT_FOUND, `Thread not found: ${threadId}`, 404);
|
|
26574
|
+
}
|
|
26575
|
+
const absDestPath = validateFilePath(thread.path, destPath);
|
|
26576
|
+
if (!absDestPath) {
|
|
26577
|
+
return c.json({ error: { code: "BAD_REQUEST", message: "Invalid destPath" } }, 400);
|
|
26578
|
+
}
|
|
26579
|
+
await copyCachedCatalogSkillToDirectory(sourceId, skillId, absDestPath);
|
|
26580
|
+
return c.json({ success: true, path: destPath });
|
|
26581
|
+
} catch (error) {
|
|
26582
|
+
return errorResponse(
|
|
26583
|
+
c,
|
|
26584
|
+
ErrorCodes.INTERNAL_ERROR,
|
|
26585
|
+
"Failed to install catalog skill",
|
|
26586
|
+
500,
|
|
26587
|
+
error instanceof Error ? error.message : String(error)
|
|
26588
|
+
);
|
|
26589
|
+
}
|
|
26590
|
+
}
|
|
25642
26591
|
|
|
25643
26592
|
// src/features/threads/threads-project-scripts.route.ts
|
|
25644
26593
|
init_database();
|
|
@@ -25953,6 +26902,9 @@ function createThreadRoutes(threadManager, gitManager2, conversationManager) {
|
|
|
25953
26902
|
router.post("/:id/ai-files/github-folder", async (c) => {
|
|
25954
26903
|
return handleInstallGithubFolder(c, threadManager);
|
|
25955
26904
|
});
|
|
26905
|
+
router.post("/:id/ai-files/catalog-skill", async (c) => {
|
|
26906
|
+
return handleInstallCatalogSkill(c, threadManager);
|
|
26907
|
+
});
|
|
25956
26908
|
return router;
|
|
25957
26909
|
}
|
|
25958
26910
|
|
|
@@ -27916,7 +28868,7 @@ async function gitCheckoutBranchHandler(c, metadataManager) {
|
|
|
27916
28868
|
import { unlinkSync } from "fs";
|
|
27917
28869
|
import { resolve as resolve6 } from "path";
|
|
27918
28870
|
init_utils();
|
|
27919
|
-
function
|
|
28871
|
+
function runGit3(args2, cwd) {
|
|
27920
28872
|
return new Promise((resolve8, reject) => {
|
|
27921
28873
|
const proc = spawnProcess("git", args2, { cwd });
|
|
27922
28874
|
let out = "";
|
|
@@ -27952,7 +28904,7 @@ async function gitRevertFileHandler(c, metadataManager) {
|
|
|
27952
28904
|
} catch {
|
|
27953
28905
|
return c.json({ error: `Path is not a git repository: ${absolutePath}` }, 400);
|
|
27954
28906
|
}
|
|
27955
|
-
const statusOutput = await
|
|
28907
|
+
const statusOutput = await runGit3(["status", "--porcelain", "--", filePath], gitRoot).catch(
|
|
27956
28908
|
() => ""
|
|
27957
28909
|
);
|
|
27958
28910
|
const statusCode = statusOutput.trim().substring(0, 2);
|
|
@@ -27962,13 +28914,13 @@ async function gitRevertFileHandler(c, metadataManager) {
|
|
|
27962
28914
|
} catch {
|
|
27963
28915
|
}
|
|
27964
28916
|
} else if (statusCode === "A ") {
|
|
27965
|
-
await
|
|
28917
|
+
await runGit3(["rm", "--cached", "--", filePath], gitRoot);
|
|
27966
28918
|
try {
|
|
27967
28919
|
unlinkSync(resolve6(gitRoot, filePath));
|
|
27968
28920
|
} catch {
|
|
27969
28921
|
}
|
|
27970
28922
|
} else {
|
|
27971
|
-
await
|
|
28923
|
+
await runGit3(["checkout", "HEAD", "--", filePath], gitRoot);
|
|
27972
28924
|
}
|
|
27973
28925
|
return c.json({ success: true });
|
|
27974
28926
|
} catch (error) {
|
|
@@ -28438,8 +29390,8 @@ function createUpdateRoutes(updater) {
|
|
|
28438
29390
|
|
|
28439
29391
|
// src/features/mcp/mcp.routes.ts
|
|
28440
29392
|
import { Hono as Hono15 } from "hono";
|
|
28441
|
-
import { readFile as
|
|
28442
|
-
import { join as
|
|
29393
|
+
import { readFile as readFile18, writeFile as writeFile9, mkdir as mkdir11, access as access6 } from "fs/promises";
|
|
29394
|
+
import { join as join35, dirname as dirname8 } from "path";
|
|
28443
29395
|
|
|
28444
29396
|
// src/features/mcp/mcp.popular.json
|
|
28445
29397
|
var mcp_popular_default = [
|
|
@@ -28561,10 +29513,10 @@ var mcp_popular_default = [
|
|
|
28561
29513
|
var MCP_CONFIG_PATHS2 = [".agents/mcp.json", "mcp.json"];
|
|
28562
29514
|
async function readMCPConfig(projectPath) {
|
|
28563
29515
|
for (const configPath of MCP_CONFIG_PATHS2) {
|
|
28564
|
-
const fullPath =
|
|
29516
|
+
const fullPath = join35(projectPath, configPath);
|
|
28565
29517
|
try {
|
|
28566
29518
|
await access6(fullPath);
|
|
28567
|
-
const content = await
|
|
29519
|
+
const content = await readFile18(fullPath, "utf-8");
|
|
28568
29520
|
const config = JSON.parse(content);
|
|
28569
29521
|
return { config, filePath: fullPath };
|
|
28570
29522
|
} catch {
|
|
@@ -28575,7 +29527,7 @@ async function readMCPConfig(projectPath) {
|
|
|
28575
29527
|
}
|
|
28576
29528
|
async function writeMCPConfig(projectPath, config) {
|
|
28577
29529
|
const existing = await readMCPConfig(projectPath);
|
|
28578
|
-
const targetPath = existing?.filePath ??
|
|
29530
|
+
const targetPath = existing?.filePath ?? join35(projectPath, ".agents/mcp.json");
|
|
28579
29531
|
await mkdir11(dirname8(targetPath), { recursive: true });
|
|
28580
29532
|
await writeFile9(targetPath, JSON.stringify(config, null, 2), "utf-8");
|
|
28581
29533
|
}
|
|
@@ -28778,11 +29730,328 @@ var skills_popular_default = [
|
|
|
28778
29730
|
}
|
|
28779
29731
|
];
|
|
28780
29732
|
|
|
29733
|
+
// src/features/skills/skills.catalog.ts
|
|
29734
|
+
init_dist();
|
|
29735
|
+
import { readdir as readdir13, readFile as readFile19 } from "fs/promises";
|
|
29736
|
+
import { join as join36 } from "path";
|
|
29737
|
+
function getSkillSources() {
|
|
29738
|
+
const sources = skills_sources_default;
|
|
29739
|
+
return [...sources].sort((a, b) => a.title.localeCompare(b.title));
|
|
29740
|
+
}
|
|
29741
|
+
async function getAllSkillSources() {
|
|
29742
|
+
const bundled = getSkillSources();
|
|
29743
|
+
const custom = await getCustomSkillSources();
|
|
29744
|
+
const bundledIds = new Set(bundled.map((source) => source.id));
|
|
29745
|
+
const uniqueCustom = custom.filter((source) => !bundledIds.has(source.id));
|
|
29746
|
+
return [...bundled, ...uniqueCustom].sort((a, b) => a.title.localeCompare(b.title));
|
|
29747
|
+
}
|
|
29748
|
+
async function scanDownloadedSkillCatalog(tmpDir, parsed, sourceId) {
|
|
29749
|
+
const entries = await readdir13(tmpDir, { withFileTypes: true });
|
|
29750
|
+
const catalogItems = [];
|
|
29751
|
+
for (const entry of entries) {
|
|
29752
|
+
if (!entry.isDirectory()) {
|
|
29753
|
+
continue;
|
|
29754
|
+
}
|
|
29755
|
+
const skillMdPath = join36(tmpDir, entry.name, "SKILL.md");
|
|
29756
|
+
let skillMdContent;
|
|
29757
|
+
try {
|
|
29758
|
+
skillMdContent = await readFile19(skillMdPath, "utf8");
|
|
29759
|
+
} catch {
|
|
29760
|
+
continue;
|
|
29761
|
+
}
|
|
29762
|
+
const { metadata } = parseSkillFrontmatter(skillMdContent);
|
|
29763
|
+
const folderPath = parsed.folderPath ? `${parsed.folderPath}/${entry.name}` : entry.name;
|
|
29764
|
+
catalogItems.push({
|
|
29765
|
+
id: entry.name,
|
|
29766
|
+
name: metadata.name ?? entry.name,
|
|
29767
|
+
description: metadata.description ?? "",
|
|
29768
|
+
sourceId,
|
|
29769
|
+
gitHubRepo: parsed.repoUrl,
|
|
29770
|
+
gitHubFolder: folderPath,
|
|
29771
|
+
gitHubRef: parsed.ref
|
|
29772
|
+
});
|
|
29773
|
+
}
|
|
29774
|
+
catalogItems.sort((a, b) => a.name.localeCompare(b.name));
|
|
29775
|
+
return catalogItems;
|
|
29776
|
+
}
|
|
29777
|
+
async function fetchSkillCatalogFromSource(sourceId) {
|
|
29778
|
+
const entry = await refreshCatalogClone(sourceId);
|
|
29779
|
+
return scanDownloadedSkillCatalog(entry.scanDir, entry.parsed, sourceId);
|
|
29780
|
+
}
|
|
29781
|
+
async function verifySkillSource(title, url) {
|
|
29782
|
+
const trimmedTitle = title.trim();
|
|
29783
|
+
const trimmedUrl = url.trim();
|
|
29784
|
+
if (!trimmedTitle) {
|
|
29785
|
+
throw new Error("Title is required");
|
|
29786
|
+
}
|
|
29787
|
+
if (!trimmedUrl) {
|
|
29788
|
+
throw new Error("URL is required");
|
|
29789
|
+
}
|
|
29790
|
+
parseGithubTreeUrl(trimmedUrl);
|
|
29791
|
+
const bundledIds = skills_sources_default.map((source2) => source2.id);
|
|
29792
|
+
const customIds = (await getCustomSkillSources()).map((source2) => source2.id);
|
|
29793
|
+
const sourceId = createCustomSkillSourceId(trimmedTitle, [...bundledIds, ...customIds]);
|
|
29794
|
+
const entry = await refreshCatalogClone(sourceId, trimmedUrl);
|
|
29795
|
+
const skills = await scanDownloadedSkillCatalog(entry.scanDir, entry.parsed, sourceId);
|
|
29796
|
+
if (skills.length === 0) {
|
|
29797
|
+
await clearCatalogClone(sourceId);
|
|
29798
|
+
throw new Error("No skills found at this URL");
|
|
29799
|
+
}
|
|
29800
|
+
const source = {
|
|
29801
|
+
id: sourceId,
|
|
29802
|
+
title: trimmedTitle,
|
|
29803
|
+
url: trimmedUrl
|
|
29804
|
+
};
|
|
29805
|
+
await saveCustomSkillSource(source);
|
|
29806
|
+
return {
|
|
29807
|
+
source,
|
|
29808
|
+
skillCount: skills.length
|
|
29809
|
+
};
|
|
29810
|
+
}
|
|
29811
|
+
async function updateCustomSkillSource(sourceId, title, url) {
|
|
29812
|
+
if (!isCustomSkillSourceId(sourceId)) {
|
|
29813
|
+
throw new Error("Only custom skill sources can be updated");
|
|
29814
|
+
}
|
|
29815
|
+
const existing = await getCustomSkillSourceById(sourceId);
|
|
29816
|
+
if (!existing) {
|
|
29817
|
+
throw new Error("Unknown custom skill source");
|
|
29818
|
+
}
|
|
29819
|
+
const trimmedTitle = title.trim();
|
|
29820
|
+
const trimmedUrl = url.trim();
|
|
29821
|
+
if (!trimmedTitle) {
|
|
29822
|
+
throw new Error("Title is required");
|
|
29823
|
+
}
|
|
29824
|
+
if (!trimmedUrl) {
|
|
29825
|
+
throw new Error("URL is required");
|
|
29826
|
+
}
|
|
29827
|
+
parseGithubTreeUrl(trimmedUrl);
|
|
29828
|
+
const entry = await refreshCatalogClone(sourceId, trimmedUrl);
|
|
29829
|
+
const skills = await scanDownloadedSkillCatalog(entry.scanDir, entry.parsed, sourceId);
|
|
29830
|
+
if (skills.length === 0) {
|
|
29831
|
+
await clearCatalogClone(sourceId);
|
|
29832
|
+
throw new Error("No skills found at this URL");
|
|
29833
|
+
}
|
|
29834
|
+
const source = {
|
|
29835
|
+
id: sourceId,
|
|
29836
|
+
title: trimmedTitle,
|
|
29837
|
+
url: trimmedUrl
|
|
29838
|
+
};
|
|
29839
|
+
await saveCustomSkillSource(source);
|
|
29840
|
+
return {
|
|
29841
|
+
source,
|
|
29842
|
+
skillCount: skills.length
|
|
29843
|
+
};
|
|
29844
|
+
}
|
|
29845
|
+
|
|
28781
29846
|
// src/features/skills/skills.routes.ts
|
|
28782
29847
|
function createSkillRoutes() {
|
|
28783
29848
|
const router = new Hono16();
|
|
28784
29849
|
router.get("/popular", (c) => {
|
|
28785
|
-
|
|
29850
|
+
const skills = [...skills_popular_default].sort((a, b) => a.name.localeCompare(b.name));
|
|
29851
|
+
return c.json({ skills });
|
|
29852
|
+
});
|
|
29853
|
+
router.get("/sources", async (c) => {
|
|
29854
|
+
return c.json({ sources: await getAllSkillSources() });
|
|
29855
|
+
});
|
|
29856
|
+
router.get("/catalog", async (c) => {
|
|
29857
|
+
const sourceId = c.req.query("sourceId");
|
|
29858
|
+
if (!sourceId) {
|
|
29859
|
+
return c.json({ error: { code: "BAD_REQUEST", message: "sourceId is required" } }, 400);
|
|
29860
|
+
}
|
|
29861
|
+
try {
|
|
29862
|
+
const skills = await fetchSkillCatalogFromSource(sourceId);
|
|
29863
|
+
return c.json({ skills });
|
|
29864
|
+
} catch (error) {
|
|
29865
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
29866
|
+
if (message.startsWith("Unknown skill source:") || message.startsWith("Invalid GitHub tree URL:")) {
|
|
29867
|
+
return c.json({ error: { code: "NOT_FOUND", message } }, 404);
|
|
29868
|
+
}
|
|
29869
|
+
return c.json({ error: { code: "INTERNAL_ERROR", message } }, 500);
|
|
29870
|
+
}
|
|
29871
|
+
});
|
|
29872
|
+
router.post("/sources/verify", async (c) => {
|
|
29873
|
+
let body;
|
|
29874
|
+
try {
|
|
29875
|
+
body = await c.req.json();
|
|
29876
|
+
} catch {
|
|
29877
|
+
return c.json({ error: { code: "BAD_REQUEST", message: "Invalid JSON body" } }, 400);
|
|
29878
|
+
}
|
|
29879
|
+
const title = body.title?.trim() ?? "";
|
|
29880
|
+
const url = body.url?.trim() ?? "";
|
|
29881
|
+
if (!title || !url) {
|
|
29882
|
+
return c.json({ error: { code: "BAD_REQUEST", message: "title and url are required" } }, 400);
|
|
29883
|
+
}
|
|
29884
|
+
try {
|
|
29885
|
+
const result = await verifySkillSource(title, url);
|
|
29886
|
+
return c.json(result);
|
|
29887
|
+
} catch (error) {
|
|
29888
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
29889
|
+
if (message === "Title is required" || message === "URL is required" || message === "No skills found at this URL" || message.startsWith("Invalid GitHub tree URL:")) {
|
|
29890
|
+
return c.json({ error: { code: "BAD_REQUEST", message } }, 400);
|
|
29891
|
+
}
|
|
29892
|
+
return c.json({ error: { code: "INTERNAL_ERROR", message } }, 500);
|
|
29893
|
+
}
|
|
29894
|
+
});
|
|
29895
|
+
router.put("/sources/:sourceId", async (c) => {
|
|
29896
|
+
const sourceId = c.req.param("sourceId");
|
|
29897
|
+
let body;
|
|
29898
|
+
try {
|
|
29899
|
+
body = await c.req.json();
|
|
29900
|
+
} catch {
|
|
29901
|
+
return c.json({ error: { code: "BAD_REQUEST", message: "Invalid JSON body" } }, 400);
|
|
29902
|
+
}
|
|
29903
|
+
const title = body.title?.trim() ?? "";
|
|
29904
|
+
const url = body.url?.trim() ?? "";
|
|
29905
|
+
if (!title || !url) {
|
|
29906
|
+
return c.json({ error: { code: "BAD_REQUEST", message: "title and url are required" } }, 400);
|
|
29907
|
+
}
|
|
29908
|
+
try {
|
|
29909
|
+
const result = await updateCustomSkillSource(sourceId, title, url);
|
|
29910
|
+
return c.json(result);
|
|
29911
|
+
} catch (error) {
|
|
29912
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
29913
|
+
if (message === "Unknown custom skill source") {
|
|
29914
|
+
return c.json({ error: { code: "NOT_FOUND", message } }, 404);
|
|
29915
|
+
}
|
|
29916
|
+
if (message === "Only custom skill sources can be updated" || message === "Title is required" || message === "URL is required" || message === "No skills found at this URL" || message.startsWith("Invalid GitHub tree URL:")) {
|
|
29917
|
+
return c.json({ error: { code: "BAD_REQUEST", message } }, 400);
|
|
29918
|
+
}
|
|
29919
|
+
return c.json({ error: { code: "INTERNAL_ERROR", message } }, 500);
|
|
29920
|
+
}
|
|
29921
|
+
});
|
|
29922
|
+
router.delete("/sources/:sourceId", async (c) => {
|
|
29923
|
+
const sourceId = c.req.param("sourceId");
|
|
29924
|
+
try {
|
|
29925
|
+
await deleteCustomSkillSource(sourceId);
|
|
29926
|
+
return c.json({ success: true });
|
|
29927
|
+
} catch (error) {
|
|
29928
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
29929
|
+
if (message === "Unknown custom skill source") {
|
|
29930
|
+
return c.json({ error: { code: "NOT_FOUND", message } }, 404);
|
|
29931
|
+
}
|
|
29932
|
+
if (message === "Only custom skill sources can be deleted") {
|
|
29933
|
+
return c.json({ error: { code: "BAD_REQUEST", message } }, 400);
|
|
29934
|
+
}
|
|
29935
|
+
return c.json({ error: { code: "INTERNAL_ERROR", message } }, 500);
|
|
29936
|
+
}
|
|
29937
|
+
});
|
|
29938
|
+
router.post("/sources/import", async (c) => {
|
|
29939
|
+
let body;
|
|
29940
|
+
try {
|
|
29941
|
+
body = await c.req.json();
|
|
29942
|
+
} catch {
|
|
29943
|
+
return c.json({ error: { code: "BAD_REQUEST", message: "Invalid JSON body" } }, 400);
|
|
29944
|
+
}
|
|
29945
|
+
if (!Array.isArray(body.sources)) {
|
|
29946
|
+
return c.json({ error: { code: "BAD_REQUEST", message: "sources array is required" } }, 400);
|
|
29947
|
+
}
|
|
29948
|
+
try {
|
|
29949
|
+
const sources = await importCustomSkillSources(
|
|
29950
|
+
body.sources.filter(
|
|
29951
|
+
(source) => typeof source?.id === "string" && typeof source?.title === "string" && typeof source?.url === "string"
|
|
29952
|
+
)
|
|
29953
|
+
);
|
|
29954
|
+
return c.json({ sources });
|
|
29955
|
+
} catch (error) {
|
|
29956
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
29957
|
+
return c.json({ error: { code: "INTERNAL_ERROR", message } }, 500);
|
|
29958
|
+
}
|
|
29959
|
+
});
|
|
29960
|
+
router.get("/catalog/files", async (c) => {
|
|
29961
|
+
const sourceId = c.req.query("sourceId");
|
|
29962
|
+
const skillId = c.req.query("skillId");
|
|
29963
|
+
if (!sourceId || !skillId) {
|
|
29964
|
+
return c.json(
|
|
29965
|
+
{ error: { code: "BAD_REQUEST", message: "sourceId and skillId are required" } },
|
|
29966
|
+
400
|
|
29967
|
+
);
|
|
29968
|
+
}
|
|
29969
|
+
try {
|
|
29970
|
+
const files = await listCachedCatalogSkillFiles(sourceId, skillId);
|
|
29971
|
+
return c.json({ files });
|
|
29972
|
+
} catch (error) {
|
|
29973
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
29974
|
+
if (message.startsWith("Unknown skill source:")) {
|
|
29975
|
+
return c.json({ error: { code: "NOT_FOUND", message } }, 404);
|
|
29976
|
+
}
|
|
29977
|
+
return c.json({ error: { code: "INTERNAL_ERROR", message } }, 500);
|
|
29978
|
+
}
|
|
29979
|
+
});
|
|
29980
|
+
router.get("/catalog/file", async (c) => {
|
|
29981
|
+
const sourceId = c.req.query("sourceId");
|
|
29982
|
+
const skillId = c.req.query("skillId");
|
|
29983
|
+
const relativePath = c.req.query("relativePath");
|
|
29984
|
+
if (!sourceId || !skillId || !relativePath) {
|
|
29985
|
+
return c.json(
|
|
29986
|
+
{
|
|
29987
|
+
error: {
|
|
29988
|
+
code: "BAD_REQUEST",
|
|
29989
|
+
message: "sourceId, skillId, and relativePath are required"
|
|
29990
|
+
}
|
|
29991
|
+
},
|
|
29992
|
+
400
|
|
29993
|
+
);
|
|
29994
|
+
}
|
|
29995
|
+
try {
|
|
29996
|
+
const content = await readCachedCatalogSkillFile(sourceId, skillId, relativePath);
|
|
29997
|
+
return c.json({ content, relativePath });
|
|
29998
|
+
} catch (error) {
|
|
29999
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
30000
|
+
if (message.startsWith("Unknown skill source:") || message.startsWith("Invalid file path:")) {
|
|
30001
|
+
return c.json({ error: { code: "NOT_FOUND", message } }, 404);
|
|
30002
|
+
}
|
|
30003
|
+
return c.json({ error: { code: "INTERNAL_ERROR", message } }, 500);
|
|
30004
|
+
}
|
|
30005
|
+
});
|
|
30006
|
+
router.get("/github-folder/files", async (c) => {
|
|
30007
|
+
const gitHubRepo = c.req.query("gitHubRepo");
|
|
30008
|
+
const gitHubFolder = c.req.query("gitHubFolder");
|
|
30009
|
+
if (!gitHubRepo || !gitHubFolder) {
|
|
30010
|
+
return c.json(
|
|
30011
|
+
{ error: { code: "BAD_REQUEST", message: "gitHubRepo and gitHubFolder are required" } },
|
|
30012
|
+
400
|
|
30013
|
+
);
|
|
30014
|
+
}
|
|
30015
|
+
try {
|
|
30016
|
+
const result = await listCachedCatalogSkillFilesByGithubFolder(gitHubRepo, gitHubFolder);
|
|
30017
|
+
return c.json(result);
|
|
30018
|
+
} catch (error) {
|
|
30019
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
30020
|
+
if (message.startsWith("No catalog source found")) {
|
|
30021
|
+
return c.json({ error: { code: "NOT_FOUND", message } }, 404);
|
|
30022
|
+
}
|
|
30023
|
+
return c.json({ error: { code: "INTERNAL_ERROR", message } }, 500);
|
|
30024
|
+
}
|
|
30025
|
+
});
|
|
30026
|
+
router.get("/github-folder/file", async (c) => {
|
|
30027
|
+
const gitHubRepo = c.req.query("gitHubRepo");
|
|
30028
|
+
const gitHubFolder = c.req.query("gitHubFolder");
|
|
30029
|
+
const relativePath = c.req.query("relativePath");
|
|
30030
|
+
if (!gitHubRepo || !gitHubFolder || !relativePath) {
|
|
30031
|
+
return c.json(
|
|
30032
|
+
{
|
|
30033
|
+
error: {
|
|
30034
|
+
code: "BAD_REQUEST",
|
|
30035
|
+
message: "gitHubRepo, gitHubFolder, and relativePath are required"
|
|
30036
|
+
}
|
|
30037
|
+
},
|
|
30038
|
+
400
|
|
30039
|
+
);
|
|
30040
|
+
}
|
|
30041
|
+
try {
|
|
30042
|
+
const content = await readCachedCatalogSkillFileByGithubFolder(
|
|
30043
|
+
gitHubRepo,
|
|
30044
|
+
gitHubFolder,
|
|
30045
|
+
relativePath
|
|
30046
|
+
);
|
|
30047
|
+
return c.json({ content, relativePath });
|
|
30048
|
+
} catch (error) {
|
|
30049
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
30050
|
+
if (message.startsWith("No catalog source found") || message.startsWith("Invalid file path:")) {
|
|
30051
|
+
return c.json({ error: { code: "NOT_FOUND", message } }, 404);
|
|
30052
|
+
}
|
|
30053
|
+
return c.json({ error: { code: "INTERNAL_ERROR", message } }, 500);
|
|
30054
|
+
}
|
|
28786
30055
|
});
|
|
28787
30056
|
return router;
|
|
28788
30057
|
}
|
|
@@ -28992,7 +30261,7 @@ function getLocalNetworkAddresses() {
|
|
|
28992
30261
|
|
|
28993
30262
|
// src/core/server-bind-mode-store.ts
|
|
28994
30263
|
import { existsSync as existsSync25, mkdirSync as mkdirSync2, readFileSync as readFileSync9, writeFileSync } from "fs";
|
|
28995
|
-
import { join as
|
|
30264
|
+
import { join as join37 } from "path";
|
|
28996
30265
|
|
|
28997
30266
|
// src/core/server-bind-mode.ts
|
|
28998
30267
|
var DEFAULT_SERVER_BIND_MODE = "local";
|
|
@@ -29004,7 +30273,7 @@ function getServerBindHostname(mode) {
|
|
|
29004
30273
|
}
|
|
29005
30274
|
|
|
29006
30275
|
// src/core/server-bind-mode-store.ts
|
|
29007
|
-
var SERVER_BIND_MODE_FILE =
|
|
30276
|
+
var SERVER_BIND_MODE_FILE = join37(DATA_DIR, "server-bind-mode.json");
|
|
29008
30277
|
function hasServerBindModeFile() {
|
|
29009
30278
|
return existsSync25(SERVER_BIND_MODE_FILE);
|
|
29010
30279
|
}
|
|
@@ -29358,7 +30627,7 @@ async function clipboardWriteImage(c) {
|
|
|
29358
30627
|
|
|
29359
30628
|
// src/features/image/image-save.route.ts
|
|
29360
30629
|
init_dist();
|
|
29361
|
-
import { join as
|
|
30630
|
+
import { join as join38 } from "path";
|
|
29362
30631
|
var Utils5 = null;
|
|
29363
30632
|
var utilsLoaded5 = false;
|
|
29364
30633
|
async function loadUtils5() {
|
|
@@ -29396,7 +30665,7 @@ async function saveImage(c) {
|
|
|
29396
30665
|
return c.json({ error: "imageUrl or imageData is required" }, 400);
|
|
29397
30666
|
}
|
|
29398
30667
|
const name = filename ?? `generated-image-${Date.now()}.png`;
|
|
29399
|
-
const savePath =
|
|
30668
|
+
const savePath = join38(Utils5.paths.downloads, name);
|
|
29400
30669
|
await Bun.write(savePath, data);
|
|
29401
30670
|
return c.json({ success: true, path: savePath });
|
|
29402
30671
|
} catch (error) {
|
|
@@ -29870,7 +31139,7 @@ async function startTarskServerInternal(options) {
|
|
|
29870
31139
|
processingThreadIds: processingStateManager.getProcessingThreadIds()
|
|
29871
31140
|
});
|
|
29872
31141
|
});
|
|
29873
|
-
app.route("/api/projects", createProjectRoutes(projectManager, threadManager));
|
|
31142
|
+
app.route("/api/projects", createProjectRoutes(projectManager, threadManager, metadataManager));
|
|
29874
31143
|
app.route("/api/projects", createRunRoutes(projectManager));
|
|
29875
31144
|
app.route("/api/projects", createProjectTodosRoutes(metadataManager));
|
|
29876
31145
|
app.route("/api/threads", createThreadRoutes(threadManager, gitManager2, conversationManager));
|