@mastra/factory 0.4.0 → 0.5.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +7 -4
- package/dist/factory.js.map +1 -1
- package/dist/integrations/base.d.ts +43 -5
- package/dist/integrations/base.d.ts.map +1 -1
- package/dist/integrations/github/webhook.d.ts +25 -5
- package/dist/integrations/github/webhook.d.ts.map +1 -1
- package/dist/integrations/github/webhook.js +13 -4
- package/dist/integrations/github/webhook.js.map +1 -1
- package/dist/integrations/platform/github/event-worker.d.ts +2 -7
- package/dist/integrations/platform/github/event-worker.d.ts.map +1 -1
- package/dist/integrations/platform/github/event-worker.js +1 -0
- package/dist/integrations/platform/github/event-worker.js.map +1 -1
- package/dist/integrations/slack/connect-route.d.ts +46 -0
- package/dist/integrations/slack/connect-route.d.ts.map +1 -0
- package/dist/integrations/slack/connect-route.js +186 -0
- package/dist/integrations/slack/connect-route.js.map +1 -0
- package/dist/integrations/slack/integration.d.ts +62 -0
- package/dist/integrations/slack/integration.d.ts.map +1 -0
- package/dist/integrations/slack/integration.js +67 -0
- package/dist/integrations/slack/integration.js.map +1 -0
- package/dist/integrations/slack/slack.d.ts +273 -0
- package/dist/integrations/slack/slack.d.ts.map +1 -0
- package/dist/integrations/slack/slack.js +406 -0
- package/dist/integrations/slack/slack.js.map +1 -0
- package/dist/routes/surface.d.ts +6 -0
- package/dist/routes/surface.d.ts.map +1 -1
- package/dist/routes/surface.js +28 -1
- package/dist/routes/surface.js.map +1 -1
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +2 -1
- package/dist/workspace.js.map +1 -1
- package/package.json +5 -3
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `SlackIntegration` — Slack as a `FactoryIntegration`.
|
|
3
|
+
*
|
|
4
|
+
* Slack contributes two things to a factory: the chat channels that carry
|
|
5
|
+
* inbound messages into agent runs (`channels()`), and the browser-facing
|
|
6
|
+
* account-link routes that bind a Slack sender to a Mastra tenant (`routes()`).
|
|
7
|
+
* Both used to be assembled by hand in the deploy entry, which had to reach
|
|
8
|
+
* into the prepared agent controller by string key, resolve storage domains
|
|
9
|
+
* itself, mint its own state signer, and splice routes onto the factory's
|
|
10
|
+
* assembled server config. Implementing the integration interface instead means
|
|
11
|
+
* the factory does all of that the same way it already does for GitHub and
|
|
12
|
+
* Linear, and the entry's job shrinks to reading Slack's env vars once.
|
|
13
|
+
*
|
|
14
|
+
* Slack ships as a factory built-in alongside GitHub and Linear (it originally
|
|
15
|
+
* lived in `mastracode/web` to keep `@mastra/slack`/`chat` out of this package;
|
|
16
|
+
* the team reversed that so `create-factory` consumers get Slack channels out
|
|
17
|
+
* of the box). The integration interface remains open — third parties still add
|
|
18
|
+
* capabilities by implementing `FactoryIntegration` from outside the package.
|
|
19
|
+
*/
|
|
20
|
+
import type { ApiRoute } from '@mastra/core/server';
|
|
21
|
+
import type { FactoryChannelsConfig, FactoryIntegration, IntegrationContext } from '../base.js';
|
|
22
|
+
/**
|
|
23
|
+
* Slack app credentials, read from env ONCE by the deploy entry. `signingSecret`
|
|
24
|
+
* is required because the Slack adapter validates it at construction — an
|
|
25
|
+
* integration constructed without it would throw during `prepare()` rather than
|
|
26
|
+
* reporting itself unconfigured.
|
|
27
|
+
*/
|
|
28
|
+
export interface SlackIntegrationConfig {
|
|
29
|
+
/** Verifies inbound Slack request signatures. Required. */
|
|
30
|
+
signingSecret: string;
|
|
31
|
+
/** Bot token used to post replies and ephemeral cards. */
|
|
32
|
+
botToken?: string;
|
|
33
|
+
/**
|
|
34
|
+
* OAuth client credentials. Required for account linking: "Sign in with
|
|
35
|
+
* Slack" (OIDC) is the only flow that writes a link, so without these the
|
|
36
|
+
* settings surface reports `canConnect: false` and no sender can link.
|
|
37
|
+
*/
|
|
38
|
+
clientId?: string;
|
|
39
|
+
clientSecret?: string;
|
|
40
|
+
/**
|
|
41
|
+
* HTTPS origin Slack redirects back to. Slack requires HTTPS, so locally this
|
|
42
|
+
* is the tunnel origin rather than the app's own public URL.
|
|
43
|
+
*/
|
|
44
|
+
oidcRedirectBaseUrl?: string;
|
|
45
|
+
/** SPA origin the post-connect redirect returns to. */
|
|
46
|
+
uiOrigin?: string;
|
|
47
|
+
}
|
|
48
|
+
export declare class SlackIntegration implements FactoryIntegration {
|
|
49
|
+
#private;
|
|
50
|
+
readonly id = "slack";
|
|
51
|
+
/**
|
|
52
|
+
* The OIDC connect flow round-trips a signed `state` through Slack, so the
|
|
53
|
+
* replica handling the callback must be able to verify a state a different
|
|
54
|
+
* replica signed.
|
|
55
|
+
*/
|
|
56
|
+
readonly requiresStableStateSigner = true;
|
|
57
|
+
constructor(config: SlackIntegrationConfig);
|
|
58
|
+
channels(ctx: IntegrationContext): FactoryChannelsConfig;
|
|
59
|
+
routes(ctx: IntegrationContext): ApiRoute[];
|
|
60
|
+
diagnostics(): Record<string, unknown>;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=integration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integration.d.ts","sourceRoot":"","sources":["../../../src/integrations/slack/integration.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,KAAK,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAKhG;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC,2DAA2D;IAC3D,aAAa,EAAE,MAAM,CAAC;IACtB,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,uDAAuD;IACvD,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,gBAAiB,YAAW,kBAAkB;;IACzD,QAAQ,CAAC,EAAE,WAAW;IACtB;;;;OAIG;IACH,QAAQ,CAAC,yBAAyB,QAAQ;gBAU9B,MAAM,EAAE,sBAAsB;IAS1C,QAAQ,CAAC,GAAG,EAAE,kBAAkB,GAAG,qBAAqB;IAmBxD,MAAM,CAAC,GAAG,EAAE,kBAAkB,GAAG,QAAQ,EAAE;IAc3C,WAAW,IAAI,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CASvC"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { createSlackConnectRoutes } from "./connect-route.js";
|
|
2
|
+
import { adaptSourceControlOwner, createSlackChannelsConfig } from "./slack.js";
|
|
3
|
+
//#region src/integrations/slack/integration.ts
|
|
4
|
+
var SlackIntegration = class {
|
|
5
|
+
id = "slack";
|
|
6
|
+
/**
|
|
7
|
+
* The OIDC connect flow round-trips a signed `state` through Slack, so the
|
|
8
|
+
* replica handling the callback must be able to verify a state a different
|
|
9
|
+
* replica signed.
|
|
10
|
+
*/
|
|
11
|
+
requiresStableStateSigner = true;
|
|
12
|
+
#config;
|
|
13
|
+
/**
|
|
14
|
+
* Whether `channels()` found a source-control owner on the context and wired
|
|
15
|
+
* repo-backed sessions. Set at the channels() attach path, which runs once at
|
|
16
|
+
* boot before diagnostics are served.
|
|
17
|
+
*/
|
|
18
|
+
#repoBackedSessions = false;
|
|
19
|
+
constructor(config) {
|
|
20
|
+
if (!config.signingSecret) throw new Error("SlackIntegration: 'signingSecret' is required — Slack cannot verify inbound requests without it.");
|
|
21
|
+
this.#config = config;
|
|
22
|
+
}
|
|
23
|
+
channels(ctx) {
|
|
24
|
+
const sourceControlOwner = ctx.storage.sourceControlOwner;
|
|
25
|
+
this.#repoBackedSessions = Boolean(sourceControlOwner);
|
|
26
|
+
return createSlackChannelsConfig({
|
|
27
|
+
slack: {
|
|
28
|
+
clientId: this.#config.clientId,
|
|
29
|
+
clientSecret: this.#config.clientSecret,
|
|
30
|
+
signingSecret: this.#config.signingSecret,
|
|
31
|
+
botToken: this.#config.botToken
|
|
32
|
+
},
|
|
33
|
+
accountLinks: ctx.storage.channelIdentity,
|
|
34
|
+
projects: ctx.storage.projects,
|
|
35
|
+
sourceControl: sourceControlOwner ? adaptSourceControlOwner(sourceControlOwner) : void 0,
|
|
36
|
+
workItems: ctx.rules?.workItems
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
routes(ctx) {
|
|
40
|
+
const { clientId, clientSecret, oidcRedirectBaseUrl, uiOrigin } = this.#config;
|
|
41
|
+
return createSlackConnectRoutes({
|
|
42
|
+
auth: ctx.auth,
|
|
43
|
+
accountLinks: ctx.storage.channelIdentity,
|
|
44
|
+
tenantStateSigner: ctx.stateSigner,
|
|
45
|
+
oidc: clientId && clientSecret && oidcRedirectBaseUrl ? {
|
|
46
|
+
clientId,
|
|
47
|
+
clientSecret,
|
|
48
|
+
redirectBaseUrl: oidcRedirectBaseUrl,
|
|
49
|
+
uiOrigin
|
|
50
|
+
} : void 0,
|
|
51
|
+
projects: ctx.storage.projects
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
diagnostics() {
|
|
55
|
+
const { clientId, clientSecret, botToken, oidcRedirectBaseUrl } = this.#config;
|
|
56
|
+
return {
|
|
57
|
+
configured: true,
|
|
58
|
+
botTokenConfigured: Boolean(botToken),
|
|
59
|
+
oidcConfigured: Boolean(clientId && clientSecret && oidcRedirectBaseUrl),
|
|
60
|
+
repoBackedSessions: this.#repoBackedSessions
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
//#endregion
|
|
65
|
+
export { SlackIntegration };
|
|
66
|
+
|
|
67
|
+
//# sourceMappingURL=integration.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"integration.js","names":["#config","#repoBackedSessions"],"sources":["../../../src/integrations/slack/integration.ts"],"sourcesContent":["/**\n * `SlackIntegration` — Slack as a `FactoryIntegration`.\n *\n * Slack contributes two things to a factory: the chat channels that carry\n * inbound messages into agent runs (`channels()`), and the browser-facing\n * account-link routes that bind a Slack sender to a Mastra tenant (`routes()`).\n * Both used to be assembled by hand in the deploy entry, which had to reach\n * into the prepared agent controller by string key, resolve storage domains\n * itself, mint its own state signer, and splice routes onto the factory's\n * assembled server config. Implementing the integration interface instead means\n * the factory does all of that the same way it already does for GitHub and\n * Linear, and the entry's job shrinks to reading Slack's env vars once.\n *\n * Slack ships as a factory built-in alongside GitHub and Linear (it originally\n * lived in `mastracode/web` to keep `@mastra/slack`/`chat` out of this package;\n * the team reversed that so `create-factory` consumers get Slack channels out\n * of the box). The integration interface remains open — third parties still add\n * capabilities by implementing `FactoryIntegration` from outside the package.\n */\n\nimport type { ApiRoute } from '@mastra/core/server';\n\nimport type { FactoryChannelsConfig, FactoryIntegration, IntegrationContext } from '../base.js';\n\nimport { createSlackConnectRoutes } from './connect-route.js';\nimport { createSlackChannelsConfig, adaptSourceControlOwner } from './slack.js';\n\n/**\n * Slack app credentials, read from env ONCE by the deploy entry. `signingSecret`\n * is required because the Slack adapter validates it at construction — an\n * integration constructed without it would throw during `prepare()` rather than\n * reporting itself unconfigured.\n */\nexport interface SlackIntegrationConfig {\n /** Verifies inbound Slack request signatures. Required. */\n signingSecret: string;\n /** Bot token used to post replies and ephemeral cards. */\n botToken?: string;\n /**\n * OAuth client credentials. Required for account linking: \"Sign in with\n * Slack\" (OIDC) is the only flow that writes a link, so without these the\n * settings surface reports `canConnect: false` and no sender can link.\n */\n clientId?: string;\n clientSecret?: string;\n /**\n * HTTPS origin Slack redirects back to. Slack requires HTTPS, so locally this\n * is the tunnel origin rather than the app's own public URL.\n */\n oidcRedirectBaseUrl?: string;\n /** SPA origin the post-connect redirect returns to. */\n uiOrigin?: string;\n}\n\nexport class SlackIntegration implements FactoryIntegration {\n readonly id = 'slack';\n /**\n * The OIDC connect flow round-trips a signed `state` through Slack, so the\n * replica handling the callback must be able to verify a state a different\n * replica signed.\n */\n readonly requiresStableStateSigner = true;\n\n readonly #config: SlackIntegrationConfig;\n /**\n * Whether `channels()` found a source-control owner on the context and wired\n * repo-backed sessions. Set at the channels() attach path, which runs once at\n * boot before diagnostics are served.\n */\n #repoBackedSessions = false;\n\n constructor(config: SlackIntegrationConfig) {\n if (!config.signingSecret) {\n throw new Error(\n \"SlackIntegration: 'signingSecret' is required — Slack cannot verify inbound requests without it.\",\n );\n }\n this.#config = config;\n }\n\n channels(ctx: IntegrationContext): FactoryChannelsConfig {\n // Repo-backed sessions come from the factory's source-control owner\n // (GitHub, when registered) — no config-level wiring by the entry.\n const sourceControlOwner = ctx.storage.sourceControlOwner;\n this.#repoBackedSessions = Boolean(sourceControlOwner);\n return createSlackChannelsConfig({\n slack: {\n clientId: this.#config.clientId,\n clientSecret: this.#config.clientSecret,\n signingSecret: this.#config.signingSecret,\n botToken: this.#config.botToken,\n },\n accountLinks: ctx.storage.channelIdentity,\n projects: ctx.storage.projects,\n sourceControl: sourceControlOwner ? adaptSourceControlOwner(sourceControlOwner) : undefined,\n workItems: ctx.rules?.workItems,\n });\n }\n\n routes(ctx: IntegrationContext): ApiRoute[] {\n const { clientId, clientSecret, oidcRedirectBaseUrl, uiOrigin } = this.#config;\n return createSlackConnectRoutes({\n auth: ctx.auth,\n accountLinks: ctx.storage.channelIdentity,\n tenantStateSigner: ctx.stateSigner,\n oidc:\n clientId && clientSecret && oidcRedirectBaseUrl\n ? { clientId, clientSecret, redirectBaseUrl: oidcRedirectBaseUrl, uiOrigin }\n : undefined,\n projects: ctx.storage.projects,\n });\n }\n\n diagnostics(): Record<string, unknown> {\n const { clientId, clientSecret, botToken, oidcRedirectBaseUrl } = this.#config;\n return {\n configured: true,\n botTokenConfigured: Boolean(botToken),\n oidcConfigured: Boolean(clientId && clientSecret && oidcRedirectBaseUrl),\n repoBackedSessions: this.#repoBackedSessions,\n };\n }\n}\n"],"mappings":";;;AAsDA,IAAa,mBAAb,MAA4D;CAC1D,KAAc;;;;;;CAMd,4BAAqC;CAErC;;;;;;CAMA,sBAAsB;CAEtB,YAAY,QAAgC;EAC1C,IAAI,CAAC,OAAO,eACV,MAAM,IAAI,MACR,kGACF;EAEF,KAAKA,UAAU;CACjB;CAEA,SAAS,KAAgD;EAGvD,MAAM,qBAAqB,IAAI,QAAQ;EACvC,KAAKC,sBAAsB,QAAQ,kBAAkB;EACrD,OAAO,0BAA0B;GAC/B,OAAO;IACL,UAAU,KAAKD,QAAQ;IACvB,cAAc,KAAKA,QAAQ;IAC3B,eAAe,KAAKA,QAAQ;IAC5B,UAAU,KAAKA,QAAQ;GACzB;GACA,cAAc,IAAI,QAAQ;GAC1B,UAAU,IAAI,QAAQ;GACtB,eAAe,qBAAqB,wBAAwB,kBAAkB,IAAI,KAAA;GAClF,WAAW,IAAI,OAAO;EACxB,CAAC;CACH;CAEA,OAAO,KAAqC;EAC1C,MAAM,EAAE,UAAU,cAAc,qBAAqB,aAAa,KAAKA;EACvE,OAAO,yBAAyB;GAC9B,MAAM,IAAI;GACV,cAAc,IAAI,QAAQ;GAC1B,mBAAmB,IAAI;GACvB,MACE,YAAY,gBAAgB,sBACxB;IAAE;IAAU;IAAc,iBAAiB;IAAqB;GAAS,IACzE,KAAA;GACN,UAAU,IAAI,QAAQ;EACxB,CAAC;CACH;CAEA,cAAuC;EACrC,MAAM,EAAE,UAAU,cAAc,UAAU,wBAAwB,KAAKA;EACvE,OAAO;GACL,YAAY;GACZ,oBAAoB,QAAQ,QAAQ;GACpC,gBAAgB,QAAQ,YAAY,gBAAgB,mBAAmB;GACvE,oBAAoB,KAAKC;EAC3B;CACF;AACF"}
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import type { ChannelHandler, ChannelHandlers, ResolveResourceId, ResolveThreadId } from '@mastra/core/channels';
|
|
2
|
+
import type { ChannelAccountLink, ChannelAccountLinkKey, ChannelIdentityStorage } from '../../storage/domains/channel-identity/base.js';
|
|
3
|
+
import type { FactoryProjectsStorage } from '../../storage/domains/projects/base.js';
|
|
4
|
+
import type { WorkItemsStorage } from '../../storage/domains/work-items/base.js';
|
|
5
|
+
import type { FactoryChannelsConfig } from '../base.js';
|
|
6
|
+
type HandlerThread = Parameters<ChannelHandler>[0];
|
|
7
|
+
type HandlerMessage = Parameters<ChannelHandler>[1];
|
|
8
|
+
/** Dependencies the Slack channel handlers close over, injected from the web entry. */
|
|
9
|
+
interface SlackChannelDeps {
|
|
10
|
+
/**
|
|
11
|
+
* The factory's reverse-index store mapping a Slack sender to a Mastra
|
|
12
|
+
* tenant. When provided, inbound messages from an unlinked sender are not
|
|
13
|
+
* dispatched — the run only proceeds (with the sender's tenant stamped on
|
|
14
|
+
* the request context) once they've linked their account. Unlinked senders
|
|
15
|
+
* get an ephemeral "connect your account" card instead.
|
|
16
|
+
*/
|
|
17
|
+
accountLinks?: ChannelIdentityStorage;
|
|
18
|
+
/**
|
|
19
|
+
* Factory projects domain. When provided (alongside `accountLinks`), a
|
|
20
|
+
* linked sender's run must also resolve to a Factory project before it
|
|
21
|
+
* dispatches: their link's default factory, else their tenant's only
|
|
22
|
+
* factory (stamped back onto the link), else an ephemeral "pick a default
|
|
23
|
+
* factory" card and no run. Unset → no factory routing (runs dispatch as
|
|
24
|
+
* before).
|
|
25
|
+
*/
|
|
26
|
+
projects?: FactoryProjectsStorage;
|
|
27
|
+
/**
|
|
28
|
+
* Narrow source-control surface used to make new Slack threads repo-backed:
|
|
29
|
+
* when the sender is linked and their factory has a repository, the thread's
|
|
30
|
+
* resourceId becomes a Factory user-session id (repo cloned on a
|
|
31
|
+
* `slack/{threadTs}` branch) instead of the chat-only `channel:...` id.
|
|
32
|
+
* Absent (no source-control integration registered) → chat-only sessions as
|
|
33
|
+
* before.
|
|
34
|
+
*/
|
|
35
|
+
sourceControl?: SlackSourceControl;
|
|
36
|
+
/**
|
|
37
|
+
* Factory work-items domain. When provided, a dispatched new-session thread
|
|
38
|
+
* (DM or mention) upserts a Work-board card in Building (`execute`) carrying
|
|
39
|
+
* the Slack thread as its external source and binding the repo-backed
|
|
40
|
+
* session. Best-effort — a failure never blocks the run. Unset → no card.
|
|
41
|
+
*/
|
|
42
|
+
workItems?: WorkItemsStorage;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The slice of the source-control owner's storage the Slack wiring needs.
|
|
46
|
+
* Structural (not the storage types themselves) so slack.ts stays decoupled
|
|
47
|
+
* from the owning integration's module graph and tests can stub it.
|
|
48
|
+
*/
|
|
49
|
+
export interface SlackSourceControl {
|
|
50
|
+
/**
|
|
51
|
+
* Resolve the factory's linked repository — first repo on the factory's
|
|
52
|
+
* source-control connection, the same single-repo assumption the web kickoff
|
|
53
|
+
* makes.
|
|
54
|
+
*/
|
|
55
|
+
resolveProjectRepository(args: {
|
|
56
|
+
orgId: string;
|
|
57
|
+
factoryProjectId: string;
|
|
58
|
+
}): Promise<{
|
|
59
|
+
projectRepositoryId: string;
|
|
60
|
+
baseBranch: string;
|
|
61
|
+
} | null>;
|
|
62
|
+
/** Look up an existing user session for a repo branch (idempotent reuse). */
|
|
63
|
+
getSessionForBranch(args: {
|
|
64
|
+
projectRepositoryId: string;
|
|
65
|
+
userId: string;
|
|
66
|
+
branch: string;
|
|
67
|
+
}): Promise<{
|
|
68
|
+
sessionId: string;
|
|
69
|
+
} | null>;
|
|
70
|
+
/** Create the durable user-session row the workspace factory materializes. */
|
|
71
|
+
createSession(args: {
|
|
72
|
+
projectRepositoryId: string;
|
|
73
|
+
orgId: string;
|
|
74
|
+
userId: string;
|
|
75
|
+
branch: string;
|
|
76
|
+
baseBranch: string;
|
|
77
|
+
}): Promise<{
|
|
78
|
+
sessionId: string;
|
|
79
|
+
}>;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Structural slice of the source-control owner's storage handle
|
|
83
|
+
* (`IntegrationContext.storage.sourceControlOwner`, a `SourceControlStorageHandle`)
|
|
84
|
+
* the adapter reads. Structural so slack.ts stays decoupled from the storage
|
|
85
|
+
* module graph and tests can stub it. The handle is bound during
|
|
86
|
+
* `factory.prepare()`; all access here is lazy (request time).
|
|
87
|
+
*/
|
|
88
|
+
interface SourceControlOwnerSlice {
|
|
89
|
+
integrationId: string;
|
|
90
|
+
connections: {
|
|
91
|
+
list(args: {
|
|
92
|
+
orgId: string;
|
|
93
|
+
factoryProjectId: string;
|
|
94
|
+
}): Promise<Array<{
|
|
95
|
+
id: string;
|
|
96
|
+
integrationId: string;
|
|
97
|
+
}>>;
|
|
98
|
+
};
|
|
99
|
+
projectRepositories: {
|
|
100
|
+
list(args: {
|
|
101
|
+
orgId: string;
|
|
102
|
+
connectionId: string;
|
|
103
|
+
}): Promise<Array<{
|
|
104
|
+
id: string;
|
|
105
|
+
repositoryId: string;
|
|
106
|
+
branch?: string | null;
|
|
107
|
+
}>>;
|
|
108
|
+
};
|
|
109
|
+
repositories: {
|
|
110
|
+
get(args: {
|
|
111
|
+
orgId: string;
|
|
112
|
+
id: string;
|
|
113
|
+
}): Promise<{
|
|
114
|
+
defaultBranch: string;
|
|
115
|
+
} | null>;
|
|
116
|
+
};
|
|
117
|
+
sessions: {
|
|
118
|
+
getForBranch(args: {
|
|
119
|
+
projectRepositoryId: string;
|
|
120
|
+
userId: string;
|
|
121
|
+
branch: string;
|
|
122
|
+
}): Promise<{
|
|
123
|
+
sessionId: string;
|
|
124
|
+
} | null>;
|
|
125
|
+
create(input: {
|
|
126
|
+
sessionId: string;
|
|
127
|
+
projectRepositoryId: string;
|
|
128
|
+
orgId: string;
|
|
129
|
+
userId: string;
|
|
130
|
+
branch: string;
|
|
131
|
+
baseBranch: string;
|
|
132
|
+
}): Promise<{
|
|
133
|
+
sessionId: string;
|
|
134
|
+
}>;
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Adapt the source-control owner's storage handle into the
|
|
139
|
+
* {@link SlackSourceControl} surface. Nothing here is GitHub-specific — the
|
|
140
|
+
* owner is whichever integration owns source control, matched by its own
|
|
141
|
+
* `integrationId`. Repo resolution mirrors the factory's own
|
|
142
|
+
* `ensureFactoryRuleSession`: the owner's connection on the factory → its
|
|
143
|
+
* first linked repository → pinned branch or repo default as the base.
|
|
144
|
+
*/
|
|
145
|
+
export declare function adaptSourceControlOwner(owner: SourceControlOwnerSlice): SlackSourceControl;
|
|
146
|
+
/** Outcome of the sender-link gate for one inbound message. */
|
|
147
|
+
type LinkedSenderResult =
|
|
148
|
+
/** Gating not configured — dispatch as before account linking existed. */
|
|
149
|
+
{
|
|
150
|
+
status: 'ungated';
|
|
151
|
+
}
|
|
152
|
+
/** Sender unlinked — Connect card posted (when possible), do not dispatch. */
|
|
153
|
+
| {
|
|
154
|
+
status: 'blocked';
|
|
155
|
+
}
|
|
156
|
+
/** Sender linked — their tenant plus the sender key the link lives under. */
|
|
157
|
+
| {
|
|
158
|
+
status: 'linked';
|
|
159
|
+
link: ChannelAccountLink;
|
|
160
|
+
key: ChannelAccountLinkKey;
|
|
161
|
+
};
|
|
162
|
+
/**
|
|
163
|
+
* Resolve the sender's account link, posting an ephemeral "connect your
|
|
164
|
+
* account" card (visible only to the sender) linking into the web UI's
|
|
165
|
+
* Slack-connect flow when they're unlinked.
|
|
166
|
+
*/
|
|
167
|
+
export declare function resolveLinkedSender({ thread, message, accountLinks, }: {
|
|
168
|
+
thread: HandlerThread;
|
|
169
|
+
message: HandlerMessage;
|
|
170
|
+
accountLinks?: ChannelIdentityStorage;
|
|
171
|
+
}): Promise<LinkedSenderResult>;
|
|
172
|
+
/** Outcome of factory routing for one linked sender's inbound message. */
|
|
173
|
+
type FactoryRouteResult =
|
|
174
|
+
/** Factory routing not configured — dispatch without a factory. */
|
|
175
|
+
{
|
|
176
|
+
status: 'ungated';
|
|
177
|
+
}
|
|
178
|
+
/** No factory resolved — prompt card posted (when possible), do not dispatch. */
|
|
179
|
+
| {
|
|
180
|
+
status: 'blocked';
|
|
181
|
+
}
|
|
182
|
+
/** The Factory project this sender's runs route to. */
|
|
183
|
+
| {
|
|
184
|
+
status: 'resolved';
|
|
185
|
+
factoryProjectId: string;
|
|
186
|
+
slackWorkItemsEnabled: boolean;
|
|
187
|
+
};
|
|
188
|
+
/**
|
|
189
|
+
* Decide which Factory project a linked sender's run belongs to:
|
|
190
|
+
*
|
|
191
|
+
* 1. The link's `defaultFactoryProjectId`, when it still exists (a stale id —
|
|
192
|
+
* deleted factory — falls through as if unset).
|
|
193
|
+
* 2. Else, the tenant's only factory, stamped back onto the link so it shows
|
|
194
|
+
* up (and stays editable) in Connected Accounts settings.
|
|
195
|
+
* 3. Else — zero or several factories — an ephemeral "pick a default factory"
|
|
196
|
+
* card deep-linking to settings, and the run is blocked.
|
|
197
|
+
*/
|
|
198
|
+
export declare function resolveFactoryForLink({ thread, message, link, key, accountLinks, projects, }: {
|
|
199
|
+
thread: HandlerThread;
|
|
200
|
+
message: HandlerMessage;
|
|
201
|
+
link: ChannelAccountLink;
|
|
202
|
+
key: ChannelAccountLinkKey;
|
|
203
|
+
accountLinks: ChannelIdentityStorage;
|
|
204
|
+
projects?: FactoryProjectsStorage;
|
|
205
|
+
}): Promise<FactoryRouteResult>;
|
|
206
|
+
/**
|
|
207
|
+
* Resolve the resourceId for a NEW Slack channel thread. A linked sender whose
|
|
208
|
+
* factory has a repository gets a Factory user-session id — the controller
|
|
209
|
+
* session then materializes the repo sandbox via the factory's dynamic
|
|
210
|
+
* workspace (clone + PAT), the session shows up in the web Sessions list, and
|
|
211
|
+
* View Session deep-links land on the normal workspace route. Everything else
|
|
212
|
+
* (unlinked, unrouted, repo-less, or no source control) keeps the chat-only
|
|
213
|
+
* `defaultResourceId`.
|
|
214
|
+
*
|
|
215
|
+
* Pure lookups only — cards for unlinked/unrouted senders are the dispatch
|
|
216
|
+
* gate's job; this hook must never post.
|
|
217
|
+
*/
|
|
218
|
+
export declare function createChannelResourceIdResolver(deps: SlackChannelDeps): ResolveResourceId;
|
|
219
|
+
/**
|
|
220
|
+
* Thread id for a NEW Slack channel thread. Repo-backed threads take the
|
|
221
|
+
* user-session id AS their thread id, matching the web convention
|
|
222
|
+
* (FactoryStartCoordinator seeds threads with threadId = sessionId) so
|
|
223
|
+
* `/workspaces/{sessionId}/threads/{sessionId}` resolves Slack-created
|
|
224
|
+
* sessions exactly like web-created ones — no `?resourceId=` override needed.
|
|
225
|
+
* Chat-only threads keep the default random id: their `channel:...`
|
|
226
|
+
* resourceId is a memory key, not a unique thread id.
|
|
227
|
+
*/
|
|
228
|
+
export declare const resolveChannelThreadId: ResolveThreadId;
|
|
229
|
+
/**
|
|
230
|
+
* Upsert the Work-board card for a dispatched Slack-thread run. Keyed on the
|
|
231
|
+
* thread via `externalSource` — the work-items domain's unique
|
|
232
|
+
* `(factory_project_id, source_key)` index makes repeat messages reuse the
|
|
233
|
+
* same card, and `reuseMode: 'preserve'` keeps a card a human already dragged
|
|
234
|
+
* across stages untouched. The card lands in Building (`execute`) for every
|
|
235
|
+
* dispatched thread (DM or mention) — there is deliberately no per-origin
|
|
236
|
+
* stage split; smart routing is a follow-up.
|
|
237
|
+
*
|
|
238
|
+
* The session id / branch / threadId and the workspace deep-link are resolved
|
|
239
|
+
* by the caller (which already looked up the internal thread), so this helper
|
|
240
|
+
* just shapes and writes. Best-effort: the run is already dispatched, so a
|
|
241
|
+
* failure logs instead of throwing — work-item creation must never abort a Slack run.
|
|
242
|
+
*/
|
|
243
|
+
export declare function upsertThreadWorkItem({ workItems, thread, message, link, factoryProjectId, session, url, }: {
|
|
244
|
+
workItems: WorkItemsStorage;
|
|
245
|
+
thread: HandlerThread;
|
|
246
|
+
message: HandlerMessage;
|
|
247
|
+
link: ChannelAccountLink;
|
|
248
|
+
factoryProjectId: string;
|
|
249
|
+
/**
|
|
250
|
+
* The repo-backed Factory session to bind under the `chat` role, or
|
|
251
|
+
* `undefined` for a chat-only thread (no Factory session to bind).
|
|
252
|
+
*/
|
|
253
|
+
session?: {
|
|
254
|
+
sessionId: string;
|
|
255
|
+
branch: string;
|
|
256
|
+
threadId: string;
|
|
257
|
+
};
|
|
258
|
+
/** Workspace deep-link to the running session; omitted when no public URL. */
|
|
259
|
+
url?: string;
|
|
260
|
+
}): Promise<void>;
|
|
261
|
+
export declare const createHandlers: (deps: SlackChannelDeps) => ChannelHandlers;
|
|
262
|
+
/** Slack app credentials, passed in explicitly rather than read from env here. */
|
|
263
|
+
interface SlackCredentials {
|
|
264
|
+
clientId?: string;
|
|
265
|
+
clientSecret?: string;
|
|
266
|
+
signingSecret: string;
|
|
267
|
+
botToken?: string;
|
|
268
|
+
}
|
|
269
|
+
export declare function createSlackChannelsConfig(deps: SlackChannelDeps & {
|
|
270
|
+
slack: SlackCredentials;
|
|
271
|
+
}): FactoryChannelsConfig;
|
|
272
|
+
export {};
|
|
273
|
+
//# sourceMappingURL=slack.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"slack.d.ts","sourceRoot":"","sources":["../../../src/integrations/slack/slack.ts"],"names":[],"mappings":"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"}
|