@inkeep/agents-work-apps 0.0.0-dev-20260219033751 → 0.0.0-dev-20260219045007
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/github/index.d.ts +3 -3
- package/dist/github/mcp/index.d.ts +2 -2
- 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/middleware/permissions.js +120 -107
- package/dist/slack/routes/oauth.js +6 -3
- package/dist/slack/routes/users.js +12 -6
- package/dist/slack/routes/workspaces.js +30 -35
- package/dist/slack/services/events/utils.d.ts +1 -1
- package/package.json +2 -2
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_types0 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_types0.BlankEnv, hono_types0.BlankSchema, "/">;
|
|
11
|
+
declare const githubRoutes: Hono<hono_types0.BlankEnv, hono_types0.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 };
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
|
-
import * as
|
|
2
|
+
import * as hono_types9 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
8
|
};
|
|
9
|
-
},
|
|
9
|
+
}, hono_types9.BlankSchema, "/">;
|
|
10
10
|
//#endregion
|
|
11
11
|
export { app as default };
|
|
@@ -1,7 +1,7 @@
|
|
|
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/setup.d.ts
|
|
5
|
-
declare const app: Hono<
|
|
5
|
+
declare const app: Hono<hono_types5.BlankEnv, hono_types5.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_types3 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_types3.BlankEnv, hono_types3.BlankSchema, "/">;
|
|
11
11
|
//#endregion
|
|
12
12
|
export { WebhookVerificationResult, app as default, verifyWebhookSignature };
|
|
@@ -4,6 +4,7 @@ import { findWorkspaceConnectionByTeamId } from "../services/nango.js";
|
|
|
4
4
|
import { checkUserIsChannelMember, getSlackClient } from "../services/client.js";
|
|
5
5
|
import { OrgRoles, createApiError, findWorkAppSlackUserMappingByInkeepUserId, getUserOrganizationsFromDb } from "@inkeep/agents-core";
|
|
6
6
|
import { createMiddleware } from "hono/factory";
|
|
7
|
+
import { registerAuthzMeta } from "@inkeep/agents-core/middleware";
|
|
7
8
|
|
|
8
9
|
//#region src/slack/middleware/permissions.ts
|
|
9
10
|
const logger = getLogger("slack-permissions");
|
|
@@ -41,49 +42,57 @@ async function resolveWorkAppTenantContext(c, teamId, userId) {
|
|
|
41
42
|
* Middleware that requires Inkeep org admin/owner role.
|
|
42
43
|
* Use for workspace-level settings that only Inkeep organization admins can modify.
|
|
43
44
|
*/
|
|
44
|
-
const requireWorkspaceAdmin = () =>
|
|
45
|
-
|
|
45
|
+
const requireWorkspaceAdmin = () => {
|
|
46
|
+
const mw = createMiddleware(async (c, next) => {
|
|
47
|
+
if (process.env.ENVIRONMENT === "test" && c.req.header("x-test-bypass-auth") === "true") {
|
|
48
|
+
await next();
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const userId = c.get("userId");
|
|
52
|
+
if (!userId) throw createApiError({
|
|
53
|
+
code: "unauthorized",
|
|
54
|
+
message: "User context not found",
|
|
55
|
+
instance: c.req.path
|
|
56
|
+
});
|
|
57
|
+
if (userId === "system" || userId.startsWith("apikey:")) {
|
|
58
|
+
await next();
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const teamId = c.req.param("teamId") || c.req.param("workspaceId");
|
|
62
|
+
if (teamId && !c.get("tenantRole")) await resolveWorkAppTenantContext(c, teamId, userId);
|
|
63
|
+
const tenantId = c.get("tenantId");
|
|
64
|
+
const tenantRole = c.get("tenantRole");
|
|
65
|
+
if (!tenantId) throw createApiError({
|
|
66
|
+
code: "unauthorized",
|
|
67
|
+
message: "Organization context not found",
|
|
68
|
+
instance: c.req.path
|
|
69
|
+
});
|
|
70
|
+
if (!isOrgAdmin(tenantRole)) {
|
|
71
|
+
logger.warn({
|
|
72
|
+
userId,
|
|
73
|
+
tenantId,
|
|
74
|
+
tenantRole,
|
|
75
|
+
path: c.req.path
|
|
76
|
+
}, "User does not have admin role for workspace operation");
|
|
77
|
+
throw createApiError({
|
|
78
|
+
code: "forbidden",
|
|
79
|
+
message: "Only organization administrators can modify workspace settings",
|
|
80
|
+
instance: c.req.path,
|
|
81
|
+
extensions: {
|
|
82
|
+
requiredRole: "admin or owner",
|
|
83
|
+
currentRole: tenantRole
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
46
87
|
await next();
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
const userId = c.get("userId");
|
|
50
|
-
if (!userId) throw createApiError({
|
|
51
|
-
code: "unauthorized",
|
|
52
|
-
message: "User context not found",
|
|
53
|
-
instance: c.req.path
|
|
54
88
|
});
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
const teamId = c.req.param("teamId") || c.req.param("workspaceId");
|
|
60
|
-
if (teamId && !c.get("tenantRole")) await resolveWorkAppTenantContext(c, teamId, userId);
|
|
61
|
-
const tenantId = c.get("tenantId");
|
|
62
|
-
const tenantRole = c.get("tenantRole");
|
|
63
|
-
if (!tenantId) throw createApiError({
|
|
64
|
-
code: "unauthorized",
|
|
65
|
-
message: "Organization context not found",
|
|
66
|
-
instance: c.req.path
|
|
89
|
+
registerAuthzMeta(mw, {
|
|
90
|
+
resource: "organization",
|
|
91
|
+
permission: "admin",
|
|
92
|
+
description: "Requires Inkeep org admin/owner role to modify workspace settings"
|
|
67
93
|
});
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
userId,
|
|
71
|
-
tenantId,
|
|
72
|
-
tenantRole,
|
|
73
|
-
path: c.req.path
|
|
74
|
-
}, "User does not have admin role for workspace operation");
|
|
75
|
-
throw createApiError({
|
|
76
|
-
code: "forbidden",
|
|
77
|
-
message: "Only organization administrators can modify workspace settings",
|
|
78
|
-
instance: c.req.path,
|
|
79
|
-
extensions: {
|
|
80
|
-
requiredRole: "admin or owner",
|
|
81
|
-
currentRole: tenantRole
|
|
82
|
-
}
|
|
83
|
-
});
|
|
84
|
-
}
|
|
85
|
-
await next();
|
|
86
|
-
});
|
|
94
|
+
return mw;
|
|
95
|
+
};
|
|
87
96
|
/**
|
|
88
97
|
* Middleware that requires either:
|
|
89
98
|
* 1. Org admin/owner role (can modify any channel), OR
|
|
@@ -91,77 +100,81 @@ const requireWorkspaceAdmin = () => createMiddleware(async (c, next) => {
|
|
|
91
100
|
*
|
|
92
101
|
* Use for channel-level settings where members can configure their own channels.
|
|
93
102
|
*/
|
|
94
|
-
const requireChannelMemberOrAdmin = () =>
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
103
|
+
const requireChannelMemberOrAdmin = () => {
|
|
104
|
+
const mw = createMiddleware(async (c, next) => {
|
|
105
|
+
if (process.env.ENVIRONMENT === "test" && c.req.header("x-test-bypass-auth") === "true") {
|
|
106
|
+
await next();
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const userId = c.get("userId");
|
|
110
|
+
if (!userId) throw createApiError({
|
|
111
|
+
code: "unauthorized",
|
|
112
|
+
message: "User context not found",
|
|
113
|
+
instance: c.req.path
|
|
114
|
+
});
|
|
115
|
+
if (userId === "system" || userId.startsWith("apikey:")) {
|
|
116
|
+
await next();
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const teamId = c.req.param("teamId");
|
|
120
|
+
if (teamId && !c.get("tenantRole")) await resolveWorkAppTenantContext(c, teamId, userId);
|
|
121
|
+
const tenantId = c.get("tenantId");
|
|
122
|
+
const tenantRole = c.get("tenantRole");
|
|
123
|
+
if (!tenantId) throw createApiError({
|
|
124
|
+
code: "unauthorized",
|
|
125
|
+
message: "Organization context not found",
|
|
126
|
+
instance: c.req.path
|
|
127
|
+
});
|
|
128
|
+
if (isOrgAdmin(tenantRole)) {
|
|
129
|
+
await next();
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
const channelId = c.req.param("channelId");
|
|
133
|
+
if (!teamId || !channelId) throw createApiError({
|
|
134
|
+
code: "bad_request",
|
|
135
|
+
message: "Team ID and Channel ID are required",
|
|
136
|
+
instance: c.req.path
|
|
137
|
+
});
|
|
138
|
+
const userMapping = (await findWorkAppSlackUserMappingByInkeepUserId(runDbClient_default)(userId)).find((m) => m.tenantId === tenantId && m.slackTeamId === teamId);
|
|
139
|
+
if (!userMapping) throw createApiError({
|
|
140
|
+
code: "forbidden",
|
|
141
|
+
message: "You must link your Slack account to modify channel settings. Use /inkeep link in Slack.",
|
|
142
|
+
instance: c.req.path
|
|
143
|
+
});
|
|
144
|
+
const workspace = await findWorkspaceConnectionByTeamId(teamId);
|
|
145
|
+
if (!workspace?.botToken) throw createApiError({
|
|
146
|
+
code: "not_found",
|
|
147
|
+
message: "Slack workspace not found or not properly configured",
|
|
148
|
+
instance: c.req.path
|
|
149
|
+
});
|
|
150
|
+
if (!await checkUserIsChannelMember(getSlackClient(workspace.botToken), channelId, userMapping.slackUserId)) {
|
|
151
|
+
logger.info({
|
|
152
|
+
userId,
|
|
153
|
+
slackUserId: userMapping.slackUserId,
|
|
154
|
+
channelId,
|
|
155
|
+
teamId
|
|
156
|
+
}, "User is not a member of the channel");
|
|
157
|
+
throw createApiError({
|
|
158
|
+
code: "forbidden",
|
|
159
|
+
message: "You can only configure channels you are a member of",
|
|
160
|
+
instance: c.req.path,
|
|
161
|
+
extensions: {
|
|
162
|
+
channelId,
|
|
163
|
+
reason: "not_channel_member"
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
logger.debug({
|
|
142
168
|
userId,
|
|
143
169
|
slackUserId: userMapping.slackUserId,
|
|
144
170
|
channelId,
|
|
145
171
|
teamId
|
|
146
|
-
}, "User
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
channelId,
|
|
153
|
-
reason: "not_channel_member"
|
|
154
|
-
}
|
|
155
|
-
});
|
|
156
|
-
}
|
|
157
|
-
logger.debug({
|
|
158
|
-
userId,
|
|
159
|
-
slackUserId: userMapping.slackUserId,
|
|
160
|
-
channelId,
|
|
161
|
-
teamId
|
|
162
|
-
}, "User verified as channel member");
|
|
163
|
-
await next();
|
|
164
|
-
});
|
|
172
|
+
}, "User verified as channel member");
|
|
173
|
+
await next();
|
|
174
|
+
});
|
|
175
|
+
registerAuthzMeta(mw, { description: "Requires Inkeep organization admin role, or Slack channel membership to modify channel settings" });
|
|
176
|
+
return mw;
|
|
177
|
+
};
|
|
165
178
|
|
|
166
179
|
//#endregion
|
|
167
180
|
export { isOrgAdmin, requireChannelMemberOrAdmin, requireWorkspaceAdmin };
|
|
@@ -5,9 +5,10 @@ import { clearWorkspaceConnectionCache, computeWorkspaceConnectionId, deleteWork
|
|
|
5
5
|
import { getSlackClient, getSlackTeamInfo, getSlackUserInfo } from "../services/client.js";
|
|
6
6
|
import { getBotTokenForTeam, setBotTokenForTeam } from "../services/workspace-tokens.js";
|
|
7
7
|
import "../services/index.js";
|
|
8
|
-
import { OpenAPIHono,
|
|
8
|
+
import { OpenAPIHono, z } from "@hono/zod-openapi";
|
|
9
9
|
import { createWorkAppSlackWorkspace } from "@inkeep/agents-core";
|
|
10
10
|
import * as crypto$1 from "node:crypto";
|
|
11
|
+
import { createProtectedRoute, noAuth } from "@inkeep/agents-core/middleware";
|
|
11
12
|
|
|
12
13
|
//#region src/slack/routes/oauth.ts
|
|
13
14
|
/**
|
|
@@ -70,7 +71,7 @@ function parseOAuthState(stateStr) {
|
|
|
70
71
|
}
|
|
71
72
|
}
|
|
72
73
|
const app = new OpenAPIHono();
|
|
73
|
-
app.openapi(
|
|
74
|
+
app.openapi(createProtectedRoute({
|
|
74
75
|
method: "get",
|
|
75
76
|
path: "/install",
|
|
76
77
|
summary: "Install Slack App",
|
|
@@ -81,6 +82,7 @@ app.openapi(createRoute({
|
|
|
81
82
|
"Slack",
|
|
82
83
|
"OAuth"
|
|
83
84
|
],
|
|
85
|
+
permission: noAuth(),
|
|
84
86
|
request: { query: z.object({ tenant_id: z.string().optional() }) },
|
|
85
87
|
responses: { 302: { description: "Redirect to Slack OAuth" } }
|
|
86
88
|
}), (c) => {
|
|
@@ -115,7 +117,7 @@ app.openapi(createRoute({
|
|
|
115
117
|
}, "Redirecting to Slack OAuth");
|
|
116
118
|
return c.redirect(slackAuthUrl.toString());
|
|
117
119
|
});
|
|
118
|
-
app.openapi(
|
|
120
|
+
app.openapi(createProtectedRoute({
|
|
119
121
|
method: "get",
|
|
120
122
|
path: "/oauth_redirect",
|
|
121
123
|
summary: "Slack OAuth Callback",
|
|
@@ -126,6 +128,7 @@ app.openapi(createRoute({
|
|
|
126
128
|
"Slack",
|
|
127
129
|
"OAuth"
|
|
128
130
|
],
|
|
131
|
+
permission: noAuth(),
|
|
129
132
|
request: { query: z.object({
|
|
130
133
|
code: z.string().optional(),
|
|
131
134
|
error: z.string().optional(),
|
|
@@ -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({
|
|
@@ -8,9 +8,9 @@ import { AgentOption } from "../modals.js";
|
|
|
8
8
|
* Called on every @mention and /inkeep command — caching avoids redundant DB queries.
|
|
9
9
|
*/
|
|
10
10
|
declare function findCachedUserMapping(tenantId: string, slackUserId: string, teamId: string, clientId?: string): Promise<{
|
|
11
|
-
id: string;
|
|
12
11
|
createdAt: string;
|
|
13
12
|
updatedAt: string;
|
|
13
|
+
id: string;
|
|
14
14
|
tenantId: string;
|
|
15
15
|
clientId: string;
|
|
16
16
|
slackUserId: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inkeep/agents-work-apps",
|
|
3
|
-
"version": "0.0.0-dev-
|
|
3
|
+
"version": "0.0.0-dev-20260219045007",
|
|
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.1.1",
|
|
35
35
|
"slack-block-builder": "^2.8.0",
|
|
36
|
-
"@inkeep/agents-core": "0.0.0-dev-
|
|
36
|
+
"@inkeep/agents-core": "0.0.0-dev-20260219045007"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
39
|
"@hono/zod-openapi": "^1.1.5",
|