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
|
@@ -85,7 +85,7 @@ declare const sessions: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
|
|
|
85
85
|
tableName: "sessions";
|
|
86
86
|
dataType: "string";
|
|
87
87
|
columnType: "SQLiteText";
|
|
88
|
-
data: "
|
|
88
|
+
data: "completed" | "error" | "active" | "waiting";
|
|
89
89
|
driverParam: string;
|
|
90
90
|
notNull: true;
|
|
91
91
|
hasDefault: true;
|
|
@@ -391,7 +391,7 @@ declare const toolExecutions: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
|
|
|
391
391
|
tableName: "tool_executions";
|
|
392
392
|
dataType: "string";
|
|
393
393
|
columnType: "SQLiteText";
|
|
394
|
-
data: "
|
|
394
|
+
data: "completed" | "error" | "pending" | "approved" | "rejected";
|
|
395
395
|
driverParam: string;
|
|
396
396
|
notNull: true;
|
|
397
397
|
hasDefault: true;
|
|
@@ -814,7 +814,7 @@ declare const terminals: drizzle_orm_sqlite_core.SQLiteTableWithColumns<{
|
|
|
814
814
|
tableName: "terminals";
|
|
815
815
|
dataType: "string";
|
|
816
816
|
columnType: "SQLiteText";
|
|
817
|
-
data: "
|
|
817
|
+
data: "running" | "error" | "stopped";
|
|
818
818
|
driverParam: string;
|
|
819
819
|
notNull: true;
|
|
820
820
|
hasDefault: true;
|
|
@@ -16,11 +16,11 @@ interface BashToolOptions {
|
|
|
16
16
|
declare function createBashTool(options: BashToolOptions): ai.Tool<{
|
|
17
17
|
background: boolean;
|
|
18
18
|
id?: string | undefined;
|
|
19
|
-
command?: string | undefined;
|
|
20
19
|
input?: string | undefined;
|
|
20
|
+
command?: string | undefined;
|
|
21
21
|
kill?: boolean | undefined;
|
|
22
22
|
tail?: number | undefined;
|
|
23
|
-
key?: "
|
|
23
|
+
key?: "Enter" | "Escape" | "Up" | "Down" | "Left" | "Right" | "Tab" | "C-c" | "C-d" | "y" | "n" | undefined;
|
|
24
24
|
}, {
|
|
25
25
|
success: boolean;
|
|
26
26
|
id: string;
|
|
@@ -66,7 +66,7 @@ declare function createBashTool(options: BashToolOptions): ai.Tool<{
|
|
|
66
66
|
id: string;
|
|
67
67
|
output: string;
|
|
68
68
|
exitCode: number;
|
|
69
|
-
status: "
|
|
69
|
+
status: "running" | "completed" | "error" | "stopped";
|
|
70
70
|
message?: undefined;
|
|
71
71
|
error?: undefined;
|
|
72
72
|
} | {
|
|
@@ -218,8 +218,8 @@ interface SearchToolOptions {
|
|
|
218
218
|
* Progress is streamed back to the UI so users can see exploration happening.
|
|
219
219
|
*/
|
|
220
220
|
declare function createSearchTool(options: SearchToolOptions): ai.Tool<{
|
|
221
|
-
context: string;
|
|
222
221
|
query: string;
|
|
222
|
+
context: string;
|
|
223
223
|
}, {
|
|
224
224
|
success: boolean;
|
|
225
225
|
error: string;
|
package/dist/server/index.js
CHANGED
|
@@ -1159,7 +1159,7 @@ function loadConfig(configPath, workingDirectory) {
|
|
|
1159
1159
|
...config,
|
|
1160
1160
|
server: {
|
|
1161
1161
|
port: config.server.port,
|
|
1162
|
-
host: config.server.host ?? "
|
|
1162
|
+
host: config.server.host ?? "0.0.0.0",
|
|
1163
1163
|
publicUrl: config.server.publicUrl
|
|
1164
1164
|
},
|
|
1165
1165
|
resolvedWorkingDirectory,
|
|
@@ -1326,7 +1326,7 @@ function createDefaultConfig() {
|
|
|
1326
1326
|
},
|
|
1327
1327
|
server: {
|
|
1328
1328
|
port: 3141,
|
|
1329
|
-
host: "
|
|
1329
|
+
host: "0.0.0.0"
|
|
1330
1330
|
},
|
|
1331
1331
|
databasePath: "./sparkecoder.db"
|
|
1332
1332
|
};
|
|
@@ -7802,9 +7802,50 @@ function isSlackConfigured() {
|
|
|
7802
7802
|
function getSlackSigningSecret() {
|
|
7803
7803
|
return readSlackConfig()?.signingSecret ?? null;
|
|
7804
7804
|
}
|
|
7805
|
+
function getSlackBotToken() {
|
|
7806
|
+
return readSlackConfig()?.botToken ?? null;
|
|
7807
|
+
}
|
|
7805
7808
|
function getDefaultOrchestratorName() {
|
|
7806
7809
|
return readSlackConfig()?.defaultOrchestratorName ?? null;
|
|
7807
7810
|
}
|
|
7811
|
+
function getCachedSlackSelfIdentity() {
|
|
7812
|
+
const cfg = readSlackConfig();
|
|
7813
|
+
if (!cfg) return null;
|
|
7814
|
+
if (cachedSelf && cachedSelf.token === cfg.botToken) return cachedSelf.identity;
|
|
7815
|
+
return null;
|
|
7816
|
+
}
|
|
7817
|
+
async function ensureSlackSelfIdentity() {
|
|
7818
|
+
const cfg = readSlackConfig();
|
|
7819
|
+
if (!cfg) return null;
|
|
7820
|
+
if (cachedSelf && cachedSelf.token === cfg.botToken) return cachedSelf.identity;
|
|
7821
|
+
if (selfInflight) return selfInflight;
|
|
7822
|
+
selfInflight = (async () => {
|
|
7823
|
+
try {
|
|
7824
|
+
const res = await fetch("https://slack.com/api/auth.test", {
|
|
7825
|
+
method: "POST",
|
|
7826
|
+
headers: { Authorization: `Bearer ${cfg.botToken}` }
|
|
7827
|
+
});
|
|
7828
|
+
const data = await res.json().catch(() => ({}));
|
|
7829
|
+
if (!data?.ok) {
|
|
7830
|
+
console.warn(`[slack] auth.test failed: ${data?.error || `HTTP ${res.status}`}`);
|
|
7831
|
+
return null;
|
|
7832
|
+
}
|
|
7833
|
+
const identity = {
|
|
7834
|
+
botUserId: String(data.user_id || ""),
|
|
7835
|
+
botId: String(data.bot_id || ""),
|
|
7836
|
+
teamId: data.team_id ? String(data.team_id) : void 0
|
|
7837
|
+
};
|
|
7838
|
+
cachedSelf = { token: cfg.botToken, identity };
|
|
7839
|
+
return identity;
|
|
7840
|
+
} catch (err) {
|
|
7841
|
+
console.warn("[slack] auth.test error:", err?.message || err);
|
|
7842
|
+
return null;
|
|
7843
|
+
} finally {
|
|
7844
|
+
selfInflight = null;
|
|
7845
|
+
}
|
|
7846
|
+
})();
|
|
7847
|
+
return selfInflight;
|
|
7848
|
+
}
|
|
7808
7849
|
function getSlackAllowlistPolicy() {
|
|
7809
7850
|
try {
|
|
7810
7851
|
const cfg = getConfig();
|
|
@@ -7818,6 +7859,62 @@ function getSlackAllowlistPolicy() {
|
|
|
7818
7859
|
return { allowedUsers: [], allowedChannels: [], allowDmsFromAnyone: true };
|
|
7819
7860
|
}
|
|
7820
7861
|
}
|
|
7862
|
+
async function fetchSlackUserName(userId) {
|
|
7863
|
+
const token = getSlackBotToken();
|
|
7864
|
+
if (!token) return null;
|
|
7865
|
+
try {
|
|
7866
|
+
const res = await fetch(`https://slack.com/api/users.info?user=${encodeURIComponent(userId)}`, {
|
|
7867
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
7868
|
+
});
|
|
7869
|
+
const data = await res.json().catch(() => ({}));
|
|
7870
|
+
if (!data?.ok) {
|
|
7871
|
+
console.warn(`[slack] users.info(${userId}) failed: ${data?.error || `HTTP ${res.status}`}`);
|
|
7872
|
+
return null;
|
|
7873
|
+
}
|
|
7874
|
+
const profile = data.user?.profile || {};
|
|
7875
|
+
const name = profile.display_name_normalized || profile.display_name || profile.real_name_normalized || profile.real_name || data.user?.real_name || data.user?.name || null;
|
|
7876
|
+
return name ? String(name) : null;
|
|
7877
|
+
} catch (err) {
|
|
7878
|
+
console.warn(`[slack] users.info(${userId}) error:`, err?.message || err);
|
|
7879
|
+
return null;
|
|
7880
|
+
}
|
|
7881
|
+
}
|
|
7882
|
+
async function resolveSlackUserName(userId) {
|
|
7883
|
+
if (!userId) return null;
|
|
7884
|
+
const now = Date.now();
|
|
7885
|
+
const hit = userNameCache.get(userId);
|
|
7886
|
+
if (hit && hit.expiresAt > now) return hit.name;
|
|
7887
|
+
const inflight = userInflight.get(userId);
|
|
7888
|
+
if (inflight) return inflight;
|
|
7889
|
+
const p = (async () => {
|
|
7890
|
+
const name = await fetchSlackUserName(userId);
|
|
7891
|
+
userNameCache.set(userId, {
|
|
7892
|
+
name,
|
|
7893
|
+
expiresAt: now + (name ? USER_TTL_MS : USER_FAIL_TTL_MS)
|
|
7894
|
+
});
|
|
7895
|
+
userInflight.delete(userId);
|
|
7896
|
+
return name;
|
|
7897
|
+
})();
|
|
7898
|
+
userInflight.set(userId, p);
|
|
7899
|
+
return p;
|
|
7900
|
+
}
|
|
7901
|
+
async function normalizeSlackMentions(text) {
|
|
7902
|
+
if (!text) return text;
|
|
7903
|
+
const userMentionRe = /<@([UW][A-Z0-9]+)(?:\|([^>]+))?>/g;
|
|
7904
|
+
const userIds = /* @__PURE__ */ new Set();
|
|
7905
|
+
for (const m of text.matchAll(userMentionRe)) {
|
|
7906
|
+
if (!m[2]) userIds.add(m[1]);
|
|
7907
|
+
}
|
|
7908
|
+
if (userIds.size > 0) {
|
|
7909
|
+
await Promise.all([...userIds].map((id) => resolveSlackUserName(id)));
|
|
7910
|
+
}
|
|
7911
|
+
return text.replace(userMentionRe, (_full, id, label) => {
|
|
7912
|
+
if (label) return `${label} <@${id}>`;
|
|
7913
|
+
const cached = userNameCache.get(id);
|
|
7914
|
+
const name = cached?.name;
|
|
7915
|
+
return name ? `${name} <@${id}>` : `<@${id}>`;
|
|
7916
|
+
});
|
|
7917
|
+
}
|
|
7821
7918
|
function getSlackDeniedReplyPolicy() {
|
|
7822
7919
|
try {
|
|
7823
7920
|
const cfg = getConfig();
|
|
@@ -7830,11 +7927,17 @@ function getSlackDeniedReplyPolicy() {
|
|
|
7830
7927
|
return { enabled: true, template: DEFAULT_DENIED_TEMPLATE };
|
|
7831
7928
|
}
|
|
7832
7929
|
}
|
|
7833
|
-
var DEFAULT_DENIED_TEMPLATE;
|
|
7930
|
+
var cachedSelf, selfInflight, USER_TTL_MS, USER_FAIL_TTL_MS, userNameCache, userInflight, DEFAULT_DENIED_TEMPLATE;
|
|
7834
7931
|
var init_client3 = __esm({
|
|
7835
7932
|
"src/integrations/slack/client.ts"() {
|
|
7836
7933
|
"use strict";
|
|
7837
7934
|
init_config();
|
|
7935
|
+
cachedSelf = null;
|
|
7936
|
+
selfInflight = null;
|
|
7937
|
+
USER_TTL_MS = 60 * 60 * 1e3;
|
|
7938
|
+
USER_FAIL_TTL_MS = 5 * 60 * 1e3;
|
|
7939
|
+
userNameCache = /* @__PURE__ */ new Map();
|
|
7940
|
+
userInflight = /* @__PURE__ */ new Map();
|
|
7838
7941
|
DEFAULT_DENIED_TEMPLATE = "Sorry, you don't have permission to use this bot. (Contact the bot owner if you think this is a mistake.)";
|
|
7839
7942
|
}
|
|
7840
7943
|
});
|
|
@@ -7849,21 +7952,36 @@ function markThreadOwned(channel, threadTs) {
|
|
|
7849
7952
|
function isThreadOwned(channel, threadTs) {
|
|
7850
7953
|
return ownedThreads.has(threadKey(channel, threadTs));
|
|
7851
7954
|
}
|
|
7852
|
-
function
|
|
7853
|
-
|
|
7955
|
+
function isSelfAuthored(event, self) {
|
|
7956
|
+
if (!self) return true;
|
|
7957
|
+
if (self.botId && event.bot_id && event.bot_id === self.botId) return true;
|
|
7958
|
+
if (self.botUserId && event.user && event.user === self.botUserId) return true;
|
|
7959
|
+
return false;
|
|
7854
7960
|
}
|
|
7855
|
-
function slackEventToInboundResult(event) {
|
|
7961
|
+
function slackEventToInboundResult(event, opts = {}) {
|
|
7856
7962
|
if (!event) return { event: null, dropReason: "empty_text" };
|
|
7857
|
-
|
|
7858
|
-
|
|
7963
|
+
const self = opts.self ?? getCachedSlackSelfIdentity();
|
|
7964
|
+
const isBotAuthored = !!event.bot_id || event.type === "message" && event.subtype === "bot_message";
|
|
7965
|
+
if (isBotAuthored && isSelfAuthored(event, self)) {
|
|
7859
7966
|
return { event: null, dropReason: "bot_message" };
|
|
7860
7967
|
}
|
|
7968
|
+
if (event.type === "message" && event.subtype && IGNORED_MESSAGE_SUBTYPES.has(event.subtype)) {
|
|
7969
|
+
return { event: null, dropReason: "ignored_subtype" };
|
|
7970
|
+
}
|
|
7861
7971
|
const isDm = event.type === "message" && event.channel_type === "im";
|
|
7862
7972
|
const isThreadReply = event.type === "message" && !isDm && typeof event.thread_ts === "string" && event.thread_ts !== event.ts;
|
|
7973
|
+
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.
|
|
7974
|
+
typeof event.channel === "string");
|
|
7863
7975
|
if (event.type !== "app_mention" && !isDm && !isThreadReply) {
|
|
7976
|
+
if (isNonThreadChannelMsg) {
|
|
7977
|
+
return { event: null, dropReason: "non_thread_channel_msg" };
|
|
7978
|
+
}
|
|
7979
|
+
if (event.type !== "message") {
|
|
7980
|
+
return { event: null, dropReason: "non_message_event" };
|
|
7981
|
+
}
|
|
7864
7982
|
return { event: null, dropReason: "unsupported_type" };
|
|
7865
7983
|
}
|
|
7866
|
-
const text =
|
|
7984
|
+
const text = (event.text ?? "").trim();
|
|
7867
7985
|
if (!text) return { event: null, dropReason: "empty_text" };
|
|
7868
7986
|
const policy = getSlackAllowlistPolicy();
|
|
7869
7987
|
const userAllowlistActive = policy.allowedUsers.length > 0;
|
|
@@ -7931,7 +8049,6 @@ var init_slack = __esm({
|
|
|
7931
8049
|
}
|
|
7932
8050
|
};
|
|
7933
8051
|
IGNORED_MESSAGE_SUBTYPES = /* @__PURE__ */ new Set([
|
|
7934
|
-
"bot_message",
|
|
7935
8052
|
"message_changed",
|
|
7936
8053
|
"message_deleted",
|
|
7937
8054
|
"channel_join",
|
|
@@ -13605,16 +13722,18 @@ init_webhook_events();
|
|
|
13605
13722
|
init_inbox();
|
|
13606
13723
|
var recentlyHandled = /* @__PURE__ */ new Map();
|
|
13607
13724
|
var MAX_RECENT = 1e3;
|
|
13608
|
-
function
|
|
13725
|
+
function wasHandled(channel, ts) {
|
|
13609
13726
|
if (!channel || !ts) return false;
|
|
13727
|
+
return recentlyHandled.has(`${channel}\u241F${ts}`);
|
|
13728
|
+
}
|
|
13729
|
+
function markHandled(channel, ts) {
|
|
13730
|
+
if (!channel || !ts) return;
|
|
13610
13731
|
const key2 = `${channel}\u241F${ts}`;
|
|
13611
|
-
if (recentlyHandled.has(key2)) return true;
|
|
13612
13732
|
recentlyHandled.set(key2, Date.now());
|
|
13613
13733
|
if (recentlyHandled.size > MAX_RECENT) {
|
|
13614
13734
|
const oldest = recentlyHandled.keys().next().value;
|
|
13615
13735
|
if (oldest) recentlyHandled.delete(oldest);
|
|
13616
13736
|
}
|
|
13617
|
-
return false;
|
|
13618
13737
|
}
|
|
13619
13738
|
var slack = new Hono6();
|
|
13620
13739
|
slack.post("/events", async (c) => {
|
|
@@ -13651,11 +13770,12 @@ slack.post("/events", async (c) => {
|
|
|
13651
13770
|
textSnippet: typeof ev.text === "string" ? ev.text : void 0,
|
|
13652
13771
|
meta: { ts: ev.ts, thread_ts: ev.thread_ts, team: ev.team, event_subtype: ev.subtype }
|
|
13653
13772
|
});
|
|
13654
|
-
if (
|
|
13773
|
+
if (wasHandled(ev.channel, ev.ts)) {
|
|
13655
13774
|
updateEvent(auditId, { status: "dropped", dropReason: "duplicate_delivery" });
|
|
13656
13775
|
return c.json({ ok: true });
|
|
13657
13776
|
}
|
|
13658
|
-
const
|
|
13777
|
+
const self = await ensureSlackSelfIdentity();
|
|
13778
|
+
const { event: inbound, dropReason } = slackEventToInboundResult(ev, { self });
|
|
13659
13779
|
if (inbound) {
|
|
13660
13780
|
const isThreadReply = ev.type === "message" && ev.channel_type !== "im" && typeof ev.thread_ts === "string" && ev.thread_ts !== ev.ts;
|
|
13661
13781
|
if (isThreadReply) {
|
|
@@ -13674,7 +13794,18 @@ slack.post("/events", async (c) => {
|
|
|
13674
13794
|
}
|
|
13675
13795
|
const orchestratorId = await findOrCreateOrchestratorId();
|
|
13676
13796
|
if (orchestratorId) {
|
|
13797
|
+
inbound.content = await normalizeSlackMentions(inbound.content);
|
|
13798
|
+
if (ev.user) {
|
|
13799
|
+
const speakerName = await resolveSlackUserName(ev.user);
|
|
13800
|
+
if (speakerName) {
|
|
13801
|
+
inbound.content = inbound.content.replace(
|
|
13802
|
+
`user=${ev.user}`,
|
|
13803
|
+
`user=${speakerName} <@${ev.user}>`
|
|
13804
|
+
);
|
|
13805
|
+
}
|
|
13806
|
+
}
|
|
13677
13807
|
pushToInbox(orchestratorId, inbound);
|
|
13808
|
+
markHandled(ev.channel, ev.ts);
|
|
13678
13809
|
updateEvent(auditId, { status: "routed", sessionId: orchestratorId });
|
|
13679
13810
|
} else {
|
|
13680
13811
|
updateEvent(auditId, { status: "error", error: "no orchestrator session available" });
|
|
@@ -14629,13 +14760,15 @@ async function startServer(options = {}) {
|
|
|
14629
14760
|
if (!options.quiet) console.warn(`[scheduler] start skipped: ${err.message}`);
|
|
14630
14761
|
}
|
|
14631
14762
|
const port = options.port || config.server.port;
|
|
14632
|
-
const
|
|
14763
|
+
const envHost = process.env.SPARKECODER_API_HOST || process.env.SPARKECODER_HOST;
|
|
14764
|
+
const host = envHost || options.host || config.server.host || "0.0.0.0";
|
|
14765
|
+
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";
|
|
14633
14766
|
const publicUrl = options.publicUrl || config.server.publicUrl;
|
|
14634
14767
|
const app = await createApp({ quiet: options.quiet });
|
|
14635
14768
|
if (!options.quiet) {
|
|
14636
14769
|
console.log(`
|
|
14637
14770
|
\u{1F680} SparkECoder API Server`);
|
|
14638
|
-
console.log(` \u2192
|
|
14771
|
+
console.log(` \u2192 Binding to ${host}:${port} (source: ${hostSource})`);
|
|
14639
14772
|
if (publicUrl) {
|
|
14640
14773
|
console.log(` \u2192 Public URL: ${publicUrl}`);
|
|
14641
14774
|
}
|
|
@@ -14644,10 +14777,22 @@ async function startServer(options = {}) {
|
|
|
14644
14777
|
console.log(` \u2192 OpenAPI spec: http://${host}:${port}/openapi.json
|
|
14645
14778
|
`);
|
|
14646
14779
|
}
|
|
14780
|
+
if (host === "127.0.0.1" || host === "localhost") {
|
|
14781
|
+
console.log(`[sparkecoder] \u26A0 API bound to ${host} only \u2014 not reachable from outside the machine.`);
|
|
14782
|
+
console.log(`[sparkecoder] For tunnels/reverse proxies (Modal/Fly/Docker), set --host 0.0.0.0 or SPARKECODER_API_HOST=0.0.0.0.`);
|
|
14783
|
+
}
|
|
14647
14784
|
serverInstance = serve({
|
|
14648
14785
|
fetch: app.fetch,
|
|
14649
14786
|
port,
|
|
14650
14787
|
hostname: host
|
|
14788
|
+
}, (info) => {
|
|
14789
|
+
const actual = `${info.address}:${info.port}`;
|
|
14790
|
+
const requested = `${host}:${port}`;
|
|
14791
|
+
if (info.address === "127.0.0.1" && host !== "127.0.0.1" && host !== "localhost") {
|
|
14792
|
+
console.warn(`[sparkecoder] \u2717 API listener bound to ${actual} but ${requested} was requested. External requests will be refused.`);
|
|
14793
|
+
} else {
|
|
14794
|
+
console.log(`[sparkecoder] \u2713 API listening on ${actual}`);
|
|
14795
|
+
}
|
|
14651
14796
|
});
|
|
14652
14797
|
let webPort;
|
|
14653
14798
|
let webStarted;
|