@inkeep/agents-work-apps 0.58.2 → 0.58.4
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/env.d.ts +2 -0
- package/dist/env.js +1 -0
- package/dist/github/index.d.ts +3 -3
- package/dist/github/mcp/auth.d.ts +2 -0
- package/dist/github/mcp/auth.js +20 -0
- package/dist/github/mcp/index.d.ts +4 -2
- package/dist/github/mcp/index.js +9 -6
- package/dist/github/mcp/schemas.d.ts +1 -1
- package/dist/github/routes/setup.d.ts +2 -2
- package/dist/github/routes/tokenExchange.d.ts +2 -2
- package/dist/github/routes/webhooks.d.ts +2 -2
- package/dist/slack/index.d.ts +3 -2
- package/dist/slack/index.js +5 -1
- package/dist/slack/mcp/auth.d.ts +12 -0
- package/dist/slack/mcp/auth.js +70 -0
- package/dist/slack/mcp/index.d.ts +23 -0
- package/dist/slack/mcp/index.js +294 -0
- package/dist/slack/mcp/utils.d.ts +18 -0
- package/dist/slack/mcp/utils.js +83 -0
- package/dist/slack/routes/oauth.js +4 -1
- package/dist/slack/routes/workspaces.js +6 -1
- package/dist/slack/services/client.d.ts +67 -1
- package/dist/slack/services/client.js +97 -1
- package/dist/slack/services/dev-config.js +1 -1
- package/dist/slack/services/index.d.ts +2 -2
- package/dist/slack/services/index.js +2 -2
- package/package.json +2 -2
package/dist/env.d.ts
CHANGED
|
@@ -28,6 +28,7 @@ declare const envSchema: z.ZodObject<{
|
|
|
28
28
|
GITHUB_STATE_SIGNING_SECRET: z.ZodOptional<z.ZodString>;
|
|
29
29
|
GITHUB_APP_NAME: z.ZodOptional<z.ZodString>;
|
|
30
30
|
GITHUB_MCP_API_KEY: z.ZodOptional<z.ZodString>;
|
|
31
|
+
SLACK_MCP_API_KEY: z.ZodOptional<z.ZodString>;
|
|
31
32
|
SLACK_CLIENT_ID: z.ZodOptional<z.ZodString>;
|
|
32
33
|
SLACK_CLIENT_SECRET: z.ZodOptional<z.ZodString>;
|
|
33
34
|
SLACK_SIGNING_SECRET: z.ZodOptional<z.ZodString>;
|
|
@@ -53,6 +54,7 @@ declare const env: {
|
|
|
53
54
|
GITHUB_STATE_SIGNING_SECRET?: string | undefined;
|
|
54
55
|
GITHUB_APP_NAME?: string | undefined;
|
|
55
56
|
GITHUB_MCP_API_KEY?: string | undefined;
|
|
57
|
+
SLACK_MCP_API_KEY?: string | undefined;
|
|
56
58
|
SLACK_CLIENT_ID?: string | undefined;
|
|
57
59
|
SLACK_CLIENT_SECRET?: string | undefined;
|
|
58
60
|
SLACK_SIGNING_SECRET?: string | undefined;
|
package/dist/env.js
CHANGED
|
@@ -30,6 +30,7 @@ const envSchema = z.object({
|
|
|
30
30
|
GITHUB_STATE_SIGNING_SECRET: z.string().min(32, "GITHUB_STATE_SIGNING_SECRET must be at least 32 characters").optional().describe("Secret for signing GitHub OAuth state (minimum 32 characters)"),
|
|
31
31
|
GITHUB_APP_NAME: z.string().optional().describe("Name of the GitHub App"),
|
|
32
32
|
GITHUB_MCP_API_KEY: z.string().optional().describe("API key for the GitHub MCP"),
|
|
33
|
+
SLACK_MCP_API_KEY: z.string().optional().describe("API key for the Slack MCP"),
|
|
33
34
|
SLACK_CLIENT_ID: z.string().optional().describe("Slack App Client ID"),
|
|
34
35
|
SLACK_CLIENT_SECRET: z.string().optional().describe("Slack App Client Secret"),
|
|
35
36
|
SLACK_SIGNING_SECRET: z.string().optional().describe("Slack App Signing Secret"),
|
package/dist/github/index.d.ts
CHANGED
|
@@ -4,10 +4,10 @@ import "./routes/setup.js";
|
|
|
4
4
|
import "./routes/tokenExchange.js";
|
|
5
5
|
import { WebhookVerificationResult, verifyWebhookSignature } from "./routes/webhooks.js";
|
|
6
6
|
import { Hono } from "hono";
|
|
7
|
-
import * as
|
|
7
|
+
import * as hono_types1 from "hono/types";
|
|
8
8
|
|
|
9
9
|
//#region src/github/index.d.ts
|
|
10
|
-
declare function createGithubRoutes(): Hono<
|
|
11
|
-
declare const githubRoutes: Hono<
|
|
10
|
+
declare function createGithubRoutes(): Hono<hono_types1.BlankEnv, hono_types1.BlankSchema, "/">;
|
|
11
|
+
declare const githubRoutes: Hono<hono_types1.BlankEnv, hono_types1.BlankSchema, "/">;
|
|
12
12
|
//#endregion
|
|
13
13
|
export { GenerateInstallationAccessTokenResult, GenerateTokenError, GenerateTokenResult, GitHubAppConfig, InstallationAccessToken, InstallationInfo, LookupInstallationError, LookupInstallationForRepoResult, LookupInstallationResult, WebhookVerificationResult, clearConfigCache, createAppJwt, createGithubRoutes, determineStatus, fetchInstallationDetails, fetchInstallationRepositories, generateInstallationAccessToken, getGitHubAppConfig, getGitHubAppName, getStateSigningSecret, getWebhookSecret, githubRoutes, isGitHubAppConfigured, isGitHubAppNameConfigured, isStateSigningConfigured, isWebhookConfigured, lookupInstallationForRepo, validateGitHubAppConfigOnStartup, validateGitHubInstallFlowConfigOnStartup, validateGitHubWebhookConfigOnStartup, verifyWebhookSignature };
|
package/dist/github/mcp/auth.js
CHANGED
|
@@ -13,6 +13,24 @@ const githubMcpAuth = () => createMiddleware(async (c, next) => {
|
|
|
13
13
|
name: "x-inkeep-tool-id"
|
|
14
14
|
} }
|
|
15
15
|
});
|
|
16
|
+
const tenantId = c.req.header("x-inkeep-tenant-id");
|
|
17
|
+
if (!tenantId) throw createApiError({
|
|
18
|
+
code: "unauthorized",
|
|
19
|
+
message: "Missing required header: x-inkeep-tenant-id",
|
|
20
|
+
extensions: { parameter: {
|
|
21
|
+
in: "header",
|
|
22
|
+
name: "x-inkeep-tenant-id"
|
|
23
|
+
} }
|
|
24
|
+
});
|
|
25
|
+
const projectId = c.req.header("x-inkeep-project-id");
|
|
26
|
+
if (!projectId) throw createApiError({
|
|
27
|
+
code: "unauthorized",
|
|
28
|
+
message: "Missing required header: x-inkeep-project-id",
|
|
29
|
+
extensions: { parameter: {
|
|
30
|
+
in: "header",
|
|
31
|
+
name: "x-inkeep-project-id"
|
|
32
|
+
} }
|
|
33
|
+
});
|
|
16
34
|
const authHeader = c.req.header("Authorization");
|
|
17
35
|
if (!authHeader) throw createApiError({
|
|
18
36
|
code: "unauthorized",
|
|
@@ -36,6 +54,8 @@ const githubMcpAuth = () => createMiddleware(async (c, next) => {
|
|
|
36
54
|
message: "Invalid API key"
|
|
37
55
|
});
|
|
38
56
|
c.set("toolId", toolId);
|
|
57
|
+
c.set("tenantId", tenantId);
|
|
58
|
+
c.set("projectId", projectId);
|
|
39
59
|
await next();
|
|
40
60
|
});
|
|
41
61
|
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
|
-
import * as
|
|
2
|
+
import * as hono_types0 from "hono/types";
|
|
3
3
|
|
|
4
4
|
//#region src/github/mcp/index.d.ts
|
|
5
5
|
declare const app: Hono<{
|
|
6
6
|
Variables: {
|
|
7
7
|
toolId: string;
|
|
8
|
+
tenantId: string;
|
|
9
|
+
projectId: string;
|
|
8
10
|
};
|
|
9
|
-
},
|
|
11
|
+
}, hono_types0.BlankSchema, "/">;
|
|
10
12
|
//#endregion
|
|
11
13
|
export { app as default };
|
package/dist/github/mcp/index.js
CHANGED
|
@@ -27,11 +27,8 @@ const getAvailableRepositoryString = (repositoryAccess) => {
|
|
|
27
27
|
if (repositoryAccess.length === 0) return "No repositories available";
|
|
28
28
|
return `Available repositories: ${repositoryAccess.map((r) => `"${r.repositoryFullName}"`).join(", ")}`;
|
|
29
29
|
};
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
*/
|
|
33
|
-
const getServer = async (toolId) => {
|
|
34
|
-
const repositoryAccess = await getMcpToolRepositoryAccessWithDetails(runDbClient_default)(toolId);
|
|
30
|
+
const getServer = async (scope) => {
|
|
31
|
+
const repositoryAccess = await getMcpToolRepositoryAccessWithDetails(runDbClient_default)(scope);
|
|
35
32
|
const installationIdMap = /* @__PURE__ */ new Map();
|
|
36
33
|
for (const repo of repositoryAccess) installationIdMap.set(repo.repositoryFullName, repo.installationId);
|
|
37
34
|
if (repositoryAccess.length === 0) throw new Error("No repository access found for tool");
|
|
@@ -985,8 +982,14 @@ app.use("/", githubMcpAuth());
|
|
|
985
982
|
app.post("/", async (c) => {
|
|
986
983
|
if (!process.env.GITHUB_APP_ID || !process.env.GITHUB_APP_PRIVATE_KEY) return c.json({ error: "GITHUB_APP_ID and GITHUB_APP_PRIVATE_KEY must be set" }, 500);
|
|
987
984
|
const toolId = c.get("toolId");
|
|
985
|
+
const tenantId = c.get("tenantId");
|
|
986
|
+
const projectId = c.get("projectId");
|
|
988
987
|
const body = await c.req.json();
|
|
989
|
-
const server = await getServer(
|
|
988
|
+
const server = await getServer({
|
|
989
|
+
tenantId,
|
|
990
|
+
projectId,
|
|
991
|
+
toolId
|
|
992
|
+
});
|
|
990
993
|
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: void 0 });
|
|
991
994
|
await server.connect(transport);
|
|
992
995
|
const { req, res } = toReqRes(c.req.raw);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
|
-
import * as
|
|
2
|
+
import * as hono_types9 from "hono/types";
|
|
3
3
|
|
|
4
4
|
//#region src/github/routes/setup.d.ts
|
|
5
|
-
declare const app: Hono<
|
|
5
|
+
declare const app: Hono<hono_types9.BlankEnv, hono_types9.BlankSchema, "/">;
|
|
6
6
|
//#endregion
|
|
7
7
|
export { app as default };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
|
-
import * as
|
|
2
|
+
import * as hono_types7 from "hono/types";
|
|
3
3
|
|
|
4
4
|
//#region src/github/routes/tokenExchange.d.ts
|
|
5
|
-
declare const app: Hono<
|
|
5
|
+
declare const app: Hono<hono_types7.BlankEnv, hono_types7.BlankSchema, "/">;
|
|
6
6
|
//#endregion
|
|
7
7
|
export { app as default };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
|
-
import * as
|
|
2
|
+
import * as hono_types5 from "hono/types";
|
|
3
3
|
|
|
4
4
|
//#region src/github/routes/webhooks.d.ts
|
|
5
5
|
interface WebhookVerificationResult {
|
|
@@ -7,6 +7,6 @@ interface WebhookVerificationResult {
|
|
|
7
7
|
error?: string;
|
|
8
8
|
}
|
|
9
9
|
declare function verifyWebhookSignature(payload: string, signature: string | undefined, secret: string): WebhookVerificationResult;
|
|
10
|
-
declare const app: Hono<
|
|
10
|
+
declare const app: Hono<hono_types5.BlankEnv, hono_types5.BlankSchema, "/">;
|
|
11
11
|
//#endregion
|
|
12
12
|
export { WebhookVerificationResult, app as default, verifyWebhookSignature };
|
package/dist/slack/index.d.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { DispatchOptions, SlackEventDispatchResult, dispatchSlackEvent } from "./dispatcher.js";
|
|
2
2
|
import { ManageAppVariables, WorkAppsVariables } from "./types.js";
|
|
3
|
+
import { resolveWorkspaceToken } from "./mcp/utils.js";
|
|
4
|
+
import { getSlackClient, validateBotChannelMembership } from "./services/client.js";
|
|
3
5
|
import { DefaultAgentConfig, SlackWorkspaceConnection, WorkspaceInstallData, clearWorkspaceConnectionCache, computeWorkspaceConnectionId, createConnectSession, deleteWorkspaceInstallation, findWorkspaceConnectionByTeamId, getConnectionAccessToken, getSlackIntegrationId, getSlackNango, getWorkspaceDefaultAgent, getWorkspaceDefaultAgentFromNango, listWorkspaceInstallations, setWorkspaceDefaultAgent, storeWorkspaceInstallation, updateConnectionMetadata } from "./services/nango.js";
|
|
4
6
|
import { getChannelAgentConfig } from "./services/events/utils.js";
|
|
5
7
|
import "./services/events/index.js";
|
|
@@ -9,7 +11,6 @@ import { startSocketMode } from "./socket-mode.js";
|
|
|
9
11
|
import { OpenAPIHono } from "@hono/zod-openapi";
|
|
10
12
|
|
|
11
13
|
//#region src/slack/index.d.ts
|
|
12
|
-
|
|
13
14
|
declare function createSlackRoutes(): OpenAPIHono<{
|
|
14
15
|
Variables: WorkAppsVariables;
|
|
15
16
|
}, {}, "/">;
|
|
@@ -17,4 +18,4 @@ declare const slackRoutes: OpenAPIHono<{
|
|
|
17
18
|
Variables: WorkAppsVariables;
|
|
18
19
|
}, {}, "/">;
|
|
19
20
|
//#endregion
|
|
20
|
-
export { DefaultAgentConfig, type DispatchOptions, ManageAppVariables, type SlackEventDispatchResult, SlackWorkspaceConnection, WorkAppsVariables, WorkspaceInstallData, clearWorkspaceConnectionCache, computeWorkspaceConnectionId, createConnectSession, createSlackRoutes, deleteWorkspaceInstallation, dispatchSlackEvent, findWorkspaceConnectionByTeamId, getBotTokenForTeam, getChannelAgentConfig, getConnectionAccessToken, getSlackIntegrationId, getSlackNango, getWorkspaceDefaultAgent, getWorkspaceDefaultAgentFromNango, listWorkspaceInstallations, setBotTokenForTeam, setWorkspaceDefaultAgent, slackRoutes, startSocketMode, storeWorkspaceInstallation, updateConnectionMetadata };
|
|
21
|
+
export { DefaultAgentConfig, type DispatchOptions, ManageAppVariables, type SlackEventDispatchResult, SlackWorkspaceConnection, WorkAppsVariables, WorkspaceInstallData, clearWorkspaceConnectionCache, computeWorkspaceConnectionId, createConnectSession, createSlackRoutes, deleteWorkspaceInstallation, dispatchSlackEvent, findWorkspaceConnectionByTeamId, getBotTokenForTeam, getChannelAgentConfig, getConnectionAccessToken, getSlackClient, getSlackIntegrationId, getSlackNango, getWorkspaceDefaultAgent, getWorkspaceDefaultAgentFromNango, listWorkspaceInstallations, resolveWorkspaceToken, setBotTokenForTeam, setWorkspaceDefaultAgent, slackRoutes, startSocketMode, storeWorkspaceInstallation, updateConnectionMetadata, validateBotChannelMembership };
|
package/dist/slack/index.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { clearWorkspaceConnectionCache, computeWorkspaceConnectionId, createConnectSession, deleteWorkspaceInstallation, findWorkspaceConnectionByTeamId, getConnectionAccessToken, getSlackIntegrationId, getSlackNango, getWorkspaceDefaultAgent, getWorkspaceDefaultAgentFromNango, listWorkspaceInstallations, setWorkspaceDefaultAgent, storeWorkspaceInstallation, updateConnectionMetadata } from "./services/nango.js";
|
|
2
2
|
import { getChannelAgentConfig } from "./services/events/utils.js";
|
|
3
|
+
import { getSlackClient, validateBotChannelMembership } from "./services/client.js";
|
|
3
4
|
import "./services/events/index.js";
|
|
4
5
|
import { getBotTokenForTeam, setBotTokenForTeam } from "./services/workspace-tokens.js";
|
|
5
6
|
import { dispatchSlackEvent } from "./dispatcher.js";
|
|
7
|
+
import { resolveWorkspaceToken } from "./mcp/utils.js";
|
|
8
|
+
import mcp_default from "./mcp/index.js";
|
|
6
9
|
import "./routes/oauth.js";
|
|
7
10
|
import routes_default from "./routes/index.js";
|
|
8
11
|
import { startSocketMode } from "./socket-mode.js";
|
|
@@ -22,9 +25,10 @@ import { OpenAPIHono } from "@hono/zod-openapi";
|
|
|
22
25
|
function createSlackRoutes() {
|
|
23
26
|
const app = new OpenAPIHono();
|
|
24
27
|
app.route("/", routes_default);
|
|
28
|
+
app.route("/mcp", mcp_default);
|
|
25
29
|
return app;
|
|
26
30
|
}
|
|
27
31
|
const slackRoutes = createSlackRoutes();
|
|
28
32
|
|
|
29
33
|
//#endregion
|
|
30
|
-
export { clearWorkspaceConnectionCache, computeWorkspaceConnectionId, createConnectSession, createSlackRoutes, deleteWorkspaceInstallation, dispatchSlackEvent, findWorkspaceConnectionByTeamId, getBotTokenForTeam, getChannelAgentConfig, getConnectionAccessToken, getSlackIntegrationId, getSlackNango, getWorkspaceDefaultAgent, getWorkspaceDefaultAgentFromNango, listWorkspaceInstallations, setBotTokenForTeam, setWorkspaceDefaultAgent, slackRoutes, startSocketMode, storeWorkspaceInstallation, updateConnectionMetadata };
|
|
34
|
+
export { clearWorkspaceConnectionCache, computeWorkspaceConnectionId, createConnectSession, createSlackRoutes, deleteWorkspaceInstallation, dispatchSlackEvent, findWorkspaceConnectionByTeamId, getBotTokenForTeam, getChannelAgentConfig, getConnectionAccessToken, getSlackClient, getSlackIntegrationId, getSlackNango, getWorkspaceDefaultAgent, getWorkspaceDefaultAgentFromNango, listWorkspaceInstallations, resolveWorkspaceToken, setBotTokenForTeam, setWorkspaceDefaultAgent, slackRoutes, startSocketMode, storeWorkspaceInstallation, updateConnectionMetadata, validateBotChannelMembership };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as hono2 from "hono";
|
|
2
|
+
|
|
3
|
+
//#region src/slack/mcp/auth.d.ts
|
|
4
|
+
declare const slackMcpAuth: () => hono2.MiddlewareHandler<{
|
|
5
|
+
Variables: {
|
|
6
|
+
toolId: string;
|
|
7
|
+
tenantId: string;
|
|
8
|
+
projectId: string;
|
|
9
|
+
};
|
|
10
|
+
}, string, {}, Response>;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { slackMcpAuth };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { env } from "../../env.js";
|
|
2
|
+
import { createApiError } from "@inkeep/agents-core";
|
|
3
|
+
import { createMiddleware } from "hono/factory";
|
|
4
|
+
import { timingSafeEqual } from "node:crypto";
|
|
5
|
+
|
|
6
|
+
//#region src/slack/mcp/auth.ts
|
|
7
|
+
const slackMcpAuth = () => createMiddleware(async (c, next) => {
|
|
8
|
+
const toolId = c.req.header("x-inkeep-tool-id");
|
|
9
|
+
if (!toolId) throw createApiError({
|
|
10
|
+
code: "unauthorized",
|
|
11
|
+
message: "Missing required header: x-inkeep-tool-id",
|
|
12
|
+
extensions: { parameter: {
|
|
13
|
+
in: "header",
|
|
14
|
+
name: "x-inkeep-tool-id"
|
|
15
|
+
} }
|
|
16
|
+
});
|
|
17
|
+
const tenantId = c.req.header("x-inkeep-tenant-id");
|
|
18
|
+
if (!tenantId) throw createApiError({
|
|
19
|
+
code: "unauthorized",
|
|
20
|
+
message: "Missing required header: x-inkeep-tenant-id",
|
|
21
|
+
extensions: { parameter: {
|
|
22
|
+
in: "header",
|
|
23
|
+
name: "x-inkeep-tenant-id"
|
|
24
|
+
} }
|
|
25
|
+
});
|
|
26
|
+
const projectId = c.req.header("x-inkeep-project-id");
|
|
27
|
+
if (!projectId) throw createApiError({
|
|
28
|
+
code: "unauthorized",
|
|
29
|
+
message: "Missing required header: x-inkeep-project-id",
|
|
30
|
+
extensions: { parameter: {
|
|
31
|
+
in: "header",
|
|
32
|
+
name: "x-inkeep-project-id"
|
|
33
|
+
} }
|
|
34
|
+
});
|
|
35
|
+
const authHeader = c.req.header("Authorization");
|
|
36
|
+
if (!authHeader) throw createApiError({
|
|
37
|
+
code: "unauthorized",
|
|
38
|
+
message: "Missing required header: Authorization",
|
|
39
|
+
extensions: { parameter: {
|
|
40
|
+
in: "header",
|
|
41
|
+
name: "Authorization"
|
|
42
|
+
} }
|
|
43
|
+
});
|
|
44
|
+
const apiKey = authHeader.startsWith("Bearer ") ? authHeader.substring(7) : void 0;
|
|
45
|
+
if (!apiKey) throw createApiError({
|
|
46
|
+
code: "unauthorized",
|
|
47
|
+
message: "Invalid Authorization header format. Expected: Bearer <token>",
|
|
48
|
+
extensions: { parameter: {
|
|
49
|
+
in: "header",
|
|
50
|
+
name: "Authorization"
|
|
51
|
+
} }
|
|
52
|
+
});
|
|
53
|
+
if (!env.SLACK_MCP_API_KEY) throw createApiError({
|
|
54
|
+
code: "internal_server_error",
|
|
55
|
+
message: "Slack MCP API key not configured"
|
|
56
|
+
});
|
|
57
|
+
const expectedKey = Buffer.from(env.SLACK_MCP_API_KEY);
|
|
58
|
+
const providedKey = Buffer.from(apiKey);
|
|
59
|
+
if (expectedKey.length !== providedKey.length || !timingSafeEqual(expectedKey, providedKey)) throw createApiError({
|
|
60
|
+
code: "unauthorized",
|
|
61
|
+
message: "Invalid API key"
|
|
62
|
+
});
|
|
63
|
+
c.set("toolId", toolId);
|
|
64
|
+
c.set("tenantId", tenantId);
|
|
65
|
+
c.set("projectId", projectId);
|
|
66
|
+
await next();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
//#endregion
|
|
70
|
+
export { slackMcpAuth };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Hono } from "hono";
|
|
2
|
+
import * as hono_types0 from "hono/types";
|
|
3
|
+
|
|
4
|
+
//#region src/slack/mcp/index.d.ts
|
|
5
|
+
interface ChannelInfo {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
}
|
|
9
|
+
type ToolScope = {
|
|
10
|
+
tenantId: string;
|
|
11
|
+
projectId: string;
|
|
12
|
+
toolId: string;
|
|
13
|
+
};
|
|
14
|
+
declare function pruneStaleChannelIds(scope: ToolScope, availableChannels: ChannelInfo[], currentChannelIds: string[]): string[];
|
|
15
|
+
declare const app: Hono<{
|
|
16
|
+
Variables: {
|
|
17
|
+
toolId: string;
|
|
18
|
+
tenantId: string;
|
|
19
|
+
projectId: string;
|
|
20
|
+
};
|
|
21
|
+
}, hono_types0.BlankSchema, "/">;
|
|
22
|
+
//#endregion
|
|
23
|
+
export { ChannelInfo, app as default, pruneStaleChannelIds };
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { getLogger } from "../../logger.js";
|
|
2
|
+
import runDbClient_default from "../../db/runDbClient.js";
|
|
3
|
+
import { getBotMemberChannels, getSlackClient, getSlackUserByEmail, getSlackUserInfo, getSlackUsers, openDmConversation, postMessage, postMessageInThread } from "../services/client.js";
|
|
4
|
+
import { slackMcpAuth } from "./auth.js";
|
|
5
|
+
import { resolveChannelId, resolveWorkspaceToken, searchUsersByName, validateChannelAccess } from "./utils.js";
|
|
6
|
+
import { z } from "@hono/zod-openapi";
|
|
7
|
+
import { getSlackMcpToolAccessConfig, updateSlackMcpToolAccessChannelIds } from "@inkeep/agents-core";
|
|
8
|
+
import { Hono } from "hono";
|
|
9
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
10
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
11
|
+
import { toFetchResponse, toReqRes } from "fetch-to-node";
|
|
12
|
+
|
|
13
|
+
//#region src/slack/mcp/index.ts
|
|
14
|
+
const logger = getLogger("slack-mcp");
|
|
15
|
+
async function getAvailableChannels(client, config) {
|
|
16
|
+
try {
|
|
17
|
+
const botChannels = await getBotMemberChannels(client);
|
|
18
|
+
if (config.channelAccessMode === "all") return botChannels.filter((ch) => !!ch.id && !!ch.name).map((ch) => ({
|
|
19
|
+
id: ch.id,
|
|
20
|
+
name: ch.name
|
|
21
|
+
}));
|
|
22
|
+
const allowedIds = new Set(config.channelIds);
|
|
23
|
+
return botChannels.filter((ch) => !!ch.id && !!ch.name && allowedIds.has(ch.id)).map((ch) => ({
|
|
24
|
+
id: ch.id,
|
|
25
|
+
name: ch.name
|
|
26
|
+
}));
|
|
27
|
+
} catch (error) {
|
|
28
|
+
logger.warn({ error }, "Failed to fetch available channels for tool description");
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function getAvailableChannelsString(channels) {
|
|
33
|
+
if (channels.length === 0) return "No channels available";
|
|
34
|
+
return `Available channels: ${channels.map((ch) => `#${ch.name} (${ch.id})`).join(", ")}`;
|
|
35
|
+
}
|
|
36
|
+
function pruneStaleChannelIds(scope, availableChannels, currentChannelIds) {
|
|
37
|
+
const availableIds = new Set(availableChannels.map((ch) => ch.id));
|
|
38
|
+
const staleIds = currentChannelIds.filter((id) => !availableIds.has(id));
|
|
39
|
+
if (staleIds.length > 0) {
|
|
40
|
+
const prunedIds = currentChannelIds.filter((id) => availableIds.has(id));
|
|
41
|
+
logger.info({
|
|
42
|
+
toolId: scope.toolId,
|
|
43
|
+
staleIds,
|
|
44
|
+
prunedIds
|
|
45
|
+
}, "Pruning stale channel IDs from MCP access config");
|
|
46
|
+
updateSlackMcpToolAccessChannelIds(runDbClient_default)(scope, prunedIds).catch((error) => {
|
|
47
|
+
logger.warn({
|
|
48
|
+
error,
|
|
49
|
+
toolId: scope.toolId
|
|
50
|
+
}, "Failed to prune stale channel IDs");
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
return currentChannelIds;
|
|
54
|
+
}
|
|
55
|
+
const getServer = async (scope) => {
|
|
56
|
+
const botToken = await resolveWorkspaceToken(scope.tenantId);
|
|
57
|
+
const config = await getSlackMcpToolAccessConfig(runDbClient_default)(scope);
|
|
58
|
+
const client = getSlackClient(botToken);
|
|
59
|
+
const availableChannels = await getAvailableChannels(client, config);
|
|
60
|
+
if (config.channelAccessMode === "selected") pruneStaleChannelIds(scope, availableChannels, config.channelIds);
|
|
61
|
+
const server = new McpServer({
|
|
62
|
+
name: "inkeep-slack-mcp-server",
|
|
63
|
+
version: "1.0.0",
|
|
64
|
+
description: "A Slack MCP server for posting messages to channels" + (config.dmEnabled ? " and sending direct messages to users" : "") + ".\n" + availableChannels.map((ch) => `• #${ch.name} (${ch.id})`).join("\n")
|
|
65
|
+
}, { capabilities: { logging: {} } });
|
|
66
|
+
server.tool("post-channel-message", `Post a message to a Slack channel. Supports mrkdwn formatting and thread replies. ${getAvailableChannelsString(availableChannels)}`, {
|
|
67
|
+
channel: z.string().describe("Channel ID (e.g., C1234567890) or channel name prefixed with # (e.g., #general)."),
|
|
68
|
+
text: z.string().describe("Message text. Supports Slack mrkdwn formatting."),
|
|
69
|
+
thread_ts: z.string().optional().describe("Thread timestamp to reply in a thread. If omitted, posts as a new message.")
|
|
70
|
+
}, async ({ channel, text, thread_ts }) => {
|
|
71
|
+
try {
|
|
72
|
+
const channelId = await resolveChannelId(client, channel);
|
|
73
|
+
const accessCheck = validateChannelAccess(channelId, config);
|
|
74
|
+
if (!accessCheck.allowed) return {
|
|
75
|
+
content: [{
|
|
76
|
+
type: "text",
|
|
77
|
+
text: `Error: ${accessCheck.reason}`
|
|
78
|
+
}],
|
|
79
|
+
isError: true
|
|
80
|
+
};
|
|
81
|
+
const result = thread_ts ? await postMessageInThread(client, channelId, thread_ts, text) : await postMessage(client, channelId, text);
|
|
82
|
+
return { content: [{
|
|
83
|
+
type: "text",
|
|
84
|
+
text: JSON.stringify({
|
|
85
|
+
ok: result.ok,
|
|
86
|
+
ts: result.ts,
|
|
87
|
+
channel: result.channel
|
|
88
|
+
})
|
|
89
|
+
}] };
|
|
90
|
+
} catch (error) {
|
|
91
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
92
|
+
logger.error({
|
|
93
|
+
error,
|
|
94
|
+
toolId: scope.toolId,
|
|
95
|
+
channel
|
|
96
|
+
}, "Failed to post Slack message via MCP");
|
|
97
|
+
return {
|
|
98
|
+
content: [{
|
|
99
|
+
type: "text",
|
|
100
|
+
text: `Error posting message: ${message}`
|
|
101
|
+
}],
|
|
102
|
+
isError: true
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
if (config.dmEnabled) server.tool("post-direct-message", "Send a direct message to a Slack user. Opens or continues an existing DM conversation with the user.", {
|
|
107
|
+
user_id: z.string().describe("The Slack user ID to DM (e.g., U1234567890)."),
|
|
108
|
+
text: z.string().describe("Message text. Supports Slack mrkdwn formatting."),
|
|
109
|
+
thread_ts: z.string().optional().describe("Thread timestamp to reply in a thread. If omitted, posts as a new message.")
|
|
110
|
+
}, async ({ user_id, text, thread_ts }) => {
|
|
111
|
+
try {
|
|
112
|
+
const dmChannelId = await openDmConversation(client, user_id);
|
|
113
|
+
const result = thread_ts ? await postMessageInThread(client, dmChannelId, thread_ts, text) : await postMessage(client, dmChannelId, text);
|
|
114
|
+
return { content: [{
|
|
115
|
+
type: "text",
|
|
116
|
+
text: JSON.stringify({
|
|
117
|
+
ok: result.ok,
|
|
118
|
+
ts: result.ts,
|
|
119
|
+
channel: result.channel,
|
|
120
|
+
user_id
|
|
121
|
+
})
|
|
122
|
+
}] };
|
|
123
|
+
} catch (error) {
|
|
124
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
125
|
+
logger.error({
|
|
126
|
+
error,
|
|
127
|
+
toolId: scope.toolId,
|
|
128
|
+
user_id
|
|
129
|
+
}, "Failed to send Slack DM via MCP");
|
|
130
|
+
return {
|
|
131
|
+
content: [{
|
|
132
|
+
type: "text",
|
|
133
|
+
text: `Error sending DM: ${message}`
|
|
134
|
+
}],
|
|
135
|
+
isError: true
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
server.tool("get-slack-user", "Look up a Slack user by their user ID, email address, or name. Provide exactly one parameter. User ID and email return an exact match; name returns up to 5 ranked matches. If necessary, please clarify with the user when you receive multiple results.", {
|
|
140
|
+
user_id: z.string().optional().describe("Slack user ID (e.g., U1234567890). Returns an exact match."),
|
|
141
|
+
email: z.string().optional().describe("Email address (e.g., jane@company.com). Returns an exact match."),
|
|
142
|
+
name: z.string().optional().describe("Display name, real name, or username to search for (e.g., \"Jane Smith\"). Returns up to 5 ranked matches.")
|
|
143
|
+
}, async ({ user_id, email, name }) => {
|
|
144
|
+
try {
|
|
145
|
+
const providedCount = [
|
|
146
|
+
user_id,
|
|
147
|
+
email,
|
|
148
|
+
name
|
|
149
|
+
].filter(Boolean).length;
|
|
150
|
+
if (providedCount === 0) return {
|
|
151
|
+
content: [{
|
|
152
|
+
type: "text",
|
|
153
|
+
text: "Error: Provide at least one of user_id, email, or name."
|
|
154
|
+
}],
|
|
155
|
+
isError: true
|
|
156
|
+
};
|
|
157
|
+
if (providedCount > 1) return {
|
|
158
|
+
content: [{
|
|
159
|
+
type: "text",
|
|
160
|
+
text: "Error: Provide exactly one of user_id, email, or name."
|
|
161
|
+
}],
|
|
162
|
+
isError: true
|
|
163
|
+
};
|
|
164
|
+
if (user_id) {
|
|
165
|
+
const user = await getSlackUserInfo(client, user_id);
|
|
166
|
+
if (!user) return { content: [{
|
|
167
|
+
type: "text",
|
|
168
|
+
text: `No user found with ID: ${user_id}`
|
|
169
|
+
}] };
|
|
170
|
+
return { content: [{
|
|
171
|
+
type: "text",
|
|
172
|
+
text: JSON.stringify(user)
|
|
173
|
+
}] };
|
|
174
|
+
}
|
|
175
|
+
if (email) {
|
|
176
|
+
const user = await getSlackUserByEmail(client, email);
|
|
177
|
+
if (!user) return { content: [{
|
|
178
|
+
type: "text",
|
|
179
|
+
text: `No user found with email: ${email}`
|
|
180
|
+
}] };
|
|
181
|
+
return { content: [{
|
|
182
|
+
type: "text",
|
|
183
|
+
text: JSON.stringify(user)
|
|
184
|
+
}] };
|
|
185
|
+
}
|
|
186
|
+
const scored = searchUsersByName(await getSlackUsers(client), name ?? "");
|
|
187
|
+
if (scored.length === 0) return { content: [{
|
|
188
|
+
type: "text",
|
|
189
|
+
text: `No users found matching name: "${name}"`
|
|
190
|
+
}] };
|
|
191
|
+
return { content: [{
|
|
192
|
+
type: "text",
|
|
193
|
+
text: JSON.stringify(scored)
|
|
194
|
+
}] };
|
|
195
|
+
} catch (error) {
|
|
196
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
197
|
+
logger.error({
|
|
198
|
+
error,
|
|
199
|
+
toolId: scope.toolId,
|
|
200
|
+
user_id,
|
|
201
|
+
email,
|
|
202
|
+
name
|
|
203
|
+
}, "Failed to look up Slack user via MCP");
|
|
204
|
+
return {
|
|
205
|
+
content: [{
|
|
206
|
+
type: "text",
|
|
207
|
+
text: `Error looking up user: ${message}`
|
|
208
|
+
}],
|
|
209
|
+
isError: true
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
});
|
|
213
|
+
return server;
|
|
214
|
+
};
|
|
215
|
+
const app = new Hono();
|
|
216
|
+
app.onError((err, c) => {
|
|
217
|
+
const message = err.message || "Internal server error";
|
|
218
|
+
logger.error({ error: err }, "Slack MCP error");
|
|
219
|
+
return c.json({
|
|
220
|
+
jsonrpc: "2.0",
|
|
221
|
+
error: {
|
|
222
|
+
code: -32603,
|
|
223
|
+
message
|
|
224
|
+
},
|
|
225
|
+
id: null
|
|
226
|
+
}, 500);
|
|
227
|
+
});
|
|
228
|
+
app.use("/", slackMcpAuth());
|
|
229
|
+
app.post("/", async (c) => {
|
|
230
|
+
const toolId = c.get("toolId");
|
|
231
|
+
const tenantId = c.get("tenantId");
|
|
232
|
+
const projectId = c.get("projectId");
|
|
233
|
+
let server;
|
|
234
|
+
try {
|
|
235
|
+
const body = await c.req.json();
|
|
236
|
+
server = await getServer({
|
|
237
|
+
tenantId,
|
|
238
|
+
projectId,
|
|
239
|
+
toolId
|
|
240
|
+
});
|
|
241
|
+
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: void 0 });
|
|
242
|
+
await server.connect(transport);
|
|
243
|
+
const { req, res } = toReqRes(c.req.raw);
|
|
244
|
+
await transport.handleRequest(req, res, body);
|
|
245
|
+
return toFetchResponse(res);
|
|
246
|
+
} catch (error) {
|
|
247
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
248
|
+
logger.error({
|
|
249
|
+
error,
|
|
250
|
+
toolId
|
|
251
|
+
}, "MCP request failed");
|
|
252
|
+
return c.json({
|
|
253
|
+
jsonrpc: "2.0",
|
|
254
|
+
error: {
|
|
255
|
+
code: -32603,
|
|
256
|
+
message
|
|
257
|
+
},
|
|
258
|
+
id: null
|
|
259
|
+
}, 500);
|
|
260
|
+
} finally {
|
|
261
|
+
if (server) await server.close().catch((e) => logger.warn({ error: e }, "Failed to close MCP server"));
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
app.delete("/", async (c) => {
|
|
265
|
+
return c.json({
|
|
266
|
+
jsonrpc: "2.0",
|
|
267
|
+
error: {
|
|
268
|
+
code: -32001,
|
|
269
|
+
message: "Method Not Allowed"
|
|
270
|
+
},
|
|
271
|
+
id: null
|
|
272
|
+
}, { status: 405 });
|
|
273
|
+
});
|
|
274
|
+
app.get("/", async (c) => {
|
|
275
|
+
return c.json({
|
|
276
|
+
jsonrpc: "2.0",
|
|
277
|
+
error: {
|
|
278
|
+
code: -32e3,
|
|
279
|
+
message: "Method not allowed."
|
|
280
|
+
},
|
|
281
|
+
id: null
|
|
282
|
+
}, { status: 405 });
|
|
283
|
+
});
|
|
284
|
+
app.get("/health", async (c) => {
|
|
285
|
+
return c.json({
|
|
286
|
+
status: "healthy",
|
|
287
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
288
|
+
service: "Slack MCP Server"
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
var mcp_default = app;
|
|
292
|
+
|
|
293
|
+
//#endregion
|
|
294
|
+
export { mcp_default as default, pruneStaleChannelIds };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { SlackMcpToolAccessConfig } from "@inkeep/agents-core";
|
|
2
|
+
import { WebClient } from "@slack/web-api";
|
|
3
|
+
|
|
4
|
+
//#region src/slack/mcp/utils.d.ts
|
|
5
|
+
declare function resolveChannelId(client: WebClient, channelInput: string): Promise<string>;
|
|
6
|
+
declare function resolveWorkspaceToken(tenantId: string): Promise<string>;
|
|
7
|
+
interface UserWithNameFields {
|
|
8
|
+
realName?: string;
|
|
9
|
+
displayName?: string;
|
|
10
|
+
name?: string;
|
|
11
|
+
}
|
|
12
|
+
declare function searchUsersByName<T extends UserWithNameFields>(users: T[], query: string, maxResults?: number): T[];
|
|
13
|
+
declare function validateChannelAccess(channelId: string, config: SlackMcpToolAccessConfig): {
|
|
14
|
+
allowed: boolean;
|
|
15
|
+
reason?: string;
|
|
16
|
+
};
|
|
17
|
+
//#endregion
|
|
18
|
+
export { resolveChannelId, resolveWorkspaceToken, searchUsersByName, validateChannelAccess };
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import runDbClient_default from "../../db/runDbClient.js";
|
|
2
|
+
import { isSlackDevMode, loadSlackDevConfig } from "../services/dev-config.js";
|
|
3
|
+
import { getConnectionAccessToken } from "../services/nango.js";
|
|
4
|
+
import { listWorkAppSlackWorkspacesByTenant } from "@inkeep/agents-core";
|
|
5
|
+
|
|
6
|
+
//#region src/slack/mcp/utils.ts
|
|
7
|
+
const MAX_PAGES = 10;
|
|
8
|
+
async function resolveChannelId(client, channelInput) {
|
|
9
|
+
if (!channelInput.startsWith("#")) return channelInput;
|
|
10
|
+
const channelName = channelInput.slice(1);
|
|
11
|
+
let cursor;
|
|
12
|
+
let pageCount = 0;
|
|
13
|
+
do {
|
|
14
|
+
if (pageCount >= MAX_PAGES) throw new Error(`Channel not found within first ${MAX_PAGES * 200} channels: ${channelInput}`);
|
|
15
|
+
const result = await client.conversations.list({
|
|
16
|
+
types: "public_channel,private_channel",
|
|
17
|
+
exclude_archived: true,
|
|
18
|
+
limit: 200,
|
|
19
|
+
cursor
|
|
20
|
+
});
|
|
21
|
+
const match = result.channels?.find((ch) => ch.name === channelName);
|
|
22
|
+
if (match?.id) return match.id;
|
|
23
|
+
cursor = result.response_metadata?.next_cursor || void 0;
|
|
24
|
+
pageCount++;
|
|
25
|
+
} while (cursor);
|
|
26
|
+
throw new Error(`Channel not found: ${channelInput}`);
|
|
27
|
+
}
|
|
28
|
+
async function resolveWorkspaceToken(tenantId) {
|
|
29
|
+
if (isSlackDevMode()) {
|
|
30
|
+
const devConfig = loadSlackDevConfig();
|
|
31
|
+
if (devConfig?.botToken) return devConfig.botToken;
|
|
32
|
+
throw new Error("Slack dev mode enabled but no botToken found in .slack-dev.json");
|
|
33
|
+
}
|
|
34
|
+
const workspaces = await listWorkAppSlackWorkspacesByTenant(runDbClient_default)(tenantId);
|
|
35
|
+
if (workspaces.length === 0) throw new Error(`No Slack workspace installed for tenant ${tenantId}`);
|
|
36
|
+
const workspace = workspaces[0];
|
|
37
|
+
const botToken = await getConnectionAccessToken(workspace.nangoConnectionId);
|
|
38
|
+
if (!botToken) throw new Error(`Failed to retrieve bot token for workspace ${workspace.slackTeamId}`);
|
|
39
|
+
return botToken;
|
|
40
|
+
}
|
|
41
|
+
function searchUsersByName(users, query, maxResults = 5) {
|
|
42
|
+
const needle = query.trim().toLowerCase();
|
|
43
|
+
if (!needle) return [];
|
|
44
|
+
return users.map((user) => {
|
|
45
|
+
const fields = [
|
|
46
|
+
user.realName,
|
|
47
|
+
user.displayName,
|
|
48
|
+
user.name
|
|
49
|
+
].filter(Boolean);
|
|
50
|
+
let bestScore = 0;
|
|
51
|
+
for (const field of fields) {
|
|
52
|
+
const lower = field.toLowerCase();
|
|
53
|
+
if (lower === needle) {
|
|
54
|
+
bestScore = 3;
|
|
55
|
+
break;
|
|
56
|
+
}
|
|
57
|
+
if (lower.startsWith(needle)) bestScore = Math.max(bestScore, 2);
|
|
58
|
+
else if (lower.includes(needle)) bestScore = Math.max(bestScore, 1);
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
user,
|
|
62
|
+
score: bestScore
|
|
63
|
+
};
|
|
64
|
+
}).filter((entry) => entry.score > 0).sort((a, b) => b.score - a.score).slice(0, maxResults).map((entry) => entry.user);
|
|
65
|
+
}
|
|
66
|
+
function validateChannelAccess(channelId, config) {
|
|
67
|
+
if (channelId.startsWith("D")) {
|
|
68
|
+
if (!config.dmEnabled) return {
|
|
69
|
+
allowed: false,
|
|
70
|
+
reason: "DM access is not enabled for this tool"
|
|
71
|
+
};
|
|
72
|
+
return { allowed: true };
|
|
73
|
+
}
|
|
74
|
+
if (config.channelAccessMode === "all") return { allowed: true };
|
|
75
|
+
if (config.channelIds.includes(channelId)) return { allowed: true };
|
|
76
|
+
return {
|
|
77
|
+
allowed: false,
|
|
78
|
+
reason: "Channel not in allowed list"
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
//#endregion
|
|
83
|
+
export { resolveChannelId, resolveWorkspaceToken, searchUsersByName, validateChannelAccess };
|
|
@@ -104,7 +104,10 @@ app.openapi(createProtectedRoute({
|
|
|
104
104
|
"im:write",
|
|
105
105
|
"team:read",
|
|
106
106
|
"users:read",
|
|
107
|
-
"users:read.email"
|
|
107
|
+
"users:read.email",
|
|
108
|
+
"search:read.public",
|
|
109
|
+
"search:read.files",
|
|
110
|
+
"search:read.users"
|
|
108
111
|
].join(",");
|
|
109
112
|
const state = createOAuthState(tenantId);
|
|
110
113
|
const slackAuthUrl = new URL("https://slack.com/oauth/v2/authorize");
|
|
@@ -6,7 +6,7 @@ import { getBotMemberChannels, getSlackChannels, getSlackClient, revokeSlackToke
|
|
|
6
6
|
import "../services/index.js";
|
|
7
7
|
import { requireChannelMemberOrAdmin, requireWorkspaceAdmin } from "../middleware/permissions.js";
|
|
8
8
|
import { OpenAPIHono, z } from "@hono/zod-openapi";
|
|
9
|
-
import { WorkAppSlackAgentConfigRequestSchema, deleteAllWorkAppSlackChannelAgentConfigsByTeam, deleteAllWorkAppSlackUserMappingsByTeam, deleteWorkAppSlackChannelAgentConfig, deleteWorkAppSlackWorkspaceByNangoConnectionId, findWorkAppSlackChannelAgentConfig, findWorkAppSlackWorkspaceByTeamId, listWorkAppSlackChannelAgentConfigsByTeam, listWorkAppSlackUserMappingsByTeam, updateWorkAppSlackWorkspace, upsertWorkAppSlackChannelAgentConfig } from "@inkeep/agents-core";
|
|
9
|
+
import { WorkAppSlackAgentConfigRequestSchema, deleteAllSlackMcpToolAccessConfigsByTenant, deleteAllWorkAppSlackChannelAgentConfigsByTeam, deleteAllWorkAppSlackUserMappingsByTeam, deleteWorkAppSlackChannelAgentConfig, deleteWorkAppSlackWorkspaceByNangoConnectionId, findWorkAppSlackChannelAgentConfig, findWorkAppSlackWorkspaceByTeamId, listWorkAppSlackChannelAgentConfigsByTeam, listWorkAppSlackUserMappingsByTeam, updateWorkAppSlackWorkspace, upsertWorkAppSlackChannelAgentConfig } from "@inkeep/agents-core";
|
|
10
10
|
import { createProtectedRoute, inheritedWorkAppsAuth } from "@inkeep/agents-core/middleware";
|
|
11
11
|
|
|
12
12
|
//#region src/slack/routes/workspaces.ts
|
|
@@ -376,6 +376,11 @@ app.openapi(createProtectedRoute({
|
|
|
376
376
|
teamId,
|
|
377
377
|
deletedMappings
|
|
378
378
|
}, "Deleted user mappings for uninstalled workspace");
|
|
379
|
+
const deletedMcpConfigs = await deleteAllSlackMcpToolAccessConfigsByTenant(runDbClient_default)(tenantId);
|
|
380
|
+
if (deletedMcpConfigs > 0) logger.info({
|
|
381
|
+
teamId,
|
|
382
|
+
deletedMcpConfigs
|
|
383
|
+
}, "Deleted MCP tool access configs for uninstalled workspace");
|
|
379
384
|
if (await deleteWorkAppSlackWorkspaceByNangoConnectionId(runDbClient_default)(connectionId)) logger.info({ connectionId }, "Deleted workspace from database");
|
|
380
385
|
if (!await deleteWorkspaceInstallation(connectionId)) logger.error({ connectionId }, "deleteWorkspaceInstallation returned false (DB already cleaned up)");
|
|
381
386
|
clearWorkspaceConnectionCache(teamId);
|
|
@@ -32,6 +32,53 @@ declare function getSlackUserInfo(client: WebClient, userId: string): Promise<{
|
|
|
32
32
|
tz: string | undefined;
|
|
33
33
|
tzOffset: number | undefined;
|
|
34
34
|
} | null>;
|
|
35
|
+
/**
|
|
36
|
+
* Look up a Slack user by email address.
|
|
37
|
+
*
|
|
38
|
+
* @param client - Authenticated Slack WebClient
|
|
39
|
+
* @param email - Email address to look up
|
|
40
|
+
* @returns User profile object, or null if not found
|
|
41
|
+
*/
|
|
42
|
+
declare function getSlackUserByEmail(client: WebClient, email: string): Promise<{
|
|
43
|
+
id: string | undefined;
|
|
44
|
+
name: string | undefined;
|
|
45
|
+
realName: string | undefined;
|
|
46
|
+
displayName: string | undefined;
|
|
47
|
+
email: string | undefined;
|
|
48
|
+
isAdmin: boolean | undefined;
|
|
49
|
+
isOwner: boolean | undefined;
|
|
50
|
+
avatar: string | undefined;
|
|
51
|
+
tz: string | undefined;
|
|
52
|
+
tzOffset: number | undefined;
|
|
53
|
+
} | null>;
|
|
54
|
+
/**
|
|
55
|
+
* List all members of a Slack workspace.
|
|
56
|
+
*
|
|
57
|
+
* Automatically paginates through results. Excludes deleted users and bots by default.
|
|
58
|
+
*
|
|
59
|
+
* @param client - Authenticated Slack WebClient
|
|
60
|
+
* @param options - Optional filters
|
|
61
|
+
* @param options.includeDeleted - Include deactivated users (default: false)
|
|
62
|
+
* @param options.includeBots - Include bot users (default: false)
|
|
63
|
+
* @param options.limit - Maximum number of users to return
|
|
64
|
+
* @returns Array of user profile objects
|
|
65
|
+
*/
|
|
66
|
+
declare function getSlackUsers(client: WebClient, options?: {
|
|
67
|
+
includeDeleted?: boolean;
|
|
68
|
+
includeBots?: boolean;
|
|
69
|
+
limit?: number;
|
|
70
|
+
}): Promise<{
|
|
71
|
+
id: string | undefined;
|
|
72
|
+
name: string | undefined;
|
|
73
|
+
realName: string | undefined;
|
|
74
|
+
displayName: string | undefined;
|
|
75
|
+
email: string | undefined;
|
|
76
|
+
isAdmin: boolean | undefined;
|
|
77
|
+
isOwner: boolean | undefined;
|
|
78
|
+
avatar: string | undefined;
|
|
79
|
+
tz: string | undefined;
|
|
80
|
+
tzOffset: number | undefined;
|
|
81
|
+
}[]>;
|
|
35
82
|
/**
|
|
36
83
|
* Fetch workspace (team) information from Slack.
|
|
37
84
|
*
|
|
@@ -132,6 +179,25 @@ declare function postMessageInThread(client: WebClient, channel: string, threadT
|
|
|
132
179
|
* @returns true if user is a member, false otherwise
|
|
133
180
|
*/
|
|
134
181
|
declare function checkUserIsChannelMember(client: WebClient, channelId: string, userId: string): Promise<boolean>;
|
|
182
|
+
/**
|
|
183
|
+
* Open (or resume) a direct message conversation with a Slack user.
|
|
184
|
+
*
|
|
185
|
+
* @param client - Authenticated Slack WebClient
|
|
186
|
+
* @param userId - Slack user ID to DM (e.g., U0ABC123)
|
|
187
|
+
* @returns The DM channel ID
|
|
188
|
+
*/
|
|
189
|
+
declare function openDmConversation(client: WebClient, userId: string): Promise<string>;
|
|
190
|
+
/**
|
|
191
|
+
* Validate that the bot is a member of the given channels.
|
|
192
|
+
*
|
|
193
|
+
* @param client - Authenticated Slack WebClient
|
|
194
|
+
* @param channelIds - Channel IDs to validate
|
|
195
|
+
* @returns Object with valid and invalid channel ID arrays
|
|
196
|
+
*/
|
|
197
|
+
declare function validateBotChannelMembership(client: WebClient, channelIds: string[]): Promise<{
|
|
198
|
+
valid: string[];
|
|
199
|
+
invalid: string[];
|
|
200
|
+
}>;
|
|
135
201
|
/**
|
|
136
202
|
* Revoke a Slack bot token.
|
|
137
203
|
*
|
|
@@ -143,4 +209,4 @@ declare function checkUserIsChannelMember(client: WebClient, channelId: string,
|
|
|
143
209
|
*/
|
|
144
210
|
declare function revokeSlackToken(token: string): Promise<boolean>;
|
|
145
211
|
//#endregion
|
|
146
|
-
export { checkUserIsChannelMember, getBotMemberChannels, getSlackChannelInfo, getSlackChannels, getSlackClient, getSlackTeamInfo, getSlackUserInfo, postMessage, postMessageInThread, revokeSlackToken };
|
|
212
|
+
export { checkUserIsChannelMember, getBotMemberChannels, getSlackChannelInfo, getSlackChannels, getSlackClient, getSlackTeamInfo, getSlackUserByEmail, getSlackUserInfo, getSlackUsers, openDmConversation, postMessage, postMessageInThread, revokeSlackToken, validateBotChannelMembership };
|
|
@@ -63,6 +63,71 @@ async function getSlackUserInfo(client, userId) {
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
/**
|
|
66
|
+
* Look up a Slack user by email address.
|
|
67
|
+
*
|
|
68
|
+
* @param client - Authenticated Slack WebClient
|
|
69
|
+
* @param email - Email address to look up
|
|
70
|
+
* @returns User profile object, or null if not found
|
|
71
|
+
*/
|
|
72
|
+
async function getSlackUserByEmail(client, email) {
|
|
73
|
+
const result = await client.users.lookupByEmail({ email });
|
|
74
|
+
if (result.ok && result.user) return {
|
|
75
|
+
id: result.user.id,
|
|
76
|
+
name: result.user.name,
|
|
77
|
+
realName: result.user.real_name,
|
|
78
|
+
displayName: result.user.profile?.display_name,
|
|
79
|
+
email: result.user.profile?.email,
|
|
80
|
+
isAdmin: result.user.is_admin,
|
|
81
|
+
isOwner: result.user.is_owner,
|
|
82
|
+
avatar: result.user.profile?.image_72,
|
|
83
|
+
tz: result.user.tz,
|
|
84
|
+
tzOffset: result.user.tz_offset
|
|
85
|
+
};
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* List all members of a Slack workspace.
|
|
90
|
+
*
|
|
91
|
+
* Automatically paginates through results. Excludes deleted users and bots by default.
|
|
92
|
+
*
|
|
93
|
+
* @param client - Authenticated Slack WebClient
|
|
94
|
+
* @param options - Optional filters
|
|
95
|
+
* @param options.includeDeleted - Include deactivated users (default: false)
|
|
96
|
+
* @param options.includeBots - Include bot users (default: false)
|
|
97
|
+
* @param options.limit - Maximum number of users to return
|
|
98
|
+
* @returns Array of user profile objects
|
|
99
|
+
*/
|
|
100
|
+
async function getSlackUsers(client, options = {}) {
|
|
101
|
+
const { includeDeleted = false, includeBots = false, limit } = options;
|
|
102
|
+
return (await paginateSlack({
|
|
103
|
+
fetchPage: (cursor) => client.users.list({
|
|
104
|
+
limit: 200,
|
|
105
|
+
cursor
|
|
106
|
+
}),
|
|
107
|
+
extractItems: (result) => {
|
|
108
|
+
if (!result.ok) throw new Error(`Slack API error during user listing: ${result.error}`);
|
|
109
|
+
return result.members ?? [];
|
|
110
|
+
},
|
|
111
|
+
getNextCursor: (result) => result.response_metadata?.next_cursor || void 0,
|
|
112
|
+
limit
|
|
113
|
+
})).filter((user) => {
|
|
114
|
+
if (!includeDeleted && user.deleted) return false;
|
|
115
|
+
if (!includeBots && (user.is_bot || user.id === "USLACKBOT")) return false;
|
|
116
|
+
return true;
|
|
117
|
+
}).map((user) => ({
|
|
118
|
+
id: user.id,
|
|
119
|
+
name: user.name,
|
|
120
|
+
realName: user.real_name,
|
|
121
|
+
displayName: user.profile?.display_name,
|
|
122
|
+
email: user.profile?.email,
|
|
123
|
+
isAdmin: user.is_admin,
|
|
124
|
+
isOwner: user.is_owner,
|
|
125
|
+
avatar: user.profile?.image_72,
|
|
126
|
+
tz: user.tz,
|
|
127
|
+
tzOffset: user.tz_offset
|
|
128
|
+
}));
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
66
131
|
* Fetch workspace (team) information from Slack.
|
|
67
132
|
*
|
|
68
133
|
* @param client - Authenticated Slack WebClient
|
|
@@ -272,6 +337,37 @@ async function checkUserIsChannelMember(client, channelId, userId) {
|
|
|
272
337
|
})).includes(userId);
|
|
273
338
|
}
|
|
274
339
|
/**
|
|
340
|
+
* Open (or resume) a direct message conversation with a Slack user.
|
|
341
|
+
*
|
|
342
|
+
* @param client - Authenticated Slack WebClient
|
|
343
|
+
* @param userId - Slack user ID to DM (e.g., U0ABC123)
|
|
344
|
+
* @returns The DM channel ID
|
|
345
|
+
*/
|
|
346
|
+
async function openDmConversation(client, userId) {
|
|
347
|
+
const channelId = (await client.conversations.open({ users: userId })).channel?.id;
|
|
348
|
+
if (!channelId) throw new Error(`Failed to open DM conversation with user ${userId}`);
|
|
349
|
+
return channelId;
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Validate that the bot is a member of the given channels.
|
|
353
|
+
*
|
|
354
|
+
* @param client - Authenticated Slack WebClient
|
|
355
|
+
* @param channelIds - Channel IDs to validate
|
|
356
|
+
* @returns Object with valid and invalid channel ID arrays
|
|
357
|
+
*/
|
|
358
|
+
async function validateBotChannelMembership(client, channelIds) {
|
|
359
|
+
const botChannels = await getBotMemberChannels(client);
|
|
360
|
+
const memberChannelIds = new Set(botChannels.filter((ch) => !!ch.id).map((ch) => ch.id));
|
|
361
|
+
const valid = [];
|
|
362
|
+
const invalid = [];
|
|
363
|
+
for (const id of channelIds) if (memberChannelIds.has(id)) valid.push(id);
|
|
364
|
+
else invalid.push(id);
|
|
365
|
+
return {
|
|
366
|
+
valid,
|
|
367
|
+
invalid
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
/**
|
|
275
371
|
* Revoke a Slack bot token.
|
|
276
372
|
*
|
|
277
373
|
* This should be called when uninstalling a workspace to ensure
|
|
@@ -301,4 +397,4 @@ async function revokeSlackToken(token) {
|
|
|
301
397
|
}
|
|
302
398
|
|
|
303
399
|
//#endregion
|
|
304
|
-
export { checkUserIsChannelMember, getBotMemberChannels, getSlackChannelInfo, getSlackChannels, getSlackClient, getSlackTeamInfo, getSlackUserInfo, postMessage, postMessageInThread, revokeSlackToken };
|
|
400
|
+
export { checkUserIsChannelMember, getBotMemberChannels, getSlackChannelInfo, getSlackChannels, getSlackClient, getSlackTeamInfo, getSlackUserByEmail, getSlackUserInfo, getSlackUsers, openDmConversation, postMessage, postMessageInThread, revokeSlackToken, validateBotChannelMembership };
|
|
@@ -74,7 +74,7 @@ function saveSlackDevConfig(config) {
|
|
|
74
74
|
const configPath = getDevConfigPath();
|
|
75
75
|
if (!configPath) return false;
|
|
76
76
|
try {
|
|
77
|
-
writeFileSync(configPath, JSON.stringify(config, null, 2)
|
|
77
|
+
writeFileSync(configPath, `${JSON.stringify(config, null, 2)}\n`, "utf-8");
|
|
78
78
|
cachedConfig = config;
|
|
79
79
|
cacheExpiresAt = Date.now() + CACHE_TTL_MS;
|
|
80
80
|
return true;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AgentResolutionParams, ResolvedAgentConfig, getAgentConfigSources, lookupAgentName, lookupProjectName, resolveEffectiveAgent } from "./agent-resolution.js";
|
|
2
2
|
import { AgentConfigSources, ContextBlockParams, ToolApprovalButtonValue, ToolApprovalButtonValueSchema, buildCitationsBlock, buildDataArtifactBlocks, buildDataComponentBlocks, buildSummaryBreadcrumbBlock, buildToolApprovalBlocks, buildToolApprovalDoneBlocks, buildToolApprovalExpiredBlocks, buildToolOutputErrorBlock, createAlreadyLinkedMessage, createContextBlock, createContextBlockFromText, createCreateInkeepAccountMessage, createErrorMessage, createNotLinkedMessage, createSmartLinkMessage, createStatusMessage, createUnlinkSuccessMessage, createUpdatedHelpMessage } from "./blocks/index.js";
|
|
3
|
-
import { checkUserIsChannelMember, getBotMemberChannels, getSlackChannelInfo, getSlackChannels, getSlackClient, getSlackTeamInfo, getSlackUserInfo, postMessage, postMessageInThread, revokeSlackToken } from "./client.js";
|
|
3
|
+
import { checkUserIsChannelMember, getBotMemberChannels, getSlackChannelInfo, getSlackChannels, getSlackClient, getSlackTeamInfo, getSlackUserByEmail, getSlackUserInfo, getSlackUsers, openDmConversation, postMessage, postMessageInThread, revokeSlackToken, validateBotChannelMembership } from "./client.js";
|
|
4
4
|
import { DefaultAgentConfig, SlackWorkspaceConnection, WorkspaceInstallData, clearWorkspaceConnectionCache, computeWorkspaceConnectionId, createConnectSession, deleteWorkspaceInstallation, findWorkspaceConnectionByTeamId, getConnectionAccessToken, getSlackIntegrationId, getSlackNango, getWorkspaceDefaultAgent, getWorkspaceDefaultAgentFromNango, listWorkspaceInstallations, setWorkspaceDefaultAgent, storeWorkspaceInstallation, updateConnectionMetadata } from "./nango.js";
|
|
5
5
|
import { SlackCommandPayload, SlackCommandResponse } from "./types.js";
|
|
6
6
|
import { handleAgentPickerCommand, handleCommand, handleHelpCommand, handleLinkCommand, handleQuestionCommand, handleStatusCommand, handleUnlinkCommand } from "./commands/index.js";
|
|
@@ -15,4 +15,4 @@ import { handleModalSubmission } from "./events/modal-submission.js";
|
|
|
15
15
|
import "./events/index.js";
|
|
16
16
|
import { parseSlackCommandBody, parseSlackEventBody, verifySlackRequest } from "./security.js";
|
|
17
17
|
import { getBotTokenForTeam, setBotTokenForTeam } from "./workspace-tokens.js";
|
|
18
|
-
export { AgentConfigSources, AgentOption, AgentResolutionParams, BuildAgentSelectorModalParams, BuildMessageShortcutModalParams, ContextBlockParams, DefaultAgentConfig, InlineSelectorMetadata, ModalMetadata, PublicExecutionParams, ResolvedAgentConfig, SlackCommandPayload, SlackCommandResponse, SlackErrorType, SlackWorkspaceConnection, StreamResult, ToolApprovalButtonValue, ToolApprovalButtonValueSchema, WorkspaceInstallData, buildAgentSelectorModal, buildCitationsBlock, buildDataArtifactBlocks, buildDataComponentBlocks, buildMessageShortcutModal, buildSummaryBreadcrumbBlock, buildToolApprovalBlocks, buildToolApprovalDoneBlocks, buildToolApprovalExpiredBlocks, buildToolOutputErrorBlock, checkIfBotThread, checkUserIsChannelMember, classifyError, clearWorkspaceConnectionCache, computeWorkspaceConnectionId, createAlreadyLinkedMessage, createConnectSession, createContextBlock, createContextBlockFromText, createCreateInkeepAccountMessage, createErrorMessage, createNotLinkedMessage, createSmartLinkMessage, createStatusMessage, createUnlinkSuccessMessage, createUpdatedHelpMessage, deleteWorkspaceInstallation, executeAgentPublicly, extractApiErrorMessage, fetchAgentsForProject, fetchProjectsForTenant, findCachedUserMapping, findWorkspaceConnectionByTeamId, generateSlackConversationId, getAgentConfigSources, getBotMemberChannels, getBotTokenForTeam, getChannelAgentConfig, getConnectionAccessToken, getSlackChannelInfo, getSlackChannels, getSlackClient, getSlackIntegrationId, getSlackNango, getSlackTeamInfo, getSlackUserInfo, getThreadContext, getUserFriendlyErrorMessage, getWorkspaceDefaultAgent, getWorkspaceDefaultAgentFromNango, handleAgentPickerCommand, handleAppMention, handleCommand, handleDirectMessage, handleHelpCommand, handleLinkCommand, handleMessageShortcut, handleModalSubmission, handleOpenAgentSelectorModal, handleQuestionCommand, handleStatusCommand, handleToolApproval, handleUnlinkCommand, listWorkspaceInstallations, lookupAgentName, lookupProjectName, markdownToMrkdwn, parseSlackCommandBody, parseSlackEventBody, postMessage, postMessageInThread, resolveEffectiveAgent, revokeSlackToken, sendResponseUrlMessage, setBotTokenForTeam, setWorkspaceDefaultAgent, storeWorkspaceInstallation, streamAgentResponse, updateConnectionMetadata, verifySlackRequest };
|
|
18
|
+
export { AgentConfigSources, AgentOption, AgentResolutionParams, BuildAgentSelectorModalParams, BuildMessageShortcutModalParams, ContextBlockParams, DefaultAgentConfig, InlineSelectorMetadata, ModalMetadata, PublicExecutionParams, ResolvedAgentConfig, SlackCommandPayload, SlackCommandResponse, SlackErrorType, SlackWorkspaceConnection, StreamResult, ToolApprovalButtonValue, ToolApprovalButtonValueSchema, WorkspaceInstallData, buildAgentSelectorModal, buildCitationsBlock, buildDataArtifactBlocks, buildDataComponentBlocks, buildMessageShortcutModal, buildSummaryBreadcrumbBlock, buildToolApprovalBlocks, buildToolApprovalDoneBlocks, buildToolApprovalExpiredBlocks, buildToolOutputErrorBlock, checkIfBotThread, checkUserIsChannelMember, classifyError, clearWorkspaceConnectionCache, computeWorkspaceConnectionId, createAlreadyLinkedMessage, createConnectSession, createContextBlock, createContextBlockFromText, createCreateInkeepAccountMessage, createErrorMessage, createNotLinkedMessage, createSmartLinkMessage, createStatusMessage, createUnlinkSuccessMessage, createUpdatedHelpMessage, deleteWorkspaceInstallation, executeAgentPublicly, extractApiErrorMessage, fetchAgentsForProject, fetchProjectsForTenant, findCachedUserMapping, findWorkspaceConnectionByTeamId, generateSlackConversationId, getAgentConfigSources, getBotMemberChannels, getBotTokenForTeam, getChannelAgentConfig, getConnectionAccessToken, getSlackChannelInfo, getSlackChannels, getSlackClient, getSlackIntegrationId, getSlackNango, getSlackTeamInfo, getSlackUserByEmail, getSlackUserInfo, getSlackUsers, getThreadContext, getUserFriendlyErrorMessage, getWorkspaceDefaultAgent, getWorkspaceDefaultAgentFromNango, handleAgentPickerCommand, handleAppMention, handleCommand, handleDirectMessage, handleHelpCommand, handleLinkCommand, handleMessageShortcut, handleModalSubmission, handleOpenAgentSelectorModal, handleQuestionCommand, handleStatusCommand, handleToolApproval, handleUnlinkCommand, listWorkspaceInstallations, lookupAgentName, lookupProjectName, markdownToMrkdwn, openDmConversation, parseSlackCommandBody, parseSlackEventBody, postMessage, postMessageInThread, resolveEffectiveAgent, revokeSlackToken, sendResponseUrlMessage, setBotTokenForTeam, setWorkspaceDefaultAgent, storeWorkspaceInstallation, streamAgentResponse, updateConnectionMetadata, validateBotChannelMembership, verifySlackRequest };
|
|
@@ -2,7 +2,7 @@ import { clearWorkspaceConnectionCache, computeWorkspaceConnectionId, createConn
|
|
|
2
2
|
import { SlackErrorType, checkIfBotThread, classifyError, extractApiErrorMessage, fetchAgentsForProject, fetchProjectsForTenant, findCachedUserMapping, generateSlackConversationId, getChannelAgentConfig, getThreadContext, getUserFriendlyErrorMessage, markdownToMrkdwn, sendResponseUrlMessage } from "./events/utils.js";
|
|
3
3
|
import { getAgentConfigSources, lookupAgentName, lookupProjectName, resolveEffectiveAgent } from "./agent-resolution.js";
|
|
4
4
|
import { ToolApprovalButtonValueSchema, buildCitationsBlock, buildDataArtifactBlocks, buildDataComponentBlocks, buildSummaryBreadcrumbBlock, buildToolApprovalBlocks, buildToolApprovalDoneBlocks, buildToolApprovalExpiredBlocks, buildToolOutputErrorBlock, createAlreadyLinkedMessage, createContextBlock, createContextBlockFromText, createCreateInkeepAccountMessage, createErrorMessage, createNotLinkedMessage, createSmartLinkMessage, createStatusMessage, createUnlinkSuccessMessage, createUpdatedHelpMessage } from "./blocks/index.js";
|
|
5
|
-
import { checkUserIsChannelMember, getBotMemberChannels, getSlackChannelInfo, getSlackChannels, getSlackClient, getSlackTeamInfo, getSlackUserInfo, postMessage, postMessageInThread, revokeSlackToken } from "./client.js";
|
|
5
|
+
import { checkUserIsChannelMember, getBotMemberChannels, getSlackChannelInfo, getSlackChannels, getSlackClient, getSlackTeamInfo, getSlackUserByEmail, getSlackUserInfo, getSlackUsers, openDmConversation, postMessage, postMessageInThread, revokeSlackToken, validateBotChannelMembership } from "./client.js";
|
|
6
6
|
import { streamAgentResponse } from "./events/streaming.js";
|
|
7
7
|
import { executeAgentPublicly } from "./events/execution.js";
|
|
8
8
|
import { buildAgentSelectorModal, buildMessageShortcutModal } from "./modals.js";
|
|
@@ -15,4 +15,4 @@ import "./events/index.js";
|
|
|
15
15
|
import { parseSlackCommandBody, parseSlackEventBody, verifySlackRequest } from "./security.js";
|
|
16
16
|
import { getBotTokenForTeam, setBotTokenForTeam } from "./workspace-tokens.js";
|
|
17
17
|
|
|
18
|
-
export { SlackErrorType, ToolApprovalButtonValueSchema, buildAgentSelectorModal, buildCitationsBlock, buildDataArtifactBlocks, buildDataComponentBlocks, buildMessageShortcutModal, buildSummaryBreadcrumbBlock, buildToolApprovalBlocks, buildToolApprovalDoneBlocks, buildToolApprovalExpiredBlocks, buildToolOutputErrorBlock, checkIfBotThread, checkUserIsChannelMember, classifyError, clearWorkspaceConnectionCache, computeWorkspaceConnectionId, createAlreadyLinkedMessage, createConnectSession, createContextBlock, createContextBlockFromText, createCreateInkeepAccountMessage, createErrorMessage, createNotLinkedMessage, createSmartLinkMessage, createStatusMessage, createUnlinkSuccessMessage, createUpdatedHelpMessage, deleteWorkspaceInstallation, executeAgentPublicly, extractApiErrorMessage, fetchAgentsForProject, fetchProjectsForTenant, findCachedUserMapping, findWorkspaceConnectionByTeamId, generateSlackConversationId, getAgentConfigSources, getBotMemberChannels, getBotTokenForTeam, getChannelAgentConfig, getConnectionAccessToken, getSlackChannelInfo, getSlackChannels, getSlackClient, getSlackIntegrationId, getSlackNango, getSlackTeamInfo, getSlackUserInfo, getThreadContext, getUserFriendlyErrorMessage, getWorkspaceDefaultAgent, getWorkspaceDefaultAgentFromNango, handleAgentPickerCommand, handleAppMention, handleCommand, handleDirectMessage, handleHelpCommand, handleLinkCommand, handleMessageShortcut, handleModalSubmission, handleOpenAgentSelectorModal, handleQuestionCommand, handleStatusCommand, handleToolApproval, handleUnlinkCommand, listWorkspaceInstallations, lookupAgentName, lookupProjectName, markdownToMrkdwn, parseSlackCommandBody, parseSlackEventBody, postMessage, postMessageInThread, resolveEffectiveAgent, revokeSlackToken, sendResponseUrlMessage, setBotTokenForTeam, setWorkspaceDefaultAgent, storeWorkspaceInstallation, streamAgentResponse, updateConnectionMetadata, verifySlackRequest };
|
|
18
|
+
export { SlackErrorType, ToolApprovalButtonValueSchema, buildAgentSelectorModal, buildCitationsBlock, buildDataArtifactBlocks, buildDataComponentBlocks, buildMessageShortcutModal, buildSummaryBreadcrumbBlock, buildToolApprovalBlocks, buildToolApprovalDoneBlocks, buildToolApprovalExpiredBlocks, buildToolOutputErrorBlock, checkIfBotThread, checkUserIsChannelMember, classifyError, clearWorkspaceConnectionCache, computeWorkspaceConnectionId, createAlreadyLinkedMessage, createConnectSession, createContextBlock, createContextBlockFromText, createCreateInkeepAccountMessage, createErrorMessage, createNotLinkedMessage, createSmartLinkMessage, createStatusMessage, createUnlinkSuccessMessage, createUpdatedHelpMessage, deleteWorkspaceInstallation, executeAgentPublicly, extractApiErrorMessage, fetchAgentsForProject, fetchProjectsForTenant, findCachedUserMapping, findWorkspaceConnectionByTeamId, generateSlackConversationId, getAgentConfigSources, getBotMemberChannels, getBotTokenForTeam, getChannelAgentConfig, getConnectionAccessToken, getSlackChannelInfo, getSlackChannels, getSlackClient, getSlackIntegrationId, getSlackNango, getSlackTeamInfo, getSlackUserByEmail, getSlackUserInfo, getSlackUsers, getThreadContext, getUserFriendlyErrorMessage, getWorkspaceDefaultAgent, getWorkspaceDefaultAgentFromNango, handleAgentPickerCommand, handleAppMention, handleCommand, handleDirectMessage, handleHelpCommand, handleLinkCommand, handleMessageShortcut, handleModalSubmission, handleOpenAgentSelectorModal, handleQuestionCommand, handleStatusCommand, handleToolApproval, handleUnlinkCommand, listWorkspaceInstallations, lookupAgentName, lookupProjectName, markdownToMrkdwn, openDmConversation, parseSlackCommandBody, parseSlackEventBody, postMessage, postMessageInThread, resolveEffectiveAgent, revokeSlackToken, sendResponseUrlMessage, setBotTokenForTeam, setWorkspaceDefaultAgent, storeWorkspaceInstallation, streamAgentResponse, updateConnectionMetadata, validateBotChannelMembership, verifySlackRequest };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inkeep/agents-work-apps",
|
|
3
|
-
"version": "0.58.
|
|
3
|
+
"version": "0.58.4",
|
|
4
4
|
"description": "First party integrations for Inkeep Agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"jose": "^6.1.0",
|
|
34
34
|
"minimatch": "^10.2.1",
|
|
35
35
|
"slack-block-builder": "^2.8.0",
|
|
36
|
-
"@inkeep/agents-core": "0.58.
|
|
36
|
+
"@inkeep/agents-core": "0.58.4"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@hono/zod-openapi": "^1.1.5",
|