@inkeep/agents-work-apps 0.73.3 → 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.
@@ -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 hono_types6 from "hono/types";
7
+ import * as hono_types0 from "hono/types";
8
8
 
9
9
  //#region src/github/index.d.ts
10
- declare function createGithubRoutes(): Hono<hono_types6.BlankEnv, hono_types6.BlankSchema, "/">;
11
- declare const githubRoutes: Hono<hono_types6.BlankEnv, hono_types6.BlankSchema, "/">;
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,7 +1,7 @@
1
- import * as hono2 from "hono";
1
+ import * as hono0 from "hono";
2
2
 
3
3
  //#region src/github/mcp/auth.d.ts
4
- declare const githubMcpAuth: () => hono2.MiddlewareHandler<{
4
+ declare const githubMcpAuth: () => hono0.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 hono_types10 from "hono/types";
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
- }, hono_types10.BlankSchema, "/">;
11
+ }, hono_types3.BlankSchema, "/">;
12
12
  //#endregion
13
13
  export { app as default };
@@ -1,7 +1,7 @@
1
1
  import { Hono } from "hono";
2
- import * as hono_types0 from "hono/types";
2
+ import * as hono_types4 from "hono/types";
3
3
 
4
4
  //#region src/github/routes/setup.d.ts
5
- declare const app: Hono<hono_types0.BlankEnv, hono_types0.BlankSchema, "/">;
5
+ declare const app: Hono<hono_types4.BlankEnv, hono_types4.BlankSchema, "/">;
6
6
  //#endregion
7
7
  export { app as default };
@@ -1,7 +1,7 @@
1
1
  import { Hono } from "hono";
2
- import * as hono_types1 from "hono/types";
2
+ import * as hono_types6 from "hono/types";
3
3
 
4
4
  //#region src/github/routes/tokenExchange.d.ts
5
- declare const app: Hono<hono_types1.BlankEnv, hono_types1.BlankSchema, "/">;
5
+ declare const app: Hono<hono_types6.BlankEnv, hono_types6.BlankSchema, "/">;
6
6
  //#endregion
7
7
  export { app as default };
@@ -1,5 +1,5 @@
1
1
  import { Hono } from "hono";
2
- import * as hono_types3 from "hono/types";
2
+ import * as hono_types8 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<hono_types3.BlankEnv, hono_types3.BlankSchema, "/">;
10
+ declare const app: Hono<hono_types8.BlankEnv, hono_types8.BlankSchema, "/">;
11
11
  //#endregion
12
12
  export { WebhookVerificationResult, app as default, verifyWebhookSignature };
@@ -1,7 +1,7 @@
1
- import * as hono0 from "hono";
1
+ import * as hono2 from "hono";
2
2
 
3
3
  //#region src/slack/mcp/auth.d.ts
4
- declare const slackMcpAuth: () => hono0.MiddlewareHandler<{
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 hono_types5 from "hono/types";
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
- }, hono_types5.BlankSchema, "/">;
21
+ }, hono_types10.BlankSchema, "/">;
22
22
  //#endregion
23
23
  export { ChannelInfo, app as default, pruneStaleChannelIds };
@@ -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 sanitizeTenantId(raw: string): string;
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, sanitizeTenantId, setBotTokenForTeam };
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 sanitizeTenantId(raw) {
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({ tenant_id: z.string().optional() }) },
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", BOT_SCOPES_CSV);
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 = sanitizeTenantId(rawTenantId);
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, sanitizeTenantId, setBotTokenForTeam };
369
+ export { createOAuthState, oauth_default as default, getBotTokenForTeam, getStateSigningSecret, parseOAuthState, sanitizeId, setBotTokenForTeam };
@@ -1,5 +1,5 @@
1
1
  import { SlackLinkIntent } from "@inkeep/agents-core";
2
- import * as slack_block_builder7 from "slack-block-builder";
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<slack_block_builder7.SlackMessageDto>;
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",
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.3"
38
+ "@inkeep/agents-core": "0.73.5"
39
39
  },
40
40
  "peerDependencies": {
41
41
  "@hono/zod-openapi": "^1.1.5",