crewly 1.16.0 → 1.17.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.
Files changed (130) hide show
  1. package/README.md +33 -1
  2. package/config/roles/orchestrator/prompt.md +20 -5
  3. package/config/skills/agent/core/calendar-create/SKILL.md +62 -0
  4. package/config/skills/agent/core/calendar-create/execute.sh +91 -0
  5. package/config/skills/agent/core/calendar-create/execute.test.sh +80 -0
  6. package/config/skills/agent/core/calendar-list/SKILL.md +58 -0
  7. package/config/skills/agent/core/calendar-list/execute.sh +88 -0
  8. package/config/skills/agent/core/calendar-list/execute.test.sh +71 -0
  9. package/config/skills/agent/core/gmail-read/SKILL.md +61 -0
  10. package/config/skills/agent/core/gmail-read/execute.sh +62 -0
  11. package/config/skills/agent/core/gmail-read/execute.test.sh +68 -0
  12. package/config/skills/agent/core/gmail-search/SKILL.md +64 -0
  13. package/config/skills/agent/core/gmail-search/execute.sh +70 -0
  14. package/config/skills/agent/core/gmail-search/execute.test.sh +67 -0
  15. package/config/skills/agent/core/gmail-send/SKILL.md +69 -0
  16. package/config/skills/agent/core/gmail-send/execute.sh +114 -0
  17. package/config/skills/agent/core/gmail-send/execute.test.sh +83 -0
  18. package/config/skills/orchestrator/list-missions/SKILL.md +47 -0
  19. package/config/skills/orchestrator/list-missions/execute.sh +34 -0
  20. package/config/skills/orchestrator/list-missions/execute.test.sh +33 -0
  21. package/config/slack-master-app-manifest.json +64 -0
  22. package/dist/backend/backend/src/constants.d.ts +125 -0
  23. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  24. package/dist/backend/backend/src/constants.js +125 -0
  25. package/dist/backend/backend/src/constants.js.map +1 -1
  26. package/dist/backend/backend/src/controllers/cloud/cloud.controller.d.ts.map +1 -1
  27. package/dist/backend/backend/src/controllers/cloud/cloud.controller.js +3 -1
  28. package/dist/backend/backend/src/controllers/cloud/cloud.controller.js.map +1 -1
  29. package/dist/backend/backend/src/controllers/google/google.controller.d.ts +100 -0
  30. package/dist/backend/backend/src/controllers/google/google.controller.d.ts.map +1 -0
  31. package/dist/backend/backend/src/controllers/google/google.controller.js +293 -0
  32. package/dist/backend/backend/src/controllers/google/google.controller.js.map +1 -0
  33. package/dist/backend/backend/src/controllers/google/google.routes.d.ts +26 -0
  34. package/dist/backend/backend/src/controllers/google/google.routes.d.ts.map +1 -0
  35. package/dist/backend/backend/src/controllers/google/google.routes.js +38 -0
  36. package/dist/backend/backend/src/controllers/google/google.routes.js.map +1 -0
  37. package/dist/backend/backend/src/controllers/slack/slack.controller.d.ts.map +1 -1
  38. package/dist/backend/backend/src/controllers/slack/slack.controller.js +209 -2
  39. package/dist/backend/backend/src/controllers/slack/slack.controller.js.map +1 -1
  40. package/dist/backend/backend/src/index.d.ts.map +1 -1
  41. package/dist/backend/backend/src/index.js +39 -8
  42. package/dist/backend/backend/src/index.js.map +1 -1
  43. package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
  44. package/dist/backend/backend/src/routes/api.routes.js +4 -0
  45. package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
  46. package/dist/backend/backend/src/services/cloud/cloud-initializer.d.ts +8 -0
  47. package/dist/backend/backend/src/services/cloud/cloud-initializer.d.ts.map +1 -1
  48. package/dist/backend/backend/src/services/cloud/cloud-initializer.js +25 -0
  49. package/dist/backend/backend/src/services/cloud/cloud-initializer.js.map +1 -1
  50. package/dist/backend/backend/src/services/cloud/cloud-sync.types.d.ts +1 -1
  51. package/dist/backend/backend/src/services/cloud/cloud-sync.types.d.ts.map +1 -1
  52. package/dist/backend/backend/src/services/cloud/cloud-sync.types.js +1 -0
  53. package/dist/backend/backend/src/services/cloud/cloud-sync.types.js.map +1 -1
  54. package/dist/backend/backend/src/services/google/calendar.service.d.ts +98 -0
  55. package/dist/backend/backend/src/services/google/calendar.service.d.ts.map +1 -0
  56. package/dist/backend/backend/src/services/google/calendar.service.js +125 -0
  57. package/dist/backend/backend/src/services/google/calendar.service.js.map +1 -0
  58. package/dist/backend/backend/src/services/google/gmail.service.d.ts +165 -0
  59. package/dist/backend/backend/src/services/google/gmail.service.d.ts.map +1 -0
  60. package/dist/backend/backend/src/services/google/gmail.service.js +296 -0
  61. package/dist/backend/backend/src/services/google/gmail.service.js.map +1 -0
  62. package/dist/backend/backend/src/services/google/google-api.client.d.ts +44 -0
  63. package/dist/backend/backend/src/services/google/google-api.client.d.ts.map +1 -0
  64. package/dist/backend/backend/src/services/google/google-api.client.js +104 -0
  65. package/dist/backend/backend/src/services/google/google-api.client.js.map +1 -0
  66. package/dist/backend/backend/src/services/google/google-workspace-token.service.d.ts +159 -0
  67. package/dist/backend/backend/src/services/google/google-workspace-token.service.d.ts.map +1 -0
  68. package/dist/backend/backend/src/services/google/google-workspace-token.service.js +274 -0
  69. package/dist/backend/backend/src/services/google/google-workspace-token.service.js.map +1 -0
  70. package/dist/backend/backend/src/services/slack/slack-agent-identity.service.d.ts +12 -1
  71. package/dist/backend/backend/src/services/slack/slack-agent-identity.service.d.ts.map +1 -1
  72. package/dist/backend/backend/src/services/slack/slack-agent-identity.service.js +30 -0
  73. package/dist/backend/backend/src/services/slack/slack-agent-identity.service.js.map +1 -1
  74. package/dist/backend/backend/src/services/slack/slack-cloud-config.service.d.ts +186 -0
  75. package/dist/backend/backend/src/services/slack/slack-cloud-config.service.d.ts.map +1 -0
  76. package/dist/backend/backend/src/services/slack/slack-cloud-config.service.js +409 -0
  77. package/dist/backend/backend/src/services/slack/slack-cloud-config.service.js.map +1 -0
  78. package/dist/backend/backend/src/services/slack/slack-initializer.d.ts +95 -1
  79. package/dist/backend/backend/src/services/slack/slack-initializer.d.ts.map +1 -1
  80. package/dist/backend/backend/src/services/slack/slack-initializer.js +284 -4
  81. package/dist/backend/backend/src/services/slack/slack-initializer.js.map +1 -1
  82. package/dist/backend/backend/src/services/slack/slack-instance-registry.service.d.ts +178 -0
  83. package/dist/backend/backend/src/services/slack/slack-instance-registry.service.d.ts.map +1 -0
  84. package/dist/backend/backend/src/services/slack/slack-instance-registry.service.js +392 -0
  85. package/dist/backend/backend/src/services/slack/slack-instance-registry.service.js.map +1 -0
  86. package/dist/backend/backend/src/services/slack/slack.service.d.ts +76 -1
  87. package/dist/backend/backend/src/services/slack/slack.service.d.ts.map +1 -1
  88. package/dist/backend/backend/src/services/slack/slack.service.js +234 -47
  89. package/dist/backend/backend/src/services/slack/slack.service.js.map +1 -1
  90. package/dist/backend/backend/src/services/v3/mission-reminder.service.d.ts +4 -0
  91. package/dist/backend/backend/src/services/v3/mission-reminder.service.d.ts.map +1 -1
  92. package/dist/backend/backend/src/services/v3/mission-reminder.service.js +17 -0
  93. package/dist/backend/backend/src/services/v3/mission-reminder.service.js.map +1 -1
  94. package/dist/backend/backend/src/services/v3/okr-owner-guidance.service.d.ts +116 -0
  95. package/dist/backend/backend/src/services/v3/okr-owner-guidance.service.d.ts.map +1 -0
  96. package/dist/backend/backend/src/services/v3/okr-owner-guidance.service.js +254 -0
  97. package/dist/backend/backend/src/services/v3/okr-owner-guidance.service.js.map +1 -0
  98. package/dist/backend/backend/src/types/slack.types.d.ts +141 -2
  99. package/dist/backend/backend/src/types/slack.types.d.ts.map +1 -1
  100. package/dist/backend/backend/src/types/slack.types.js.map +1 -1
  101. package/dist/backend/backend/src/types/v2/mission.types.d.ts +2 -0
  102. package/dist/backend/backend/src/types/v2/mission.types.d.ts.map +1 -1
  103. package/dist/backend/backend/src/types/v2/mission.types.js.map +1 -1
  104. package/dist/backend/build-info.json +2 -2
  105. package/dist/cli/backend/src/constants.d.ts +125 -0
  106. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  107. package/dist/cli/backend/src/constants.js +125 -0
  108. package/dist/cli/backend/src/constants.js.map +1 -1
  109. package/dist/cli/backend/src/services/cloud/cloud-sync.types.d.ts +1 -1
  110. package/dist/cli/backend/src/services/cloud/cloud-sync.types.d.ts.map +1 -1
  111. package/dist/cli/backend/src/services/cloud/cloud-sync.types.js +1 -0
  112. package/dist/cli/backend/src/services/cloud/cloud-sync.types.js.map +1 -1
  113. package/dist/cli/backend/src/services/slack/slack-agent-identity.service.d.ts +12 -1
  114. package/dist/cli/backend/src/services/slack/slack-agent-identity.service.d.ts.map +1 -1
  115. package/dist/cli/backend/src/services/slack/slack-agent-identity.service.js +30 -0
  116. package/dist/cli/backend/src/services/slack/slack-agent-identity.service.js.map +1 -1
  117. package/dist/cli/backend/src/services/slack/slack.service.d.ts +76 -1
  118. package/dist/cli/backend/src/services/slack/slack.service.d.ts.map +1 -1
  119. package/dist/cli/backend/src/services/slack/slack.service.js +234 -47
  120. package/dist/cli/backend/src/services/slack/slack.service.js.map +1 -1
  121. package/dist/cli/backend/src/types/slack.types.d.ts +141 -2
  122. package/dist/cli/backend/src/types/slack.types.d.ts.map +1 -1
  123. package/dist/cli/backend/src/types/slack.types.js.map +1 -1
  124. package/dist/cli/backend/src/types/v2/mission.types.d.ts +2 -0
  125. package/dist/cli/backend/src/types/v2/mission.types.d.ts.map +1 -1
  126. package/dist/cli/backend/src/types/v2/mission.types.js.map +1 -1
  127. package/frontend/dist/assets/{index-019817e2.js → index-c87332bf.js} +273 -273
  128. package/frontend/dist/assets/{index-a56613e7.css → index-f6b4230e.css} +1 -1
  129. package/frontend/dist/index.html +2 -2
  130. package/package.json +1 -1
@@ -0,0 +1,409 @@
1
+ /**
2
+ * Slack Cloud Config Service — the Cloud-owned Slack setup, cached locally.
3
+ *
4
+ * Slack v3 ("Crewly Cloud owns Slack"): the owner installs the Crewly Slack
5
+ * app once from the portal; every OSS instance signed in to the same Crewly
6
+ * account then gets the workspace bot token, bot user id and the installed
7
+ * per-agent identities from `GET /api/cloud/slack/config` — no manual
8
+ * tokens. This service:
9
+ *
10
+ * - fetches that config on boot and every 10 minutes while signed in;
11
+ * - caches it at `~/.crewly/slack-cloud-config.json` (mode 0600) so a
12
+ * restart without Cloud reachability still connects;
13
+ * - exposes the master bot as a {@link SlackConfig} with the `cloud`
14
+ * transport for `SlackService`, and the agents for
15
+ * `SlackAgentIdentityService`;
16
+ * - notifies listeners when the config appears, changes or goes away, so
17
+ * the initializer can (re)connect without a restart.
18
+ *
19
+ * Source precedence lives in {@link resolveSlackSourceMode}: unset → Cloud
20
+ * wins when both exist (logged once), `CREWLY_SLACK_SOURCE=env` → never
21
+ * touch Cloud, `CREWLY_SLACK_SOURCE=cloud` → never use local tokens.
22
+ *
23
+ * @module services/slack/slack-cloud-config.service
24
+ */
25
+ import * as path from 'path';
26
+ import { promises as fs } from 'fs';
27
+ import { getCrewlyHomePath } from '../core/crewly-home.utils.js';
28
+ import { atomicWriteJson, safeReadJson } from '../../utils/file-io.utils.js';
29
+ import { LoggerService } from '../core/logger.service.js';
30
+ import { SLACK_CLOUD_CONSTANTS } from '../../constants.js';
31
+ import { SlackIdentityCloudError } from './slack-agent-identity.service.js';
32
+ /**
33
+ * Read `CREWLY_SLACK_SOURCE` into a mode. Anything but `env` / `cloud`
34
+ * (case-insensitive) is `auto`.
35
+ *
36
+ * @param env - Environment to read (defaults to `process.env`)
37
+ * @returns The mode
38
+ */
39
+ export function resolveSlackSourceMode(env = process.env) {
40
+ const raw = (env[SLACK_CLOUD_CONSTANTS.SOURCE_ENV_VAR] ?? '').trim().toLowerCase();
41
+ if (raw === 'env' || raw === 'cloud')
42
+ return raw;
43
+ return 'auto';
44
+ }
45
+ /**
46
+ * Service — see module docs.
47
+ */
48
+ export class SlackCloudConfigService {
49
+ logger;
50
+ deps;
51
+ storePath;
52
+ fetchImpl;
53
+ config = null;
54
+ fetchedAt = null;
55
+ loading = null;
56
+ refreshTimer = null;
57
+ lastError = null;
58
+ listeners = new Set();
59
+ constructor(deps) {
60
+ this.deps = deps;
61
+ this.storePath =
62
+ deps.storePath ?? path.join(getCrewlyHomePath(), SLACK_CLOUD_CONSTANTS.CONFIG_CACHE_FILENAME);
63
+ this.fetchImpl = deps.fetchImpl ?? fetch;
64
+ this.logger = LoggerService.getInstance().createComponentLogger('SlackCloudConfig');
65
+ }
66
+ // -------------------------------------------------------------------------
67
+ // Availability / mode
68
+ // -------------------------------------------------------------------------
69
+ /**
70
+ * Whether Cloud can be asked (signed in, url + token known) and the source
71
+ * mode does not forbid it.
72
+ *
73
+ * @returns True when `refresh()` can reach Cloud
74
+ */
75
+ isAvailable() {
76
+ if (this.getSourceMode() === 'env')
77
+ return false;
78
+ return this.deps.cloud.isConnected() && !!this.deps.cloud.getToken() && !!this.deps.cloud.getCloudUrl();
79
+ }
80
+ /**
81
+ * The configured source mode (`CREWLY_SLACK_SOURCE`).
82
+ *
83
+ * @returns `env`, `cloud` or `auto`
84
+ */
85
+ getSourceMode() {
86
+ return resolveSlackSourceMode(this.deps.env ?? process.env);
87
+ }
88
+ // -------------------------------------------------------------------------
89
+ // Cache + Cloud
90
+ // -------------------------------------------------------------------------
91
+ /**
92
+ * Load the cached config from disk once. Never touches Cloud.
93
+ *
94
+ * @returns The cached config or null
95
+ */
96
+ async load() {
97
+ if (this.config)
98
+ return this.config;
99
+ if (!this.loading) {
100
+ this.loading = safeReadJson(this.storePath, null).then((raw) => {
101
+ if (raw && isCloudConfig(raw.config)) {
102
+ this.config = raw.config;
103
+ this.fetchedAt = typeof raw.fetchedAt === 'string' ? raw.fetchedAt : null;
104
+ }
105
+ return this.config;
106
+ });
107
+ }
108
+ return this.loading;
109
+ }
110
+ /**
111
+ * Fetch `GET /api/cloud/slack/config`, update the cache and notify
112
+ * listeners when the config changed. A 404 means the account has no
113
+ * workspace installed: the cache is cleared. A network / Cloud failure
114
+ * keeps whatever is cached and is reported via {@link getLastError}.
115
+ *
116
+ * @returns The current config after the refresh (cached on failure)
117
+ */
118
+ async refresh() {
119
+ await this.load();
120
+ if (!this.isAvailable())
121
+ return this.config;
122
+ try {
123
+ const fetched = await this.fetchFromCloud();
124
+ this.lastError = null;
125
+ await this.apply(fetched);
126
+ }
127
+ catch (err) {
128
+ this.lastError = err instanceof Error ? err.message : String(err);
129
+ this.logger.warn('Cloud Slack config refresh failed — keeping cached copy', {
130
+ error: this.lastError,
131
+ cached: !!this.config,
132
+ });
133
+ }
134
+ return this.config;
135
+ }
136
+ /**
137
+ * Convenience for boot: cached copy first, then a Cloud refresh when
138
+ * possible. Returns the best available config.
139
+ *
140
+ * @returns The config or null
141
+ */
142
+ async loadOrRefresh() {
143
+ await this.load();
144
+ if (this.isAvailable())
145
+ await this.refresh();
146
+ return this.config;
147
+ }
148
+ /**
149
+ * Forget the cached config (workspace removed). Notifies listeners.
150
+ */
151
+ async clear() {
152
+ await this.apply(null);
153
+ }
154
+ /**
155
+ * Remove the account's Slack workspace on Cloud (`DELETE
156
+ * /api/cloud/slack/workspace`) and forget the local cache. Every instance
157
+ * of the account loses Slack on its next refresh.
158
+ *
159
+ * @returns True when Cloud removed a workspace
160
+ * @throws {SlackIdentityCloudError} on a Cloud failure
161
+ */
162
+ async removeWorkspace() {
163
+ const token = this.deps.cloud.getToken();
164
+ const base = this.deps.cloud.getCloudUrl();
165
+ if (!token || !base) {
166
+ throw new SlackIdentityCloudError(401, 'not_logged_in', 'Not logged in to Crewly Cloud');
167
+ }
168
+ const url = `${base.replace(/\/$/, '')}${SLACK_CLOUD_CONSTANTS.CLOUD_PATH}${SLACK_CLOUD_CONSTANTS.WORKSPACE_PATH}`;
169
+ const controller = new AbortController();
170
+ const timer = setTimeout(() => controller.abort(), SLACK_CLOUD_CONSTANTS.REQUEST_TIMEOUT_MS);
171
+ let removed = false;
172
+ try {
173
+ const res = await this.fetchImpl(url, {
174
+ method: 'DELETE',
175
+ headers: { Authorization: `Bearer ${token}` },
176
+ signal: controller.signal,
177
+ });
178
+ const text = await res.text();
179
+ let parsed = {};
180
+ try {
181
+ parsed = JSON.parse(text);
182
+ }
183
+ catch {
184
+ parsed = {};
185
+ }
186
+ if (res.status !== 404 && (!res.ok || parsed.success !== true)) {
187
+ throw new SlackIdentityCloudError(res.status, parsed.code ?? `http_${res.status}`, parsed.error ?? `Cloud request failed (${res.status})`);
188
+ }
189
+ removed = res.ok && parsed.data?.removed !== false;
190
+ }
191
+ catch (err) {
192
+ if (err instanceof SlackIdentityCloudError)
193
+ throw err;
194
+ const message = err instanceof Error ? err.message : String(err);
195
+ throw new SlackIdentityCloudError(502, 'network', `Cloud unreachable: ${message}`);
196
+ }
197
+ finally {
198
+ clearTimeout(timer);
199
+ }
200
+ await this.clear();
201
+ return removed;
202
+ }
203
+ /**
204
+ * Periodic refresh (every 10 min). Idempotent; never keeps the process
205
+ * alive on its own.
206
+ */
207
+ start() {
208
+ if (this.refreshTimer)
209
+ return;
210
+ const setI = this.deps.setInterval ?? setInterval;
211
+ this.refreshTimer = setI(() => {
212
+ void this.refresh();
213
+ }, SLACK_CLOUD_CONSTANTS.CONFIG_REFRESH_INTERVAL_MS);
214
+ this.refreshTimer.unref?.();
215
+ }
216
+ /** Stop the periodic refresh. */
217
+ stop() {
218
+ if (!this.refreshTimer)
219
+ return;
220
+ const clearI = this.deps.clearInterval ?? clearInterval;
221
+ clearI(this.refreshTimer);
222
+ this.refreshTimer = null;
223
+ }
224
+ // -------------------------------------------------------------------------
225
+ // Read model
226
+ // -------------------------------------------------------------------------
227
+ /**
228
+ * The in-memory config (call {@link load} / {@link refresh} first).
229
+ *
230
+ * @returns The config or null
231
+ */
232
+ getConfig() {
233
+ return this.config;
234
+ }
235
+ /**
236
+ * ISO timestamp of the last successful Cloud fetch (or the cache's).
237
+ *
238
+ * @returns Timestamp or null
239
+ */
240
+ getFetchedAt() {
241
+ return this.fetchedAt;
242
+ }
243
+ /**
244
+ * Last refresh failure, if the most recent attempt failed.
245
+ *
246
+ * @returns Error message or null
247
+ */
248
+ getLastError() {
249
+ return this.lastError;
250
+ }
251
+ /**
252
+ * The master workspace as a {@link SlackConfig} for `SlackService`
253
+ * (`transport: 'cloud'`, no socket credentials). Optional env knobs
254
+ * (`SLACK_DEFAULT_CHANNEL`, `SLACK_ALLOWED_USERS`) still apply.
255
+ *
256
+ * @returns The config, or null without a workspace
257
+ */
258
+ toSlackConfig() {
259
+ const workspace = this.config?.workspace;
260
+ if (!workspace?.botToken)
261
+ return null;
262
+ const env = this.deps.env ?? process.env;
263
+ return {
264
+ botToken: workspace.botToken,
265
+ appToken: '',
266
+ signingSecret: '',
267
+ socketMode: false,
268
+ transport: 'cloud',
269
+ botUserId: workspace.botUserId,
270
+ defaultChannelId: env.SLACK_DEFAULT_CHANNEL,
271
+ allowedUserIds: env.SLACK_ALLOWED_USERS?.split(',').filter(Boolean),
272
+ };
273
+ }
274
+ /**
275
+ * Installed per-agent identities from the config.
276
+ *
277
+ * @returns Copies (tokens included — never log them)
278
+ */
279
+ getAgents() {
280
+ return (this.config?.agents ?? []).map((a) => ({ ...a }));
281
+ }
282
+ /**
283
+ * Register a change listener. Fired after the cache is written.
284
+ *
285
+ * @param listener - Called with the new config (null when removed)
286
+ * @returns Unsubscribe
287
+ */
288
+ onChange(listener) {
289
+ this.listeners.add(listener);
290
+ return () => this.listeners.delete(listener);
291
+ }
292
+ // -------------------------------------------------------------------------
293
+ // Internals
294
+ // -------------------------------------------------------------------------
295
+ /** Persist + notify when the config differs from the current one. */
296
+ async apply(next) {
297
+ const changed = JSON.stringify(next) !== JSON.stringify(this.config);
298
+ this.config = next;
299
+ this.fetchedAt = new Date(this.now()).toISOString();
300
+ if (next) {
301
+ const file = { version: 1, fetchedAt: this.fetchedAt, config: next };
302
+ await fs.mkdir(path.dirname(this.storePath), { recursive: true });
303
+ await atomicWriteJson(this.storePath, file);
304
+ await fs.chmod(this.storePath, 0o600).catch(() => undefined);
305
+ }
306
+ else {
307
+ await fs.unlink(this.storePath).catch(() => undefined);
308
+ }
309
+ if (!changed)
310
+ return;
311
+ this.logger.info(next ? 'Cloud Slack config updated' : 'Cloud Slack config removed', {
312
+ workspace: next?.workspace.slackTeamName,
313
+ agents: next?.agents.length ?? 0,
314
+ });
315
+ for (const listener of this.listeners) {
316
+ try {
317
+ listener(next ? { ...next } : null);
318
+ }
319
+ catch (err) {
320
+ this.logger.warn('onChange listener threw', { error: err instanceof Error ? err.message : String(err) });
321
+ }
322
+ }
323
+ }
324
+ /** `GET /api/cloud/slack/config`; null on 404 (not connected). */
325
+ async fetchFromCloud() {
326
+ const token = this.deps.cloud.getToken();
327
+ const base = this.deps.cloud.getCloudUrl();
328
+ if (!token || !base) {
329
+ throw new SlackIdentityCloudError(401, 'not_logged_in', 'Not logged in to Crewly Cloud');
330
+ }
331
+ const url = `${base.replace(/\/$/, '')}${SLACK_CLOUD_CONSTANTS.CLOUD_PATH}${SLACK_CLOUD_CONSTANTS.CONFIG_PATH}`;
332
+ const controller = new AbortController();
333
+ const timer = setTimeout(() => controller.abort(), SLACK_CLOUD_CONSTANTS.REQUEST_TIMEOUT_MS);
334
+ try {
335
+ const res = await this.fetchImpl(url, {
336
+ method: 'GET',
337
+ headers: { Authorization: `Bearer ${token}` },
338
+ signal: controller.signal,
339
+ });
340
+ if (res.status === 404)
341
+ return null;
342
+ const text = await res.text();
343
+ let parsed = {};
344
+ try {
345
+ parsed = JSON.parse(text);
346
+ }
347
+ catch {
348
+ parsed = {};
349
+ }
350
+ if (!res.ok || parsed.success !== true) {
351
+ throw new SlackIdentityCloudError(res.status, parsed.code ?? `http_${res.status}`, parsed.error ?? `Cloud request failed (${res.status})`);
352
+ }
353
+ if (!isCloudConfig(parsed.data)) {
354
+ throw new SlackIdentityCloudError(502, 'bad_config', 'Cloud returned a malformed Slack config');
355
+ }
356
+ return parsed.data;
357
+ }
358
+ catch (err) {
359
+ if (err instanceof SlackIdentityCloudError)
360
+ throw err;
361
+ const message = err instanceof Error ? err.message : String(err);
362
+ throw new SlackIdentityCloudError(502, 'network', `Cloud unreachable: ${message}`);
363
+ }
364
+ finally {
365
+ clearTimeout(timer);
366
+ }
367
+ }
368
+ now() {
369
+ return this.deps.now?.() ?? Date.now();
370
+ }
371
+ }
372
+ /**
373
+ * Runtime guard for the Cloud config payload.
374
+ *
375
+ * @param value - Parsed JSON
376
+ * @returns True when it has a workspace with a bot token and an agents list
377
+ */
378
+ export function isCloudConfig(value) {
379
+ const v = value;
380
+ if (!v || typeof v !== 'object')
381
+ return false;
382
+ const w = v.workspace;
383
+ if (!w || typeof w.botToken !== 'string' || !w.botToken || typeof w.slackTeamId !== 'string')
384
+ return false;
385
+ if (!Array.isArray(v.agents))
386
+ return false;
387
+ return v.agents.every((a) => a &&
388
+ typeof a.agentSession === 'string' &&
389
+ typeof a.botToken === 'string' &&
390
+ typeof a.botUserId === 'string');
391
+ }
392
+ let instance = null;
393
+ /**
394
+ * Install the process-wide instance (composition root).
395
+ *
396
+ * @param service - The service or null
397
+ */
398
+ export function setSlackCloudConfigService(service) {
399
+ instance = service;
400
+ }
401
+ /**
402
+ * The process-wide instance, or null before wiring.
403
+ *
404
+ * @returns The service or null
405
+ */
406
+ export function getSlackCloudConfigService() {
407
+ return instance;
408
+ }
409
+ //# sourceMappingURL=slack-cloud-config.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slack-cloud-config.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/slack/slack-cloud-config.service.ts"],"names":[],"mappings":"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"}
@@ -6,8 +6,17 @@
6
6
  *
7
7
  * @module services/slack/initializer
8
8
  */
9
- import { SlackConfig } from '../../types/slack.types.js';
9
+ import { SlackCloudConfigService } from './slack-cloud-config.service.js';
10
+ import { SlackInstanceRegistryService } from './slack-instance-registry.service.js';
11
+ import { SlackConfig, SlackCloudConfig } from '../../types/slack.types.js';
10
12
  import type { MessageQueueService } from '../messaging/message-queue.service.js';
13
+ /** Where the active Slack connection's tokens came from. */
14
+ export type SlackSource = 'env' | 'cloud';
15
+ /** A config together with its provenance. */
16
+ export interface ResolvedSlackConfig {
17
+ config: SlackConfig;
18
+ source: SlackSource;
19
+ }
11
20
  /**
12
21
  * Result of initialization attempt
13
22
  */
@@ -38,6 +47,37 @@ export declare function getSlackConfigFromEnv(): SlackConfig | null;
38
47
  * @returns SlackConfig object or null if not configured
39
48
  */
40
49
  export declare function getSlackConfig(): Promise<SlackConfig | null>;
50
+ /**
51
+ * Build (once) the Cloud config service on top of the Cloud client.
52
+ *
53
+ * @returns The process-wide SlackCloudConfigService
54
+ */
55
+ export declare function ensureSlackCloudConfigService(): Promise<SlackCloudConfigService>;
56
+ /**
57
+ * Pick the Slack config to connect with, applying the source precedence:
58
+ *
59
+ * - `CREWLY_SLACK_SOURCE=env` → local tokens only (env / credentials file);
60
+ * - `CREWLY_SLACK_SOURCE=cloud` → the Cloud-owned workspace only;
61
+ * - unset → Cloud when the account has a workspace
62
+ * installed, otherwise local tokens. When both exist Cloud wins and this
63
+ * is logged once so the migration is visible.
64
+ *
65
+ * @returns The config and where it came from, or null when nothing is set up
66
+ */
67
+ export declare function resolveSlackConfig(): Promise<ResolvedSlackConfig | null>;
68
+ /**
69
+ * The source of the live Slack connection.
70
+ *
71
+ * @returns `env`, `cloud`, or null when Slack is not connected
72
+ */
73
+ export declare function getActiveSlackSource(): SlackSource | null;
74
+ /**
75
+ * Record the source of a connection made outside {@link connectSlack}
76
+ * (the manual `/api/slack/connect` route).
77
+ *
78
+ * @param source - The source, or null after a disconnect
79
+ */
80
+ export declare function setActiveSlackSource(source: SlackSource | null): void;
41
81
  /**
42
82
  * Options for Slack initialization
43
83
  */
@@ -70,6 +110,60 @@ export interface SlackInitOptions {
70
110
  * ```
71
111
  */
72
112
  export declare function initializeSlackIfConfigured(options?: SlackInitOptions): Promise<SlackInitResult>;
113
+ /**
114
+ * Connect SlackService with a resolved config and bring up the bridge, team
115
+ * channels and — for the Cloud source — the relay transport, the identities
116
+ * from the Cloud config and the instance registry.
117
+ *
118
+ * @param resolved - Config + source
119
+ * @param options - Init options (message queue)
120
+ * @returns Result object indicating success or failure
121
+ */
122
+ export declare function connectSlack(resolved: ResolvedSlackConfig, options?: SlackInitOptions): Promise<SlackInitResult>;
123
+ /**
124
+ * Build (once) the instance registry service without starting it — the
125
+ * primary toggle needs it even before Slack is connected.
126
+ *
127
+ * @returns The process-wide SlackInstanceRegistryService
128
+ */
129
+ export declare function ensureSlackInstanceRegistry(): Promise<SlackInstanceRegistryService>;
130
+ /**
131
+ * Build (once) and start the instance registry heartbeat. Never throws.
132
+ *
133
+ * @returns The registry service, or null when its dependencies are missing
134
+ */
135
+ export declare function startSlackInstanceRegistry(): Promise<SlackInstanceRegistryService | null>;
136
+ /**
137
+ * Subscribe to Cloud config changes and start the 10-minute refresh (unless
138
+ * `CREWLY_SLACK_SOURCE=env`). Idempotent.
139
+ */
140
+ export declare function watchSlackCloudConfig(): Promise<void>;
141
+ /**
142
+ * React to the Cloud config appearing, rotating or disappearing:
143
+ *
144
+ * - appeared and Slack is not connected → connect with the cloud source;
145
+ * - changed while connected via Cloud → re-initialise with the new token
146
+ * and merge new identities;
147
+ * - removed while connected via Cloud → disconnect.
148
+ *
149
+ * A live self-hosted (`env`) connection is left alone; precedence is only
150
+ * applied at boot so a running socket is never yanked under the owner.
151
+ *
152
+ * @param config - The new Cloud config (null when removed)
153
+ */
154
+ export declare function handleSlackCloudConfigChange(config: SlackCloudConfig | null): Promise<void>;
155
+ /**
156
+ * Called once Cloud is signed in (boot restore or the login route — both
157
+ * happen after the Slack boot path ran): fetch the Cloud config now instead
158
+ * of waiting for the 10-minute tick, connect if a workspace exists and
159
+ * Slack is down, and re-register the instance now that a relay queue may
160
+ * exist. Never throws.
161
+ */
162
+ export declare function refreshSlackCloudConfig(): Promise<void>;
163
+ /**
164
+ * Reset module state (tests).
165
+ */
166
+ export declare function resetSlackInitializerState(): void;
73
167
  /**
74
168
  * Wire and start the Slack team-channel service (one Slack channel + one
75
169
  * chat-v2 huddle per team). Idempotent — safe to call from both the boot
@@ -1 +1 @@
1
- {"version":3,"file":"slack-initializer.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/slack/slack-initializer.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAC;AACzD,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;AAKjF;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,2CAA2C;IAC3C,SAAS,EAAE,OAAO,CAAC;IACnB,uCAAuC;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,8BAA8B;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAM3C;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,WAAW,GAAG,IAAI,CAiB1D;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAqBlE;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,0EAA0E;IAC1E,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;CAC3C;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,2BAA2B,CAC/C,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,eAAe,CAAC,CA8B1B;AAED;;;;;;;;;GASG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC,CAqD5D;AAED;;;;GAIG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAUnD"}
1
+ {"version":3,"file":"slack-initializer.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/slack/slack-initializer.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,OAAO,EACL,uBAAuB,EAGxB,MAAM,iCAAiC,CAAC;AACzC,OAAO,EACL,4BAA4B,EAG7B,MAAM,sCAAsC,CAAC;AAG9C,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC3E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;AAKjF,4DAA4D;AAC5D,MAAM,MAAM,WAAW,GAAG,KAAK,GAAG,OAAO,CAAC;AAE1C,6CAA6C;AAC7C,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,EAAE,WAAW,CAAC;CACrB;AASD;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,2CAA2C;IAC3C,SAAS,EAAE,OAAO,CAAC;IACnB,uCAAuC;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,8BAA8B;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAM3C;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,WAAW,GAAG,IAAI,CAiB1D;AAED;;;;;GAKG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAqBlE;AAED;;;;GAIG;AACH,wBAAsB,6BAA6B,IAAI,OAAO,CAAC,uBAAuB,CAAC,CAQtF;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAsB9E;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI,WAAW,GAAG,IAAI,CAEzD;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,WAAW,GAAG,IAAI,GAAG,IAAI,CAErE;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,0EAA0E;IAC1E,mBAAmB,CAAC,EAAE,mBAAmB,CAAC;CAC3C;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAsB,2BAA2B,CAC/C,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,eAAe,CAAC,CAc1B;AAED;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAChC,QAAQ,EAAE,mBAAmB,EAC7B,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,eAAe,CAAC,CAkC1B;AAoCD;;;;;GAKG;AACH,wBAAsB,2BAA2B,IAAI,OAAO,CAAC,4BAA4B,CAAC,CAoBzF;AAED;;;;GAIG;AACH,wBAAsB,0BAA0B,IAAI,OAAO,CAAC,4BAA4B,GAAG,IAAI,CAAC,CAW/F;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,IAAI,CAAC,CAS3D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,4BAA4B,CAAC,MAAM,EAAE,gBAAgB,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAgCjG;AAED;;;;;;GAMG;AACH,wBAAsB,uBAAuB,IAAI,OAAO,CAAC,IAAI,CAAC,CAkB7D;AAED;;GAEG;AACH,wBAAgB,0BAA0B,IAAI,IAAI,CASjD;AAED;;;;;;;;;GASG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC,CAqD5D;AAED;;;;GAIG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAanD"}