@shipfox/api-integration-slack 4.0.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/.turbo/turbo-build.log +2 -0
- package/.turbo/turbo-type$colon$emit.log +1 -0
- package/.turbo/turbo-type.log +1 -0
- package/CHANGELOG.md +22 -0
- package/LICENSE +21 -0
- package/dist/api/client.d.ts +28 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/client.js +163 -0
- package/dist/api/client.js.map +1 -0
- package/dist/config.d.ts +8 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +21 -0
- package/dist/config.js.map +1 -0
- package/dist/core/agent-tools-provider.d.ts +75 -0
- package/dist/core/agent-tools-provider.d.ts.map +1 -0
- package/dist/core/agent-tools-provider.js +111 -0
- package/dist/core/agent-tools-provider.js.map +1 -0
- package/dist/core/agent-tools.d.ts +65 -0
- package/dist/core/agent-tools.d.ts.map +1 -0
- package/dist/core/agent-tools.js +177 -0
- package/dist/core/agent-tools.js.map +1 -0
- package/dist/core/disconnect.d.ts +18 -0
- package/dist/core/disconnect.d.ts.map +1 -0
- package/dist/core/disconnect.js +23 -0
- package/dist/core/disconnect.js.map +1 -0
- package/dist/core/errors.d.ts +44 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +71 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/install.d.ts +48 -0
- package/dist/core/install.d.ts.map +1 -0
- package/dist/core/install.js +88 -0
- package/dist/core/install.js.map +1 -0
- package/dist/core/scopes.d.ts +4 -0
- package/dist/core/scopes.d.ts.map +1 -0
- package/dist/core/scopes.js +24 -0
- package/dist/core/scopes.js.map +1 -0
- package/dist/core/signature.d.ts +10 -0
- package/dist/core/signature.d.ts.map +1 -0
- package/dist/core/signature.js +18 -0
- package/dist/core/signature.js.map +1 -0
- package/dist/core/state.d.ts +12 -0
- package/dist/core/state.d.ts.map +1 -0
- package/dist/core/state.js +54 -0
- package/dist/core/state.js.map +1 -0
- package/dist/core/tokens.d.ts +35 -0
- package/dist/core/tokens.d.ts.map +1 -0
- package/dist/core/tokens.js +51 -0
- package/dist/core/tokens.js.map +1 -0
- package/dist/core/webhook.d.ts +25 -0
- package/dist/core/webhook.d.ts.map +1 -0
- package/dist/core/webhook.js +160 -0
- package/dist/core/webhook.js.map +1 -0
- package/dist/db/db.d.ts +403 -0
- package/dist/db/db.d.ts.map +1 -0
- package/dist/db/db.js +18 -0
- package/dist/db/db.js.map +1 -0
- package/dist/db/installations.d.ts +40 -0
- package/dist/db/installations.d.ts.map +1 -0
- package/dist/db/installations.js +73 -0
- package/dist/db/installations.js.map +1 -0
- package/dist/db/migrations.d.ts +2 -0
- package/dist/db/migrations.d.ts.map +1 -0
- package/dist/db/migrations.js +5 -0
- package/dist/db/migrations.js.map +1 -0
- package/dist/db/schema/common.d.ts +2 -0
- package/dist/db/schema/common.d.ts.map +1 -0
- package/dist/db/schema/common.js +4 -0
- package/dist/db/schema/common.js.map +1 -0
- package/dist/db/schema/installations.d.ts +203 -0
- package/dist/db/schema/installations.d.ts.map +1 -0
- package/dist/db/schema/installations.js +42 -0
- package/dist/db/schema/installations.js.map +1 -0
- package/dist/index.d.ts +59 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +73 -0
- package/dist/index.js.map +1 -0
- package/dist/presentation/dto/integrations.d.ts +2 -0
- package/dist/presentation/dto/integrations.d.ts.map +1 -0
- package/dist/presentation/dto/integrations.js +3 -0
- package/dist/presentation/dto/integrations.js.map +1 -0
- package/dist/presentation/e2eRoutes/create-connection.d.ts +16 -0
- package/dist/presentation/e2eRoutes/create-connection.d.ts.map +1 -0
- package/dist/presentation/e2eRoutes/create-connection.js +65 -0
- package/dist/presentation/e2eRoutes/create-connection.js.map +1 -0
- package/dist/presentation/e2eRoutes/index.d.ts +5 -0
- package/dist/presentation/e2eRoutes/index.d.ts.map +1 -0
- package/dist/presentation/e2eRoutes/index.js +11 -0
- package/dist/presentation/e2eRoutes/index.js.map +1 -0
- package/dist/presentation/routes/errors.d.ts +2 -0
- package/dist/presentation/routes/errors.d.ts.map +1 -0
- package/dist/presentation/routes/errors.js +75 -0
- package/dist/presentation/routes/errors.js.map +1 -0
- package/dist/presentation/routes/install.d.ts +19 -0
- package/dist/presentation/routes/install.d.ts.map +1 -0
- package/dist/presentation/routes/install.js +98 -0
- package/dist/presentation/routes/install.js.map +1 -0
- package/dist/presentation/routes/webhooks.d.ts +16 -0
- package/dist/presentation/routes/webhooks.d.ts.map +1 -0
- package/dist/presentation/routes/webhooks.js +166 -0
- package/dist/presentation/routes/webhooks.js.map +1 -0
- package/dist/tsconfig.test.tsbuildinfo +1 -0
- package/drizzle/0000_calm_fantastic_four.sql +16 -0
- package/drizzle/meta/0000_snapshot.json +132 -0
- package/drizzle/meta/_journal.json +13 -0
- package/drizzle.config.ts +7 -0
- package/package.json +71 -0
- package/src/api/client.test.ts +239 -0
- package/src/api/client.ts +243 -0
- package/src/config.test.ts +32 -0
- package/src/config.ts +20 -0
- package/src/connection-external-url.test.ts +25 -0
- package/src/core/agent-tools-provider.test.ts +256 -0
- package/src/core/agent-tools-provider.ts +147 -0
- package/src/core/agent-tools.test.ts +90 -0
- package/src/core/agent-tools.ts +189 -0
- package/src/core/disconnect.ts +26 -0
- package/src/core/errors.ts +92 -0
- package/src/core/install.test.ts +155 -0
- package/src/core/install.ts +137 -0
- package/src/core/scopes.test.ts +22 -0
- package/src/core/scopes.ts +25 -0
- package/src/core/signature.test.ts +45 -0
- package/src/core/signature.ts +41 -0
- package/src/core/state.test.ts +48 -0
- package/src/core/state.ts +82 -0
- package/src/core/tokens.test.ts +163 -0
- package/src/core/tokens.ts +98 -0
- package/src/core/webhook.test.ts +269 -0
- package/src/core/webhook.ts +223 -0
- package/src/db/db.ts +16 -0
- package/src/db/installations.test.ts +93 -0
- package/src/db/installations.ts +141 -0
- package/src/db/migrations.ts +4 -0
- package/src/db/schema/common.ts +3 -0
- package/src/db/schema/installations.ts +44 -0
- package/src/index.test.ts +25 -0
- package/src/index.ts +196 -0
- package/src/presentation/dto/integrations.ts +1 -0
- package/src/presentation/e2eRoutes/create-connection.ts +82 -0
- package/src/presentation/e2eRoutes/index.test.ts +96 -0
- package/src/presentation/e2eRoutes/index.ts +11 -0
- package/src/presentation/routes/errors.ts +68 -0
- package/src/presentation/routes/install.test.ts +229 -0
- package/src/presentation/routes/install.ts +117 -0
- package/src/presentation/routes/webhooks.test.ts +266 -0
- package/src/presentation/routes/webhooks.ts +170 -0
- package/test/api-secrets.d.ts +26 -0
- package/test/env.ts +13 -0
- package/test/factories/index.ts +1 -0
- package/test/globalSetup.ts +23 -0
- package/test/index.ts +1 -0
- package/test/setup.ts +17 -0
- package/tsconfig.build.json +9 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/tsconfig.json +3 -0
- package/tsconfig.test.json +9 -0
- package/vitest.config.ts +12 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
$ shipfox-tsc-emit
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
$ shipfox-tsc-check
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# @shipfox/api-integration-slack
|
|
2
|
+
|
|
3
|
+
## 4.0.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 67176d4: Adds the Slack OAuth connection flow with provider routes, secure bot-token storage, and E2E setup.
|
|
8
|
+
- 7267872: Adds signed Slack Events API and slash-command receivers that publish normalized integration events without persisting command verification tokens.
|
|
9
|
+
- bbba3b7: Adds the Slack integration provider scaffold with installation storage, bot-token custody, and flag-gated registration.
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 0745ee9: Prevents Slack bot tokens from being read for revoked, expired, or missing installations.
|
|
14
|
+
- 23c8e4d: Rejects Slack OAuth grants that enable unsupported token rotation.
|
|
15
|
+
- 1951293: Adds in-process Slack agent tools for reading conversations and acting on messages through the lease-authenticated gateway.
|
|
16
|
+
- Updated dependencies [dda7c54]
|
|
17
|
+
- Updated dependencies [7267872]
|
|
18
|
+
- Updated dependencies [bbba3b7]
|
|
19
|
+
- @shipfox/api-integration-slack-dto@4.0.0
|
|
20
|
+
- @shipfox/node-drizzle@0.3.0
|
|
21
|
+
- @shipfox/api-secrets@4.0.0
|
|
22
|
+
- @shipfox/api-workspaces@4.0.0
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Shipfox
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface SlackAuthorization {
|
|
2
|
+
accessToken: string;
|
|
3
|
+
botUserId: string;
|
|
4
|
+
appId: string;
|
|
5
|
+
teamId: string;
|
|
6
|
+
teamName: string;
|
|
7
|
+
scopes: string[];
|
|
8
|
+
}
|
|
9
|
+
export interface SlackWebApiResponse {
|
|
10
|
+
ok: boolean;
|
|
11
|
+
error?: string | undefined;
|
|
12
|
+
[key: string]: unknown;
|
|
13
|
+
}
|
|
14
|
+
export interface SlackApiClient {
|
|
15
|
+
exchangeAuthorizationCode(input: {
|
|
16
|
+
code: string;
|
|
17
|
+
}): Promise<SlackAuthorization>;
|
|
18
|
+
revokeToken(input: {
|
|
19
|
+
token: string;
|
|
20
|
+
}): Promise<void>;
|
|
21
|
+
callMethod(input: {
|
|
22
|
+
method: string;
|
|
23
|
+
token: string;
|
|
24
|
+
arguments: Record<string, unknown>;
|
|
25
|
+
}): Promise<SlackWebApiResponse>;
|
|
26
|
+
}
|
|
27
|
+
export declare function createSlackApiClient(): SlackApiClient;
|
|
28
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/api/client.ts"],"names":[],"mappings":"AAWA,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,yBAAyB,CAAC,KAAK,EAAE;QAAC,IAAI,EAAE,MAAM,CAAA;KAAC,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAC9E,WAAW,CAAC,KAAK,EAAE;QAAC,KAAK,EAAE,MAAM,CAAA;KAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,UAAU,CAAC,KAAK,EAAE;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACpC,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;CAClC;AAeD,wBAAgB,oBAAoB,IAAI,cAAc,CAwCrD"}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
import { logger } from '@shipfox/node-opentelemetry';
|
|
2
|
+
import ky, { HTTPError, TimeoutError } from 'ky';
|
|
3
|
+
import { config } from '#config.js';
|
|
4
|
+
import { SlackEnterpriseInstallUnsupportedError, SlackIntegrationProviderError, SlackTokenRotationUnsupportedError } from '#core/errors.js';
|
|
5
|
+
const SLACK_API_TIMEOUT_MS = 10_000;
|
|
6
|
+
export function createSlackApiClient() {
|
|
7
|
+
return {
|
|
8
|
+
async exchangeAuthorizationCode (input) {
|
|
9
|
+
const body = await mapSlackError('exchange-authorization-code', ()=>ky.post(slackApiUrl('oauth.v2.access'), {
|
|
10
|
+
body: new URLSearchParams({
|
|
11
|
+
client_id: config.SLACK_OAUTH_CLIENT_ID,
|
|
12
|
+
client_secret: config.SLACK_OAUTH_CLIENT_SECRET,
|
|
13
|
+
code: input.code,
|
|
14
|
+
redirect_uri: config.SLACK_OAUTH_REDIRECT_URL
|
|
15
|
+
}),
|
|
16
|
+
timeout: SLACK_API_TIMEOUT_MS
|
|
17
|
+
}).json());
|
|
18
|
+
return parseOAuthAccess(body);
|
|
19
|
+
},
|
|
20
|
+
async revokeToken (input) {
|
|
21
|
+
await mapSlackError('revoke-token', async ()=>{
|
|
22
|
+
await ky.post(slackApiUrl('auth.revoke'), {
|
|
23
|
+
headers: {
|
|
24
|
+
authorization: `Bearer ${input.token}`
|
|
25
|
+
},
|
|
26
|
+
timeout: SLACK_API_TIMEOUT_MS
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
},
|
|
30
|
+
async callMethod (input) {
|
|
31
|
+
return await mapSlackWebApiError(input.method, ()=>ky.post(slackApiUrl(input.method), {
|
|
32
|
+
headers: {
|
|
33
|
+
authorization: `Bearer ${input.token}`
|
|
34
|
+
},
|
|
35
|
+
body: slackMethodArguments(input.arguments),
|
|
36
|
+
timeout: SLACK_API_TIMEOUT_MS
|
|
37
|
+
}).json());
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function parseOAuthAccess(body) {
|
|
42
|
+
if (body.ok !== true) {
|
|
43
|
+
throw new SlackIntegrationProviderError(slackOAuthErrorReason(body.error), 'Slack authorization request failed');
|
|
44
|
+
}
|
|
45
|
+
if (body.is_enterprise_install === true || body.team == null) {
|
|
46
|
+
throw new SlackEnterpriseInstallUnsupportedError();
|
|
47
|
+
}
|
|
48
|
+
// The bot-token store has no refresh path, so a rotating (expiring) token must never be persisted.
|
|
49
|
+
// Slack omits both fields when rotation is off, so presence of either signals rotation.
|
|
50
|
+
// The rejected token self-expires, matching the Enterprise Grid rejection path.
|
|
51
|
+
if (body.expires_in !== undefined || body.refresh_token !== undefined) {
|
|
52
|
+
throw new SlackTokenRotationUnsupportedError();
|
|
53
|
+
}
|
|
54
|
+
const { access_token: accessToken, bot_user_id: botUserId, app_id: appId, team, scope } = body;
|
|
55
|
+
if (typeof accessToken !== 'string' || typeof botUserId !== 'string' || typeof appId !== 'string' || typeof team.id !== 'string' || typeof team.name !== 'string' || typeof scope !== 'string') {
|
|
56
|
+
throw new SlackIntegrationProviderError('malformed-provider-response', 'Slack authorization response did not include the required installation details');
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
accessToken,
|
|
60
|
+
botUserId,
|
|
61
|
+
appId,
|
|
62
|
+
teamId: team.id,
|
|
63
|
+
teamName: team.name,
|
|
64
|
+
scopes: scope.split(',').map((value)=>value.trim()).filter(Boolean)
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
function slackOAuthErrorReason(error) {
|
|
68
|
+
if (error === 'service_unavailable' || error === 'internal_error') return 'provider-unavailable';
|
|
69
|
+
if (error === 'ratelimited') return 'rate-limited';
|
|
70
|
+
return 'access-denied';
|
|
71
|
+
}
|
|
72
|
+
async function mapSlackError(operation, request) {
|
|
73
|
+
try {
|
|
74
|
+
return await request();
|
|
75
|
+
} catch (error) {
|
|
76
|
+
if (error instanceof SlackIntegrationProviderError) throw error;
|
|
77
|
+
if (error instanceof HTTPError) {
|
|
78
|
+
const { status, statusText, headers } = error.response;
|
|
79
|
+
logger().warn({
|
|
80
|
+
operation,
|
|
81
|
+
status,
|
|
82
|
+
statusText
|
|
83
|
+
}, 'Slack API request rejected');
|
|
84
|
+
if (status === 429) {
|
|
85
|
+
throw new SlackIntegrationProviderError('rate-limited', 'Slack request was rate limited', retryAfterSeconds(headers));
|
|
86
|
+
}
|
|
87
|
+
if (status >= 500) {
|
|
88
|
+
throw new SlackIntegrationProviderError('provider-unavailable', 'Slack request failed');
|
|
89
|
+
}
|
|
90
|
+
throw new SlackIntegrationProviderError('access-denied', 'Slack request was rejected');
|
|
91
|
+
}
|
|
92
|
+
if (error instanceof TimeoutError) {
|
|
93
|
+
logger().warn({
|
|
94
|
+
operation
|
|
95
|
+
}, 'Slack API request timed out');
|
|
96
|
+
throw new SlackIntegrationProviderError('timeout', 'Slack request timed out');
|
|
97
|
+
}
|
|
98
|
+
logger().warn({
|
|
99
|
+
operation,
|
|
100
|
+
errName: error instanceof Error ? error.name : typeof error
|
|
101
|
+
}, 'Slack API request failed');
|
|
102
|
+
throw new SlackIntegrationProviderError('provider-unavailable', 'Slack request failed');
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async function mapSlackWebApiError(method, request) {
|
|
106
|
+
try {
|
|
107
|
+
return await request();
|
|
108
|
+
} catch (error) {
|
|
109
|
+
if (error instanceof SlackIntegrationProviderError) throw error;
|
|
110
|
+
if (error instanceof HTTPError) {
|
|
111
|
+
const { status, statusText, headers } = error.response;
|
|
112
|
+
logger().warn({
|
|
113
|
+
operation: 'call-method',
|
|
114
|
+
method,
|
|
115
|
+
status,
|
|
116
|
+
statusText
|
|
117
|
+
}, 'Slack API request rejected');
|
|
118
|
+
if (status === 429) {
|
|
119
|
+
throw new SlackIntegrationProviderError('rate-limited', 'Slack request was rate limited', retryAfterSeconds(headers));
|
|
120
|
+
}
|
|
121
|
+
if (status === 413) {
|
|
122
|
+
throw new SlackIntegrationProviderError('content-too-large', 'Slack request content was too large');
|
|
123
|
+
}
|
|
124
|
+
if (status >= 500) {
|
|
125
|
+
throw new SlackIntegrationProviderError('provider-unavailable', 'Slack request failed');
|
|
126
|
+
}
|
|
127
|
+
throw new SlackIntegrationProviderError('malformed-provider-response', 'Slack request was rejected');
|
|
128
|
+
}
|
|
129
|
+
if (error instanceof TimeoutError) {
|
|
130
|
+
logger().warn({
|
|
131
|
+
operation: 'call-method',
|
|
132
|
+
method
|
|
133
|
+
}, 'Slack API request timed out');
|
|
134
|
+
throw new SlackIntegrationProviderError('timeout', 'Slack request timed out');
|
|
135
|
+
}
|
|
136
|
+
logger().warn({
|
|
137
|
+
operation: 'call-method',
|
|
138
|
+
method,
|
|
139
|
+
errName: error instanceof Error ? error.name : typeof error
|
|
140
|
+
}, 'Slack API request failed');
|
|
141
|
+
throw new SlackIntegrationProviderError('provider-unavailable', 'Slack request failed');
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function slackMethodArguments(input) {
|
|
145
|
+
const body = new URLSearchParams();
|
|
146
|
+
for (const [key, value] of Object.entries(input)){
|
|
147
|
+
if (value === null || value === undefined) continue;
|
|
148
|
+
const serialized = typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean' ? String(value) : JSON.stringify(value);
|
|
149
|
+
if (serialized !== undefined) body.set(key, serialized);
|
|
150
|
+
}
|
|
151
|
+
return body;
|
|
152
|
+
}
|
|
153
|
+
function slackApiUrl(path) {
|
|
154
|
+
return new URL(path, `${config.SLACK_API_BASE_URL}/`).toString();
|
|
155
|
+
}
|
|
156
|
+
function retryAfterSeconds(headers) {
|
|
157
|
+
const retryAfter = headers.get('retry-after');
|
|
158
|
+
if (!retryAfter) return undefined;
|
|
159
|
+
const parsed = Number.parseInt(retryAfter, 10);
|
|
160
|
+
return Number.isNaN(parsed) ? undefined : parsed;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/api/client.ts"],"sourcesContent":["import {logger} from '@shipfox/node-opentelemetry';\nimport ky, {HTTPError, TimeoutError} from 'ky';\nimport {config} from '#config.js';\nimport {\n SlackEnterpriseInstallUnsupportedError,\n SlackIntegrationProviderError,\n SlackTokenRotationUnsupportedError,\n} from '#core/errors.js';\n\nconst SLACK_API_TIMEOUT_MS = 10_000;\n\nexport interface SlackAuthorization {\n accessToken: string;\n botUserId: string;\n appId: string;\n teamId: string;\n teamName: string;\n scopes: string[];\n}\n\nexport interface SlackWebApiResponse {\n ok: boolean;\n error?: string | undefined;\n [key: string]: unknown;\n}\n\nexport interface SlackApiClient {\n exchangeAuthorizationCode(input: {code: string}): Promise<SlackAuthorization>;\n revokeToken(input: {token: string}): Promise<void>;\n callMethod(input: {\n method: string;\n token: string;\n arguments: Record<string, unknown>;\n }): Promise<SlackWebApiResponse>;\n}\n\ninterface SlackOAuthAccessResponse {\n ok?: unknown;\n error?: unknown;\n access_token?: unknown;\n bot_user_id?: unknown;\n app_id?: unknown;\n team?: {id?: unknown; name?: unknown} | null | undefined;\n scope?: unknown;\n is_enterprise_install?: unknown;\n expires_in?: unknown;\n refresh_token?: unknown;\n}\n\nexport function createSlackApiClient(): SlackApiClient {\n return {\n async exchangeAuthorizationCode(input) {\n const body = await mapSlackError('exchange-authorization-code', () =>\n ky\n .post(slackApiUrl('oauth.v2.access'), {\n body: new URLSearchParams({\n client_id: config.SLACK_OAUTH_CLIENT_ID,\n client_secret: config.SLACK_OAUTH_CLIENT_SECRET,\n code: input.code,\n redirect_uri: config.SLACK_OAUTH_REDIRECT_URL,\n }),\n timeout: SLACK_API_TIMEOUT_MS,\n })\n .json<SlackOAuthAccessResponse>(),\n );\n return parseOAuthAccess(body);\n },\n\n async revokeToken(input) {\n await mapSlackError('revoke-token', async () => {\n await ky.post(slackApiUrl('auth.revoke'), {\n headers: {authorization: `Bearer ${input.token}`},\n timeout: SLACK_API_TIMEOUT_MS,\n });\n });\n },\n\n async callMethod(input) {\n return await mapSlackWebApiError(input.method, () =>\n ky\n .post(slackApiUrl(input.method), {\n headers: {authorization: `Bearer ${input.token}`},\n body: slackMethodArguments(input.arguments),\n timeout: SLACK_API_TIMEOUT_MS,\n })\n .json<SlackWebApiResponse>(),\n );\n },\n };\n}\n\nfunction parseOAuthAccess(body: SlackOAuthAccessResponse): SlackAuthorization {\n if (body.ok !== true) {\n throw new SlackIntegrationProviderError(\n slackOAuthErrorReason(body.error),\n 'Slack authorization request failed',\n );\n }\n if (body.is_enterprise_install === true || body.team == null) {\n throw new SlackEnterpriseInstallUnsupportedError();\n }\n // The bot-token store has no refresh path, so a rotating (expiring) token must never be persisted.\n // Slack omits both fields when rotation is off, so presence of either signals rotation.\n // The rejected token self-expires, matching the Enterprise Grid rejection path.\n if (body.expires_in !== undefined || body.refresh_token !== undefined) {\n throw new SlackTokenRotationUnsupportedError();\n }\n const {access_token: accessToken, bot_user_id: botUserId, app_id: appId, team, scope} = body;\n if (\n typeof accessToken !== 'string' ||\n typeof botUserId !== 'string' ||\n typeof appId !== 'string' ||\n typeof team.id !== 'string' ||\n typeof team.name !== 'string' ||\n typeof scope !== 'string'\n ) {\n throw new SlackIntegrationProviderError(\n 'malformed-provider-response',\n 'Slack authorization response did not include the required installation details',\n );\n }\n return {\n accessToken,\n botUserId,\n appId,\n teamId: team.id,\n teamName: team.name,\n scopes: scope\n .split(',')\n .map((value) => value.trim())\n .filter(Boolean),\n };\n}\n\nfunction slackOAuthErrorReason(error: unknown) {\n if (error === 'service_unavailable' || error === 'internal_error') return 'provider-unavailable';\n if (error === 'ratelimited') return 'rate-limited';\n return 'access-denied';\n}\n\nasync function mapSlackError<T>(operation: string, request: () => Promise<T>): Promise<T> {\n try {\n return await request();\n } catch (error) {\n if (error instanceof SlackIntegrationProviderError) throw error;\n if (error instanceof HTTPError) {\n const {status, statusText, headers} = error.response;\n logger().warn({operation, status, statusText}, 'Slack API request rejected');\n if (status === 429) {\n throw new SlackIntegrationProviderError(\n 'rate-limited',\n 'Slack request was rate limited',\n retryAfterSeconds(headers),\n );\n }\n if (status >= 500) {\n throw new SlackIntegrationProviderError('provider-unavailable', 'Slack request failed');\n }\n throw new SlackIntegrationProviderError('access-denied', 'Slack request was rejected');\n }\n if (error instanceof TimeoutError) {\n logger().warn({operation}, 'Slack API request timed out');\n throw new SlackIntegrationProviderError('timeout', 'Slack request timed out');\n }\n logger().warn(\n {operation, errName: error instanceof Error ? error.name : typeof error},\n 'Slack API request failed',\n );\n throw new SlackIntegrationProviderError('provider-unavailable', 'Slack request failed');\n }\n}\n\nasync function mapSlackWebApiError<T>(method: string, request: () => Promise<T>): Promise<T> {\n try {\n return await request();\n } catch (error) {\n if (error instanceof SlackIntegrationProviderError) throw error;\n if (error instanceof HTTPError) {\n const {status, statusText, headers} = error.response;\n logger().warn(\n {operation: 'call-method', method, status, statusText},\n 'Slack API request rejected',\n );\n if (status === 429) {\n throw new SlackIntegrationProviderError(\n 'rate-limited',\n 'Slack request was rate limited',\n retryAfterSeconds(headers),\n );\n }\n if (status === 413) {\n throw new SlackIntegrationProviderError(\n 'content-too-large',\n 'Slack request content was too large',\n );\n }\n if (status >= 500) {\n throw new SlackIntegrationProviderError('provider-unavailable', 'Slack request failed');\n }\n throw new SlackIntegrationProviderError(\n 'malformed-provider-response',\n 'Slack request was rejected',\n );\n }\n if (error instanceof TimeoutError) {\n logger().warn({operation: 'call-method', method}, 'Slack API request timed out');\n throw new SlackIntegrationProviderError('timeout', 'Slack request timed out');\n }\n logger().warn(\n {\n operation: 'call-method',\n method,\n errName: error instanceof Error ? error.name : typeof error,\n },\n 'Slack API request failed',\n );\n throw new SlackIntegrationProviderError('provider-unavailable', 'Slack request failed');\n }\n}\n\nfunction slackMethodArguments(input: Record<string, unknown>): URLSearchParams {\n const body = new URLSearchParams();\n for (const [key, value] of Object.entries(input)) {\n if (value === null || value === undefined) continue;\n const serialized =\n typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean'\n ? String(value)\n : JSON.stringify(value);\n if (serialized !== undefined) body.set(key, serialized);\n }\n return body;\n}\n\nfunction slackApiUrl(path: string): string {\n return new URL(path, `${config.SLACK_API_BASE_URL}/`).toString();\n}\n\nfunction retryAfterSeconds(headers: Headers): number | undefined {\n const retryAfter = headers.get('retry-after');\n if (!retryAfter) return undefined;\n const parsed = Number.parseInt(retryAfter, 10);\n return Number.isNaN(parsed) ? undefined : parsed;\n}\n"],"names":["logger","ky","HTTPError","TimeoutError","config","SlackEnterpriseInstallUnsupportedError","SlackIntegrationProviderError","SlackTokenRotationUnsupportedError","SLACK_API_TIMEOUT_MS","createSlackApiClient","exchangeAuthorizationCode","input","body","mapSlackError","post","slackApiUrl","URLSearchParams","client_id","SLACK_OAUTH_CLIENT_ID","client_secret","SLACK_OAUTH_CLIENT_SECRET","code","redirect_uri","SLACK_OAUTH_REDIRECT_URL","timeout","json","parseOAuthAccess","revokeToken","headers","authorization","token","callMethod","mapSlackWebApiError","method","slackMethodArguments","arguments","ok","slackOAuthErrorReason","error","is_enterprise_install","team","expires_in","undefined","refresh_token","access_token","accessToken","bot_user_id","botUserId","app_id","appId","scope","id","name","teamId","teamName","scopes","split","map","value","trim","filter","Boolean","operation","request","status","statusText","response","warn","retryAfterSeconds","errName","Error","key","Object","entries","serialized","String","JSON","stringify","set","path","URL","SLACK_API_BASE_URL","toString","retryAfter","get","parsed","Number","parseInt","isNaN"],"mappings":"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"}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const config: Readonly<{
|
|
2
|
+
SLACK_OAUTH_CLIENT_ID: string;
|
|
3
|
+
SLACK_OAUTH_CLIENT_SECRET: string;
|
|
4
|
+
SLACK_SIGNING_SECRET: string;
|
|
5
|
+
SLACK_OAUTH_REDIRECT_URL: string;
|
|
6
|
+
SLACK_API_BASE_URL: string;
|
|
7
|
+
} & import("@shipfox/config").CleanedEnvAccessors>;
|
|
8
|
+
//# sourceMappingURL=config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,MAAM;;;;;;kDAiBjB,CAAC"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { createConfig, str, url } from '@shipfox/config';
|
|
2
|
+
export const config = createConfig({
|
|
3
|
+
SLACK_OAUTH_CLIENT_ID: str({
|
|
4
|
+
desc: 'OAuth client ID of the Slack app, used to start and complete the Slack connect flow. Required.'
|
|
5
|
+
}),
|
|
6
|
+
SLACK_OAUTH_CLIENT_SECRET: str({
|
|
7
|
+
desc: 'OAuth client secret of the Slack app, used to exchange authorization codes for workspace bot tokens. Required.'
|
|
8
|
+
}),
|
|
9
|
+
SLACK_SIGNING_SECRET: str({
|
|
10
|
+
desc: 'Secret used to verify incoming Slack events and commands. It must match the signing secret configured on the Slack app. Required.'
|
|
11
|
+
}),
|
|
12
|
+
SLACK_OAUTH_REDIRECT_URL: url({
|
|
13
|
+
desc: 'Public client callback URL Slack redirects to after OAuth authorization, such as https://shipfox.example.com/integrations/slack/callback. Required.'
|
|
14
|
+
}),
|
|
15
|
+
SLACK_API_BASE_URL: url({
|
|
16
|
+
desc: 'Slack Web API base URL. Override this only when routing Slack API requests through a proxy or test server.',
|
|
17
|
+
default: 'https://slack.com/api'
|
|
18
|
+
})
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config.ts"],"sourcesContent":["import {createConfig, str, url} from '@shipfox/config';\n\nexport const config = createConfig({\n SLACK_OAUTH_CLIENT_ID: str({\n desc: 'OAuth client ID of the Slack app, used to start and complete the Slack connect flow. Required.',\n }),\n SLACK_OAUTH_CLIENT_SECRET: str({\n desc: 'OAuth client secret of the Slack app, used to exchange authorization codes for workspace bot tokens. Required.',\n }),\n SLACK_SIGNING_SECRET: str({\n desc: 'Secret used to verify incoming Slack events and commands. It must match the signing secret configured on the Slack app. Required.',\n }),\n SLACK_OAUTH_REDIRECT_URL: url({\n desc: 'Public client callback URL Slack redirects to after OAuth authorization, such as https://shipfox.example.com/integrations/slack/callback. Required.',\n }),\n SLACK_API_BASE_URL: url({\n desc: 'Slack Web API base URL. Override this only when routing Slack API requests through a proxy or test server.',\n default: 'https://slack.com/api',\n }),\n});\n"],"names":["createConfig","str","url","config","SLACK_OAUTH_CLIENT_ID","desc","SLACK_OAUTH_CLIENT_SECRET","SLACK_SIGNING_SECRET","SLACK_OAUTH_REDIRECT_URL","SLACK_API_BASE_URL","default"],"mappings":"AAAA,SAAQA,YAAY,EAAEC,GAAG,EAAEC,GAAG,QAAO,kBAAkB;AAEvD,OAAO,MAAMC,SAASH,aAAa;IACjCI,uBAAuBH,IAAI;QACzBI,MAAM;IACR;IACAC,2BAA2BL,IAAI;QAC7BI,MAAM;IACR;IACAE,sBAAsBN,IAAI;QACxBI,MAAM;IACR;IACAG,0BAA0BN,IAAI;QAC5BG,MAAM;IACR;IACAI,oBAAoBP,IAAI;QACtBG,MAAM;QACNK,SAAS;IACX;AACF,GAAG"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { AgentToolSession, AgentToolsProvider, IntegrationConnection, OpenAgentToolsSessionInput } from '@shipfox/api-integration-core-dto';
|
|
2
|
+
import type { SlackApiClient } from '#api/client.js';
|
|
3
|
+
import { type SlackAgentToolRequiredScope } from '#core/agent-tools.js';
|
|
4
|
+
import type { SlackTokenStore } from '#core/tokens.js';
|
|
5
|
+
type SlackIntegrationConnection = IntegrationConnection<'slack'>;
|
|
6
|
+
export type SlackToolCallResult = {
|
|
7
|
+
isError?: boolean | undefined;
|
|
8
|
+
content: readonly {
|
|
9
|
+
type: 'text';
|
|
10
|
+
text: string;
|
|
11
|
+
}[];
|
|
12
|
+
structuredContent?: Record<string, unknown> | undefined;
|
|
13
|
+
};
|
|
14
|
+
export interface SlackAgentToolsProviderOptions {
|
|
15
|
+
slack: Pick<SlackApiClient, 'callMethod'>;
|
|
16
|
+
tokenStore: Pick<SlackTokenStore, 'getAccessToken'>;
|
|
17
|
+
}
|
|
18
|
+
export declare class SlackAgentToolsProvider implements AgentToolsProvider<SlackIntegrationConnection, SlackAgentToolRequiredScope, unknown, SlackToolCallResult> {
|
|
19
|
+
private readonly options;
|
|
20
|
+
constructor(options: SlackAgentToolsProviderOptions);
|
|
21
|
+
catalog(): readonly [{
|
|
22
|
+
readonly id: "conversations_history";
|
|
23
|
+
readonly description: "List messages in a Slack channel or direct message.";
|
|
24
|
+
readonly sensitivity: "read";
|
|
25
|
+
readonly sensitive: false;
|
|
26
|
+
readonly requiredScope: "read";
|
|
27
|
+
readonly inputSchema: import("@shipfox/api-integration-core-dto").AgentToolJsonSchema;
|
|
28
|
+
}, {
|
|
29
|
+
readonly id: "conversations_replies";
|
|
30
|
+
readonly description: "List replies in a Slack message thread.";
|
|
31
|
+
readonly sensitivity: "read";
|
|
32
|
+
readonly sensitive: false;
|
|
33
|
+
readonly requiredScope: "read";
|
|
34
|
+
readonly inputSchema: import("@shipfox/api-integration-core-dto").AgentToolJsonSchema;
|
|
35
|
+
}, {
|
|
36
|
+
readonly id: "conversations_list";
|
|
37
|
+
readonly description: "List Slack channels visible to the bot.";
|
|
38
|
+
readonly sensitivity: "read";
|
|
39
|
+
readonly sensitive: false;
|
|
40
|
+
readonly requiredScope: "read";
|
|
41
|
+
readonly inputSchema: import("@shipfox/api-integration-core-dto").AgentToolJsonSchema;
|
|
42
|
+
}, {
|
|
43
|
+
readonly id: "users_info";
|
|
44
|
+
readonly description: "Look up a Slack user and profile by user ID.";
|
|
45
|
+
readonly sensitivity: "read";
|
|
46
|
+
readonly sensitive: false;
|
|
47
|
+
readonly requiredScope: "read";
|
|
48
|
+
readonly inputSchema: import("@shipfox/api-integration-core-dto").AgentToolJsonSchema;
|
|
49
|
+
}, {
|
|
50
|
+
readonly id: "chat_postMessage";
|
|
51
|
+
readonly description: "Post a Slack message to a channel or thread. Provide text, blocks, or both.";
|
|
52
|
+
readonly sensitivity: "write";
|
|
53
|
+
readonly sensitive: false;
|
|
54
|
+
readonly requiredScope: "write";
|
|
55
|
+
readonly inputSchema: import("@shipfox/api-integration-core-dto").AgentToolJsonSchema;
|
|
56
|
+
}, {
|
|
57
|
+
readonly id: "chat_update";
|
|
58
|
+
readonly description: "Update a Slack message with text, blocks, or both.";
|
|
59
|
+
readonly sensitivity: "write";
|
|
60
|
+
readonly sensitive: false;
|
|
61
|
+
readonly requiredScope: "write";
|
|
62
|
+
readonly inputSchema: import("@shipfox/api-integration-core-dto").AgentToolJsonSchema;
|
|
63
|
+
}, {
|
|
64
|
+
readonly id: "reactions_add";
|
|
65
|
+
readonly description: "Add an emoji reaction to a Slack message.";
|
|
66
|
+
readonly sensitivity: "write";
|
|
67
|
+
readonly sensitive: false;
|
|
68
|
+
readonly requiredScope: "write";
|
|
69
|
+
readonly inputSchema: import("@shipfox/api-integration-core-dto").AgentToolJsonSchema;
|
|
70
|
+
}];
|
|
71
|
+
selectionCatalog(): import("@shipfox/api-integration-core-dto").AgentToolSelectionCatalog;
|
|
72
|
+
openSession(input: OpenAgentToolsSessionInput<SlackIntegrationConnection, SlackAgentToolRequiredScope, unknown>): Promise<AgentToolSession<SlackToolCallResult>>;
|
|
73
|
+
}
|
|
74
|
+
export {};
|
|
75
|
+
//# sourceMappingURL=agent-tools-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-tools-provider.d.ts","sourceRoot":"","sources":["../../src/core/agent-tools-provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,kBAAkB,EAClB,qBAAqB,EACrB,0BAA0B,EAC3B,MAAM,mCAAmC,CAAC;AAE3C,OAAO,KAAK,EAAC,cAAc,EAAsB,MAAM,gBAAgB,CAAC;AACxE,OAAO,EAIL,KAAK,2BAA2B,EAGjC,MAAM,sBAAsB,CAAC;AAE9B,OAAO,KAAK,EAAC,eAAe,EAAC,MAAM,iBAAiB,CAAC;AAErD,KAAK,0BAA0B,GAAG,qBAAqB,CAAC,OAAO,CAAC,CAAC;AAEjE,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IAC9B,OAAO,EAAE,SAAS;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAC,EAAE,CAAC;IACjD,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;CACzD,CAAC;AAEF,MAAM,WAAW,8BAA8B;IAC7C,KAAK,EAAE,IAAI,CAAC,cAAc,EAAE,YAAY,CAAC,CAAC;IAC1C,UAAU,EAAE,IAAI,CAAC,eAAe,EAAE,gBAAgB,CAAC,CAAC;CACrD;AAED,qBAAa,uBACX,YACE,kBAAkB,CAChB,0BAA0B,EAC1B,2BAA2B,EAC3B,OAAO,EACP,mBAAmB,CACpB;IAES,OAAO,CAAC,QAAQ,CAAC,OAAO;gBAAP,OAAO,EAAE,8BAA8B;IAEpE,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAIP,gBAAgB;IAIV,WAAW,CACf,KAAK,EAAE,0BAA0B,CAC/B,0BAA0B,EAC1B,2BAA2B,EAC3B,OAAO,CACR,GACA,OAAO,CAAC,gBAAgB,CAAC,mBAAmB,CAAC,CAAC;CAgDlD"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { logger } from '@shipfox/node-opentelemetry';
|
|
2
|
+
import { SLACK_TOOL_METHODS, slackAgentToolCatalog, slackAgentToolSelectionCatalog } from '#core/agent-tools.js';
|
|
3
|
+
import { SlackIntegrationProviderError } from '#core/errors.js';
|
|
4
|
+
export class SlackAgentToolsProvider {
|
|
5
|
+
constructor(options){
|
|
6
|
+
this.options = options;
|
|
7
|
+
}
|
|
8
|
+
catalog() {
|
|
9
|
+
return slackAgentToolCatalog;
|
|
10
|
+
}
|
|
11
|
+
selectionCatalog() {
|
|
12
|
+
return slackAgentToolSelectionCatalog;
|
|
13
|
+
}
|
|
14
|
+
async openSession(input) {
|
|
15
|
+
const token = await this.options.tokenStore.getAccessToken({
|
|
16
|
+
connectionId: input.connection.id
|
|
17
|
+
});
|
|
18
|
+
return {
|
|
19
|
+
call: async (call)=>{
|
|
20
|
+
const tool = input.tools.find((candidate)=>candidate.id === call.toolId);
|
|
21
|
+
if (!tool) return slackToolError(`Unknown Slack tool: ${call.toolId}`);
|
|
22
|
+
const method = slackToolMethod(tool.id);
|
|
23
|
+
if (!method) return slackToolError(`Unknown Slack tool method: ${tool.id}`);
|
|
24
|
+
const missingParameter = missingRequiredParameter(tool, call.arguments);
|
|
25
|
+
if (missingParameter) {
|
|
26
|
+
return slackToolError(`Missing required parameter: ${missingParameter}`);
|
|
27
|
+
}
|
|
28
|
+
let body;
|
|
29
|
+
try {
|
|
30
|
+
body = await this.options.slack.callMethod({
|
|
31
|
+
method,
|
|
32
|
+
token,
|
|
33
|
+
arguments: call.arguments
|
|
34
|
+
});
|
|
35
|
+
} catch (error) {
|
|
36
|
+
if (error instanceof SlackIntegrationProviderError) {
|
|
37
|
+
return slackToolError(error.message, {
|
|
38
|
+
code: error.reason,
|
|
39
|
+
retryAfterSeconds: error.retryAfterSeconds
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
throw error;
|
|
43
|
+
}
|
|
44
|
+
if (body.ok) return slackToolResult(body);
|
|
45
|
+
const slackError = typeof body.error === 'string' ? body.error : 'Slack request failed';
|
|
46
|
+
if (isSlackAccessError(slackError)) {
|
|
47
|
+
logger().warn({
|
|
48
|
+
connectionId: input.connection.id,
|
|
49
|
+
slackError
|
|
50
|
+
}, 'Slack API rejected integration credentials');
|
|
51
|
+
return slackToolError(slackError, {
|
|
52
|
+
code: 'access-denied'
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
if (slackError === 'ratelimited') {
|
|
56
|
+
return slackToolError(slackError, {
|
|
57
|
+
code: 'rate-limited'
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
return slackToolError(slackError);
|
|
61
|
+
},
|
|
62
|
+
close: ()=>Promise.resolve()
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function slackToolMethod(toolId) {
|
|
67
|
+
return SLACK_TOOL_METHODS[toolId];
|
|
68
|
+
}
|
|
69
|
+
function missingRequiredParameter(tool, args) {
|
|
70
|
+
const required = tool.inputSchema.required;
|
|
71
|
+
if (!Array.isArray(required)) return undefined;
|
|
72
|
+
return required.find((parameter)=>typeof parameter === 'string' && !(parameter in args));
|
|
73
|
+
}
|
|
74
|
+
function isSlackAccessError(error) {
|
|
75
|
+
return error === 'invalid_auth' || error === 'token_revoked' || error === 'account_inactive';
|
|
76
|
+
}
|
|
77
|
+
function slackToolResult(body) {
|
|
78
|
+
return {
|
|
79
|
+
content: [
|
|
80
|
+
{
|
|
81
|
+
type: 'text',
|
|
82
|
+
text: JSON.stringify(body)
|
|
83
|
+
}
|
|
84
|
+
],
|
|
85
|
+
structuredContent: body
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function slackToolError(message, options = {}) {
|
|
89
|
+
const structuredContent = {
|
|
90
|
+
...options.code === undefined ? {} : {
|
|
91
|
+
code: options.code
|
|
92
|
+
},
|
|
93
|
+
...options.retryAfterSeconds === undefined ? {} : {
|
|
94
|
+
retryAfterSeconds: options.retryAfterSeconds
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
return {
|
|
98
|
+
isError: true,
|
|
99
|
+
content: [
|
|
100
|
+
{
|
|
101
|
+
type: 'text',
|
|
102
|
+
text: message
|
|
103
|
+
}
|
|
104
|
+
],
|
|
105
|
+
...Object.keys(structuredContent).length === 0 ? {} : {
|
|
106
|
+
structuredContent
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
//# sourceMappingURL=agent-tools-provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/core/agent-tools-provider.ts"],"sourcesContent":["import type {\n AgentToolSession,\n AgentToolsProvider,\n IntegrationConnection,\n OpenAgentToolsSessionInput,\n} from '@shipfox/api-integration-core-dto';\nimport {logger} from '@shipfox/node-opentelemetry';\nimport type {SlackApiClient, SlackWebApiResponse} from '#api/client.js';\nimport {\n SLACK_TOOL_METHODS,\n type SlackAgentToolCatalogEntry,\n type SlackAgentToolId,\n type SlackAgentToolRequiredScope,\n slackAgentToolCatalog,\n slackAgentToolSelectionCatalog,\n} from '#core/agent-tools.js';\nimport {SlackIntegrationProviderError} from '#core/errors.js';\nimport type {SlackTokenStore} from '#core/tokens.js';\n\ntype SlackIntegrationConnection = IntegrationConnection<'slack'>;\n\nexport type SlackToolCallResult = {\n isError?: boolean | undefined;\n content: readonly {type: 'text'; text: string}[];\n structuredContent?: Record<string, unknown> | undefined;\n};\n\nexport interface SlackAgentToolsProviderOptions {\n slack: Pick<SlackApiClient, 'callMethod'>;\n tokenStore: Pick<SlackTokenStore, 'getAccessToken'>;\n}\n\nexport class SlackAgentToolsProvider\n implements\n AgentToolsProvider<\n SlackIntegrationConnection,\n SlackAgentToolRequiredScope,\n unknown,\n SlackToolCallResult\n >\n{\n constructor(private readonly options: SlackAgentToolsProviderOptions) {}\n\n catalog() {\n return slackAgentToolCatalog;\n }\n\n selectionCatalog() {\n return slackAgentToolSelectionCatalog;\n }\n\n async openSession(\n input: OpenAgentToolsSessionInput<\n SlackIntegrationConnection,\n SlackAgentToolRequiredScope,\n unknown\n >,\n ): Promise<AgentToolSession<SlackToolCallResult>> {\n const token = await this.options.tokenStore.getAccessToken({connectionId: input.connection.id});\n\n return {\n call: async (call) => {\n const tool = input.tools.find((candidate) => candidate.id === call.toolId);\n if (!tool) return slackToolError(`Unknown Slack tool: ${call.toolId}`);\n const method = slackToolMethod(tool.id);\n if (!method) return slackToolError(`Unknown Slack tool method: ${tool.id}`);\n const missingParameter = missingRequiredParameter(tool, call.arguments);\n if (missingParameter) {\n return slackToolError(`Missing required parameter: ${missingParameter}`);\n }\n\n let body: SlackWebApiResponse;\n try {\n body = await this.options.slack.callMethod({\n method,\n token,\n arguments: call.arguments,\n });\n } catch (error) {\n if (error instanceof SlackIntegrationProviderError) {\n return slackToolError(error.message, {\n code: error.reason,\n retryAfterSeconds: error.retryAfterSeconds,\n });\n }\n throw error;\n }\n\n if (body.ok) return slackToolResult(body);\n const slackError = typeof body.error === 'string' ? body.error : 'Slack request failed';\n if (isSlackAccessError(slackError)) {\n logger().warn(\n {connectionId: input.connection.id, slackError},\n 'Slack API rejected integration credentials',\n );\n return slackToolError(slackError, {code: 'access-denied'});\n }\n if (slackError === 'ratelimited') {\n return slackToolError(slackError, {code: 'rate-limited'});\n }\n return slackToolError(slackError);\n },\n close: () => Promise.resolve(),\n };\n }\n}\n\nfunction slackToolMethod(toolId: string): string | undefined {\n return SLACK_TOOL_METHODS[toolId as SlackAgentToolId];\n}\n\nfunction missingRequiredParameter(\n tool: SlackAgentToolCatalogEntry,\n args: Record<string, unknown>,\n): string | undefined {\n const required = tool.inputSchema.required;\n if (!Array.isArray(required)) return undefined;\n return required.find((parameter) => typeof parameter === 'string' && !(parameter in args));\n}\n\nfunction isSlackAccessError(error: string): boolean {\n return error === 'invalid_auth' || error === 'token_revoked' || error === 'account_inactive';\n}\n\nfunction slackToolResult(body: SlackWebApiResponse): SlackToolCallResult {\n return {\n content: [{type: 'text', text: JSON.stringify(body)}],\n structuredContent: body,\n };\n}\n\nfunction slackToolError(\n message: string,\n options: {code?: string | undefined; retryAfterSeconds?: number | undefined} = {},\n): SlackToolCallResult {\n const structuredContent = {\n ...(options.code === undefined ? {} : {code: options.code}),\n ...(options.retryAfterSeconds === undefined\n ? {}\n : {retryAfterSeconds: options.retryAfterSeconds}),\n };\n return {\n isError: true,\n content: [{type: 'text', text: message}],\n ...(Object.keys(structuredContent).length === 0 ? {} : {structuredContent}),\n };\n}\n"],"names":["logger","SLACK_TOOL_METHODS","slackAgentToolCatalog","slackAgentToolSelectionCatalog","SlackIntegrationProviderError","SlackAgentToolsProvider","options","catalog","selectionCatalog","openSession","input","token","tokenStore","getAccessToken","connectionId","connection","id","call","tool","tools","find","candidate","toolId","slackToolError","method","slackToolMethod","missingParameter","missingRequiredParameter","arguments","body","slack","callMethod","error","message","code","reason","retryAfterSeconds","ok","slackToolResult","slackError","isSlackAccessError","warn","close","Promise","resolve","args","required","inputSchema","Array","isArray","undefined","parameter","content","type","text","JSON","stringify","structuredContent","isError","Object","keys","length"],"mappings":"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"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { AgentToolCatalogEntry, AgentToolJsonSchema, AgentToolSelectionCatalog } from '@shipfox/api-integration-core-dto';
|
|
2
|
+
export type SlackAgentToolRequiredScope = 'read' | 'write';
|
|
3
|
+
export type SlackAgentToolCatalogEntry = AgentToolCatalogEntry<SlackAgentToolRequiredScope>;
|
|
4
|
+
export declare const slackAgentToolCatalog: readonly [{
|
|
5
|
+
readonly id: "conversations_history";
|
|
6
|
+
readonly description: "List messages in a Slack channel or direct message.";
|
|
7
|
+
readonly sensitivity: "read";
|
|
8
|
+
readonly sensitive: false;
|
|
9
|
+
readonly requiredScope: "read";
|
|
10
|
+
readonly inputSchema: AgentToolJsonSchema;
|
|
11
|
+
}, {
|
|
12
|
+
readonly id: "conversations_replies";
|
|
13
|
+
readonly description: "List replies in a Slack message thread.";
|
|
14
|
+
readonly sensitivity: "read";
|
|
15
|
+
readonly sensitive: false;
|
|
16
|
+
readonly requiredScope: "read";
|
|
17
|
+
readonly inputSchema: AgentToolJsonSchema;
|
|
18
|
+
}, {
|
|
19
|
+
readonly id: "conversations_list";
|
|
20
|
+
readonly description: "List Slack channels visible to the bot.";
|
|
21
|
+
readonly sensitivity: "read";
|
|
22
|
+
readonly sensitive: false;
|
|
23
|
+
readonly requiredScope: "read";
|
|
24
|
+
readonly inputSchema: AgentToolJsonSchema;
|
|
25
|
+
}, {
|
|
26
|
+
readonly id: "users_info";
|
|
27
|
+
readonly description: "Look up a Slack user and profile by user ID.";
|
|
28
|
+
readonly sensitivity: "read";
|
|
29
|
+
readonly sensitive: false;
|
|
30
|
+
readonly requiredScope: "read";
|
|
31
|
+
readonly inputSchema: AgentToolJsonSchema;
|
|
32
|
+
}, {
|
|
33
|
+
readonly id: "chat_postMessage";
|
|
34
|
+
readonly description: "Post a Slack message to a channel or thread. Provide text, blocks, or both.";
|
|
35
|
+
readonly sensitivity: "write";
|
|
36
|
+
readonly sensitive: false;
|
|
37
|
+
readonly requiredScope: "write";
|
|
38
|
+
readonly inputSchema: AgentToolJsonSchema;
|
|
39
|
+
}, {
|
|
40
|
+
readonly id: "chat_update";
|
|
41
|
+
readonly description: "Update a Slack message with text, blocks, or both.";
|
|
42
|
+
readonly sensitivity: "write";
|
|
43
|
+
readonly sensitive: false;
|
|
44
|
+
readonly requiredScope: "write";
|
|
45
|
+
readonly inputSchema: AgentToolJsonSchema;
|
|
46
|
+
}, {
|
|
47
|
+
readonly id: "reactions_add";
|
|
48
|
+
readonly description: "Add an emoji reaction to a Slack message.";
|
|
49
|
+
readonly sensitivity: "write";
|
|
50
|
+
readonly sensitive: false;
|
|
51
|
+
readonly requiredScope: "write";
|
|
52
|
+
readonly inputSchema: AgentToolJsonSchema;
|
|
53
|
+
}];
|
|
54
|
+
export type SlackAgentToolId = (typeof slackAgentToolCatalog)[number]['id'];
|
|
55
|
+
export declare const SLACK_TOOL_METHODS: {
|
|
56
|
+
readonly conversations_history: "conversations.history";
|
|
57
|
+
readonly conversations_replies: "conversations.replies";
|
|
58
|
+
readonly conversations_list: "conversations.list";
|
|
59
|
+
readonly users_info: "users.info";
|
|
60
|
+
readonly chat_postMessage: "chat.postMessage";
|
|
61
|
+
readonly chat_update: "chat.update";
|
|
62
|
+
readonly reactions_add: "reactions.add";
|
|
63
|
+
};
|
|
64
|
+
export declare const slackAgentToolSelectionCatalog: AgentToolSelectionCatalog;
|
|
65
|
+
//# sourceMappingURL=agent-tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-tools.d.ts","sourceRoot":"","sources":["../../src/core/agent-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,qBAAqB,EACrB,mBAAmB,EACnB,yBAAyB,EAE1B,MAAM,mCAAmC,CAAC;AAE3C,MAAM,MAAM,2BAA2B,GAAG,MAAM,GAAG,OAAO,CAAC;AAC3D,MAAM,MAAM,0BAA0B,GAAG,qBAAqB,CAAC,2BAA2B,CAAC,CAAC;AAe5F,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuGwB,CAAC;AAE3D,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC;AAE5E,eAAO,MAAM,kBAAkB;;;;;;;;CAQsB,CAAC;AAEtD,eAAO,MAAM,8BAA8B,2BACiB,CAAC"}
|