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/cli.js
CHANGED
|
@@ -964,6 +964,7 @@ __export(config_exports, {
|
|
|
964
964
|
setApiKey: () => setApiKey,
|
|
965
965
|
setMcpServers: () => setMcpServers,
|
|
966
966
|
setPublicUrl: () => setPublicUrl,
|
|
967
|
+
setSkillsAdditionalDirectories: () => setSkillsAdditionalDirectories,
|
|
967
968
|
setSlackConfig: () => setSlackConfig,
|
|
968
969
|
setWebhookToken: () => setWebhookToken
|
|
969
970
|
});
|
|
@@ -1253,6 +1254,40 @@ function setWebhookToken(token) {
|
|
|
1253
1254
|
console.warn("[config] failed to persist webhook token:", err?.message || err);
|
|
1254
1255
|
}
|
|
1255
1256
|
}
|
|
1257
|
+
function setSkillsAdditionalDirectories(directories) {
|
|
1258
|
+
if (cachedConfig) {
|
|
1259
|
+
const cur = cachedConfig.skills || {};
|
|
1260
|
+
cachedConfig.skills = { ...cur, additionalDirectories: directories };
|
|
1261
|
+
try {
|
|
1262
|
+
const discovered = discoverSkillDirectories(cachedConfig.resolvedWorkingDirectory);
|
|
1263
|
+
const additionalAbs = directories.map((d) => resolve(cachedConfig.resolvedWorkingDirectory, d)).filter((d) => existsSync(d));
|
|
1264
|
+
cachedConfig.discoveredSkills = discovered;
|
|
1265
|
+
cachedConfig.resolvedSkillsDirectories = [
|
|
1266
|
+
...discovered.allDirectories,
|
|
1267
|
+
...additionalAbs
|
|
1268
|
+
];
|
|
1269
|
+
} catch {
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
try {
|
|
1273
|
+
const cwdPath = resolve(process.cwd(), "sparkecoder.config.json");
|
|
1274
|
+
const target = existsSync(cwdPath) ? cwdPath : join(ensureAppDataDirectory(), "sparkecoder.config.json");
|
|
1275
|
+
let raw = {};
|
|
1276
|
+
if (existsSync(target)) {
|
|
1277
|
+
try {
|
|
1278
|
+
raw = JSON.parse(readFileSync(target, "utf-8"));
|
|
1279
|
+
} catch {
|
|
1280
|
+
raw = {};
|
|
1281
|
+
}
|
|
1282
|
+
} else {
|
|
1283
|
+
raw = createDefaultConfig();
|
|
1284
|
+
}
|
|
1285
|
+
raw.skills = { ...raw.skills || {}, additionalDirectories: directories };
|
|
1286
|
+
writeFileSync(target, JSON.stringify(raw, null, 2));
|
|
1287
|
+
} catch (err) {
|
|
1288
|
+
console.warn("[config] failed to persist skill directories:", err?.message || err);
|
|
1289
|
+
}
|
|
1290
|
+
}
|
|
1256
1291
|
function setPublicUrl(publicUrl) {
|
|
1257
1292
|
if (cachedConfig) {
|
|
1258
1293
|
cachedConfig.server = { ...cachedConfig.server ?? {}, publicUrl };
|
|
@@ -3014,7 +3049,7 @@ async function createClient(serverId, handle, root) {
|
|
|
3014
3049
|
},
|
|
3015
3050
|
async waitForDiagnostics(filePath, timeoutMs = 5e3) {
|
|
3016
3051
|
const normalized = normalizePath(filePath);
|
|
3017
|
-
return new Promise((
|
|
3052
|
+
return new Promise((resolve14) => {
|
|
3018
3053
|
const startTime = Date.now();
|
|
3019
3054
|
let debounceTimer;
|
|
3020
3055
|
let resolved = false;
|
|
@@ -3033,7 +3068,7 @@ async function createClient(serverId, handle, root) {
|
|
|
3033
3068
|
if (resolved) return;
|
|
3034
3069
|
resolved = true;
|
|
3035
3070
|
cleanup2();
|
|
3036
|
-
|
|
3071
|
+
resolve14(diagnostics.get(normalized) || []);
|
|
3037
3072
|
};
|
|
3038
3073
|
const onDiagnostic = () => {
|
|
3039
3074
|
if (debounceTimer) clearTimeout(debounceTimer);
|
|
@@ -3408,7 +3443,7 @@ Working directory: ${options.workingDirectory}`,
|
|
|
3408
3443
|
isChunked: true
|
|
3409
3444
|
});
|
|
3410
3445
|
if (chunkCount > 1) {
|
|
3411
|
-
await new Promise((
|
|
3446
|
+
await new Promise((resolve14) => setTimeout(resolve14, 0));
|
|
3412
3447
|
}
|
|
3413
3448
|
}
|
|
3414
3449
|
}
|
|
@@ -4027,7 +4062,7 @@ async function loadSkillsFromDirectory(directory, options = {}) {
|
|
|
4027
4062
|
if (!existsSync10(directory)) {
|
|
4028
4063
|
return [];
|
|
4029
4064
|
}
|
|
4030
|
-
const
|
|
4065
|
+
const skills2 = [];
|
|
4031
4066
|
const entries = await readdir(directory, { withFileTypes: true });
|
|
4032
4067
|
for (const entry2 of entries) {
|
|
4033
4068
|
let filePath;
|
|
@@ -4051,7 +4086,7 @@ async function loadSkillsFromDirectory(directory, options = {}) {
|
|
|
4051
4086
|
if (parsed) {
|
|
4052
4087
|
const alwaysApply = forceAlwaysApply || parsed.metadata.alwaysApply;
|
|
4053
4088
|
const loadType = alwaysApply ? "always" : defaultLoadType;
|
|
4054
|
-
|
|
4089
|
+
skills2.push({
|
|
4055
4090
|
name: parsed.metadata.name,
|
|
4056
4091
|
description: parsed.metadata.description,
|
|
4057
4092
|
filePath,
|
|
@@ -4065,7 +4100,7 @@ async function loadSkillsFromDirectory(directory, options = {}) {
|
|
|
4065
4100
|
} else {
|
|
4066
4101
|
const name = getSkillNameFromPath(filePath);
|
|
4067
4102
|
const firstParagraph = content.split("\n\n")[0]?.slice(0, 200) || "No description";
|
|
4068
|
-
|
|
4103
|
+
skills2.push({
|
|
4069
4104
|
name,
|
|
4070
4105
|
description: firstParagraph.replace(/^#\s*/, "").trim(),
|
|
4071
4106
|
filePath,
|
|
@@ -4078,7 +4113,7 @@ async function loadSkillsFromDirectory(directory, options = {}) {
|
|
|
4078
4113
|
});
|
|
4079
4114
|
}
|
|
4080
4115
|
}
|
|
4081
|
-
return
|
|
4116
|
+
return skills2.filter(
|
|
4082
4117
|
(s) => s.platforms.length === 0 || s.platforms.includes(process.platform)
|
|
4083
4118
|
);
|
|
4084
4119
|
}
|
|
@@ -4086,8 +4121,8 @@ async function loadAllSkills(directories) {
|
|
|
4086
4121
|
const allSkills = [];
|
|
4087
4122
|
const seenNames = /* @__PURE__ */ new Set();
|
|
4088
4123
|
for (const dir of directories) {
|
|
4089
|
-
const
|
|
4090
|
-
for (const skill of
|
|
4124
|
+
const skills2 = await loadSkillsFromDirectory(dir);
|
|
4125
|
+
for (const skill of skills2) {
|
|
4091
4126
|
if (!seenNames.has(skill.name.toLowerCase())) {
|
|
4092
4127
|
seenNames.add(skill.name.toLowerCase());
|
|
4093
4128
|
allSkills.push(skill);
|
|
@@ -4100,12 +4135,12 @@ async function loadAllSkillsFromDiscovered(discovered) {
|
|
|
4100
4135
|
const allSkills = [];
|
|
4101
4136
|
const seenNames = /* @__PURE__ */ new Set();
|
|
4102
4137
|
for (const { path, priority } of discovered.alwaysLoadedDirs) {
|
|
4103
|
-
const
|
|
4138
|
+
const skills2 = await loadSkillsFromDirectory(path, {
|
|
4104
4139
|
priority,
|
|
4105
4140
|
defaultLoadType: "always",
|
|
4106
4141
|
forceAlwaysApply: true
|
|
4107
4142
|
});
|
|
4108
|
-
for (const skill of
|
|
4143
|
+
for (const skill of skills2) {
|
|
4109
4144
|
if (!seenNames.has(skill.name.toLowerCase())) {
|
|
4110
4145
|
seenNames.add(skill.name.toLowerCase());
|
|
4111
4146
|
allSkills.push(skill);
|
|
@@ -4113,12 +4148,12 @@ async function loadAllSkillsFromDiscovered(discovered) {
|
|
|
4113
4148
|
}
|
|
4114
4149
|
}
|
|
4115
4150
|
for (const { path, priority } of discovered.onDemandDirs) {
|
|
4116
|
-
const
|
|
4151
|
+
const skills2 = await loadSkillsFromDirectory(path, {
|
|
4117
4152
|
priority,
|
|
4118
4153
|
defaultLoadType: "on_demand",
|
|
4119
4154
|
forceAlwaysApply: false
|
|
4120
4155
|
});
|
|
4121
|
-
for (const skill of
|
|
4156
|
+
for (const skill of skills2) {
|
|
4122
4157
|
if (!seenNames.has(skill.name.toLowerCase())) {
|
|
4123
4158
|
seenNames.add(skill.name.toLowerCase());
|
|
4124
4159
|
allSkills.push(skill);
|
|
@@ -4143,7 +4178,7 @@ async function loadAllSkillsFromDiscovered(discovered) {
|
|
|
4143
4178
|
all: allSkills
|
|
4144
4179
|
};
|
|
4145
4180
|
}
|
|
4146
|
-
async function getGlobMatchedSkills(
|
|
4181
|
+
async function getGlobMatchedSkills(skills2, activeFiles, workingDirectory) {
|
|
4147
4182
|
if (activeFiles.length === 0) {
|
|
4148
4183
|
return [];
|
|
4149
4184
|
}
|
|
@@ -4153,7 +4188,7 @@ async function getGlobMatchedSkills(skills, activeFiles, workingDirectory) {
|
|
|
4153
4188
|
}
|
|
4154
4189
|
return f;
|
|
4155
4190
|
});
|
|
4156
|
-
const matchedSkills =
|
|
4191
|
+
const matchedSkills = skills2.filter((skill) => {
|
|
4157
4192
|
if (skill.alwaysApply || skill.loadType === "always") {
|
|
4158
4193
|
return false;
|
|
4159
4194
|
}
|
|
@@ -4199,8 +4234,8 @@ async function loadSkillContent(skillName, directories) {
|
|
|
4199
4234
|
content: parsed ? parsed.body : content
|
|
4200
4235
|
};
|
|
4201
4236
|
}
|
|
4202
|
-
function formatSkillsForContext(
|
|
4203
|
-
const onDemandSkills =
|
|
4237
|
+
function formatSkillsForContext(skills2) {
|
|
4238
|
+
const onDemandSkills = skills2.filter((s) => !s.alwaysApply && s.loadType !== "always");
|
|
4204
4239
|
if (onDemandSkills.length === 0) {
|
|
4205
4240
|
return "No on-demand skills available.";
|
|
4206
4241
|
}
|
|
@@ -4211,12 +4246,12 @@ function formatSkillsForContext(skills) {
|
|
|
4211
4246
|
}
|
|
4212
4247
|
return lines.join("\n");
|
|
4213
4248
|
}
|
|
4214
|
-
function formatAlwaysLoadedSkills(
|
|
4215
|
-
if (
|
|
4249
|
+
function formatAlwaysLoadedSkills(skills2) {
|
|
4250
|
+
if (skills2.length === 0) {
|
|
4216
4251
|
return "";
|
|
4217
4252
|
}
|
|
4218
4253
|
const sections = [];
|
|
4219
|
-
for (const skill of
|
|
4254
|
+
for (const skill of skills2) {
|
|
4220
4255
|
sections.push(`### ${skill.name}
|
|
4221
4256
|
|
|
4222
4257
|
${skill.content}`);
|
|
@@ -4225,12 +4260,12 @@ ${skill.content}`);
|
|
|
4225
4260
|
|
|
4226
4261
|
${sections.join("\n\n---\n\n")}`;
|
|
4227
4262
|
}
|
|
4228
|
-
function formatGlobMatchedSkills(
|
|
4229
|
-
if (
|
|
4263
|
+
function formatGlobMatchedSkills(skills2) {
|
|
4264
|
+
if (skills2.length === 0) {
|
|
4230
4265
|
return "";
|
|
4231
4266
|
}
|
|
4232
4267
|
const sections = [];
|
|
4233
|
-
for (const skill of
|
|
4268
|
+
for (const skill of skills2) {
|
|
4234
4269
|
sections.push(`### ${skill.name}
|
|
4235
4270
|
|
|
4236
4271
|
${skill.content}`);
|
|
@@ -4272,16 +4307,16 @@ Once loaded, a skill's content will be available in the conversation context.`,
|
|
|
4272
4307
|
try {
|
|
4273
4308
|
switch (action) {
|
|
4274
4309
|
case "list": {
|
|
4275
|
-
const
|
|
4310
|
+
const skills2 = await loadAllSkills(options.skillsDirectories);
|
|
4276
4311
|
return {
|
|
4277
4312
|
success: true,
|
|
4278
4313
|
action: "list",
|
|
4279
|
-
skillCount:
|
|
4280
|
-
skills:
|
|
4314
|
+
skillCount: skills2.length,
|
|
4315
|
+
skills: skills2.map((s) => ({
|
|
4281
4316
|
name: s.name,
|
|
4282
4317
|
description: s.description
|
|
4283
4318
|
})),
|
|
4284
|
-
formatted: formatSkillsForContext(
|
|
4319
|
+
formatted: formatSkillsForContext(skills2)
|
|
4285
4320
|
};
|
|
4286
4321
|
}
|
|
4287
4322
|
case "load": {
|
|
@@ -4719,8 +4754,8 @@ var init_subagent = __esm({
|
|
|
4719
4754
|
if (eventQueue.length > 0) {
|
|
4720
4755
|
yield eventQueue.shift();
|
|
4721
4756
|
} else if (!done) {
|
|
4722
|
-
const event = await new Promise((
|
|
4723
|
-
resolveNext =
|
|
4757
|
+
const event = await new Promise((resolve14) => {
|
|
4758
|
+
resolveNext = resolve14;
|
|
4724
4759
|
});
|
|
4725
4760
|
if (event) {
|
|
4726
4761
|
yield event;
|
|
@@ -5362,7 +5397,7 @@ function formatError(error) {
|
|
|
5362
5397
|
}
|
|
5363
5398
|
}
|
|
5364
5399
|
function sleep(ms) {
|
|
5365
|
-
return new Promise((
|
|
5400
|
+
return new Promise((resolve14) => setTimeout(resolve14, ms));
|
|
5366
5401
|
}
|
|
5367
5402
|
function isPathExcluded(relativePath, exclude) {
|
|
5368
5403
|
return exclude.some((pattern) => {
|
|
@@ -5380,7 +5415,7 @@ function isPathExcluded(relativePath, exclude) {
|
|
|
5380
5415
|
}
|
|
5381
5416
|
async function walkDirectory(dir, include, exclude, baseDir) {
|
|
5382
5417
|
const { readdirSync: readdirSync4 } = await import("fs");
|
|
5383
|
-
const { join:
|
|
5418
|
+
const { join: join18, relative: relative10 } = await import("path");
|
|
5384
5419
|
const files = [];
|
|
5385
5420
|
function walk(currentDir) {
|
|
5386
5421
|
let entries;
|
|
@@ -5390,7 +5425,7 @@ async function walkDirectory(dir, include, exclude, baseDir) {
|
|
|
5390
5425
|
return;
|
|
5391
5426
|
}
|
|
5392
5427
|
for (const entry2 of entries) {
|
|
5393
|
-
const fullPath =
|
|
5428
|
+
const fullPath = join18(currentDir, entry2.name);
|
|
5394
5429
|
const relativePath = relative10(baseDir, fullPath);
|
|
5395
5430
|
if (isPathExcluded(relativePath, exclude)) {
|
|
5396
5431
|
continue;
|
|
@@ -7238,8 +7273,8 @@ async function buildSystemPrompt(options) {
|
|
|
7238
7273
|
}
|
|
7239
7274
|
} else {
|
|
7240
7275
|
const { loadAllSkills: loadAllSkills2 } = await Promise.resolve().then(() => (init_skills(), skills_exports));
|
|
7241
|
-
const
|
|
7242
|
-
onDemandSkillsContext = formatSkillsForContext(
|
|
7276
|
+
const skills2 = await loadAllSkills2(skillsDirectories);
|
|
7277
|
+
onDemandSkillsContext = formatSkillsForContext(skills2);
|
|
7243
7278
|
}
|
|
7244
7279
|
const todos = await todoQueries.getBySession(sessionId);
|
|
7245
7280
|
const todosContext = formatTodosForContext(todos);
|
|
@@ -8545,6 +8580,9 @@ function isSlackConfigured() {
|
|
|
8545
8580
|
function getSlackSigningSecret() {
|
|
8546
8581
|
return readSlackConfig()?.signingSecret ?? null;
|
|
8547
8582
|
}
|
|
8583
|
+
function getSlackBotToken() {
|
|
8584
|
+
return readSlackConfig()?.botToken ?? null;
|
|
8585
|
+
}
|
|
8548
8586
|
function getDefaultOrchestratorName() {
|
|
8549
8587
|
return readSlackConfig()?.defaultOrchestratorName ?? null;
|
|
8550
8588
|
}
|
|
@@ -8599,6 +8637,62 @@ function getSlackAllowlistPolicy() {
|
|
|
8599
8637
|
return { allowedUsers: [], allowedChannels: [], allowDmsFromAnyone: true };
|
|
8600
8638
|
}
|
|
8601
8639
|
}
|
|
8640
|
+
async function fetchSlackUserName(userId) {
|
|
8641
|
+
const token = getSlackBotToken();
|
|
8642
|
+
if (!token) return null;
|
|
8643
|
+
try {
|
|
8644
|
+
const res = await fetch(`https://slack.com/api/users.info?user=${encodeURIComponent(userId)}`, {
|
|
8645
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
8646
|
+
});
|
|
8647
|
+
const data = await res.json().catch(() => ({}));
|
|
8648
|
+
if (!data?.ok) {
|
|
8649
|
+
console.warn(`[slack] users.info(${userId}) failed: ${data?.error || `HTTP ${res.status}`}`);
|
|
8650
|
+
return null;
|
|
8651
|
+
}
|
|
8652
|
+
const profile = data.user?.profile || {};
|
|
8653
|
+
const name = profile.display_name_normalized || profile.display_name || profile.real_name_normalized || profile.real_name || data.user?.real_name || data.user?.name || null;
|
|
8654
|
+
return name ? String(name) : null;
|
|
8655
|
+
} catch (err) {
|
|
8656
|
+
console.warn(`[slack] users.info(${userId}) error:`, err?.message || err);
|
|
8657
|
+
return null;
|
|
8658
|
+
}
|
|
8659
|
+
}
|
|
8660
|
+
async function resolveSlackUserName(userId) {
|
|
8661
|
+
if (!userId) return null;
|
|
8662
|
+
const now = Date.now();
|
|
8663
|
+
const hit = userNameCache.get(userId);
|
|
8664
|
+
if (hit && hit.expiresAt > now) return hit.name;
|
|
8665
|
+
const inflight = userInflight.get(userId);
|
|
8666
|
+
if (inflight) return inflight;
|
|
8667
|
+
const p = (async () => {
|
|
8668
|
+
const name = await fetchSlackUserName(userId);
|
|
8669
|
+
userNameCache.set(userId, {
|
|
8670
|
+
name,
|
|
8671
|
+
expiresAt: now + (name ? USER_TTL_MS : USER_FAIL_TTL_MS)
|
|
8672
|
+
});
|
|
8673
|
+
userInflight.delete(userId);
|
|
8674
|
+
return name;
|
|
8675
|
+
})();
|
|
8676
|
+
userInflight.set(userId, p);
|
|
8677
|
+
return p;
|
|
8678
|
+
}
|
|
8679
|
+
async function normalizeSlackMentions(text) {
|
|
8680
|
+
if (!text) return text;
|
|
8681
|
+
const userMentionRe = /<@([UW][A-Z0-9]+)(?:\|([^>]+))?>/g;
|
|
8682
|
+
const userIds = /* @__PURE__ */ new Set();
|
|
8683
|
+
for (const m of text.matchAll(userMentionRe)) {
|
|
8684
|
+
if (!m[2]) userIds.add(m[1]);
|
|
8685
|
+
}
|
|
8686
|
+
if (userIds.size > 0) {
|
|
8687
|
+
await Promise.all([...userIds].map((id) => resolveSlackUserName(id)));
|
|
8688
|
+
}
|
|
8689
|
+
return text.replace(userMentionRe, (_full, id, label) => {
|
|
8690
|
+
if (label) return `${label} <@${id}>`;
|
|
8691
|
+
const cached = userNameCache.get(id);
|
|
8692
|
+
const name = cached?.name;
|
|
8693
|
+
return name ? `${name} <@${id}>` : `<@${id}>`;
|
|
8694
|
+
});
|
|
8695
|
+
}
|
|
8602
8696
|
function getSlackDeniedReplyPolicy() {
|
|
8603
8697
|
try {
|
|
8604
8698
|
const cfg = getConfig();
|
|
@@ -8611,13 +8705,17 @@ function getSlackDeniedReplyPolicy() {
|
|
|
8611
8705
|
return { enabled: true, template: DEFAULT_DENIED_TEMPLATE };
|
|
8612
8706
|
}
|
|
8613
8707
|
}
|
|
8614
|
-
var cachedSelf, selfInflight, DEFAULT_DENIED_TEMPLATE;
|
|
8708
|
+
var cachedSelf, selfInflight, USER_TTL_MS, USER_FAIL_TTL_MS, userNameCache, userInflight, DEFAULT_DENIED_TEMPLATE;
|
|
8615
8709
|
var init_client3 = __esm({
|
|
8616
8710
|
"src/integrations/slack/client.ts"() {
|
|
8617
8711
|
"use strict";
|
|
8618
8712
|
init_config();
|
|
8619
8713
|
cachedSelf = null;
|
|
8620
8714
|
selfInflight = null;
|
|
8715
|
+
USER_TTL_MS = 60 * 60 * 1e3;
|
|
8716
|
+
USER_FAIL_TTL_MS = 5 * 60 * 1e3;
|
|
8717
|
+
userNameCache = /* @__PURE__ */ new Map();
|
|
8718
|
+
userInflight = /* @__PURE__ */ new Map();
|
|
8621
8719
|
DEFAULT_DENIED_TEMPLATE = "Sorry, you don't have permission to use this bot. (Contact the bot owner if you think this is a mistake.)";
|
|
8622
8720
|
}
|
|
8623
8721
|
});
|
|
@@ -8632,9 +8730,6 @@ function markThreadOwned(channel, threadTs) {
|
|
|
8632
8730
|
function isThreadOwned(channel, threadTs) {
|
|
8633
8731
|
return ownedThreads.has(threadKey(channel, threadTs));
|
|
8634
8732
|
}
|
|
8635
|
-
function stripMention(text) {
|
|
8636
|
-
return String(text || "").replace(/<@[^>]+>/g, "").trim();
|
|
8637
|
-
}
|
|
8638
8733
|
function isSelfAuthored(event, self) {
|
|
8639
8734
|
if (!self) return true;
|
|
8640
8735
|
if (self.botId && event.bot_id && event.bot_id === self.botId) return true;
|
|
@@ -8649,14 +8744,22 @@ function slackEventToInboundResult(event, opts = {}) {
|
|
|
8649
8744
|
return { event: null, dropReason: "bot_message" };
|
|
8650
8745
|
}
|
|
8651
8746
|
if (event.type === "message" && event.subtype && IGNORED_MESSAGE_SUBTYPES.has(event.subtype)) {
|
|
8652
|
-
return { event: null, dropReason: "
|
|
8747
|
+
return { event: null, dropReason: "ignored_subtype" };
|
|
8653
8748
|
}
|
|
8654
8749
|
const isDm = event.type === "message" && event.channel_type === "im";
|
|
8655
8750
|
const isThreadReply = event.type === "message" && !isDm && typeof event.thread_ts === "string" && event.thread_ts !== event.ts;
|
|
8751
|
+
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.
|
|
8752
|
+
typeof event.channel === "string");
|
|
8656
8753
|
if (event.type !== "app_mention" && !isDm && !isThreadReply) {
|
|
8754
|
+
if (isNonThreadChannelMsg) {
|
|
8755
|
+
return { event: null, dropReason: "non_thread_channel_msg" };
|
|
8756
|
+
}
|
|
8757
|
+
if (event.type !== "message") {
|
|
8758
|
+
return { event: null, dropReason: "non_message_event" };
|
|
8759
|
+
}
|
|
8657
8760
|
return { event: null, dropReason: "unsupported_type" };
|
|
8658
8761
|
}
|
|
8659
|
-
const text =
|
|
8762
|
+
const text = (event.text ?? "").trim();
|
|
8660
8763
|
if (!text) return { event: null, dropReason: "empty_text" };
|
|
8661
8764
|
const policy = getSlackAllowlistPolicy();
|
|
8662
8765
|
const userAllowlistActive = policy.allowedUsers.length > 0;
|
|
@@ -9639,11 +9742,11 @@ function waitForTaskQuestionAnswer(question) {
|
|
|
9639
9742
|
if (pendingQuestions.has(k)) {
|
|
9640
9743
|
return Promise.reject(new Error(`Question already pending: ${question.questionId}`));
|
|
9641
9744
|
}
|
|
9642
|
-
return new Promise((
|
|
9745
|
+
return new Promise((resolve14, reject) => {
|
|
9643
9746
|
pendingQuestions.set(k, {
|
|
9644
9747
|
...question,
|
|
9645
9748
|
createdAt: /* @__PURE__ */ new Date(),
|
|
9646
|
-
resolve:
|
|
9749
|
+
resolve: resolve14,
|
|
9647
9750
|
reject
|
|
9648
9751
|
});
|
|
9649
9752
|
});
|
|
@@ -10792,14 +10895,14 @@ ${p.text}` : p.text;
|
|
|
10792
10895
|
const result = await recorder.encode();
|
|
10793
10896
|
recorder.clear();
|
|
10794
10897
|
if (!result) return [];
|
|
10795
|
-
const { readFile:
|
|
10898
|
+
const { readFile: readFile13, unlink: unlink4 } = await import("fs/promises");
|
|
10796
10899
|
const uploadInfo = await storageQueries2.getUploadUrl(
|
|
10797
10900
|
this.session.id,
|
|
10798
10901
|
`browser-recording-${Date.now()}.mp4`,
|
|
10799
10902
|
"video/mp4",
|
|
10800
10903
|
"browser-recording"
|
|
10801
10904
|
);
|
|
10802
|
-
const fileData = await
|
|
10905
|
+
const fileData = await readFile13(result.path);
|
|
10803
10906
|
await fetch(uploadInfo.uploadUrl, {
|
|
10804
10907
|
method: "PUT",
|
|
10805
10908
|
headers: { "Content-Type": "video/mp4" },
|
|
@@ -10807,7 +10910,7 @@ ${p.text}` : p.text;
|
|
|
10807
10910
|
});
|
|
10808
10911
|
await storageQueries2.updateFile(uploadInfo.fileId, { sizeBytes: result.sizeBytes });
|
|
10809
10912
|
const dlInfo = await storageQueries2.getDownloadUrl(uploadInfo.fileId);
|
|
10810
|
-
await
|
|
10913
|
+
await unlink4(result.path).catch(() => {
|
|
10811
10914
|
});
|
|
10812
10915
|
console.log(`[TASK] Browser recording uploaded (${result.sizeBytes} bytes)`);
|
|
10813
10916
|
return [dlInfo.downloadUrl];
|
|
@@ -10825,13 +10928,13 @@ ${p.text}` : p.text;
|
|
|
10825
10928
|
try {
|
|
10826
10929
|
const { isRemoteConfigured: isRemoteConfigured2, storageQueries: storageQueries2 } = await Promise.resolve().then(() => (init_remote(), remote_exports));
|
|
10827
10930
|
if (!isRemoteConfigured2()) return [];
|
|
10828
|
-
const { readFile:
|
|
10829
|
-
const { join:
|
|
10931
|
+
const { readFile: readFile13 } = await import("fs/promises");
|
|
10932
|
+
const { join: join18, basename: basename7 } = await import("path");
|
|
10830
10933
|
const urls = [];
|
|
10831
10934
|
for (const filePath of filePaths) {
|
|
10832
10935
|
try {
|
|
10833
|
-
const fullPath = filePath.startsWith("/") ? filePath :
|
|
10834
|
-
const fileName =
|
|
10936
|
+
const fullPath = filePath.startsWith("/") ? filePath : join18(this.session.workingDirectory, filePath);
|
|
10937
|
+
const fileName = basename7(fullPath);
|
|
10835
10938
|
const ext = fileName.split(".").pop()?.toLowerCase() || "";
|
|
10836
10939
|
const mimeMap = {
|
|
10837
10940
|
pdf: "application/pdf",
|
|
@@ -10855,7 +10958,7 @@ ${p.text}` : p.text;
|
|
|
10855
10958
|
contentType,
|
|
10856
10959
|
"task-output"
|
|
10857
10960
|
);
|
|
10858
|
-
const fileData = await
|
|
10961
|
+
const fileData = await readFile13(fullPath);
|
|
10859
10962
|
await fetch(uploadInfo.uploadUrl, {
|
|
10860
10963
|
method: "PUT",
|
|
10861
10964
|
headers: { "Content-Type": contentType },
|
|
@@ -10904,8 +11007,8 @@ ${p.text}` : p.text;
|
|
|
10904
11007
|
this.pendingApprovals.set(toolCallId, await execution);
|
|
10905
11008
|
options.onApprovalRequired?.(await execution);
|
|
10906
11009
|
await sessionQueries.updateStatus(this.session.id, "waiting");
|
|
10907
|
-
const approved = await new Promise((
|
|
10908
|
-
approvalResolvers.set(toolCallId, { resolve:
|
|
11010
|
+
const approved = await new Promise((resolve14) => {
|
|
11011
|
+
approvalResolvers.set(toolCallId, { resolve: resolve14, sessionId: this.session.id });
|
|
10909
11012
|
});
|
|
10910
11013
|
const resolverData = approvalResolvers.get(toolCallId);
|
|
10911
11014
|
approvalResolvers.delete(toolCallId);
|
|
@@ -11011,8 +11114,8 @@ async function withSessionLock(sessionId, fn) {
|
|
|
11011
11114
|
state2.pending++;
|
|
11012
11115
|
const prev = state2.tail;
|
|
11013
11116
|
let release;
|
|
11014
|
-
const next = new Promise((
|
|
11015
|
-
release =
|
|
11117
|
+
const next = new Promise((resolve14) => {
|
|
11118
|
+
release = resolve14;
|
|
11016
11119
|
});
|
|
11017
11120
|
state2.tail = prev.then(() => next);
|
|
11018
11121
|
await prev;
|
|
@@ -11625,17 +11728,17 @@ import chalk from "chalk";
|
|
|
11625
11728
|
import ora from "ora";
|
|
11626
11729
|
import "dotenv/config";
|
|
11627
11730
|
import { createInterface } from "readline";
|
|
11628
|
-
import { dirname as
|
|
11731
|
+
import { dirname as dirname10 } from "path";
|
|
11629
11732
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
11630
11733
|
|
|
11631
11734
|
// src/server/index.ts
|
|
11632
11735
|
import "dotenv/config";
|
|
11633
|
-
import { Hono as
|
|
11736
|
+
import { Hono as Hono10 } from "hono";
|
|
11634
11737
|
import { serve } from "@hono/node-server";
|
|
11635
11738
|
import { cors } from "hono/cors";
|
|
11636
11739
|
import { logger } from "hono/logger";
|
|
11637
|
-
import { existsSync as
|
|
11638
|
-
import { resolve as
|
|
11740
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync9, writeFileSync as writeFileSync6 } from "fs";
|
|
11741
|
+
import { resolve as resolve12, dirname as dirname9, join as join16 } from "path";
|
|
11639
11742
|
import { spawn as spawn2 } from "child_process";
|
|
11640
11743
|
import { createServer as createNetServer } from "net";
|
|
11641
11744
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
@@ -12810,7 +12913,7 @@ async function emitSyntheticToolStreaming(writeSSE, toolCallStarts, toolCallId,
|
|
|
12810
12913
|
toolCallId,
|
|
12811
12914
|
argsTextDelta: chunk
|
|
12812
12915
|
}));
|
|
12813
|
-
await new Promise((
|
|
12916
|
+
await new Promise((resolve14) => setTimeout(resolve14, 0));
|
|
12814
12917
|
}
|
|
12815
12918
|
}
|
|
12816
12919
|
function buildDevtoolsContextXml(sessionId) {
|
|
@@ -13065,7 +13168,7 @@ ${prompt}` });
|
|
|
13065
13168
|
chunkIndex,
|
|
13066
13169
|
chunkCount
|
|
13067
13170
|
}));
|
|
13068
|
-
await new Promise((
|
|
13171
|
+
await new Promise((resolve14) => setTimeout(resolve14, 0));
|
|
13069
13172
|
}
|
|
13070
13173
|
const browserPort = progress.data?.browserStreamPort;
|
|
13071
13174
|
const browserClosed = progress.data?.browserClosed;
|
|
@@ -13612,7 +13715,7 @@ agents.post(
|
|
|
13612
13715
|
chunkIndex,
|
|
13613
13716
|
chunkCount
|
|
13614
13717
|
}));
|
|
13615
|
-
await new Promise((
|
|
13718
|
+
await new Promise((resolve14) => setTimeout(resolve14, 0));
|
|
13616
13719
|
}
|
|
13617
13720
|
const browserPort = progress.data?.browserStreamPort;
|
|
13618
13721
|
const browserClosed = progress.data?.browserClosed;
|
|
@@ -14625,16 +14728,18 @@ init_webhook_events();
|
|
|
14625
14728
|
init_inbox();
|
|
14626
14729
|
var recentlyHandled = /* @__PURE__ */ new Map();
|
|
14627
14730
|
var MAX_RECENT = 1e3;
|
|
14628
|
-
function
|
|
14731
|
+
function wasHandled(channel, ts) {
|
|
14629
14732
|
if (!channel || !ts) return false;
|
|
14733
|
+
return recentlyHandled.has(`${channel}\u241F${ts}`);
|
|
14734
|
+
}
|
|
14735
|
+
function markHandled(channel, ts) {
|
|
14736
|
+
if (!channel || !ts) return;
|
|
14630
14737
|
const key2 = `${channel}\u241F${ts}`;
|
|
14631
|
-
if (recentlyHandled.has(key2)) return true;
|
|
14632
14738
|
recentlyHandled.set(key2, Date.now());
|
|
14633
14739
|
if (recentlyHandled.size > MAX_RECENT) {
|
|
14634
14740
|
const oldest = recentlyHandled.keys().next().value;
|
|
14635
14741
|
if (oldest) recentlyHandled.delete(oldest);
|
|
14636
14742
|
}
|
|
14637
|
-
return false;
|
|
14638
14743
|
}
|
|
14639
14744
|
var slack = new Hono6();
|
|
14640
14745
|
slack.post("/events", async (c) => {
|
|
@@ -14671,7 +14776,7 @@ slack.post("/events", async (c) => {
|
|
|
14671
14776
|
textSnippet: typeof ev.text === "string" ? ev.text : void 0,
|
|
14672
14777
|
meta: { ts: ev.ts, thread_ts: ev.thread_ts, team: ev.team, event_subtype: ev.subtype }
|
|
14673
14778
|
});
|
|
14674
|
-
if (
|
|
14779
|
+
if (wasHandled(ev.channel, ev.ts)) {
|
|
14675
14780
|
updateEvent(auditId, { status: "dropped", dropReason: "duplicate_delivery" });
|
|
14676
14781
|
return c.json({ ok: true });
|
|
14677
14782
|
}
|
|
@@ -14695,7 +14800,18 @@ slack.post("/events", async (c) => {
|
|
|
14695
14800
|
}
|
|
14696
14801
|
const orchestratorId = await findOrCreateOrchestratorId();
|
|
14697
14802
|
if (orchestratorId) {
|
|
14803
|
+
inbound.content = await normalizeSlackMentions(inbound.content);
|
|
14804
|
+
if (ev.user) {
|
|
14805
|
+
const speakerName = await resolveSlackUserName(ev.user);
|
|
14806
|
+
if (speakerName) {
|
|
14807
|
+
inbound.content = inbound.content.replace(
|
|
14808
|
+
`user=${ev.user}`,
|
|
14809
|
+
`user=${speakerName} <@${ev.user}>`
|
|
14810
|
+
);
|
|
14811
|
+
}
|
|
14812
|
+
}
|
|
14698
14813
|
pushToInbox(orchestratorId, inbound);
|
|
14814
|
+
markHandled(ev.channel, ev.ts);
|
|
14699
14815
|
updateEvent(auditId, { status: "routed", sessionId: orchestratorId });
|
|
14700
14816
|
} else {
|
|
14701
14817
|
updateEvent(auditId, { status: "error", error: "no orchestrator session available" });
|
|
@@ -15107,6 +15223,224 @@ mcpRouter.post("/:id/test", async (c) => {
|
|
|
15107
15223
|
return c.json(result);
|
|
15108
15224
|
});
|
|
15109
15225
|
|
|
15226
|
+
// src/server/routes/skills.ts
|
|
15227
|
+
init_config();
|
|
15228
|
+
init_skills();
|
|
15229
|
+
import { Hono as Hono9 } from "hono";
|
|
15230
|
+
import { zValidator as zValidator7 } from "@hono/zod-validator";
|
|
15231
|
+
import { z as z22 } from "zod";
|
|
15232
|
+
import { existsSync as existsSync20, statSync as statSync3 } from "fs";
|
|
15233
|
+
import { readFile as readFile12, writeFile as writeFile6, unlink as unlink3, mkdir as mkdir5 } from "fs/promises";
|
|
15234
|
+
import { resolve as resolve11, join as join15, basename as basename6, dirname as dirname8, extname as extname9 } from "path";
|
|
15235
|
+
var skills = new Hono9();
|
|
15236
|
+
function encodeId(filePath) {
|
|
15237
|
+
return Buffer.from(filePath, "utf-8").toString("base64url");
|
|
15238
|
+
}
|
|
15239
|
+
function decodeId(id) {
|
|
15240
|
+
try {
|
|
15241
|
+
const decoded = Buffer.from(id, "base64url").toString("utf-8");
|
|
15242
|
+
return decoded || null;
|
|
15243
|
+
} catch {
|
|
15244
|
+
return null;
|
|
15245
|
+
}
|
|
15246
|
+
}
|
|
15247
|
+
function listAllDirectories() {
|
|
15248
|
+
const cfg = getConfig();
|
|
15249
|
+
const discovered = discoverSkillDirectories(cfg.resolvedWorkingDirectory);
|
|
15250
|
+
const out = [];
|
|
15251
|
+
for (const { path, priority } of discovered.alwaysLoadedDirs) {
|
|
15252
|
+
out.push({
|
|
15253
|
+
path,
|
|
15254
|
+
source: pathToSource(path),
|
|
15255
|
+
label: pathToLabel(path),
|
|
15256
|
+
priority,
|
|
15257
|
+
alwaysApply: true
|
|
15258
|
+
});
|
|
15259
|
+
}
|
|
15260
|
+
for (const { path, priority } of discovered.onDemandDirs) {
|
|
15261
|
+
out.push({
|
|
15262
|
+
path,
|
|
15263
|
+
source: pathToSource(path),
|
|
15264
|
+
label: pathToLabel(path),
|
|
15265
|
+
priority,
|
|
15266
|
+
alwaysApply: false
|
|
15267
|
+
});
|
|
15268
|
+
}
|
|
15269
|
+
const additional = cfg.skills?.additionalDirectories || [];
|
|
15270
|
+
for (const dir of additional) {
|
|
15271
|
+
const abs = resolve11(cfg.resolvedWorkingDirectory, dir);
|
|
15272
|
+
if (out.some((d) => d.path === abs)) continue;
|
|
15273
|
+
out.push({
|
|
15274
|
+
path: abs,
|
|
15275
|
+
source: "additional",
|
|
15276
|
+
label: pathToLabel(abs),
|
|
15277
|
+
priority: 50,
|
|
15278
|
+
alwaysApply: false
|
|
15279
|
+
});
|
|
15280
|
+
}
|
|
15281
|
+
return out;
|
|
15282
|
+
}
|
|
15283
|
+
function pathToSource(path) {
|
|
15284
|
+
if (path.includes("/skills/default") || path.endsWith("/skills/default")) return "builtin";
|
|
15285
|
+
return "project";
|
|
15286
|
+
}
|
|
15287
|
+
function pathToLabel(path) {
|
|
15288
|
+
if (path.includes("/skills/default")) return "Built-in (default)";
|
|
15289
|
+
if (path.includes("/.sparkecoder/rules")) return ".sparkecoder/rules";
|
|
15290
|
+
if (path.includes("/.sparkecoder/skills")) return ".sparkecoder/skills";
|
|
15291
|
+
if (path.includes("/.cursor/rules")) return ".cursor/rules";
|
|
15292
|
+
if (path.includes("/.claude/skills")) return ".claude/skills";
|
|
15293
|
+
return basename6(dirname8(path)) + "/" + basename6(path);
|
|
15294
|
+
}
|
|
15295
|
+
skills.get("/", async (c) => {
|
|
15296
|
+
const dirs = listAllDirectories();
|
|
15297
|
+
const allSkills = [];
|
|
15298
|
+
for (const dir of dirs) {
|
|
15299
|
+
if (!existsSync20(dir.path)) continue;
|
|
15300
|
+
try {
|
|
15301
|
+
const list = await loadSkillsFromDirectory(dir.path, {
|
|
15302
|
+
priority: dir.priority,
|
|
15303
|
+
defaultLoadType: dir.alwaysApply ? "always" : "on_demand",
|
|
15304
|
+
forceAlwaysApply: dir.alwaysApply
|
|
15305
|
+
});
|
|
15306
|
+
for (const s of list) {
|
|
15307
|
+
let size = 0;
|
|
15308
|
+
try {
|
|
15309
|
+
size = statSync3(s.filePath).size;
|
|
15310
|
+
} catch {
|
|
15311
|
+
}
|
|
15312
|
+
allSkills.push({
|
|
15313
|
+
id: encodeId(s.filePath),
|
|
15314
|
+
name: s.name,
|
|
15315
|
+
description: s.description,
|
|
15316
|
+
filePath: s.filePath,
|
|
15317
|
+
fileName: basename6(s.filePath),
|
|
15318
|
+
sourceDir: dir.path,
|
|
15319
|
+
sourceLabel: dir.label,
|
|
15320
|
+
sourceType: dir.source,
|
|
15321
|
+
alwaysApply: s.alwaysApply,
|
|
15322
|
+
globs: s.globs,
|
|
15323
|
+
sizeBytes: size
|
|
15324
|
+
});
|
|
15325
|
+
}
|
|
15326
|
+
} catch (err) {
|
|
15327
|
+
console.warn("[skills] failed to read", dir.path, err?.message || err);
|
|
15328
|
+
}
|
|
15329
|
+
}
|
|
15330
|
+
return c.json({
|
|
15331
|
+
directories: dirs.map((d) => ({
|
|
15332
|
+
path: d.path,
|
|
15333
|
+
label: d.label,
|
|
15334
|
+
source: d.source,
|
|
15335
|
+
alwaysApply: d.alwaysApply,
|
|
15336
|
+
exists: existsSync20(d.path),
|
|
15337
|
+
writable: isWritable(d.path)
|
|
15338
|
+
})),
|
|
15339
|
+
skills: allSkills
|
|
15340
|
+
});
|
|
15341
|
+
});
|
|
15342
|
+
function isWritable(dir) {
|
|
15343
|
+
try {
|
|
15344
|
+
if (!existsSync20(dir)) return false;
|
|
15345
|
+
if (dir.includes("/skills/default")) return false;
|
|
15346
|
+
return true;
|
|
15347
|
+
} catch {
|
|
15348
|
+
return false;
|
|
15349
|
+
}
|
|
15350
|
+
}
|
|
15351
|
+
skills.get("/:id", async (c) => {
|
|
15352
|
+
const filePath = decodeId(c.req.param("id"));
|
|
15353
|
+
if (!filePath || !existsSync20(filePath)) {
|
|
15354
|
+
return c.json({ error: "skill not found" }, 404);
|
|
15355
|
+
}
|
|
15356
|
+
const content = await readFile12(filePath, "utf-8");
|
|
15357
|
+
return c.json({
|
|
15358
|
+
id: c.req.param("id"),
|
|
15359
|
+
filePath,
|
|
15360
|
+
fileName: basename6(filePath),
|
|
15361
|
+
content,
|
|
15362
|
+
writable: !filePath.includes("/skills/default")
|
|
15363
|
+
});
|
|
15364
|
+
});
|
|
15365
|
+
skills.post(
|
|
15366
|
+
"/",
|
|
15367
|
+
zValidator7("json", z22.object({
|
|
15368
|
+
directory: z22.string().min(1),
|
|
15369
|
+
fileName: z22.string().min(1),
|
|
15370
|
+
content: z22.string()
|
|
15371
|
+
})),
|
|
15372
|
+
async (c) => {
|
|
15373
|
+
const { directory, fileName, content } = c.req.valid("json");
|
|
15374
|
+
const cfg = getConfig();
|
|
15375
|
+
const targetDir = resolve11(cfg.resolvedWorkingDirectory, directory);
|
|
15376
|
+
if (targetDir.includes("/skills/default")) {
|
|
15377
|
+
return c.json({ error: "cannot write into built-in skills directory" }, 400);
|
|
15378
|
+
}
|
|
15379
|
+
const safeName = basename6(fileName).replace(/[^A-Za-z0-9._-]/g, "-");
|
|
15380
|
+
const ext = extname9(safeName).toLowerCase();
|
|
15381
|
+
const finalName = ext === ".md" || ext === ".mdc" ? safeName : `${safeName}.md`;
|
|
15382
|
+
const filePath = join15(targetDir, finalName);
|
|
15383
|
+
if (existsSync20(filePath)) {
|
|
15384
|
+
return c.json({ error: `file already exists: ${finalName}` }, 409);
|
|
15385
|
+
}
|
|
15386
|
+
try {
|
|
15387
|
+
await mkdir5(targetDir, { recursive: true });
|
|
15388
|
+
await writeFile6(filePath, content, "utf-8");
|
|
15389
|
+
} catch (err) {
|
|
15390
|
+
return c.json({ error: err?.message || "write failed" }, 500);
|
|
15391
|
+
}
|
|
15392
|
+
return c.json({ id: encodeId(filePath), filePath, fileName: finalName }, 201);
|
|
15393
|
+
}
|
|
15394
|
+
);
|
|
15395
|
+
skills.put(
|
|
15396
|
+
"/:id",
|
|
15397
|
+
zValidator7("json", z22.object({ content: z22.string() })),
|
|
15398
|
+
async (c) => {
|
|
15399
|
+
const filePath = decodeId(c.req.param("id"));
|
|
15400
|
+
if (!filePath || !existsSync20(filePath)) return c.json({ error: "skill not found" }, 404);
|
|
15401
|
+
if (filePath.includes("/skills/default")) {
|
|
15402
|
+
return c.json({ error: "built-in skills are read-only" }, 400);
|
|
15403
|
+
}
|
|
15404
|
+
await writeFile6(filePath, c.req.valid("json").content, "utf-8");
|
|
15405
|
+
return c.json({ ok: true });
|
|
15406
|
+
}
|
|
15407
|
+
);
|
|
15408
|
+
skills.delete("/:id", async (c) => {
|
|
15409
|
+
const filePath = decodeId(c.req.param("id"));
|
|
15410
|
+
if (!filePath || !existsSync20(filePath)) return c.json({ error: "skill not found" }, 404);
|
|
15411
|
+
if (filePath.includes("/skills/default")) {
|
|
15412
|
+
return c.json({ error: "built-in skills are read-only" }, 400);
|
|
15413
|
+
}
|
|
15414
|
+
await unlink3(filePath);
|
|
15415
|
+
return c.json({ ok: true });
|
|
15416
|
+
});
|
|
15417
|
+
skills.post(
|
|
15418
|
+
"/directories",
|
|
15419
|
+
zValidator7("json", z22.object({ path: z22.string().min(1) })),
|
|
15420
|
+
(c) => {
|
|
15421
|
+
const cfg = getConfig();
|
|
15422
|
+
const inputPath = c.req.valid("json").path.trim();
|
|
15423
|
+
const abs = resolve11(cfg.resolvedWorkingDirectory, inputPath);
|
|
15424
|
+
const current = cfg.skills?.additionalDirectories || [];
|
|
15425
|
+
if (current.some((d) => resolve11(cfg.resolvedWorkingDirectory, d) === abs)) {
|
|
15426
|
+
return c.json({ error: "directory already added" }, 409);
|
|
15427
|
+
}
|
|
15428
|
+
const next = [...current, abs];
|
|
15429
|
+
setSkillsAdditionalDirectories(next);
|
|
15430
|
+
return c.json({ ok: true, path: abs, exists: existsSync20(abs) }, 201);
|
|
15431
|
+
}
|
|
15432
|
+
);
|
|
15433
|
+
skills.delete("/directories", (c) => {
|
|
15434
|
+
const inputPath = c.req.query("path");
|
|
15435
|
+
if (!inputPath) return c.json({ error: "path query param required" }, 400);
|
|
15436
|
+
const cfg = getConfig();
|
|
15437
|
+
const abs = resolve11(cfg.resolvedWorkingDirectory, inputPath);
|
|
15438
|
+
const current = cfg.skills?.additionalDirectories || [];
|
|
15439
|
+
const next = current.filter((d) => resolve11(cfg.resolvedWorkingDirectory, d) !== abs);
|
|
15440
|
+
setSkillsAdditionalDirectories(next);
|
|
15441
|
+
return c.json({ ok: true });
|
|
15442
|
+
});
|
|
15443
|
+
|
|
15110
15444
|
// src/server/auth/cf-access.ts
|
|
15111
15445
|
init_config();
|
|
15112
15446
|
import { createRemoteJWKSet, jwtVerify } from "jose";
|
|
@@ -15363,13 +15697,13 @@ var DEFAULT_WEB_PORT = 6969;
|
|
|
15363
15697
|
var WEB_PORT_SEQUENCE = [6969, 6970, 6971, 6972, 6973, 6974, 6975, 6976, 6977, 6978];
|
|
15364
15698
|
function getWebDirectory() {
|
|
15365
15699
|
try {
|
|
15366
|
-
const currentDir =
|
|
15367
|
-
const webDir =
|
|
15368
|
-
if (
|
|
15700
|
+
const currentDir = dirname9(fileURLToPath4(import.meta.url));
|
|
15701
|
+
const webDir = resolve12(currentDir, "..", "web");
|
|
15702
|
+
if (existsSync21(webDir) && existsSync21(join16(webDir, "package.json"))) {
|
|
15369
15703
|
return webDir;
|
|
15370
15704
|
}
|
|
15371
|
-
const altWebDir =
|
|
15372
|
-
if (
|
|
15705
|
+
const altWebDir = resolve12(currentDir, "..", "..", "web");
|
|
15706
|
+
if (existsSync21(altWebDir) && existsSync21(join16(altWebDir, "package.json"))) {
|
|
15373
15707
|
return altWebDir;
|
|
15374
15708
|
}
|
|
15375
15709
|
return null;
|
|
@@ -15392,18 +15726,18 @@ async function isSparkcoderWebRunning(port) {
|
|
|
15392
15726
|
}
|
|
15393
15727
|
}
|
|
15394
15728
|
function isPortInUse(port) {
|
|
15395
|
-
return new Promise((
|
|
15729
|
+
return new Promise((resolve14) => {
|
|
15396
15730
|
const server = createNetServer();
|
|
15397
15731
|
server.once("error", (err) => {
|
|
15398
15732
|
if (err.code === "EADDRINUSE") {
|
|
15399
|
-
|
|
15733
|
+
resolve14(true);
|
|
15400
15734
|
} else {
|
|
15401
|
-
|
|
15735
|
+
resolve14(false);
|
|
15402
15736
|
}
|
|
15403
15737
|
});
|
|
15404
15738
|
server.once("listening", () => {
|
|
15405
15739
|
server.close();
|
|
15406
|
-
|
|
15740
|
+
resolve14(false);
|
|
15407
15741
|
});
|
|
15408
15742
|
server.listen(port, "0.0.0.0");
|
|
15409
15743
|
});
|
|
@@ -15427,30 +15761,30 @@ async function findWebPort(preferredPort) {
|
|
|
15427
15761
|
return { port: preferredPort, alreadyRunning: false };
|
|
15428
15762
|
}
|
|
15429
15763
|
function hasProductionBuild(webDir) {
|
|
15430
|
-
const buildIdPath =
|
|
15431
|
-
return
|
|
15764
|
+
const buildIdPath = join16(webDir, ".next", "BUILD_ID");
|
|
15765
|
+
return existsSync21(buildIdPath);
|
|
15432
15766
|
}
|
|
15433
15767
|
function hasSourceFiles(webDir) {
|
|
15434
|
-
const appDir =
|
|
15435
|
-
const pagesDir =
|
|
15436
|
-
const rootAppDir =
|
|
15437
|
-
const rootPagesDir =
|
|
15438
|
-
return
|
|
15768
|
+
const appDir = join16(webDir, "src", "app");
|
|
15769
|
+
const pagesDir = join16(webDir, "src", "pages");
|
|
15770
|
+
const rootAppDir = join16(webDir, "app");
|
|
15771
|
+
const rootPagesDir = join16(webDir, "pages");
|
|
15772
|
+
return existsSync21(appDir) || existsSync21(pagesDir) || existsSync21(rootAppDir) || existsSync21(rootPagesDir);
|
|
15439
15773
|
}
|
|
15440
15774
|
function getStandaloneServerPath(webDir) {
|
|
15441
15775
|
const possiblePaths2 = [
|
|
15442
|
-
|
|
15443
|
-
|
|
15776
|
+
join16(webDir, ".next", "standalone", "server.js"),
|
|
15777
|
+
join16(webDir, ".next", "standalone", "web", "server.js")
|
|
15444
15778
|
];
|
|
15445
15779
|
for (const serverPath of possiblePaths2) {
|
|
15446
|
-
if (
|
|
15780
|
+
if (existsSync21(serverPath)) {
|
|
15447
15781
|
return serverPath;
|
|
15448
15782
|
}
|
|
15449
15783
|
}
|
|
15450
15784
|
return null;
|
|
15451
15785
|
}
|
|
15452
15786
|
function runCommand(command, args, cwd, env) {
|
|
15453
|
-
return new Promise((
|
|
15787
|
+
return new Promise((resolve14) => {
|
|
15454
15788
|
const child = spawn2(command, args, {
|
|
15455
15789
|
cwd,
|
|
15456
15790
|
stdio: ["ignore", "pipe", "pipe"],
|
|
@@ -15465,10 +15799,10 @@ function runCommand(command, args, cwd, env) {
|
|
|
15465
15799
|
output += data.toString();
|
|
15466
15800
|
});
|
|
15467
15801
|
child.on("close", (code) => {
|
|
15468
|
-
|
|
15802
|
+
resolve14({ success: code === 0, output });
|
|
15469
15803
|
});
|
|
15470
15804
|
child.on("error", (err) => {
|
|
15471
|
-
|
|
15805
|
+
resolve14({ success: false, output: err.message });
|
|
15472
15806
|
});
|
|
15473
15807
|
});
|
|
15474
15808
|
}
|
|
@@ -15483,13 +15817,13 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
15483
15817
|
if (!quiet) console.log(` \u2713 Web UI already running at http://localhost:${actualPort}`);
|
|
15484
15818
|
return { process: null, port: actualPort };
|
|
15485
15819
|
}
|
|
15486
|
-
const usePnpm =
|
|
15487
|
-
const useNpm = !usePnpm &&
|
|
15820
|
+
const usePnpm = existsSync21(join16(webDir, "pnpm-lock.yaml"));
|
|
15821
|
+
const useNpm = !usePnpm && existsSync21(join16(webDir, "package-lock.json"));
|
|
15488
15822
|
const pkgManager = usePnpm ? "pnpm" : useNpm ? "npm" : "npx";
|
|
15489
15823
|
const { NODE_OPTIONS, TSX_TSCONFIG_PATH, ...cleanEnv } = process.env;
|
|
15490
15824
|
const apiUrl = publicUrl || `http://127.0.0.1:${apiPort}`;
|
|
15491
15825
|
const runtimeConfig = { apiBaseUrl: apiUrl };
|
|
15492
|
-
const runtimeConfigPath =
|
|
15826
|
+
const runtimeConfigPath = join16(webDir, "runtime-config.json");
|
|
15493
15827
|
try {
|
|
15494
15828
|
writeFileSync6(runtimeConfigPath, JSON.stringify(runtimeConfig, null, 2));
|
|
15495
15829
|
if (!quiet) console.log(` \u{1F4DD} Runtime config written to ${runtimeConfigPath}`);
|
|
@@ -15511,7 +15845,7 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
15511
15845
|
if (standaloneServerPath) {
|
|
15512
15846
|
command = "node";
|
|
15513
15847
|
args = ["server.js"];
|
|
15514
|
-
cwd =
|
|
15848
|
+
cwd = dirname9(standaloneServerPath);
|
|
15515
15849
|
webEnv.PORT = String(actualPort);
|
|
15516
15850
|
webEnv.HOSTNAME = "0.0.0.0";
|
|
15517
15851
|
if (!quiet) console.log(" \u{1F4E6} Starting Web UI from standalone build...");
|
|
@@ -15552,10 +15886,10 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
15552
15886
|
let started = false;
|
|
15553
15887
|
let exited = false;
|
|
15554
15888
|
let exitCode = null;
|
|
15555
|
-
const startedPromise = new Promise((
|
|
15889
|
+
const startedPromise = new Promise((resolve14) => {
|
|
15556
15890
|
const timeout = setTimeout(() => {
|
|
15557
15891
|
if (!started && !exited) {
|
|
15558
|
-
|
|
15892
|
+
resolve14(false);
|
|
15559
15893
|
}
|
|
15560
15894
|
}, startupTimeout);
|
|
15561
15895
|
child.stdout?.on("data", (data) => {
|
|
@@ -15569,7 +15903,7 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
15569
15903
|
if (!started && (output.includes("Ready") || output.includes("started") || output.includes("localhost"))) {
|
|
15570
15904
|
started = true;
|
|
15571
15905
|
clearTimeout(timeout);
|
|
15572
|
-
|
|
15906
|
+
resolve14(true);
|
|
15573
15907
|
}
|
|
15574
15908
|
});
|
|
15575
15909
|
child.stderr?.on("data", (data) => {
|
|
@@ -15581,14 +15915,14 @@ async function startWebUI(apiPort, webPort = DEFAULT_WEB_PORT, quiet = false, pu
|
|
|
15581
15915
|
child.on("error", (err) => {
|
|
15582
15916
|
if (!quiet) console.error(` \u274C Web UI spawn error: ${err.message}`);
|
|
15583
15917
|
clearTimeout(timeout);
|
|
15584
|
-
|
|
15918
|
+
resolve14(false);
|
|
15585
15919
|
});
|
|
15586
15920
|
child.on("exit", (code) => {
|
|
15587
15921
|
exited = true;
|
|
15588
15922
|
exitCode = code;
|
|
15589
15923
|
if (!started) {
|
|
15590
15924
|
clearTimeout(timeout);
|
|
15591
|
-
|
|
15925
|
+
resolve14(false);
|
|
15592
15926
|
}
|
|
15593
15927
|
webUIProcess = null;
|
|
15594
15928
|
});
|
|
@@ -15611,7 +15945,7 @@ function stopWebUI() {
|
|
|
15611
15945
|
}
|
|
15612
15946
|
}
|
|
15613
15947
|
async function createApp(options = {}) {
|
|
15614
|
-
const app = new
|
|
15948
|
+
const app = new Hono10();
|
|
15615
15949
|
app.use("*", cors({
|
|
15616
15950
|
origin: "*",
|
|
15617
15951
|
// Allow all origins
|
|
@@ -15639,6 +15973,7 @@ async function createApp(options = {}) {
|
|
|
15639
15973
|
app.route("/api/schedules", schedulesRouter);
|
|
15640
15974
|
app.route("/api/orchestrator", orchestratorRouter);
|
|
15641
15975
|
app.route("/api/mcp", mcpRouter);
|
|
15976
|
+
app.route("/api/skills", skills);
|
|
15642
15977
|
app.route("/api/webhooks", webhooksRouter);
|
|
15643
15978
|
const config = getConfig();
|
|
15644
15979
|
const webhookToken = config?.webhooks?.token;
|
|
@@ -15704,7 +16039,7 @@ async function startServer(options = {}) {
|
|
|
15704
16039
|
if (options.workingDirectory) {
|
|
15705
16040
|
config.resolvedWorkingDirectory = options.workingDirectory;
|
|
15706
16041
|
}
|
|
15707
|
-
if (!
|
|
16042
|
+
if (!existsSync21(config.resolvedWorkingDirectory)) {
|
|
15708
16043
|
mkdirSync9(config.resolvedWorkingDirectory, { recursive: true });
|
|
15709
16044
|
if (!options.quiet) console.log(`\u{1F4C1} Created agent workspace: ${config.resolvedWorkingDirectory}`);
|
|
15710
16045
|
}
|
|
@@ -16263,14 +16598,14 @@ function generateOpenAPISpec() {
|
|
|
16263
16598
|
init_config();
|
|
16264
16599
|
init_semantic();
|
|
16265
16600
|
init_db();
|
|
16266
|
-
import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync7, readFileSync as readFileSync10, existsSync as
|
|
16267
|
-
import { resolve as
|
|
16601
|
+
import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync7, readFileSync as readFileSync10, existsSync as existsSync22, statSync as statSync4 } from "fs";
|
|
16602
|
+
import { resolve as resolve13, join as join17 } from "path";
|
|
16268
16603
|
function getCliVersion() {
|
|
16269
|
-
const here =
|
|
16604
|
+
const here = dirname10(fileURLToPath5(import.meta.url));
|
|
16270
16605
|
const candidates = [
|
|
16271
|
-
|
|
16272
|
-
|
|
16273
|
-
|
|
16606
|
+
join17(here, "..", "package.json"),
|
|
16607
|
+
join17(here, "..", "..", "package.json"),
|
|
16608
|
+
join17(process.cwd(), "package.json")
|
|
16274
16609
|
];
|
|
16275
16610
|
for (const p of candidates) {
|
|
16276
16611
|
try {
|
|
@@ -16315,18 +16650,18 @@ async function getActiveStream(baseUrl, sessionId) {
|
|
|
16315
16650
|
return { hasActiveStream: false };
|
|
16316
16651
|
}
|
|
16317
16652
|
function promptApproval(rl, toolName, input) {
|
|
16318
|
-
return new Promise((
|
|
16653
|
+
return new Promise((resolve14) => {
|
|
16319
16654
|
const inputStr = JSON.stringify(input);
|
|
16320
16655
|
const truncatedInput = inputStr.length > 100 ? inputStr.slice(0, 100) + "..." : inputStr;
|
|
16321
16656
|
console.log(chalk.dim(` Command: ${truncatedInput}`));
|
|
16322
16657
|
rl.question(chalk.yellow(` Approve? [y/n/a(lways)]: `), (answer) => {
|
|
16323
16658
|
const lower = answer.toLowerCase().trim();
|
|
16324
16659
|
if (lower === "a" || lower === "always") {
|
|
16325
|
-
|
|
16660
|
+
resolve14("always");
|
|
16326
16661
|
} else if (lower.startsWith("y")) {
|
|
16327
|
-
|
|
16662
|
+
resolve14("approve");
|
|
16328
16663
|
} else {
|
|
16329
|
-
|
|
16664
|
+
resolve14("reject");
|
|
16330
16665
|
}
|
|
16331
16666
|
});
|
|
16332
16667
|
});
|
|
@@ -16523,9 +16858,9 @@ async function runChat(options) {
|
|
|
16523
16858
|
input: process.stdin,
|
|
16524
16859
|
output: process.stdout
|
|
16525
16860
|
});
|
|
16526
|
-
const apiKey = await new Promise((
|
|
16861
|
+
const apiKey = await new Promise((resolve14) => {
|
|
16527
16862
|
keyRl.question(chalk.cyan("Enter your AI Gateway API key: "), (answer) => {
|
|
16528
|
-
|
|
16863
|
+
resolve14(answer.trim());
|
|
16529
16864
|
});
|
|
16530
16865
|
});
|
|
16531
16866
|
keyRl.close();
|
|
@@ -16921,7 +17256,7 @@ program.command("task").description("Run an autonomous task that completes witho
|
|
|
16921
17256
|
let outputSchema;
|
|
16922
17257
|
try {
|
|
16923
17258
|
const schemaStr = options.schema;
|
|
16924
|
-
if (
|
|
17259
|
+
if (existsSync22(schemaStr)) {
|
|
16925
17260
|
outputSchema = JSON.parse(readFileSync10(schemaStr, "utf-8"));
|
|
16926
17261
|
} else {
|
|
16927
17262
|
outputSchema = JSON.parse(schemaStr);
|
|
@@ -16989,13 +17324,13 @@ program.command("init").description("Create a sparkecoder.config.json file").opt
|
|
|
16989
17324
|
let configLocation;
|
|
16990
17325
|
if (options.global) {
|
|
16991
17326
|
const appDataDir = ensureAppDataDirectory();
|
|
16992
|
-
configPath =
|
|
17327
|
+
configPath = join17(appDataDir, "sparkecoder.config.json");
|
|
16993
17328
|
configLocation = "global";
|
|
16994
17329
|
} else {
|
|
16995
|
-
configPath =
|
|
17330
|
+
configPath = resolve13(process.cwd(), "sparkecoder.config.json");
|
|
16996
17331
|
configLocation = "local";
|
|
16997
17332
|
}
|
|
16998
|
-
if (
|
|
17333
|
+
if (existsSync22(configPath) && !options.force) {
|
|
16999
17334
|
console.log(chalk.yellow("Config file already exists. Use --force to overwrite."));
|
|
17000
17335
|
console.log(chalk.dim(` ${configPath}`));
|
|
17001
17336
|
return;
|
|
@@ -17008,9 +17343,9 @@ program.command("init").description("Create a sparkecoder.config.json file").opt
|
|
|
17008
17343
|
});
|
|
17009
17344
|
program.command("slack-setup").description("Interactively configure Slack integration (bot token + signing secret)").option("--bot-token <token>", "Slack bot token (xoxb-...)").option("--signing-secret <secret>", "Slack app signing secret").option("-g, --global", "Write to global config (~/.sparkecoder/sparkecoder.config.json)").action(async (options) => {
|
|
17010
17345
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
17011
|
-
const ask = (label, fallback = "") => new Promise((
|
|
17346
|
+
const ask = (label, fallback = "") => new Promise((resolve14) => {
|
|
17012
17347
|
const suffix = fallback ? ` (${fallback})` : "";
|
|
17013
|
-
rl.question(`${label}${suffix}: `, (answer) =>
|
|
17348
|
+
rl.question(`${label}${suffix}: `, (answer) => resolve14(answer.trim() || fallback));
|
|
17014
17349
|
});
|
|
17015
17350
|
try {
|
|
17016
17351
|
const botToken = options.botToken || await ask("Slack bot token (xoxb-...)");
|
|
@@ -17020,9 +17355,9 @@ program.command("slack-setup").description("Interactively configure Slack integr
|
|
|
17020
17355
|
console.error(chalk.red("Both bot token and signing secret are required."));
|
|
17021
17356
|
process.exit(1);
|
|
17022
17357
|
}
|
|
17023
|
-
const configPath = options.global ?
|
|
17358
|
+
const configPath = options.global ? join17(ensureAppDataDirectory(), "sparkecoder.config.json") : resolve13(process.cwd(), "sparkecoder.config.json");
|
|
17024
17359
|
let existing = {};
|
|
17025
|
-
if (
|
|
17360
|
+
if (existsSync22(configPath)) {
|
|
17026
17361
|
try {
|
|
17027
17362
|
existing = JSON.parse(readFileSync10(configPath, "utf-8"));
|
|
17028
17363
|
} catch {
|
|
@@ -17288,9 +17623,9 @@ program.command("cloudflared-setup").description("Auto-detect cloudflared + set
|
|
|
17288
17623
|
}
|
|
17289
17624
|
const verOut = run("cloudflared", ["--version"]).stdout?.toString().split("\n")[0] || "installed";
|
|
17290
17625
|
console.log(chalk.green("\u2713"), "cloudflared:", chalk.dim(verOut));
|
|
17291
|
-
const cfDir =
|
|
17292
|
-
const certPath =
|
|
17293
|
-
if (!
|
|
17626
|
+
const cfDir = join17(homedir2(), ".cloudflared");
|
|
17627
|
+
const certPath = join17(cfDir, "cert.pem");
|
|
17628
|
+
if (!existsSync22(certPath)) {
|
|
17294
17629
|
console.log(chalk.yellow("No Cloudflare login cert found."));
|
|
17295
17630
|
if (await confirm("Run `cloudflared tunnel login` now (opens a browser)?", true)) {
|
|
17296
17631
|
run("cloudflared", ["tunnel", "login"], { inheritIO: true, check: true });
|
|
@@ -17334,8 +17669,8 @@ program.command("cloudflared-setup").description("Auto-detect cloudflared + set
|
|
|
17334
17669
|
return;
|
|
17335
17670
|
}
|
|
17336
17671
|
}
|
|
17337
|
-
const credsFile = tunnel.credentials_file ||
|
|
17338
|
-
if (!
|
|
17672
|
+
const credsFile = tunnel.credentials_file || join17(cfDir, `${tunnel.id}.json`);
|
|
17673
|
+
if (!existsSync22(credsFile)) {
|
|
17339
17674
|
console.log(chalk.yellow(`Credentials file not found at ${credsFile}. The tunnel may still work via cert.pem.`));
|
|
17340
17675
|
}
|
|
17341
17676
|
let hostname = options.hostname;
|
|
@@ -17358,7 +17693,7 @@ program.command("cloudflared-setup").description("Auto-detect cloudflared + set
|
|
|
17358
17693
|
console.log(chalk.yellow("DNS route warning:"), err.trim().split("\n").slice(-2).join(" "));
|
|
17359
17694
|
}
|
|
17360
17695
|
}
|
|
17361
|
-
const configPath =
|
|
17696
|
+
const configPath = join17(cfDir, "config.yml");
|
|
17362
17697
|
const configBody = `tunnel: ${tunnel.id}
|
|
17363
17698
|
credentials-file: ${credsFile}
|
|
17364
17699
|
ingress:
|
|
@@ -17369,7 +17704,7 @@ ingress:
|
|
|
17369
17704
|
- service: http_status:404
|
|
17370
17705
|
`;
|
|
17371
17706
|
let wroteConfig = false;
|
|
17372
|
-
if (
|
|
17707
|
+
if (existsSync22(configPath)) {
|
|
17373
17708
|
const existing = readFileSync10(configPath, "utf8");
|
|
17374
17709
|
if (existing.trim() === configBody.trim()) {
|
|
17375
17710
|
console.log(chalk.green("\u2713"), `config.yml already up to date: ${configPath}`);
|
|
@@ -17485,7 +17820,7 @@ program.command("config").description("Show current configuration").option("-c,
|
|
|
17485
17820
|
});
|
|
17486
17821
|
program.command("index").description("Index repository for semantic search").option("--status", "Show index status instead of indexing").option("--full", "Force full re-index (ignore existing embeddings)").option("-w, --working-dir <path>", "Working directory (defaults to current directory)").option("-c, --config <path>", "Path to config file").option("-v, --verbose", "Show detailed progress").action(async (options) => {
|
|
17487
17822
|
try {
|
|
17488
|
-
const workingDir = options.workingDir ?
|
|
17823
|
+
const workingDir = options.workingDir ? resolve13(options.workingDir) : process.cwd();
|
|
17489
17824
|
let config = loadConfig(options.config, workingDir);
|
|
17490
17825
|
const remoteUrl = config.resolvedRemoteServer.url;
|
|
17491
17826
|
if (!remoteUrl) {
|
|
@@ -17619,7 +17954,7 @@ Indexing ${chalk.cyan(namespace)}...
|
|
|
17619
17954
|
});
|
|
17620
17955
|
program.command("search").description("Search indexed repository using semantic search").argument("<query>", "Search query").option("-n, --num <count>", "Number of results to show", "5").option("-w, --working-dir <path>", "Working directory (defaults to current directory)").option("-c, --config <path>", "Path to config file").action(async (query, options) => {
|
|
17621
17956
|
try {
|
|
17622
|
-
const workingDir = options.workingDir ?
|
|
17957
|
+
const workingDir = options.workingDir ? resolve13(options.workingDir) : process.cwd();
|
|
17623
17958
|
const config = loadConfig(options.config, workingDir);
|
|
17624
17959
|
const remoteUrl = config.resolvedRemoteServer.url;
|
|
17625
17960
|
if (!remoteUrl) {
|
|
@@ -17874,10 +18209,10 @@ program.command("request-permissions").description("Open System Settings to the
|
|
|
17874
18209
|
});
|
|
17875
18210
|
{
|
|
17876
18211
|
let stateFilePath = function() {
|
|
17877
|
-
return
|
|
18212
|
+
return join17(ensureAppDataDirectory(), "recordings.json");
|
|
17878
18213
|
}, readState = function() {
|
|
17879
18214
|
const p = stateFilePath();
|
|
17880
|
-
if (!
|
|
18215
|
+
if (!existsSync22(p)) return [];
|
|
17881
18216
|
try {
|
|
17882
18217
|
return JSON.parse(readFileSync10(p, "utf-8"));
|
|
17883
18218
|
} catch {
|
|
@@ -17899,16 +18234,16 @@ program.command("request-permissions").description("Open System Settings to the
|
|
|
17899
18234
|
const record = program.command("record").description("Start/stop screen recordings");
|
|
17900
18235
|
record.command("start").description("Start a screen recording (returns id, path, pid as JSON)").option("--name <slug>", "Optional human-readable label for the recording").option("--dir <path>", "Output directory (default: ~/recordings)").action(async (opts) => {
|
|
17901
18236
|
const { homedir: homedir2, platform: osPlatform } = await import("os");
|
|
17902
|
-
const outDir = opts.dir ?
|
|
18237
|
+
const outDir = opts.dir ? resolve13(opts.dir.replace(/^~/, homedir2())) : join17(homedir2(), "recordings");
|
|
17903
18238
|
mkdirSync10(outDir, { recursive: true });
|
|
17904
18239
|
const id = `rec-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
|
|
17905
18240
|
const ext = osPlatform() === "darwin" ? "mov" : "mp4";
|
|
17906
18241
|
const filename = `${id}${opts.name ? `-${String(opts.name).replace(/[^a-z0-9-]+/gi, "-").toLowerCase()}` : ""}.${ext}`;
|
|
17907
|
-
const path =
|
|
18242
|
+
const path = join17(outDir, filename);
|
|
17908
18243
|
let cmd;
|
|
17909
18244
|
let args;
|
|
17910
18245
|
if (osPlatform() === "darwin") {
|
|
17911
|
-
cmd =
|
|
18246
|
+
cmd = existsSync22("/usr/sbin/screencapture") ? "/usr/sbin/screencapture" : "screencapture";
|
|
17912
18247
|
args = ["-v", "-C", "-k", path];
|
|
17913
18248
|
} else if (osPlatform() === "linux") {
|
|
17914
18249
|
const display = process.env.DISPLAY || ":0.0";
|
|
@@ -18011,8 +18346,8 @@ program.command("request-permissions").description("Open System Settings to the
|
|
|
18011
18346
|
}
|
|
18012
18347
|
}
|
|
18013
18348
|
writeState(rows.filter((r) => r.id !== id));
|
|
18014
|
-
const fileExists =
|
|
18015
|
-
const sizeMb = fileExists ? Math.round(
|
|
18349
|
+
const fileExists = existsSync22(row.path);
|
|
18350
|
+
const sizeMb = fileExists ? Math.round(statSync4(row.path).size / (1024 * 1024) * 10) / 10 : 0;
|
|
18016
18351
|
console.log(JSON.stringify({
|
|
18017
18352
|
id,
|
|
18018
18353
|
path: row.path,
|
|
@@ -18046,7 +18381,7 @@ program.command("request-permissions").description("Open System Settings to the
|
|
|
18046
18381
|
}
|
|
18047
18382
|
}
|
|
18048
18383
|
}
|
|
18049
|
-
stopped.push({ id: r.id, path: r.path, ok:
|
|
18384
|
+
stopped.push({ id: r.id, path: r.path, ok: existsSync22(r.path) });
|
|
18050
18385
|
}
|
|
18051
18386
|
writeState([]);
|
|
18052
18387
|
console.log(JSON.stringify({ stopped }));
|