@inkeep/agents-work-apps 0.73.4 → 0.73.5
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/mcp/index.d.ts +2 -2
- package/dist/slack/mcp/auth.d.ts +2 -2
- package/dist/slack/mcp/index.d.ts +2 -2
- package/dist/slack/middleware/permissions.d.ts +3 -3
- package/dist/slack/routes/oauth.d.ts +4 -3
- package/dist/slack/routes/oauth.js +44 -10
- package/dist/slack/services/events/utils.d.ts +2 -2
- package/dist/slack/services/link-prompt.d.ts +2 -2
- package/package.json +2 -2
|
@@ -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/mcp/index.d.ts
|
|
5
5
|
declare const app: Hono<{
|
|
@@ -8,6 +8,6 @@ declare const app: Hono<{
|
|
|
8
8
|
tenantId: string;
|
|
9
9
|
projectId: string;
|
|
10
10
|
};
|
|
11
|
-
},
|
|
11
|
+
}, hono_types3.BlankSchema, "/">;
|
|
12
12
|
//#endregion
|
|
13
13
|
export { app as default };
|
package/dist/slack/mcp/auth.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import * as hono2 from "hono";
|
|
2
2
|
|
|
3
3
|
//#region src/slack/mcp/auth.d.ts
|
|
4
|
-
declare const slackMcpAuth: () =>
|
|
4
|
+
declare const slackMcpAuth: () => hono2.MiddlewareHandler<{
|
|
5
5
|
Variables: {
|
|
6
6
|
toolId: string;
|
|
7
7
|
tenantId: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
|
-
import * as
|
|
2
|
+
import * as hono_types10 from "hono/types";
|
|
3
3
|
|
|
4
4
|
//#region src/slack/mcp/index.d.ts
|
|
5
5
|
interface ChannelInfo {
|
|
@@ -18,6 +18,6 @@ declare const app: Hono<{
|
|
|
18
18
|
tenantId: string;
|
|
19
19
|
projectId: string;
|
|
20
20
|
};
|
|
21
|
-
},
|
|
21
|
+
}, hono_types10.BlankSchema, "/">;
|
|
22
22
|
//#endregion
|
|
23
23
|
export { ChannelInfo, app as default, pruneStaleChannelIds };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ManageAppVariables } from "../types.js";
|
|
2
|
-
import * as
|
|
2
|
+
import * as hono0 from "hono";
|
|
3
3
|
|
|
4
4
|
//#region src/slack/middleware/permissions.d.ts
|
|
5
5
|
/**
|
|
@@ -14,7 +14,7 @@ declare const requireWorkspaceAdmin: <Env extends {
|
|
|
14
14
|
Variables: ManageAppVariables;
|
|
15
15
|
} = {
|
|
16
16
|
Variables: ManageAppVariables;
|
|
17
|
-
}>() =>
|
|
17
|
+
}>() => hono0.MiddlewareHandler<Env, string, {}, Response>;
|
|
18
18
|
/**
|
|
19
19
|
* Middleware that requires either:
|
|
20
20
|
* 1. Org admin/owner role (can modify any channel), OR
|
|
@@ -26,6 +26,6 @@ declare const requireChannelMemberOrAdmin: <Env extends {
|
|
|
26
26
|
Variables: ManageAppVariables;
|
|
27
27
|
} = {
|
|
28
28
|
Variables: ManageAppVariables;
|
|
29
|
-
}>() =>
|
|
29
|
+
}>() => hono0.MiddlewareHandler<Env, string, {}, Response>;
|
|
30
30
|
//#endregion
|
|
31
31
|
export { isOrgAdmin, requireChannelMemberOrAdmin, requireWorkspaceAdmin };
|
|
@@ -8,14 +8,15 @@ import { OpenAPIHono } from "@hono/zod-openapi";
|
|
|
8
8
|
interface OAuthState {
|
|
9
9
|
nonce: string;
|
|
10
10
|
tenantId?: string;
|
|
11
|
+
projectId?: string;
|
|
11
12
|
timestamp: number;
|
|
12
13
|
}
|
|
13
14
|
declare function getStateSigningSecret(): string;
|
|
14
|
-
declare function createOAuthState(tenantId?: string): string;
|
|
15
|
+
declare function createOAuthState(tenantId?: string, projectId?: string): string;
|
|
15
16
|
declare function parseOAuthState(stateStr: string): OAuthState | null;
|
|
16
|
-
declare function
|
|
17
|
+
declare function sanitizeId(raw: string): string;
|
|
17
18
|
declare const app: OpenAPIHono<{
|
|
18
19
|
Variables: WorkAppsVariables;
|
|
19
20
|
}, {}, "/">;
|
|
20
21
|
//#endregion
|
|
21
|
-
export { createOAuthState, app as default, getBotTokenForTeam, getStateSigningSecret, parseOAuthState,
|
|
22
|
+
export { createOAuthState, app as default, getBotTokenForTeam, getStateSigningSecret, parseOAuthState, sanitizeId, setBotTokenForTeam };
|
|
@@ -31,10 +31,11 @@ function getStateSigningSecret() {
|
|
|
31
31
|
}
|
|
32
32
|
return secret;
|
|
33
33
|
}
|
|
34
|
-
function createOAuthState(tenantId) {
|
|
34
|
+
function createOAuthState(tenantId, projectId) {
|
|
35
35
|
const state = {
|
|
36
36
|
nonce: crypto$1.randomBytes(16).toString("hex"),
|
|
37
37
|
tenantId: tenantId || "",
|
|
38
|
+
projectId: projectId || "",
|
|
38
39
|
timestamp: Date.now()
|
|
39
40
|
};
|
|
40
41
|
const data = Buffer.from(JSON.stringify(state)).toString("base64url");
|
|
@@ -71,7 +72,7 @@ function parseOAuthState(stateStr) {
|
|
|
71
72
|
return null;
|
|
72
73
|
}
|
|
73
74
|
}
|
|
74
|
-
function
|
|
75
|
+
function sanitizeId(raw) {
|
|
75
76
|
return /^[a-zA-Z0-9_-]+$/.test(raw) ? raw : "";
|
|
76
77
|
}
|
|
77
78
|
const app = new OpenAPIHono();
|
|
@@ -79,7 +80,7 @@ app.openapi(createProtectedRoute({
|
|
|
79
80
|
method: "get",
|
|
80
81
|
path: "/install",
|
|
81
82
|
summary: "Install Slack App",
|
|
82
|
-
description: "Redirects to Slack OAuth page for workspace installation",
|
|
83
|
+
description: "Redirects to Slack OAuth page for workspace installation. Pass include_webhook=true to additionally request the incoming-webhook scope (shows channel picker for webhook URL).",
|
|
83
84
|
operationId: "slack-install",
|
|
84
85
|
tags: [
|
|
85
86
|
"Work Apps",
|
|
@@ -87,21 +88,27 @@ app.openapi(createProtectedRoute({
|
|
|
87
88
|
"OAuth"
|
|
88
89
|
],
|
|
89
90
|
permission: noAuth(),
|
|
90
|
-
request: { query: z.object({
|
|
91
|
+
request: { query: z.object({
|
|
92
|
+
tenant_id: z.string().optional(),
|
|
93
|
+
project_id: z.string().optional(),
|
|
94
|
+
include_webhook: z.string().optional()
|
|
95
|
+
}) },
|
|
91
96
|
responses: { 302: { description: "Redirect to Slack OAuth" } }
|
|
92
97
|
}), (c) => {
|
|
93
|
-
const { tenant_id: tenantId } = c.req.valid("query");
|
|
98
|
+
const { tenant_id: tenantId, project_id: projectId, include_webhook: includeWebhook } = c.req.valid("query");
|
|
94
99
|
const clientId = env.SLACK_CLIENT_ID;
|
|
95
100
|
const redirectUri = `${env.SLACK_APP_URL}/work-apps/slack/oauth_redirect`;
|
|
96
|
-
const state = createOAuthState(tenantId);
|
|
101
|
+
const state = createOAuthState(tenantId, projectId);
|
|
102
|
+
const scope = includeWebhook === "true" ? `${BOT_SCOPES_CSV},incoming-webhook` : BOT_SCOPES_CSV;
|
|
97
103
|
const slackAuthUrl = new URL("https://slack.com/oauth/v2/authorize");
|
|
98
104
|
slackAuthUrl.searchParams.set("client_id", clientId || "");
|
|
99
|
-
slackAuthUrl.searchParams.set("scope",
|
|
105
|
+
slackAuthUrl.searchParams.set("scope", scope);
|
|
100
106
|
slackAuthUrl.searchParams.set("redirect_uri", redirectUri);
|
|
101
107
|
slackAuthUrl.searchParams.set("state", state);
|
|
102
108
|
logger.info({
|
|
103
109
|
redirectUri,
|
|
104
|
-
tenantId: tenantId || ""
|
|
110
|
+
tenantId: tenantId || "",
|
|
111
|
+
includeWebhook: includeWebhook === "true"
|
|
105
112
|
}, "Redirecting to Slack OAuth");
|
|
106
113
|
return c.redirect(slackAuthUrl.toString());
|
|
107
114
|
});
|
|
@@ -127,7 +134,7 @@ app.openapi(createProtectedRoute({
|
|
|
127
134
|
const { code, error, state: stateParam } = c.req.valid("query");
|
|
128
135
|
const parsedState = stateParam ? parseOAuthState(stateParam) : null;
|
|
129
136
|
const rawTenantId = parsedState?.tenantId || "";
|
|
130
|
-
const tenantId =
|
|
137
|
+
const tenantId = sanitizeId(rawTenantId);
|
|
131
138
|
if (rawTenantId && !tenantId) logger.warn({ rawTenantId: rawTenantId.slice(0, 50) }, "Rejected invalid tenantId from OAuth state");
|
|
132
139
|
const dashboardUrl = `${manageUiUrl}/${tenantId}/work-apps/slack`;
|
|
133
140
|
if (!stateParam || !parsedState) {
|
|
@@ -302,6 +309,33 @@ app.openapi(createProtectedRoute({
|
|
|
302
309
|
teamId: workspaceData.teamId,
|
|
303
310
|
teamName: workspaceData.teamName
|
|
304
311
|
}, "Slack workspace installation successful");
|
|
312
|
+
if (tokenData.incoming_webhook?.url) {
|
|
313
|
+
const webhookUrl = tokenData.incoming_webhook.url;
|
|
314
|
+
if (!webhookUrl.startsWith("https://hooks.slack.com/")) logger.warn({ prefix: webhookUrl.slice(0, 60) }, "Unexpected incoming_webhook URL prefix, skipping form redirect");
|
|
315
|
+
else {
|
|
316
|
+
const rawProjectId = parsedState.projectId || "";
|
|
317
|
+
const stateProjectId = sanitizeId(rawProjectId);
|
|
318
|
+
if (rawProjectId && !stateProjectId) logger.warn({ rawProjectId: rawProjectId.slice(0, 50) }, "Rejected invalid projectId from OAuth state");
|
|
319
|
+
if (!tenantId || !stateProjectId) {
|
|
320
|
+
logger.warn({
|
|
321
|
+
tenantId,
|
|
322
|
+
projectId: stateProjectId
|
|
323
|
+
}, "Incoming webhook received but missing tenantId or projectId for redirect");
|
|
324
|
+
return c.redirect(`${dashboardUrl}?error=missing_project_context`);
|
|
325
|
+
}
|
|
326
|
+
try {
|
|
327
|
+
const newWebhookFormUrl = new URL(`${manageUiUrl}/${tenantId}/projects/${stateProjectId}/webhook-destinations/new`);
|
|
328
|
+
newWebhookFormUrl.searchParams.set("url", webhookUrl);
|
|
329
|
+
logger.info({
|
|
330
|
+
teamId: workspaceData.teamId,
|
|
331
|
+
channel: tokenData.incoming_webhook.channel
|
|
332
|
+
}, "Incoming webhook URL received, redirecting to webhook form");
|
|
333
|
+
return c.redirect(newWebhookFormUrl.toString());
|
|
334
|
+
} catch (err) {
|
|
335
|
+
logger.error({ err }, "Failed to build webhook form URL after successful install");
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
|
305
339
|
const safeWorkspaceData = {
|
|
306
340
|
ok: workspaceData.ok,
|
|
307
341
|
teamId: workspaceData.teamId,
|
|
@@ -332,4 +366,4 @@ app.openapi(createProtectedRoute({
|
|
|
332
366
|
var oauth_default = app;
|
|
333
367
|
|
|
334
368
|
//#endregion
|
|
335
|
-
export { createOAuthState, oauth_default as default, getBotTokenForTeam, getStateSigningSecret, parseOAuthState,
|
|
369
|
+
export { createOAuthState, oauth_default as default, getBotTokenForTeam, getStateSigningSecret, parseOAuthState, sanitizeId, setBotTokenForTeam };
|
|
@@ -9,12 +9,12 @@ import { AgentOption } from "../modals.js";
|
|
|
9
9
|
* Called on every @mention and /inkeep command — caching avoids redundant DB queries.
|
|
10
10
|
*/
|
|
11
11
|
declare function findCachedUserMapping(tenantId: string, slackUserId: string, teamId: string, clientId?: string): Promise<{
|
|
12
|
+
id: string;
|
|
12
13
|
createdAt: string;
|
|
13
14
|
updatedAt: string;
|
|
14
|
-
|
|
15
|
+
slackUserId: string;
|
|
15
16
|
tenantId: string;
|
|
16
17
|
clientId: string;
|
|
17
|
-
slackUserId: string;
|
|
18
18
|
slackTeamId: string;
|
|
19
19
|
slackEnterpriseId: string | null;
|
|
20
20
|
inkeepUserId: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { SlackLinkIntent } from "@inkeep/agents-core";
|
|
2
|
-
import * as
|
|
2
|
+
import * as slack_block_builder0 from "slack-block-builder";
|
|
3
3
|
|
|
4
4
|
//#region src/slack/services/link-prompt.d.ts
|
|
5
5
|
type LinkPromptResult = {
|
|
@@ -22,6 +22,6 @@ interface ResolveLinkActionParams {
|
|
|
22
22
|
intent?: SlackLinkIntent;
|
|
23
23
|
}
|
|
24
24
|
declare function resolveUnlinkedUserAction(params: ResolveLinkActionParams): Promise<LinkPromptResult>;
|
|
25
|
-
declare function buildLinkPromptMessage(result: LinkPromptResult): Readonly<
|
|
25
|
+
declare function buildLinkPromptMessage(result: LinkPromptResult): Readonly<slack_block_builder0.SlackMessageDto>;
|
|
26
26
|
//#endregion
|
|
27
27
|
export { LinkPromptResult, ResolveLinkActionParams, buildLinkPromptMessage, resolveUnlinkedUserAction };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inkeep/agents-work-apps",
|
|
3
|
-
"version": "0.73.
|
|
3
|
+
"version": "0.73.5",
|
|
4
4
|
"description": "First party integrations for Inkeep Agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"minimatch": "^10.2.1",
|
|
36
36
|
"oxfmt": "^0.42.0",
|
|
37
37
|
"slack-block-builder": "^2.8.0",
|
|
38
|
-
"@inkeep/agents-core": "0.73.
|
|
38
|
+
"@inkeep/agents-core": "0.73.5"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"@hono/zod-openapi": "^1.1.5",
|