@fastagent-sh/fastagent 0.14.0 → 0.16.0
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 +16 -6
- package/dist/agent.d.ts +20 -0
- package/dist/agent.js +9 -0
- package/dist/channels/agentcore-limits.d.ts +7 -0
- package/dist/channels/agentcore-limits.js +9 -0
- package/dist/channels/agentcore-state.d.ts +83 -0
- package/dist/channels/agentcore-state.js +258 -0
- package/dist/channels/agentcore.d.ts +98 -0
- package/dist/channels/agentcore.js +260 -0
- package/dist/channels/busy.d.ts +23 -0
- package/dist/channels/busy.js +53 -0
- package/dist/channels/context-buffer.d.ts +35 -0
- package/dist/channels/context-buffer.js +103 -0
- package/dist/channels/control.d.ts +28 -0
- package/dist/channels/control.js +214 -0
- package/dist/channels/feishu/card.js +1 -1
- package/dist/channels/feishu/context-buffer.d.ts +15 -13
- package/dist/channels/feishu/context-buffer.js +62 -69
- package/dist/channels/feishu/feishu-api.d.ts +11 -3
- package/dist/channels/feishu/feishu-api.js +24 -3
- package/dist/channels/feishu/feishu.d.ts +27 -27
- package/dist/channels/feishu/feishu.js +318 -207
- package/dist/channels/feishu/invoke-turn.d.ts +7 -20
- package/dist/channels/feishu/invoke-turn.js +44 -79
- package/dist/channels/feishu/model.d.ts +2 -2
- package/dist/channels/feishu/normalize.d.ts +2 -1
- package/dist/channels/feishu/normalize.js +0 -1
- package/dist/channels/feishu/parse.d.ts +22 -8
- package/dist/channels/feishu/parse.js +24 -7
- package/dist/channels/feishu/preview.d.ts +4 -7
- package/dist/channels/feishu/preview.js +27 -142
- package/dist/channels/feishu/register-app.d.ts +2 -1
- package/dist/channels/feishu/register-webhook.d.ts +2 -1
- package/dist/channels/feishu/scaffold/channel.ts +9 -8
- package/dist/channels/feishu/scaffold/feishu-send.ts +6 -4
- package/dist/channels/feishu/setup-mode.d.ts +37 -0
- package/dist/channels/feishu/setup-mode.js +28 -0
- package/dist/channels/feishu/ws-ingress.d.ts +28 -0
- package/dist/channels/feishu/ws-ingress.js +136 -0
- package/dist/channels/github/github.js +16 -7
- package/dist/channels/http.d.ts +14 -0
- package/dist/channels/http.js +36 -3
- package/dist/channels/invoke-turn-kit.d.ts +65 -0
- package/dist/channels/invoke-turn-kit.js +87 -0
- package/dist/channels/lark/lark.d.ts +4 -2
- package/dist/channels/lark/lark.js +4 -1
- package/dist/channels/lark/onboard.d.ts +8 -4
- package/dist/channels/lark/onboard.js +8 -0
- package/dist/channels/lark/scaffold/channel.ts +9 -8
- package/dist/channels/lark/scaffold/lark-send.ts +6 -4
- package/dist/channels/preview-kit.d.ts +115 -0
- package/dist/channels/preview-kit.js +184 -0
- package/dist/channels/seen.d.ts +5 -0
- package/dist/channels/seen.js +35 -0
- package/dist/channels/slack/bot-auth.d.ts +15 -0
- package/dist/channels/slack/bot-auth.js +146 -0
- package/dist/channels/slack/config-api.d.ts +60 -0
- package/dist/channels/slack/config-api.js +149 -0
- package/dist/channels/slack/context-buffer.d.ts +24 -0
- package/dist/channels/slack/context-buffer.js +37 -0
- package/dist/channels/slack/invoke-turn.d.ts +19 -0
- package/dist/channels/slack/invoke-turn.js +63 -0
- package/dist/channels/slack/manifest.d.ts +49 -0
- package/dist/channels/slack/manifest.js +69 -0
- package/dist/channels/slack/model.d.ts +67 -0
- package/dist/channels/slack/model.js +2 -0
- package/dist/channels/slack/onboard.d.ts +41 -0
- package/dist/channels/slack/onboard.js +120 -0
- package/dist/channels/slack/onboarding-state.d.ts +31 -0
- package/dist/channels/slack/onboarding-state.js +69 -0
- package/dist/channels/slack/parse.d.ts +38 -0
- package/dist/channels/slack/parse.js +124 -0
- package/dist/channels/slack/preview.d.ts +23 -0
- package/dist/channels/slack/preview.js +403 -0
- package/dist/channels/slack/reaction.d.ts +24 -0
- package/dist/channels/slack/reaction.js +62 -0
- package/dist/channels/slack/register-webhook.d.ts +10 -0
- package/dist/channels/slack/register-webhook.js +49 -0
- package/dist/channels/slack/scaffold/channel.ts +33 -0
- package/dist/channels/slack/scaffold/slack-send.ts +171 -0
- package/dist/channels/slack/setup-server.d.ts +17 -0
- package/dist/channels/slack/setup-server.js +103 -0
- package/dist/channels/slack/slack-api.d.ts +57 -0
- package/dist/channels/slack/slack-api.js +399 -0
- package/dist/channels/slack/slack.d.ts +51 -0
- package/dist/channels/slack/slack.js +496 -0
- package/dist/channels/slack/welcomed.d.ts +5 -0
- package/dist/channels/slack/welcomed.js +32 -0
- package/dist/channels/state.d.ts +11 -4
- package/dist/channels/state.js +22 -15
- package/dist/channels/stop-command.d.ts +6 -0
- package/dist/channels/stop-command.js +36 -0
- package/dist/channels/tasks.d.ts +7 -0
- package/dist/channels/tasks.js +25 -0
- package/dist/channels/telegram/context-buffer.d.ts +8 -17
- package/dist/channels/telegram/context-buffer.js +6 -85
- package/dist/channels/telegram/invoke-turn.d.ts +5 -22
- package/dist/channels/telegram/invoke-turn.js +11 -58
- package/dist/channels/telegram/parse.d.ts +0 -7
- package/dist/channels/telegram/parse.js +4 -2
- package/dist/channels/telegram/preview.d.ts +4 -7
- package/dist/channels/telegram/preview.js +24 -142
- package/dist/channels/telegram/scaffold/telegram-send.ts +6 -3
- package/dist/channels/telegram/telegram.js +24 -10
- package/dist/channels/{feishu/text.d.ts → text.d.ts} +15 -1
- package/dist/channels/{feishu/text.js → text.js} +15 -1
- package/dist/channels/thread-participants.d.ts +21 -0
- package/dist/channels/thread-participants.js +132 -0
- package/dist/channels/turn-queue.js +8 -1
- package/dist/channels/turn-store.d.ts +1 -1
- package/dist/channels/turn-store.js +2 -3
- package/dist/cli/add-feishu.d.ts +30 -0
- package/dist/cli/add-feishu.js +366 -0
- package/dist/cli/add-slack.d.ts +11 -0
- package/dist/cli/add-slack.js +199 -0
- package/dist/{cli-auth.d.ts → cli/auth-view.d.ts} +1 -1
- package/dist/{cli-auth.js → cli/auth-view.js} +1 -1
- package/dist/cli/commands/add.d.ts +4 -0
- package/dist/cli/commands/add.js +191 -60
- package/dist/cli/commands/attach.d.ts +82 -0
- package/dist/cli/commands/attach.js +563 -0
- package/dist/cli/commands/chat.d.ts +1 -0
- package/dist/cli/commands/chat.js +12 -7
- package/dist/cli/commands/deploy.d.ts +3 -1
- package/dist/cli/commands/deploy.js +384 -82
- package/dist/cli/commands/dev.js +34 -26
- package/dist/cli/commands/fire.js +17 -18
- package/dist/cli/commands/info.js +36 -29
- package/dist/cli/commands/init.d.ts +1 -1
- package/dist/cli/commands/init.js +65 -53
- package/dist/cli/commands/invoke.js +11 -9
- package/dist/cli/commands/login.js +48 -45
- package/dist/cli/commands/models.js +1 -1
- package/dist/cli/commands/schedule.js +6 -8
- package/dist/cli/commands/start.js +102 -39
- package/dist/cli/commands/tool.js +23 -18
- package/dist/cli/fail.d.ts +17 -2
- package/dist/cli/fail.js +25 -1
- package/dist/{invoke-stream.d.ts → cli/invoke-stream.d.ts} +1 -1
- package/dist/{invoke-stream.js → cli/invoke-stream.js} +4 -0
- package/dist/cli/kernel.d.ts +3 -2
- package/dist/cli/models-view.d.ts +21 -0
- package/dist/cli/models-view.js +66 -0
- package/dist/cli/program.js +135 -56
- package/dist/cli/serve.d.ts +60 -20
- package/dist/cli/serve.js +252 -47
- package/dist/cli/shared.d.ts +40 -7
- package/dist/cli/shared.js +180 -38
- package/dist/collect.d.ts +14 -3
- package/dist/collect.js +24 -0
- package/dist/core.d.ts +3 -1
- package/dist/core.js +2 -0
- package/dist/deploy/agentcore/plan.d.ts +117 -0
- package/dist/deploy/agentcore/plan.js +721 -0
- package/dist/deploy/agentcore/run.d.ts +73 -0
- package/dist/deploy/agentcore/run.js +412 -0
- package/dist/deploy/agentcore/zip.d.ts +17 -0
- package/dist/deploy/agentcore/zip.js +68 -0
- package/dist/deploy/container.d.ts +26 -25
- package/dist/deploy/container.js +93 -89
- package/dist/deploy/docker/plan.d.ts +3 -3
- package/dist/deploy/docker/plan.js +20 -23
- package/dist/deploy/fly/plan.d.ts +5 -1
- package/dist/deploy/fly/plan.js +39 -26
- package/dist/deploy/fly/run.d.ts +17 -5
- package/dist/deploy/fly/run.js +52 -7
- package/dist/deploy/preflight.d.ts +20 -8
- package/dist/deploy/preflight.js +267 -71
- package/dist/deploy/railway/plan.d.ts +10 -1
- package/dist/deploy/railway/plan.js +53 -20
- package/dist/deploy/railway/run.d.ts +13 -5
- package/dist/deploy/railway/run.js +22 -7
- package/dist/deploy/runner.d.ts +7 -3
- package/dist/deploy/runner.js +9 -3
- package/dist/deploy/secrets.d.ts +2 -1
- package/dist/deploy/secrets.js +23 -3
- package/dist/dev-supervisor.d.ts +11 -10
- package/dist/dev-supervisor.js +56 -50
- package/dist/engines/pi/auth.d.ts +8 -7
- package/dist/engines/pi/auth.js +12 -10
- package/dist/engines/pi/channel.d.ts +22 -16
- package/dist/engines/pi/channel.js +92 -62
- package/dist/engines/pi/chat.d.ts +4 -16
- package/dist/engines/pi/chat.js +8 -261
- package/dist/engines/pi/config.d.ts +20 -55
- package/dist/engines/pi/config.js +49 -121
- package/dist/engines/pi/create.d.ts +30 -10
- package/dist/engines/pi/create.js +33 -26
- package/dist/engines/pi/definition.d.ts +7 -26
- package/dist/engines/pi/definition.js +8 -54
- package/dist/engines/pi/harness.d.ts +49 -0
- package/dist/engines/pi/harness.js +90 -2
- package/dist/engines/pi/invoke.d.ts +55 -2
- package/dist/engines/pi/invoke.js +304 -19
- package/dist/engines/pi/login.d.ts +12 -1
- package/dist/engines/pi/login.js +16 -4
- package/dist/engines/pi/models.d.ts +59 -13
- package/dist/engines/pi/models.js +62 -24
- package/dist/engines/pi/open.d.ts +114 -0
- package/dist/engines/pi/open.js +127 -0
- package/dist/engines/pi/session-builder.d.ts +16 -0
- package/dist/engines/pi/session-builder.js +308 -0
- package/dist/engines/pi/session-control.d.ts +50 -0
- package/dist/engines/pi/session-control.js +604 -0
- package/dist/engines/pi/sessions.d.ts +17 -2
- package/dist/engines/pi/sessions.js +9 -0
- package/dist/engines/pi/tool-context.d.ts +18 -11
- package/dist/engines/pi/tool-context.js +3 -9
- package/dist/engines/pi/tool.d.ts +6 -8
- package/dist/engines/pi/tool.js +10 -1
- package/dist/engines/pi/wake-tool.d.ts +0 -3
- package/dist/engines/pi/wake-tool.js +9 -7
- package/dist/env.d.ts +16 -4
- package/dist/env.js +43 -5
- package/dist/feishu.d.ts +1 -1
- package/dist/feishu.js +1 -1
- package/dist/host/node.d.ts +25 -8
- package/dist/host/node.js +5 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/lark.d.ts +1 -1
- package/dist/lark.js +1 -1
- package/dist/loader.d.ts +2 -2
- package/dist/loader.js +3 -3
- package/dist/log.d.ts +1 -1
- package/dist/log.js +1 -1
- package/dist/observe.js +3 -0
- package/dist/paths.d.ts +138 -0
- package/dist/paths.js +326 -0
- package/dist/pi.d.ts +6 -5
- package/dist/pi.js +4 -3
- package/dist/runtime.d.ts +7 -5
- package/dist/runtime.js +2 -2
- package/dist/scaffold/add-channel.d.ts +26 -9
- package/dist/scaffold/add-channel.js +175 -40
- package/dist/scaffold/init.d.ts +32 -41
- package/dist/scaffold/init.js +161 -185
- package/dist/scaffold/templates/env.example +15 -6
- package/dist/scaffold/templates/fastagent.config.mjs +5 -4
- package/dist/scaffold/templates/gitignore +14 -6
- package/dist/scaffold/templates/persona.md +4 -2
- package/dist/scaffold/templates/secrets.gitignore +5 -0
- package/dist/scaffold/templates.d.ts +1 -7
- package/dist/scaffold/templates.js +3 -25
- package/dist/scaffold/vendor-skill.d.ts +2 -2
- package/dist/scaffold/vendor-skill.js +13 -13
- package/dist/schedule/discover.js +4 -4
- package/dist/schedule/scheduler.d.ts +40 -1
- package/dist/schedule/scheduler.js +89 -56
- package/dist/schedule/state.js +1 -1
- package/dist/schedule/wake-alarm.d.ts +47 -0
- package/dist/schedule/wake-alarm.js +136 -0
- package/dist/schedule/wakeups.d.ts +1 -3
- package/dist/schedule/wakeups.js +19 -1
- package/dist/session-remote.d.ts +53 -0
- package/dist/session-remote.js +336 -0
- package/dist/session.d.ts +265 -0
- package/dist/session.js +37 -0
- package/dist/slack.d.ts +2 -0
- package/dist/slack.js +2 -0
- package/dist/tunnel.d.ts +7 -4
- package/dist/tunnel.js +23 -12
- package/package.json +22 -7
- package/dist/channels/feishu/owned-threads.d.ts +0 -11
- package/dist/channels/feishu/owned-threads.js +0 -47
- package/dist/channels/feishu/seen.d.ts +0 -5
- package/dist/channels/feishu/seen.js +0 -47
- package/dist/cli-add-feishu.d.ts +0 -8
- package/dist/cli-add-feishu.js +0 -223
- package/dist/cli-models.d.ts +0 -11
- package/dist/cli-models.js +0 -20
- package/dist/engines/pi/workspace.d.ts +0 -56
- package/dist/engines/pi/workspace.js +0 -72
- package/dist/scaffold/templates/gitignore.agentdir-root +0 -5
- package/dist/scaffold/templates/gitignore.kit +0 -2
- package/dist/workspace.d.ts +0 -9
- package/dist/workspace.js +0 -45
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { SlackAppManifest } from "./manifest.ts";
|
|
2
|
+
interface SlackErrorShape {
|
|
3
|
+
ok?: boolean;
|
|
4
|
+
error?: string;
|
|
5
|
+
errors?: {
|
|
6
|
+
message?: string;
|
|
7
|
+
pointer?: string;
|
|
8
|
+
}[];
|
|
9
|
+
}
|
|
10
|
+
export declare class SlackConfigApiError extends Error {
|
|
11
|
+
readonly code: string;
|
|
12
|
+
readonly status: number;
|
|
13
|
+
constructor(method: string, status: number, data: SlackErrorShape, fallback: string);
|
|
14
|
+
}
|
|
15
|
+
export interface SlackAppCredentials {
|
|
16
|
+
appId: string;
|
|
17
|
+
clientId: string;
|
|
18
|
+
clientSecret: string;
|
|
19
|
+
signingSecret: string;
|
|
20
|
+
oauthAuthorizeUrl?: string;
|
|
21
|
+
}
|
|
22
|
+
/** Create is intentionally single-attempt: an ambiguous response may already have created the app. */
|
|
23
|
+
export declare function createSlackApp(configToken: string, manifest: SlackAppManifest, options?: {
|
|
24
|
+
apiBaseUrl?: string;
|
|
25
|
+
fetch?: typeof fetch;
|
|
26
|
+
}): Promise<SlackAppCredentials>;
|
|
27
|
+
export declare function updateSlackAppManifest(configToken: string, appId: string, manifest: SlackAppManifest, options?: {
|
|
28
|
+
apiBaseUrl?: string;
|
|
29
|
+
fetch?: typeof fetch;
|
|
30
|
+
}): Promise<void>;
|
|
31
|
+
export interface RotatedSlackConfigToken {
|
|
32
|
+
token: string;
|
|
33
|
+
refreshToken: string;
|
|
34
|
+
expiresAt: number;
|
|
35
|
+
teamId?: string;
|
|
36
|
+
}
|
|
37
|
+
export declare function rotateSlackConfigToken(refreshToken: string, options?: {
|
|
38
|
+
apiBaseUrl?: string;
|
|
39
|
+
fetch?: typeof fetch;
|
|
40
|
+
}): Promise<RotatedSlackConfigToken>;
|
|
41
|
+
export interface SlackOAuthResult {
|
|
42
|
+
botToken: string;
|
|
43
|
+
botRefreshToken: string;
|
|
44
|
+
botTokenExpiresAt: number;
|
|
45
|
+
appId: string;
|
|
46
|
+
teamId: string;
|
|
47
|
+
teamName?: string;
|
|
48
|
+
botUserId?: string;
|
|
49
|
+
scopes: string[];
|
|
50
|
+
}
|
|
51
|
+
export declare function exchangeSlackOAuthCode(input: {
|
|
52
|
+
clientId: string;
|
|
53
|
+
clientSecret: string;
|
|
54
|
+
code: string;
|
|
55
|
+
redirectUrl: string;
|
|
56
|
+
}, options?: {
|
|
57
|
+
apiBaseUrl?: string;
|
|
58
|
+
fetch?: typeof fetch;
|
|
59
|
+
}): Promise<SlackOAuthResult>;
|
|
60
|
+
export {};
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
const SLACK_API = "https://slack.com/api";
|
|
2
|
+
const REQUEST_TIMEOUT_MS = 30_000;
|
|
3
|
+
export class SlackConfigApiError extends Error {
|
|
4
|
+
code;
|
|
5
|
+
status;
|
|
6
|
+
constructor(method, status, data, fallback) {
|
|
7
|
+
const code = data.error ?? fallback;
|
|
8
|
+
const fields = data.errors
|
|
9
|
+
?.map((error) => `${error.pointer ?? "manifest"}: ${error.message ?? "invalid"}`)
|
|
10
|
+
.join("; ");
|
|
11
|
+
super(`Slack ${method} failed: ${code}${fields ? ` — ${fields}` : ""}`);
|
|
12
|
+
this.name = "SlackConfigApiError";
|
|
13
|
+
this.code = code;
|
|
14
|
+
this.status = status;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
async function slackJson(method, body, options = {}) {
|
|
18
|
+
const fetchFn = options.fetch ?? globalThis.fetch;
|
|
19
|
+
let response;
|
|
20
|
+
let raw;
|
|
21
|
+
try {
|
|
22
|
+
response = await fetchFn(`${options.apiBaseUrl ?? SLACK_API}/${method}`, {
|
|
23
|
+
method: "POST",
|
|
24
|
+
headers: {
|
|
25
|
+
"content-type": "application/json; charset=utf-8",
|
|
26
|
+
...(options.token ? { authorization: `Bearer ${options.token}` } : {}),
|
|
27
|
+
},
|
|
28
|
+
body: JSON.stringify(body),
|
|
29
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
30
|
+
});
|
|
31
|
+
raw = await response.text();
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
throw new Error(`Slack ${method} request failed before receiving a response`, { cause: error });
|
|
35
|
+
}
|
|
36
|
+
let data;
|
|
37
|
+
try {
|
|
38
|
+
data = JSON.parse(raw);
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
throw new Error(`Slack ${method} failed: HTTP ${response.status} returned non-JSON`);
|
|
42
|
+
}
|
|
43
|
+
if (!response.ok || data.ok !== true) {
|
|
44
|
+
throw new SlackConfigApiError(method, response.status, data, raw.trim().slice(0, 200) || "unknown_error");
|
|
45
|
+
}
|
|
46
|
+
return data;
|
|
47
|
+
}
|
|
48
|
+
/** Create is intentionally single-attempt: an ambiguous response may already have created the app. */
|
|
49
|
+
export async function createSlackApp(configToken, manifest, options = {}) {
|
|
50
|
+
const data = await slackJson("apps.manifest.create", { manifest: JSON.stringify(manifest) }, { ...options, token: configToken });
|
|
51
|
+
const credentials = data.credentials;
|
|
52
|
+
if (!data.app_id || !credentials?.client_id || !credentials.client_secret || !credentials.signing_secret) {
|
|
53
|
+
throw new Error("Slack apps.manifest.create succeeded but returned incomplete app credentials");
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
appId: data.app_id,
|
|
57
|
+
clientId: credentials.client_id,
|
|
58
|
+
clientSecret: credentials.client_secret,
|
|
59
|
+
signingSecret: credentials.signing_secret,
|
|
60
|
+
oauthAuthorizeUrl: data.oauth_authorize_url,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
export async function updateSlackAppManifest(configToken, appId, manifest, options = {}) {
|
|
64
|
+
await slackJson("apps.manifest.update", { app_id: appId, manifest: JSON.stringify(manifest) }, { ...options, token: configToken });
|
|
65
|
+
}
|
|
66
|
+
export async function rotateSlackConfigToken(refreshToken, options = {}) {
|
|
67
|
+
const data = await slackJson("tooling.tokens.rotate", { refresh_token: refreshToken }, options);
|
|
68
|
+
if (!data.token || !data.refresh_token || typeof data.exp !== "number") {
|
|
69
|
+
throw new Error("Slack tooling.tokens.rotate succeeded but returned incomplete token credentials");
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
token: data.token,
|
|
73
|
+
refreshToken: data.refresh_token,
|
|
74
|
+
expiresAt: data.exp * 1000,
|
|
75
|
+
teamId: data.team_id,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function slackOAuthFailure(status, code) {
|
|
79
|
+
const reason = (() => {
|
|
80
|
+
switch (code) {
|
|
81
|
+
case "bad_client_secret":
|
|
82
|
+
return "Slack rejected the app client secret";
|
|
83
|
+
case "invalid_client_id":
|
|
84
|
+
return "Slack rejected the app client ID";
|
|
85
|
+
case "bad_redirect_uri":
|
|
86
|
+
case "invalid_redirect_uri":
|
|
87
|
+
return "Slack rejected the temporary OAuth redirect URL";
|
|
88
|
+
case "invalid_code":
|
|
89
|
+
return "Slack rejected the authorization code";
|
|
90
|
+
case "code_already_used":
|
|
91
|
+
return "the Slack authorization code was already used";
|
|
92
|
+
case "access_denied":
|
|
93
|
+
return "the Slack installation was not approved";
|
|
94
|
+
default:
|
|
95
|
+
return "Slack rejected the OAuth exchange";
|
|
96
|
+
}
|
|
97
|
+
})();
|
|
98
|
+
return new Error(`${reason} (HTTP ${status}); re-run fastagent add slack to resume installation`);
|
|
99
|
+
}
|
|
100
|
+
export async function exchangeSlackOAuthCode(input, options = {}) {
|
|
101
|
+
const authorization = Buffer.from(`${input.clientId}:${input.clientSecret}`, "utf8").toString("base64");
|
|
102
|
+
const fetchFn = options.fetch ?? globalThis.fetch;
|
|
103
|
+
let response;
|
|
104
|
+
let raw;
|
|
105
|
+
try {
|
|
106
|
+
response = await fetchFn(`${options.apiBaseUrl ?? SLACK_API}/oauth.v2.access`, {
|
|
107
|
+
method: "POST",
|
|
108
|
+
headers: {
|
|
109
|
+
authorization: `Basic ${authorization}`,
|
|
110
|
+
"content-type": "application/x-www-form-urlencoded",
|
|
111
|
+
},
|
|
112
|
+
body: new URLSearchParams({ code: input.code, redirect_uri: input.redirectUrl }).toString(),
|
|
113
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
114
|
+
});
|
|
115
|
+
raw = await response.text();
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
throw new Error("Slack oauth.v2.access request failed before receiving a response", { cause: error });
|
|
119
|
+
}
|
|
120
|
+
let data;
|
|
121
|
+
try {
|
|
122
|
+
data = JSON.parse(raw);
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
throw new Error(`Slack oauth.v2.access failed: HTTP ${response.status} returned non-JSON`);
|
|
126
|
+
}
|
|
127
|
+
if (!response.ok || data.ok !== true)
|
|
128
|
+
throw slackOAuthFailure(response.status, data.error);
|
|
129
|
+
if (!data.access_token ||
|
|
130
|
+
!data.refresh_token ||
|
|
131
|
+
typeof data.expires_in !== "number" ||
|
|
132
|
+
!data.app_id ||
|
|
133
|
+
!data.team?.id) {
|
|
134
|
+
throw new Error("Slack oauth.v2.access succeeded but returned incomplete rotating bot credentials/app identity");
|
|
135
|
+
}
|
|
136
|
+
return {
|
|
137
|
+
botToken: data.access_token,
|
|
138
|
+
botRefreshToken: data.refresh_token,
|
|
139
|
+
botTokenExpiresAt: Date.now() + data.expires_in * 1_000,
|
|
140
|
+
appId: data.app_id,
|
|
141
|
+
teamId: data.team.id,
|
|
142
|
+
teamName: data.team.name,
|
|
143
|
+
botUserId: data.bot_user_id,
|
|
144
|
+
scopes: (data.scope ?? "")
|
|
145
|
+
.split(",")
|
|
146
|
+
.map((scope) => scope.trim())
|
|
147
|
+
.filter(Boolean),
|
|
148
|
+
};
|
|
149
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slack's half of the shared context buffer (mechanics + consume protocol: ../context-buffer.ts):
|
|
3
|
+
* the entry shape, its fold-line rendering, and buffered-file selection. Durable, bounded
|
|
4
|
+
* unsummoned Slack discussion folded into the next answered turn in the same place.
|
|
5
|
+
*/
|
|
6
|
+
import { type ContextBuffer } from "../context-buffer.ts";
|
|
7
|
+
export interface SlackBufferEntry {
|
|
8
|
+
sender: string;
|
|
9
|
+
body: string;
|
|
10
|
+
messageId: string;
|
|
11
|
+
replyTo?: string;
|
|
12
|
+
fileIds?: string[];
|
|
13
|
+
}
|
|
14
|
+
export interface SlackBufferedFileRef {
|
|
15
|
+
id: string;
|
|
16
|
+
from: string;
|
|
17
|
+
messageId: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function collectSlackBufferedFiles(consumed: SlackBufferEntry[], primaryIds: Set<string>): {
|
|
20
|
+
files: SlackBufferedFileRef[];
|
|
21
|
+
skipped: number;
|
|
22
|
+
};
|
|
23
|
+
export type SlackContextBuffer = ContextBuffer<SlackBufferEntry>;
|
|
24
|
+
export declare function createSlackContextBuffer(path: string, label: string): SlackContextBuffer;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slack's half of the shared context buffer (mechanics + consume protocol: ../context-buffer.ts):
|
|
3
|
+
* the entry shape, its fold-line rendering, and buffered-file selection. Durable, bounded
|
|
4
|
+
* unsummoned Slack discussion folded into the next answered turn in the same place.
|
|
5
|
+
*/
|
|
6
|
+
import { BUFFER_ATTACH_MAX, createContextBuffer as createGenericContextBuffer, } from "../context-buffer.js";
|
|
7
|
+
function bufferLine(entry) {
|
|
8
|
+
const meta = [`msg ${entry.messageId}`, entry.replyTo ? `thread root ${entry.replyTo}` : undefined]
|
|
9
|
+
.filter(Boolean)
|
|
10
|
+
.join(", ");
|
|
11
|
+
return `${entry.sender} (${meta}): ${entry.body}`;
|
|
12
|
+
}
|
|
13
|
+
export function collectSlackBufferedFiles(consumed, primaryIds) {
|
|
14
|
+
const seen = new Set();
|
|
15
|
+
const files = [];
|
|
16
|
+
for (const entry of consumed) {
|
|
17
|
+
for (const id of entry.fileIds ?? []) {
|
|
18
|
+
if (primaryIds.has(id) || seen.has(id))
|
|
19
|
+
continue;
|
|
20
|
+
seen.add(id);
|
|
21
|
+
files.push({ id, from: entry.sender, messageId: entry.messageId });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return { files: files.slice(-BUFFER_ATTACH_MAX), skipped: Math.max(0, files.length - BUFFER_ATTACH_MAX) };
|
|
25
|
+
}
|
|
26
|
+
function isEntry(value) {
|
|
27
|
+
const entry = value;
|
|
28
|
+
return (typeof entry?.sender === "string" &&
|
|
29
|
+
typeof entry.body === "string" &&
|
|
30
|
+
typeof entry.messageId === "string" &&
|
|
31
|
+
(entry.replyTo === undefined || typeof entry.replyTo === "string") &&
|
|
32
|
+
(entry.fileIds === undefined ||
|
|
33
|
+
(Array.isArray(entry.fileIds) && entry.fileIds.every((id) => typeof id === "string"))));
|
|
34
|
+
}
|
|
35
|
+
export function createSlackContextBuffer(path, label) {
|
|
36
|
+
return createGenericContextBuffer({ path, label, isEntry, line: bufferLine });
|
|
37
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Resolve Slack file IDs at dequeue, then stream one engine-neutral Agent turn. */
|
|
2
|
+
import type { Agent, AgentEvent } from "../../agent.ts";
|
|
3
|
+
import { type BusyRetry } from "../invoke-turn-kit.ts";
|
|
4
|
+
import type { SlackBufferedFileRef } from "./context-buffer.ts";
|
|
5
|
+
import type { SlackApi } from "./slack-api.ts";
|
|
6
|
+
export interface SlackTurnTransport {
|
|
7
|
+
api: SlackApi;
|
|
8
|
+
channelId: string;
|
|
9
|
+
filesDir: string;
|
|
10
|
+
label: string;
|
|
11
|
+
}
|
|
12
|
+
export interface SlackTurnAttachments {
|
|
13
|
+
primaryFileIds: string[];
|
|
14
|
+
buffered: {
|
|
15
|
+
files: SlackBufferedFileRef[];
|
|
16
|
+
skipped: number;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
export declare function invokeSlackTurn(agent: Agent, session: string, text: string, transport: SlackTurnTransport, attachments: SlackTurnAttachments, onCompleted?: () => void, busyRetry?: BusyRetry): AsyncIterable<AgentEvent>;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { log } from "../../log.js";
|
|
2
|
+
import { DEFAULT_BUSY_RETRY, attachedFilesManifest, attributedFileName, backgroundImagesManifest, missingAttachmentsNote, streamTurnWithBusyRetry, } from "../invoke-turn-kit.js";
|
|
3
|
+
const MARKDOWN_INSTRUCTION = "\n\n(Format your reply as standard Markdown. Slack renders it natively. Do not use HTML or Slack control-mention syntax such as <!here>, <!channel>, or <!everyone>.)";
|
|
4
|
+
async function resolveFile(transport, fileId) {
|
|
5
|
+
const info = await transport.api.fileInfo(fileId);
|
|
6
|
+
return info.mimetype?.toLowerCase().startsWith("image/")
|
|
7
|
+
? { image: await transport.api.fetchImage(info) }
|
|
8
|
+
: { file: await transport.api.fetchFile(info, transport.channelId, transport.filesDir) };
|
|
9
|
+
}
|
|
10
|
+
async function resolveInputs(transport, attachments) {
|
|
11
|
+
const images = [];
|
|
12
|
+
const files = [];
|
|
13
|
+
for (const id of attachments.primaryFileIds) {
|
|
14
|
+
const resolved = await resolveFile(transport, id);
|
|
15
|
+
if (resolved.image)
|
|
16
|
+
images.push(resolved.image);
|
|
17
|
+
if (resolved.file)
|
|
18
|
+
files.push(resolved.file);
|
|
19
|
+
}
|
|
20
|
+
const backgroundImages = [];
|
|
21
|
+
const backgroundFiles = [];
|
|
22
|
+
let lost = 0;
|
|
23
|
+
const results = await Promise.allSettled(attachments.buffered.files.map(async (ref) => ({ ref, resolved: await resolveFile(transport, ref.id) })));
|
|
24
|
+
for (const result of results) {
|
|
25
|
+
if (result.status === "fulfilled") {
|
|
26
|
+
const { ref, resolved } = result.value;
|
|
27
|
+
if (resolved.image)
|
|
28
|
+
backgroundImages.push({ image: resolved.image, ref });
|
|
29
|
+
if (resolved.file)
|
|
30
|
+
backgroundFiles.push({ file: resolved.file, ref });
|
|
31
|
+
}
|
|
32
|
+
else {
|
|
33
|
+
lost++;
|
|
34
|
+
log.warn(`${transport.label} could not load an earlier (buffered) Slack file: ${String(result.reason)}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const missingNote = missingAttachmentsNote(lost + attachments.buffered.skipped);
|
|
38
|
+
const imageManifest = backgroundImagesManifest(images.length, backgroundImages.map(({ ref }) => ref));
|
|
39
|
+
const allFiles = [
|
|
40
|
+
...files,
|
|
41
|
+
...backgroundFiles.map(({ file, ref }) => ({
|
|
42
|
+
...file,
|
|
43
|
+
name: attributedFileName(file.name, ref.from, ref.messageId),
|
|
44
|
+
})),
|
|
45
|
+
];
|
|
46
|
+
const allImages = [...images, ...backgroundImages.map(({ image }) => image)];
|
|
47
|
+
return {
|
|
48
|
+
images: allImages.length ? allImages : undefined,
|
|
49
|
+
promptSuffix: `${missingNote}${imageManifest}${attachedFilesManifest(allFiles)}`,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
export async function* invokeSlackTurn(agent, session, text, transport, attachments, onCompleted, busyRetry = DEFAULT_BUSY_RETRY) {
|
|
53
|
+
let resolved;
|
|
54
|
+
try {
|
|
55
|
+
resolved = await resolveInputs(transport, attachments);
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
yield { type: "failed", details: `could not load Slack attachment: ${String(error)}`, retryable: true };
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const prompt = { text: `${text}${resolved.promptSuffix}${MARKDOWN_INSTRUCTION}`, images: resolved.images };
|
|
62
|
+
yield* streamTurnWithBusyRetry(agent, session, prompt, { label: transport.label, onCompleted, busyRetry });
|
|
63
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
export type SlackGroupBehavior = "context" | "mentions";
|
|
2
|
+
export interface SlackAppManifest {
|
|
3
|
+
display_information: {
|
|
4
|
+
name: string;
|
|
5
|
+
description: string;
|
|
6
|
+
background_color: string;
|
|
7
|
+
};
|
|
8
|
+
features: {
|
|
9
|
+
app_home: {
|
|
10
|
+
home_tab_enabled: boolean;
|
|
11
|
+
messages_tab_enabled: boolean;
|
|
12
|
+
messages_tab_read_only_enabled: boolean;
|
|
13
|
+
};
|
|
14
|
+
agent_view: {
|
|
15
|
+
agent_description: string;
|
|
16
|
+
suggested_prompts: Array<{
|
|
17
|
+
title: string;
|
|
18
|
+
message: string;
|
|
19
|
+
}>;
|
|
20
|
+
};
|
|
21
|
+
bot_user: {
|
|
22
|
+
display_name: string;
|
|
23
|
+
always_online: boolean;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
oauth_config: {
|
|
27
|
+
scopes: {
|
|
28
|
+
bot: string[];
|
|
29
|
+
};
|
|
30
|
+
redirect_urls?: string[];
|
|
31
|
+
};
|
|
32
|
+
settings: {
|
|
33
|
+
event_subscriptions?: {
|
|
34
|
+
request_url: string;
|
|
35
|
+
bot_events: string[];
|
|
36
|
+
};
|
|
37
|
+
org_deploy_enabled: boolean;
|
|
38
|
+
socket_mode_enabled: boolean;
|
|
39
|
+
token_rotation_enabled: boolean;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export declare function slackBotScopes(groupBehavior: SlackGroupBehavior): string[];
|
|
43
|
+
export declare function slackBotEvents(groupBehavior: SlackGroupBehavior): string[];
|
|
44
|
+
export declare function buildSlackManifest(input: {
|
|
45
|
+
name: string;
|
|
46
|
+
groupBehavior: SlackGroupBehavior;
|
|
47
|
+
requestUrl?: string;
|
|
48
|
+
redirectUrl?: string;
|
|
49
|
+
}): SlackAppManifest;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
const SLACK_BASE_BOT_SCOPES = [
|
|
2
|
+
"app_mentions:read",
|
|
3
|
+
"assistant:write",
|
|
4
|
+
"chat:write",
|
|
5
|
+
"files:read",
|
|
6
|
+
"files:write",
|
|
7
|
+
"im:history",
|
|
8
|
+
"reactions:write",
|
|
9
|
+
];
|
|
10
|
+
const SLACK_CONTEXT_BOT_SCOPES = ["channels:history", "groups:history", "mpim:history"];
|
|
11
|
+
const SLACK_BASE_BOT_EVENTS = ["app_context_changed", "app_home_opened", "app_mention", "message.im"];
|
|
12
|
+
const SLACK_CONTEXT_BOT_EVENTS = ["message.channels", "message.groups", "message.mpim"];
|
|
13
|
+
export function slackBotScopes(groupBehavior) {
|
|
14
|
+
return [...SLACK_BASE_BOT_SCOPES, ...(groupBehavior === "context" ? SLACK_CONTEXT_BOT_SCOPES : [])].sort();
|
|
15
|
+
}
|
|
16
|
+
export function slackBotEvents(groupBehavior) {
|
|
17
|
+
return [...SLACK_BASE_BOT_EVENTS, ...(groupBehavior === "context" ? SLACK_CONTEXT_BOT_EVENTS : [])].sort();
|
|
18
|
+
}
|
|
19
|
+
/** Slack's bot display name is more restrictive than the app name. */
|
|
20
|
+
function slackBotDisplayName(name) {
|
|
21
|
+
const normalized = name
|
|
22
|
+
.toLowerCase()
|
|
23
|
+
.replace(/[^a-z0-9._-]+/g, "-")
|
|
24
|
+
.replace(/^[^a-z0-9]+|[^a-z0-9]+$/g, "")
|
|
25
|
+
.slice(0, 80);
|
|
26
|
+
return normalized || "fastagent";
|
|
27
|
+
}
|
|
28
|
+
export function buildSlackManifest(input) {
|
|
29
|
+
const name = input.name.trim().slice(0, 35) || "FastAgent";
|
|
30
|
+
return {
|
|
31
|
+
display_information: {
|
|
32
|
+
name,
|
|
33
|
+
description: "A FastAgent-powered internal workspace agent.",
|
|
34
|
+
background_color: "#111827",
|
|
35
|
+
},
|
|
36
|
+
features: {
|
|
37
|
+
app_home: {
|
|
38
|
+
home_tab_enabled: false,
|
|
39
|
+
messages_tab_enabled: true,
|
|
40
|
+
messages_tab_read_only_enabled: false,
|
|
41
|
+
},
|
|
42
|
+
agent_view: {
|
|
43
|
+
agent_description: "A workspace agent powered by the files, skills, and tools in its FastAgent definition.",
|
|
44
|
+
suggested_prompts: [
|
|
45
|
+
{ title: "What can you do?", message: "What can you help me with?" },
|
|
46
|
+
{ title: "Summarize context", message: "Summarize the relevant context and propose next steps." },
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
bot_user: { display_name: slackBotDisplayName(name), always_online: false },
|
|
50
|
+
},
|
|
51
|
+
oauth_config: {
|
|
52
|
+
scopes: { bot: slackBotScopes(input.groupBehavior) },
|
|
53
|
+
...(input.redirectUrl ? { redirect_urls: [input.redirectUrl] } : {}),
|
|
54
|
+
},
|
|
55
|
+
settings: {
|
|
56
|
+
...(input.requestUrl
|
|
57
|
+
? {
|
|
58
|
+
event_subscriptions: {
|
|
59
|
+
request_url: input.requestUrl,
|
|
60
|
+
bot_events: slackBotEvents(input.groupBehavior),
|
|
61
|
+
},
|
|
62
|
+
}
|
|
63
|
+
: {}),
|
|
64
|
+
org_deploy_enabled: false,
|
|
65
|
+
socket_mode_enabled: false,
|
|
66
|
+
token_rotation_enabled: true,
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/** Minimal Slack Events/Web API shapes used by the first-party channel. Unknown fields remain ignored. */
|
|
2
|
+
export interface SlackFile {
|
|
3
|
+
id?: string;
|
|
4
|
+
name?: string;
|
|
5
|
+
title?: string;
|
|
6
|
+
mimetype?: string;
|
|
7
|
+
filetype?: string;
|
|
8
|
+
pretty_type?: string;
|
|
9
|
+
size?: number;
|
|
10
|
+
mode?: string;
|
|
11
|
+
is_external?: boolean;
|
|
12
|
+
external_type?: string;
|
|
13
|
+
file_access?: string;
|
|
14
|
+
url_private?: string;
|
|
15
|
+
url_private_download?: string;
|
|
16
|
+
}
|
|
17
|
+
interface SlackAppContextEntity {
|
|
18
|
+
type?: string;
|
|
19
|
+
value?: string;
|
|
20
|
+
team_id?: string;
|
|
21
|
+
}
|
|
22
|
+
export interface SlackMessageEvent {
|
|
23
|
+
type?: string;
|
|
24
|
+
subtype?: string;
|
|
25
|
+
hidden?: boolean;
|
|
26
|
+
user?: string;
|
|
27
|
+
bot_id?: string;
|
|
28
|
+
text?: string;
|
|
29
|
+
ts?: string;
|
|
30
|
+
event_ts?: string;
|
|
31
|
+
thread_ts?: string;
|
|
32
|
+
channel?: string;
|
|
33
|
+
channel_type?: string;
|
|
34
|
+
/** `app_home_opened` only: which App Home tab was opened (`messages` signals a DM open). */
|
|
35
|
+
tab?: string;
|
|
36
|
+
team?: string;
|
|
37
|
+
files?: SlackFile[];
|
|
38
|
+
/** Included on message.im when the Agent messaging experience is enabled. */
|
|
39
|
+
app_context?: {
|
|
40
|
+
entities?: SlackAppContextEntity[];
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export interface SlackEventEnvelope {
|
|
44
|
+
type?: string;
|
|
45
|
+
challenge?: string;
|
|
46
|
+
team_id?: string;
|
|
47
|
+
context_team_id?: string;
|
|
48
|
+
enterprise_id?: string;
|
|
49
|
+
api_app_id?: string;
|
|
50
|
+
event_id?: string;
|
|
51
|
+
event_time?: number;
|
|
52
|
+
event?: SlackMessageEvent;
|
|
53
|
+
authorizations?: Array<{
|
|
54
|
+
team_id?: string;
|
|
55
|
+
enterprise_id?: string;
|
|
56
|
+
user_id?: string;
|
|
57
|
+
is_bot?: boolean;
|
|
58
|
+
}>;
|
|
59
|
+
}
|
|
60
|
+
/** A routed Slack turn. `threadTs:null` explicitly sends at channel top level. */
|
|
61
|
+
export interface SlackRoute {
|
|
62
|
+
session?: string;
|
|
63
|
+
channelId?: string;
|
|
64
|
+
threadTs?: string | null;
|
|
65
|
+
text?: string;
|
|
66
|
+
}
|
|
67
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { createSlackApp, exchangeSlackOAuthCode, updateSlackAppManifest } from "./config-api.ts";
|
|
2
|
+
import { type SlackGroupBehavior } from "./manifest.ts";
|
|
3
|
+
import { type SlackOnboardingState } from "./onboarding-state.ts";
|
|
4
|
+
export interface SlackOnboardIO {
|
|
5
|
+
note(message: string): void;
|
|
6
|
+
openUrl(url: string): void;
|
|
7
|
+
/** Wait for the one OAuth redirect. Implementations must validate only the path; core validates state. */
|
|
8
|
+
waitForOAuth(): Promise<{
|
|
9
|
+
code?: string;
|
|
10
|
+
state?: string;
|
|
11
|
+
error?: string;
|
|
12
|
+
}>;
|
|
13
|
+
/** Stage runtime-only credentials into the gitignored .env. */
|
|
14
|
+
writeRuntimeSecrets(values: {
|
|
15
|
+
botToken?: string;
|
|
16
|
+
botRefreshToken?: string;
|
|
17
|
+
botTokenExpiresAt?: number;
|
|
18
|
+
clientId?: string;
|
|
19
|
+
clientSecret?: string;
|
|
20
|
+
signingSecret?: string;
|
|
21
|
+
}): Promise<void>;
|
|
22
|
+
}
|
|
23
|
+
export interface SlackOnboardInput {
|
|
24
|
+
stateRoot: string;
|
|
25
|
+
state: SlackOnboardingState;
|
|
26
|
+
requestUrl: string;
|
|
27
|
+
redirectUrl: string;
|
|
28
|
+
}
|
|
29
|
+
/** Create/resume one internal Slack app and complete its workspace OAuth installation. */
|
|
30
|
+
export declare function onboardSlackApp(input: SlackOnboardInput, io: SlackOnboardIO, deps?: {
|
|
31
|
+
createApp?: typeof createSlackApp;
|
|
32
|
+
updateManifest?: typeof updateSlackAppManifest;
|
|
33
|
+
exchangeCode?: typeof exchangeSlackOAuthCode;
|
|
34
|
+
}): Promise<SlackOnboardingState>;
|
|
35
|
+
export declare function newSlackOnboardingState(input: {
|
|
36
|
+
appName: string;
|
|
37
|
+
groupBehavior: SlackGroupBehavior;
|
|
38
|
+
configToken: string;
|
|
39
|
+
configRefreshToken: string;
|
|
40
|
+
now?: number;
|
|
41
|
+
}): SlackOnboardingState;
|