@sideboard-ai/core 0.1.67 → 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 (44) hide show
  1. package/dist/{agents-OTBZVTZO.js → agents-GB7XL2LI.js} +8 -7
  2. package/dist/{agents-TS3EI2YI.js → agents-TCAK3MXN.js} +8 -7
  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-WAFIF22N.js → chunk-3CY6WJ7O.js} +2 -2
  8. package/dist/{chunk-7ZOQY4A7.js → chunk-57ROTAFD.js} +62 -3
  9. package/dist/{chunk-57WWNMFP.js → chunk-7NCIHRAU.js} +22 -4
  10. package/dist/{chunk-FGDJOJ2L.js → chunk-AE5VBOFE.js} +1 -1
  11. package/dist/{chunk-DB3I75FI.js → chunk-BTL7EMGT.js} +4 -4
  12. package/dist/{chunk-4UIYN56C.js → chunk-D4DPQ552.js} +1 -1
  13. package/dist/{chunk-JWZNK3CQ.js → chunk-DGPUAZA4.js} +62 -3
  14. package/dist/{chunk-ZRFAZVGL.js → chunk-H2IXNFQ2.js} +22 -4
  15. package/dist/chunk-JAWEDVVA.js +106 -0
  16. package/dist/chunk-JT7R45JB.js +20 -0
  17. package/dist/{chunk-4BUCGNZS.js → chunk-MF2YMEGD.js} +83 -29
  18. package/dist/{chunk-K7LM4CLD.js → chunk-NG7S3OPX.js} +12 -6
  19. package/dist/chunk-NSTQ6QKD.js +23 -0
  20. package/dist/{chunk-LW5CMQGZ.js → chunk-OE7YBB5X.js} +83 -29
  21. package/dist/{chunk-BPOTVI5J.js → chunk-PTJJIQK7.js} +472 -18
  22. package/dist/{chunk-B4PRRKMA.js → chunk-S25IHL5H.js} +12 -6
  23. package/dist/{chunk-5HTTCYWA.js → chunk-UH57WVFP.js} +2 -2
  24. package/dist/{chunk-M5V4QIWF.js → chunk-VROPG6QF.js} +1 -1
  25. package/dist/{chunk-MHR4OGZ4.js → chunk-W7YOTDVO.js} +493 -18
  26. package/dist/{connected-teams-RUNMOCG5.js → connected-teams-35EIRJCP.js} +2 -2
  27. package/dist/{connected-teams-OMDHTL6A.js → connected-teams-ZRS53OAT.js} +2 -2
  28. package/dist/{coordinator-prompt-6HMTOX4G.js → coordinator-prompt-2J4PIMUF.js} +7 -4
  29. package/dist/{coordinator-prompt-3BSMKZL4.js → coordinator-prompt-QYYG2IBB.js} +7 -4
  30. package/dist/{global-workspace-YZ2NJVLL.js → global-workspace-KHIFQUUM.js} +12 -5
  31. package/dist/{global-workspace-QS7A23CB.js → global-workspace-SVRYNJZA.js} +12 -5
  32. package/dist/index.cjs +4805 -1262
  33. package/dist/index.d.cts +570 -22
  34. package/dist/index.d.ts +570 -22
  35. package/dist/index.js +3084 -340
  36. package/dist/mcp/run-stdio.cjs +2339 -617
  37. package/dist/mcp/run-stdio.js +1289 -290
  38. package/dist/{run-Y2MI3EVV.js → run-A4RHY7HH.js} +1 -1
  39. package/dist/{run-JLD2Y52J.js → run-V2WZUMJA.js} +1 -1
  40. package/dist/{workspaces-EBFW7I7S.js → workspaces-AHFTHVBT.js} +7 -6
  41. package/dist/{workspaces-AIUYUXB7.js → workspaces-SVQIEVIZ.js} +7 -6
  42. package/dist/{worktree-WMDJW2BL.js → worktree-KWXHMKRN.js} +2 -2
  43. package/dist/{worktree-7NBPIVFZ.js → worktree-VOCH3WS3.js} +2 -2
  44. package/package.json +3 -1
@@ -1,3 +1,7 @@
1
+ import {
2
+ chmodOwnerOnly,
3
+ writePrivateFile
4
+ } from "./chunk-JT7R45JB.js";
1
5
  import {
2
6
  normalizeThinkingEffort
3
7
  } from "./chunk-77WWLBCI.js";
@@ -6,9 +10,196 @@ import {
6
10
  } from "./chunk-M37RITA6.js";
7
11
 
8
12
  // src/store/app-settings.ts
9
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
10
- import { homedir } from "os";
13
+ import { randomUUID } from "crypto";
14
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "fs";
15
+ import { homedir, hostname } from "os";
16
+ import { join as join2 } from "path";
17
+
18
+ // src/store/secret-vault.ts
11
19
  import { join } from "path";
20
+
21
+ // src/store/secure-file.ts
22
+ import { execFileSync } from "child_process";
23
+ import { createCipheriv, createDecipheriv, randomBytes } from "crypto";
24
+ import { existsSync, readFileSync } from "fs";
25
+ var KEYCHAIN_SERVICE = "ai.sideboard.app";
26
+ var KEYCHAIN_ACCOUNT = "secret-vault-v1";
27
+ var ALG = "aes-256-gcm";
28
+ var injectedKey = null;
29
+ function setVaultMasterKey(key) {
30
+ injectedKey = key && key.length === 32 ? Buffer.from(key) : null;
31
+ }
32
+ function persistVaultKeyInKeychain(key) {
33
+ if (process.platform !== "darwin" || key.length !== 32) return;
34
+ try {
35
+ execFileSync(
36
+ "security",
37
+ [
38
+ "add-generic-password",
39
+ "-s",
40
+ KEYCHAIN_SERVICE,
41
+ "-a",
42
+ KEYCHAIN_ACCOUNT,
43
+ "-w",
44
+ key.toString("hex"),
45
+ "-U"
46
+ ],
47
+ { encoding: "utf8", timeout: 8e3, stdio: ["ignore", "pipe", "pipe"] }
48
+ );
49
+ } catch {
50
+ }
51
+ }
52
+ function readKeychainVaultKey() {
53
+ return keychainKey(false);
54
+ }
55
+ function hexKey(value) {
56
+ const trimmed = value?.trim() ?? "";
57
+ if (!/^[0-9a-f]{64}$/i.test(trimmed)) return null;
58
+ return Buffer.from(trimmed, "hex");
59
+ }
60
+ function keychainKey(create) {
61
+ if (process.platform !== "darwin") return null;
62
+ try {
63
+ const out = execFileSync(
64
+ "security",
65
+ ["find-generic-password", "-s", KEYCHAIN_SERVICE, "-a", KEYCHAIN_ACCOUNT, "-w"],
66
+ { encoding: "utf8", timeout: 5e3, stdio: ["ignore", "pipe", "pipe"] }
67
+ ).trim();
68
+ const key2 = hexKey(out);
69
+ if (key2) return key2;
70
+ } catch {
71
+ }
72
+ if (!create) return null;
73
+ const key = randomBytes(32);
74
+ persistVaultKeyInKeychain(key);
75
+ return key;
76
+ }
77
+ function resolveVaultKey() {
78
+ if (process.env.SIDEBOARD_SECRET_VAULT === "plain") return null;
79
+ if (injectedKey) return injectedKey;
80
+ const fromEnv = hexKey(process.env.SIDEBOARD_VAULT_KEY);
81
+ if (fromEnv) return fromEnv;
82
+ if (process.env.VITEST) return null;
83
+ return keychainKey(true);
84
+ }
85
+ function isEnvelope(value) {
86
+ if (!value || typeof value !== "object") return false;
87
+ const o = value;
88
+ return o.v === 1 && o.alg === ALG && typeof o.iv === "string" && typeof o.tag === "string" && typeof o.data === "string";
89
+ }
90
+ function encryptJson(value, key) {
91
+ const iv = randomBytes(12);
92
+ const cipher = createCipheriv(ALG, key, iv);
93
+ const plaintext = Buffer.from(`${JSON.stringify(value)}
94
+ `, "utf8");
95
+ const data = Buffer.concat([cipher.update(plaintext), cipher.final()]);
96
+ const tag = cipher.getAuthTag();
97
+ return {
98
+ v: 1,
99
+ alg: ALG,
100
+ iv: iv.toString("base64"),
101
+ tag: tag.toString("base64"),
102
+ data: data.toString("base64")
103
+ };
104
+ }
105
+ function decryptEnvelope(envelope, key) {
106
+ const decipher = createDecipheriv(ALG, key, Buffer.from(envelope.iv, "base64"));
107
+ decipher.setAuthTag(Buffer.from(envelope.tag, "base64"));
108
+ const plaintext = Buffer.concat([
109
+ decipher.update(Buffer.from(envelope.data, "base64")),
110
+ decipher.final()
111
+ ]);
112
+ return JSON.parse(plaintext.toString("utf8"));
113
+ }
114
+ function readSecureJson(path) {
115
+ if (!existsSync(path)) return null;
116
+ chmodOwnerOnly(path);
117
+ let parsed;
118
+ try {
119
+ parsed = JSON.parse(readFileSync(path, "utf8"));
120
+ } catch {
121
+ return null;
122
+ }
123
+ if (!isEnvelope(parsed)) return parsed;
124
+ const key = resolveVaultKey();
125
+ if (!key) {
126
+ throw new Error(
127
+ "Encrypted Sideboard secrets need a vault key (desktop Keychain / safeStorage, or SIDEBOARD_VAULT_KEY)."
128
+ );
129
+ }
130
+ return decryptEnvelope(parsed, key);
131
+ }
132
+ function writeSecureJson(path, value) {
133
+ const key = resolveVaultKey();
134
+ const body = key ? encryptJson(value, key) : value;
135
+ writePrivateFile(path, `${JSON.stringify(body, null, 2)}
136
+ `);
137
+ }
138
+ function isSecureFileEncrypted(path) {
139
+ if (!existsSync(path)) return false;
140
+ try {
141
+ return isEnvelope(JSON.parse(readFileSync(path, "utf8")));
142
+ } catch {
143
+ return false;
144
+ }
145
+ }
146
+ function secureFileUnlocksWith(path, key) {
147
+ if (!existsSync(path) || key.length !== 32) return true;
148
+ try {
149
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
150
+ if (!isEnvelope(parsed)) return true;
151
+ decryptEnvelope(parsed, key);
152
+ return true;
153
+ } catch {
154
+ return false;
155
+ }
156
+ }
157
+
158
+ // src/store/secret-vault.ts
159
+ function secretVaultPath() {
160
+ return join(appDataDir(), "secrets.json");
161
+ }
162
+ function loadSecretVault() {
163
+ const parsed = readSecureJson(secretVaultPath());
164
+ if (!parsed || typeof parsed !== "object") return {};
165
+ return normalizeVault(parsed);
166
+ }
167
+ function saveSecretVault(vault) {
168
+ writeSecureJson(secretVaultPath(), normalizeVault(vault));
169
+ }
170
+ function normalizeVault(raw) {
171
+ const out = {};
172
+ if (typeof raw.linearApiKey === "string" && raw.linearApiKey.trim()) {
173
+ out.linearApiKey = raw.linearApiKey.trim();
174
+ }
175
+ if (typeof raw.linearAccessToken === "string" && raw.linearAccessToken.trim()) {
176
+ out.linearAccessToken = raw.linearAccessToken.trim();
177
+ }
178
+ if (typeof raw.linearRefreshToken === "string" && raw.linearRefreshToken.trim()) {
179
+ out.linearRefreshToken = raw.linearRefreshToken.trim();
180
+ }
181
+ if (typeof raw.linearClientSecret === "string" && raw.linearClientSecret.trim()) {
182
+ out.linearClientSecret = raw.linearClientSecret.trim();
183
+ }
184
+ if (typeof raw.slackClientSecret === "string" && raw.slackClientSecret.trim()) {
185
+ out.slackClientSecret = raw.slackClientSecret.trim();
186
+ }
187
+ if (typeof raw.slackAppToken === "string" && raw.slackAppToken.trim()) {
188
+ out.slackAppToken = raw.slackAppToken.trim();
189
+ }
190
+ if (raw.environment && typeof raw.environment === "object") {
191
+ const environment = {};
192
+ for (const [key, value] of Object.entries(raw.environment)) {
193
+ const k = key.trim();
194
+ const v = typeof value === "string" ? value.trim() : "";
195
+ if (k && v) environment[k] = v;
196
+ }
197
+ if (Object.keys(environment).length > 0) out.environment = environment;
198
+ }
199
+ return out;
200
+ }
201
+
202
+ // src/store/app-settings.ts
12
203
  var HARNESS_ENV_KEYS = {
13
204
  claude: "ANTHROPIC_API_KEY",
14
205
  codex: "CODEX_API_KEY",
@@ -23,11 +214,45 @@ var DEFAULT_AGENTS = /* @__PURE__ */ new Set([
23
214
  "brightsy",
24
215
  "cursor"
25
216
  ]);
217
+ function hasLinearOAuth(integrations) {
218
+ return Boolean(
219
+ integrations.linearAccessToken?.trim() || integrations.linearRefreshToken?.trim()
220
+ );
221
+ }
222
+ function hasLinearCredentials(integrations) {
223
+ return Boolean(integrations.linearApiKey?.trim()) || hasLinearOAuth(integrations);
224
+ }
225
+ function toPublicAppSettings(settings) {
226
+ const environment = {};
227
+ for (const key of Object.keys(settings.environment)) {
228
+ if (key.trim()) environment[key] = "";
229
+ }
230
+ const integrations = { ...settings.integrations };
231
+ const slackClientSecret = integrations.slackClientSecret;
232
+ const slackAppToken = integrations.slackAppToken;
233
+ delete integrations.linearApiKey;
234
+ delete integrations.linearAccessToken;
235
+ delete integrations.linearRefreshToken;
236
+ delete integrations.linearClientSecret;
237
+ delete integrations.slackClientSecret;
238
+ delete integrations.slackAppToken;
239
+ return {
240
+ ...settings,
241
+ environment,
242
+ integrations: {
243
+ ...integrations,
244
+ hasLinearApiKey: hasLinearCredentials(settings.integrations),
245
+ hasLinearOAuth: hasLinearOAuth(settings.integrations),
246
+ hasSlackClientSecret: Boolean(slackClientSecret?.trim()),
247
+ hasSlackAppToken: Boolean(slackAppToken?.trim())
248
+ }
249
+ };
250
+ }
26
251
  function appSettingsPath() {
27
- return join(appDataDir(), "settings.json");
252
+ return join2(appDataDir(), "settings.json");
28
253
  }
29
254
  function claudeUserSettingsPath() {
30
- return join(homedir(), ".claude", "settings.json");
255
+ return join2(homedir(), ".claude", "settings.json");
31
256
  }
32
257
  function normalizeClaude(raw) {
33
258
  if (!raw || typeof raw !== "object") return {};
@@ -86,9 +311,62 @@ function normalizeIntegrations(raw) {
86
311
  const key = source.linearApiKey.trim();
87
312
  if (key) out.linearApiKey = key;
88
313
  }
314
+ if (typeof source.linearAccessToken === "string") {
315
+ const token = source.linearAccessToken.trim();
316
+ if (token) out.linearAccessToken = token;
317
+ }
318
+ if (typeof source.linearRefreshToken === "string") {
319
+ const token = source.linearRefreshToken.trim();
320
+ if (token) out.linearRefreshToken = token;
321
+ }
322
+ if (typeof source.linearTokenExpiresAt === "number" && Number.isFinite(source.linearTokenExpiresAt)) {
323
+ out.linearTokenExpiresAt = source.linearTokenExpiresAt;
324
+ } else if (typeof source.linearTokenExpiresAt === "string") {
325
+ const n = Number(source.linearTokenExpiresAt);
326
+ if (Number.isFinite(n)) out.linearTokenExpiresAt = n;
327
+ }
328
+ if (typeof source.linearClientId === "string") {
329
+ const id = source.linearClientId.trim();
330
+ if (id) out.linearClientId = id;
331
+ }
332
+ if (typeof source.linearClientSecret === "string") {
333
+ const secret = source.linearClientSecret.trim();
334
+ if (secret) out.linearClientSecret = secret;
335
+ }
336
+ if (typeof source.linearViewerName === "string") {
337
+ const name = source.linearViewerName.trim().slice(0, 120);
338
+ if (name) out.linearViewerName = name;
339
+ }
340
+ if (typeof source.linearOrganizationName === "string") {
341
+ const name = source.linearOrganizationName.trim().slice(0, 120);
342
+ if (name) out.linearOrganizationName = name;
343
+ }
89
344
  if (typeof source.issueSource === "string" && ISSUE_SOURCES.has(source.issueSource)) {
90
345
  out.issueSource = source.issueSource;
91
346
  }
347
+ if (typeof source.slackClientId === "string") {
348
+ const id = source.slackClientId.trim();
349
+ if (id) out.slackClientId = id;
350
+ }
351
+ if (typeof source.slackClientSecret === "string") {
352
+ const secret = source.slackClientSecret.trim();
353
+ if (secret) out.slackClientSecret = secret;
354
+ }
355
+ if (typeof source.slackAppToken === "string") {
356
+ const token = source.slackAppToken.trim();
357
+ if (token) out.slackAppToken = token;
358
+ }
359
+ if (typeof source.slackListenEnabled === "boolean") {
360
+ out.slackListenEnabled = source.slackListenEnabled;
361
+ }
362
+ if (typeof source.slackDeviceId === "string") {
363
+ const id = source.slackDeviceId.trim();
364
+ if (id) out.slackDeviceId = id;
365
+ }
366
+ if (typeof source.slackDeviceLabel === "string") {
367
+ const label = source.slackDeviceLabel.trim().slice(0, 64);
368
+ if (label) out.slackDeviceLabel = label;
369
+ }
92
370
  return out;
93
371
  }
94
372
  function normalizeDefaults(raw) {
@@ -123,8 +401,10 @@ function normalizeAdvanced(raw) {
123
401
  if (typeof source.caffeinateWhileRunning === "boolean") {
124
402
  out.caffeinateWhileRunning = source.caffeinateWhileRunning;
125
403
  }
126
- if (typeof source.caffeinateWhileCloudConnect === "boolean") {
127
- out.caffeinateWhileCloudConnect = source.caffeinateWhileCloudConnect;
404
+ if (typeof source.caffeinateWhileSlackListen === "boolean") {
405
+ out.caffeinateWhileSlackListen = source.caffeinateWhileSlackListen;
406
+ } else if (typeof source.caffeinateWhileCloudConnect === "boolean") {
407
+ out.caffeinateWhileSlackListen = source.caffeinateWhileCloudConnect;
128
408
  }
129
409
  if (typeof source.deleteBranchOnPurge === "boolean") {
130
410
  out.deleteBranchOnPurge = source.deleteBranchOnPurge;
@@ -223,23 +503,84 @@ var EMPTY_SETTINGS = {
223
503
  defaults: {},
224
504
  advanced: {}
225
505
  };
226
- function loadAppSettings() {
506
+ function readSettingsFile() {
227
507
  const path = appSettingsPath();
228
- if (!existsSync(path)) {
229
- return { ...EMPTY_SETTINGS };
230
- }
508
+ if (!existsSync2(path)) return { ...EMPTY_SETTINGS };
231
509
  try {
232
- const parsed = JSON.parse(readFileSync(path, "utf8"));
510
+ chmodOwnerOnly(path);
511
+ const parsed = JSON.parse(readFileSync2(path, "utf8"));
233
512
  return normalizeSettings(parsed);
234
513
  } catch {
235
514
  return { ...EMPTY_SETTINGS };
236
515
  }
237
516
  }
517
+ function diskHoldsSecrets(disk) {
518
+ return Boolean(
519
+ disk.integrations.linearApiKey || disk.integrations.linearAccessToken || disk.integrations.linearRefreshToken || disk.integrations.linearClientSecret || disk.integrations.slackClientSecret || disk.integrations.slackAppToken || Object.keys(disk.environment).length > 0
520
+ );
521
+ }
522
+ function mergeVault(disk) {
523
+ const vault = loadSecretVault();
524
+ const environment = { ...vault.environment ?? {}, ...disk.environment };
525
+ const integrations = { ...disk.integrations };
526
+ if (vault.linearApiKey && !integrations.linearApiKey) {
527
+ integrations.linearApiKey = vault.linearApiKey;
528
+ }
529
+ if (vault.linearAccessToken && !integrations.linearAccessToken) {
530
+ integrations.linearAccessToken = vault.linearAccessToken;
531
+ }
532
+ if (vault.linearRefreshToken && !integrations.linearRefreshToken) {
533
+ integrations.linearRefreshToken = vault.linearRefreshToken;
534
+ }
535
+ if (vault.linearClientSecret && !integrations.linearClientSecret) {
536
+ integrations.linearClientSecret = vault.linearClientSecret;
537
+ }
538
+ if (vault.slackClientSecret && !integrations.slackClientSecret) {
539
+ integrations.slackClientSecret = vault.slackClientSecret;
540
+ }
541
+ if (vault.slackAppToken && !integrations.slackAppToken) {
542
+ integrations.slackAppToken = vault.slackAppToken;
543
+ }
544
+ return { ...disk, environment, integrations };
545
+ }
546
+ function persistSplit(settings) {
547
+ const integrations = { ...settings.integrations };
548
+ const linearApiKey = integrations.linearApiKey;
549
+ const linearAccessToken = integrations.linearAccessToken;
550
+ const linearRefreshToken = integrations.linearRefreshToken;
551
+ const linearClientSecret = integrations.linearClientSecret;
552
+ const slackClientSecret = integrations.slackClientSecret;
553
+ const slackAppToken = integrations.slackAppToken;
554
+ delete integrations.linearApiKey;
555
+ delete integrations.linearAccessToken;
556
+ delete integrations.linearRefreshToken;
557
+ delete integrations.linearClientSecret;
558
+ delete integrations.slackClientSecret;
559
+ delete integrations.slackAppToken;
560
+ writePrivateFile(
561
+ appSettingsPath(),
562
+ `${JSON.stringify({ ...settings, environment: {}, integrations }, null, 2)}
563
+ `
564
+ );
565
+ saveSecretVault({
566
+ linearApiKey,
567
+ linearAccessToken,
568
+ linearRefreshToken,
569
+ linearClientSecret,
570
+ slackClientSecret,
571
+ slackAppToken,
572
+ environment: settings.environment
573
+ });
574
+ }
575
+ function loadAppSettings() {
576
+ const disk = readSettingsFile();
577
+ const merged = mergeVault(disk);
578
+ if (diskHoldsSecrets(disk)) persistSplit(merged);
579
+ return merged;
580
+ }
238
581
  function saveAppSettings(settings) {
239
582
  const normalized = normalizeSettings(settings);
240
- mkdirSync(appDataDir(), { recursive: true });
241
- writeFileSync(appSettingsPath(), `${JSON.stringify(normalized, null, 2)}
242
- `, "utf8");
583
+ persistSplit(normalized);
243
584
  return normalized;
244
585
  }
245
586
  function updateAppEnvironment(patch) {
@@ -330,6 +671,20 @@ function updateIntegrationsSettings(patch) {
330
671
  integrations.linearApiKey = patch.linearApiKey.trim();
331
672
  }
332
673
  }
674
+ if ("linearClientId" in patch) {
675
+ if (patch.linearClientId == null || patch.linearClientId.trim() === "") {
676
+ delete integrations.linearClientId;
677
+ } else {
678
+ integrations.linearClientId = patch.linearClientId.trim();
679
+ }
680
+ }
681
+ if ("linearClientSecret" in patch) {
682
+ if (patch.linearClientSecret == null || patch.linearClientSecret.trim() === "") {
683
+ delete integrations.linearClientSecret;
684
+ } else {
685
+ integrations.linearClientSecret = patch.linearClientSecret.trim();
686
+ }
687
+ }
333
688
  if ("issueSource" in patch) {
334
689
  if (patch.issueSource == null) {
335
690
  delete integrations.issueSource;
@@ -337,6 +692,48 @@ function updateIntegrationsSettings(patch) {
337
692
  integrations.issueSource = patch.issueSource;
338
693
  }
339
694
  }
695
+ if ("slackClientId" in patch) {
696
+ if (patch.slackClientId == null || patch.slackClientId.trim() === "") {
697
+ delete integrations.slackClientId;
698
+ } else {
699
+ integrations.slackClientId = patch.slackClientId.trim();
700
+ }
701
+ }
702
+ if ("slackClientSecret" in patch) {
703
+ if (patch.slackClientSecret == null || patch.slackClientSecret.trim() === "") {
704
+ delete integrations.slackClientSecret;
705
+ } else {
706
+ integrations.slackClientSecret = patch.slackClientSecret.trim();
707
+ }
708
+ }
709
+ if ("slackAppToken" in patch) {
710
+ if (patch.slackAppToken == null || patch.slackAppToken.trim() === "") {
711
+ delete integrations.slackAppToken;
712
+ } else {
713
+ integrations.slackAppToken = patch.slackAppToken.trim();
714
+ }
715
+ }
716
+ if ("slackListenEnabled" in patch) {
717
+ if (patch.slackListenEnabled == null) {
718
+ delete integrations.slackListenEnabled;
719
+ } else {
720
+ integrations.slackListenEnabled = Boolean(patch.slackListenEnabled);
721
+ }
722
+ }
723
+ if ("slackDeviceId" in patch) {
724
+ if (patch.slackDeviceId == null || patch.slackDeviceId.trim() === "") {
725
+ delete integrations.slackDeviceId;
726
+ } else {
727
+ integrations.slackDeviceId = patch.slackDeviceId.trim();
728
+ }
729
+ }
730
+ if ("slackDeviceLabel" in patch) {
731
+ if (patch.slackDeviceLabel == null || patch.slackDeviceLabel.trim() === "") {
732
+ delete integrations.slackDeviceLabel;
733
+ } else {
734
+ integrations.slackDeviceLabel = patch.slackDeviceLabel.trim().slice(0, 64);
735
+ }
736
+ }
340
737
  return saveAppSettings({ ...current, integrations });
341
738
  }
342
739
  function updateDefaultsSettings(patch) {
@@ -410,7 +807,36 @@ function resolveNewThreadOptions(overrides = {}, settings = loadAppSettings()) {
410
807
  };
411
808
  }
412
809
  function isLinearConnected(settings = loadAppSettings()) {
413
- return Boolean(settings.integrations.linearApiKey?.trim());
810
+ return hasLinearCredentials(settings.integrations);
811
+ }
812
+ function saveLinearOAuth(input) {
813
+ const current = loadAppSettings();
814
+ const integrations = { ...current.integrations };
815
+ integrations.linearAccessToken = input.accessToken.trim();
816
+ if (input.refreshToken?.trim()) {
817
+ integrations.linearRefreshToken = input.refreshToken.trim();
818
+ }
819
+ if (typeof input.expiresIn === "number" && Number.isFinite(input.expiresIn)) {
820
+ integrations.linearTokenExpiresAt = Date.now() + Math.max(0, input.expiresIn) * 1e3;
821
+ }
822
+ if (input.viewerName?.trim()) {
823
+ integrations.linearViewerName = input.viewerName.trim().slice(0, 120);
824
+ }
825
+ if (input.organizationName?.trim()) {
826
+ integrations.linearOrganizationName = input.organizationName.trim().slice(0, 120);
827
+ }
828
+ return saveAppSettings({ ...current, integrations });
829
+ }
830
+ function disconnectLinearConnection() {
831
+ const current = loadAppSettings();
832
+ const integrations = { ...current.integrations };
833
+ delete integrations.linearApiKey;
834
+ delete integrations.linearAccessToken;
835
+ delete integrations.linearRefreshToken;
836
+ delete integrations.linearTokenExpiresAt;
837
+ delete integrations.linearViewerName;
838
+ delete integrations.linearOrganizationName;
839
+ return saveAppSettings({ ...current, integrations });
414
840
  }
415
841
  function getIssueSource(settings = loadAppSettings()) {
416
842
  return settings.integrations.issueSource ?? "github";
@@ -427,6 +853,33 @@ function getLinearApiKey(settings = loadAppSettings()) {
427
853
  function brightsyCloudConnectEnabled(settings = loadAppSettings()) {
428
854
  return Boolean(settings.brightsy.cloudConnectEnabled);
429
855
  }
856
+ function slackListenEnabled(settings = loadAppSettings()) {
857
+ return settings.integrations.slackListenEnabled === true;
858
+ }
859
+ function ensureSlackDeviceIdentity(settings = loadAppSettings()) {
860
+ let deviceId = settings.integrations.slackDeviceId?.trim() ?? "";
861
+ let deviceLabel = settings.integrations.slackDeviceLabel?.trim() ?? "";
862
+ const patch = {};
863
+ if (!deviceId) {
864
+ deviceId = randomUUID();
865
+ patch.slackDeviceId = deviceId;
866
+ }
867
+ if (!deviceLabel) {
868
+ try {
869
+ deviceLabel = hostname().split(".")[0]?.trim() || "This Mac";
870
+ } catch {
871
+ deviceLabel = "This Mac";
872
+ }
873
+ patch.slackDeviceLabel = deviceLabel;
874
+ }
875
+ if (Object.keys(patch).length > 0) {
876
+ updateIntegrationsSettings(patch);
877
+ }
878
+ return { deviceId, deviceLabel };
879
+ }
880
+ function slackAppLevelToken(settings = loadAppSettings()) {
881
+ return process.env.SIDEBOARD_SLACK_APP_TOKEN?.trim() || settings.integrations.slackAppToken?.trim() || "";
882
+ }
430
883
  function brightsyInjectWorktreeMcpEnabled(settings = loadAppSettings()) {
431
884
  return Boolean(settings.brightsy.injectWorktreeMcp);
432
885
  }
@@ -450,8 +903,12 @@ function updateAdvancedSettings(patch) {
450
903
  if (typeof patch.caffeinateWhileRunning === "boolean") {
451
904
  advanced.caffeinateWhileRunning = patch.caffeinateWhileRunning;
452
905
  }
453
- if (typeof patch.caffeinateWhileCloudConnect === "boolean") {
454
- advanced.caffeinateWhileCloudConnect = patch.caffeinateWhileCloudConnect;
906
+ if (typeof patch.caffeinateWhileSlackListen === "boolean") {
907
+ advanced.caffeinateWhileSlackListen = patch.caffeinateWhileSlackListen;
908
+ delete advanced.caffeinateWhileCloudConnect;
909
+ } else if (typeof patch.caffeinateWhileCloudConnect === "boolean") {
910
+ advanced.caffeinateWhileSlackListen = patch.caffeinateWhileCloudConnect;
911
+ delete advanced.caffeinateWhileCloudConnect;
455
912
  }
456
913
  if (typeof patch.deleteBranchOnPurge === "boolean") {
457
914
  advanced.deleteBranchOnPurge = patch.deleteBranchOnPurge;
@@ -494,8 +951,11 @@ function autoRunAfterSetupEnabled(settings = loadAppSettings()) {
494
951
  function caffeinateWhileRunningEnabled(settings = loadAppSettings()) {
495
952
  return Boolean(settings.advanced.caffeinateWhileRunning);
496
953
  }
954
+ function caffeinateWhileSlackListenEnabled(settings = loadAppSettings()) {
955
+ return Boolean(settings.advanced.caffeinateWhileSlackListen);
956
+ }
497
957
  function caffeinateWhileCloudConnectEnabled(settings = loadAppSettings()) {
498
- return Boolean(settings.advanced.caffeinateWhileCloudConnect);
958
+ return caffeinateWhileSlackListenEnabled(settings);
499
959
  }
500
960
  function deleteBranchOnPurgeEnabled(settings = loadAppSettings()) {
501
961
  return Boolean(settings.advanced.deleteBranchOnPurge);
@@ -589,7 +1049,16 @@ function harnessEnvKey(harness) {
589
1049
  }
590
1050
 
591
1051
  export {
1052
+ setVaultMasterKey,
1053
+ persistVaultKeyInKeychain,
1054
+ readKeychainVaultKey,
1055
+ resolveVaultKey,
1056
+ readSecureJson,
1057
+ writeSecureJson,
1058
+ isSecureFileEncrypted,
1059
+ secureFileUnlocksWith,
592
1060
  HARNESS_ENV_KEYS,
1061
+ toPublicAppSettings,
593
1062
  appSettingsPath,
594
1063
  claudeUserSettingsPath,
595
1064
  loadAppSettings,
@@ -608,16 +1077,22 @@ export {
608
1077
  resolveThreadDefaults,
609
1078
  resolveNewThreadOptions,
610
1079
  isLinearConnected,
1080
+ saveLinearOAuth,
1081
+ disconnectLinearConnection,
611
1082
  getIssueSource,
612
1083
  resolveEffectiveIssueSource,
613
1084
  getLinearApiKey,
614
1085
  brightsyCloudConnectEnabled,
1086
+ slackListenEnabled,
1087
+ ensureSlackDeviceIdentity,
1088
+ slackAppLevelToken,
615
1089
  brightsyInjectWorktreeMcpEnabled,
616
1090
  brightsyCloudConnectAgent,
617
1091
  updateAdvancedSettings,
618
1092
  autoRenameBranchEnabled,
619
1093
  autoRunAfterSetupEnabled,
620
1094
  caffeinateWhileRunningEnabled,
1095
+ caffeinateWhileSlackListenEnabled,
621
1096
  caffeinateWhileCloudConnectEnabled,
622
1097
  deleteBranchOnPurgeEnabled,
623
1098
  autoArchiveOnMergeEnabled,
@@ -9,8 +9,8 @@ import {
9
9
  ensureConnectedBrightsyTeamTokens,
10
10
  getConnectedBrightsyTeamsRaw,
11
11
  listConnectedBrightsyTeams
12
- } from "./chunk-M5V4QIWF.js";
13
- import "./chunk-FGDJOJ2L.js";
12
+ } from "./chunk-VROPG6QF.js";
13
+ import "./chunk-AE5VBOFE.js";
14
14
  import "./chunk-7MV3RXSC.js";
15
15
  export {
16
16
  applyConnectedTeamToCli,
@@ -7,9 +7,9 @@ import {
7
7
  ensureConnectedBrightsyTeamTokens,
8
8
  getConnectedBrightsyTeamsRaw,
9
9
  listConnectedBrightsyTeams
10
- } from "./chunk-DB3I75FI.js";
10
+ } from "./chunk-BTL7EMGT.js";
11
11
  import "./chunk-M37RITA6.js";
12
- import "./chunk-4UIYN56C.js";
12
+ import "./chunk-D4DPQ552.js";
13
13
  export {
14
14
  applyConnectedTeamToCli,
15
15
  brightsyMcpServerName,
@@ -2,22 +2,25 @@
2
2
 
3
3
  import {
4
4
  COORDINATOR_TOOL_PLAYBOOK,
5
+ SLACK_REPLY_FORMATTING,
5
6
  coordinatorGreenfieldPlaybook,
6
7
  coordinatorSystemPrompt,
7
8
  coordinatorTurnReminder,
8
9
  enrichWorkspacesWithGithub,
9
10
  ensureGlobalCoordinatorCwd,
10
11
  formatWorkspaceInventory
11
- } from "./chunk-57WWNMFP.js";
12
- import "./chunk-LW5CMQGZ.js";
12
+ } from "./chunk-7NCIHRAU.js";
13
+ import "./chunk-OE7YBB5X.js";
13
14
  import "./chunk-B3SJXYIJ.js";
14
15
  import "./chunk-7FD7COKE.js";
15
- import "./chunk-FGDJOJ2L.js";
16
- import "./chunk-BPOTVI5J.js";
16
+ import "./chunk-AE5VBOFE.js";
17
+ import "./chunk-PTJJIQK7.js";
18
+ import "./chunk-NSTQ6QKD.js";
17
19
  import "./chunk-KGZBYWZ3.js";
18
20
  import "./chunk-7MV3RXSC.js";
19
21
  export {
20
22
  COORDINATOR_TOOL_PLAYBOOK,
23
+ SLACK_REPLY_FORMATTING,
21
24
  coordinatorGreenfieldPlaybook,
22
25
  coordinatorSystemPrompt,
23
26
  coordinatorTurnReminder,