@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.
- package/dist/{agents-PE3RHQS5.js → agents-GB7XL2LI.js} +6 -5
- package/dist/{agents-JBD7IAGK.js → agents-TCAK3MXN.js} +6 -5
- package/dist/{app-settings-ONKMFSEJ.js → app-settings-3TLA2EJ7.js} +16 -1
- package/dist/{app-settings-GJVZGU3M.js → app-settings-P42Z3VOB.js} +16 -1
- package/dist/caffeinate-hold-BEJIYPJ7.js +107 -0
- package/dist/caffeinate-hold-KLC6SABD.js +14 -0
- package/dist/{chunk-DPDGMMGI.js → chunk-3CY6WJ7O.js} +2 -2
- package/dist/{chunk-ROXEENBT.js → chunk-57ROTAFD.js} +62 -3
- package/dist/{chunk-N5LMEP65.js → chunk-7NCIHRAU.js} +22 -4
- package/dist/{chunk-OICHV4DH.js → chunk-DGPUAZA4.js} +62 -3
- package/dist/{chunk-VX6ACNXS.js → chunk-H2IXNFQ2.js} +22 -4
- package/dist/chunk-JAWEDVVA.js +106 -0
- package/dist/chunk-JT7R45JB.js +20 -0
- package/dist/{chunk-EEBDVLDK.js → chunk-MF2YMEGD.js} +61 -9
- package/dist/{chunk-47RBLBRK.js → chunk-NG7S3OPX.js} +10 -4
- package/dist/chunk-NSTQ6QKD.js +23 -0
- package/dist/{chunk-REKGFVUX.js → chunk-OE7YBB5X.js} +61 -9
- package/dist/{chunk-BPOTVI5J.js → chunk-PTJJIQK7.js} +472 -18
- package/dist/{chunk-6Q7H6SMX.js → chunk-S25IHL5H.js} +10 -4
- package/dist/{chunk-QQC2D4MY.js → chunk-UH57WVFP.js} +2 -2
- package/dist/{chunk-MHR4OGZ4.js → chunk-W7YOTDVO.js} +493 -18
- package/dist/{coordinator-prompt-YZYZ5P6P.js → coordinator-prompt-2J4PIMUF.js} +6 -3
- package/dist/{coordinator-prompt-HD6IMCKS.js → coordinator-prompt-QYYG2IBB.js} +6 -3
- package/dist/{global-workspace-Z6GF7D2K.js → global-workspace-KHIFQUUM.js} +11 -4
- package/dist/{global-workspace-7P5NG3JZ.js → global-workspace-SVRYNJZA.js} +11 -4
- package/dist/index.cjs +4184 -850
- package/dist/index.d.cts +543 -21
- package/dist/index.d.ts +543 -21
- package/dist/index.js +3285 -748
- package/dist/mcp/run-stdio.cjs +1995 -482
- package/dist/mcp/run-stdio.js +943 -151
- package/dist/{workspaces-BWJNGEE6.js → workspaces-AHFTHVBT.js} +6 -5
- package/dist/{workspaces-O6EZGKQK.js → workspaces-SVQIEVIZ.js} +6 -5
- package/dist/{worktree-VDXLUOWR.js → worktree-KWXHMKRN.js} +1 -1
- package/dist/{worktree-ZPSKEZFE.js → worktree-VOCH3WS3.js} +1 -1
- 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 {
|
|
10
|
-
import {
|
|
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
|
|
252
|
+
return join2(appDataDir(), "settings.json");
|
|
28
253
|
}
|
|
29
254
|
function claudeUserSettingsPath() {
|
|
30
|
-
return
|
|
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.
|
|
127
|
-
out.
|
|
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
|
|
506
|
+
function readSettingsFile() {
|
|
227
507
|
const path = appSettingsPath();
|
|
228
|
-
if (!
|
|
229
|
-
return { ...EMPTY_SETTINGS };
|
|
230
|
-
}
|
|
508
|
+
if (!existsSync2(path)) return { ...EMPTY_SETTINGS };
|
|
231
509
|
try {
|
|
232
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
454
|
-
advanced.
|
|
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
|
|
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,
|
|
@@ -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-
|
|
12
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-7NCIHRAU.js";
|
|
13
|
+
import "./chunk-OE7YBB5X.js";
|
|
13
14
|
import "./chunk-B3SJXYIJ.js";
|
|
14
15
|
import "./chunk-7FD7COKE.js";
|
|
15
16
|
import "./chunk-AE5VBOFE.js";
|
|
16
|
-
import "./chunk-
|
|
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,
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
COORDINATOR_TOOL_PLAYBOOK,
|
|
3
|
+
SLACK_REPLY_FORMATTING,
|
|
3
4
|
coordinatorGreenfieldPlaybook,
|
|
4
5
|
coordinatorSystemPrompt,
|
|
5
6
|
coordinatorTurnReminder,
|
|
6
7
|
enrichWorkspacesWithGithub,
|
|
7
8
|
ensureGlobalCoordinatorCwd,
|
|
8
9
|
formatWorkspaceInventory
|
|
9
|
-
} from "./chunk-
|
|
10
|
-
import "./chunk-
|
|
11
|
-
import "./chunk-
|
|
10
|
+
} from "./chunk-H2IXNFQ2.js";
|
|
11
|
+
import "./chunk-W7YOTDVO.js";
|
|
12
|
+
import "./chunk-JT7R45JB.js";
|
|
13
|
+
import "./chunk-MF2YMEGD.js";
|
|
12
14
|
import "./chunk-FKOIHGKV.js";
|
|
13
15
|
import "./chunk-FWJYLBO6.js";
|
|
14
16
|
import "./chunk-77WWLBCI.js";
|
|
@@ -16,6 +18,7 @@ import "./chunk-M37RITA6.js";
|
|
|
16
18
|
import "./chunk-D4DPQ552.js";
|
|
17
19
|
export {
|
|
18
20
|
COORDINATOR_TOOL_PLAYBOOK,
|
|
21
|
+
SLACK_REPLY_FORMATTING,
|
|
19
22
|
coordinatorGreenfieldPlaybook,
|
|
20
23
|
coordinatorSystemPrompt,
|
|
21
24
|
coordinatorTurnReminder,
|