@sideboard-ai/core 0.1.68 → 0.1.71

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 (36) hide show
  1. package/dist/{agents-PE3RHQS5.js → agents-GB7XL2LI.js} +6 -5
  2. package/dist/{agents-JBD7IAGK.js → agents-TCAK3MXN.js} +6 -5
  3. package/dist/{app-settings-ONKMFSEJ.js → app-settings-3TLA2EJ7.js} +16 -1
  4. package/dist/{app-settings-GJVZGU3M.js → app-settings-P42Z3VOB.js} +16 -1
  5. package/dist/caffeinate-hold-BEJIYPJ7.js +107 -0
  6. package/dist/caffeinate-hold-KLC6SABD.js +14 -0
  7. package/dist/{chunk-DPDGMMGI.js → chunk-3CY6WJ7O.js} +2 -2
  8. package/dist/{chunk-ROXEENBT.js → chunk-57ROTAFD.js} +62 -3
  9. package/dist/{chunk-N5LMEP65.js → chunk-7NCIHRAU.js} +22 -4
  10. package/dist/{chunk-OICHV4DH.js → chunk-DGPUAZA4.js} +62 -3
  11. package/dist/{chunk-VX6ACNXS.js → chunk-H2IXNFQ2.js} +22 -4
  12. package/dist/chunk-JAWEDVVA.js +106 -0
  13. package/dist/chunk-JT7R45JB.js +20 -0
  14. package/dist/{chunk-EEBDVLDK.js → chunk-MF2YMEGD.js} +61 -9
  15. package/dist/{chunk-47RBLBRK.js → chunk-NG7S3OPX.js} +10 -4
  16. package/dist/chunk-NSTQ6QKD.js +23 -0
  17. package/dist/{chunk-REKGFVUX.js → chunk-OE7YBB5X.js} +61 -9
  18. package/dist/{chunk-BPOTVI5J.js → chunk-PTJJIQK7.js} +472 -18
  19. package/dist/{chunk-6Q7H6SMX.js → chunk-S25IHL5H.js} +10 -4
  20. package/dist/{chunk-QQC2D4MY.js → chunk-UH57WVFP.js} +2 -2
  21. package/dist/{chunk-MHR4OGZ4.js → chunk-W7YOTDVO.js} +493 -18
  22. package/dist/{coordinator-prompt-YZYZ5P6P.js → coordinator-prompt-2J4PIMUF.js} +6 -3
  23. package/dist/{coordinator-prompt-HD6IMCKS.js → coordinator-prompt-QYYG2IBB.js} +6 -3
  24. package/dist/{global-workspace-Z6GF7D2K.js → global-workspace-KHIFQUUM.js} +11 -4
  25. package/dist/{global-workspace-7P5NG3JZ.js → global-workspace-SVRYNJZA.js} +11 -4
  26. package/dist/index.cjs +4184 -850
  27. package/dist/index.d.cts +543 -21
  28. package/dist/index.d.ts +543 -21
  29. package/dist/index.js +3285 -748
  30. package/dist/mcp/run-stdio.cjs +1995 -482
  31. package/dist/mcp/run-stdio.js +943 -151
  32. package/dist/{workspaces-BWJNGEE6.js → workspaces-AHFTHVBT.js} +6 -5
  33. package/dist/{workspaces-O6EZGKQK.js → workspaces-SVQIEVIZ.js} +6 -5
  34. package/dist/{worktree-VDXLUOWR.js → worktree-KWXHMKRN.js} +1 -1
  35. package/dist/{worktree-ZPSKEZFE.js → worktree-VOCH3WS3.js} +1 -1
  36. package/package.json +3 -1
@@ -1,5 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ import {
4
+ chmodOwnerOnly,
5
+ writePrivateFile
6
+ } from "./chunk-NSTQ6QKD.js";
3
7
  import {
4
8
  normalizeThinkingEffort
5
9
  } from "./chunk-KGZBYWZ3.js";
@@ -8,9 +12,179 @@ import {
8
12
  } from "./chunk-7MV3RXSC.js";
9
13
 
10
14
  // src/store/app-settings.ts
11
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
12
- import { homedir } from "os";
15
+ import { randomUUID } from "crypto";
16
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
17
+ import { homedir, hostname } from "os";
18
+ import { join as join2 } from "path";
19
+
20
+ // src/store/secret-vault.ts
13
21
  import { join } from "path";
22
+
23
+ // src/store/secure-file.ts
24
+ import { execFileSync } from "child_process";
25
+ import { createCipheriv, createDecipheriv, randomBytes } from "crypto";
26
+ import { existsSync, readFileSync } from "fs";
27
+ var KEYCHAIN_SERVICE = "ai.sideboard.app";
28
+ var KEYCHAIN_ACCOUNT = "secret-vault-v1";
29
+ var ALG = "aes-256-gcm";
30
+ var injectedKey = null;
31
+ function persistVaultKeyInKeychain(key) {
32
+ if (process.platform !== "darwin" || key.length !== 32) return;
33
+ try {
34
+ execFileSync(
35
+ "security",
36
+ [
37
+ "add-generic-password",
38
+ "-s",
39
+ KEYCHAIN_SERVICE,
40
+ "-a",
41
+ KEYCHAIN_ACCOUNT,
42
+ "-w",
43
+ key.toString("hex"),
44
+ "-U"
45
+ ],
46
+ { encoding: "utf8", timeout: 8e3, stdio: ["ignore", "pipe", "pipe"] }
47
+ );
48
+ } catch {
49
+ }
50
+ }
51
+ function hexKey(value) {
52
+ const trimmed = value?.trim() ?? "";
53
+ if (!/^[0-9a-f]{64}$/i.test(trimmed)) return null;
54
+ return Buffer.from(trimmed, "hex");
55
+ }
56
+ function keychainKey(create) {
57
+ if (process.platform !== "darwin") return null;
58
+ try {
59
+ const out = execFileSync(
60
+ "security",
61
+ ["find-generic-password", "-s", KEYCHAIN_SERVICE, "-a", KEYCHAIN_ACCOUNT, "-w"],
62
+ { encoding: "utf8", timeout: 5e3, stdio: ["ignore", "pipe", "pipe"] }
63
+ ).trim();
64
+ const key2 = hexKey(out);
65
+ if (key2) return key2;
66
+ } catch {
67
+ }
68
+ if (!create) return null;
69
+ const key = randomBytes(32);
70
+ persistVaultKeyInKeychain(key);
71
+ return key;
72
+ }
73
+ function resolveVaultKey() {
74
+ if (process.env.SIDEBOARD_SECRET_VAULT === "plain") return null;
75
+ if (injectedKey) return injectedKey;
76
+ const fromEnv = hexKey(process.env.SIDEBOARD_VAULT_KEY);
77
+ if (fromEnv) return fromEnv;
78
+ if (process.env.VITEST) return null;
79
+ return keychainKey(true);
80
+ }
81
+ function isEnvelope(value) {
82
+ if (!value || typeof value !== "object") return false;
83
+ const o = value;
84
+ return o.v === 1 && o.alg === ALG && typeof o.iv === "string" && typeof o.tag === "string" && typeof o.data === "string";
85
+ }
86
+ function encryptJson(value, key) {
87
+ const iv = randomBytes(12);
88
+ const cipher = createCipheriv(ALG, key, iv);
89
+ const plaintext = Buffer.from(`${JSON.stringify(value)}
90
+ `, "utf8");
91
+ const data = Buffer.concat([cipher.update(plaintext), cipher.final()]);
92
+ const tag = cipher.getAuthTag();
93
+ return {
94
+ v: 1,
95
+ alg: ALG,
96
+ iv: iv.toString("base64"),
97
+ tag: tag.toString("base64"),
98
+ data: data.toString("base64")
99
+ };
100
+ }
101
+ function decryptEnvelope(envelope, key) {
102
+ const decipher = createDecipheriv(ALG, key, Buffer.from(envelope.iv, "base64"));
103
+ decipher.setAuthTag(Buffer.from(envelope.tag, "base64"));
104
+ const plaintext = Buffer.concat([
105
+ decipher.update(Buffer.from(envelope.data, "base64")),
106
+ decipher.final()
107
+ ]);
108
+ return JSON.parse(plaintext.toString("utf8"));
109
+ }
110
+ function readSecureJson(path) {
111
+ if (!existsSync(path)) return null;
112
+ chmodOwnerOnly(path);
113
+ let parsed;
114
+ try {
115
+ parsed = JSON.parse(readFileSync(path, "utf8"));
116
+ } catch {
117
+ return null;
118
+ }
119
+ if (!isEnvelope(parsed)) return parsed;
120
+ const key = resolveVaultKey();
121
+ if (!key) {
122
+ throw new Error(
123
+ "Encrypted Sideboard secrets need a vault key (desktop Keychain / safeStorage, or SIDEBOARD_VAULT_KEY)."
124
+ );
125
+ }
126
+ return decryptEnvelope(parsed, key);
127
+ }
128
+ function writeSecureJson(path, value) {
129
+ const key = resolveVaultKey();
130
+ const body = key ? encryptJson(value, key) : value;
131
+ writePrivateFile(path, `${JSON.stringify(body, null, 2)}
132
+ `);
133
+ }
134
+ function isSecureFileEncrypted(path) {
135
+ if (!existsSync(path)) return false;
136
+ try {
137
+ return isEnvelope(JSON.parse(readFileSync(path, "utf8")));
138
+ } catch {
139
+ return false;
140
+ }
141
+ }
142
+
143
+ // src/store/secret-vault.ts
144
+ function secretVaultPath() {
145
+ return join(appDataDir(), "secrets.json");
146
+ }
147
+ function loadSecretVault() {
148
+ const parsed = readSecureJson(secretVaultPath());
149
+ if (!parsed || typeof parsed !== "object") return {};
150
+ return normalizeVault(parsed);
151
+ }
152
+ function saveSecretVault(vault) {
153
+ writeSecureJson(secretVaultPath(), normalizeVault(vault));
154
+ }
155
+ function normalizeVault(raw) {
156
+ const out = {};
157
+ if (typeof raw.linearApiKey === "string" && raw.linearApiKey.trim()) {
158
+ out.linearApiKey = raw.linearApiKey.trim();
159
+ }
160
+ if (typeof raw.linearAccessToken === "string" && raw.linearAccessToken.trim()) {
161
+ out.linearAccessToken = raw.linearAccessToken.trim();
162
+ }
163
+ if (typeof raw.linearRefreshToken === "string" && raw.linearRefreshToken.trim()) {
164
+ out.linearRefreshToken = raw.linearRefreshToken.trim();
165
+ }
166
+ if (typeof raw.linearClientSecret === "string" && raw.linearClientSecret.trim()) {
167
+ out.linearClientSecret = raw.linearClientSecret.trim();
168
+ }
169
+ if (typeof raw.slackClientSecret === "string" && raw.slackClientSecret.trim()) {
170
+ out.slackClientSecret = raw.slackClientSecret.trim();
171
+ }
172
+ if (typeof raw.slackAppToken === "string" && raw.slackAppToken.trim()) {
173
+ out.slackAppToken = raw.slackAppToken.trim();
174
+ }
175
+ if (raw.environment && typeof raw.environment === "object") {
176
+ const environment = {};
177
+ for (const [key, value] of Object.entries(raw.environment)) {
178
+ const k = key.trim();
179
+ const v = typeof value === "string" ? value.trim() : "";
180
+ if (k && v) environment[k] = v;
181
+ }
182
+ if (Object.keys(environment).length > 0) out.environment = environment;
183
+ }
184
+ return out;
185
+ }
186
+
187
+ // src/store/app-settings.ts
14
188
  var HARNESS_ENV_KEYS = {
15
189
  claude: "ANTHROPIC_API_KEY",
16
190
  codex: "CODEX_API_KEY",
@@ -25,11 +199,45 @@ var DEFAULT_AGENTS = /* @__PURE__ */ new Set([
25
199
  "brightsy",
26
200
  "cursor"
27
201
  ]);
202
+ function hasLinearOAuth(integrations) {
203
+ return Boolean(
204
+ integrations.linearAccessToken?.trim() || integrations.linearRefreshToken?.trim()
205
+ );
206
+ }
207
+ function hasLinearCredentials(integrations) {
208
+ return Boolean(integrations.linearApiKey?.trim()) || hasLinearOAuth(integrations);
209
+ }
210
+ function toPublicAppSettings(settings) {
211
+ const environment = {};
212
+ for (const key of Object.keys(settings.environment)) {
213
+ if (key.trim()) environment[key] = "";
214
+ }
215
+ const integrations = { ...settings.integrations };
216
+ const slackClientSecret = integrations.slackClientSecret;
217
+ const slackAppToken = integrations.slackAppToken;
218
+ delete integrations.linearApiKey;
219
+ delete integrations.linearAccessToken;
220
+ delete integrations.linearRefreshToken;
221
+ delete integrations.linearClientSecret;
222
+ delete integrations.slackClientSecret;
223
+ delete integrations.slackAppToken;
224
+ return {
225
+ ...settings,
226
+ environment,
227
+ integrations: {
228
+ ...integrations,
229
+ hasLinearApiKey: hasLinearCredentials(settings.integrations),
230
+ hasLinearOAuth: hasLinearOAuth(settings.integrations),
231
+ hasSlackClientSecret: Boolean(slackClientSecret?.trim()),
232
+ hasSlackAppToken: Boolean(slackAppToken?.trim())
233
+ }
234
+ };
235
+ }
28
236
  function appSettingsPath() {
29
- return join(appDataDir(), "settings.json");
237
+ return join2(appDataDir(), "settings.json");
30
238
  }
31
239
  function claudeUserSettingsPath() {
32
- return join(homedir(), ".claude", "settings.json");
240
+ return join2(homedir(), ".claude", "settings.json");
33
241
  }
34
242
  function normalizeClaude(raw) {
35
243
  if (!raw || typeof raw !== "object") return {};
@@ -88,9 +296,62 @@ function normalizeIntegrations(raw) {
88
296
  const key = source.linearApiKey.trim();
89
297
  if (key) out.linearApiKey = key;
90
298
  }
299
+ if (typeof source.linearAccessToken === "string") {
300
+ const token = source.linearAccessToken.trim();
301
+ if (token) out.linearAccessToken = token;
302
+ }
303
+ if (typeof source.linearRefreshToken === "string") {
304
+ const token = source.linearRefreshToken.trim();
305
+ if (token) out.linearRefreshToken = token;
306
+ }
307
+ if (typeof source.linearTokenExpiresAt === "number" && Number.isFinite(source.linearTokenExpiresAt)) {
308
+ out.linearTokenExpiresAt = source.linearTokenExpiresAt;
309
+ } else if (typeof source.linearTokenExpiresAt === "string") {
310
+ const n = Number(source.linearTokenExpiresAt);
311
+ if (Number.isFinite(n)) out.linearTokenExpiresAt = n;
312
+ }
313
+ if (typeof source.linearClientId === "string") {
314
+ const id = source.linearClientId.trim();
315
+ if (id) out.linearClientId = id;
316
+ }
317
+ if (typeof source.linearClientSecret === "string") {
318
+ const secret = source.linearClientSecret.trim();
319
+ if (secret) out.linearClientSecret = secret;
320
+ }
321
+ if (typeof source.linearViewerName === "string") {
322
+ const name = source.linearViewerName.trim().slice(0, 120);
323
+ if (name) out.linearViewerName = name;
324
+ }
325
+ if (typeof source.linearOrganizationName === "string") {
326
+ const name = source.linearOrganizationName.trim().slice(0, 120);
327
+ if (name) out.linearOrganizationName = name;
328
+ }
91
329
  if (typeof source.issueSource === "string" && ISSUE_SOURCES.has(source.issueSource)) {
92
330
  out.issueSource = source.issueSource;
93
331
  }
332
+ if (typeof source.slackClientId === "string") {
333
+ const id = source.slackClientId.trim();
334
+ if (id) out.slackClientId = id;
335
+ }
336
+ if (typeof source.slackClientSecret === "string") {
337
+ const secret = source.slackClientSecret.trim();
338
+ if (secret) out.slackClientSecret = secret;
339
+ }
340
+ if (typeof source.slackAppToken === "string") {
341
+ const token = source.slackAppToken.trim();
342
+ if (token) out.slackAppToken = token;
343
+ }
344
+ if (typeof source.slackListenEnabled === "boolean") {
345
+ out.slackListenEnabled = source.slackListenEnabled;
346
+ }
347
+ if (typeof source.slackDeviceId === "string") {
348
+ const id = source.slackDeviceId.trim();
349
+ if (id) out.slackDeviceId = id;
350
+ }
351
+ if (typeof source.slackDeviceLabel === "string") {
352
+ const label = source.slackDeviceLabel.trim().slice(0, 64);
353
+ if (label) out.slackDeviceLabel = label;
354
+ }
94
355
  return out;
95
356
  }
96
357
  function normalizeDefaults(raw) {
@@ -125,8 +386,10 @@ function normalizeAdvanced(raw) {
125
386
  if (typeof source.caffeinateWhileRunning === "boolean") {
126
387
  out.caffeinateWhileRunning = source.caffeinateWhileRunning;
127
388
  }
128
- if (typeof source.caffeinateWhileCloudConnect === "boolean") {
129
- out.caffeinateWhileCloudConnect = source.caffeinateWhileCloudConnect;
389
+ if (typeof source.caffeinateWhileSlackListen === "boolean") {
390
+ out.caffeinateWhileSlackListen = source.caffeinateWhileSlackListen;
391
+ } else if (typeof source.caffeinateWhileCloudConnect === "boolean") {
392
+ out.caffeinateWhileSlackListen = source.caffeinateWhileCloudConnect;
130
393
  }
131
394
  if (typeof source.deleteBranchOnPurge === "boolean") {
132
395
  out.deleteBranchOnPurge = source.deleteBranchOnPurge;
@@ -225,23 +488,84 @@ var EMPTY_SETTINGS = {
225
488
  defaults: {},
226
489
  advanced: {}
227
490
  };
228
- function loadAppSettings() {
491
+ function readSettingsFile() {
229
492
  const path = appSettingsPath();
230
- if (!existsSync(path)) {
231
- return { ...EMPTY_SETTINGS };
232
- }
493
+ if (!existsSync2(path)) return { ...EMPTY_SETTINGS };
233
494
  try {
234
- const parsed = JSON.parse(readFileSync(path, "utf8"));
495
+ chmodOwnerOnly(path);
496
+ const parsed = JSON.parse(readFileSync2(path, "utf8"));
235
497
  return normalizeSettings(parsed);
236
498
  } catch {
237
499
  return { ...EMPTY_SETTINGS };
238
500
  }
239
501
  }
502
+ function diskHoldsSecrets(disk) {
503
+ return Boolean(
504
+ disk.integrations.linearApiKey || disk.integrations.linearAccessToken || disk.integrations.linearRefreshToken || disk.integrations.linearClientSecret || disk.integrations.slackClientSecret || disk.integrations.slackAppToken || Object.keys(disk.environment).length > 0
505
+ );
506
+ }
507
+ function mergeVault(disk) {
508
+ const vault = loadSecretVault();
509
+ const environment = { ...vault.environment ?? {}, ...disk.environment };
510
+ const integrations = { ...disk.integrations };
511
+ if (vault.linearApiKey && !integrations.linearApiKey) {
512
+ integrations.linearApiKey = vault.linearApiKey;
513
+ }
514
+ if (vault.linearAccessToken && !integrations.linearAccessToken) {
515
+ integrations.linearAccessToken = vault.linearAccessToken;
516
+ }
517
+ if (vault.linearRefreshToken && !integrations.linearRefreshToken) {
518
+ integrations.linearRefreshToken = vault.linearRefreshToken;
519
+ }
520
+ if (vault.linearClientSecret && !integrations.linearClientSecret) {
521
+ integrations.linearClientSecret = vault.linearClientSecret;
522
+ }
523
+ if (vault.slackClientSecret && !integrations.slackClientSecret) {
524
+ integrations.slackClientSecret = vault.slackClientSecret;
525
+ }
526
+ if (vault.slackAppToken && !integrations.slackAppToken) {
527
+ integrations.slackAppToken = vault.slackAppToken;
528
+ }
529
+ return { ...disk, environment, integrations };
530
+ }
531
+ function persistSplit(settings) {
532
+ const integrations = { ...settings.integrations };
533
+ const linearApiKey = integrations.linearApiKey;
534
+ const linearAccessToken = integrations.linearAccessToken;
535
+ const linearRefreshToken = integrations.linearRefreshToken;
536
+ const linearClientSecret = integrations.linearClientSecret;
537
+ const slackClientSecret = integrations.slackClientSecret;
538
+ const slackAppToken = integrations.slackAppToken;
539
+ delete integrations.linearApiKey;
540
+ delete integrations.linearAccessToken;
541
+ delete integrations.linearRefreshToken;
542
+ delete integrations.linearClientSecret;
543
+ delete integrations.slackClientSecret;
544
+ delete integrations.slackAppToken;
545
+ writePrivateFile(
546
+ appSettingsPath(),
547
+ `${JSON.stringify({ ...settings, environment: {}, integrations }, null, 2)}
548
+ `
549
+ );
550
+ saveSecretVault({
551
+ linearApiKey,
552
+ linearAccessToken,
553
+ linearRefreshToken,
554
+ linearClientSecret,
555
+ slackClientSecret,
556
+ slackAppToken,
557
+ environment: settings.environment
558
+ });
559
+ }
560
+ function loadAppSettings() {
561
+ const disk = readSettingsFile();
562
+ const merged = mergeVault(disk);
563
+ if (diskHoldsSecrets(disk)) persistSplit(merged);
564
+ return merged;
565
+ }
240
566
  function saveAppSettings(settings) {
241
567
  const normalized = normalizeSettings(settings);
242
- mkdirSync(appDataDir(), { recursive: true });
243
- writeFileSync(appSettingsPath(), `${JSON.stringify(normalized, null, 2)}
244
- `, "utf8");
568
+ persistSplit(normalized);
245
569
  return normalized;
246
570
  }
247
571
  function updateAppEnvironment(patch) {
@@ -332,6 +656,20 @@ function updateIntegrationsSettings(patch) {
332
656
  integrations.linearApiKey = patch.linearApiKey.trim();
333
657
  }
334
658
  }
659
+ if ("linearClientId" in patch) {
660
+ if (patch.linearClientId == null || patch.linearClientId.trim() === "") {
661
+ delete integrations.linearClientId;
662
+ } else {
663
+ integrations.linearClientId = patch.linearClientId.trim();
664
+ }
665
+ }
666
+ if ("linearClientSecret" in patch) {
667
+ if (patch.linearClientSecret == null || patch.linearClientSecret.trim() === "") {
668
+ delete integrations.linearClientSecret;
669
+ } else {
670
+ integrations.linearClientSecret = patch.linearClientSecret.trim();
671
+ }
672
+ }
335
673
  if ("issueSource" in patch) {
336
674
  if (patch.issueSource == null) {
337
675
  delete integrations.issueSource;
@@ -339,6 +677,48 @@ function updateIntegrationsSettings(patch) {
339
677
  integrations.issueSource = patch.issueSource;
340
678
  }
341
679
  }
680
+ if ("slackClientId" in patch) {
681
+ if (patch.slackClientId == null || patch.slackClientId.trim() === "") {
682
+ delete integrations.slackClientId;
683
+ } else {
684
+ integrations.slackClientId = patch.slackClientId.trim();
685
+ }
686
+ }
687
+ if ("slackClientSecret" in patch) {
688
+ if (patch.slackClientSecret == null || patch.slackClientSecret.trim() === "") {
689
+ delete integrations.slackClientSecret;
690
+ } else {
691
+ integrations.slackClientSecret = patch.slackClientSecret.trim();
692
+ }
693
+ }
694
+ if ("slackAppToken" in patch) {
695
+ if (patch.slackAppToken == null || patch.slackAppToken.trim() === "") {
696
+ delete integrations.slackAppToken;
697
+ } else {
698
+ integrations.slackAppToken = patch.slackAppToken.trim();
699
+ }
700
+ }
701
+ if ("slackListenEnabled" in patch) {
702
+ if (patch.slackListenEnabled == null) {
703
+ delete integrations.slackListenEnabled;
704
+ } else {
705
+ integrations.slackListenEnabled = Boolean(patch.slackListenEnabled);
706
+ }
707
+ }
708
+ if ("slackDeviceId" in patch) {
709
+ if (patch.slackDeviceId == null || patch.slackDeviceId.trim() === "") {
710
+ delete integrations.slackDeviceId;
711
+ } else {
712
+ integrations.slackDeviceId = patch.slackDeviceId.trim();
713
+ }
714
+ }
715
+ if ("slackDeviceLabel" in patch) {
716
+ if (patch.slackDeviceLabel == null || patch.slackDeviceLabel.trim() === "") {
717
+ delete integrations.slackDeviceLabel;
718
+ } else {
719
+ integrations.slackDeviceLabel = patch.slackDeviceLabel.trim().slice(0, 64);
720
+ }
721
+ }
342
722
  return saveAppSettings({ ...current, integrations });
343
723
  }
344
724
  function updateDefaultsSettings(patch) {
@@ -412,7 +792,36 @@ function resolveNewThreadOptions(overrides = {}, settings = loadAppSettings()) {
412
792
  };
413
793
  }
414
794
  function isLinearConnected(settings = loadAppSettings()) {
415
- return Boolean(settings.integrations.linearApiKey?.trim());
795
+ return hasLinearCredentials(settings.integrations);
796
+ }
797
+ function saveLinearOAuth(input) {
798
+ const current = loadAppSettings();
799
+ const integrations = { ...current.integrations };
800
+ integrations.linearAccessToken = input.accessToken.trim();
801
+ if (input.refreshToken?.trim()) {
802
+ integrations.linearRefreshToken = input.refreshToken.trim();
803
+ }
804
+ if (typeof input.expiresIn === "number" && Number.isFinite(input.expiresIn)) {
805
+ integrations.linearTokenExpiresAt = Date.now() + Math.max(0, input.expiresIn) * 1e3;
806
+ }
807
+ if (input.viewerName?.trim()) {
808
+ integrations.linearViewerName = input.viewerName.trim().slice(0, 120);
809
+ }
810
+ if (input.organizationName?.trim()) {
811
+ integrations.linearOrganizationName = input.organizationName.trim().slice(0, 120);
812
+ }
813
+ return saveAppSettings({ ...current, integrations });
814
+ }
815
+ function disconnectLinearConnection() {
816
+ const current = loadAppSettings();
817
+ const integrations = { ...current.integrations };
818
+ delete integrations.linearApiKey;
819
+ delete integrations.linearAccessToken;
820
+ delete integrations.linearRefreshToken;
821
+ delete integrations.linearTokenExpiresAt;
822
+ delete integrations.linearViewerName;
823
+ delete integrations.linearOrganizationName;
824
+ return saveAppSettings({ ...current, integrations });
416
825
  }
417
826
  function getIssueSource(settings = loadAppSettings()) {
418
827
  return settings.integrations.issueSource ?? "github";
@@ -429,6 +838,33 @@ function getLinearApiKey(settings = loadAppSettings()) {
429
838
  function brightsyCloudConnectEnabled(settings = loadAppSettings()) {
430
839
  return Boolean(settings.brightsy.cloudConnectEnabled);
431
840
  }
841
+ function slackListenEnabled(settings = loadAppSettings()) {
842
+ return settings.integrations.slackListenEnabled === true;
843
+ }
844
+ function ensureSlackDeviceIdentity(settings = loadAppSettings()) {
845
+ let deviceId = settings.integrations.slackDeviceId?.trim() ?? "";
846
+ let deviceLabel = settings.integrations.slackDeviceLabel?.trim() ?? "";
847
+ const patch = {};
848
+ if (!deviceId) {
849
+ deviceId = randomUUID();
850
+ patch.slackDeviceId = deviceId;
851
+ }
852
+ if (!deviceLabel) {
853
+ try {
854
+ deviceLabel = hostname().split(".")[0]?.trim() || "This Mac";
855
+ } catch {
856
+ deviceLabel = "This Mac";
857
+ }
858
+ patch.slackDeviceLabel = deviceLabel;
859
+ }
860
+ if (Object.keys(patch).length > 0) {
861
+ updateIntegrationsSettings(patch);
862
+ }
863
+ return { deviceId, deviceLabel };
864
+ }
865
+ function slackAppLevelToken(settings = loadAppSettings()) {
866
+ return process.env.SIDEBOARD_SLACK_APP_TOKEN?.trim() || settings.integrations.slackAppToken?.trim() || "";
867
+ }
432
868
  function brightsyInjectWorktreeMcpEnabled(settings = loadAppSettings()) {
433
869
  return Boolean(settings.brightsy.injectWorktreeMcp);
434
870
  }
@@ -452,8 +888,12 @@ function updateAdvancedSettings(patch) {
452
888
  if (typeof patch.caffeinateWhileRunning === "boolean") {
453
889
  advanced.caffeinateWhileRunning = patch.caffeinateWhileRunning;
454
890
  }
455
- if (typeof patch.caffeinateWhileCloudConnect === "boolean") {
456
- advanced.caffeinateWhileCloudConnect = patch.caffeinateWhileCloudConnect;
891
+ if (typeof patch.caffeinateWhileSlackListen === "boolean") {
892
+ advanced.caffeinateWhileSlackListen = patch.caffeinateWhileSlackListen;
893
+ delete advanced.caffeinateWhileCloudConnect;
894
+ } else if (typeof patch.caffeinateWhileCloudConnect === "boolean") {
895
+ advanced.caffeinateWhileSlackListen = patch.caffeinateWhileCloudConnect;
896
+ delete advanced.caffeinateWhileCloudConnect;
457
897
  }
458
898
  if (typeof patch.deleteBranchOnPurge === "boolean") {
459
899
  advanced.deleteBranchOnPurge = patch.deleteBranchOnPurge;
@@ -496,8 +936,11 @@ function autoRunAfterSetupEnabled(settings = loadAppSettings()) {
496
936
  function caffeinateWhileRunningEnabled(settings = loadAppSettings()) {
497
937
  return Boolean(settings.advanced.caffeinateWhileRunning);
498
938
  }
939
+ function caffeinateWhileSlackListenEnabled(settings = loadAppSettings()) {
940
+ return Boolean(settings.advanced.caffeinateWhileSlackListen);
941
+ }
499
942
  function caffeinateWhileCloudConnectEnabled(settings = loadAppSettings()) {
500
- return Boolean(settings.advanced.caffeinateWhileCloudConnect);
943
+ return caffeinateWhileSlackListenEnabled(settings);
501
944
  }
502
945
  function deleteBranchOnPurgeEnabled(settings = loadAppSettings()) {
503
946
  return Boolean(settings.advanced.deleteBranchOnPurge);
@@ -591,7 +1034,12 @@ function harnessEnvKey(harness) {
591
1034
  }
592
1035
 
593
1036
  export {
1037
+ resolveVaultKey,
1038
+ readSecureJson,
1039
+ writeSecureJson,
1040
+ isSecureFileEncrypted,
594
1041
  HARNESS_ENV_KEYS,
1042
+ toPublicAppSettings,
595
1043
  appSettingsPath,
596
1044
  claudeUserSettingsPath,
597
1045
  loadAppSettings,
@@ -610,16 +1058,22 @@ export {
610
1058
  resolveThreadDefaults,
611
1059
  resolveNewThreadOptions,
612
1060
  isLinearConnected,
1061
+ saveLinearOAuth,
1062
+ disconnectLinearConnection,
613
1063
  getIssueSource,
614
1064
  resolveEffectiveIssueSource,
615
1065
  getLinearApiKey,
616
1066
  brightsyCloudConnectEnabled,
1067
+ slackListenEnabled,
1068
+ ensureSlackDeviceIdentity,
1069
+ slackAppLevelToken,
617
1070
  brightsyInjectWorktreeMcpEnabled,
618
1071
  brightsyCloudConnectAgent,
619
1072
  updateAdvancedSettings,
620
1073
  autoRenameBranchEnabled,
621
1074
  autoRunAfterSetupEnabled,
622
1075
  caffeinateWhileRunningEnabled,
1076
+ caffeinateWhileSlackListenEnabled,
623
1077
  caffeinateWhileCloudConnectEnabled,
624
1078
  deleteBranchOnPurgeEnabled,
625
1079
  autoArchiveOnMergeEnabled,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  isOrchestratorThread
3
- } from "./chunk-ROXEENBT.js";
3
+ } from "./chunk-57ROTAFD.js";
4
4
  import {
5
5
  applyConnectedTeamToCli,
6
6
  brightsyMcpServerName,
@@ -19,7 +19,7 @@ import {
19
19
  loadAppSettings,
20
20
  resolveAgentExecutable,
21
21
  resolveClaudeExecutable
22
- } from "./chunk-MHR4OGZ4.js";
22
+ } from "./chunk-W7YOTDVO.js";
23
23
  import {
24
24
  appDataDir
25
25
  } from "./chunk-M37RITA6.js";
@@ -513,7 +513,13 @@ var SIDEBOARD_ARTIFACT_MCP_ALLOWED_TOOLS = [
513
513
  "mcp__sideboard__present_schema",
514
514
  "mcp__sideboard__present_files",
515
515
  "mcp__sideboard__ask_user",
516
- "mcp__sideboard__present_plan"
516
+ "mcp__sideboard__present_plan",
517
+ "mcp__sideboard__list_teams",
518
+ "mcp__sideboard__slack_list_channels",
519
+ "mcp__sideboard__slack_list_users",
520
+ "mcp__sideboard__slack_search",
521
+ "mcp__sideboard__slack_read",
522
+ "mcp__sideboard__slack_post"
517
523
  ];
518
524
  var BRIGHTSY_MCP_ALLOWED_TOOLS = [
519
525
  "mcp__brightsy",
@@ -940,7 +946,7 @@ var claudeAdapter = {
940
946
  );
941
947
  }
942
948
  const mode = permissionMode(thread);
943
- const { isOrchestratorThread: isOrchestratorThread2 } = await import("./global-workspace-7P5NG3JZ.js");
949
+ const { isOrchestratorThread: isOrchestratorThread2 } = await import("./global-workspace-SVRYNJZA.js");
944
950
  const isOrchestrator = isOrchestratorThread2(thread);
945
951
  const injectedServers = await buildInjectedMcpServers({
946
952
  includeSideboard: true,
@@ -2,11 +2,11 @@
2
2
 
3
3
  import {
4
4
  isGlobalRepoPath
5
- } from "./chunk-OICHV4DH.js";
5
+ } from "./chunk-DGPUAZA4.js";
6
6
  import {
7
7
  ensureGhPreferOrigin,
8
8
  resolveRepoRoot
9
- } from "./chunk-REKGFVUX.js";
9
+ } from "./chunk-OE7YBB5X.js";
10
10
  import {
11
11
  appDataDir
12
12
  } from "./chunk-7MV3RXSC.js";