@meetopenbot/slack 0.0.1 → 0.0.3
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/README.md +4 -2
- package/dist/cloud-mode.js +10 -0
- package/dist/config.js +59 -10
- package/dist/credits-auth.js +53 -0
- package/dist/index.js +51 -35
- package/dist/model-registry.js +40 -0
- package/dist/model.js +24 -0
- package/dist/slack-agent.js +6 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -24,11 +24,13 @@ plugins:
|
|
|
24
24
|
signingSecret: your-signing-secret
|
|
25
25
|
botToken: xoxb-your-bot-token
|
|
26
26
|
teamId: T01234567
|
|
27
|
-
|
|
28
|
-
model: gpt-4o-mini
|
|
27
|
+
authMode: credits
|
|
28
|
+
model: openai/gpt-4o-mini
|
|
29
29
|
channelMappings: "C01234567:engineering,C89ABCDEF:support"
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
+
On cloud, `authMode: credits` uses your workspace credit balance. For BYOK (bring your own key), set `authMode: byok` and add `OPENAI_API_KEY` under workspace settings — no API key field in plugin config.
|
|
33
|
+
|
|
32
34
|
### Channel routing (webhook ingress)
|
|
33
35
|
|
|
34
36
|
When a Slack message arrives via the Events API webhook, the plugin delegates to the OpenBot **system** agent in a target channel. Set `channelMappings` as comma-separated `slackChannelId:openbotChannelId` pairs:
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** True when this runtime is a platform-managed cloud deployment. */
|
|
2
|
+
export const isCloudMode = () => process.env.OPENBOT_CLOUD_MODE === "1";
|
|
3
|
+
/** Default auth mode: Credits on cloud, BYOK locally. */
|
|
4
|
+
export const defaultAuthMode = () => isCloudMode() ? "credits" : "byok";
|
|
5
|
+
export function resolveAuthMode(config) {
|
|
6
|
+
if (config.authMode === "byok" || config.authMode === "credits") {
|
|
7
|
+
return config.authMode;
|
|
8
|
+
}
|
|
9
|
+
return defaultAuthMode();
|
|
10
|
+
}
|
package/dist/config.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { resolveAuthMode } from "./cloud-mode.js";
|
|
2
|
+
import { shouldUseCreditsAuth } from "./credits-auth.js";
|
|
1
3
|
export const DEFAULT_OPENBOT_CHANNEL_ID = "uncategorized";
|
|
2
4
|
/** Parse `C0123:engineering,C0456:support` into a lookup index. */
|
|
3
5
|
export function buildSlackChannelMappingIndex(config) {
|
|
@@ -30,13 +32,58 @@ export function resolveOpenBotChannelId(slackChannelId, config) {
|
|
|
30
32
|
const mapped = buildSlackChannelMappingIndex(config).get(normalized);
|
|
31
33
|
return mapped ?? DEFAULT_OPENBOT_CHANNEL_ID;
|
|
32
34
|
}
|
|
33
|
-
function
|
|
34
|
-
const
|
|
35
|
-
|
|
35
|
+
function variableValue(variables, key) {
|
|
36
|
+
const entry = variables[key];
|
|
37
|
+
if (typeof entry === "string")
|
|
38
|
+
return entry || undefined;
|
|
39
|
+
return entry?.value || undefined;
|
|
40
|
+
}
|
|
41
|
+
export function readSlackConfig(config) {
|
|
42
|
+
return {
|
|
43
|
+
signingSecret: typeof config.signingSecret === "string" && config.signingSecret.trim()
|
|
44
|
+
? config.signingSecret.trim()
|
|
45
|
+
: undefined,
|
|
46
|
+
botToken: typeof config.botToken === "string" && config.botToken.trim()
|
|
47
|
+
? config.botToken.trim()
|
|
48
|
+
: undefined,
|
|
49
|
+
teamId: typeof config.teamId === "string" && config.teamId.trim()
|
|
50
|
+
? config.teamId.trim()
|
|
51
|
+
: undefined,
|
|
52
|
+
channelMappings: typeof config.channelMappings === "string" &&
|
|
53
|
+
config.channelMappings.trim()
|
|
54
|
+
? config.channelMappings.trim()
|
|
55
|
+
: undefined,
|
|
56
|
+
authMode: resolveAuthMode(config),
|
|
57
|
+
model: typeof config.model === "string" && config.model.trim()
|
|
58
|
+
? config.model.trim()
|
|
59
|
+
: undefined,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export function formatMissingCredentials(missing, authMode) {
|
|
63
|
+
const lines = [
|
|
64
|
+
"Slack agent setup is incomplete. Configure the following in plugin config, workspace settings, or environment variables:",
|
|
65
|
+
];
|
|
66
|
+
if (missing.includes("botToken")) {
|
|
67
|
+
lines.push("- `botToken` / `SLACK_BOT_TOKEN` — Slack bot OAuth token (`xoxb-…`)");
|
|
68
|
+
}
|
|
69
|
+
if (missing.includes("teamId")) {
|
|
70
|
+
lines.push("- `teamId` / `SLACK_TEAM_ID` — workspace team id (`T…`)");
|
|
71
|
+
}
|
|
72
|
+
if (missing.includes("openaiApiKey")) {
|
|
73
|
+
if (authMode === "credits") {
|
|
74
|
+
lines.push("- OpenAI API key is required in BYOK mode — add `OPENAI_API_KEY` under workspace settings, or switch `authMode` to `credits` on cloud");
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
lines.push("- `OPENAI_API_KEY` — OpenAI API key for the agent loop (BYOK mode), or switch `authMode` to `credits` on cloud");
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return lines.join("\n");
|
|
36
81
|
}
|
|
37
82
|
export async function resolveSlackCredentials(config, storage, options) {
|
|
38
|
-
const
|
|
39
|
-
const
|
|
83
|
+
const authMode = config.authMode ?? resolveAuthMode({});
|
|
84
|
+
const useCredits = shouldUseCreditsAuth({ authMode });
|
|
85
|
+
const requireOpenAi = options?.requireOpenAi ?? !useCredits;
|
|
86
|
+
const variables = (await storage.getVariables().catch(() => ({})));
|
|
40
87
|
const resolve = (configKey, envKey) => {
|
|
41
88
|
const fromConfig = config[configKey];
|
|
42
89
|
if (typeof fromConfig === "string" && fromConfig.trim()) {
|
|
@@ -44,16 +91,17 @@ export async function resolveSlackCredentials(config, storage, options) {
|
|
|
44
91
|
}
|
|
45
92
|
if (process.env[envKey]?.trim())
|
|
46
93
|
return process.env[envKey].trim();
|
|
47
|
-
return
|
|
94
|
+
return variableValue(variables, envKey)?.trim();
|
|
48
95
|
};
|
|
49
96
|
const signingSecret = (typeof config.signingSecret === "string" && config.signingSecret.trim()) ||
|
|
50
97
|
process.env.SLACK_SIGNING_SECRET?.trim() ||
|
|
51
|
-
|
|
98
|
+
variableValue(variables, "SLACK_SIGNING_SECRET") ||
|
|
52
99
|
"";
|
|
53
100
|
const botToken = resolve("botToken", "SLACK_BOT_TOKEN");
|
|
54
101
|
const teamId = resolve("teamId", "SLACK_TEAM_ID");
|
|
55
|
-
const openaiApiKey =
|
|
56
|
-
|
|
102
|
+
const openaiApiKey = process.env.OPENAI_API_KEY?.trim() ||
|
|
103
|
+
variableValue(variables, "OPENAI_API_KEY");
|
|
104
|
+
const model = resolve("model", "OPENAI_MODEL") ?? "openai/gpt-4o-mini";
|
|
57
105
|
const missing = [];
|
|
58
106
|
if (!botToken)
|
|
59
107
|
missing.push("botToken");
|
|
@@ -70,7 +118,8 @@ export async function resolveSlackCredentials(config, storage, options) {
|
|
|
70
118
|
signingSecret,
|
|
71
119
|
botToken: botToken,
|
|
72
120
|
teamId: teamId,
|
|
73
|
-
|
|
121
|
+
authMode,
|
|
122
|
+
openaiApiKey: openaiApiKey || undefined,
|
|
74
123
|
model,
|
|
75
124
|
},
|
|
76
125
|
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { isCloudMode } from "./cloud-mode.js";
|
|
2
|
+
export const INTEGRATIONS_TOKEN_HEADER = "x-openbot-integrations-token";
|
|
3
|
+
export const CREDITS_API_KEY_PLACEHOLDER = "openbot-credits";
|
|
4
|
+
/** Cloud host injects these when routing LLM calls through OpenBot Credits. */
|
|
5
|
+
export function resolveCreditsAuthConfig() {
|
|
6
|
+
const baseUrl = process.env.OPENBOT_INTEGRATIONS_BASE_URL?.trim();
|
|
7
|
+
const token = process.env.OPENBOT_INTEGRATIONS_TOKEN?.trim();
|
|
8
|
+
if (!baseUrl || !token)
|
|
9
|
+
return undefined;
|
|
10
|
+
return { baseUrl: baseUrl.replace(/\/$/, ""), token };
|
|
11
|
+
}
|
|
12
|
+
export function creditsProviderBaseUrl(config) {
|
|
13
|
+
return `${config.baseUrl}/openai/v1`;
|
|
14
|
+
}
|
|
15
|
+
export function shouldUseCreditsAuth(options) {
|
|
16
|
+
if (options?.authMode === "byok")
|
|
17
|
+
return false;
|
|
18
|
+
if (options?.authMode === "credits")
|
|
19
|
+
return true;
|
|
20
|
+
return isCloudMode() && resolveCreditsAuthConfig() !== undefined;
|
|
21
|
+
}
|
|
22
|
+
export function isCreditsErrorMessage(message) {
|
|
23
|
+
const lower = message.toLowerCase();
|
|
24
|
+
return (lower.includes("insufficient_credits") ||
|
|
25
|
+
lower.includes("insufficient credits") ||
|
|
26
|
+
lower.includes("402"));
|
|
27
|
+
}
|
|
28
|
+
export function isAuthErrorMessage(message) {
|
|
29
|
+
const lower = message.toLowerCase();
|
|
30
|
+
return (lower.includes("api key") ||
|
|
31
|
+
lower.includes("401") ||
|
|
32
|
+
lower.includes("unauthorized") ||
|
|
33
|
+
lower.includes("authentication"));
|
|
34
|
+
}
|
|
35
|
+
export function isIntegrationsProviderError(message) {
|
|
36
|
+
const lower = message.toLowerCase();
|
|
37
|
+
return (lower.includes("provider api key not configured") ||
|
|
38
|
+
(lower.includes("503") && lower.includes("provider")));
|
|
39
|
+
}
|
|
40
|
+
export const CREDITS_NOT_CONFIGURED_MESSAGE = "OpenBot Credits is not configured on this runtime. The cloud host must set OPENBOT_INTEGRATIONS_BASE_URL and OPENBOT_INTEGRATIONS_TOKEN (try redeploying the workspace).";
|
|
41
|
+
export const CREDITS_PROVIDER_UNAVAILABLE_MESSAGE = "OpenBot Credits could not reach OpenAI — the platform provider API key is not configured yet. Try again later or switch this agent to BYOK mode.";
|
|
42
|
+
export const CREDITS_AUTH_FAILED_MESSAGE = "Slack could not authenticate via OpenBot Credits. Check your workspace credit balance in settings, or switch this agent to BYOK mode.";
|
|
43
|
+
export function creditsErrorMessage(message) {
|
|
44
|
+
if (isIntegrationsProviderError(message)) {
|
|
45
|
+
return CREDITS_PROVIDER_UNAVAILABLE_MESSAGE;
|
|
46
|
+
}
|
|
47
|
+
if (isCreditsErrorMessage(message)) {
|
|
48
|
+
return "Insufficient workspace credits. Add credits in workspace settings or switch this agent to BYOK mode.";
|
|
49
|
+
}
|
|
50
|
+
if (isAuthErrorMessage(message))
|
|
51
|
+
return CREDITS_AUTH_FAILED_MESSAGE;
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { agentOutput, definePlugin, decodeWebhookRawBody, getWebhookHeader, shouldHandleInvoke, webhookHttpResponse, } from "@meetopenbot/plugin-sdk";
|
|
3
|
-
import {
|
|
3
|
+
import { isCloudMode } from "./cloud-mode.js";
|
|
4
|
+
import { CREDITS_NOT_CONFIGURED_MESSAGE, creditsErrorMessage, resolveCreditsAuthConfig, } from "./credits-auth.js";
|
|
5
|
+
import { DEFAULT_OPENBOT_CHANNEL_ID, formatMissingCredentials, readSlackConfig, resolveOpenBotChannelId, resolveSlackCredentials, } from "./config.js";
|
|
6
|
+
import { resolveModelConfigField } from "./model-registry.js";
|
|
4
7
|
import { postSlackMessage } from "./slack-post.js";
|
|
5
8
|
import { runSlackAgent } from "./slack-agent.js";
|
|
6
9
|
import { verifySlackSignature } from "./slack-verify.js";
|
|
7
10
|
const ROUTING_AGENT_ID = "system";
|
|
11
|
+
const SLACK_USER_DISPLAY_NAME = "Slack user";
|
|
8
12
|
function isSlackWebhookIngress(meta) {
|
|
9
13
|
return meta?.source === "slack";
|
|
10
14
|
}
|
|
@@ -51,21 +55,6 @@ function getSlackThreadMeta(meta) {
|
|
|
51
55
|
return undefined;
|
|
52
56
|
return slack;
|
|
53
57
|
}
|
|
54
|
-
function formatMissingCredentials(missing) {
|
|
55
|
-
const lines = [
|
|
56
|
-
"Slack agent setup is incomplete. Configure the following in plugin config or environment variables:",
|
|
57
|
-
];
|
|
58
|
-
if (missing.includes("botToken")) {
|
|
59
|
-
lines.push("- `botToken` / `SLACK_BOT_TOKEN` — Slack bot OAuth token (`xoxb-…`)");
|
|
60
|
-
}
|
|
61
|
-
if (missing.includes("teamId")) {
|
|
62
|
-
lines.push("- `teamId` / `SLACK_TEAM_ID` — workspace team id (`T…`)");
|
|
63
|
-
}
|
|
64
|
-
if (missing.includes("openaiApiKey")) {
|
|
65
|
-
lines.push("- `openaiApiKey` / `OPENAI_API_KEY` — OpenAI API key for the agent loop");
|
|
66
|
-
}
|
|
67
|
-
return lines.join("\n");
|
|
68
|
-
}
|
|
69
58
|
async function* bridgeSystemAgentRun(args) {
|
|
70
59
|
const runId = `slack_${randomUUID()}`;
|
|
71
60
|
const slackMeta = getSlackThreadMeta(args.meta);
|
|
@@ -91,6 +80,10 @@ async function* bridgeSystemAgentRun(args) {
|
|
|
91
80
|
threadId: args.threadId,
|
|
92
81
|
source: "slack",
|
|
93
82
|
slack: slackMeta,
|
|
83
|
+
userName: SLACK_USER_DISPLAY_NAME,
|
|
84
|
+
...(typeof args.meta?.userId === "string"
|
|
85
|
+
? { userId: args.meta.userId }
|
|
86
|
+
: {}),
|
|
94
87
|
},
|
|
95
88
|
},
|
|
96
89
|
onEvent: async (outEvent) => {
|
|
@@ -146,36 +139,41 @@ async function* bridgeSystemAgentRun(args) {
|
|
|
146
139
|
}
|
|
147
140
|
await runPromise;
|
|
148
141
|
}
|
|
142
|
+
const modelField = await resolveModelConfigField();
|
|
149
143
|
const slackPluginConfigSchema = {
|
|
150
144
|
type: "object",
|
|
151
145
|
properties: {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
146
|
+
...(isCloudMode()
|
|
147
|
+
? {
|
|
148
|
+
authMode: {
|
|
149
|
+
type: "string",
|
|
150
|
+
description: "Outbound — Credits uses your workspace credit balance via OpenBot. BYOK uses `OPENAI_API_KEY` from workspace settings.",
|
|
151
|
+
enum: ["credits", "byok"],
|
|
152
|
+
default: "credits",
|
|
153
|
+
},
|
|
154
|
+
}
|
|
155
|
+
: {}),
|
|
156
|
+
model: {
|
|
157
|
+
...modelField,
|
|
158
|
+
description: "Outbound — OpenAI model for direct Slack agent invocations",
|
|
156
159
|
},
|
|
157
160
|
botToken: {
|
|
158
161
|
type: "string",
|
|
159
|
-
description: "Slack bot token (OAuth & Permissions → Bot User OAuth Token)",
|
|
162
|
+
description: "Outbound — Slack bot token (OAuth & Permissions → Bot User OAuth Token)",
|
|
160
163
|
format: "password",
|
|
161
164
|
},
|
|
162
165
|
teamId: {
|
|
163
166
|
type: "string",
|
|
164
|
-
description: "Slack workspace team id (starts with T)",
|
|
165
|
-
},
|
|
166
|
-
channelMappings: {
|
|
167
|
-
type: "string",
|
|
168
|
-
description: "Webhook ingress routing. Comma-separated slackChannelId:openbotChannelId pairs (e.g. C01234567:engineering,C89ABCDEF:support). Unmapped Slack channels use uncategorized.",
|
|
167
|
+
description: "Outbound — Slack workspace team id (starts with T)",
|
|
169
168
|
},
|
|
170
|
-
|
|
169
|
+
signingSecret: {
|
|
171
170
|
type: "string",
|
|
172
|
-
description: "
|
|
171
|
+
description: "Inbound — Slack app signing secret (Basic Information → App Credentials)",
|
|
173
172
|
format: "password",
|
|
174
173
|
},
|
|
175
|
-
|
|
174
|
+
channelMappings: {
|
|
176
175
|
type: "string",
|
|
177
|
-
description: "
|
|
178
|
-
default: "gpt-4o-mini",
|
|
176
|
+
description: "Inbound — webhook routing. Comma-separated slackChannelId:openbotChannelId pairs (e.g. C01234567:engineering,C89ABCDEF:support). Unmapped Slack channels use uncategorized.",
|
|
179
177
|
},
|
|
180
178
|
},
|
|
181
179
|
required: ["signingSecret"],
|
|
@@ -186,7 +184,7 @@ export default definePlugin({
|
|
|
186
184
|
configSchema: slackPluginConfigSchema,
|
|
187
185
|
factory: (pluginContext) => (builder) => {
|
|
188
186
|
const { agentId, config, storage } = pluginContext;
|
|
189
|
-
const slackConfig = config;
|
|
187
|
+
const slackConfig = readSlackConfig(config);
|
|
190
188
|
const host = pluginContext.host;
|
|
191
189
|
const publicBaseUrl = pluginContext.publicBaseUrl ?? "";
|
|
192
190
|
builder.on("action:webhook", async function* (event, ctx) {
|
|
@@ -274,6 +272,8 @@ export default definePlugin({
|
|
|
274
272
|
threadId,
|
|
275
273
|
source: "slack",
|
|
276
274
|
slack: slackMeta,
|
|
275
|
+
userName: SLACK_USER_DISPLAY_NAME,
|
|
276
|
+
...(slackEvent.user ? { userId: slackEvent.user } : {}),
|
|
277
277
|
},
|
|
278
278
|
};
|
|
279
279
|
});
|
|
@@ -301,7 +301,9 @@ export default definePlugin({
|
|
|
301
301
|
if (!auth.ok) {
|
|
302
302
|
yield {
|
|
303
303
|
type: "agent:output",
|
|
304
|
-
data: {
|
|
304
|
+
data: {
|
|
305
|
+
content: formatMissingCredentials(auth.missing, slackConfig.authMode),
|
|
306
|
+
},
|
|
305
307
|
meta: {
|
|
306
308
|
agentId: ROUTING_AGENT_ID,
|
|
307
309
|
threadId,
|
|
@@ -361,7 +363,17 @@ export default definePlugin({
|
|
|
361
363
|
if (!auth.ok) {
|
|
362
364
|
yield agentOutput({
|
|
363
365
|
agentId,
|
|
364
|
-
content: formatMissingCredentials(auth.missing),
|
|
366
|
+
content: formatMissingCredentials(auth.missing, slackConfig.authMode),
|
|
367
|
+
threadId,
|
|
368
|
+
meta: event.meta,
|
|
369
|
+
});
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
if (auth.credentials.authMode === "credits" &&
|
|
373
|
+
!resolveCreditsAuthConfig()) {
|
|
374
|
+
yield agentOutput({
|
|
375
|
+
agentId,
|
|
376
|
+
content: CREDITS_NOT_CONFIGURED_MESSAGE,
|
|
365
377
|
threadId,
|
|
366
378
|
meta: event.meta,
|
|
367
379
|
});
|
|
@@ -370,6 +382,7 @@ export default definePlugin({
|
|
|
370
382
|
try {
|
|
371
383
|
const reply = await runSlackAgent({
|
|
372
384
|
prompt: userMessage,
|
|
385
|
+
authMode: auth.credentials.authMode,
|
|
373
386
|
openaiApiKey: auth.credentials.openaiApiKey,
|
|
374
387
|
botToken: auth.credentials.botToken,
|
|
375
388
|
teamId: auth.credentials.teamId,
|
|
@@ -384,9 +397,12 @@ export default definePlugin({
|
|
|
384
397
|
}
|
|
385
398
|
catch (error) {
|
|
386
399
|
const message = error instanceof Error ? error.message : String(error);
|
|
400
|
+
const creditsMessage = auth.credentials.authMode === "credits"
|
|
401
|
+
? creditsErrorMessage(message)
|
|
402
|
+
: undefined;
|
|
387
403
|
yield agentOutput({
|
|
388
404
|
agentId,
|
|
389
|
-
content: `Slack agent error: ${message}`,
|
|
405
|
+
content: creditsMessage ?? `Slack agent error: ${message}`,
|
|
390
406
|
threadId,
|
|
391
407
|
meta: event.meta,
|
|
392
408
|
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
const REGISTRY_URL = "https://raw.githubusercontent.com/meetopenbot/openbot-registry/main/registry.json";
|
|
2
|
+
const OPENAI_PROVIDER = "openai";
|
|
3
|
+
const freeInputModelField = () => ({
|
|
4
|
+
type: "string",
|
|
5
|
+
override: true,
|
|
6
|
+
description: "OpenAI model in provider/model-id format (e.g. openai/gpt-4o-mini).",
|
|
7
|
+
default: "openai/gpt-4o-mini",
|
|
8
|
+
});
|
|
9
|
+
/** Registry-backed model field for plugin configSchema (`enum` + labeled `options`). */
|
|
10
|
+
export async function resolveModelConfigField() {
|
|
11
|
+
try {
|
|
12
|
+
const res = await fetch(REGISTRY_URL, {
|
|
13
|
+
headers: { Accept: "application/json" },
|
|
14
|
+
signal: AbortSignal.timeout(15_000),
|
|
15
|
+
});
|
|
16
|
+
if (!res.ok)
|
|
17
|
+
return freeInputModelField();
|
|
18
|
+
const registry = (await res.json());
|
|
19
|
+
const provider = registry.providers?.[OPENAI_PROVIDER];
|
|
20
|
+
const models = provider?.models ?? [];
|
|
21
|
+
if (models.length === 0)
|
|
22
|
+
return freeInputModelField();
|
|
23
|
+
const defaultModel = models.find((model) => model.id === "gpt-4o-mini")?.id ?? models[0].id;
|
|
24
|
+
return {
|
|
25
|
+
type: "string",
|
|
26
|
+
override: true,
|
|
27
|
+
description: "OpenAI model from the OpenBot registry.",
|
|
28
|
+
default: `${OPENAI_PROVIDER}/${defaultModel}`,
|
|
29
|
+
enum: models.map((model) => `${OPENAI_PROVIDER}/${model.id}`),
|
|
30
|
+
options: models.map((model) => ({
|
|
31
|
+
label: `${provider?.label ?? "OpenAI"} — ${model.label}`,
|
|
32
|
+
value: `${OPENAI_PROVIDER}/${model.id}`,
|
|
33
|
+
description: model.description,
|
|
34
|
+
})),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return freeInputModelField();
|
|
39
|
+
}
|
|
40
|
+
}
|
package/dist/model.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { createOpenAI } from "@ai-sdk/openai";
|
|
2
|
+
import { CREDITS_API_KEY_PLACEHOLDER, INTEGRATIONS_TOKEN_HEADER, creditsProviderBaseUrl, resolveCreditsAuthConfig, shouldUseCreditsAuth, } from "./credits-auth.js";
|
|
3
|
+
function normalizeOpenAiModelId(model) {
|
|
4
|
+
return model.includes("/") ? model.split("/").slice(1).join("/") : model;
|
|
5
|
+
}
|
|
6
|
+
export function resolveOpenAiModel(model, options) {
|
|
7
|
+
const modelId = normalizeOpenAiModelId(model);
|
|
8
|
+
const useCredits = shouldUseCreditsAuth(options);
|
|
9
|
+
if (useCredits) {
|
|
10
|
+
const config = resolveCreditsAuthConfig();
|
|
11
|
+
if (!config) {
|
|
12
|
+
throw new Error("OpenBot Credits is not configured. The cloud host must set OPENBOT_INTEGRATIONS_BASE_URL and OPENBOT_INTEGRATIONS_TOKEN.");
|
|
13
|
+
}
|
|
14
|
+
const baseURL = creditsProviderBaseUrl(config);
|
|
15
|
+
const headers = { [INTEGRATIONS_TOKEN_HEADER]: config.token };
|
|
16
|
+
const apiKey = config.token || CREDITS_API_KEY_PLACEHOLDER;
|
|
17
|
+
return createOpenAI({ baseURL, apiKey, headers })(modelId);
|
|
18
|
+
}
|
|
19
|
+
const apiKey = options?.openaiApiKey?.trim();
|
|
20
|
+
if (!apiKey) {
|
|
21
|
+
throw new Error("OpenAI API key is required in BYOK mode. Add `OPENAI_API_KEY` under workspace settings or switch `authMode` to `credits` on cloud.");
|
|
22
|
+
}
|
|
23
|
+
return createOpenAI({ apiKey })(modelId);
|
|
24
|
+
}
|
package/dist/slack-agent.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
import { createOpenAI } from "@ai-sdk/openai";
|
|
2
1
|
import { generateText, stepCountIs } from "ai";
|
|
2
|
+
import { resolveOpenAiModel } from "./model.js";
|
|
3
3
|
import { createSlackMcpClient } from "./slack-mcp.js";
|
|
4
4
|
const SYSTEM_PROMPT = `You are the OpenBot Slack specialist agent.
|
|
5
5
|
Use Slack MCP tools to post messages, list channels, read history, and perform other workspace actions when needed.
|
|
6
6
|
Summarize results clearly in plain text for the user. Be concise and friendly.`;
|
|
7
7
|
export async function runSlackAgent(args) {
|
|
8
|
-
const
|
|
8
|
+
const model = resolveOpenAiModel(args.model ?? "openai/gpt-4o-mini", {
|
|
9
|
+
authMode: args.authMode,
|
|
10
|
+
openaiApiKey: args.openaiApiKey,
|
|
11
|
+
});
|
|
9
12
|
const mcpClient = await createSlackMcpClient({
|
|
10
13
|
botToken: args.botToken,
|
|
11
14
|
teamId: args.teamId,
|
|
@@ -13,7 +16,7 @@ export async function runSlackAgent(args) {
|
|
|
13
16
|
try {
|
|
14
17
|
const tools = await mcpClient.tools();
|
|
15
18
|
const result = await generateText({
|
|
16
|
-
model
|
|
19
|
+
model,
|
|
17
20
|
system: SYSTEM_PROMPT,
|
|
18
21
|
prompt: args.prompt,
|
|
19
22
|
tools,
|