sparkecoder 0.1.119 → 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.d.ts +3 -3
- package/dist/agent/index.js +2 -0
- package/dist/agent/index.js.map +1 -1
- package/dist/cli.js +470 -135
- package/dist/cli.js.map +1 -1
- package/dist/db/index.d.ts +2 -2
- package/dist/{index-Bcz0aCAR.d.ts → index-DczYH89U.d.ts} +104 -104
- package/dist/index.d.ts +5 -5
- package/dist/index.js +429 -94
- package/dist/index.js.map +1 -1
- package/dist/{schema-BWbWmfDQ.d.ts → schema-DxrKyetI.d.ts} +3 -3
- package/dist/{search-DOzC4ojH.d.ts → search-CVVfuBPZ.d.ts} +4 -4
- package/dist/server/index.js +429 -94
- package/dist/server/index.js.map +1 -1
- package/dist/tools/index.d.ts +3 -3
- 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/{Bt00m8W4k5F79ALhN700F → static/wP9z41wtqT4k-O6AlEXqw}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{Bt00m8W4k5F79ALhN700F → static/wP9z41wtqT4k-O6AlEXqw}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{Bt00m8W4k5F79ALhN700F → static/wP9z41wtqT4k-O6AlEXqw}/_ssgManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{static/Bt00m8W4k5F79ALhN700F → wP9z41wtqT4k-O6AlEXqw}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{static/Bt00m8W4k5F79ALhN700F → wP9z41wtqT4k-O6AlEXqw}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{static/Bt00m8W4k5F79ALhN700F → wP9z41wtqT4k-O6AlEXqw}/_ssgManifest.js +0 -0
- /package/web/.next/static/{Bt00m8W4k5F79ALhN700F → wP9z41wtqT4k-O6AlEXqw}/_buildManifest.js +0 -0
- /package/web/.next/static/{Bt00m8W4k5F79ALhN700F → wP9z41wtqT4k-O6AlEXqw}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{Bt00m8W4k5F79ALhN700F → 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);
|
|
@@ -7819,6 +7854,9 @@ function isSlackConfigured() {
|
|
|
7819
7854
|
function getSlackSigningSecret() {
|
|
7820
7855
|
return readSlackConfig()?.signingSecret ?? null;
|
|
7821
7856
|
}
|
|
7857
|
+
function getSlackBotToken() {
|
|
7858
|
+
return readSlackConfig()?.botToken ?? null;
|
|
7859
|
+
}
|
|
7822
7860
|
function getDefaultOrchestratorName() {
|
|
7823
7861
|
return readSlackConfig()?.defaultOrchestratorName ?? null;
|
|
7824
7862
|
}
|
|
@@ -7873,6 +7911,62 @@ function getSlackAllowlistPolicy() {
|
|
|
7873
7911
|
return { allowedUsers: [], allowedChannels: [], allowDmsFromAnyone: true };
|
|
7874
7912
|
}
|
|
7875
7913
|
}
|
|
7914
|
+
async function fetchSlackUserName(userId) {
|
|
7915
|
+
const token = getSlackBotToken();
|
|
7916
|
+
if (!token) return null;
|
|
7917
|
+
try {
|
|
7918
|
+
const res = await fetch(`https://slack.com/api/users.info?user=${encodeURIComponent(userId)}`, {
|
|
7919
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
7920
|
+
});
|
|
7921
|
+
const data = await res.json().catch(() => ({}));
|
|
7922
|
+
if (!data?.ok) {
|
|
7923
|
+
console.warn(`[slack] users.info(${userId}) failed: ${data?.error || `HTTP ${res.status}`}`);
|
|
7924
|
+
return null;
|
|
7925
|
+
}
|
|
7926
|
+
const profile = data.user?.profile || {};
|
|
7927
|
+
const name = profile.display_name_normalized || profile.display_name || profile.real_name_normalized || profile.real_name || data.user?.real_name || data.user?.name || null;
|
|
7928
|
+
return name ? String(name) : null;
|
|
7929
|
+
} catch (err) {
|
|
7930
|
+
console.warn(`[slack] users.info(${userId}) error:`, err?.message || err);
|
|
7931
|
+
return null;
|
|
7932
|
+
}
|
|
7933
|
+
}
|
|
7934
|
+
async function resolveSlackUserName(userId) {
|
|
7935
|
+
if (!userId) return null;
|
|
7936
|
+
const now = Date.now();
|
|
7937
|
+
const hit = userNameCache.get(userId);
|
|
7938
|
+
if (hit && hit.expiresAt > now) return hit.name;
|
|
7939
|
+
const inflight = userInflight.get(userId);
|
|
7940
|
+
if (inflight) return inflight;
|
|
7941
|
+
const p = (async () => {
|
|
7942
|
+
const name = await fetchSlackUserName(userId);
|
|
7943
|
+
userNameCache.set(userId, {
|
|
7944
|
+
name,
|
|
7945
|
+
expiresAt: now + (name ? USER_TTL_MS : USER_FAIL_TTL_MS)
|
|
7946
|
+
});
|
|
7947
|
+
userInflight.delete(userId);
|
|
7948
|
+
return name;
|
|
7949
|
+
})();
|
|
7950
|
+
userInflight.set(userId, p);
|
|
7951
|
+
return p;
|
|
7952
|
+
}
|
|
7953
|
+
async function normalizeSlackMentions(text) {
|
|
7954
|
+
if (!text) return text;
|
|
7955
|
+
const userMentionRe = /<@([UW][A-Z0-9]+)(?:\|([^>]+))?>/g;
|
|
7956
|
+
const userIds = /* @__PURE__ */ new Set();
|
|
7957
|
+
for (const m of text.matchAll(userMentionRe)) {
|
|
7958
|
+
if (!m[2]) userIds.add(m[1]);
|
|
7959
|
+
}
|
|
7960
|
+
if (userIds.size > 0) {
|
|
7961
|
+
await Promise.all([...userIds].map((id) => resolveSlackUserName(id)));
|
|
7962
|
+
}
|
|
7963
|
+
return text.replace(userMentionRe, (_full, id, label) => {
|
|
7964
|
+
if (label) return `${label} <@${id}>`;
|
|
7965
|
+
const cached = userNameCache.get(id);
|
|
7966
|
+
const name = cached?.name;
|
|
7967
|
+
return name ? `${name} <@${id}>` : `<@${id}>`;
|
|
7968
|
+
});
|
|
7969
|
+
}
|
|
7876
7970
|
function getSlackDeniedReplyPolicy() {
|
|
7877
7971
|
try {
|
|
7878
7972
|
const cfg = getConfig();
|
|
@@ -7885,13 +7979,17 @@ function getSlackDeniedReplyPolicy() {
|
|
|
7885
7979
|
return { enabled: true, template: DEFAULT_DENIED_TEMPLATE };
|
|
7886
7980
|
}
|
|
7887
7981
|
}
|
|
7888
|
-
var cachedSelf, selfInflight, DEFAULT_DENIED_TEMPLATE;
|
|
7982
|
+
var cachedSelf, selfInflight, USER_TTL_MS, USER_FAIL_TTL_MS, userNameCache, userInflight, DEFAULT_DENIED_TEMPLATE;
|
|
7889
7983
|
var init_client3 = __esm({
|
|
7890
7984
|
"src/integrations/slack/client.ts"() {
|
|
7891
7985
|
"use strict";
|
|
7892
7986
|
init_config();
|
|
7893
7987
|
cachedSelf = null;
|
|
7894
7988
|
selfInflight = null;
|
|
7989
|
+
USER_TTL_MS = 60 * 60 * 1e3;
|
|
7990
|
+
USER_FAIL_TTL_MS = 5 * 60 * 1e3;
|
|
7991
|
+
userNameCache = /* @__PURE__ */ new Map();
|
|
7992
|
+
userInflight = /* @__PURE__ */ new Map();
|
|
7895
7993
|
DEFAULT_DENIED_TEMPLATE = "Sorry, you don't have permission to use this bot. (Contact the bot owner if you think this is a mistake.)";
|
|
7896
7994
|
}
|
|
7897
7995
|
});
|
|
@@ -7906,9 +8004,6 @@ function markThreadOwned(channel, threadTs) {
|
|
|
7906
8004
|
function isThreadOwned(channel, threadTs) {
|
|
7907
8005
|
return ownedThreads.has(threadKey(channel, threadTs));
|
|
7908
8006
|
}
|
|
7909
|
-
function stripMention(text) {
|
|
7910
|
-
return String(text || "").replace(/<@[^>]+>/g, "").trim();
|
|
7911
|
-
}
|
|
7912
8007
|
function isSelfAuthored(event, self) {
|
|
7913
8008
|
if (!self) return true;
|
|
7914
8009
|
if (self.botId && event.bot_id && event.bot_id === self.botId) return true;
|
|
@@ -7923,14 +8018,22 @@ function slackEventToInboundResult(event, opts = {}) {
|
|
|
7923
8018
|
return { event: null, dropReason: "bot_message" };
|
|
7924
8019
|
}
|
|
7925
8020
|
if (event.type === "message" && event.subtype && IGNORED_MESSAGE_SUBTYPES.has(event.subtype)) {
|
|
7926
|
-
return { event: null, dropReason: "
|
|
8021
|
+
return { event: null, dropReason: "ignored_subtype" };
|
|
7927
8022
|
}
|
|
7928
8023
|
const isDm = event.type === "message" && event.channel_type === "im";
|
|
7929
8024
|
const isThreadReply = event.type === "message" && !isDm && typeof event.thread_ts === "string" && event.thread_ts !== event.ts;
|
|
8025
|
+
const isNonThreadChannelMsg = event.type === "message" && !isDm && !isThreadReply && (event.channel_type === "channel" || event.channel_type === "group" || event.channel_type === "mpim" || // Some payload shapes omit channel_type for channel messages.
|
|
8026
|
+
typeof event.channel === "string");
|
|
7930
8027
|
if (event.type !== "app_mention" && !isDm && !isThreadReply) {
|
|
8028
|
+
if (isNonThreadChannelMsg) {
|
|
8029
|
+
return { event: null, dropReason: "non_thread_channel_msg" };
|
|
8030
|
+
}
|
|
8031
|
+
if (event.type !== "message") {
|
|
8032
|
+
return { event: null, dropReason: "non_message_event" };
|
|
8033
|
+
}
|
|
7931
8034
|
return { event: null, dropReason: "unsupported_type" };
|
|
7932
8035
|
}
|
|
7933
|
-
const text =
|
|
8036
|
+
const text = (event.text ?? "").trim();
|
|
7934
8037
|
if (!text) return { event: null, dropReason: "empty_text" };
|
|
7935
8038
|
const policy = getSlackAllowlistPolicy();
|
|
7936
8039
|
const userAllowlistActive = policy.allowedUsers.length > 0;
|
|
@@ -8913,11 +9016,11 @@ function waitForTaskQuestionAnswer(question) {
|
|
|
8913
9016
|
if (pendingQuestions.has(k)) {
|
|
8914
9017
|
return Promise.reject(new Error(`Question already pending: ${question.questionId}`));
|
|
8915
9018
|
}
|
|
8916
|
-
return new Promise((
|
|
9019
|
+
return new Promise((resolve13, reject) => {
|
|
8917
9020
|
pendingQuestions.set(k, {
|
|
8918
9021
|
...question,
|
|
8919
9022
|
createdAt: /* @__PURE__ */ new Date(),
|
|
8920
|
-
resolve:
|
|
9023
|
+
resolve: resolve13,
|
|
8921
9024
|
reject
|
|
8922
9025
|
});
|
|
8923
9026
|
});
|
|
@@ -10066,14 +10169,14 @@ ${p.text}` : p.text;
|
|
|
10066
10169
|
const result = await recorder.encode();
|
|
10067
10170
|
recorder.clear();
|
|
10068
10171
|
if (!result) return [];
|
|
10069
|
-
const { readFile:
|
|
10172
|
+
const { readFile: readFile13, unlink: unlink4 } = await import("fs/promises");
|
|
10070
10173
|
const uploadInfo = await storageQueries2.getUploadUrl(
|
|
10071
10174
|
this.session.id,
|
|
10072
10175
|
`browser-recording-${Date.now()}.mp4`,
|
|
10073
10176
|
"video/mp4",
|
|
10074
10177
|
"browser-recording"
|
|
10075
10178
|
);
|
|
10076
|
-
const fileData = await
|
|
10179
|
+
const fileData = await readFile13(result.path);
|
|
10077
10180
|
await fetch(uploadInfo.uploadUrl, {
|
|
10078
10181
|
method: "PUT",
|
|
10079
10182
|
headers: { "Content-Type": "video/mp4" },
|
|
@@ -10081,7 +10184,7 @@ ${p.text}` : p.text;
|
|
|
10081
10184
|
});
|
|
10082
10185
|
await storageQueries2.updateFile(uploadInfo.fileId, { sizeBytes: result.sizeBytes });
|
|
10083
10186
|
const dlInfo = await storageQueries2.getDownloadUrl(uploadInfo.fileId);
|
|
10084
|
-
await
|
|
10187
|
+
await unlink4(result.path).catch(() => {
|
|
10085
10188
|
});
|
|
10086
10189
|
console.log(`[TASK] Browser recording uploaded (${result.sizeBytes} bytes)`);
|
|
10087
10190
|
return [dlInfo.downloadUrl];
|
|
@@ -10099,13 +10202,13 @@ ${p.text}` : p.text;
|
|
|
10099
10202
|
try {
|
|
10100
10203
|
const { isRemoteConfigured: isRemoteConfigured2, storageQueries: storageQueries2 } = await Promise.resolve().then(() => (init_remote(), remote_exports));
|
|
10101
10204
|
if (!isRemoteConfigured2()) return [];
|
|
10102
|
-
const { readFile:
|
|
10103
|
-
const { join:
|
|
10205
|
+
const { readFile: readFile13 } = await import("fs/promises");
|
|
10206
|
+
const { join: join17, basename: basename7 } = await import("path");
|
|
10104
10207
|
const urls = [];
|
|
10105
10208
|
for (const filePath of filePaths) {
|
|
10106
10209
|
try {
|
|
10107
|
-
const fullPath = filePath.startsWith("/") ? filePath :
|
|
10108
|
-
const fileName =
|
|
10210
|
+
const fullPath = filePath.startsWith("/") ? filePath : join17(this.session.workingDirectory, filePath);
|
|
10211
|
+
const fileName = basename7(fullPath);
|
|
10109
10212
|
const ext = fileName.split(".").pop()?.toLowerCase() || "";
|
|
10110
10213
|
const mimeMap = {
|
|
10111
10214
|
pdf: "application/pdf",
|
|
@@ -10129,7 +10232,7 @@ ${p.text}` : p.text;
|
|
|
10129
10232
|
contentType,
|
|
10130
10233
|
"task-output"
|
|
10131
10234
|
);
|
|
10132
|
-
const fileData = await
|
|
10235
|
+
const fileData = await readFile13(fullPath);
|
|
10133
10236
|
await fetch(uploadInfo.uploadUrl, {
|
|
10134
10237
|
method: "PUT",
|
|
10135
10238
|
headers: { "Content-Type": contentType },
|
|
@@ -10178,8 +10281,8 @@ ${p.text}` : p.text;
|
|
|
10178
10281
|
this.pendingApprovals.set(toolCallId, await execution);
|
|
10179
10282
|
options.onApprovalRequired?.(await execution);
|
|
10180
10283
|
await sessionQueries.updateStatus(this.session.id, "waiting");
|
|
10181
|
-
const approved = await new Promise((
|
|
10182
|
-
approvalResolvers.set(toolCallId, { resolve:
|
|
10284
|
+
const approved = await new Promise((resolve13) => {
|
|
10285
|
+
approvalResolvers.set(toolCallId, { resolve: resolve13, sessionId: this.session.id });
|
|
10183
10286
|
});
|
|
10184
10287
|
const resolverData = approvalResolvers.get(toolCallId);
|
|
10185
10288
|
approvalResolvers.delete(toolCallId);
|
|
@@ -10285,8 +10388,8 @@ async function withSessionLock(sessionId, fn) {
|
|
|
10285
10388
|
state2.pending++;
|
|
10286
10389
|
const prev = state2.tail;
|
|
10287
10390
|
let release;
|
|
10288
|
-
const next = new Promise((
|
|
10289
|
-
release =
|
|
10391
|
+
const next = new Promise((resolve13) => {
|
|
10392
|
+
release = resolve13;
|
|
10290
10393
|
});
|
|
10291
10394
|
state2.tail = prev.then(() => next);
|
|
10292
10395
|
await prev;
|
|
@@ -10679,12 +10782,12 @@ init_agent();
|
|
|
10679
10782
|
|
|
10680
10783
|
// src/server/index.ts
|
|
10681
10784
|
import "dotenv/config";
|
|
10682
|
-
import { Hono as
|
|
10785
|
+
import { Hono as Hono10 } from "hono";
|
|
10683
10786
|
import { serve } from "@hono/node-server";
|
|
10684
10787
|
import { cors } from "hono/cors";
|
|
10685
10788
|
import { logger } from "hono/logger";
|
|
10686
|
-
import { existsSync as
|
|
10687
|
-
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";
|
|
10688
10791
|
import { spawn as spawn2 } from "child_process";
|
|
10689
10792
|
import { createServer as createNetServer } from "net";
|
|
10690
10793
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
@@ -11859,7 +11962,7 @@ async function emitSyntheticToolStreaming(writeSSE, toolCallStarts, toolCallId,
|
|
|
11859
11962
|
toolCallId,
|
|
11860
11963
|
argsTextDelta: chunk
|
|
11861
11964
|
}));
|
|
11862
|
-
await new Promise((
|
|
11965
|
+
await new Promise((resolve13) => setTimeout(resolve13, 0));
|
|
11863
11966
|
}
|
|
11864
11967
|
}
|
|
11865
11968
|
function buildDevtoolsContextXml(sessionId) {
|
|
@@ -12114,7 +12217,7 @@ ${prompt}` });
|
|
|
12114
12217
|
chunkIndex,
|
|
12115
12218
|
chunkCount
|
|
12116
12219
|
}));
|
|
12117
|
-
await new Promise((
|
|
12220
|
+
await new Promise((resolve13) => setTimeout(resolve13, 0));
|
|
12118
12221
|
}
|
|
12119
12222
|
const browserPort = progress.data?.browserStreamPort;
|
|
12120
12223
|
const browserClosed = progress.data?.browserClosed;
|
|
@@ -12661,7 +12764,7 @@ agents.post(
|
|
|
12661
12764
|
chunkIndex,
|
|
12662
12765
|
chunkCount
|
|
12663
12766
|
}));
|
|
12664
|
-
await new Promise((
|
|
12767
|
+
await new Promise((resolve13) => setTimeout(resolve13, 0));
|
|
12665
12768
|
}
|
|
12666
12769
|
const browserPort = progress.data?.browserStreamPort;
|
|
12667
12770
|
const browserClosed = progress.data?.browserClosed;
|
|
@@ -13674,16 +13777,18 @@ init_webhook_events();
|
|
|
13674
13777
|
init_inbox();
|
|
13675
13778
|
var recentlyHandled = /* @__PURE__ */ new Map();
|
|
13676
13779
|
var MAX_RECENT = 1e3;
|
|
13677
|
-
function
|
|
13780
|
+
function wasHandled(channel, ts) {
|
|
13678
13781
|
if (!channel || !ts) return false;
|
|
13782
|
+
return recentlyHandled.has(`${channel}\u241F${ts}`);
|
|
13783
|
+
}
|
|
13784
|
+
function markHandled(channel, ts) {
|
|
13785
|
+
if (!channel || !ts) return;
|
|
13679
13786
|
const key2 = `${channel}\u241F${ts}`;
|
|
13680
|
-
if (recentlyHandled.has(key2)) return true;
|
|
13681
13787
|
recentlyHandled.set(key2, Date.now());
|
|
13682
13788
|
if (recentlyHandled.size > MAX_RECENT) {
|
|
13683
13789
|
const oldest = recentlyHandled.keys().next().value;
|
|
13684
13790
|
if (oldest) recentlyHandled.delete(oldest);
|
|
13685
13791
|
}
|
|
13686
|
-
return false;
|
|
13687
13792
|
}
|
|
13688
13793
|
var slack = new Hono6();
|
|
13689
13794
|
slack.post("/events", async (c) => {
|
|
@@ -13720,7 +13825,7 @@ slack.post("/events", async (c) => {
|
|
|
13720
13825
|
textSnippet: typeof ev.text === "string" ? ev.text : void 0,
|
|
13721
13826
|
meta: { ts: ev.ts, thread_ts: ev.thread_ts, team: ev.team, event_subtype: ev.subtype }
|
|
13722
13827
|
});
|
|
13723
|
-
if (
|
|
13828
|
+
if (wasHandled(ev.channel, ev.ts)) {
|
|
13724
13829
|
updateEvent(auditId, { status: "dropped", dropReason: "duplicate_delivery" });
|
|
13725
13830
|
return c.json({ ok: true });
|
|
13726
13831
|
}
|
|
@@ -13744,7 +13849,18 @@ slack.post("/events", async (c) => {
|
|
|
13744
13849
|
}
|
|
13745
13850
|
const orchestratorId = await findOrCreateOrchestratorId();
|
|
13746
13851
|
if (orchestratorId) {
|
|
13852
|
+
inbound.content = await normalizeSlackMentions(inbound.content);
|
|
13853
|
+
if (ev.user) {
|
|
13854
|
+
const speakerName = await resolveSlackUserName(ev.user);
|
|
13855
|
+
if (speakerName) {
|
|
13856
|
+
inbound.content = inbound.content.replace(
|
|
13857
|
+
`user=${ev.user}`,
|
|
13858
|
+
`user=${speakerName} <@${ev.user}>`
|
|
13859
|
+
);
|
|
13860
|
+
}
|
|
13861
|
+
}
|
|
13747
13862
|
pushToInbox(orchestratorId, inbound);
|
|
13863
|
+
markHandled(ev.channel, ev.ts);
|
|
13748
13864
|
updateEvent(auditId, { status: "routed", sessionId: orchestratorId });
|
|
13749
13865
|
} else {
|
|
13750
13866
|
updateEvent(auditId, { status: "error", error: "no orchestrator session available" });
|
|
@@ -14156,6 +14272,224 @@ mcpRouter.post("/:id/test", async (c) => {
|
|
|
14156
14272
|
return c.json(result);
|
|
14157
14273
|
});
|
|
14158
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
|
+
|
|
14159
14493
|
// src/server/auth/cf-access.ts
|
|
14160
14494
|
init_config();
|
|
14161
14495
|
import { createRemoteJWKSet, jwtVerify } from "jose";
|
|
@@ -14316,13 +14650,13 @@ var DEFAULT_WEB_PORT = 6969;
|
|
|
14316
14650
|
var WEB_PORT_SEQUENCE = [6969, 6970, 6971, 6972, 6973, 6974, 6975, 6976, 6977, 6978];
|
|
14317
14651
|
function getWebDirectory() {
|
|
14318
14652
|
try {
|
|
14319
|
-
const currentDir =
|
|
14320
|
-
const webDir =
|
|
14321
|
-
if (
|
|
14653
|
+
const currentDir = dirname9(fileURLToPath4(import.meta.url));
|
|
14654
|
+
const webDir = resolve12(currentDir, "..", "web");
|
|
14655
|
+
if (existsSync21(webDir) && existsSync21(join16(webDir, "package.json"))) {
|
|
14322
14656
|
return webDir;
|
|
14323
14657
|
}
|
|
14324
|
-
const altWebDir =
|
|
14325
|
-
if (
|
|
14658
|
+
const altWebDir = resolve12(currentDir, "..", "..", "web");
|
|
14659
|
+
if (existsSync21(altWebDir) && existsSync21(join16(altWebDir, "package.json"))) {
|
|
14326
14660
|
return altWebDir;
|
|
14327
14661
|
}
|
|
14328
14662
|
return null;
|
|
@@ -14345,18 +14679,18 @@ async function isSparkcoderWebRunning(port) {
|
|
|
14345
14679
|
}
|
|
14346
14680
|
}
|
|
14347
14681
|
function isPortInUse(port) {
|
|
14348
|
-
return new Promise((
|
|
14682
|
+
return new Promise((resolve13) => {
|
|
14349
14683
|
const server = createNetServer();
|
|
14350
14684
|
server.once("error", (err) => {
|
|
14351
14685
|
if (err.code === "EADDRINUSE") {
|
|
14352
|
-
|
|
14686
|
+
resolve13(true);
|
|
14353
14687
|
} else {
|
|
14354
|
-
|
|
14688
|
+
resolve13(false);
|
|
14355
14689
|
}
|
|
14356
14690
|
});
|
|
14357
14691
|
server.once("listening", () => {
|
|
14358
14692
|
server.close();
|
|
14359
|
-
|
|
14693
|
+
resolve13(false);
|
|
14360
14694
|
});
|
|
14361
14695
|
server.listen(port, "0.0.0.0");
|
|
14362
14696
|
});
|
|
@@ -14380,30 +14714,30 @@ async function findWebPort(preferredPort) {
|
|
|
14380
14714
|
return { port: preferredPort, alreadyRunning: false };
|
|
14381
14715
|
}
|
|
14382
14716
|
function hasProductionBuild(webDir) {
|
|
14383
|
-
const buildIdPath =
|
|
14384
|
-
return
|
|
14717
|
+
const buildIdPath = join16(webDir, ".next", "BUILD_ID");
|
|
14718
|
+
return existsSync21(buildIdPath);
|
|
14385
14719
|
}
|
|
14386
14720
|
function hasSourceFiles(webDir) {
|
|
14387
|
-
const appDir =
|
|
14388
|
-
const pagesDir =
|
|
14389
|
-
const rootAppDir =
|
|
14390
|
-
const rootPagesDir =
|
|
14391
|
-
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);
|
|
14392
14726
|
}
|
|
14393
14727
|
function getStandaloneServerPath(webDir) {
|
|
14394
14728
|
const possiblePaths2 = [
|
|
14395
|
-
|
|
14396
|
-
|
|
14729
|
+
join16(webDir, ".next", "standalone", "server.js"),
|
|
14730
|
+
join16(webDir, ".next", "standalone", "web", "server.js")
|
|
14397
14731
|
];
|
|
14398
14732
|
for (const serverPath of possiblePaths2) {
|
|
14399
|
-
if (
|
|
14733
|
+
if (existsSync21(serverPath)) {
|
|
14400
14734
|
return serverPath;
|
|
14401
14735
|
}
|
|
14402
14736
|
}
|
|
14403
14737
|
return null;
|
|
14404
14738
|
}
|
|
14405
14739
|
function runCommand(command, args, cwd, env) {
|
|
14406
|
-
return new Promise((
|
|
14740
|
+
return new Promise((resolve13) => {
|
|
14407
14741
|
const child = spawn2(command, args, {
|
|
14408
14742
|
cwd,
|
|
14409
14743
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -14418,10 +14752,10 @@ function runCommand(command, args, cwd, env) {
|
|
|
14418
14752
|
output += data.toString();
|
|
14419
14753
|
});
|
|
14420
14754
|
child.on("close", (code) => {
|
|
14421
|
-
|
|
14755
|
+
resolve13({ success: code === 0, output });
|
|
14422
14756
|
});
|
|
14423
14757
|
child.on("error", (err) => {
|
|
14424
|
-
|
|
14758
|
+
resolve13({ success: false, output: err.message });
|
|
14425
14759
|
});
|
|
14426
14760
|
});
|
|
14427
14761
|
}
|
|
@@ -14436,13 +14770,13 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
14436
14770
|
if (!quiet) console.log(` \u2713 Web UI already running at http://localhost:${actualPort}`);
|
|
14437
14771
|
return { process: null, port: actualPort };
|
|
14438
14772
|
}
|
|
14439
|
-
const usePnpm =
|
|
14440
|
-
const useNpm = !usePnpm &&
|
|
14773
|
+
const usePnpm = existsSync21(join16(webDir, "pnpm-lock.yaml"));
|
|
14774
|
+
const useNpm = !usePnpm && existsSync21(join16(webDir, "package-lock.json"));
|
|
14441
14775
|
const pkgManager = usePnpm ? "pnpm" : useNpm ? "npm" : "npx";
|
|
14442
14776
|
const { NODE_OPTIONS, TSX_TSCONFIG_PATH, ...cleanEnv } = process.env;
|
|
14443
14777
|
const apiUrl = publicUrl || `http://127.0.0.1:${apiPort}`;
|
|
14444
14778
|
const runtimeConfig = { apiBaseUrl: apiUrl };
|
|
14445
|
-
const runtimeConfigPath =
|
|
14779
|
+
const runtimeConfigPath = join16(webDir, "runtime-config.json");
|
|
14446
14780
|
try {
|
|
14447
14781
|
writeFileSync6(runtimeConfigPath, JSON.stringify(runtimeConfig, null, 2));
|
|
14448
14782
|
if (!quiet) console.log(` \u{1F4DD} Runtime config written to ${runtimeConfigPath}`);
|
|
@@ -14464,7 +14798,7 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
14464
14798
|
if (standaloneServerPath) {
|
|
14465
14799
|
command = "node";
|
|
14466
14800
|
args = ["server.js"];
|
|
14467
|
-
cwd =
|
|
14801
|
+
cwd = dirname9(standaloneServerPath);
|
|
14468
14802
|
webEnv.PORT = String(actualPort);
|
|
14469
14803
|
webEnv.HOSTNAME = "0.0.0.0";
|
|
14470
14804
|
if (!quiet) console.log(" \u{1F4E6} Starting Web UI from standalone build...");
|
|
@@ -14505,10 +14839,10 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
14505
14839
|
let started = false;
|
|
14506
14840
|
let exited = false;
|
|
14507
14841
|
let exitCode = null;
|
|
14508
|
-
const startedPromise = new Promise((
|
|
14842
|
+
const startedPromise = new Promise((resolve13) => {
|
|
14509
14843
|
const timeout = setTimeout(() => {
|
|
14510
14844
|
if (!started && !exited) {
|
|
14511
|
-
|
|
14845
|
+
resolve13(false);
|
|
14512
14846
|
}
|
|
14513
14847
|
}, startupTimeout);
|
|
14514
14848
|
child.stdout?.on("data", (data) => {
|
|
@@ -14522,7 +14856,7 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
14522
14856
|
if (!started && (output.includes("Ready") || output.includes("started") || output.includes("localhost"))) {
|
|
14523
14857
|
started = true;
|
|
14524
14858
|
clearTimeout(timeout);
|
|
14525
|
-
|
|
14859
|
+
resolve13(true);
|
|
14526
14860
|
}
|
|
14527
14861
|
});
|
|
14528
14862
|
child.stderr?.on("data", (data) => {
|
|
@@ -14534,14 +14868,14 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
14534
14868
|
child.on("error", (err) => {
|
|
14535
14869
|
if (!quiet) console.error(` \u274C Web UI spawn error: ${err.message}`);
|
|
14536
14870
|
clearTimeout(timeout);
|
|
14537
|
-
|
|
14871
|
+
resolve13(false);
|
|
14538
14872
|
});
|
|
14539
14873
|
child.on("exit", (code) => {
|
|
14540
14874
|
exited = true;
|
|
14541
14875
|
exitCode = code;
|
|
14542
14876
|
if (!started) {
|
|
14543
14877
|
clearTimeout(timeout);
|
|
14544
|
-
|
|
14878
|
+
resolve13(false);
|
|
14545
14879
|
}
|
|
14546
14880
|
webUIProcess = null;
|
|
14547
14881
|
});
|
|
@@ -14564,7 +14898,7 @@ function stopWebUI() {
|
|
|
14564
14898
|
}
|
|
14565
14899
|
}
|
|
14566
14900
|
async function createApp(options = {}) {
|
|
14567
|
-
const app = new
|
|
14901
|
+
const app = new Hono10();
|
|
14568
14902
|
app.use("*", cors({
|
|
14569
14903
|
origin: "*",
|
|
14570
14904
|
// Allow all origins
|
|
@@ -14592,6 +14926,7 @@ async function createApp(options = {}) {
|
|
|
14592
14926
|
app.route("/api/schedules", schedulesRouter);
|
|
14593
14927
|
app.route("/api/orchestrator", orchestratorRouter);
|
|
14594
14928
|
app.route("/api/mcp", mcpRouter);
|
|
14929
|
+
app.route("/api/skills", skills);
|
|
14595
14930
|
app.route("/api/webhooks", webhooksRouter);
|
|
14596
14931
|
const config = getConfig();
|
|
14597
14932
|
const webhookToken = config?.webhooks?.token;
|
|
@@ -14657,7 +14992,7 @@ async function startServer(options = {}) {
|
|
|
14657
14992
|
if (options.workingDirectory) {
|
|
14658
14993
|
config.resolvedWorkingDirectory = options.workingDirectory;
|
|
14659
14994
|
}
|
|
14660
|
-
if (!
|
|
14995
|
+
if (!existsSync21(config.resolvedWorkingDirectory)) {
|
|
14661
14996
|
mkdirSync9(config.resolvedWorkingDirectory, { recursive: true });
|
|
14662
14997
|
if (!options.quiet) console.log(`\u{1F4C1} Created agent workspace: ${config.resolvedWorkingDirectory}`);
|
|
14663
14998
|
}
|