sparkecoder 0.1.118 → 0.1.120
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 +3 -1
- package/dist/agent/index.js.map +1 -1
- package/dist/cli.js +163 -18
- 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 +162 -17
- 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 +162 -17
- 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/_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.html +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- 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 +1 -1
- 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 +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.html +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.rsc +1 -1
- 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 +1 -1
- 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 +1 -1
- package/web/.next/standalone/web/.next/server/app/agents.segments/_tree.segment.rsc +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 +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +1 -1
- 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 +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +1 -1
- 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/skills.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +1 -1
- 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 +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +1 -1
- 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.html +2 -2
- package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +1 -1
- 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 +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +1 -1
- 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 +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +1 -1
- 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 +1 -1
- package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +1 -1
- 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 +1 -1
- 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 +1 -1
- 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 +1 -1
- package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.html +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.segments/!KG1haW4p/settings/__PAGE__.segment.rsc +1 -1
- 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 +1 -1
- 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 +1 -1
- package/web/.next/standalone/web/.next/server/app/settings.segments/_tree.segment.rsc +1 -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/{T8x1J_CS0n9FaWBr5GhLe → static/uy1OnyxIm3QeGGgKEmxAj}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{T8x1J_CS0n9FaWBr5GhLe → static/uy1OnyxIm3QeGGgKEmxAj}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{T8x1J_CS0n9FaWBr5GhLe → static/uy1OnyxIm3QeGGgKEmxAj}/_ssgManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{static/T8x1J_CS0n9FaWBr5GhLe → uy1OnyxIm3QeGGgKEmxAj}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{static/T8x1J_CS0n9FaWBr5GhLe → uy1OnyxIm3QeGGgKEmxAj}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{static/T8x1J_CS0n9FaWBr5GhLe → uy1OnyxIm3QeGGgKEmxAj}/_ssgManifest.js +0 -0
- /package/web/.next/static/{T8x1J_CS0n9FaWBr5GhLe → uy1OnyxIm3QeGGgKEmxAj}/_buildManifest.js +0 -0
- /package/web/.next/static/{T8x1J_CS0n9FaWBr5GhLe → uy1OnyxIm3QeGGgKEmxAj}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/static/{T8x1J_CS0n9FaWBr5GhLe → uy1OnyxIm3QeGGgKEmxAj}/_ssgManifest.js +0 -0
package/dist/cli.js
CHANGED
|
@@ -1160,7 +1160,7 @@ function loadConfig(configPath, workingDirectory) {
|
|
|
1160
1160
|
...config,
|
|
1161
1161
|
server: {
|
|
1162
1162
|
port: config.server.port,
|
|
1163
|
-
host: config.server.host ?? "
|
|
1163
|
+
host: config.server.host ?? "0.0.0.0",
|
|
1164
1164
|
publicUrl: config.server.publicUrl
|
|
1165
1165
|
},
|
|
1166
1166
|
resolvedWorkingDirectory,
|
|
@@ -1327,7 +1327,7 @@ function createDefaultConfig() {
|
|
|
1327
1327
|
},
|
|
1328
1328
|
server: {
|
|
1329
1329
|
port: 3141,
|
|
1330
|
-
host: "
|
|
1330
|
+
host: "0.0.0.0"
|
|
1331
1331
|
},
|
|
1332
1332
|
databasePath: "./sparkecoder.db"
|
|
1333
1333
|
};
|
|
@@ -8545,9 +8545,50 @@ function isSlackConfigured() {
|
|
|
8545
8545
|
function getSlackSigningSecret() {
|
|
8546
8546
|
return readSlackConfig()?.signingSecret ?? null;
|
|
8547
8547
|
}
|
|
8548
|
+
function getSlackBotToken() {
|
|
8549
|
+
return readSlackConfig()?.botToken ?? null;
|
|
8550
|
+
}
|
|
8548
8551
|
function getDefaultOrchestratorName() {
|
|
8549
8552
|
return readSlackConfig()?.defaultOrchestratorName ?? null;
|
|
8550
8553
|
}
|
|
8554
|
+
function getCachedSlackSelfIdentity() {
|
|
8555
|
+
const cfg = readSlackConfig();
|
|
8556
|
+
if (!cfg) return null;
|
|
8557
|
+
if (cachedSelf && cachedSelf.token === cfg.botToken) return cachedSelf.identity;
|
|
8558
|
+
return null;
|
|
8559
|
+
}
|
|
8560
|
+
async function ensureSlackSelfIdentity() {
|
|
8561
|
+
const cfg = readSlackConfig();
|
|
8562
|
+
if (!cfg) return null;
|
|
8563
|
+
if (cachedSelf && cachedSelf.token === cfg.botToken) return cachedSelf.identity;
|
|
8564
|
+
if (selfInflight) return selfInflight;
|
|
8565
|
+
selfInflight = (async () => {
|
|
8566
|
+
try {
|
|
8567
|
+
const res = await fetch("https://slack.com/api/auth.test", {
|
|
8568
|
+
method: "POST",
|
|
8569
|
+
headers: { Authorization: `Bearer ${cfg.botToken}` }
|
|
8570
|
+
});
|
|
8571
|
+
const data = await res.json().catch(() => ({}));
|
|
8572
|
+
if (!data?.ok) {
|
|
8573
|
+
console.warn(`[slack] auth.test failed: ${data?.error || `HTTP ${res.status}`}`);
|
|
8574
|
+
return null;
|
|
8575
|
+
}
|
|
8576
|
+
const identity = {
|
|
8577
|
+
botUserId: String(data.user_id || ""),
|
|
8578
|
+
botId: String(data.bot_id || ""),
|
|
8579
|
+
teamId: data.team_id ? String(data.team_id) : void 0
|
|
8580
|
+
};
|
|
8581
|
+
cachedSelf = { token: cfg.botToken, identity };
|
|
8582
|
+
return identity;
|
|
8583
|
+
} catch (err) {
|
|
8584
|
+
console.warn("[slack] auth.test error:", err?.message || err);
|
|
8585
|
+
return null;
|
|
8586
|
+
} finally {
|
|
8587
|
+
selfInflight = null;
|
|
8588
|
+
}
|
|
8589
|
+
})();
|
|
8590
|
+
return selfInflight;
|
|
8591
|
+
}
|
|
8551
8592
|
function getSlackAllowlistPolicy() {
|
|
8552
8593
|
try {
|
|
8553
8594
|
const cfg = getConfig();
|
|
@@ -8561,6 +8602,62 @@ function getSlackAllowlistPolicy() {
|
|
|
8561
8602
|
return { allowedUsers: [], allowedChannels: [], allowDmsFromAnyone: true };
|
|
8562
8603
|
}
|
|
8563
8604
|
}
|
|
8605
|
+
async function fetchSlackUserName(userId) {
|
|
8606
|
+
const token = getSlackBotToken();
|
|
8607
|
+
if (!token) return null;
|
|
8608
|
+
try {
|
|
8609
|
+
const res = await fetch(`https://slack.com/api/users.info?user=${encodeURIComponent(userId)}`, {
|
|
8610
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
8611
|
+
});
|
|
8612
|
+
const data = await res.json().catch(() => ({}));
|
|
8613
|
+
if (!data?.ok) {
|
|
8614
|
+
console.warn(`[slack] users.info(${userId}) failed: ${data?.error || `HTTP ${res.status}`}`);
|
|
8615
|
+
return null;
|
|
8616
|
+
}
|
|
8617
|
+
const profile = data.user?.profile || {};
|
|
8618
|
+
const name = profile.display_name_normalized || profile.display_name || profile.real_name_normalized || profile.real_name || data.user?.real_name || data.user?.name || null;
|
|
8619
|
+
return name ? String(name) : null;
|
|
8620
|
+
} catch (err) {
|
|
8621
|
+
console.warn(`[slack] users.info(${userId}) error:`, err?.message || err);
|
|
8622
|
+
return null;
|
|
8623
|
+
}
|
|
8624
|
+
}
|
|
8625
|
+
async function resolveSlackUserName(userId) {
|
|
8626
|
+
if (!userId) return null;
|
|
8627
|
+
const now = Date.now();
|
|
8628
|
+
const hit = userNameCache.get(userId);
|
|
8629
|
+
if (hit && hit.expiresAt > now) return hit.name;
|
|
8630
|
+
const inflight = userInflight.get(userId);
|
|
8631
|
+
if (inflight) return inflight;
|
|
8632
|
+
const p = (async () => {
|
|
8633
|
+
const name = await fetchSlackUserName(userId);
|
|
8634
|
+
userNameCache.set(userId, {
|
|
8635
|
+
name,
|
|
8636
|
+
expiresAt: now + (name ? USER_TTL_MS : USER_FAIL_TTL_MS)
|
|
8637
|
+
});
|
|
8638
|
+
userInflight.delete(userId);
|
|
8639
|
+
return name;
|
|
8640
|
+
})();
|
|
8641
|
+
userInflight.set(userId, p);
|
|
8642
|
+
return p;
|
|
8643
|
+
}
|
|
8644
|
+
async function normalizeSlackMentions(text) {
|
|
8645
|
+
if (!text) return text;
|
|
8646
|
+
const userMentionRe = /<@([UW][A-Z0-9]+)(?:\|([^>]+))?>/g;
|
|
8647
|
+
const userIds = /* @__PURE__ */ new Set();
|
|
8648
|
+
for (const m of text.matchAll(userMentionRe)) {
|
|
8649
|
+
if (!m[2]) userIds.add(m[1]);
|
|
8650
|
+
}
|
|
8651
|
+
if (userIds.size > 0) {
|
|
8652
|
+
await Promise.all([...userIds].map((id) => resolveSlackUserName(id)));
|
|
8653
|
+
}
|
|
8654
|
+
return text.replace(userMentionRe, (_full, id, label) => {
|
|
8655
|
+
if (label) return `${label} <@${id}>`;
|
|
8656
|
+
const cached = userNameCache.get(id);
|
|
8657
|
+
const name = cached?.name;
|
|
8658
|
+
return name ? `${name} <@${id}>` : `<@${id}>`;
|
|
8659
|
+
});
|
|
8660
|
+
}
|
|
8564
8661
|
function getSlackDeniedReplyPolicy() {
|
|
8565
8662
|
try {
|
|
8566
8663
|
const cfg = getConfig();
|
|
@@ -8573,11 +8670,17 @@ function getSlackDeniedReplyPolicy() {
|
|
|
8573
8670
|
return { enabled: true, template: DEFAULT_DENIED_TEMPLATE };
|
|
8574
8671
|
}
|
|
8575
8672
|
}
|
|
8576
|
-
var DEFAULT_DENIED_TEMPLATE;
|
|
8673
|
+
var cachedSelf, selfInflight, USER_TTL_MS, USER_FAIL_TTL_MS, userNameCache, userInflight, DEFAULT_DENIED_TEMPLATE;
|
|
8577
8674
|
var init_client3 = __esm({
|
|
8578
8675
|
"src/integrations/slack/client.ts"() {
|
|
8579
8676
|
"use strict";
|
|
8580
8677
|
init_config();
|
|
8678
|
+
cachedSelf = null;
|
|
8679
|
+
selfInflight = null;
|
|
8680
|
+
USER_TTL_MS = 60 * 60 * 1e3;
|
|
8681
|
+
USER_FAIL_TTL_MS = 5 * 60 * 1e3;
|
|
8682
|
+
userNameCache = /* @__PURE__ */ new Map();
|
|
8683
|
+
userInflight = /* @__PURE__ */ new Map();
|
|
8581
8684
|
DEFAULT_DENIED_TEMPLATE = "Sorry, you don't have permission to use this bot. (Contact the bot owner if you think this is a mistake.)";
|
|
8582
8685
|
}
|
|
8583
8686
|
});
|
|
@@ -8592,21 +8695,36 @@ function markThreadOwned(channel, threadTs) {
|
|
|
8592
8695
|
function isThreadOwned(channel, threadTs) {
|
|
8593
8696
|
return ownedThreads.has(threadKey(channel, threadTs));
|
|
8594
8697
|
}
|
|
8595
|
-
function
|
|
8596
|
-
|
|
8698
|
+
function isSelfAuthored(event, self) {
|
|
8699
|
+
if (!self) return true;
|
|
8700
|
+
if (self.botId && event.bot_id && event.bot_id === self.botId) return true;
|
|
8701
|
+
if (self.botUserId && event.user && event.user === self.botUserId) return true;
|
|
8702
|
+
return false;
|
|
8597
8703
|
}
|
|
8598
|
-
function slackEventToInboundResult(event) {
|
|
8704
|
+
function slackEventToInboundResult(event, opts = {}) {
|
|
8599
8705
|
if (!event) return { event: null, dropReason: "empty_text" };
|
|
8600
|
-
|
|
8601
|
-
|
|
8706
|
+
const self = opts.self ?? getCachedSlackSelfIdentity();
|
|
8707
|
+
const isBotAuthored = !!event.bot_id || event.type === "message" && event.subtype === "bot_message";
|
|
8708
|
+
if (isBotAuthored && isSelfAuthored(event, self)) {
|
|
8602
8709
|
return { event: null, dropReason: "bot_message" };
|
|
8603
8710
|
}
|
|
8711
|
+
if (event.type === "message" && event.subtype && IGNORED_MESSAGE_SUBTYPES.has(event.subtype)) {
|
|
8712
|
+
return { event: null, dropReason: "ignored_subtype" };
|
|
8713
|
+
}
|
|
8604
8714
|
const isDm = event.type === "message" && event.channel_type === "im";
|
|
8605
8715
|
const isThreadReply = event.type === "message" && !isDm && typeof event.thread_ts === "string" && event.thread_ts !== event.ts;
|
|
8716
|
+
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.
|
|
8717
|
+
typeof event.channel === "string");
|
|
8606
8718
|
if (event.type !== "app_mention" && !isDm && !isThreadReply) {
|
|
8719
|
+
if (isNonThreadChannelMsg) {
|
|
8720
|
+
return { event: null, dropReason: "non_thread_channel_msg" };
|
|
8721
|
+
}
|
|
8722
|
+
if (event.type !== "message") {
|
|
8723
|
+
return { event: null, dropReason: "non_message_event" };
|
|
8724
|
+
}
|
|
8607
8725
|
return { event: null, dropReason: "unsupported_type" };
|
|
8608
8726
|
}
|
|
8609
|
-
const text =
|
|
8727
|
+
const text = (event.text ?? "").trim();
|
|
8610
8728
|
if (!text) return { event: null, dropReason: "empty_text" };
|
|
8611
8729
|
const policy = getSlackAllowlistPolicy();
|
|
8612
8730
|
const userAllowlistActive = policy.allowedUsers.length > 0;
|
|
@@ -8674,7 +8792,6 @@ var init_slack = __esm({
|
|
|
8674
8792
|
}
|
|
8675
8793
|
};
|
|
8676
8794
|
IGNORED_MESSAGE_SUBTYPES = /* @__PURE__ */ new Set([
|
|
8677
|
-
"bot_message",
|
|
8678
8795
|
"message_changed",
|
|
8679
8796
|
"message_deleted",
|
|
8680
8797
|
"channel_join",
|
|
@@ -14576,16 +14693,18 @@ init_webhook_events();
|
|
|
14576
14693
|
init_inbox();
|
|
14577
14694
|
var recentlyHandled = /* @__PURE__ */ new Map();
|
|
14578
14695
|
var MAX_RECENT = 1e3;
|
|
14579
|
-
function
|
|
14696
|
+
function wasHandled(channel, ts) {
|
|
14580
14697
|
if (!channel || !ts) return false;
|
|
14698
|
+
return recentlyHandled.has(`${channel}\u241F${ts}`);
|
|
14699
|
+
}
|
|
14700
|
+
function markHandled(channel, ts) {
|
|
14701
|
+
if (!channel || !ts) return;
|
|
14581
14702
|
const key2 = `${channel}\u241F${ts}`;
|
|
14582
|
-
if (recentlyHandled.has(key2)) return true;
|
|
14583
14703
|
recentlyHandled.set(key2, Date.now());
|
|
14584
14704
|
if (recentlyHandled.size > MAX_RECENT) {
|
|
14585
14705
|
const oldest = recentlyHandled.keys().next().value;
|
|
14586
14706
|
if (oldest) recentlyHandled.delete(oldest);
|
|
14587
14707
|
}
|
|
14588
|
-
return false;
|
|
14589
14708
|
}
|
|
14590
14709
|
var slack = new Hono6();
|
|
14591
14710
|
slack.post("/events", async (c) => {
|
|
@@ -14622,11 +14741,12 @@ slack.post("/events", async (c) => {
|
|
|
14622
14741
|
textSnippet: typeof ev.text === "string" ? ev.text : void 0,
|
|
14623
14742
|
meta: { ts: ev.ts, thread_ts: ev.thread_ts, team: ev.team, event_subtype: ev.subtype }
|
|
14624
14743
|
});
|
|
14625
|
-
if (
|
|
14744
|
+
if (wasHandled(ev.channel, ev.ts)) {
|
|
14626
14745
|
updateEvent(auditId, { status: "dropped", dropReason: "duplicate_delivery" });
|
|
14627
14746
|
return c.json({ ok: true });
|
|
14628
14747
|
}
|
|
14629
|
-
const
|
|
14748
|
+
const self = await ensureSlackSelfIdentity();
|
|
14749
|
+
const { event: inbound, dropReason } = slackEventToInboundResult(ev, { self });
|
|
14630
14750
|
if (inbound) {
|
|
14631
14751
|
const isThreadReply = ev.type === "message" && ev.channel_type !== "im" && typeof ev.thread_ts === "string" && ev.thread_ts !== ev.ts;
|
|
14632
14752
|
if (isThreadReply) {
|
|
@@ -14645,7 +14765,18 @@ slack.post("/events", async (c) => {
|
|
|
14645
14765
|
}
|
|
14646
14766
|
const orchestratorId = await findOrCreateOrchestratorId();
|
|
14647
14767
|
if (orchestratorId) {
|
|
14768
|
+
inbound.content = await normalizeSlackMentions(inbound.content);
|
|
14769
|
+
if (ev.user) {
|
|
14770
|
+
const speakerName = await resolveSlackUserName(ev.user);
|
|
14771
|
+
if (speakerName) {
|
|
14772
|
+
inbound.content = inbound.content.replace(
|
|
14773
|
+
`user=${ev.user}`,
|
|
14774
|
+
`user=${speakerName} <@${ev.user}>`
|
|
14775
|
+
);
|
|
14776
|
+
}
|
|
14777
|
+
}
|
|
14648
14778
|
pushToInbox(orchestratorId, inbound);
|
|
14779
|
+
markHandled(ev.channel, ev.ts);
|
|
14649
14780
|
updateEvent(auditId, { status: "routed", sessionId: orchestratorId });
|
|
14650
14781
|
} else {
|
|
14651
14782
|
updateEvent(auditId, { status: "error", error: "no orchestrator session available" });
|
|
@@ -15696,13 +15827,15 @@ async function startServer(options = {}) {
|
|
|
15696
15827
|
if (!options.quiet) console.warn(`[scheduler] start skipped: ${err.message}`);
|
|
15697
15828
|
}
|
|
15698
15829
|
const port = options.port || config.server.port;
|
|
15699
|
-
const
|
|
15830
|
+
const envHost = process.env.SPARKECODER_API_HOST || process.env.SPARKECODER_HOST;
|
|
15831
|
+
const host = envHost || options.host || config.server.host || "0.0.0.0";
|
|
15832
|
+
const hostSource = envHost ? process.env.SPARKECODER_API_HOST ? "env SPARKECODER_API_HOST" : "env SPARKECODER_HOST" : options.host ? "--host flag" : config.server.host ? "config.server.host" : "default";
|
|
15700
15833
|
const publicUrl = options.publicUrl || config.server.publicUrl;
|
|
15701
15834
|
const app = await createApp({ quiet: options.quiet });
|
|
15702
15835
|
if (!options.quiet) {
|
|
15703
15836
|
console.log(`
|
|
15704
15837
|
\u{1F680} SparkECoder API Server`);
|
|
15705
|
-
console.log(` \u2192
|
|
15838
|
+
console.log(` \u2192 Binding to ${host}:${port} (source: ${hostSource})`);
|
|
15706
15839
|
if (publicUrl) {
|
|
15707
15840
|
console.log(` \u2192 Public URL: ${publicUrl}`);
|
|
15708
15841
|
}
|
|
@@ -15711,10 +15844,22 @@ async function startServer(options = {}) {
|
|
|
15711
15844
|
console.log(` \u2192 OpenAPI spec: http://${host}:${port}/openapi.json
|
|
15712
15845
|
`);
|
|
15713
15846
|
}
|
|
15847
|
+
if (host === "127.0.0.1" || host === "localhost") {
|
|
15848
|
+
console.log(`[sparkecoder] \u26A0 API bound to ${host} only \u2014 not reachable from outside the machine.`);
|
|
15849
|
+
console.log(`[sparkecoder] For tunnels/reverse proxies (Modal/Fly/Docker), set --host 0.0.0.0 or SPARKECODER_API_HOST=0.0.0.0.`);
|
|
15850
|
+
}
|
|
15714
15851
|
serverInstance = serve({
|
|
15715
15852
|
fetch: app.fetch,
|
|
15716
15853
|
port,
|
|
15717
15854
|
hostname: host
|
|
15855
|
+
}, (info) => {
|
|
15856
|
+
const actual = `${info.address}:${info.port}`;
|
|
15857
|
+
const requested = `${host}:${port}`;
|
|
15858
|
+
if (info.address === "127.0.0.1" && host !== "127.0.0.1" && host !== "localhost") {
|
|
15859
|
+
console.warn(`[sparkecoder] \u2717 API listener bound to ${actual} but ${requested} was requested. External requests will be refused.`);
|
|
15860
|
+
} else {
|
|
15861
|
+
console.log(`[sparkecoder] \u2713 API listening on ${actual}`);
|
|
15862
|
+
}
|
|
15718
15863
|
});
|
|
15719
15864
|
let webPort;
|
|
15720
15865
|
let webStarted;
|
|
@@ -16782,7 +16927,7 @@ var program = new Command();
|
|
|
16782
16927
|
program.name("sparkecoder").description("AI coding agent - just type sparkecoder to start chatting").version(getCliVersion()).option("-s, --session <id>", "Resume an existing session").option("-n, --name <name>", "Name for the new session").option("-m, --model <model>", "Model to use").option("-w, --working-dir <path>", "Working directory").option("-c, --config <path>", "Path to config file").option("-p, --port <port>", "Server port", "3141").option("-H, --host <host>", "Server host", "127.0.0.1").option("--no-auto-start", "Do not auto-start server if not running").option("--web-port <port>", "Web UI port", "6969").option("--no-web", "Do not start web UI when auto-starting server").option("--public-url <url>", "Public URL for web UI to connect to API (for Docker/remote access)").option("-v, --verbose", "Enable verbose logging for web server").option("--dangerously-skip-approvals", "Auto-approve all tool calls (no confirmation prompts)").action(async (options) => {
|
|
16783
16928
|
await runChat(options);
|
|
16784
16929
|
});
|
|
16785
|
-
program.command("server").description("Start the SparkECoder server (API + Web UI)").option("-p, --port <port>", "API server port", "3141").option("-h, --host <host>", "Server
|
|
16930
|
+
program.command("server").description("Start the SparkECoder server (API + Web UI)").option("-p, --port <port>", "API server port", "3141").option("-h, --host <host>", "Server bind address (use 127.0.0.1 to restrict to loopback). Overridable via SPARKECODER_API_HOST / SPARKECODER_HOST env.", "0.0.0.0").option("-c, --config <path>", "Path to config file").option("-w, --working-dir <path>", "Working directory").option("--web-port <port>", "Web UI port", "6969").option("--no-web", "Do not start web UI").option("--public-url <url>", "Public URL for web UI to connect to API (for Docker/remote access)").option("-v, --verbose", "Enable verbose logging for web server").option("--setup-secret <secret>", "Setup secret (or short-lived JWT) used for /auth/register and /tunnels when the remote server has SETUP_SECRET configured. Equivalent to setting SPARKECODER_SETUP_SECRET env.").action(async (options) => {
|
|
16786
16931
|
if (options.setupSecret) {
|
|
16787
16932
|
process.env.SPARKECODER_SETUP_SECRET = options.setupSecret;
|
|
16788
16933
|
if (!process.env.SPARKECODER_TUNNEL_SECRET) {
|