@inkeep/agents-work-apps 0.50.1 → 0.50.3
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/_virtual/rolldown_runtime.js +32 -0
- package/dist/env.d.ts +2 -0
- package/dist/env.js +1 -0
- package/dist/github/mcp/auth.d.ts +2 -2
- package/dist/github/mcp/index.d.ts +2 -2
- 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/node_modules/.pnpm/@slack_logger@4.0.0/node_modules/@slack/logger/dist/index.js +89 -0
- package/dist/node_modules/.pnpm/@slack_socket-mode@2.0.5/node_modules/@slack/socket-mode/dist/package.js +85 -0
- package/dist/node_modules/.pnpm/@slack_socket-mode@2.0.5/node_modules/@slack/socket-mode/dist/src/SlackWebSocket.js +223 -0
- package/dist/node_modules/.pnpm/@slack_socket-mode@2.0.5/node_modules/@slack/socket-mode/dist/src/SocketModeClient.js +367 -0
- package/dist/node_modules/.pnpm/@slack_socket-mode@2.0.5/node_modules/@slack/socket-mode/dist/src/UnrecoverableSocketModeStartError.js +20 -0
- package/dist/node_modules/.pnpm/@slack_socket-mode@2.0.5/node_modules/@slack/socket-mode/dist/src/errors.js +71 -0
- package/dist/node_modules/.pnpm/@slack_socket-mode@2.0.5/node_modules/@slack/socket-mode/dist/src/index.js +44 -0
- package/dist/node_modules/.pnpm/@slack_socket-mode@2.0.5/node_modules/@slack/socket-mode/dist/src/logger.js +32 -0
- package/dist/node_modules/.pnpm/eventemitter3@5.0.1/node_modules/eventemitter3/index.js +241 -0
- package/dist/node_modules/.pnpm/ws@8.19.0/node_modules/ws/index.js +23 -0
- package/dist/node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/buffer-util.js +107 -0
- package/dist/node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/constants.js +29 -0
- package/dist/node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/event-target.js +226 -0
- package/dist/node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/extension.js +150 -0
- package/dist/node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/limiter.js +57 -0
- package/dist/node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/permessage-deflate.js +342 -0
- package/dist/node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/receiver.js +457 -0
- package/dist/node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/sender.js +505 -0
- package/dist/node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/stream.js +123 -0
- package/dist/node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/subprotocol.js +46 -0
- package/dist/node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/validation.js +203 -0
- package/dist/node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/websocket-server.js +385 -0
- package/dist/node_modules/.pnpm/ws@8.19.0/node_modules/ws/lib/websocket.js +985 -0
- package/dist/slack/dispatcher.d.ts +16 -0
- package/dist/slack/dispatcher.js +335 -0
- package/dist/slack/i18n/strings.d.ts +5 -5
- package/dist/slack/i18n/strings.js +9 -9
- package/dist/slack/index.d.ts +3 -1
- package/dist/slack/index.js +4 -2
- package/dist/slack/middleware/permissions.js +120 -107
- package/dist/slack/routes/events.js +10 -328
- package/dist/slack/routes/oauth.js +6 -3
- package/dist/slack/routes/users.js +12 -6
- package/dist/slack/routes/workspaces.js +31 -36
- package/dist/slack/services/blocks/index.js +7 -11
- package/dist/slack/services/commands/index.js +2 -2
- package/dist/slack/services/dev-config.d.ts +23 -0
- package/dist/slack/services/dev-config.js +91 -0
- package/dist/slack/services/events/app-mention.js +6 -17
- package/dist/slack/services/events/modal-submission.js +5 -5
- package/dist/slack/services/events/streaming.js +4 -3
- package/dist/slack/services/events/utils.js +8 -8
- package/dist/slack/services/index.js +1 -1
- package/dist/slack/services/modals.js +4 -4
- package/dist/slack/services/nango.d.ts +2 -0
- package/dist/slack/services/nango.js +84 -2
- package/dist/slack/socket-mode.d.ts +4 -0
- package/dist/slack/socket-mode.js +130 -0
- package/package.json +3 -2
|
@@ -2,8 +2,9 @@ import { getLogger } from "../../logger.js";
|
|
|
2
2
|
import runDbClient_default from "../../db/runDbClient.js";
|
|
3
3
|
import { createConnectSession } from "../services/nango.js";
|
|
4
4
|
import "../services/index.js";
|
|
5
|
-
import { OpenAPIHono,
|
|
5
|
+
import { OpenAPIHono, z } from "@hono/zod-openapi";
|
|
6
6
|
import { createWorkAppSlackUserMapping, deleteWorkAppSlackUserMapping, findWorkAppSlackUserMapping, findWorkAppSlackUserMappingByInkeepUserId, verifySlackLinkToken } from "@inkeep/agents-core";
|
|
7
|
+
import { createProtectedRoute, inheritedWorkAppsAuth } from "@inkeep/agents-core/middleware";
|
|
7
8
|
|
|
8
9
|
//#region src/slack/routes/users.ts
|
|
9
10
|
/**
|
|
@@ -29,7 +30,7 @@ function isAuthorizedForUser(c, requestedUserId) {
|
|
|
29
30
|
return false;
|
|
30
31
|
}
|
|
31
32
|
const app = new OpenAPIHono();
|
|
32
|
-
app.openapi(
|
|
33
|
+
app.openapi(createProtectedRoute({
|
|
33
34
|
method: "get",
|
|
34
35
|
path: "/link-status",
|
|
35
36
|
summary: "Check Link Status",
|
|
@@ -40,6 +41,7 @@ app.openapi(createRoute({
|
|
|
40
41
|
"Slack",
|
|
41
42
|
"Users"
|
|
42
43
|
],
|
|
44
|
+
permission: inheritedWorkAppsAuth(),
|
|
43
45
|
request: { query: z.object({
|
|
44
46
|
slackUserId: z.string(),
|
|
45
47
|
slackTeamId: z.string(),
|
|
@@ -67,7 +69,7 @@ app.openapi(createRoute({
|
|
|
67
69
|
});
|
|
68
70
|
return c.json({ linked: false });
|
|
69
71
|
});
|
|
70
|
-
app.openapi(
|
|
72
|
+
app.openapi(createProtectedRoute({
|
|
71
73
|
method: "post",
|
|
72
74
|
path: "/link/verify-token",
|
|
73
75
|
summary: "Verify Link Token",
|
|
@@ -78,6 +80,7 @@ app.openapi(createRoute({
|
|
|
78
80
|
"Slack",
|
|
79
81
|
"Users"
|
|
80
82
|
],
|
|
83
|
+
permission: inheritedWorkAppsAuth(),
|
|
81
84
|
request: { body: { content: { "application/json": { schema: z.object({
|
|
82
85
|
token: z.string().min(1),
|
|
83
86
|
userId: z.string().min(1),
|
|
@@ -171,7 +174,7 @@ app.openapi(createRoute({
|
|
|
171
174
|
return c.json({ error: "Failed to verify link. Please try again." }, 500);
|
|
172
175
|
}
|
|
173
176
|
});
|
|
174
|
-
app.openapi(
|
|
177
|
+
app.openapi(createProtectedRoute({
|
|
175
178
|
method: "post",
|
|
176
179
|
path: "/connect",
|
|
177
180
|
summary: "Create Nango Connect Session",
|
|
@@ -182,6 +185,7 @@ app.openapi(createRoute({
|
|
|
182
185
|
"Slack",
|
|
183
186
|
"Users"
|
|
184
187
|
],
|
|
188
|
+
permission: inheritedWorkAppsAuth(),
|
|
185
189
|
request: { body: { content: { "application/json": { schema: z.object({
|
|
186
190
|
userId: z.string().describe("Inkeep user ID"),
|
|
187
191
|
userEmail: z.string().optional().describe("User email"),
|
|
@@ -217,7 +221,7 @@ app.openapi(createRoute({
|
|
|
217
221
|
if (!session) return c.json({ error: "Failed to create session" }, 500);
|
|
218
222
|
return c.json(session);
|
|
219
223
|
});
|
|
220
|
-
app.openapi(
|
|
224
|
+
app.openapi(createProtectedRoute({
|
|
221
225
|
method: "post",
|
|
222
226
|
path: "/disconnect",
|
|
223
227
|
summary: "Disconnect User",
|
|
@@ -228,6 +232,7 @@ app.openapi(createRoute({
|
|
|
228
232
|
"Slack",
|
|
229
233
|
"Users"
|
|
230
234
|
],
|
|
235
|
+
permission: inheritedWorkAppsAuth(),
|
|
231
236
|
request: { body: { content: { "application/json": { schema: z.object({
|
|
232
237
|
userId: z.string().optional().describe("Inkeep user ID"),
|
|
233
238
|
slackUserId: z.string().optional().describe("Slack user ID"),
|
|
@@ -285,7 +290,7 @@ app.openapi(createRoute({
|
|
|
285
290
|
return c.json({ error: "Failed to disconnect" }, 500);
|
|
286
291
|
}
|
|
287
292
|
});
|
|
288
|
-
app.openapi(
|
|
293
|
+
app.openapi(createProtectedRoute({
|
|
289
294
|
method: "get",
|
|
290
295
|
path: "/status",
|
|
291
296
|
summary: "Get Connection Status",
|
|
@@ -296,6 +301,7 @@ app.openapi(createRoute({
|
|
|
296
301
|
"Slack",
|
|
297
302
|
"Users"
|
|
298
303
|
],
|
|
304
|
+
permission: inheritedWorkAppsAuth(),
|
|
299
305
|
request: { query: z.object({ userId: z.string().describe("Inkeep user ID") }) },
|
|
300
306
|
responses: {
|
|
301
307
|
200: {
|
|
@@ -4,8 +4,9 @@ import { clearWorkspaceConnectionCache, computeWorkspaceConnectionId, deleteWork
|
|
|
4
4
|
import { getSlackChannels, getSlackClient, revokeSlackToken } from "../services/client.js";
|
|
5
5
|
import "../services/index.js";
|
|
6
6
|
import { requireChannelMemberOrAdmin, requireWorkspaceAdmin } from "../middleware/permissions.js";
|
|
7
|
-
import { OpenAPIHono,
|
|
7
|
+
import { OpenAPIHono, z } from "@hono/zod-openapi";
|
|
8
8
|
import { deleteAllWorkAppSlackChannelAgentConfigsByTeam, deleteAllWorkAppSlackUserMappingsByTeam, deleteWorkAppSlackChannelAgentConfig, deleteWorkAppSlackWorkspaceByNangoConnectionId, findWorkAppSlackChannelAgentConfig, listWorkAppSlackChannelAgentConfigsByTeam, listWorkAppSlackUserMappingsByTeam, upsertWorkAppSlackChannelAgentConfig } from "@inkeep/agents-core";
|
|
9
|
+
import { createProtectedRoute, inheritedWorkAppsAuth } from "@inkeep/agents-core/middleware";
|
|
9
10
|
|
|
10
11
|
//#region src/slack/routes/workspaces.ts
|
|
11
12
|
/**
|
|
@@ -39,22 +40,6 @@ function verifyTenantOwnership(c, workspaceTenantId) {
|
|
|
39
40
|
return sessionTenantId === workspaceTenantId;
|
|
40
41
|
}
|
|
41
42
|
const app = new OpenAPIHono();
|
|
42
|
-
app.use("/:teamId/settings", async (c, next) => {
|
|
43
|
-
if (c.req.method === "PUT") return requireWorkspaceAdmin()(c, next);
|
|
44
|
-
return next();
|
|
45
|
-
});
|
|
46
|
-
app.use("/:teamId", async (c, next) => {
|
|
47
|
-
if (c.req.method === "DELETE") return requireWorkspaceAdmin()(c, next);
|
|
48
|
-
return next();
|
|
49
|
-
});
|
|
50
|
-
app.use("/:teamId/channels/:channelId/settings", async (c, next) => {
|
|
51
|
-
if (c.req.method === "PUT" || c.req.method === "DELETE") return requireChannelMemberOrAdmin()(c, next);
|
|
52
|
-
return next();
|
|
53
|
-
});
|
|
54
|
-
app.use("/:teamId/test-message", async (c, next) => {
|
|
55
|
-
if (c.req.method === "POST") return requireWorkspaceAdmin()(c, next);
|
|
56
|
-
return next();
|
|
57
|
-
});
|
|
58
43
|
const ChannelAgentConfigSchema = z.object({
|
|
59
44
|
projectId: z.string(),
|
|
60
45
|
agentId: z.string(),
|
|
@@ -62,7 +47,7 @@ const ChannelAgentConfigSchema = z.object({
|
|
|
62
47
|
projectName: z.string().optional()
|
|
63
48
|
});
|
|
64
49
|
const WorkspaceSettingsSchema = z.object({ defaultAgent: ChannelAgentConfigSchema.optional() });
|
|
65
|
-
app.openapi(
|
|
50
|
+
app.openapi(createProtectedRoute({
|
|
66
51
|
method: "get",
|
|
67
52
|
path: "/",
|
|
68
53
|
summary: "List Workspaces",
|
|
@@ -73,6 +58,7 @@ app.openapi(createRoute({
|
|
|
73
58
|
"Slack",
|
|
74
59
|
"Workspaces"
|
|
75
60
|
],
|
|
61
|
+
permission: inheritedWorkAppsAuth(),
|
|
76
62
|
responses: { 200: {
|
|
77
63
|
description: "List of workspaces",
|
|
78
64
|
content: { "application/json": { schema: z.object({ workspaces: z.array(z.object({
|
|
@@ -111,7 +97,7 @@ app.openapi(createRoute({
|
|
|
111
97
|
return c.json({ workspaces: [] });
|
|
112
98
|
}
|
|
113
99
|
});
|
|
114
|
-
app.openapi(
|
|
100
|
+
app.openapi(createProtectedRoute({
|
|
115
101
|
method: "get",
|
|
116
102
|
path: "/{teamId}",
|
|
117
103
|
summary: "Get Workspace",
|
|
@@ -122,6 +108,7 @@ app.openapi(createRoute({
|
|
|
122
108
|
"Slack",
|
|
123
109
|
"Workspaces"
|
|
124
110
|
],
|
|
111
|
+
permission: inheritedWorkAppsAuth(),
|
|
125
112
|
request: { params: z.object({ teamId: z.string() }) },
|
|
126
113
|
responses: {
|
|
127
114
|
200: {
|
|
@@ -155,7 +142,7 @@ app.openapi(createRoute({
|
|
|
155
142
|
defaultAgent
|
|
156
143
|
});
|
|
157
144
|
});
|
|
158
|
-
app.openapi(
|
|
145
|
+
app.openapi(createProtectedRoute({
|
|
159
146
|
method: "get",
|
|
160
147
|
path: "/{teamId}/settings",
|
|
161
148
|
summary: "Get Workspace Settings",
|
|
@@ -166,6 +153,7 @@ app.openapi(createRoute({
|
|
|
166
153
|
"Slack",
|
|
167
154
|
"Workspaces"
|
|
168
155
|
],
|
|
156
|
+
permission: inheritedWorkAppsAuth(),
|
|
169
157
|
request: { params: z.object({ teamId: z.string() }) },
|
|
170
158
|
responses: { 200: {
|
|
171
159
|
description: "Workspace settings",
|
|
@@ -188,7 +176,7 @@ app.openapi(createRoute({
|
|
|
188
176
|
};
|
|
189
177
|
return c.json({ defaultAgent });
|
|
190
178
|
});
|
|
191
|
-
app.openapi(
|
|
179
|
+
app.openapi(createProtectedRoute({
|
|
192
180
|
method: "put",
|
|
193
181
|
path: "/{teamId}/settings",
|
|
194
182
|
summary: "Update Workspace Settings",
|
|
@@ -199,6 +187,7 @@ app.openapi(createRoute({
|
|
|
199
187
|
"Slack",
|
|
200
188
|
"Workspaces"
|
|
201
189
|
],
|
|
190
|
+
permission: requireWorkspaceAdmin(),
|
|
202
191
|
request: {
|
|
203
192
|
params: z.object({ teamId: z.string() }),
|
|
204
193
|
body: { content: { "application/json": { schema: WorkspaceSettingsSchema } } }
|
|
@@ -232,7 +221,7 @@ app.openapi(createRoute({
|
|
|
232
221
|
}
|
|
233
222
|
return c.json({ success: true });
|
|
234
223
|
});
|
|
235
|
-
app.openapi(
|
|
224
|
+
app.openapi(createProtectedRoute({
|
|
236
225
|
method: "delete",
|
|
237
226
|
path: "/{teamId}",
|
|
238
227
|
summary: "Uninstall Workspace",
|
|
@@ -243,6 +232,7 @@ app.openapi(createRoute({
|
|
|
243
232
|
"Slack",
|
|
244
233
|
"Workspaces"
|
|
245
234
|
],
|
|
235
|
+
permission: requireWorkspaceAdmin(),
|
|
246
236
|
request: { params: z.object({ teamId: z.string() }) },
|
|
247
237
|
responses: {
|
|
248
238
|
200: {
|
|
@@ -301,7 +291,7 @@ app.openapi(createRoute({
|
|
|
301
291
|
return c.json({ error: "Failed to uninstall workspace" }, 500);
|
|
302
292
|
}
|
|
303
293
|
});
|
|
304
|
-
app.openapi(
|
|
294
|
+
app.openapi(createProtectedRoute({
|
|
305
295
|
method: "get",
|
|
306
296
|
path: "/{teamId}/channels",
|
|
307
297
|
summary: "List Channels",
|
|
@@ -312,6 +302,7 @@ app.openapi(createRoute({
|
|
|
312
302
|
"Slack",
|
|
313
303
|
"Channels"
|
|
314
304
|
],
|
|
305
|
+
permission: inheritedWorkAppsAuth(),
|
|
315
306
|
request: {
|
|
316
307
|
params: z.object({ teamId: z.string() }),
|
|
317
308
|
query: z.object({
|
|
@@ -385,7 +376,7 @@ app.openapi(createRoute({
|
|
|
385
376
|
return c.json({ error: "Failed to list channels" }, 500);
|
|
386
377
|
}
|
|
387
378
|
});
|
|
388
|
-
app.openapi(
|
|
379
|
+
app.openapi(createProtectedRoute({
|
|
389
380
|
method: "get",
|
|
390
381
|
path: "/{teamId}/channels/{channelId}/settings",
|
|
391
382
|
summary: "Get Channel Settings",
|
|
@@ -396,6 +387,7 @@ app.openapi(createRoute({
|
|
|
396
387
|
"Slack",
|
|
397
388
|
"Channels"
|
|
398
389
|
],
|
|
390
|
+
permission: inheritedWorkAppsAuth(),
|
|
399
391
|
request: { params: z.object({
|
|
400
392
|
teamId: z.string(),
|
|
401
393
|
channelId: z.string()
|
|
@@ -428,7 +420,7 @@ app.openapi(createRoute({
|
|
|
428
420
|
} : void 0
|
|
429
421
|
});
|
|
430
422
|
});
|
|
431
|
-
app.openapi(
|
|
423
|
+
app.openapi(createProtectedRoute({
|
|
432
424
|
method: "put",
|
|
433
425
|
path: "/{teamId}/channels/{channelId}/settings",
|
|
434
426
|
summary: "Set Channel Default Agent",
|
|
@@ -439,6 +431,7 @@ app.openapi(createRoute({
|
|
|
439
431
|
"Slack",
|
|
440
432
|
"Channels"
|
|
441
433
|
],
|
|
434
|
+
permission: requireChannelMemberOrAdmin(),
|
|
442
435
|
request: {
|
|
443
436
|
params: z.object({
|
|
444
437
|
teamId: z.string(),
|
|
@@ -490,11 +483,7 @@ app.openapi(createRoute({
|
|
|
490
483
|
configId: config.id
|
|
491
484
|
});
|
|
492
485
|
});
|
|
493
|
-
app.
|
|
494
|
-
if (c.req.method === "PUT" || c.req.method === "DELETE") return requireWorkspaceAdmin()(c, next);
|
|
495
|
-
return next();
|
|
496
|
-
});
|
|
497
|
-
app.openapi(createRoute({
|
|
486
|
+
app.openapi(createProtectedRoute({
|
|
498
487
|
method: "put",
|
|
499
488
|
path: "/{teamId}/channels/bulk",
|
|
500
489
|
summary: "Bulk Set Channel Agents",
|
|
@@ -505,6 +494,7 @@ app.openapi(createRoute({
|
|
|
505
494
|
"Slack",
|
|
506
495
|
"Channels"
|
|
507
496
|
],
|
|
497
|
+
permission: requireWorkspaceAdmin(),
|
|
508
498
|
request: {
|
|
509
499
|
params: z.object({ teamId: z.string() }),
|
|
510
500
|
body: { content: { "application/json": { schema: z.object({
|
|
@@ -591,7 +581,7 @@ app.openapi(createRoute({
|
|
|
591
581
|
errors: errors.length > 0 ? errors : void 0
|
|
592
582
|
});
|
|
593
583
|
});
|
|
594
|
-
app.openapi(
|
|
584
|
+
app.openapi(createProtectedRoute({
|
|
595
585
|
method: "delete",
|
|
596
586
|
path: "/{teamId}/channels/bulk",
|
|
597
587
|
summary: "Bulk Remove Channel Configs",
|
|
@@ -602,6 +592,7 @@ app.openapi(createRoute({
|
|
|
602
592
|
"Slack",
|
|
603
593
|
"Channels"
|
|
604
594
|
],
|
|
595
|
+
permission: requireWorkspaceAdmin(),
|
|
605
596
|
request: {
|
|
606
597
|
params: z.object({ teamId: z.string() }),
|
|
607
598
|
body: { content: { "application/json": { schema: z.object({ channelIds: z.array(z.string()).min(1) }) } } }
|
|
@@ -638,7 +629,7 @@ app.openapi(createRoute({
|
|
|
638
629
|
removed
|
|
639
630
|
});
|
|
640
631
|
});
|
|
641
|
-
app.openapi(
|
|
632
|
+
app.openapi(createProtectedRoute({
|
|
642
633
|
method: "delete",
|
|
643
634
|
path: "/{teamId}/channels/{channelId}/settings",
|
|
644
635
|
summary: "Remove Channel Config",
|
|
@@ -649,6 +640,7 @@ app.openapi(createRoute({
|
|
|
649
640
|
"Slack",
|
|
650
641
|
"Channels"
|
|
651
642
|
],
|
|
643
|
+
permission: requireChannelMemberOrAdmin(),
|
|
652
644
|
request: { params: z.object({
|
|
653
645
|
teamId: z.string(),
|
|
654
646
|
channelId: z.string()
|
|
@@ -673,7 +665,7 @@ app.openapi(createRoute({
|
|
|
673
665
|
}, "Removed channel agent config");
|
|
674
666
|
return c.json({ success: deleted });
|
|
675
667
|
});
|
|
676
|
-
app.openapi(
|
|
668
|
+
app.openapi(createProtectedRoute({
|
|
677
669
|
method: "get",
|
|
678
670
|
path: "/{teamId}/users",
|
|
679
671
|
summary: "List Linked Users",
|
|
@@ -684,6 +676,7 @@ app.openapi(createRoute({
|
|
|
684
676
|
"Slack",
|
|
685
677
|
"Users"
|
|
686
678
|
],
|
|
679
|
+
permission: inheritedWorkAppsAuth(),
|
|
687
680
|
request: { params: z.object({ teamId: z.string() }) },
|
|
688
681
|
responses: { 200: {
|
|
689
682
|
description: "List of linked users",
|
|
@@ -723,7 +716,7 @@ app.openapi(createRoute({
|
|
|
723
716
|
lastUsedAt: link.lastUsedAt || void 0
|
|
724
717
|
})) });
|
|
725
718
|
});
|
|
726
|
-
app.openapi(
|
|
719
|
+
app.openapi(createProtectedRoute({
|
|
727
720
|
method: "get",
|
|
728
721
|
path: "/{teamId}/health",
|
|
729
722
|
summary: "Check Workspace Health",
|
|
@@ -734,6 +727,7 @@ app.openapi(createRoute({
|
|
|
734
727
|
"Slack",
|
|
735
728
|
"Workspaces"
|
|
736
729
|
],
|
|
730
|
+
permission: inheritedWorkAppsAuth(),
|
|
737
731
|
request: { params: z.object({ teamId: z.string() }) },
|
|
738
732
|
responses: {
|
|
739
733
|
200: {
|
|
@@ -819,7 +813,7 @@ app.openapi(createRoute({
|
|
|
819
813
|
});
|
|
820
814
|
}
|
|
821
815
|
});
|
|
822
|
-
app.openapi(
|
|
816
|
+
app.openapi(createProtectedRoute({
|
|
823
817
|
method: "post",
|
|
824
818
|
path: "/{teamId}/test-message",
|
|
825
819
|
summary: "Send Test Message",
|
|
@@ -830,6 +824,7 @@ app.openapi(createRoute({
|
|
|
830
824
|
"Slack",
|
|
831
825
|
"Workspaces"
|
|
832
826
|
],
|
|
827
|
+
permission: requireWorkspaceAdmin(),
|
|
833
828
|
request: {
|
|
834
829
|
params: z.object({ teamId: z.string() }),
|
|
835
830
|
body: { content: { "application/json": { schema: z.object({
|
|
@@ -859,7 +854,7 @@ app.openapi(createRoute({
|
|
|
859
854
|
}, 404);
|
|
860
855
|
try {
|
|
861
856
|
const slackClient = getSlackClient(workspace.botToken);
|
|
862
|
-
const testMessage = message || "
|
|
857
|
+
const testMessage = message || "*Test message from Inkeep*\n\nYour Slack integration is working correctly.";
|
|
863
858
|
const result = await slackClient.chat.postMessage({
|
|
864
859
|
channel: channelId,
|
|
865
860
|
text: testMessage,
|
|
@@ -3,7 +3,7 @@ import { Blocks, Elements, Md, Message } from "slack-block-builder";
|
|
|
3
3
|
|
|
4
4
|
//#region src/slack/services/blocks/index.ts
|
|
5
5
|
function createErrorMessage(message) {
|
|
6
|
-
return Message().blocks(Blocks.Section().text(
|
|
6
|
+
return Message().blocks(Blocks.Section().text(message)).buildToObject();
|
|
7
7
|
}
|
|
8
8
|
function createContextBlock(params) {
|
|
9
9
|
const { agentName, isPrivate = false } = params;
|
|
@@ -40,7 +40,7 @@ function buildConversationResponseBlocks(params) {
|
|
|
40
40
|
type: "context",
|
|
41
41
|
elements: [{
|
|
42
42
|
type: "mrkdwn",
|
|
43
|
-
text:
|
|
43
|
+
text: `*You:* ${userMessage.length > 200 ? `${userMessage.slice(0, 200)}...` : userMessage}`
|
|
44
44
|
}]
|
|
45
45
|
},
|
|
46
46
|
{ type: "divider" },
|
|
@@ -69,27 +69,23 @@ function createUpdatedHelpMessage() {
|
|
|
69
69
|
return Message().blocks(Blocks.Section().text(`${Md.bold(SlackStrings.help.title)}`), Blocks.Section().text(SlackStrings.help.publicSection), Blocks.Divider(), Blocks.Section().text(SlackStrings.help.privateSection), Blocks.Divider(), Blocks.Section().text(SlackStrings.help.otherCommands), Blocks.Divider(), Blocks.Section().text(SlackStrings.help.docsLink)).buildToObject();
|
|
70
70
|
}
|
|
71
71
|
function createAlreadyLinkedMessage(email, linkedAt, dashboardUrl) {
|
|
72
|
-
return Message().blocks(Blocks.Section().text(Md.bold("
|
|
72
|
+
return Message().blocks(Blocks.Section().text(Md.bold("Already linked") + "\n\nYour Slack account is connected to Inkeep.\n\n" + Md.bold("Account:") + ` ${email}\n` + Md.bold("Linked:") + ` ${new Date(linkedAt).toLocaleDateString()}\n\nTo switch accounts, first run \`/inkeep unlink\``), Blocks.Actions().elements(Elements.Button().text(SlackStrings.buttons.openDashboard).url(dashboardUrl).actionId("open_dashboard"))).buildToObject();
|
|
73
73
|
}
|
|
74
74
|
function createUnlinkSuccessMessage() {
|
|
75
|
-
return Message().blocks(Blocks.Section().text(Md.bold("
|
|
75
|
+
return Message().blocks(Blocks.Section().text(Md.bold("Account unlinked") + "\n\nYour Slack account has been disconnected from Inkeep.\n\nRun `/inkeep link` to connect a new account.")).buildToObject();
|
|
76
76
|
}
|
|
77
77
|
function createNotLinkedMessage() {
|
|
78
|
-
return Message().blocks(Blocks.Section().text(Md.bold("
|
|
78
|
+
return Message().blocks(Blocks.Section().text(Md.bold("Not linked") + "\n\nYour Slack account is not connected to Inkeep. Run `/inkeep link` to connect.")).buildToObject();
|
|
79
79
|
}
|
|
80
80
|
function createStatusMessage(email, linkedAt, dashboardUrl, agentConfigs) {
|
|
81
81
|
const { effective } = agentConfigs;
|
|
82
82
|
let agentLine;
|
|
83
83
|
if (effective) agentLine = `${Md.bold("Agent:")} ${effective.agentName || effective.agentId}`;
|
|
84
84
|
else agentLine = `${Md.bold("Agent:")} None configured\n${Md.italic("Ask your admin to set up an agent in the dashboard.")}`;
|
|
85
|
-
return Message().blocks(Blocks.Section().text(Md.bold("
|
|
85
|
+
return Message().blocks(Blocks.Section().text(Md.bold("Connected to Inkeep") + `\n\n${Md.bold("Account:")} ${email}\n${Md.bold("Linked:")} ${new Date(linkedAt).toLocaleDateString()}\n` + agentLine), Blocks.Actions().elements(Elements.Button().text(SlackStrings.buttons.openDashboard).url(dashboardUrl).actionId("open_dashboard"))).buildToObject();
|
|
86
86
|
}
|
|
87
87
|
function createJwtLinkMessage(linkUrl, expiresInMinutes) {
|
|
88
|
-
return Message().blocks(Blocks.Section().text(`${Md.bold("
|
|
89
|
-
• Get personalized responses from AI agents
|
|
90
|
-
• Set your own default agent preferences`), Blocks.Section().text(`${Md.bold("How to link:")}\n1. Click the button below
|
|
91
|
-
2. Sign in to Inkeep (or create an account)
|
|
92
|
-
3. Done! Come back here and start asking questions`), Blocks.Actions().elements(Elements.Button().text("🔗 Link Account").url(linkUrl).actionId("link_account").primary()), Blocks.Context().elements(`This link expires in ${expiresInMinutes} minutes`)).buildToObject();
|
|
88
|
+
return Message().blocks(Blocks.Section().text(`${Md.bold("Link your Inkeep account")}\n\nConnect your Slack and Inkeep accounts to use Inkeep agents.`), Blocks.Actions().elements(Elements.Button().text("Link Account").url(linkUrl).actionId("link_account").primary()), Blocks.Context().elements(`This link expires in ${expiresInMinutes} minutes.`)).buildToObject();
|
|
93
89
|
}
|
|
94
90
|
|
|
95
91
|
//#endregion
|
|
@@ -8,7 +8,7 @@ import { createAlreadyLinkedMessage, createContextBlock, createErrorMessage, cre
|
|
|
8
8
|
import { getSlackClient } from "../client.js";
|
|
9
9
|
import { fetchAgentsForProject, fetchProjectsForTenant, getChannelAgentConfig, sendResponseUrlMessage } from "../events/utils.js";
|
|
10
10
|
import { buildAgentSelectorModal } from "../modals.js";
|
|
11
|
-
import { deleteWorkAppSlackUserMapping, findWorkAppSlackUserMapping, findWorkAppSlackUserMappingBySlackUser, flushTraces, getWaitUntil, signSlackLinkToken, signSlackUserToken } from "@inkeep/agents-core";
|
|
11
|
+
import { deleteWorkAppSlackUserMapping, findWorkAppSlackUserMapping, findWorkAppSlackUserMappingBySlackUser, flushTraces, getInProcessFetch, getWaitUntil, signSlackLinkToken, signSlackUserToken } from "@inkeep/agents-core";
|
|
12
12
|
|
|
13
13
|
//#region src/slack/services/commands/index.ts
|
|
14
14
|
const DEFAULT_CLIENT_ID = "work-apps-slack";
|
|
@@ -253,7 +253,7 @@ async function executeAgentInBackground(payload, existingLink, targetAgent, ques
|
|
|
253
253
|
const timeout = setTimeout(() => controller.abort(), 3e4);
|
|
254
254
|
let response;
|
|
255
255
|
try {
|
|
256
|
-
response = await
|
|
256
|
+
response = await getInProcessFetch()(`${apiBaseUrl}/run/api/chat`, {
|
|
257
257
|
method: "POST",
|
|
258
258
|
headers: {
|
|
259
259
|
"Content-Type": "application/json",
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { DefaultAgentConfig } from "./nango.js";
|
|
2
|
+
|
|
3
|
+
//#region src/slack/services/dev-config.d.ts
|
|
4
|
+
interface SlackDevConfig {
|
|
5
|
+
devId: string;
|
|
6
|
+
appId: string;
|
|
7
|
+
clientId: string;
|
|
8
|
+
clientSecret: string;
|
|
9
|
+
signingSecret: string;
|
|
10
|
+
appToken: string;
|
|
11
|
+
botToken: string;
|
|
12
|
+
teamId: string;
|
|
13
|
+
teamName: string;
|
|
14
|
+
configRefreshToken?: string;
|
|
15
|
+
metadata?: Record<string, string>;
|
|
16
|
+
}
|
|
17
|
+
declare function isSlackDevMode(): boolean;
|
|
18
|
+
declare function loadSlackDevConfig(): SlackDevConfig | null;
|
|
19
|
+
declare function getDevDefaultAgent(config: SlackDevConfig | null): DefaultAgentConfig | null;
|
|
20
|
+
declare function resetDevConfigCache(): void;
|
|
21
|
+
declare function saveSlackDevConfig(config: SlackDevConfig): boolean;
|
|
22
|
+
//#endregion
|
|
23
|
+
export { SlackDevConfig, getDevDefaultAgent, isSlackDevMode, loadSlackDevConfig, resetDevConfigCache, saveSlackDevConfig };
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { env } from "../../env.js";
|
|
2
|
+
import { getLogger } from "../../logger.js";
|
|
3
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { dirname, join, parse } from "node:path";
|
|
5
|
+
|
|
6
|
+
//#region src/slack/services/dev-config.ts
|
|
7
|
+
const logger = getLogger("slack-dev-config");
|
|
8
|
+
const DEV_CONFIG_FILENAME = ".slack-dev.json";
|
|
9
|
+
const CACHE_TTL_MS = 5e3;
|
|
10
|
+
let cachedConfig = null;
|
|
11
|
+
let cacheExpiresAt = 0;
|
|
12
|
+
let resolvedConfigPath;
|
|
13
|
+
function findDevConfigPath() {
|
|
14
|
+
let dir = process.cwd();
|
|
15
|
+
while (true) {
|
|
16
|
+
const candidate = join(dir, DEV_CONFIG_FILENAME);
|
|
17
|
+
if (existsSync(candidate)) return candidate;
|
|
18
|
+
const parent = dirname(dir);
|
|
19
|
+
if (parent === dir || parse(dir).root === dir) break;
|
|
20
|
+
dir = parent;
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
function getDevConfigPath() {
|
|
25
|
+
if (resolvedConfigPath !== void 0) return resolvedConfigPath;
|
|
26
|
+
resolvedConfigPath = findDevConfigPath();
|
|
27
|
+
return resolvedConfigPath;
|
|
28
|
+
}
|
|
29
|
+
let devModeChecked = false;
|
|
30
|
+
let devModeResult = false;
|
|
31
|
+
function isSlackDevMode() {
|
|
32
|
+
if (devModeChecked) return devModeResult;
|
|
33
|
+
devModeResult = env.ENVIRONMENT === "development" && getDevConfigPath() !== null;
|
|
34
|
+
devModeChecked = true;
|
|
35
|
+
return devModeResult;
|
|
36
|
+
}
|
|
37
|
+
function loadSlackDevConfig() {
|
|
38
|
+
if (cachedConfig && Date.now() < cacheExpiresAt) return cachedConfig;
|
|
39
|
+
const configPath = getDevConfigPath();
|
|
40
|
+
if (!configPath) return null;
|
|
41
|
+
try {
|
|
42
|
+
const raw = readFileSync(configPath, "utf-8");
|
|
43
|
+
cachedConfig = JSON.parse(raw);
|
|
44
|
+
cacheExpiresAt = Date.now() + CACHE_TTL_MS;
|
|
45
|
+
return cachedConfig;
|
|
46
|
+
} catch (error) {
|
|
47
|
+
logger.error({
|
|
48
|
+
error,
|
|
49
|
+
configPath
|
|
50
|
+
}, "Failed to read .slack-dev.json");
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function getDevDefaultAgent(config) {
|
|
55
|
+
if (!config?.metadata?.default_agent) return null;
|
|
56
|
+
try {
|
|
57
|
+
return JSON.parse(config.metadata.default_agent);
|
|
58
|
+
} catch (error) {
|
|
59
|
+
logger.warn({
|
|
60
|
+
error,
|
|
61
|
+
rawValue: config.metadata.default_agent
|
|
62
|
+
}, "Failed to parse default_agent metadata as JSON - check .slack-dev.json format");
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function resetDevConfigCache() {
|
|
67
|
+
cachedConfig = null;
|
|
68
|
+
cacheExpiresAt = 0;
|
|
69
|
+
resolvedConfigPath = void 0;
|
|
70
|
+
devModeChecked = false;
|
|
71
|
+
devModeResult = false;
|
|
72
|
+
}
|
|
73
|
+
function saveSlackDevConfig(config) {
|
|
74
|
+
const configPath = getDevConfigPath();
|
|
75
|
+
if (!configPath) return false;
|
|
76
|
+
try {
|
|
77
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
78
|
+
cachedConfig = config;
|
|
79
|
+
cacheExpiresAt = Date.now() + CACHE_TTL_MS;
|
|
80
|
+
return true;
|
|
81
|
+
} catch (error) {
|
|
82
|
+
logger.error({
|
|
83
|
+
error,
|
|
84
|
+
configPath
|
|
85
|
+
}, "Failed to write .slack-dev.json");
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
//#endregion
|
|
91
|
+
export { getDevDefaultAgent, isSlackDevMode, loadSlackDevConfig, resetDevConfigCache, saveSlackDevConfig };
|
|
@@ -5,7 +5,6 @@ import { SlackStrings } from "../../i18n/strings.js";
|
|
|
5
5
|
import { getSlackChannelInfo, getSlackClient, getSlackUserInfo, postMessageInThread } from "../client.js";
|
|
6
6
|
import { checkIfBotThread, classifyError, findCachedUserMapping, formatChannelContext, generateSlackConversationId, getThreadContext, getUserFriendlyErrorMessage, resolveChannelAgentConfig, timedOp } from "./utils.js";
|
|
7
7
|
import { SLACK_SPAN_KEYS, SLACK_SPAN_NAMES, setSpanWithError, tracer } from "../../tracer.js";
|
|
8
|
-
import { getBotTokenForTeam } from "../workspace-tokens.js";
|
|
9
8
|
import { streamAgentResponse } from "./streaming.js";
|
|
10
9
|
import { signSlackUserToken } from "@inkeep/agents-core";
|
|
11
10
|
|
|
@@ -60,19 +59,18 @@ async function handleAppMention(params) {
|
|
|
60
59
|
label: "workspace connection lookup",
|
|
61
60
|
context: { teamId }
|
|
62
61
|
});
|
|
63
|
-
|
|
64
|
-
if (!botToken) {
|
|
62
|
+
if (!workspaceConnection?.botToken) {
|
|
65
63
|
logger.error({ teamId }, "No bot token available — cannot respond to @mention");
|
|
66
64
|
span.end();
|
|
67
65
|
return;
|
|
68
66
|
}
|
|
69
|
-
const tenantId = workspaceConnection
|
|
67
|
+
const { botToken, tenantId } = workspaceConnection;
|
|
70
68
|
if (!tenantId) {
|
|
71
69
|
logger.error({ teamId }, "Workspace connection has no tenantId — workspace may need reinstall");
|
|
72
70
|
await getSlackClient(botToken).chat.postEphemeral({
|
|
73
71
|
channel,
|
|
74
72
|
user: slackUserId,
|
|
75
|
-
text: "
|
|
73
|
+
text: "This workspace is not properly configured. Please reinstall the Slack app from the Inkeep dashboard."
|
|
76
74
|
}).catch((e) => logger.warn({
|
|
77
75
|
error: e,
|
|
78
76
|
channel
|
|
@@ -104,7 +102,7 @@ async function handleAppMention(params) {
|
|
|
104
102
|
channel,
|
|
105
103
|
user: slackUserId,
|
|
106
104
|
thread_ts: isInThread ? threadTs : void 0,
|
|
107
|
-
text:
|
|
105
|
+
text: `No agents configured for this workspace. *<${dashboardUrl}|Set up agents in the dashboard>*`
|
|
108
106
|
});
|
|
109
107
|
span.end();
|
|
110
108
|
return;
|
|
@@ -122,11 +120,7 @@ async function handleAppMention(params) {
|
|
|
122
120
|
channel,
|
|
123
121
|
user: slackUserId,
|
|
124
122
|
thread_ts: isInThread ? threadTs : void 0,
|
|
125
|
-
text:
|
|
126
|
-
|
|
127
|
-
Run \`/inkeep link\` to connect your Slack and Inkeep accounts.
|
|
128
|
-
|
|
129
|
-
This workspace uses: *${agentDisplayName}*`
|
|
123
|
+
text: "*Link your account to use @Inkeep*\n\nRun `/inkeep link` to connect your Slack and Inkeep accounts."
|
|
130
124
|
});
|
|
131
125
|
span.end();
|
|
132
126
|
return;
|
|
@@ -162,12 +156,7 @@ This workspace uses: *${agentDisplayName}*`
|
|
|
162
156
|
channel,
|
|
163
157
|
user: slackUserId,
|
|
164
158
|
thread_ts: threadTs,
|
|
165
|
-
text:
|
|
166
|
-
|
|
167
|
-
Just type your follow-up — no need to mention me in this thread.
|
|
168
|
-
Or use \`@Inkeep <prompt>\` to run a new prompt.
|
|
169
|
-
|
|
170
|
-
_Using: ${agentDisplayName}_`
|
|
159
|
+
text: "*Continue the conversation*\n\nType your follow-up directly in this thread — no need to mention me.\nOr use `@Inkeep <prompt>` to start a new prompt."
|
|
171
160
|
});
|
|
172
161
|
span.end();
|
|
173
162
|
return;
|
|
@@ -6,7 +6,7 @@ import { buildConversationResponseBlocks } from "../blocks/index.js";
|
|
|
6
6
|
import { getSlackClient } from "../client.js";
|
|
7
7
|
import { classifyError, findCachedUserMapping, generateSlackConversationId, getThreadContext, getUserFriendlyErrorMessage, markdownToMrkdwn, sendResponseUrlMessage } from "./utils.js";
|
|
8
8
|
import { SLACK_SPAN_KEYS, SLACK_SPAN_NAMES, setSpanWithError, tracer } from "../../tracer.js";
|
|
9
|
-
import { signSlackUserToken } from "@inkeep/agents-core";
|
|
9
|
+
import { getInProcessFetch, signSlackUserToken } from "@inkeep/agents-core";
|
|
10
10
|
|
|
11
11
|
//#region src/slack/services/events/modal-submission.ts
|
|
12
12
|
/**
|
|
@@ -29,7 +29,7 @@ async function handleModalSubmission(view) {
|
|
|
29
29
|
span.setAttribute(SLACK_SPAN_KEYS.USER_ID, metadata.slackUserId || "");
|
|
30
30
|
span.setAttribute(SLACK_SPAN_KEYS.TENANT_ID, metadata.tenantId || "");
|
|
31
31
|
const values = view.state?.values || {};
|
|
32
|
-
const agentSelectValue = values.
|
|
32
|
+
const agentSelectValue = Object.values(values).map((block) => block.agent_select).find(Boolean);
|
|
33
33
|
const questionValue = values.question_block?.question_input;
|
|
34
34
|
const includeContextValue = values.context_block?.include_context_checkbox;
|
|
35
35
|
const question = questionValue?.value || "";
|
|
@@ -90,7 +90,7 @@ async function handleModalSubmission(view) {
|
|
|
90
90
|
await slackClient.chat.postEphemeral({
|
|
91
91
|
channel: metadata.channel,
|
|
92
92
|
user: metadata.slackUserId,
|
|
93
|
-
text: "
|
|
93
|
+
text: "Link your account first. Run `/inkeep link` to connect."
|
|
94
94
|
});
|
|
95
95
|
span.end();
|
|
96
96
|
return;
|
|
@@ -215,7 +215,7 @@ async function handleFollowUpSubmission(view) {
|
|
|
215
215
|
await slackClient.chat.postEphemeral({
|
|
216
216
|
channel,
|
|
217
217
|
user: slackUserId,
|
|
218
|
-
text: "
|
|
218
|
+
text: "Link your account first. Run `/inkeep link` to connect."
|
|
219
219
|
});
|
|
220
220
|
span.end();
|
|
221
221
|
return;
|
|
@@ -305,7 +305,7 @@ async function callAgentApi(params) {
|
|
|
305
305
|
const timeout = setTimeout(() => controller.abort(), 3e4);
|
|
306
306
|
let response;
|
|
307
307
|
try {
|
|
308
|
-
response = await
|
|
308
|
+
response = await getInProcessFetch()(`${apiBaseUrl}/run/api/chat`, {
|
|
309
309
|
method: "POST",
|
|
310
310
|
headers: {
|
|
311
311
|
"Content-Type": "application/json",
|