sparkecoder 0.1.120 → 0.1.121
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/agent/index.js.map +1 -1
- package/dist/cli.js +379 -125
- package/dist/cli.js.map +1 -1
- package/dist/index.js +338 -84
- package/dist/index.js.map +1 -1
- package/dist/server/index.js +338 -84
- package/dist/server/index.js.map +1 -1
- package/dist/tools/index.js.map +1 -1
- package/package.json +1 -1
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/standalone/web/.next/BUILD_ID +1 -1
- package/web/.next/standalone/web/.next/build-manifest.json +2 -2
- package/web/.next/standalone/web/.next/prerender-manifest.json +3 -3
- package/web/.next/standalone/web/.next/server/app/(main)/agents/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/(main)/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/(main)/settings/page.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/(main)/settings/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
- package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/agents.html +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p/agents/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p/agents.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.segments/!KG1haW4p.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/agents.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/agents.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.html +1 -1
- package/web/.next/standalone/web/.next/server/app/index.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/settings.html +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.rsc +3 -3
- package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p/settings/__PAGE__.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p/settings.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.segments/_full.segment.rsc +3 -3
- package/web/.next/standalone/web/.next/server/app/settings.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/settings.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_lucide-react_dist_esm_icons_6ab1f7b7._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__f3e6443f._.js → [root-of-the-server]__4de426bd._.js} +2 -2
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_62ca4286._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_src_app_(main)_settings_page_tsx_eb320e07._.js +3 -1
- package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
- package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
- package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
- package/web/.next/standalone/web/.next/static/chunks/74ae1f17d607b2fc.js +7 -0
- package/web/.next/standalone/web/.next/static/chunks/883ea0d08f88e366.js +1 -0
- package/web/.next/standalone/web/.next/static/chunks/{44c575e006ddb992.js → 91988e253d5fa420.js} +4 -4
- package/web/.next/standalone/web/.next/static/chunks/9b88f148788e4504.js +3 -0
- package/web/.next/standalone/web/.next/static/chunks/acb0fc66f5414af6.css +1 -0
- package/web/.next/standalone/web/.next/static/static/chunks/74ae1f17d607b2fc.js +7 -0
- package/web/.next/standalone/web/.next/static/static/chunks/883ea0d08f88e366.js +1 -0
- package/web/.next/standalone/web/.next/static/static/chunks/{44c575e006ddb992.js → 91988e253d5fa420.js} +4 -4
- package/web/.next/standalone/web/.next/static/static/chunks/9b88f148788e4504.js +3 -0
- package/web/.next/standalone/web/.next/static/static/chunks/acb0fc66f5414af6.css +1 -0
- package/web/.next/standalone/web/src/app/(main)/settings/page.tsx +464 -1
- package/web/.next/static/chunks/74ae1f17d607b2fc.js +7 -0
- package/web/.next/static/chunks/883ea0d08f88e366.js +1 -0
- package/web/.next/static/chunks/{44c575e006ddb992.js → 91988e253d5fa420.js} +4 -4
- package/web/.next/static/chunks/9b88f148788e4504.js +3 -0
- package/web/.next/static/chunks/acb0fc66f5414af6.css +1 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_lucide-react_dist_esm_icons_7340c8b3._.js +0 -3
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_41927ef5._.js +0 -3
- package/web/.next/standalone/web/.next/static/chunks/2c3c1d478808e4e4.js +0 -1
- package/web/.next/standalone/web/.next/static/chunks/3b0501ec3249235f.js +0 -7
- package/web/.next/standalone/web/.next/static/chunks/9a3afb48c245fb2a.js +0 -1
- package/web/.next/standalone/web/.next/static/chunks/b3bc7244f3477729.css +0 -1
- package/web/.next/standalone/web/.next/static/static/chunks/2c3c1d478808e4e4.js +0 -1
- package/web/.next/standalone/web/.next/static/static/chunks/3b0501ec3249235f.js +0 -7
- package/web/.next/standalone/web/.next/static/static/chunks/9a3afb48c245fb2a.js +0 -1
- package/web/.next/standalone/web/.next/static/static/chunks/b3bc7244f3477729.css +0 -1
- package/web/.next/static/chunks/2c3c1d478808e4e4.js +0 -1
- package/web/.next/static/chunks/3b0501ec3249235f.js +0 -7
- package/web/.next/static/chunks/9a3afb48c245fb2a.js +0 -1
- package/web/.next/static/chunks/b3bc7244f3477729.css +0 -1
- /package/web/.next/standalone/web/.next/static/static/{uy1OnyxIm3QeGGgKEmxAj → wP9z41wtqT4k-O6AlEXqw}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/static/{uy1OnyxIm3QeGGgKEmxAj → wP9z41wtqT4k-O6AlEXqw}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/static/{uy1OnyxIm3QeGGgKEmxAj → wP9z41wtqT4k-O6AlEXqw}/_ssgManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{uy1OnyxIm3QeGGgKEmxAj → wP9z41wtqT4k-O6AlEXqw}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{uy1OnyxIm3QeGGgKEmxAj → wP9z41wtqT4k-O6AlEXqw}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{uy1OnyxIm3QeGGgKEmxAj → wP9z41wtqT4k-O6AlEXqw}/_ssgManifest.js +0 -0
- /package/web/.next/static/{uy1OnyxIm3QeGGgKEmxAj → wP9z41wtqT4k-O6AlEXqw}/_buildManifest.js +0 -0
- /package/web/.next/static/{uy1OnyxIm3QeGGgKEmxAj → wP9z41wtqT4k-O6AlEXqw}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{uy1OnyxIm3QeGGgKEmxAj → wP9z41wtqT4k-O6AlEXqw}/_ssgManifest.js +0 -0
package/dist/index.js
CHANGED
|
@@ -450,6 +450,7 @@ __export(config_exports, {
|
|
|
450
450
|
setApiKey: () => setApiKey,
|
|
451
451
|
setMcpServers: () => setMcpServers,
|
|
452
452
|
setPublicUrl: () => setPublicUrl,
|
|
453
|
+
setSkillsAdditionalDirectories: () => setSkillsAdditionalDirectories,
|
|
453
454
|
setSlackConfig: () => setSlackConfig,
|
|
454
455
|
setWebhookToken: () => setWebhookToken
|
|
455
456
|
});
|
|
@@ -739,6 +740,40 @@ function setWebhookToken(token) {
|
|
|
739
740
|
console.warn("[config] failed to persist webhook token:", err?.message || err);
|
|
740
741
|
}
|
|
741
742
|
}
|
|
743
|
+
function setSkillsAdditionalDirectories(directories) {
|
|
744
|
+
if (cachedConfig) {
|
|
745
|
+
const cur = cachedConfig.skills || {};
|
|
746
|
+
cachedConfig.skills = { ...cur, additionalDirectories: directories };
|
|
747
|
+
try {
|
|
748
|
+
const discovered = discoverSkillDirectories(cachedConfig.resolvedWorkingDirectory);
|
|
749
|
+
const additionalAbs = directories.map((d) => resolve(cachedConfig.resolvedWorkingDirectory, d)).filter((d) => existsSync(d));
|
|
750
|
+
cachedConfig.discoveredSkills = discovered;
|
|
751
|
+
cachedConfig.resolvedSkillsDirectories = [
|
|
752
|
+
...discovered.allDirectories,
|
|
753
|
+
...additionalAbs
|
|
754
|
+
];
|
|
755
|
+
} catch {
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
try {
|
|
759
|
+
const cwdPath = resolve(process.cwd(), "sparkecoder.config.json");
|
|
760
|
+
const target = existsSync(cwdPath) ? cwdPath : join(ensureAppDataDirectory(), "sparkecoder.config.json");
|
|
761
|
+
let raw = {};
|
|
762
|
+
if (existsSync(target)) {
|
|
763
|
+
try {
|
|
764
|
+
raw = JSON.parse(readFileSync(target, "utf-8"));
|
|
765
|
+
} catch {
|
|
766
|
+
raw = {};
|
|
767
|
+
}
|
|
768
|
+
} else {
|
|
769
|
+
raw = createDefaultConfig();
|
|
770
|
+
}
|
|
771
|
+
raw.skills = { ...raw.skills || {}, additionalDirectories: directories };
|
|
772
|
+
writeFileSync(target, JSON.stringify(raw, null, 2));
|
|
773
|
+
} catch (err) {
|
|
774
|
+
console.warn("[config] failed to persist skill directories:", err?.message || err);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
742
777
|
function setPublicUrl(publicUrl) {
|
|
743
778
|
if (cachedConfig) {
|
|
744
779
|
cachedConfig.server = { ...cachedConfig.server ?? {}, publicUrl };
|
|
@@ -3030,7 +3065,7 @@ async function createClient(serverId, handle, root) {
|
|
|
3030
3065
|
},
|
|
3031
3066
|
async waitForDiagnostics(filePath, timeoutMs = 5e3) {
|
|
3032
3067
|
const normalized = normalizePath(filePath);
|
|
3033
|
-
return new Promise((
|
|
3068
|
+
return new Promise((resolve13) => {
|
|
3034
3069
|
const startTime = Date.now();
|
|
3035
3070
|
let debounceTimer;
|
|
3036
3071
|
let resolved = false;
|
|
@@ -3049,7 +3084,7 @@ async function createClient(serverId, handle, root) {
|
|
|
3049
3084
|
if (resolved) return;
|
|
3050
3085
|
resolved = true;
|
|
3051
3086
|
cleanup2();
|
|
3052
|
-
|
|
3087
|
+
resolve13(diagnostics.get(normalized) || []);
|
|
3053
3088
|
};
|
|
3054
3089
|
const onDiagnostic = () => {
|
|
3055
3090
|
if (debounceTimer) clearTimeout(debounceTimer);
|
|
@@ -3424,7 +3459,7 @@ Working directory: ${options.workingDirectory}`,
|
|
|
3424
3459
|
isChunked: true
|
|
3425
3460
|
});
|
|
3426
3461
|
if (chunkCount > 1) {
|
|
3427
|
-
await new Promise((
|
|
3462
|
+
await new Promise((resolve13) => setTimeout(resolve13, 0));
|
|
3428
3463
|
}
|
|
3429
3464
|
}
|
|
3430
3465
|
}
|
|
@@ -4043,7 +4078,7 @@ async function loadSkillsFromDirectory(directory, options = {}) {
|
|
|
4043
4078
|
if (!existsSync10(directory)) {
|
|
4044
4079
|
return [];
|
|
4045
4080
|
}
|
|
4046
|
-
const
|
|
4081
|
+
const skills2 = [];
|
|
4047
4082
|
const entries = await readdir(directory, { withFileTypes: true });
|
|
4048
4083
|
for (const entry2 of entries) {
|
|
4049
4084
|
let filePath;
|
|
@@ -4067,7 +4102,7 @@ async function loadSkillsFromDirectory(directory, options = {}) {
|
|
|
4067
4102
|
if (parsed) {
|
|
4068
4103
|
const alwaysApply = forceAlwaysApply || parsed.metadata.alwaysApply;
|
|
4069
4104
|
const loadType = alwaysApply ? "always" : defaultLoadType;
|
|
4070
|
-
|
|
4105
|
+
skills2.push({
|
|
4071
4106
|
name: parsed.metadata.name,
|
|
4072
4107
|
description: parsed.metadata.description,
|
|
4073
4108
|
filePath,
|
|
@@ -4081,7 +4116,7 @@ async function loadSkillsFromDirectory(directory, options = {}) {
|
|
|
4081
4116
|
} else {
|
|
4082
4117
|
const name = getSkillNameFromPath(filePath);
|
|
4083
4118
|
const firstParagraph = content.split("\n\n")[0]?.slice(0, 200) || "No description";
|
|
4084
|
-
|
|
4119
|
+
skills2.push({
|
|
4085
4120
|
name,
|
|
4086
4121
|
description: firstParagraph.replace(/^#\s*/, "").trim(),
|
|
4087
4122
|
filePath,
|
|
@@ -4094,7 +4129,7 @@ async function loadSkillsFromDirectory(directory, options = {}) {
|
|
|
4094
4129
|
});
|
|
4095
4130
|
}
|
|
4096
4131
|
}
|
|
4097
|
-
return
|
|
4132
|
+
return skills2.filter(
|
|
4098
4133
|
(s) => s.platforms.length === 0 || s.platforms.includes(process.platform)
|
|
4099
4134
|
);
|
|
4100
4135
|
}
|
|
@@ -4102,8 +4137,8 @@ async function loadAllSkills(directories) {
|
|
|
4102
4137
|
const allSkills = [];
|
|
4103
4138
|
const seenNames = /* @__PURE__ */ new Set();
|
|
4104
4139
|
for (const dir of directories) {
|
|
4105
|
-
const
|
|
4106
|
-
for (const skill of
|
|
4140
|
+
const skills2 = await loadSkillsFromDirectory(dir);
|
|
4141
|
+
for (const skill of skills2) {
|
|
4107
4142
|
if (!seenNames.has(skill.name.toLowerCase())) {
|
|
4108
4143
|
seenNames.add(skill.name.toLowerCase());
|
|
4109
4144
|
allSkills.push(skill);
|
|
@@ -4116,12 +4151,12 @@ async function loadAllSkillsFromDiscovered(discovered) {
|
|
|
4116
4151
|
const allSkills = [];
|
|
4117
4152
|
const seenNames = /* @__PURE__ */ new Set();
|
|
4118
4153
|
for (const { path, priority } of discovered.alwaysLoadedDirs) {
|
|
4119
|
-
const
|
|
4154
|
+
const skills2 = await loadSkillsFromDirectory(path, {
|
|
4120
4155
|
priority,
|
|
4121
4156
|
defaultLoadType: "always",
|
|
4122
4157
|
forceAlwaysApply: true
|
|
4123
4158
|
});
|
|
4124
|
-
for (const skill of
|
|
4159
|
+
for (const skill of skills2) {
|
|
4125
4160
|
if (!seenNames.has(skill.name.toLowerCase())) {
|
|
4126
4161
|
seenNames.add(skill.name.toLowerCase());
|
|
4127
4162
|
allSkills.push(skill);
|
|
@@ -4129,12 +4164,12 @@ async function loadAllSkillsFromDiscovered(discovered) {
|
|
|
4129
4164
|
}
|
|
4130
4165
|
}
|
|
4131
4166
|
for (const { path, priority } of discovered.onDemandDirs) {
|
|
4132
|
-
const
|
|
4167
|
+
const skills2 = await loadSkillsFromDirectory(path, {
|
|
4133
4168
|
priority,
|
|
4134
4169
|
defaultLoadType: "on_demand",
|
|
4135
4170
|
forceAlwaysApply: false
|
|
4136
4171
|
});
|
|
4137
|
-
for (const skill of
|
|
4172
|
+
for (const skill of skills2) {
|
|
4138
4173
|
if (!seenNames.has(skill.name.toLowerCase())) {
|
|
4139
4174
|
seenNames.add(skill.name.toLowerCase());
|
|
4140
4175
|
allSkills.push(skill);
|
|
@@ -4159,7 +4194,7 @@ async function loadAllSkillsFromDiscovered(discovered) {
|
|
|
4159
4194
|
all: allSkills
|
|
4160
4195
|
};
|
|
4161
4196
|
}
|
|
4162
|
-
async function getGlobMatchedSkills(
|
|
4197
|
+
async function getGlobMatchedSkills(skills2, activeFiles, workingDirectory) {
|
|
4163
4198
|
if (activeFiles.length === 0) {
|
|
4164
4199
|
return [];
|
|
4165
4200
|
}
|
|
@@ -4169,7 +4204,7 @@ async function getGlobMatchedSkills(skills, activeFiles, workingDirectory) {
|
|
|
4169
4204
|
}
|
|
4170
4205
|
return f;
|
|
4171
4206
|
});
|
|
4172
|
-
const matchedSkills =
|
|
4207
|
+
const matchedSkills = skills2.filter((skill) => {
|
|
4173
4208
|
if (skill.alwaysApply || skill.loadType === "always") {
|
|
4174
4209
|
return false;
|
|
4175
4210
|
}
|
|
@@ -4215,8 +4250,8 @@ async function loadSkillContent(skillName, directories) {
|
|
|
4215
4250
|
content: parsed ? parsed.body : content
|
|
4216
4251
|
};
|
|
4217
4252
|
}
|
|
4218
|
-
function formatSkillsForContext(
|
|
4219
|
-
const onDemandSkills =
|
|
4253
|
+
function formatSkillsForContext(skills2) {
|
|
4254
|
+
const onDemandSkills = skills2.filter((s) => !s.alwaysApply && s.loadType !== "always");
|
|
4220
4255
|
if (onDemandSkills.length === 0) {
|
|
4221
4256
|
return "No on-demand skills available.";
|
|
4222
4257
|
}
|
|
@@ -4227,12 +4262,12 @@ function formatSkillsForContext(skills) {
|
|
|
4227
4262
|
}
|
|
4228
4263
|
return lines.join("\n");
|
|
4229
4264
|
}
|
|
4230
|
-
function formatAlwaysLoadedSkills(
|
|
4231
|
-
if (
|
|
4265
|
+
function formatAlwaysLoadedSkills(skills2) {
|
|
4266
|
+
if (skills2.length === 0) {
|
|
4232
4267
|
return "";
|
|
4233
4268
|
}
|
|
4234
4269
|
const sections = [];
|
|
4235
|
-
for (const skill of
|
|
4270
|
+
for (const skill of skills2) {
|
|
4236
4271
|
sections.push(`### ${skill.name}
|
|
4237
4272
|
|
|
4238
4273
|
${skill.content}`);
|
|
@@ -4241,12 +4276,12 @@ ${skill.content}`);
|
|
|
4241
4276
|
|
|
4242
4277
|
${sections.join("\n\n---\n\n")}`;
|
|
4243
4278
|
}
|
|
4244
|
-
function formatGlobMatchedSkills(
|
|
4245
|
-
if (
|
|
4279
|
+
function formatGlobMatchedSkills(skills2) {
|
|
4280
|
+
if (skills2.length === 0) {
|
|
4246
4281
|
return "";
|
|
4247
4282
|
}
|
|
4248
4283
|
const sections = [];
|
|
4249
|
-
for (const skill of
|
|
4284
|
+
for (const skill of skills2) {
|
|
4250
4285
|
sections.push(`### ${skill.name}
|
|
4251
4286
|
|
|
4252
4287
|
${skill.content}`);
|
|
@@ -4288,16 +4323,16 @@ Once loaded, a skill's content will be available in the conversation context.`,
|
|
|
4288
4323
|
try {
|
|
4289
4324
|
switch (action) {
|
|
4290
4325
|
case "list": {
|
|
4291
|
-
const
|
|
4326
|
+
const skills2 = await loadAllSkills(options.skillsDirectories);
|
|
4292
4327
|
return {
|
|
4293
4328
|
success: true,
|
|
4294
4329
|
action: "list",
|
|
4295
|
-
skillCount:
|
|
4296
|
-
skills:
|
|
4330
|
+
skillCount: skills2.length,
|
|
4331
|
+
skills: skills2.map((s) => ({
|
|
4297
4332
|
name: s.name,
|
|
4298
4333
|
description: s.description
|
|
4299
4334
|
})),
|
|
4300
|
-
formatted: formatSkillsForContext(
|
|
4335
|
+
formatted: formatSkillsForContext(skills2)
|
|
4301
4336
|
};
|
|
4302
4337
|
}
|
|
4303
4338
|
case "load": {
|
|
@@ -4735,8 +4770,8 @@ var init_subagent = __esm({
|
|
|
4735
4770
|
if (eventQueue.length > 0) {
|
|
4736
4771
|
yield eventQueue.shift();
|
|
4737
4772
|
} else if (!done) {
|
|
4738
|
-
const event = await new Promise((
|
|
4739
|
-
resolveNext =
|
|
4773
|
+
const event = await new Promise((resolve13) => {
|
|
4774
|
+
resolveNext = resolve13;
|
|
4740
4775
|
});
|
|
4741
4776
|
if (event) {
|
|
4742
4777
|
yield event;
|
|
@@ -6512,8 +6547,8 @@ async function buildSystemPrompt(options) {
|
|
|
6512
6547
|
}
|
|
6513
6548
|
} else {
|
|
6514
6549
|
const { loadAllSkills: loadAllSkills2 } = await Promise.resolve().then(() => (init_skills(), skills_exports));
|
|
6515
|
-
const
|
|
6516
|
-
onDemandSkillsContext = formatSkillsForContext(
|
|
6550
|
+
const skills2 = await loadAllSkills2(skillsDirectories);
|
|
6551
|
+
onDemandSkillsContext = formatSkillsForContext(skills2);
|
|
6517
6552
|
}
|
|
6518
6553
|
const todos = await todoQueries.getBySession(sessionId);
|
|
6519
6554
|
const todosContext = formatTodosForContext(todos);
|
|
@@ -8981,11 +9016,11 @@ function waitForTaskQuestionAnswer(question) {
|
|
|
8981
9016
|
if (pendingQuestions.has(k)) {
|
|
8982
9017
|
return Promise.reject(new Error(`Question already pending: ${question.questionId}`));
|
|
8983
9018
|
}
|
|
8984
|
-
return new Promise((
|
|
9019
|
+
return new Promise((resolve13, reject) => {
|
|
8985
9020
|
pendingQuestions.set(k, {
|
|
8986
9021
|
...question,
|
|
8987
9022
|
createdAt: /* @__PURE__ */ new Date(),
|
|
8988
|
-
resolve:
|
|
9023
|
+
resolve: resolve13,
|
|
8989
9024
|
reject
|
|
8990
9025
|
});
|
|
8991
9026
|
});
|
|
@@ -10134,14 +10169,14 @@ ${p.text}` : p.text;
|
|
|
10134
10169
|
const result = await recorder.encode();
|
|
10135
10170
|
recorder.clear();
|
|
10136
10171
|
if (!result) return [];
|
|
10137
|
-
const { readFile:
|
|
10172
|
+
const { readFile: readFile13, unlink: unlink4 } = await import("fs/promises");
|
|
10138
10173
|
const uploadInfo = await storageQueries2.getUploadUrl(
|
|
10139
10174
|
this.session.id,
|
|
10140
10175
|
`browser-recording-${Date.now()}.mp4`,
|
|
10141
10176
|
"video/mp4",
|
|
10142
10177
|
"browser-recording"
|
|
10143
10178
|
);
|
|
10144
|
-
const fileData = await
|
|
10179
|
+
const fileData = await readFile13(result.path);
|
|
10145
10180
|
await fetch(uploadInfo.uploadUrl, {
|
|
10146
10181
|
method: "PUT",
|
|
10147
10182
|
headers: { "Content-Type": "video/mp4" },
|
|
@@ -10149,7 +10184,7 @@ ${p.text}` : p.text;
|
|
|
10149
10184
|
});
|
|
10150
10185
|
await storageQueries2.updateFile(uploadInfo.fileId, { sizeBytes: result.sizeBytes });
|
|
10151
10186
|
const dlInfo = await storageQueries2.getDownloadUrl(uploadInfo.fileId);
|
|
10152
|
-
await
|
|
10187
|
+
await unlink4(result.path).catch(() => {
|
|
10153
10188
|
});
|
|
10154
10189
|
console.log(`[TASK] Browser recording uploaded (${result.sizeBytes} bytes)`);
|
|
10155
10190
|
return [dlInfo.downloadUrl];
|
|
@@ -10167,13 +10202,13 @@ ${p.text}` : p.text;
|
|
|
10167
10202
|
try {
|
|
10168
10203
|
const { isRemoteConfigured: isRemoteConfigured2, storageQueries: storageQueries2 } = await Promise.resolve().then(() => (init_remote(), remote_exports));
|
|
10169
10204
|
if (!isRemoteConfigured2()) return [];
|
|
10170
|
-
const { readFile:
|
|
10171
|
-
const { join:
|
|
10205
|
+
const { readFile: readFile13 } = await import("fs/promises");
|
|
10206
|
+
const { join: join17, basename: basename7 } = await import("path");
|
|
10172
10207
|
const urls = [];
|
|
10173
10208
|
for (const filePath of filePaths) {
|
|
10174
10209
|
try {
|
|
10175
|
-
const fullPath = filePath.startsWith("/") ? filePath :
|
|
10176
|
-
const fileName =
|
|
10210
|
+
const fullPath = filePath.startsWith("/") ? filePath : join17(this.session.workingDirectory, filePath);
|
|
10211
|
+
const fileName = basename7(fullPath);
|
|
10177
10212
|
const ext = fileName.split(".").pop()?.toLowerCase() || "";
|
|
10178
10213
|
const mimeMap = {
|
|
10179
10214
|
pdf: "application/pdf",
|
|
@@ -10197,7 +10232,7 @@ ${p.text}` : p.text;
|
|
|
10197
10232
|
contentType,
|
|
10198
10233
|
"task-output"
|
|
10199
10234
|
);
|
|
10200
|
-
const fileData = await
|
|
10235
|
+
const fileData = await readFile13(fullPath);
|
|
10201
10236
|
await fetch(uploadInfo.uploadUrl, {
|
|
10202
10237
|
method: "PUT",
|
|
10203
10238
|
headers: { "Content-Type": contentType },
|
|
@@ -10246,8 +10281,8 @@ ${p.text}` : p.text;
|
|
|
10246
10281
|
this.pendingApprovals.set(toolCallId, await execution);
|
|
10247
10282
|
options.onApprovalRequired?.(await execution);
|
|
10248
10283
|
await sessionQueries.updateStatus(this.session.id, "waiting");
|
|
10249
|
-
const approved = await new Promise((
|
|
10250
|
-
approvalResolvers.set(toolCallId, { resolve:
|
|
10284
|
+
const approved = await new Promise((resolve13) => {
|
|
10285
|
+
approvalResolvers.set(toolCallId, { resolve: resolve13, sessionId: this.session.id });
|
|
10251
10286
|
});
|
|
10252
10287
|
const resolverData = approvalResolvers.get(toolCallId);
|
|
10253
10288
|
approvalResolvers.delete(toolCallId);
|
|
@@ -10353,8 +10388,8 @@ async function withSessionLock(sessionId, fn) {
|
|
|
10353
10388
|
state2.pending++;
|
|
10354
10389
|
const prev = state2.tail;
|
|
10355
10390
|
let release;
|
|
10356
|
-
const next = new Promise((
|
|
10357
|
-
release =
|
|
10391
|
+
const next = new Promise((resolve13) => {
|
|
10392
|
+
release = resolve13;
|
|
10358
10393
|
});
|
|
10359
10394
|
state2.tail = prev.then(() => next);
|
|
10360
10395
|
await prev;
|
|
@@ -10747,12 +10782,12 @@ init_agent();
|
|
|
10747
10782
|
|
|
10748
10783
|
// src/server/index.ts
|
|
10749
10784
|
import "dotenv/config";
|
|
10750
|
-
import { Hono as
|
|
10785
|
+
import { Hono as Hono10 } from "hono";
|
|
10751
10786
|
import { serve } from "@hono/node-server";
|
|
10752
10787
|
import { cors } from "hono/cors";
|
|
10753
10788
|
import { logger } from "hono/logger";
|
|
10754
|
-
import { existsSync as
|
|
10755
|
-
import { resolve as
|
|
10789
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync9, writeFileSync as writeFileSync6 } from "fs";
|
|
10790
|
+
import { resolve as resolve12, dirname as dirname9, join as join16 } from "path";
|
|
10756
10791
|
import { spawn as spawn2 } from "child_process";
|
|
10757
10792
|
import { createServer as createNetServer } from "net";
|
|
10758
10793
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
@@ -11927,7 +11962,7 @@ async function emitSyntheticToolStreaming(writeSSE, toolCallStarts, toolCallId,
|
|
|
11927
11962
|
toolCallId,
|
|
11928
11963
|
argsTextDelta: chunk
|
|
11929
11964
|
}));
|
|
11930
|
-
await new Promise((
|
|
11965
|
+
await new Promise((resolve13) => setTimeout(resolve13, 0));
|
|
11931
11966
|
}
|
|
11932
11967
|
}
|
|
11933
11968
|
function buildDevtoolsContextXml(sessionId) {
|
|
@@ -12182,7 +12217,7 @@ ${prompt}` });
|
|
|
12182
12217
|
chunkIndex,
|
|
12183
12218
|
chunkCount
|
|
12184
12219
|
}));
|
|
12185
|
-
await new Promise((
|
|
12220
|
+
await new Promise((resolve13) => setTimeout(resolve13, 0));
|
|
12186
12221
|
}
|
|
12187
12222
|
const browserPort = progress.data?.browserStreamPort;
|
|
12188
12223
|
const browserClosed = progress.data?.browserClosed;
|
|
@@ -12729,7 +12764,7 @@ agents.post(
|
|
|
12729
12764
|
chunkIndex,
|
|
12730
12765
|
chunkCount
|
|
12731
12766
|
}));
|
|
12732
|
-
await new Promise((
|
|
12767
|
+
await new Promise((resolve13) => setTimeout(resolve13, 0));
|
|
12733
12768
|
}
|
|
12734
12769
|
const browserPort = progress.data?.browserStreamPort;
|
|
12735
12770
|
const browserClosed = progress.data?.browserClosed;
|
|
@@ -14237,6 +14272,224 @@ mcpRouter.post("/:id/test", async (c) => {
|
|
|
14237
14272
|
return c.json(result);
|
|
14238
14273
|
});
|
|
14239
14274
|
|
|
14275
|
+
// src/server/routes/skills.ts
|
|
14276
|
+
init_config();
|
|
14277
|
+
init_skills();
|
|
14278
|
+
import { Hono as Hono9 } from "hono";
|
|
14279
|
+
import { zValidator as zValidator7 } from "@hono/zod-validator";
|
|
14280
|
+
import { z as z22 } from "zod";
|
|
14281
|
+
import { existsSync as existsSync20, statSync as statSync3 } from "fs";
|
|
14282
|
+
import { readFile as readFile12, writeFile as writeFile6, unlink as unlink3, mkdir as mkdir5 } from "fs/promises";
|
|
14283
|
+
import { resolve as resolve11, join as join15, basename as basename6, dirname as dirname8, extname as extname9 } from "path";
|
|
14284
|
+
var skills = new Hono9();
|
|
14285
|
+
function encodeId(filePath) {
|
|
14286
|
+
return Buffer.from(filePath, "utf-8").toString("base64url");
|
|
14287
|
+
}
|
|
14288
|
+
function decodeId(id) {
|
|
14289
|
+
try {
|
|
14290
|
+
const decoded = Buffer.from(id, "base64url").toString("utf-8");
|
|
14291
|
+
return decoded || null;
|
|
14292
|
+
} catch {
|
|
14293
|
+
return null;
|
|
14294
|
+
}
|
|
14295
|
+
}
|
|
14296
|
+
function listAllDirectories() {
|
|
14297
|
+
const cfg = getConfig();
|
|
14298
|
+
const discovered = discoverSkillDirectories(cfg.resolvedWorkingDirectory);
|
|
14299
|
+
const out = [];
|
|
14300
|
+
for (const { path, priority } of discovered.alwaysLoadedDirs) {
|
|
14301
|
+
out.push({
|
|
14302
|
+
path,
|
|
14303
|
+
source: pathToSource(path),
|
|
14304
|
+
label: pathToLabel(path),
|
|
14305
|
+
priority,
|
|
14306
|
+
alwaysApply: true
|
|
14307
|
+
});
|
|
14308
|
+
}
|
|
14309
|
+
for (const { path, priority } of discovered.onDemandDirs) {
|
|
14310
|
+
out.push({
|
|
14311
|
+
path,
|
|
14312
|
+
source: pathToSource(path),
|
|
14313
|
+
label: pathToLabel(path),
|
|
14314
|
+
priority,
|
|
14315
|
+
alwaysApply: false
|
|
14316
|
+
});
|
|
14317
|
+
}
|
|
14318
|
+
const additional = cfg.skills?.additionalDirectories || [];
|
|
14319
|
+
for (const dir of additional) {
|
|
14320
|
+
const abs = resolve11(cfg.resolvedWorkingDirectory, dir);
|
|
14321
|
+
if (out.some((d) => d.path === abs)) continue;
|
|
14322
|
+
out.push({
|
|
14323
|
+
path: abs,
|
|
14324
|
+
source: "additional",
|
|
14325
|
+
label: pathToLabel(abs),
|
|
14326
|
+
priority: 50,
|
|
14327
|
+
alwaysApply: false
|
|
14328
|
+
});
|
|
14329
|
+
}
|
|
14330
|
+
return out;
|
|
14331
|
+
}
|
|
14332
|
+
function pathToSource(path) {
|
|
14333
|
+
if (path.includes("/skills/default") || path.endsWith("/skills/default")) return "builtin";
|
|
14334
|
+
return "project";
|
|
14335
|
+
}
|
|
14336
|
+
function pathToLabel(path) {
|
|
14337
|
+
if (path.includes("/skills/default")) return "Built-in (default)";
|
|
14338
|
+
if (path.includes("/.sparkecoder/rules")) return ".sparkecoder/rules";
|
|
14339
|
+
if (path.includes("/.sparkecoder/skills")) return ".sparkecoder/skills";
|
|
14340
|
+
if (path.includes("/.cursor/rules")) return ".cursor/rules";
|
|
14341
|
+
if (path.includes("/.claude/skills")) return ".claude/skills";
|
|
14342
|
+
return basename6(dirname8(path)) + "/" + basename6(path);
|
|
14343
|
+
}
|
|
14344
|
+
skills.get("/", async (c) => {
|
|
14345
|
+
const dirs = listAllDirectories();
|
|
14346
|
+
const allSkills = [];
|
|
14347
|
+
for (const dir of dirs) {
|
|
14348
|
+
if (!existsSync20(dir.path)) continue;
|
|
14349
|
+
try {
|
|
14350
|
+
const list = await loadSkillsFromDirectory(dir.path, {
|
|
14351
|
+
priority: dir.priority,
|
|
14352
|
+
defaultLoadType: dir.alwaysApply ? "always" : "on_demand",
|
|
14353
|
+
forceAlwaysApply: dir.alwaysApply
|
|
14354
|
+
});
|
|
14355
|
+
for (const s of list) {
|
|
14356
|
+
let size = 0;
|
|
14357
|
+
try {
|
|
14358
|
+
size = statSync3(s.filePath).size;
|
|
14359
|
+
} catch {
|
|
14360
|
+
}
|
|
14361
|
+
allSkills.push({
|
|
14362
|
+
id: encodeId(s.filePath),
|
|
14363
|
+
name: s.name,
|
|
14364
|
+
description: s.description,
|
|
14365
|
+
filePath: s.filePath,
|
|
14366
|
+
fileName: basename6(s.filePath),
|
|
14367
|
+
sourceDir: dir.path,
|
|
14368
|
+
sourceLabel: dir.label,
|
|
14369
|
+
sourceType: dir.source,
|
|
14370
|
+
alwaysApply: s.alwaysApply,
|
|
14371
|
+
globs: s.globs,
|
|
14372
|
+
sizeBytes: size
|
|
14373
|
+
});
|
|
14374
|
+
}
|
|
14375
|
+
} catch (err) {
|
|
14376
|
+
console.warn("[skills] failed to read", dir.path, err?.message || err);
|
|
14377
|
+
}
|
|
14378
|
+
}
|
|
14379
|
+
return c.json({
|
|
14380
|
+
directories: dirs.map((d) => ({
|
|
14381
|
+
path: d.path,
|
|
14382
|
+
label: d.label,
|
|
14383
|
+
source: d.source,
|
|
14384
|
+
alwaysApply: d.alwaysApply,
|
|
14385
|
+
exists: existsSync20(d.path),
|
|
14386
|
+
writable: isWritable(d.path)
|
|
14387
|
+
})),
|
|
14388
|
+
skills: allSkills
|
|
14389
|
+
});
|
|
14390
|
+
});
|
|
14391
|
+
function isWritable(dir) {
|
|
14392
|
+
try {
|
|
14393
|
+
if (!existsSync20(dir)) return false;
|
|
14394
|
+
if (dir.includes("/skills/default")) return false;
|
|
14395
|
+
return true;
|
|
14396
|
+
} catch {
|
|
14397
|
+
return false;
|
|
14398
|
+
}
|
|
14399
|
+
}
|
|
14400
|
+
skills.get("/:id", async (c) => {
|
|
14401
|
+
const filePath = decodeId(c.req.param("id"));
|
|
14402
|
+
if (!filePath || !existsSync20(filePath)) {
|
|
14403
|
+
return c.json({ error: "skill not found" }, 404);
|
|
14404
|
+
}
|
|
14405
|
+
const content = await readFile12(filePath, "utf-8");
|
|
14406
|
+
return c.json({
|
|
14407
|
+
id: c.req.param("id"),
|
|
14408
|
+
filePath,
|
|
14409
|
+
fileName: basename6(filePath),
|
|
14410
|
+
content,
|
|
14411
|
+
writable: !filePath.includes("/skills/default")
|
|
14412
|
+
});
|
|
14413
|
+
});
|
|
14414
|
+
skills.post(
|
|
14415
|
+
"/",
|
|
14416
|
+
zValidator7("json", z22.object({
|
|
14417
|
+
directory: z22.string().min(1),
|
|
14418
|
+
fileName: z22.string().min(1),
|
|
14419
|
+
content: z22.string()
|
|
14420
|
+
})),
|
|
14421
|
+
async (c) => {
|
|
14422
|
+
const { directory, fileName, content } = c.req.valid("json");
|
|
14423
|
+
const cfg = getConfig();
|
|
14424
|
+
const targetDir = resolve11(cfg.resolvedWorkingDirectory, directory);
|
|
14425
|
+
if (targetDir.includes("/skills/default")) {
|
|
14426
|
+
return c.json({ error: "cannot write into built-in skills directory" }, 400);
|
|
14427
|
+
}
|
|
14428
|
+
const safeName = basename6(fileName).replace(/[^A-Za-z0-9._-]/g, "-");
|
|
14429
|
+
const ext = extname9(safeName).toLowerCase();
|
|
14430
|
+
const finalName = ext === ".md" || ext === ".mdc" ? safeName : `${safeName}.md`;
|
|
14431
|
+
const filePath = join15(targetDir, finalName);
|
|
14432
|
+
if (existsSync20(filePath)) {
|
|
14433
|
+
return c.json({ error: `file already exists: ${finalName}` }, 409);
|
|
14434
|
+
}
|
|
14435
|
+
try {
|
|
14436
|
+
await mkdir5(targetDir, { recursive: true });
|
|
14437
|
+
await writeFile6(filePath, content, "utf-8");
|
|
14438
|
+
} catch (err) {
|
|
14439
|
+
return c.json({ error: err?.message || "write failed" }, 500);
|
|
14440
|
+
}
|
|
14441
|
+
return c.json({ id: encodeId(filePath), filePath, fileName: finalName }, 201);
|
|
14442
|
+
}
|
|
14443
|
+
);
|
|
14444
|
+
skills.put(
|
|
14445
|
+
"/:id",
|
|
14446
|
+
zValidator7("json", z22.object({ content: z22.string() })),
|
|
14447
|
+
async (c) => {
|
|
14448
|
+
const filePath = decodeId(c.req.param("id"));
|
|
14449
|
+
if (!filePath || !existsSync20(filePath)) return c.json({ error: "skill not found" }, 404);
|
|
14450
|
+
if (filePath.includes("/skills/default")) {
|
|
14451
|
+
return c.json({ error: "built-in skills are read-only" }, 400);
|
|
14452
|
+
}
|
|
14453
|
+
await writeFile6(filePath, c.req.valid("json").content, "utf-8");
|
|
14454
|
+
return c.json({ ok: true });
|
|
14455
|
+
}
|
|
14456
|
+
);
|
|
14457
|
+
skills.delete("/:id", async (c) => {
|
|
14458
|
+
const filePath = decodeId(c.req.param("id"));
|
|
14459
|
+
if (!filePath || !existsSync20(filePath)) return c.json({ error: "skill not found" }, 404);
|
|
14460
|
+
if (filePath.includes("/skills/default")) {
|
|
14461
|
+
return c.json({ error: "built-in skills are read-only" }, 400);
|
|
14462
|
+
}
|
|
14463
|
+
await unlink3(filePath);
|
|
14464
|
+
return c.json({ ok: true });
|
|
14465
|
+
});
|
|
14466
|
+
skills.post(
|
|
14467
|
+
"/directories",
|
|
14468
|
+
zValidator7("json", z22.object({ path: z22.string().min(1) })),
|
|
14469
|
+
(c) => {
|
|
14470
|
+
const cfg = getConfig();
|
|
14471
|
+
const inputPath = c.req.valid("json").path.trim();
|
|
14472
|
+
const abs = resolve11(cfg.resolvedWorkingDirectory, inputPath);
|
|
14473
|
+
const current = cfg.skills?.additionalDirectories || [];
|
|
14474
|
+
if (current.some((d) => resolve11(cfg.resolvedWorkingDirectory, d) === abs)) {
|
|
14475
|
+
return c.json({ error: "directory already added" }, 409);
|
|
14476
|
+
}
|
|
14477
|
+
const next = [...current, abs];
|
|
14478
|
+
setSkillsAdditionalDirectories(next);
|
|
14479
|
+
return c.json({ ok: true, path: abs, exists: existsSync20(abs) }, 201);
|
|
14480
|
+
}
|
|
14481
|
+
);
|
|
14482
|
+
skills.delete("/directories", (c) => {
|
|
14483
|
+
const inputPath = c.req.query("path");
|
|
14484
|
+
if (!inputPath) return c.json({ error: "path query param required" }, 400);
|
|
14485
|
+
const cfg = getConfig();
|
|
14486
|
+
const abs = resolve11(cfg.resolvedWorkingDirectory, inputPath);
|
|
14487
|
+
const current = cfg.skills?.additionalDirectories || [];
|
|
14488
|
+
const next = current.filter((d) => resolve11(cfg.resolvedWorkingDirectory, d) !== abs);
|
|
14489
|
+
setSkillsAdditionalDirectories(next);
|
|
14490
|
+
return c.json({ ok: true });
|
|
14491
|
+
});
|
|
14492
|
+
|
|
14240
14493
|
// src/server/auth/cf-access.ts
|
|
14241
14494
|
init_config();
|
|
14242
14495
|
import { createRemoteJWKSet, jwtVerify } from "jose";
|
|
@@ -14397,13 +14650,13 @@ var DEFAULT_WEB_PORT = 6969;
|
|
|
14397
14650
|
var WEB_PORT_SEQUENCE = [6969, 6970, 6971, 6972, 6973, 6974, 6975, 6976, 6977, 6978];
|
|
14398
14651
|
function getWebDirectory() {
|
|
14399
14652
|
try {
|
|
14400
|
-
const currentDir =
|
|
14401
|
-
const webDir =
|
|
14402
|
-
if (
|
|
14653
|
+
const currentDir = dirname9(fileURLToPath4(import.meta.url));
|
|
14654
|
+
const webDir = resolve12(currentDir, "..", "web");
|
|
14655
|
+
if (existsSync21(webDir) && existsSync21(join16(webDir, "package.json"))) {
|
|
14403
14656
|
return webDir;
|
|
14404
14657
|
}
|
|
14405
|
-
const altWebDir =
|
|
14406
|
-
if (
|
|
14658
|
+
const altWebDir = resolve12(currentDir, "..", "..", "web");
|
|
14659
|
+
if (existsSync21(altWebDir) && existsSync21(join16(altWebDir, "package.json"))) {
|
|
14407
14660
|
return altWebDir;
|
|
14408
14661
|
}
|
|
14409
14662
|
return null;
|
|
@@ -14426,18 +14679,18 @@ async function isSparkcoderWebRunning(port) {
|
|
|
14426
14679
|
}
|
|
14427
14680
|
}
|
|
14428
14681
|
function isPortInUse(port) {
|
|
14429
|
-
return new Promise((
|
|
14682
|
+
return new Promise((resolve13) => {
|
|
14430
14683
|
const server = createNetServer();
|
|
14431
14684
|
server.once("error", (err) => {
|
|
14432
14685
|
if (err.code === "EADDRINUSE") {
|
|
14433
|
-
|
|
14686
|
+
resolve13(true);
|
|
14434
14687
|
} else {
|
|
14435
|
-
|
|
14688
|
+
resolve13(false);
|
|
14436
14689
|
}
|
|
14437
14690
|
});
|
|
14438
14691
|
server.once("listening", () => {
|
|
14439
14692
|
server.close();
|
|
14440
|
-
|
|
14693
|
+
resolve13(false);
|
|
14441
14694
|
});
|
|
14442
14695
|
server.listen(port, "0.0.0.0");
|
|
14443
14696
|
});
|
|
@@ -14461,30 +14714,30 @@ async function findWebPort(preferredPort) {
|
|
|
14461
14714
|
return { port: preferredPort, alreadyRunning: false };
|
|
14462
14715
|
}
|
|
14463
14716
|
function hasProductionBuild(webDir) {
|
|
14464
|
-
const buildIdPath =
|
|
14465
|
-
return
|
|
14717
|
+
const buildIdPath = join16(webDir, ".next", "BUILD_ID");
|
|
14718
|
+
return existsSync21(buildIdPath);
|
|
14466
14719
|
}
|
|
14467
14720
|
function hasSourceFiles(webDir) {
|
|
14468
|
-
const appDir =
|
|
14469
|
-
const pagesDir =
|
|
14470
|
-
const rootAppDir =
|
|
14471
|
-
const rootPagesDir =
|
|
14472
|
-
return
|
|
14721
|
+
const appDir = join16(webDir, "src", "app");
|
|
14722
|
+
const pagesDir = join16(webDir, "src", "pages");
|
|
14723
|
+
const rootAppDir = join16(webDir, "app");
|
|
14724
|
+
const rootPagesDir = join16(webDir, "pages");
|
|
14725
|
+
return existsSync21(appDir) || existsSync21(pagesDir) || existsSync21(rootAppDir) || existsSync21(rootPagesDir);
|
|
14473
14726
|
}
|
|
14474
14727
|
function getStandaloneServerPath(webDir) {
|
|
14475
14728
|
const possiblePaths2 = [
|
|
14476
|
-
|
|
14477
|
-
|
|
14729
|
+
join16(webDir, ".next", "standalone", "server.js"),
|
|
14730
|
+
join16(webDir, ".next", "standalone", "web", "server.js")
|
|
14478
14731
|
];
|
|
14479
14732
|
for (const serverPath of possiblePaths2) {
|
|
14480
|
-
if (
|
|
14733
|
+
if (existsSync21(serverPath)) {
|
|
14481
14734
|
return serverPath;
|
|
14482
14735
|
}
|
|
14483
14736
|
}
|
|
14484
14737
|
return null;
|
|
14485
14738
|
}
|
|
14486
14739
|
function runCommand(command, args, cwd, env) {
|
|
14487
|
-
return new Promise((
|
|
14740
|
+
return new Promise((resolve13) => {
|
|
14488
14741
|
const child = spawn2(command, args, {
|
|
14489
14742
|
cwd,
|
|
14490
14743
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -14499,10 +14752,10 @@ function runCommand(command, args, cwd, env) {
|
|
|
14499
14752
|
output += data.toString();
|
|
14500
14753
|
});
|
|
14501
14754
|
child.on("close", (code) => {
|
|
14502
|
-
|
|
14755
|
+
resolve13({ success: code === 0, output });
|
|
14503
14756
|
});
|
|
14504
14757
|
child.on("error", (err) => {
|
|
14505
|
-
|
|
14758
|
+
resolve13({ success: false, output: err.message });
|
|
14506
14759
|
});
|
|
14507
14760
|
});
|
|
14508
14761
|
}
|
|
@@ -14517,13 +14770,13 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
14517
14770
|
if (!quiet) console.log(` \u2713 Web UI already running at http://localhost:${actualPort}`);
|
|
14518
14771
|
return { process: null, port: actualPort };
|
|
14519
14772
|
}
|
|
14520
|
-
const usePnpm =
|
|
14521
|
-
const useNpm = !usePnpm &&
|
|
14773
|
+
const usePnpm = existsSync21(join16(webDir, "pnpm-lock.yaml"));
|
|
14774
|
+
const useNpm = !usePnpm && existsSync21(join16(webDir, "package-lock.json"));
|
|
14522
14775
|
const pkgManager = usePnpm ? "pnpm" : useNpm ? "npm" : "npx";
|
|
14523
14776
|
const { NODE_OPTIONS, TSX_TSCONFIG_PATH, ...cleanEnv } = process.env;
|
|
14524
14777
|
const apiUrl = publicUrl || `http://127.0.0.1:${apiPort}`;
|
|
14525
14778
|
const runtimeConfig = { apiBaseUrl: apiUrl };
|
|
14526
|
-
const runtimeConfigPath =
|
|
14779
|
+
const runtimeConfigPath = join16(webDir, "runtime-config.json");
|
|
14527
14780
|
try {
|
|
14528
14781
|
writeFileSync6(runtimeConfigPath, JSON.stringify(runtimeConfig, null, 2));
|
|
14529
14782
|
if (!quiet) console.log(` \u{1F4DD} Runtime config written to ${runtimeConfigPath}`);
|
|
@@ -14545,7 +14798,7 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
14545
14798
|
if (standaloneServerPath) {
|
|
14546
14799
|
command = "node";
|
|
14547
14800
|
args = ["server.js"];
|
|
14548
|
-
cwd =
|
|
14801
|
+
cwd = dirname9(standaloneServerPath);
|
|
14549
14802
|
webEnv.PORT = String(actualPort);
|
|
14550
14803
|
webEnv.HOSTNAME = "0.0.0.0";
|
|
14551
14804
|
if (!quiet) console.log(" \u{1F4E6} Starting Web UI from standalone build...");
|
|
@@ -14586,10 +14839,10 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
14586
14839
|
let started = false;
|
|
14587
14840
|
let exited = false;
|
|
14588
14841
|
let exitCode = null;
|
|
14589
|
-
const startedPromise = new Promise((
|
|
14842
|
+
const startedPromise = new Promise((resolve13) => {
|
|
14590
14843
|
const timeout = setTimeout(() => {
|
|
14591
14844
|
if (!started && !exited) {
|
|
14592
|
-
|
|
14845
|
+
resolve13(false);
|
|
14593
14846
|
}
|
|
14594
14847
|
}, startupTimeout);
|
|
14595
14848
|
child.stdout?.on("data", (data) => {
|
|
@@ -14603,7 +14856,7 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
14603
14856
|
if (!started && (output.includes("Ready") || output.includes("started") || output.includes("localhost"))) {
|
|
14604
14857
|
started = true;
|
|
14605
14858
|
clearTimeout(timeout);
|
|
14606
|
-
|
|
14859
|
+
resolve13(true);
|
|
14607
14860
|
}
|
|
14608
14861
|
});
|
|
14609
14862
|
child.stderr?.on("data", (data) => {
|
|
@@ -14615,14 +14868,14 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
14615
14868
|
child.on("error", (err) => {
|
|
14616
14869
|
if (!quiet) console.error(` \u274C Web UI spawn error: ${err.message}`);
|
|
14617
14870
|
clearTimeout(timeout);
|
|
14618
|
-
|
|
14871
|
+
resolve13(false);
|
|
14619
14872
|
});
|
|
14620
14873
|
child.on("exit", (code) => {
|
|
14621
14874
|
exited = true;
|
|
14622
14875
|
exitCode = code;
|
|
14623
14876
|
if (!started) {
|
|
14624
14877
|
clearTimeout(timeout);
|
|
14625
|
-
|
|
14878
|
+
resolve13(false);
|
|
14626
14879
|
}
|
|
14627
14880
|
webUIProcess = null;
|
|
14628
14881
|
});
|
|
@@ -14645,7 +14898,7 @@ function stopWebUI() {
|
|
|
14645
14898
|
}
|
|
14646
14899
|
}
|
|
14647
14900
|
async function createApp(options = {}) {
|
|
14648
|
-
const app = new
|
|
14901
|
+
const app = new Hono10();
|
|
14649
14902
|
app.use("*", cors({
|
|
14650
14903
|
origin: "*",
|
|
14651
14904
|
// Allow all origins
|
|
@@ -14673,6 +14926,7 @@ async function createApp(options = {}) {
|
|
|
14673
14926
|
app.route("/api/schedules", schedulesRouter);
|
|
14674
14927
|
app.route("/api/orchestrator", orchestratorRouter);
|
|
14675
14928
|
app.route("/api/mcp", mcpRouter);
|
|
14929
|
+
app.route("/api/skills", skills);
|
|
14676
14930
|
app.route("/api/webhooks", webhooksRouter);
|
|
14677
14931
|
const config = getConfig();
|
|
14678
14932
|
const webhookToken = config?.webhooks?.token;
|
|
@@ -14738,7 +14992,7 @@ async function startServer(options = {}) {
|
|
|
14738
14992
|
if (options.workingDirectory) {
|
|
14739
14993
|
config.resolvedWorkingDirectory = options.workingDirectory;
|
|
14740
14994
|
}
|
|
14741
|
-
if (!
|
|
14995
|
+
if (!existsSync21(config.resolvedWorkingDirectory)) {
|
|
14742
14996
|
mkdirSync9(config.resolvedWorkingDirectory, { recursive: true });
|
|
14743
14997
|
if (!options.quiet) console.log(`\u{1F4C1} Created agent workspace: ${config.resolvedWorkingDirectory}`);
|
|
14744
14998
|
}
|