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/index.js
CHANGED
|
@@ -646,7 +646,7 @@ function loadConfig(configPath, workingDirectory) {
|
|
|
646
646
|
...config,
|
|
647
647
|
server: {
|
|
648
648
|
port: config.server.port,
|
|
649
|
-
host: config.server.host ?? "
|
|
649
|
+
host: config.server.host ?? "0.0.0.0",
|
|
650
650
|
publicUrl: config.server.publicUrl
|
|
651
651
|
},
|
|
652
652
|
resolvedWorkingDirectory,
|
|
@@ -813,7 +813,7 @@ function createDefaultConfig() {
|
|
|
813
813
|
},
|
|
814
814
|
server: {
|
|
815
815
|
port: 3141,
|
|
816
|
-
host: "
|
|
816
|
+
host: "0.0.0.0"
|
|
817
817
|
},
|
|
818
818
|
databasePath: "./sparkecoder.db"
|
|
819
819
|
};
|
|
@@ -7819,9 +7819,50 @@ function isSlackConfigured() {
|
|
|
7819
7819
|
function getSlackSigningSecret() {
|
|
7820
7820
|
return readSlackConfig()?.signingSecret ?? null;
|
|
7821
7821
|
}
|
|
7822
|
+
function getSlackBotToken() {
|
|
7823
|
+
return readSlackConfig()?.botToken ?? null;
|
|
7824
|
+
}
|
|
7822
7825
|
function getDefaultOrchestratorName() {
|
|
7823
7826
|
return readSlackConfig()?.defaultOrchestratorName ?? null;
|
|
7824
7827
|
}
|
|
7828
|
+
function getCachedSlackSelfIdentity() {
|
|
7829
|
+
const cfg = readSlackConfig();
|
|
7830
|
+
if (!cfg) return null;
|
|
7831
|
+
if (cachedSelf && cachedSelf.token === cfg.botToken) return cachedSelf.identity;
|
|
7832
|
+
return null;
|
|
7833
|
+
}
|
|
7834
|
+
async function ensureSlackSelfIdentity() {
|
|
7835
|
+
const cfg = readSlackConfig();
|
|
7836
|
+
if (!cfg) return null;
|
|
7837
|
+
if (cachedSelf && cachedSelf.token === cfg.botToken) return cachedSelf.identity;
|
|
7838
|
+
if (selfInflight) return selfInflight;
|
|
7839
|
+
selfInflight = (async () => {
|
|
7840
|
+
try {
|
|
7841
|
+
const res = await fetch("https://slack.com/api/auth.test", {
|
|
7842
|
+
method: "POST",
|
|
7843
|
+
headers: { Authorization: `Bearer ${cfg.botToken}` }
|
|
7844
|
+
});
|
|
7845
|
+
const data = await res.json().catch(() => ({}));
|
|
7846
|
+
if (!data?.ok) {
|
|
7847
|
+
console.warn(`[slack] auth.test failed: ${data?.error || `HTTP ${res.status}`}`);
|
|
7848
|
+
return null;
|
|
7849
|
+
}
|
|
7850
|
+
const identity = {
|
|
7851
|
+
botUserId: String(data.user_id || ""),
|
|
7852
|
+
botId: String(data.bot_id || ""),
|
|
7853
|
+
teamId: data.team_id ? String(data.team_id) : void 0
|
|
7854
|
+
};
|
|
7855
|
+
cachedSelf = { token: cfg.botToken, identity };
|
|
7856
|
+
return identity;
|
|
7857
|
+
} catch (err) {
|
|
7858
|
+
console.warn("[slack] auth.test error:", err?.message || err);
|
|
7859
|
+
return null;
|
|
7860
|
+
} finally {
|
|
7861
|
+
selfInflight = null;
|
|
7862
|
+
}
|
|
7863
|
+
})();
|
|
7864
|
+
return selfInflight;
|
|
7865
|
+
}
|
|
7825
7866
|
function getSlackAllowlistPolicy() {
|
|
7826
7867
|
try {
|
|
7827
7868
|
const cfg = getConfig();
|
|
@@ -7835,6 +7876,62 @@ function getSlackAllowlistPolicy() {
|
|
|
7835
7876
|
return { allowedUsers: [], allowedChannels: [], allowDmsFromAnyone: true };
|
|
7836
7877
|
}
|
|
7837
7878
|
}
|
|
7879
|
+
async function fetchSlackUserName(userId) {
|
|
7880
|
+
const token = getSlackBotToken();
|
|
7881
|
+
if (!token) return null;
|
|
7882
|
+
try {
|
|
7883
|
+
const res = await fetch(`https://slack.com/api/users.info?user=${encodeURIComponent(userId)}`, {
|
|
7884
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
7885
|
+
});
|
|
7886
|
+
const data = await res.json().catch(() => ({}));
|
|
7887
|
+
if (!data?.ok) {
|
|
7888
|
+
console.warn(`[slack] users.info(${userId}) failed: ${data?.error || `HTTP ${res.status}`}`);
|
|
7889
|
+
return null;
|
|
7890
|
+
}
|
|
7891
|
+
const profile = data.user?.profile || {};
|
|
7892
|
+
const name = profile.display_name_normalized || profile.display_name || profile.real_name_normalized || profile.real_name || data.user?.real_name || data.user?.name || null;
|
|
7893
|
+
return name ? String(name) : null;
|
|
7894
|
+
} catch (err) {
|
|
7895
|
+
console.warn(`[slack] users.info(${userId}) error:`, err?.message || err);
|
|
7896
|
+
return null;
|
|
7897
|
+
}
|
|
7898
|
+
}
|
|
7899
|
+
async function resolveSlackUserName(userId) {
|
|
7900
|
+
if (!userId) return null;
|
|
7901
|
+
const now = Date.now();
|
|
7902
|
+
const hit = userNameCache.get(userId);
|
|
7903
|
+
if (hit && hit.expiresAt > now) return hit.name;
|
|
7904
|
+
const inflight = userInflight.get(userId);
|
|
7905
|
+
if (inflight) return inflight;
|
|
7906
|
+
const p = (async () => {
|
|
7907
|
+
const name = await fetchSlackUserName(userId);
|
|
7908
|
+
userNameCache.set(userId, {
|
|
7909
|
+
name,
|
|
7910
|
+
expiresAt: now + (name ? USER_TTL_MS : USER_FAIL_TTL_MS)
|
|
7911
|
+
});
|
|
7912
|
+
userInflight.delete(userId);
|
|
7913
|
+
return name;
|
|
7914
|
+
})();
|
|
7915
|
+
userInflight.set(userId, p);
|
|
7916
|
+
return p;
|
|
7917
|
+
}
|
|
7918
|
+
async function normalizeSlackMentions(text) {
|
|
7919
|
+
if (!text) return text;
|
|
7920
|
+
const userMentionRe = /<@([UW][A-Z0-9]+)(?:\|([^>]+))?>/g;
|
|
7921
|
+
const userIds = /* @__PURE__ */ new Set();
|
|
7922
|
+
for (const m of text.matchAll(userMentionRe)) {
|
|
7923
|
+
if (!m[2]) userIds.add(m[1]);
|
|
7924
|
+
}
|
|
7925
|
+
if (userIds.size > 0) {
|
|
7926
|
+
await Promise.all([...userIds].map((id) => resolveSlackUserName(id)));
|
|
7927
|
+
}
|
|
7928
|
+
return text.replace(userMentionRe, (_full, id, label) => {
|
|
7929
|
+
if (label) return `${label} <@${id}>`;
|
|
7930
|
+
const cached = userNameCache.get(id);
|
|
7931
|
+
const name = cached?.name;
|
|
7932
|
+
return name ? `${name} <@${id}>` : `<@${id}>`;
|
|
7933
|
+
});
|
|
7934
|
+
}
|
|
7838
7935
|
function getSlackDeniedReplyPolicy() {
|
|
7839
7936
|
try {
|
|
7840
7937
|
const cfg = getConfig();
|
|
@@ -7847,11 +7944,17 @@ function getSlackDeniedReplyPolicy() {
|
|
|
7847
7944
|
return { enabled: true, template: DEFAULT_DENIED_TEMPLATE };
|
|
7848
7945
|
}
|
|
7849
7946
|
}
|
|
7850
|
-
var DEFAULT_DENIED_TEMPLATE;
|
|
7947
|
+
var cachedSelf, selfInflight, USER_TTL_MS, USER_FAIL_TTL_MS, userNameCache, userInflight, DEFAULT_DENIED_TEMPLATE;
|
|
7851
7948
|
var init_client3 = __esm({
|
|
7852
7949
|
"src/integrations/slack/client.ts"() {
|
|
7853
7950
|
"use strict";
|
|
7854
7951
|
init_config();
|
|
7952
|
+
cachedSelf = null;
|
|
7953
|
+
selfInflight = null;
|
|
7954
|
+
USER_TTL_MS = 60 * 60 * 1e3;
|
|
7955
|
+
USER_FAIL_TTL_MS = 5 * 60 * 1e3;
|
|
7956
|
+
userNameCache = /* @__PURE__ */ new Map();
|
|
7957
|
+
userInflight = /* @__PURE__ */ new Map();
|
|
7855
7958
|
DEFAULT_DENIED_TEMPLATE = "Sorry, you don't have permission to use this bot. (Contact the bot owner if you think this is a mistake.)";
|
|
7856
7959
|
}
|
|
7857
7960
|
});
|
|
@@ -7866,21 +7969,36 @@ function markThreadOwned(channel, threadTs) {
|
|
|
7866
7969
|
function isThreadOwned(channel, threadTs) {
|
|
7867
7970
|
return ownedThreads.has(threadKey(channel, threadTs));
|
|
7868
7971
|
}
|
|
7869
|
-
function
|
|
7870
|
-
|
|
7972
|
+
function isSelfAuthored(event, self) {
|
|
7973
|
+
if (!self) return true;
|
|
7974
|
+
if (self.botId && event.bot_id && event.bot_id === self.botId) return true;
|
|
7975
|
+
if (self.botUserId && event.user && event.user === self.botUserId) return true;
|
|
7976
|
+
return false;
|
|
7871
7977
|
}
|
|
7872
|
-
function slackEventToInboundResult(event) {
|
|
7978
|
+
function slackEventToInboundResult(event, opts = {}) {
|
|
7873
7979
|
if (!event) return { event: null, dropReason: "empty_text" };
|
|
7874
|
-
|
|
7875
|
-
|
|
7980
|
+
const self = opts.self ?? getCachedSlackSelfIdentity();
|
|
7981
|
+
const isBotAuthored = !!event.bot_id || event.type === "message" && event.subtype === "bot_message";
|
|
7982
|
+
if (isBotAuthored && isSelfAuthored(event, self)) {
|
|
7876
7983
|
return { event: null, dropReason: "bot_message" };
|
|
7877
7984
|
}
|
|
7985
|
+
if (event.type === "message" && event.subtype && IGNORED_MESSAGE_SUBTYPES.has(event.subtype)) {
|
|
7986
|
+
return { event: null, dropReason: "ignored_subtype" };
|
|
7987
|
+
}
|
|
7878
7988
|
const isDm = event.type === "message" && event.channel_type === "im";
|
|
7879
7989
|
const isThreadReply = event.type === "message" && !isDm && typeof event.thread_ts === "string" && event.thread_ts !== event.ts;
|
|
7990
|
+
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.
|
|
7991
|
+
typeof event.channel === "string");
|
|
7880
7992
|
if (event.type !== "app_mention" && !isDm && !isThreadReply) {
|
|
7993
|
+
if (isNonThreadChannelMsg) {
|
|
7994
|
+
return { event: null, dropReason: "non_thread_channel_msg" };
|
|
7995
|
+
}
|
|
7996
|
+
if (event.type !== "message") {
|
|
7997
|
+
return { event: null, dropReason: "non_message_event" };
|
|
7998
|
+
}
|
|
7881
7999
|
return { event: null, dropReason: "unsupported_type" };
|
|
7882
8000
|
}
|
|
7883
|
-
const text =
|
|
8001
|
+
const text = (event.text ?? "").trim();
|
|
7884
8002
|
if (!text) return { event: null, dropReason: "empty_text" };
|
|
7885
8003
|
const policy = getSlackAllowlistPolicy();
|
|
7886
8004
|
const userAllowlistActive = policy.allowedUsers.length > 0;
|
|
@@ -7948,7 +8066,6 @@ var init_slack = __esm({
|
|
|
7948
8066
|
}
|
|
7949
8067
|
};
|
|
7950
8068
|
IGNORED_MESSAGE_SUBTYPES = /* @__PURE__ */ new Set([
|
|
7951
|
-
"bot_message",
|
|
7952
8069
|
"message_changed",
|
|
7953
8070
|
"message_deleted",
|
|
7954
8071
|
"channel_join",
|
|
@@ -13625,16 +13742,18 @@ init_webhook_events();
|
|
|
13625
13742
|
init_inbox();
|
|
13626
13743
|
var recentlyHandled = /* @__PURE__ */ new Map();
|
|
13627
13744
|
var MAX_RECENT = 1e3;
|
|
13628
|
-
function
|
|
13745
|
+
function wasHandled(channel, ts) {
|
|
13629
13746
|
if (!channel || !ts) return false;
|
|
13747
|
+
return recentlyHandled.has(`${channel}\u241F${ts}`);
|
|
13748
|
+
}
|
|
13749
|
+
function markHandled(channel, ts) {
|
|
13750
|
+
if (!channel || !ts) return;
|
|
13630
13751
|
const key2 = `${channel}\u241F${ts}`;
|
|
13631
|
-
if (recentlyHandled.has(key2)) return true;
|
|
13632
13752
|
recentlyHandled.set(key2, Date.now());
|
|
13633
13753
|
if (recentlyHandled.size > MAX_RECENT) {
|
|
13634
13754
|
const oldest = recentlyHandled.keys().next().value;
|
|
13635
13755
|
if (oldest) recentlyHandled.delete(oldest);
|
|
13636
13756
|
}
|
|
13637
|
-
return false;
|
|
13638
13757
|
}
|
|
13639
13758
|
var slack = new Hono6();
|
|
13640
13759
|
slack.post("/events", async (c) => {
|
|
@@ -13671,11 +13790,12 @@ slack.post("/events", async (c) => {
|
|
|
13671
13790
|
textSnippet: typeof ev.text === "string" ? ev.text : void 0,
|
|
13672
13791
|
meta: { ts: ev.ts, thread_ts: ev.thread_ts, team: ev.team, event_subtype: ev.subtype }
|
|
13673
13792
|
});
|
|
13674
|
-
if (
|
|
13793
|
+
if (wasHandled(ev.channel, ev.ts)) {
|
|
13675
13794
|
updateEvent(auditId, { status: "dropped", dropReason: "duplicate_delivery" });
|
|
13676
13795
|
return c.json({ ok: true });
|
|
13677
13796
|
}
|
|
13678
|
-
const
|
|
13797
|
+
const self = await ensureSlackSelfIdentity();
|
|
13798
|
+
const { event: inbound, dropReason } = slackEventToInboundResult(ev, { self });
|
|
13679
13799
|
if (inbound) {
|
|
13680
13800
|
const isThreadReply = ev.type === "message" && ev.channel_type !== "im" && typeof ev.thread_ts === "string" && ev.thread_ts !== ev.ts;
|
|
13681
13801
|
if (isThreadReply) {
|
|
@@ -13694,7 +13814,18 @@ slack.post("/events", async (c) => {
|
|
|
13694
13814
|
}
|
|
13695
13815
|
const orchestratorId = await findOrCreateOrchestratorId();
|
|
13696
13816
|
if (orchestratorId) {
|
|
13817
|
+
inbound.content = await normalizeSlackMentions(inbound.content);
|
|
13818
|
+
if (ev.user) {
|
|
13819
|
+
const speakerName = await resolveSlackUserName(ev.user);
|
|
13820
|
+
if (speakerName) {
|
|
13821
|
+
inbound.content = inbound.content.replace(
|
|
13822
|
+
`user=${ev.user}`,
|
|
13823
|
+
`user=${speakerName} <@${ev.user}>`
|
|
13824
|
+
);
|
|
13825
|
+
}
|
|
13826
|
+
}
|
|
13697
13827
|
pushToInbox(orchestratorId, inbound);
|
|
13828
|
+
markHandled(ev.channel, ev.ts);
|
|
13698
13829
|
updateEvent(auditId, { status: "routed", sessionId: orchestratorId });
|
|
13699
13830
|
} else {
|
|
13700
13831
|
updateEvent(auditId, { status: "error", error: "no orchestrator session available" });
|
|
@@ -14649,13 +14780,15 @@ async function startServer(options = {}) {
|
|
|
14649
14780
|
if (!options.quiet) console.warn(`[scheduler] start skipped: ${err.message}`);
|
|
14650
14781
|
}
|
|
14651
14782
|
const port = options.port || config.server.port;
|
|
14652
|
-
const
|
|
14783
|
+
const envHost = process.env.SPARKECODER_API_HOST || process.env.SPARKECODER_HOST;
|
|
14784
|
+
const host = envHost || options.host || config.server.host || "0.0.0.0";
|
|
14785
|
+
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";
|
|
14653
14786
|
const publicUrl = options.publicUrl || config.server.publicUrl;
|
|
14654
14787
|
const app = await createApp({ quiet: options.quiet });
|
|
14655
14788
|
if (!options.quiet) {
|
|
14656
14789
|
console.log(`
|
|
14657
14790
|
\u{1F680} SparkECoder API Server`);
|
|
14658
|
-
console.log(` \u2192
|
|
14791
|
+
console.log(` \u2192 Binding to ${host}:${port} (source: ${hostSource})`);
|
|
14659
14792
|
if (publicUrl) {
|
|
14660
14793
|
console.log(` \u2192 Public URL: ${publicUrl}`);
|
|
14661
14794
|
}
|
|
@@ -14664,10 +14797,22 @@ async function startServer(options = {}) {
|
|
|
14664
14797
|
console.log(` \u2192 OpenAPI spec: http://${host}:${port}/openapi.json
|
|
14665
14798
|
`);
|
|
14666
14799
|
}
|
|
14800
|
+
if (host === "127.0.0.1" || host === "localhost") {
|
|
14801
|
+
console.log(`[sparkecoder] \u26A0 API bound to ${host} only \u2014 not reachable from outside the machine.`);
|
|
14802
|
+
console.log(`[sparkecoder] For tunnels/reverse proxies (Modal/Fly/Docker), set --host 0.0.0.0 or SPARKECODER_API_HOST=0.0.0.0.`);
|
|
14803
|
+
}
|
|
14667
14804
|
serverInstance = serve({
|
|
14668
14805
|
fetch: app.fetch,
|
|
14669
14806
|
port,
|
|
14670
14807
|
hostname: host
|
|
14808
|
+
}, (info) => {
|
|
14809
|
+
const actual = `${info.address}:${info.port}`;
|
|
14810
|
+
const requested = `${host}:${port}`;
|
|
14811
|
+
if (info.address === "127.0.0.1" && host !== "127.0.0.1" && host !== "localhost") {
|
|
14812
|
+
console.warn(`[sparkecoder] \u2717 API listener bound to ${actual} but ${requested} was requested. External requests will be refused.`);
|
|
14813
|
+
} else {
|
|
14814
|
+
console.log(`[sparkecoder] \u2713 API listening on ${actual}`);
|
|
14815
|
+
}
|
|
14671
14816
|
});
|
|
14672
14817
|
let webPort;
|
|
14673
14818
|
let webStarted;
|