@cuylabs/channel-slack 0.3.0 → 0.5.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 +20 -12
- package/dist/bolt.d.ts +30 -1
- package/dist/bolt.js +163 -0
- package/dist/chunk-CMR6B76C.js +664 -0
- package/dist/{chunk-FX2JOVX5.js → chunk-IDVDMJ5U.js} +262 -1
- package/dist/diagnostics.d.ts +21 -104
- package/dist/diagnostics.js +21 -3
- package/dist/index.d.ts +1 -1
- package/dist/index.js +7 -1
- package/dist/inspect-BpY5JA0K.d.ts +128 -0
- package/dist/policy.d.ts +47 -1
- package/dist/policy.js +7 -1
- package/dist/setup.d.ts +1 -1
- package/dist/setup.js +1 -1
- package/docs/concepts/bolt-runtime.md +26 -0
- package/docs/concepts/message-policy.md +32 -0
- package/docs/reference/channel-slack-boundary.md +2 -2
- package/docs/reference/exports.md +12 -12
- package/package.json +1 -1
- package/dist/chunk-BODPT4I6.js +0 -322
package/README.md
CHANGED
|
@@ -25,22 +25,26 @@ use:
|
|
|
25
25
|
npm install @slack/bolt @slack/web-api @slack/types express
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
+
Postgres-backed helpers lazy-load `pg` when you pass a connection string. Install
|
|
29
|
+
`pg` in applications that use those helpers without injecting their own client
|
|
30
|
+
or pool.
|
|
31
|
+
|
|
28
32
|
## Import Map
|
|
29
33
|
|
|
30
34
|
Prefer feature-specific imports so applications only couple to the Slack
|
|
31
35
|
surface they need.
|
|
32
36
|
|
|
33
|
-
| Import
|
|
34
|
-
|
|
|
35
|
-
| `@cuylabs/channel-slack/core`
|
|
36
|
-
| `@cuylabs/channel-slack/policy`
|
|
37
|
-
| `@cuylabs/channel-slack/history`
|
|
38
|
-
| `@cuylabs/channel-slack/bolt`
|
|
39
|
-
| `@cuylabs/channel-slack/setup`
|
|
40
|
-
| `@cuylabs/channel-slack/diagnostics` | Slack token and scope checks
|
|
41
|
-
| `@cuylabs/channel-slack/users`
|
|
42
|
-
| `@cuylabs/channel-slack/targets`
|
|
43
|
-
| `@cuylabs/channel-slack/feedback`
|
|
37
|
+
| Import | Use for |
|
|
38
|
+
| ------------------------------------ | ---------------------------------------------------------------------------------- |
|
|
39
|
+
| `@cuylabs/channel-slack/core` | Activity parsing, formatting, sessions, turn context, shared types |
|
|
40
|
+
| `@cuylabs/channel-slack/policy` | Message admission, duplicate suppression, mentioned-thread state |
|
|
41
|
+
| `@cuylabs/channel-slack/history` | Slack history reading, prompt shaping, supplemental-history visibility |
|
|
42
|
+
| `@cuylabs/channel-slack/bolt` | Bolt app factories, auth options, Socket Mode runtime helpers, installation stores |
|
|
43
|
+
| `@cuylabs/channel-slack/setup` | Required scopes/events/settings, generated manifests, setup inspection |
|
|
44
|
+
| `@cuylabs/channel-slack/diagnostics` | Slack token and scope checks |
|
|
45
|
+
| `@cuylabs/channel-slack/users` | User profile lookup and mention enrichment |
|
|
46
|
+
| `@cuylabs/channel-slack/targets` | Human-friendly Slack channel/user target parsing and resolution |
|
|
47
|
+
| `@cuylabs/channel-slack/feedback` | Feedback Block Kit and action helpers |
|
|
44
48
|
|
|
45
49
|
The package root re-exports `core` and `policy` as a lightweight convenience.
|
|
46
50
|
Use feature-specific imports for peer-backed helpers such as `bolt`,
|
|
@@ -92,6 +96,8 @@ See [Activity](docs/concepts/activity.md).
|
|
|
92
96
|
`policy` decides which Slack messages an adapter should process. It accepts DMs
|
|
93
97
|
and direct mentions by default, can allow passive channel messages by channel or
|
|
94
98
|
install scope, tracks mentioned threads, and suppresses duplicate Slack events.
|
|
99
|
+
Use `createPostgresSlackMessagePolicyStateStore` when duplicate suppression and
|
|
100
|
+
mentioned-thread state must survive restarts or coordinate across workers.
|
|
95
101
|
|
|
96
102
|
See [Message Policy](docs/concepts/message-policy.md).
|
|
97
103
|
|
|
@@ -107,7 +113,9 @@ See [Supplemental History](docs/concepts/supplemental-history.md).
|
|
|
107
113
|
|
|
108
114
|
`bolt` contains app factories for HTTP and Socket Mode, direct auth resolution,
|
|
109
115
|
OAuth installation-store types, development installation stores, and Socket Mode
|
|
110
|
-
process/runtime guards. It
|
|
116
|
+
process/runtime guards. It also exposes a Postgres advisory-lock helper for
|
|
117
|
+
distributed Socket Mode single-instance coordination. It does not register agent
|
|
118
|
+
handlers.
|
|
111
119
|
|
|
112
120
|
See [Bolt Runtime](docs/concepts/bolt-runtime.md).
|
|
113
121
|
|
package/dist/bolt.d.ts
CHANGED
|
@@ -262,6 +262,34 @@ interface SlackSocketModeProcessLock {
|
|
|
262
262
|
}
|
|
263
263
|
declare function acquireSlackSocketModeProcessLock({ appSlug, appToken, enabled, lockDir, logger, }: SlackSocketModeProcessLockOptions): SlackSocketModeProcessLock | undefined;
|
|
264
264
|
|
|
265
|
+
interface SlackSocketModePostgresLockClient {
|
|
266
|
+
query<T = unknown>(sql: string, values?: readonly unknown[]): Promise<{
|
|
267
|
+
rows: T[];
|
|
268
|
+
rowCount?: number | null;
|
|
269
|
+
}>;
|
|
270
|
+
release?: () => void;
|
|
271
|
+
}
|
|
272
|
+
interface SlackSocketModePostgresLockPool {
|
|
273
|
+
connect(): Promise<SlackSocketModePostgresLockClient>;
|
|
274
|
+
end?: () => Promise<void>;
|
|
275
|
+
}
|
|
276
|
+
interface SlackSocketModePostgresLockOptions {
|
|
277
|
+
appSlug: string;
|
|
278
|
+
appToken?: string;
|
|
279
|
+
client?: SlackSocketModePostgresLockClient;
|
|
280
|
+
connectionString?: string;
|
|
281
|
+
enabled?: boolean;
|
|
282
|
+
logger?: Logger;
|
|
283
|
+
namespace?: string;
|
|
284
|
+
pool?: SlackSocketModePostgresLockPool;
|
|
285
|
+
}
|
|
286
|
+
interface SlackSocketModePostgresLock {
|
|
287
|
+
path: string;
|
|
288
|
+
close(): Promise<void>;
|
|
289
|
+
release(): Promise<void>;
|
|
290
|
+
}
|
|
291
|
+
declare function acquireSlackSocketModePostgresLock({ appSlug, appToken, client, connectionString, enabled, logger, namespace, pool, }: SlackSocketModePostgresLockOptions): Promise<SlackSocketModePostgresLock | undefined>;
|
|
292
|
+
|
|
265
293
|
type SlackSocketModeRuntimePolicy = "single-instance" | "external-coordination";
|
|
266
294
|
interface SlackSocketModeReceiverRuntimeOptions {
|
|
267
295
|
autoReconnectEnabled?: boolean;
|
|
@@ -330,6 +358,7 @@ declare class JsonFileSlackInstallationStore implements SlackInstallationStore {
|
|
|
330
358
|
private writeInstallations;
|
|
331
359
|
}
|
|
332
360
|
declare function createJsonFileSlackInstallationStore(filePath: string): SlackInstallationStore;
|
|
361
|
+
declare function createSlackInstallationStoreAuthorize(store: SlackInstallationStore): SlackAuthorizeFn;
|
|
333
362
|
declare function getSlackInstallationKey(input: SlackInstallation | SlackInstallationQuery): string;
|
|
334
363
|
|
|
335
|
-
export { type CreateSlackBoltAppOptions, type CreateSlackBoltAppResult, type CreateSlackSocketBoltAppOptions, type CreateSlackSocketBoltAppResult, InMemorySlackInstallationStore, JsonFileSlackInstallationStore, type SlackAuthorizeFn, type SlackAuthorizeResult, type SlackAuthorizeSource, type SlackCustomAuthorizeAuthOptions, type SlackDirectAuthMode, type SlackDirectAuthOptions, type SlackInstallation, type SlackInstallationQuery, type SlackInstallationStore, type SlackOAuthAuthOptions, type SlackOAuthCallbackOptions, type SlackOAuthError, type SlackOAuthInstallPathOptions, type SlackSingleWorkspaceAuthOptions, type SlackSocketModeProcessLock, type SlackSocketModeProcessLockOptions, type SlackSocketModeReceiverRuntimeOptions, type SlackSocketModeRestartGuard, type SlackSocketModeRuntime, type SlackSocketModeRuntimeOptions, type SlackSocketModeRuntimePolicy, type SlackStateStore, acquireSlackSocketModeProcessLock, createInMemorySlackInstallationStore, createJsonFileSlackInstallationStore, createSlackBoltApp, createSlackSdkLogger, createSlackSocketBoltApp, createSlackSocketModeRestartGuard, createSlackSocketModeRuntime, getSlackInstallationKey, redactSlackSocketModeLogValue };
|
|
364
|
+
export { type CreateSlackBoltAppOptions, type CreateSlackBoltAppResult, type CreateSlackSocketBoltAppOptions, type CreateSlackSocketBoltAppResult, InMemorySlackInstallationStore, JsonFileSlackInstallationStore, type SlackAuthorizeFn, type SlackAuthorizeResult, type SlackAuthorizeSource, type SlackCustomAuthorizeAuthOptions, type SlackDirectAuthMode, type SlackDirectAuthOptions, type SlackInstallation, type SlackInstallationQuery, type SlackInstallationStore, type SlackOAuthAuthOptions, type SlackOAuthCallbackOptions, type SlackOAuthError, type SlackOAuthInstallPathOptions, type SlackSingleWorkspaceAuthOptions, type SlackSocketModePostgresLock, type SlackSocketModePostgresLockClient, type SlackSocketModePostgresLockOptions, type SlackSocketModePostgresLockPool, type SlackSocketModeProcessLock, type SlackSocketModeProcessLockOptions, type SlackSocketModeReceiverRuntimeOptions, type SlackSocketModeRestartGuard, type SlackSocketModeRuntime, type SlackSocketModeRuntimeOptions, type SlackSocketModeRuntimePolicy, type SlackStateStore, acquireSlackSocketModePostgresLock, acquireSlackSocketModeProcessLock, createInMemorySlackInstallationStore, createJsonFileSlackInstallationStore, createSlackBoltApp, createSlackInstallationStoreAuthorize, createSlackSdkLogger, createSlackSocketBoltApp, createSlackSocketModeRestartGuard, createSlackSocketModeRuntime, getSlackInstallationKey, redactSlackSocketModeLogValue };
|
package/dist/bolt.js
CHANGED
|
@@ -363,6 +363,141 @@ function formatLockError(error) {
|
|
|
363
363
|
return error instanceof Error ? error.message : String(error);
|
|
364
364
|
}
|
|
365
365
|
|
|
366
|
+
// src/bolt/postgres-socket-lock.ts
|
|
367
|
+
import crypto2 from "crypto";
|
|
368
|
+
var DEFAULT_LOCK_NAMESPACE = "channel-slack-socket";
|
|
369
|
+
async function acquireSlackSocketModePostgresLock({
|
|
370
|
+
appSlug,
|
|
371
|
+
appToken,
|
|
372
|
+
client,
|
|
373
|
+
connectionString,
|
|
374
|
+
enabled = true,
|
|
375
|
+
logger,
|
|
376
|
+
namespace = DEFAULT_LOCK_NAMESPACE,
|
|
377
|
+
pool
|
|
378
|
+
}) {
|
|
379
|
+
if (!enabled) {
|
|
380
|
+
logger?.debug?.("Slack Socket Mode Postgres lock skipped", {
|
|
381
|
+
reason: "disabled"
|
|
382
|
+
});
|
|
383
|
+
return void 0;
|
|
384
|
+
}
|
|
385
|
+
if (!appToken?.trim()) {
|
|
386
|
+
throw new Error(
|
|
387
|
+
"Slack app token is required when the Slack Socket Mode Postgres lock is enabled."
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
const tokenHash = hashValue(appToken);
|
|
391
|
+
const [key1, key2] = advisoryLockKeys(`${namespace}:${appSlug}:${tokenHash}`);
|
|
392
|
+
const lockPath = `postgres-advisory:${key1}:${key2}`;
|
|
393
|
+
let activePool = pool;
|
|
394
|
+
let ownsPool = false;
|
|
395
|
+
let activeClient = client;
|
|
396
|
+
let released = false;
|
|
397
|
+
try {
|
|
398
|
+
if (!activeClient) {
|
|
399
|
+
if (!activePool) {
|
|
400
|
+
if (!connectionString) {
|
|
401
|
+
throw new Error(
|
|
402
|
+
"connectionString is required when a Postgres Slack Socket Mode lock client or pool is not provided"
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
const Pool = await importPostgresPoolConstructor();
|
|
406
|
+
activePool = new Pool({ connectionString });
|
|
407
|
+
ownsPool = true;
|
|
408
|
+
}
|
|
409
|
+
activeClient = await activePool.connect();
|
|
410
|
+
}
|
|
411
|
+
const result = await activeClient.query(
|
|
412
|
+
"SELECT pg_try_advisory_lock($1::integer, $2::integer) AS acquired",
|
|
413
|
+
[key1, key2]
|
|
414
|
+
);
|
|
415
|
+
if (!result.rows[0]?.acquired) {
|
|
416
|
+
if (activeClient !== client) {
|
|
417
|
+
activeClient.release?.();
|
|
418
|
+
activeClient = void 0;
|
|
419
|
+
}
|
|
420
|
+
if (ownsPool) {
|
|
421
|
+
await activePool?.end?.();
|
|
422
|
+
activePool = void 0;
|
|
423
|
+
}
|
|
424
|
+
throw new Error(
|
|
425
|
+
`Another Slack Socket Mode process appears to hold the Postgres advisory lock (${lockPath})`
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
logger?.info?.("Slack Socket Mode Postgres lock acquired", {
|
|
429
|
+
lockPath,
|
|
430
|
+
tokenHash
|
|
431
|
+
});
|
|
432
|
+
async function release() {
|
|
433
|
+
if (released) {
|
|
434
|
+
return;
|
|
435
|
+
}
|
|
436
|
+
released = true;
|
|
437
|
+
try {
|
|
438
|
+
const releaseResult = await activeClient?.query(
|
|
439
|
+
"SELECT pg_advisory_unlock($1::integer, $2::integer) AS released",
|
|
440
|
+
[key1, key2]
|
|
441
|
+
);
|
|
442
|
+
if (releaseResult?.rows[0]?.released === false) {
|
|
443
|
+
logger?.warn?.("Slack Socket Mode Postgres lock was not held", {
|
|
444
|
+
lockPath
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
logger?.debug?.("Slack Socket Mode Postgres lock released", {
|
|
448
|
+
lockPath
|
|
449
|
+
});
|
|
450
|
+
} finally {
|
|
451
|
+
if (activeClient !== client) {
|
|
452
|
+
activeClient?.release?.();
|
|
453
|
+
}
|
|
454
|
+
if (ownsPool) {
|
|
455
|
+
await activePool?.end?.();
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
return {
|
|
460
|
+
path: lockPath,
|
|
461
|
+
close: release,
|
|
462
|
+
release
|
|
463
|
+
};
|
|
464
|
+
} catch (error) {
|
|
465
|
+
if (activeClient && activeClient !== client) {
|
|
466
|
+
activeClient.release?.();
|
|
467
|
+
}
|
|
468
|
+
if (ownsPool) {
|
|
469
|
+
await activePool?.end?.();
|
|
470
|
+
}
|
|
471
|
+
throw error;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
function hashValue(value) {
|
|
475
|
+
return crypto2.createHash("sha256").update(value).digest("hex").slice(0, 16);
|
|
476
|
+
}
|
|
477
|
+
function advisoryLockKeys(value) {
|
|
478
|
+
const digest = crypto2.createHash("sha256").update(value).digest();
|
|
479
|
+
return [digest.readInt32BE(0), digest.readInt32BE(4)];
|
|
480
|
+
}
|
|
481
|
+
async function importPostgresPoolConstructor() {
|
|
482
|
+
const dynamicImport = new Function(
|
|
483
|
+
"specifier",
|
|
484
|
+
"return import(specifier)"
|
|
485
|
+
);
|
|
486
|
+
try {
|
|
487
|
+
const pg = await dynamicImport("pg");
|
|
488
|
+
return pg.Pool;
|
|
489
|
+
} catch (error) {
|
|
490
|
+
throw new Error(
|
|
491
|
+
`The "pg" package is required when using connectionString with acquireSlackSocketModePostgresLock. Install pg or pass a pool/client. ${formatImportError(
|
|
492
|
+
error
|
|
493
|
+
)}`
|
|
494
|
+
);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
function formatImportError(error) {
|
|
498
|
+
return error instanceof Error ? error.message : String(error);
|
|
499
|
+
}
|
|
500
|
+
|
|
366
501
|
// src/bolt/socket-runtime.ts
|
|
367
502
|
var DEFAULT_CLIENT_PING_TIMEOUT_MS = 3e4;
|
|
368
503
|
var DEFAULT_RESTART_GUARD_MAX_WARNINGS = 3;
|
|
@@ -659,6 +794,32 @@ var JsonFileSlackInstallationStore = class {
|
|
|
659
794
|
function createJsonFileSlackInstallationStore(filePath) {
|
|
660
795
|
return new JsonFileSlackInstallationStore(filePath);
|
|
661
796
|
}
|
|
797
|
+
function createSlackInstallationStoreAuthorize(store) {
|
|
798
|
+
return async (source) => {
|
|
799
|
+
const installation = await store.fetchInstallation({
|
|
800
|
+
teamId: source.teamId,
|
|
801
|
+
enterpriseId: source.enterpriseId,
|
|
802
|
+
userId: source.userId,
|
|
803
|
+
conversationId: source.conversationId,
|
|
804
|
+
isEnterpriseInstall: source.isEnterpriseInstall
|
|
805
|
+
});
|
|
806
|
+
const botToken = installation.bot?.token;
|
|
807
|
+
if (!botToken) {
|
|
808
|
+
throw new Error(
|
|
809
|
+
"Slack OAuth installation is missing a bot token. Reinstall the Slack app with bot scopes."
|
|
810
|
+
);
|
|
811
|
+
}
|
|
812
|
+
return {
|
|
813
|
+
botToken,
|
|
814
|
+
botId: installation.bot?.id,
|
|
815
|
+
botUserId: installation.bot?.userId,
|
|
816
|
+
teamId: installation.team?.id ?? source.teamId,
|
|
817
|
+
enterpriseId: installation.enterprise?.id ?? source.enterpriseId,
|
|
818
|
+
userId: installation.user.id,
|
|
819
|
+
userToken: installation.user.token
|
|
820
|
+
};
|
|
821
|
+
};
|
|
822
|
+
}
|
|
662
823
|
function getSlackInstallationKey(input) {
|
|
663
824
|
if (input.isEnterpriseInstall && "enterprise" in input && input.enterprise?.id) {
|
|
664
825
|
return `enterprise:${input.enterprise.id}`;
|
|
@@ -683,10 +844,12 @@ function isNodeErrorCode(error, code) {
|
|
|
683
844
|
export {
|
|
684
845
|
InMemorySlackInstallationStore,
|
|
685
846
|
JsonFileSlackInstallationStore,
|
|
847
|
+
acquireSlackSocketModePostgresLock,
|
|
686
848
|
acquireSlackSocketModeProcessLock,
|
|
687
849
|
createInMemorySlackInstallationStore,
|
|
688
850
|
createJsonFileSlackInstallationStore,
|
|
689
851
|
createSlackBoltApp,
|
|
852
|
+
createSlackInstallationStoreAuthorize,
|
|
690
853
|
createSlackSdkLogger,
|
|
691
854
|
createSlackSocketBoltApp,
|
|
692
855
|
createSlackSocketModeRestartGuard,
|