@inkeep/agents-work-apps 0.47.5 → 0.48.1
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/env.d.ts +24 -2
- package/dist/env.js +13 -2
- package/dist/github/mcp/index.d.ts +2 -2
- package/dist/github/mcp/index.js +23 -34
- package/dist/github/routes/setup.d.ts +2 -2
- package/dist/github/routes/webhooks.d.ts +2 -2
- package/dist/slack/i18n/index.d.ts +2 -0
- package/dist/slack/i18n/index.js +3 -0
- package/dist/slack/i18n/strings.d.ts +73 -0
- package/dist/slack/i18n/strings.js +67 -0
- package/dist/slack/index.d.ts +18 -0
- package/dist/slack/index.js +28 -0
- package/dist/slack/middleware/permissions.d.ts +31 -0
- package/dist/slack/middleware/permissions.js +167 -0
- package/dist/slack/routes/events.d.ts +10 -0
- package/dist/slack/routes/events.js +551 -0
- package/dist/slack/routes/index.d.ts +10 -0
- package/dist/slack/routes/index.js +47 -0
- package/dist/slack/routes/oauth.d.ts +20 -0
- package/dist/slack/routes/oauth.js +344 -0
- package/dist/slack/routes/users.d.ts +10 -0
- package/dist/slack/routes/users.js +365 -0
- package/dist/slack/routes/workspaces.d.ts +10 -0
- package/dist/slack/routes/workspaces.js +909 -0
- package/dist/slack/services/agent-resolution.d.ts +41 -0
- package/dist/slack/services/agent-resolution.js +99 -0
- package/dist/slack/services/blocks/index.d.ts +73 -0
- package/dist/slack/services/blocks/index.js +103 -0
- package/dist/slack/services/client.d.ts +108 -0
- package/dist/slack/services/client.js +232 -0
- package/dist/slack/services/commands/index.d.ts +19 -0
- package/dist/slack/services/commands/index.js +553 -0
- package/dist/slack/services/events/app-mention.d.ts +40 -0
- package/dist/slack/services/events/app-mention.js +304 -0
- package/dist/slack/services/events/block-actions.d.ts +40 -0
- package/dist/slack/services/events/block-actions.js +265 -0
- package/dist/slack/services/events/index.d.ts +6 -0
- package/dist/slack/services/events/index.js +7 -0
- package/dist/slack/services/events/modal-submission.d.ts +30 -0
- package/dist/slack/services/events/modal-submission.js +400 -0
- package/dist/slack/services/events/streaming.d.ts +26 -0
- package/dist/slack/services/events/streaming.js +272 -0
- package/dist/slack/services/events/utils.d.ts +146 -0
- package/dist/slack/services/events/utils.js +370 -0
- package/dist/slack/services/index.d.ts +16 -0
- package/dist/slack/services/index.js +16 -0
- package/dist/slack/services/modals.d.ts +86 -0
- package/dist/slack/services/modals.js +355 -0
- package/dist/slack/services/nango.d.ts +85 -0
- package/dist/slack/services/nango.js +476 -0
- package/dist/slack/services/security.d.ts +35 -0
- package/dist/slack/services/security.js +65 -0
- package/dist/slack/services/types.d.ts +26 -0
- package/dist/slack/services/types.js +1 -0
- package/dist/slack/services/workspace-tokens.d.ts +25 -0
- package/dist/slack/services/workspace-tokens.js +27 -0
- package/dist/slack/tracer.d.ts +40 -0
- package/dist/slack/tracer.js +39 -0
- package/dist/slack/types.d.ts +10 -0
- package/dist/slack/types.js +1 -0
- package/package.json +11 -2
package/dist/env.d.ts
CHANGED
|
@@ -14,11 +14,11 @@ declare const envSchema: z.ZodObject<{
|
|
|
14
14
|
pentest: "pentest";
|
|
15
15
|
}>>;
|
|
16
16
|
LOG_LEVEL: z.ZodDefault<z.ZodEnum<{
|
|
17
|
-
error: "error";
|
|
18
17
|
trace: "trace";
|
|
19
18
|
debug: "debug";
|
|
20
19
|
info: "info";
|
|
21
20
|
warn: "warn";
|
|
21
|
+
error: "error";
|
|
22
22
|
}>>;
|
|
23
23
|
INKEEP_AGENTS_RUN_DATABASE_URL: z.ZodString;
|
|
24
24
|
INKEEP_AGENTS_MANAGE_UI_URL: z.ZodOptional<z.ZodString>;
|
|
@@ -28,11 +28,22 @@ declare const envSchema: z.ZodObject<{
|
|
|
28
28
|
GITHUB_STATE_SIGNING_SECRET: z.ZodOptional<z.ZodString>;
|
|
29
29
|
GITHUB_APP_NAME: z.ZodOptional<z.ZodString>;
|
|
30
30
|
GITHUB_MCP_API_KEY: z.ZodOptional<z.ZodString>;
|
|
31
|
+
SLACK_CLIENT_ID: z.ZodOptional<z.ZodString>;
|
|
32
|
+
SLACK_CLIENT_SECRET: z.ZodOptional<z.ZodString>;
|
|
33
|
+
SLACK_SIGNING_SECRET: z.ZodOptional<z.ZodString>;
|
|
34
|
+
SLACK_BOT_TOKEN: z.ZodOptional<z.ZodString>;
|
|
35
|
+
SLACK_APP_URL: z.ZodOptional<z.ZodString>;
|
|
36
|
+
NANGO_SECRET_KEY: z.ZodOptional<z.ZodString>;
|
|
37
|
+
NANGO_SLACK_SECRET_KEY: z.ZodOptional<z.ZodString>;
|
|
38
|
+
NANGO_SLACK_INTEGRATION_ID: z.ZodOptional<z.ZodString>;
|
|
39
|
+
NANGO_SERVER_URL: z.ZodOptional<z.ZodString>;
|
|
40
|
+
INKEEP_AGENTS_JWT_SIGNING_SECRET: z.ZodOptional<z.ZodString>;
|
|
41
|
+
INKEEP_AGENTS_API_URL: z.ZodOptional<z.ZodString>;
|
|
31
42
|
}, z.core.$strip>;
|
|
32
43
|
declare const env: {
|
|
33
44
|
NODE_ENV: "development" | "production" | "test";
|
|
34
45
|
ENVIRONMENT: "development" | "production" | "test" | "pentest";
|
|
35
|
-
LOG_LEVEL: "
|
|
46
|
+
LOG_LEVEL: "trace" | "debug" | "info" | "warn" | "error";
|
|
36
47
|
INKEEP_AGENTS_RUN_DATABASE_URL: string;
|
|
37
48
|
INKEEP_AGENTS_MANAGE_UI_URL?: string | undefined;
|
|
38
49
|
GITHUB_APP_ID?: string | undefined;
|
|
@@ -41,6 +52,17 @@ declare const env: {
|
|
|
41
52
|
GITHUB_STATE_SIGNING_SECRET?: string | undefined;
|
|
42
53
|
GITHUB_APP_NAME?: string | undefined;
|
|
43
54
|
GITHUB_MCP_API_KEY?: string | undefined;
|
|
55
|
+
SLACK_CLIENT_ID?: string | undefined;
|
|
56
|
+
SLACK_CLIENT_SECRET?: string | undefined;
|
|
57
|
+
SLACK_SIGNING_SECRET?: string | undefined;
|
|
58
|
+
SLACK_BOT_TOKEN?: string | undefined;
|
|
59
|
+
SLACK_APP_URL?: string | undefined;
|
|
60
|
+
NANGO_SECRET_KEY?: string | undefined;
|
|
61
|
+
NANGO_SLACK_SECRET_KEY?: string | undefined;
|
|
62
|
+
NANGO_SLACK_INTEGRATION_ID?: string | undefined;
|
|
63
|
+
NANGO_SERVER_URL?: string | undefined;
|
|
64
|
+
INKEEP_AGENTS_JWT_SIGNING_SECRET?: string | undefined;
|
|
65
|
+
INKEEP_AGENTS_API_URL?: string | undefined;
|
|
44
66
|
};
|
|
45
67
|
type Env = z.infer<typeof envSchema>;
|
|
46
68
|
//#endregion
|
package/dist/env.js
CHANGED
|
@@ -22,14 +22,25 @@ const envSchema = z.object({
|
|
|
22
22
|
"warn",
|
|
23
23
|
"error"
|
|
24
24
|
]).default("info").describe("Logging verbosity level"),
|
|
25
|
-
INKEEP_AGENTS_RUN_DATABASE_URL: z.string().describe("PostgreSQL connection URL for the runtime database
|
|
25
|
+
INKEEP_AGENTS_RUN_DATABASE_URL: z.string().describe("PostgreSQL connection URL for the runtime database"),
|
|
26
26
|
INKEEP_AGENTS_MANAGE_UI_URL: z.string().optional().describe("URL where the management UI is hosted"),
|
|
27
27
|
GITHUB_APP_ID: z.string().optional().describe("GitHub App ID for GitHub integration"),
|
|
28
28
|
GITHUB_APP_PRIVATE_KEY: z.string().optional().describe("GitHub App private key for authentication"),
|
|
29
29
|
GITHUB_WEBHOOK_SECRET: z.string().optional().describe("Secret for validating GitHub webhook payloads"),
|
|
30
30
|
GITHUB_STATE_SIGNING_SECRET: z.string().min(32, "GITHUB_STATE_SIGNING_SECRET must be at least 32 characters").optional().describe("Secret for signing GitHub OAuth state (minimum 32 characters)"),
|
|
31
31
|
GITHUB_APP_NAME: z.string().optional().describe("Name of the GitHub App"),
|
|
32
|
-
GITHUB_MCP_API_KEY: z.string().optional().describe("API key for the GitHub MCP")
|
|
32
|
+
GITHUB_MCP_API_KEY: z.string().optional().describe("API key for the GitHub MCP"),
|
|
33
|
+
SLACK_CLIENT_ID: z.string().optional().describe("Slack App Client ID"),
|
|
34
|
+
SLACK_CLIENT_SECRET: z.string().optional().describe("Slack App Client Secret"),
|
|
35
|
+
SLACK_SIGNING_SECRET: z.string().optional().describe("Slack App Signing Secret"),
|
|
36
|
+
SLACK_BOT_TOKEN: z.string().optional().describe("Slack Bot Token (for testing)"),
|
|
37
|
+
SLACK_APP_URL: z.string().optional().describe("Slack App Install URL"),
|
|
38
|
+
NANGO_SECRET_KEY: z.string().optional().describe("Nango Secret Key"),
|
|
39
|
+
NANGO_SLACK_SECRET_KEY: z.string().optional().describe("Nango Slack-specific Secret Key"),
|
|
40
|
+
NANGO_SLACK_INTEGRATION_ID: z.string().optional().describe("Nango Slack Integration ID"),
|
|
41
|
+
NANGO_SERVER_URL: z.string().optional().describe("Nango Server URL"),
|
|
42
|
+
INKEEP_AGENTS_JWT_SIGNING_SECRET: z.string().optional().describe("JWT signing secret shared with agents-api. Required in production (min 32 chars). Used for Slack user tokens and link tokens."),
|
|
43
|
+
INKEEP_AGENTS_API_URL: z.string().optional().describe("Inkeep Agents API URL")
|
|
33
44
|
});
|
|
34
45
|
const parseEnv = () => {
|
|
35
46
|
try {
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
|
-
import * as
|
|
2
|
+
import * as hono_types5 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_types5.BlankSchema, "/">;
|
|
10
10
|
//#endregion
|
|
11
11
|
export { app as default };
|
package/dist/github/mcp/index.js
CHANGED
|
@@ -108,8 +108,9 @@ const getServer = async (toolId) => {
|
|
|
108
108
|
owner: z.string().describe("Repository owner name"),
|
|
109
109
|
repo: z.string().describe("Repository name"),
|
|
110
110
|
file_path: z.string().describe("Path to the file. the path is relative to the root of the repository"),
|
|
111
|
-
branch_name: z.string().optional().describe("The name of the branch to get the file content for (defaults to master/main branch). If you are analyzing a pr you created, you should use the branch name from the pr.")
|
|
112
|
-
|
|
111
|
+
branch_name: z.string().optional().describe("The name of the branch to get the file content for (defaults to master/main branch). If you are analyzing a pr you created, you should use the branch name from the pr."),
|
|
112
|
+
include_line_numbers: z.boolean().optional().describe("Whether to include line numbers in the response (defaults to false)")
|
|
113
|
+
}, async ({ owner, repo, file_path, branch_name, include_line_numbers = false }) => {
|
|
113
114
|
try {
|
|
114
115
|
let githubClient;
|
|
115
116
|
try {
|
|
@@ -130,14 +131,20 @@ const getServer = async (toolId) => {
|
|
|
130
131
|
ref: branch_name
|
|
131
132
|
});
|
|
132
133
|
if ("content" in response.data && !Array.isArray(response.data)) {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
line
|
|
136
|
-
|
|
137
|
-
|
|
134
|
+
if (include_line_numbers) {
|
|
135
|
+
const fileData = response.data;
|
|
136
|
+
const output_mapping = Buffer.from(fileData.content, "base64").toString("utf-8").split("\n").map((line, index) => ({
|
|
137
|
+
line: index + 1,
|
|
138
|
+
content: line
|
|
139
|
+
}));
|
|
140
|
+
return { content: [{
|
|
141
|
+
type: "text",
|
|
142
|
+
text: JSON.stringify(output_mapping)
|
|
143
|
+
}] };
|
|
144
|
+
}
|
|
138
145
|
return { content: [{
|
|
139
146
|
type: "text",
|
|
140
|
-
text:
|
|
147
|
+
text: Buffer.from(response.data.content, "base64").toString("utf-8")
|
|
141
148
|
}] };
|
|
142
149
|
}
|
|
143
150
|
return {
|
|
@@ -665,40 +672,22 @@ const getServer = async (toolId) => {
|
|
|
665
672
|
});
|
|
666
673
|
return server;
|
|
667
674
|
};
|
|
668
|
-
const SERVER_CACHE_TTL_MS = 300 * 1e3;
|
|
669
|
-
const SERVER_CACHE_MAX_SIZE = 100;
|
|
670
|
-
const serverCache = /* @__PURE__ */ new Map();
|
|
671
|
-
const getCachedServer = async (toolId) => {
|
|
672
|
-
const cached = serverCache.get(toolId);
|
|
673
|
-
if (cached && cached.expiresAt > Date.now()) {
|
|
674
|
-
serverCache.delete(toolId);
|
|
675
|
-
serverCache.set(toolId, cached);
|
|
676
|
-
return cached.server;
|
|
677
|
-
}
|
|
678
|
-
serverCache.delete(toolId);
|
|
679
|
-
const server = await getServer(toolId);
|
|
680
|
-
serverCache.set(toolId, {
|
|
681
|
-
server,
|
|
682
|
-
expiresAt: Date.now() + SERVER_CACHE_TTL_MS
|
|
683
|
-
});
|
|
684
|
-
if (serverCache.size > SERVER_CACHE_MAX_SIZE) {
|
|
685
|
-
const firstKey = serverCache.keys().next().value;
|
|
686
|
-
if (firstKey !== void 0) serverCache.delete(firstKey);
|
|
687
|
-
}
|
|
688
|
-
return server;
|
|
689
|
-
};
|
|
690
675
|
const app = new Hono();
|
|
691
676
|
app.use("/", githubMcpAuth());
|
|
692
677
|
app.post("/", async (c) => {
|
|
693
678
|
if (!process.env.GITHUB_APP_ID || !process.env.GITHUB_APP_PRIVATE_KEY) return c.json({ error: "GITHUB_APP_ID and GITHUB_APP_PRIVATE_KEY must be set" }, 500);
|
|
694
679
|
const toolId = c.get("toolId");
|
|
695
680
|
const body = await c.req.json();
|
|
696
|
-
const server = await
|
|
681
|
+
const server = await getServer(toolId);
|
|
697
682
|
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: void 0 });
|
|
698
|
-
server.connect(transport);
|
|
683
|
+
await server.connect(transport);
|
|
699
684
|
const { req, res } = toReqRes(c.req.raw);
|
|
700
|
-
|
|
701
|
-
|
|
685
|
+
try {
|
|
686
|
+
await transport.handleRequest(req, res, body);
|
|
687
|
+
return toFetchResponse(res);
|
|
688
|
+
} finally {
|
|
689
|
+
await server.close();
|
|
690
|
+
}
|
|
702
691
|
});
|
|
703
692
|
app.delete("/", async (c) => {
|
|
704
693
|
return c.json({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
|
-
import * as
|
|
2
|
+
import * as hono_types6 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_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
|
|
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 };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
//#region src/slack/i18n/strings.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Slack UI/UX Internationalization Strings
|
|
4
|
+
*
|
|
5
|
+
* Centralized strings for all Slack-facing UI text.
|
|
6
|
+
* Update this file to change text across the entire Slack integration.
|
|
7
|
+
*/
|
|
8
|
+
declare const SlackStrings: {
|
|
9
|
+
readonly buttons: {
|
|
10
|
+
readonly triggerAgent: "Trigger Agent";
|
|
11
|
+
readonly send: "Send";
|
|
12
|
+
readonly followUp: "Follow Up";
|
|
13
|
+
readonly cancel: "Cancel";
|
|
14
|
+
readonly openDashboard: "⚙️ Open Dashboard";
|
|
15
|
+
};
|
|
16
|
+
readonly modals: {
|
|
17
|
+
readonly triggerAgent: "Trigger Agent";
|
|
18
|
+
readonly triggerAgentThread: "Trigger Agent (Thread)";
|
|
19
|
+
readonly askAboutMessage: "Ask About Message";
|
|
20
|
+
readonly followUp: "Follow Up";
|
|
21
|
+
};
|
|
22
|
+
readonly labels: {
|
|
23
|
+
readonly project: "Project";
|
|
24
|
+
readonly agent: "Agent";
|
|
25
|
+
readonly prompt: "Prompt";
|
|
26
|
+
readonly additionalInstructions: "Additional Instructions";
|
|
27
|
+
readonly context: "Context";
|
|
28
|
+
};
|
|
29
|
+
readonly placeholders: {
|
|
30
|
+
readonly selectProject: "Select a project...";
|
|
31
|
+
readonly selectAgent: "Select an agent...";
|
|
32
|
+
readonly enterPrompt: "Enter your prompt or question...";
|
|
33
|
+
readonly additionalInstructionsOptional: "Additional instructions (optional)...";
|
|
34
|
+
readonly additionalInstructionsMessage: "Additional instructions or question about this message...";
|
|
35
|
+
};
|
|
36
|
+
readonly visibility: {
|
|
37
|
+
readonly includeThreadContext: "Include thread context";
|
|
38
|
+
};
|
|
39
|
+
readonly context: {
|
|
40
|
+
readonly poweredBy: (agentName: string) => string;
|
|
41
|
+
readonly privateResponse: "_Private response_";
|
|
42
|
+
};
|
|
43
|
+
readonly usage: {
|
|
44
|
+
readonly mentionEmpty: string;
|
|
45
|
+
};
|
|
46
|
+
readonly status: {
|
|
47
|
+
readonly thinking: (agentName: string) => string;
|
|
48
|
+
readonly noAgentsAvailable: "No agents available";
|
|
49
|
+
readonly noProjectsConfigured: "⚙️ No projects configured. Please set up projects in the dashboard.";
|
|
50
|
+
};
|
|
51
|
+
readonly errors: {
|
|
52
|
+
readonly generic: "Sorry, something went wrong. Please try again.";
|
|
53
|
+
readonly failedToOpenSelector: "❌ Failed to open agent selector. Please try again.";
|
|
54
|
+
};
|
|
55
|
+
readonly help: {
|
|
56
|
+
readonly title: "Inkeep — How to Use";
|
|
57
|
+
readonly publicSection: string;
|
|
58
|
+
readonly privateSection: string;
|
|
59
|
+
readonly otherCommands: string;
|
|
60
|
+
};
|
|
61
|
+
readonly agentList: {
|
|
62
|
+
readonly title: "🤖 Available Agents";
|
|
63
|
+
readonly usage: "Usage:";
|
|
64
|
+
readonly runUsage: "`/inkeep run \"agent name\" question` - Run a specific agent";
|
|
65
|
+
readonly andMore: (count: number) => string;
|
|
66
|
+
};
|
|
67
|
+
readonly messageContext: {
|
|
68
|
+
readonly label: "Message:";
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
type SlackStringsType = typeof SlackStrings;
|
|
72
|
+
//#endregion
|
|
73
|
+
export { SlackStrings, SlackStringsType };
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
//#region src/slack/i18n/strings.ts
|
|
2
|
+
/**
|
|
3
|
+
* Slack UI/UX Internationalization Strings
|
|
4
|
+
*
|
|
5
|
+
* Centralized strings for all Slack-facing UI text.
|
|
6
|
+
* Update this file to change text across the entire Slack integration.
|
|
7
|
+
*/
|
|
8
|
+
const SlackStrings = {
|
|
9
|
+
buttons: {
|
|
10
|
+
triggerAgent: "Trigger Agent",
|
|
11
|
+
send: "Send",
|
|
12
|
+
followUp: "Follow Up",
|
|
13
|
+
cancel: "Cancel",
|
|
14
|
+
openDashboard: "⚙️ Open Dashboard"
|
|
15
|
+
},
|
|
16
|
+
modals: {
|
|
17
|
+
triggerAgent: "Trigger Agent",
|
|
18
|
+
triggerAgentThread: "Trigger Agent (Thread)",
|
|
19
|
+
askAboutMessage: "Ask About Message",
|
|
20
|
+
followUp: "Follow Up"
|
|
21
|
+
},
|
|
22
|
+
labels: {
|
|
23
|
+
project: "Project",
|
|
24
|
+
agent: "Agent",
|
|
25
|
+
prompt: "Prompt",
|
|
26
|
+
additionalInstructions: "Additional Instructions",
|
|
27
|
+
context: "Context"
|
|
28
|
+
},
|
|
29
|
+
placeholders: {
|
|
30
|
+
selectProject: "Select a project...",
|
|
31
|
+
selectAgent: "Select an agent...",
|
|
32
|
+
enterPrompt: "Enter your prompt or question...",
|
|
33
|
+
additionalInstructionsOptional: "Additional instructions (optional)...",
|
|
34
|
+
additionalInstructionsMessage: "Additional instructions or question about this message..."
|
|
35
|
+
},
|
|
36
|
+
visibility: { includeThreadContext: "Include thread context" },
|
|
37
|
+
context: {
|
|
38
|
+
poweredBy: (agentName) => `Powered by *${agentName}* via Inkeep`,
|
|
39
|
+
privateResponse: "_Private response_"
|
|
40
|
+
},
|
|
41
|
+
usage: { mentionEmpty: "*To use your Inkeep agent, include a message:*\n\n• `@Inkeep <message>` — Send a message to your agent (reply appears in a thread)\n• `@Inkeep <message>` in a thread — Includes the thread as context for your agent\n• `@Inkeep` in a thread — Triggers your agent using the full thread as context\n\n💡 Use `/inkeep help` for all available commands." },
|
|
42
|
+
status: {
|
|
43
|
+
thinking: (agentName) => `_${agentName} is thinking..._`,
|
|
44
|
+
noAgentsAvailable: "No agents available",
|
|
45
|
+
noProjectsConfigured: "⚙️ No projects configured. Please set up projects in the dashboard."
|
|
46
|
+
},
|
|
47
|
+
errors: {
|
|
48
|
+
generic: "Sorry, something went wrong. Please try again.",
|
|
49
|
+
failedToOpenSelector: "❌ Failed to open agent selector. Please try again."
|
|
50
|
+
},
|
|
51
|
+
help: {
|
|
52
|
+
title: "Inkeep — How to Use",
|
|
53
|
+
publicSection: "🔊 *Public* — everyone in the channel can see the response\n\n• `@Inkeep <message>` — Send a message to your agent\n• `@Inkeep <message>` in a thread — Includes thread as context\n• `@Inkeep` in a thread — Uses the full thread as context",
|
|
54
|
+
privateSection: "🔒 *Private* — only you can see the response\n\n• `/inkeep <message>` — Send a message to your agent\n• `/inkeep` — Open the agent picker to choose an agent and prompt",
|
|
55
|
+
otherCommands: "⚙️ *Other Commands*\n\n• `/inkeep run \"agent name\" <message>` — Use a specific agent\n• `/inkeep list` — List available agents\n• `/inkeep status` — Check your connection and agent config\n• `/inkeep link` / `/inkeep unlink` — Manage account connection\n• `/inkeep help` — Show this message"
|
|
56
|
+
},
|
|
57
|
+
agentList: {
|
|
58
|
+
title: "🤖 Available Agents",
|
|
59
|
+
usage: "Usage:",
|
|
60
|
+
runUsage: "`/inkeep run \"agent name\" question` - Run a specific agent",
|
|
61
|
+
andMore: (count) => `...and ${count} more`
|
|
62
|
+
},
|
|
63
|
+
messageContext: { label: "Message:" }
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
//#endregion
|
|
67
|
+
export { SlackStrings };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ManageAppVariables, WorkAppsVariables } from "./types.js";
|
|
2
|
+
import { DefaultAgentConfig, SlackWorkspaceConnection, WorkspaceInstallData, clearWorkspaceConnectionCache, computeWorkspaceConnectionId, createConnectSession, deleteWorkspaceInstallation, findWorkspaceConnectionByTeamId, getConnectionAccessToken, getSlackIntegrationId, getSlackNango, getWorkspaceDefaultAgentFromNango, listWorkspaceInstallations, setWorkspaceDefaultAgent, storeWorkspaceInstallation, updateConnectionMetadata } from "./services/nango.js";
|
|
3
|
+
import { getChannelAgentConfig, getWorkspaceDefaultAgent } from "./services/events/utils.js";
|
|
4
|
+
import "./services/events/index.js";
|
|
5
|
+
import { getBotTokenForTeam, setBotTokenForTeam } from "./services/workspace-tokens.js";
|
|
6
|
+
import "./routes/oauth.js";
|
|
7
|
+
import { OpenAPIHono } from "@hono/zod-openapi";
|
|
8
|
+
|
|
9
|
+
//#region src/slack/index.d.ts
|
|
10
|
+
|
|
11
|
+
declare function createSlackRoutes(): OpenAPIHono<{
|
|
12
|
+
Variables: WorkAppsVariables;
|
|
13
|
+
}, {}, "/">;
|
|
14
|
+
declare const slackRoutes: OpenAPIHono<{
|
|
15
|
+
Variables: WorkAppsVariables;
|
|
16
|
+
}, {}, "/">;
|
|
17
|
+
//#endregion
|
|
18
|
+
export { DefaultAgentConfig, ManageAppVariables, SlackWorkspaceConnection, WorkAppsVariables, WorkspaceInstallData, clearWorkspaceConnectionCache, computeWorkspaceConnectionId, createConnectSession, createSlackRoutes, deleteWorkspaceInstallation, findWorkspaceConnectionByTeamId, getBotTokenForTeam, getChannelAgentConfig, getConnectionAccessToken, getSlackIntegrationId, getSlackNango, getWorkspaceDefaultAgent, getWorkspaceDefaultAgentFromNango, listWorkspaceInstallations, setBotTokenForTeam, setWorkspaceDefaultAgent, slackRoutes, storeWorkspaceInstallation, updateConnectionMetadata };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { clearWorkspaceConnectionCache, computeWorkspaceConnectionId, createConnectSession, deleteWorkspaceInstallation, findWorkspaceConnectionByTeamId, getConnectionAccessToken, getSlackIntegrationId, getSlackNango, getWorkspaceDefaultAgentFromNango, listWorkspaceInstallations, setWorkspaceDefaultAgent, storeWorkspaceInstallation, updateConnectionMetadata } from "./services/nango.js";
|
|
2
|
+
import { getChannelAgentConfig, getWorkspaceDefaultAgent } from "./services/events/utils.js";
|
|
3
|
+
import { getBotTokenForTeam, setBotTokenForTeam } from "./services/workspace-tokens.js";
|
|
4
|
+
import "./services/events/index.js";
|
|
5
|
+
import "./routes/oauth.js";
|
|
6
|
+
import routes_default from "./routes/index.js";
|
|
7
|
+
import { OpenAPIHono } from "@hono/zod-openapi";
|
|
8
|
+
|
|
9
|
+
//#region src/slack/index.ts
|
|
10
|
+
/**
|
|
11
|
+
* Slack Work App
|
|
12
|
+
*
|
|
13
|
+
* Provides Slack integration for Inkeep Agents including:
|
|
14
|
+
* - OAuth-based workspace installation
|
|
15
|
+
* - User account linking via JWT tokens
|
|
16
|
+
* - Slash commands for agent interaction
|
|
17
|
+
* - @mention support for workspace-wide agent access
|
|
18
|
+
* - Channel-specific agent configuration
|
|
19
|
+
*/
|
|
20
|
+
function createSlackRoutes() {
|
|
21
|
+
const app = new OpenAPIHono();
|
|
22
|
+
app.route("/", routes_default);
|
|
23
|
+
return app;
|
|
24
|
+
}
|
|
25
|
+
const slackRoutes = createSlackRoutes();
|
|
26
|
+
|
|
27
|
+
//#endregion
|
|
28
|
+
export { clearWorkspaceConnectionCache, computeWorkspaceConnectionId, createConnectSession, createSlackRoutes, deleteWorkspaceInstallation, findWorkspaceConnectionByTeamId, getBotTokenForTeam, getChannelAgentConfig, getConnectionAccessToken, getSlackIntegrationId, getSlackNango, getWorkspaceDefaultAgent, getWorkspaceDefaultAgentFromNango, listWorkspaceInstallations, setBotTokenForTeam, setWorkspaceDefaultAgent, slackRoutes, storeWorkspaceInstallation, updateConnectionMetadata };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ManageAppVariables } from "../types.js";
|
|
2
|
+
import * as hono0 from "hono";
|
|
3
|
+
|
|
4
|
+
//#region src/slack/middleware/permissions.d.ts
|
|
5
|
+
/**
|
|
6
|
+
* Check if user has admin role (owner or admin)
|
|
7
|
+
*/
|
|
8
|
+
declare function isOrgAdmin(tenantRole: string | undefined): boolean;
|
|
9
|
+
/**
|
|
10
|
+
* Middleware that requires Inkeep org admin/owner role.
|
|
11
|
+
* Use for workspace-level settings that only Inkeep organization admins can modify.
|
|
12
|
+
*/
|
|
13
|
+
declare const requireWorkspaceAdmin: <Env extends {
|
|
14
|
+
Variables: ManageAppVariables;
|
|
15
|
+
} = {
|
|
16
|
+
Variables: ManageAppVariables;
|
|
17
|
+
}>() => hono0.MiddlewareHandler<Env, string, {}, Response>;
|
|
18
|
+
/**
|
|
19
|
+
* Middleware that requires either:
|
|
20
|
+
* 1. Org admin/owner role (can modify any channel), OR
|
|
21
|
+
* 2. Member role AND membership in the specific Slack channel
|
|
22
|
+
*
|
|
23
|
+
* Use for channel-level settings where members can configure their own channels.
|
|
24
|
+
*/
|
|
25
|
+
declare const requireChannelMemberOrAdmin: <Env extends {
|
|
26
|
+
Variables: ManageAppVariables;
|
|
27
|
+
} = {
|
|
28
|
+
Variables: ManageAppVariables;
|
|
29
|
+
}>() => hono0.MiddlewareHandler<Env, string, {}, Response>;
|
|
30
|
+
//#endregion
|
|
31
|
+
export { isOrgAdmin, requireChannelMemberOrAdmin, requireWorkspaceAdmin };
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { getLogger } from "../../logger.js";
|
|
2
|
+
import runDbClient_default from "../../db/runDbClient.js";
|
|
3
|
+
import { findWorkspaceConnectionByTeamId } from "../services/nango.js";
|
|
4
|
+
import { checkUserIsChannelMember, getSlackClient } from "../services/client.js";
|
|
5
|
+
import { OrgRoles, createApiError, findWorkAppSlackUserMappingByInkeepUserId, getUserOrganizationsFromDb } from "@inkeep/agents-core";
|
|
6
|
+
import { createMiddleware } from "hono/factory";
|
|
7
|
+
|
|
8
|
+
//#region src/slack/middleware/permissions.ts
|
|
9
|
+
const logger = getLogger("slack-permissions");
|
|
10
|
+
/**
|
|
11
|
+
* Check if user has admin role (owner or admin)
|
|
12
|
+
*/
|
|
13
|
+
function isOrgAdmin(tenantRole) {
|
|
14
|
+
return tenantRole === OrgRoles.OWNER || tenantRole === OrgRoles.ADMIN;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Resolve tenantId and tenantRole from a Slack teamId.
|
|
18
|
+
* Looks up the workspace connection to find the owning tenant,
|
|
19
|
+
* then checks the user's org membership to determine their role.
|
|
20
|
+
*
|
|
21
|
+
* This is needed because /work-apps/* routes don't go through requireTenantAccess
|
|
22
|
+
* middleware (which normally sets tenantRole on /manage/* routes).
|
|
23
|
+
*/
|
|
24
|
+
async function resolveWorkAppTenantContext(c, teamId, userId) {
|
|
25
|
+
const workspace = await findWorkspaceConnectionByTeamId(teamId);
|
|
26
|
+
if (!workspace?.tenantId) throw createApiError({
|
|
27
|
+
code: "not_found",
|
|
28
|
+
message: "Slack workspace not found or not associated with a tenant",
|
|
29
|
+
instance: c.req.path
|
|
30
|
+
});
|
|
31
|
+
const orgAccess = (await getUserOrganizationsFromDb(runDbClient_default)(userId)).find((org) => org.organizationId === workspace.tenantId);
|
|
32
|
+
if (!orgAccess) throw createApiError({
|
|
33
|
+
code: "forbidden",
|
|
34
|
+
message: "Access denied to this organization",
|
|
35
|
+
instance: c.req.path
|
|
36
|
+
});
|
|
37
|
+
c.set("tenantId", workspace.tenantId);
|
|
38
|
+
c.set("tenantRole", orgAccess.role);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Middleware that requires Inkeep org admin/owner role.
|
|
42
|
+
* Use for workspace-level settings that only Inkeep organization admins can modify.
|
|
43
|
+
*/
|
|
44
|
+
const requireWorkspaceAdmin = () => createMiddleware(async (c, next) => {
|
|
45
|
+
if (process.env.ENVIRONMENT === "test" && c.req.header("x-test-bypass-auth") === "true") {
|
|
46
|
+
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
|
+
});
|
|
55
|
+
if (userId === "system" || userId.startsWith("apikey:")) {
|
|
56
|
+
await next();
|
|
57
|
+
return;
|
|
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
|
|
67
|
+
});
|
|
68
|
+
if (!isOrgAdmin(tenantRole)) {
|
|
69
|
+
logger.warn({
|
|
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
|
+
});
|
|
87
|
+
/**
|
|
88
|
+
* Middleware that requires either:
|
|
89
|
+
* 1. Org admin/owner role (can modify any channel), OR
|
|
90
|
+
* 2. Member role AND membership in the specific Slack channel
|
|
91
|
+
*
|
|
92
|
+
* Use for channel-level settings where members can configure their own channels.
|
|
93
|
+
*/
|
|
94
|
+
const requireChannelMemberOrAdmin = () => createMiddleware(async (c, next) => {
|
|
95
|
+
if (process.env.ENVIRONMENT === "test" && c.req.header("x-test-bypass-auth") === "true") {
|
|
96
|
+
await next();
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const userId = c.get("userId");
|
|
100
|
+
if (!userId) throw createApiError({
|
|
101
|
+
code: "unauthorized",
|
|
102
|
+
message: "User context not found",
|
|
103
|
+
instance: c.req.path
|
|
104
|
+
});
|
|
105
|
+
if (userId === "system" || userId.startsWith("apikey:")) {
|
|
106
|
+
await next();
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const teamId = c.req.param("teamId");
|
|
110
|
+
if (teamId && !c.get("tenantRole")) await resolveWorkAppTenantContext(c, teamId, userId);
|
|
111
|
+
const tenantId = c.get("tenantId");
|
|
112
|
+
const tenantRole = c.get("tenantRole");
|
|
113
|
+
if (!tenantId) throw createApiError({
|
|
114
|
+
code: "unauthorized",
|
|
115
|
+
message: "Organization context not found",
|
|
116
|
+
instance: c.req.path
|
|
117
|
+
});
|
|
118
|
+
if (isOrgAdmin(tenantRole)) {
|
|
119
|
+
await next();
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
const channelId = c.req.param("channelId");
|
|
123
|
+
if (!teamId || !channelId) throw createApiError({
|
|
124
|
+
code: "bad_request",
|
|
125
|
+
message: "Team ID and Channel ID are required",
|
|
126
|
+
instance: c.req.path
|
|
127
|
+
});
|
|
128
|
+
const userMapping = (await findWorkAppSlackUserMappingByInkeepUserId(runDbClient_default)(userId)).find((m) => m.tenantId === tenantId && m.slackTeamId === teamId);
|
|
129
|
+
if (!userMapping) throw createApiError({
|
|
130
|
+
code: "forbidden",
|
|
131
|
+
message: "You must link your Slack account to modify channel settings. Use /inkeep link in Slack.",
|
|
132
|
+
instance: c.req.path
|
|
133
|
+
});
|
|
134
|
+
const workspace = await findWorkspaceConnectionByTeamId(teamId);
|
|
135
|
+
if (!workspace?.botToken) throw createApiError({
|
|
136
|
+
code: "not_found",
|
|
137
|
+
message: "Slack workspace not found or not properly configured",
|
|
138
|
+
instance: c.req.path
|
|
139
|
+
});
|
|
140
|
+
if (!await checkUserIsChannelMember(getSlackClient(workspace.botToken), channelId, userMapping.slackUserId)) {
|
|
141
|
+
logger.info({
|
|
142
|
+
userId,
|
|
143
|
+
slackUserId: userMapping.slackUserId,
|
|
144
|
+
channelId,
|
|
145
|
+
teamId
|
|
146
|
+
}, "User is not a member of the channel");
|
|
147
|
+
throw createApiError({
|
|
148
|
+
code: "forbidden",
|
|
149
|
+
message: "You can only configure channels you are a member of",
|
|
150
|
+
instance: c.req.path,
|
|
151
|
+
extensions: {
|
|
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
|
+
});
|
|
165
|
+
|
|
166
|
+
//#endregion
|
|
167
|
+
export { isOrgAdmin, requireChannelMemberOrAdmin, requireWorkspaceAdmin };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { WorkAppsVariables } from "../types.js";
|
|
2
|
+
import { OpenAPIHono } from "@hono/zod-openapi";
|
|
3
|
+
|
|
4
|
+
//#region src/slack/routes/events.d.ts
|
|
5
|
+
|
|
6
|
+
declare const app: OpenAPIHono<{
|
|
7
|
+
Variables: WorkAppsVariables;
|
|
8
|
+
}, {}, "/">;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { app as default };
|