@inkeep/agents-work-apps 0.50.1 → 0.50.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/_virtual/rolldown_runtime.js +32 -0
- package/dist/env.d.ts +4 -2
- package/dist/env.js +2 -1
- 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 +39 -39
- package/dist/slack/services/agent-resolution.d.ts +1 -0
- package/dist/slack/services/agent-resolution.js +8 -4
- package/dist/slack/services/blocks/index.js +7 -11
- package/dist/slack/services/commands/index.js +15 -7
- 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 +25 -21
- package/dist/slack/services/events/index.d.ts +2 -2
- package/dist/slack/services/events/index.js +2 -2
- package/dist/slack/services/events/modal-submission.js +18 -10
- package/dist/slack/services/events/streaming.js +7 -5
- package/dist/slack/services/events/utils.d.ts +2 -1
- package/dist/slack/services/events/utils.js +16 -9
- package/dist/slack/services/index.d.ts +2 -2
- package/dist/slack/services/index.js +3 -3
- package/dist/slack/services/modals.js +4 -4
- package/dist/slack/services/nango.d.ts +3 -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/dist/slack/tracer.d.ts +2 -0
- package/dist/slack/tracer.js +3 -1
- 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,30 +40,15 @@ 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(),
|
|
61
46
|
agentName: z.string().optional(),
|
|
62
|
-
projectName: z.string().optional()
|
|
47
|
+
projectName: z.string().optional(),
|
|
48
|
+
grantAccessToMembers: z.boolean().optional()
|
|
63
49
|
});
|
|
64
50
|
const WorkspaceSettingsSchema = z.object({ defaultAgent: ChannelAgentConfigSchema.optional() });
|
|
65
|
-
app.openapi(
|
|
51
|
+
app.openapi(createProtectedRoute({
|
|
66
52
|
method: "get",
|
|
67
53
|
path: "/",
|
|
68
54
|
summary: "List Workspaces",
|
|
@@ -73,6 +59,7 @@ app.openapi(createRoute({
|
|
|
73
59
|
"Slack",
|
|
74
60
|
"Workspaces"
|
|
75
61
|
],
|
|
62
|
+
permission: inheritedWorkAppsAuth(),
|
|
76
63
|
responses: { 200: {
|
|
77
64
|
description: "List of workspaces",
|
|
78
65
|
content: { "application/json": { schema: z.object({ workspaces: z.array(z.object({
|
|
@@ -111,7 +98,7 @@ app.openapi(createRoute({
|
|
|
111
98
|
return c.json({ workspaces: [] });
|
|
112
99
|
}
|
|
113
100
|
});
|
|
114
|
-
app.openapi(
|
|
101
|
+
app.openapi(createProtectedRoute({
|
|
115
102
|
method: "get",
|
|
116
103
|
path: "/{teamId}",
|
|
117
104
|
summary: "Get Workspace",
|
|
@@ -122,6 +109,7 @@ app.openapi(createRoute({
|
|
|
122
109
|
"Slack",
|
|
123
110
|
"Workspaces"
|
|
124
111
|
],
|
|
112
|
+
permission: inheritedWorkAppsAuth(),
|
|
125
113
|
request: { params: z.object({ teamId: z.string() }) },
|
|
126
114
|
responses: {
|
|
127
115
|
200: {
|
|
@@ -155,7 +143,7 @@ app.openapi(createRoute({
|
|
|
155
143
|
defaultAgent
|
|
156
144
|
});
|
|
157
145
|
});
|
|
158
|
-
app.openapi(
|
|
146
|
+
app.openapi(createProtectedRoute({
|
|
159
147
|
method: "get",
|
|
160
148
|
path: "/{teamId}/settings",
|
|
161
149
|
summary: "Get Workspace Settings",
|
|
@@ -166,6 +154,7 @@ app.openapi(createRoute({
|
|
|
166
154
|
"Slack",
|
|
167
155
|
"Workspaces"
|
|
168
156
|
],
|
|
157
|
+
permission: inheritedWorkAppsAuth(),
|
|
169
158
|
request: { params: z.object({ teamId: z.string() }) },
|
|
170
159
|
responses: { 200: {
|
|
171
160
|
description: "Workspace settings",
|
|
@@ -188,7 +177,7 @@ app.openapi(createRoute({
|
|
|
188
177
|
};
|
|
189
178
|
return c.json({ defaultAgent });
|
|
190
179
|
});
|
|
191
|
-
app.openapi(
|
|
180
|
+
app.openapi(createProtectedRoute({
|
|
192
181
|
method: "put",
|
|
193
182
|
path: "/{teamId}/settings",
|
|
194
183
|
summary: "Update Workspace Settings",
|
|
@@ -199,6 +188,7 @@ app.openapi(createRoute({
|
|
|
199
188
|
"Slack",
|
|
200
189
|
"Workspaces"
|
|
201
190
|
],
|
|
191
|
+
permission: requireWorkspaceAdmin(),
|
|
202
192
|
request: {
|
|
203
193
|
params: z.object({ teamId: z.string() }),
|
|
204
194
|
body: { content: { "application/json": { schema: WorkspaceSettingsSchema } } }
|
|
@@ -232,7 +222,7 @@ app.openapi(createRoute({
|
|
|
232
222
|
}
|
|
233
223
|
return c.json({ success: true });
|
|
234
224
|
});
|
|
235
|
-
app.openapi(
|
|
225
|
+
app.openapi(createProtectedRoute({
|
|
236
226
|
method: "delete",
|
|
237
227
|
path: "/{teamId}",
|
|
238
228
|
summary: "Uninstall Workspace",
|
|
@@ -243,6 +233,7 @@ app.openapi(createRoute({
|
|
|
243
233
|
"Slack",
|
|
244
234
|
"Workspaces"
|
|
245
235
|
],
|
|
236
|
+
permission: requireWorkspaceAdmin(),
|
|
246
237
|
request: { params: z.object({ teamId: z.string() }) },
|
|
247
238
|
responses: {
|
|
248
239
|
200: {
|
|
@@ -301,7 +292,7 @@ app.openapi(createRoute({
|
|
|
301
292
|
return c.json({ error: "Failed to uninstall workspace" }, 500);
|
|
302
293
|
}
|
|
303
294
|
});
|
|
304
|
-
app.openapi(
|
|
295
|
+
app.openapi(createProtectedRoute({
|
|
305
296
|
method: "get",
|
|
306
297
|
path: "/{teamId}/channels",
|
|
307
298
|
summary: "List Channels",
|
|
@@ -312,6 +303,7 @@ app.openapi(createRoute({
|
|
|
312
303
|
"Slack",
|
|
313
304
|
"Channels"
|
|
314
305
|
],
|
|
306
|
+
permission: inheritedWorkAppsAuth(),
|
|
315
307
|
request: {
|
|
316
308
|
params: z.object({ teamId: z.string() }),
|
|
317
309
|
query: z.object({
|
|
@@ -369,7 +361,8 @@ app.openapi(createRoute({
|
|
|
369
361
|
agentConfig: config ? {
|
|
370
362
|
projectId: config.projectId,
|
|
371
363
|
agentId: config.agentId,
|
|
372
|
-
agentName: config.agentName || void 0
|
|
364
|
+
agentName: config.agentName || void 0,
|
|
365
|
+
grantAccessToMembers: config.grantAccessToMembers
|
|
373
366
|
} : void 0
|
|
374
367
|
};
|
|
375
368
|
});
|
|
@@ -385,7 +378,7 @@ app.openapi(createRoute({
|
|
|
385
378
|
return c.json({ error: "Failed to list channels" }, 500);
|
|
386
379
|
}
|
|
387
380
|
});
|
|
388
|
-
app.openapi(
|
|
381
|
+
app.openapi(createProtectedRoute({
|
|
389
382
|
method: "get",
|
|
390
383
|
path: "/{teamId}/channels/{channelId}/settings",
|
|
391
384
|
summary: "Get Channel Settings",
|
|
@@ -396,6 +389,7 @@ app.openapi(createRoute({
|
|
|
396
389
|
"Slack",
|
|
397
390
|
"Channels"
|
|
398
391
|
],
|
|
392
|
+
permission: inheritedWorkAppsAuth(),
|
|
399
393
|
request: { params: z.object({
|
|
400
394
|
teamId: z.string(),
|
|
401
395
|
channelId: z.string()
|
|
@@ -424,11 +418,12 @@ app.openapi(createRoute({
|
|
|
424
418
|
agentConfig: config ? {
|
|
425
419
|
projectId: config.projectId,
|
|
426
420
|
agentId: config.agentId,
|
|
427
|
-
agentName: config.agentName || void 0
|
|
421
|
+
agentName: config.agentName || void 0,
|
|
422
|
+
grantAccessToMembers: config.grantAccessToMembers
|
|
428
423
|
} : void 0
|
|
429
424
|
});
|
|
430
425
|
});
|
|
431
|
-
app.openapi(
|
|
426
|
+
app.openapi(createProtectedRoute({
|
|
432
427
|
method: "put",
|
|
433
428
|
path: "/{teamId}/channels/{channelId}/settings",
|
|
434
429
|
summary: "Set Channel Default Agent",
|
|
@@ -439,6 +434,7 @@ app.openapi(createRoute({
|
|
|
439
434
|
"Slack",
|
|
440
435
|
"Channels"
|
|
441
436
|
],
|
|
437
|
+
permission: requireChannelMemberOrAdmin(),
|
|
442
438
|
request: {
|
|
443
439
|
params: z.object({
|
|
444
440
|
teamId: z.string(),
|
|
@@ -478,6 +474,7 @@ app.openapi(createRoute({
|
|
|
478
474
|
projectId: body.agentConfig.projectId,
|
|
479
475
|
agentId: body.agentConfig.agentId,
|
|
480
476
|
agentName: body.agentConfig.agentName,
|
|
477
|
+
grantAccessToMembers: body.agentConfig.grantAccessToMembers ?? true,
|
|
481
478
|
enabled: true
|
|
482
479
|
});
|
|
483
480
|
logger.info({
|
|
@@ -490,11 +487,7 @@ app.openapi(createRoute({
|
|
|
490
487
|
configId: config.id
|
|
491
488
|
});
|
|
492
489
|
});
|
|
493
|
-
app.
|
|
494
|
-
if (c.req.method === "PUT" || c.req.method === "DELETE") return requireWorkspaceAdmin()(c, next);
|
|
495
|
-
return next();
|
|
496
|
-
});
|
|
497
|
-
app.openapi(createRoute({
|
|
490
|
+
app.openapi(createProtectedRoute({
|
|
498
491
|
method: "put",
|
|
499
492
|
path: "/{teamId}/channels/bulk",
|
|
500
493
|
summary: "Bulk Set Channel Agents",
|
|
@@ -505,6 +498,7 @@ app.openapi(createRoute({
|
|
|
505
498
|
"Slack",
|
|
506
499
|
"Channels"
|
|
507
500
|
],
|
|
501
|
+
permission: requireWorkspaceAdmin(),
|
|
508
502
|
request: {
|
|
509
503
|
params: z.object({ teamId: z.string() }),
|
|
510
504
|
body: { content: { "application/json": { schema: z.object({
|
|
@@ -567,6 +561,7 @@ app.openapi(createRoute({
|
|
|
567
561
|
projectId: body.agentConfig.projectId,
|
|
568
562
|
agentId: body.agentConfig.agentId,
|
|
569
563
|
agentName: body.agentConfig.agentName,
|
|
564
|
+
grantAccessToMembers: body.agentConfig.grantAccessToMembers ?? true,
|
|
570
565
|
enabled: true
|
|
571
566
|
});
|
|
572
567
|
updated++;
|
|
@@ -591,7 +586,7 @@ app.openapi(createRoute({
|
|
|
591
586
|
errors: errors.length > 0 ? errors : void 0
|
|
592
587
|
});
|
|
593
588
|
});
|
|
594
|
-
app.openapi(
|
|
589
|
+
app.openapi(createProtectedRoute({
|
|
595
590
|
method: "delete",
|
|
596
591
|
path: "/{teamId}/channels/bulk",
|
|
597
592
|
summary: "Bulk Remove Channel Configs",
|
|
@@ -602,6 +597,7 @@ app.openapi(createRoute({
|
|
|
602
597
|
"Slack",
|
|
603
598
|
"Channels"
|
|
604
599
|
],
|
|
600
|
+
permission: requireWorkspaceAdmin(),
|
|
605
601
|
request: {
|
|
606
602
|
params: z.object({ teamId: z.string() }),
|
|
607
603
|
body: { content: { "application/json": { schema: z.object({ channelIds: z.array(z.string()).min(1) }) } } }
|
|
@@ -638,7 +634,7 @@ app.openapi(createRoute({
|
|
|
638
634
|
removed
|
|
639
635
|
});
|
|
640
636
|
});
|
|
641
|
-
app.openapi(
|
|
637
|
+
app.openapi(createProtectedRoute({
|
|
642
638
|
method: "delete",
|
|
643
639
|
path: "/{teamId}/channels/{channelId}/settings",
|
|
644
640
|
summary: "Remove Channel Config",
|
|
@@ -649,6 +645,7 @@ app.openapi(createRoute({
|
|
|
649
645
|
"Slack",
|
|
650
646
|
"Channels"
|
|
651
647
|
],
|
|
648
|
+
permission: requireChannelMemberOrAdmin(),
|
|
652
649
|
request: { params: z.object({
|
|
653
650
|
teamId: z.string(),
|
|
654
651
|
channelId: z.string()
|
|
@@ -673,7 +670,7 @@ app.openapi(createRoute({
|
|
|
673
670
|
}, "Removed channel agent config");
|
|
674
671
|
return c.json({ success: deleted });
|
|
675
672
|
});
|
|
676
|
-
app.openapi(
|
|
673
|
+
app.openapi(createProtectedRoute({
|
|
677
674
|
method: "get",
|
|
678
675
|
path: "/{teamId}/users",
|
|
679
676
|
summary: "List Linked Users",
|
|
@@ -684,6 +681,7 @@ app.openapi(createRoute({
|
|
|
684
681
|
"Slack",
|
|
685
682
|
"Users"
|
|
686
683
|
],
|
|
684
|
+
permission: inheritedWorkAppsAuth(),
|
|
687
685
|
request: { params: z.object({ teamId: z.string() }) },
|
|
688
686
|
responses: { 200: {
|
|
689
687
|
description: "List of linked users",
|
|
@@ -723,7 +721,7 @@ app.openapi(createRoute({
|
|
|
723
721
|
lastUsedAt: link.lastUsedAt || void 0
|
|
724
722
|
})) });
|
|
725
723
|
});
|
|
726
|
-
app.openapi(
|
|
724
|
+
app.openapi(createProtectedRoute({
|
|
727
725
|
method: "get",
|
|
728
726
|
path: "/{teamId}/health",
|
|
729
727
|
summary: "Check Workspace Health",
|
|
@@ -734,6 +732,7 @@ app.openapi(createRoute({
|
|
|
734
732
|
"Slack",
|
|
735
733
|
"Workspaces"
|
|
736
734
|
],
|
|
735
|
+
permission: inheritedWorkAppsAuth(),
|
|
737
736
|
request: { params: z.object({ teamId: z.string() }) },
|
|
738
737
|
responses: {
|
|
739
738
|
200: {
|
|
@@ -819,7 +818,7 @@ app.openapi(createRoute({
|
|
|
819
818
|
});
|
|
820
819
|
}
|
|
821
820
|
});
|
|
822
|
-
app.openapi(
|
|
821
|
+
app.openapi(createProtectedRoute({
|
|
823
822
|
method: "post",
|
|
824
823
|
path: "/{teamId}/test-message",
|
|
825
824
|
summary: "Send Test Message",
|
|
@@ -830,6 +829,7 @@ app.openapi(createRoute({
|
|
|
830
829
|
"Slack",
|
|
831
830
|
"Workspaces"
|
|
832
831
|
],
|
|
832
|
+
permission: requireWorkspaceAdmin(),
|
|
833
833
|
request: {
|
|
834
834
|
params: z.object({ teamId: z.string() }),
|
|
835
835
|
body: { content: { "application/json": { schema: z.object({
|
|
@@ -859,7 +859,7 @@ app.openapi(createRoute({
|
|
|
859
859
|
}, 404);
|
|
860
860
|
try {
|
|
861
861
|
const slackClient = getSlackClient(workspace.botToken);
|
|
862
|
-
const testMessage = message || "
|
|
862
|
+
const testMessage = message || "*Test message from Inkeep*\n\nYour Slack integration is working correctly.";
|
|
863
863
|
const result = await slackClient.chat.postMessage({
|
|
864
864
|
channel: channelId,
|
|
865
865
|
text: testMessage,
|
|
@@ -37,7 +37,8 @@ async function resolveEffectiveAgent(params) {
|
|
|
37
37
|
projectId: channelConfig.projectId,
|
|
38
38
|
agentId: channelConfig.agentId,
|
|
39
39
|
agentName: channelConfig.agentName || void 0,
|
|
40
|
-
source: "channel"
|
|
40
|
+
source: "channel",
|
|
41
|
+
grantAccessToMembers: channelConfig.grantAccessToMembers
|
|
41
42
|
};
|
|
42
43
|
}
|
|
43
44
|
}
|
|
@@ -52,7 +53,8 @@ async function resolveEffectiveAgent(params) {
|
|
|
52
53
|
projectId: workspaceConfig.projectId,
|
|
53
54
|
agentId: workspaceConfig.agentId,
|
|
54
55
|
agentName: workspaceConfig.agentName,
|
|
55
|
-
source: "workspace"
|
|
56
|
+
source: "workspace",
|
|
57
|
+
grantAccessToMembers: workspaceConfig.grantAccessToMembers ?? true
|
|
56
58
|
};
|
|
57
59
|
}
|
|
58
60
|
logger.debug({
|
|
@@ -78,7 +80,8 @@ async function getAgentConfigSources(params) {
|
|
|
78
80
|
projectId: config.projectId,
|
|
79
81
|
agentId: config.agentId,
|
|
80
82
|
agentName: config.agentName || void 0,
|
|
81
|
-
source: "channel"
|
|
83
|
+
source: "channel",
|
|
84
|
+
grantAccessToMembers: config.grantAccessToMembers
|
|
82
85
|
};
|
|
83
86
|
}
|
|
84
87
|
const wsConfig = await getWorkspaceDefaultAgentFromNango(teamId);
|
|
@@ -86,7 +89,8 @@ async function getAgentConfigSources(params) {
|
|
|
86
89
|
projectId: wsConfig.projectId,
|
|
87
90
|
agentId: wsConfig.agentId,
|
|
88
91
|
agentName: wsConfig.agentName,
|
|
89
|
-
source: "workspace"
|
|
92
|
+
source: "workspace",
|
|
93
|
+
grantAccessToMembers: wsConfig.grantAccessToMembers ?? true
|
|
90
94
|
};
|
|
91
95
|
return {
|
|
92
96
|
channelConfig,
|
|
@@ -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
|
|
@@ -6,9 +6,9 @@ import { resolveEffectiveAgent } from "../agent-resolution.js";
|
|
|
6
6
|
import { SlackStrings } from "../../i18n/strings.js";
|
|
7
7
|
import { createAlreadyLinkedMessage, createContextBlock, createErrorMessage, createJwtLinkMessage, createNotLinkedMessage, createStatusMessage, createUnlinkSuccessMessage, createUpdatedHelpMessage } from "../blocks/index.js";
|
|
8
8
|
import { getSlackClient } from "../client.js";
|
|
9
|
-
import { fetchAgentsForProject, fetchProjectsForTenant, getChannelAgentConfig, sendResponseUrlMessage } from "../events/utils.js";
|
|
9
|
+
import { extractApiErrorMessage, 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";
|
|
@@ -232,28 +232,34 @@ async function handleQuestionCommand(payload, question, _dashboardUrl, tenantId)
|
|
|
232
232
|
id: resolvedAgent.agentId,
|
|
233
233
|
name: resolvedAgent.agentName || null,
|
|
234
234
|
projectId: resolvedAgent.projectId
|
|
235
|
-
}, question, userTenantId
|
|
235
|
+
}, question, userTenantId, {
|
|
236
|
+
slackAuthorized: resolvedAgent.grantAccessToMembers,
|
|
237
|
+
slackAuthSource: resolvedAgent.source === "none" ? void 0 : resolvedAgent.source,
|
|
238
|
+
slackChannelId: payload.channelId,
|
|
239
|
+
slackAuthorizedProjectId: resolvedAgent.projectId
|
|
240
|
+
}).catch((error) => {
|
|
236
241
|
logger.error({ error }, "Background execution promise rejected");
|
|
237
242
|
}).finally(() => flushTraces());
|
|
238
243
|
const waitUntil = await getWaitUntil();
|
|
239
244
|
if (waitUntil) waitUntil(questionWork);
|
|
240
245
|
return {};
|
|
241
246
|
}
|
|
242
|
-
async function executeAgentInBackground(payload, existingLink, targetAgent, question, tenantId) {
|
|
247
|
+
async function executeAgentInBackground(payload, existingLink, targetAgent, question, tenantId, channelAuth) {
|
|
243
248
|
try {
|
|
244
249
|
const slackUserToken = await signSlackUserToken({
|
|
245
250
|
inkeepUserId: existingLink.inkeepUserId,
|
|
246
251
|
tenantId,
|
|
247
252
|
slackTeamId: payload.teamId,
|
|
248
253
|
slackUserId: payload.userId,
|
|
249
|
-
slackEnterpriseId: payload.enterpriseId
|
|
254
|
+
slackEnterpriseId: payload.enterpriseId,
|
|
255
|
+
...channelAuth
|
|
250
256
|
});
|
|
251
257
|
const apiBaseUrl = env.INKEEP_AGENTS_API_URL || "http://localhost:3002";
|
|
252
258
|
const controller = new AbortController();
|
|
253
259
|
const timeout = setTimeout(() => controller.abort(), 3e4);
|
|
254
260
|
let response;
|
|
255
261
|
try {
|
|
256
|
-
response = await
|
|
262
|
+
response = await getInProcessFetch()(`${apiBaseUrl}/run/api/chat`, {
|
|
257
263
|
method: "POST",
|
|
258
264
|
headers: {
|
|
259
265
|
"Content-Type": "application/json",
|
|
@@ -295,9 +301,11 @@ async function executeAgentInBackground(payload, existingLink, targetAgent, ques
|
|
|
295
301
|
agentId: targetAgent.id,
|
|
296
302
|
projectId: targetAgent.projectId
|
|
297
303
|
}, "Run API call failed");
|
|
304
|
+
const apiMessage = extractApiErrorMessage(errorText);
|
|
305
|
+
const errorMessage = apiMessage ? `*Error.* ${apiMessage}` : `Failed to run agent: ${response.status} ${response.statusText}`;
|
|
298
306
|
await sendResponseUrlMessage(payload.responseUrl, {
|
|
299
307
|
response_type: "ephemeral",
|
|
300
|
-
text:
|
|
308
|
+
text: errorMessage
|
|
301
309
|
});
|
|
302
310
|
} else {
|
|
303
311
|
const result = await response.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 };
|