@sideboard-ai/core 0.1.219 → 0.1.220
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-IFVGY77W.js → agents-6UOWJLXX.js} +3 -3
- package/dist/{agents-IK3U4Z2O.js → agents-DYSZ2JFY.js} +3 -3
- package/dist/{chunk-IAP5NXQ4.js → chunk-3Y4FIARC.js} +3 -2
- package/dist/{chunk-XGE6YUO6.js → chunk-ALDVGAIO.js} +3 -3
- package/dist/{chunk-EIICOB7P.js → chunk-B73KKJQB.js} +4 -4
- package/dist/chunk-P2BSMNHB.js +179 -0
- package/dist/{chunk-IILG7DPX.js → chunk-QILV2BRW.js} +31 -18
- package/dist/chunk-ROPGQRPU.js +178 -0
- package/dist/{chunk-SVXIJ2IC.js → chunk-UNMMKZ4Q.js} +26 -13
- package/dist/{chunk-K642ORWF.js → chunk-YG2X34EF.js} +3 -2
- package/dist/{connected-teams-LX5DLL2I.js → connected-teams-PRHZS6B4.js} +2 -2
- package/dist/{connected-teams-PQETXUWV.js → connected-teams-YOIB42RB.js} +2 -2
- package/dist/index.cjs +132 -34
- package/dist/index.d.cts +35 -2
- package/dist/index.d.ts +35 -2
- package/dist/index.js +14 -4
- package/dist/mcp/run-stdio.cjs +114 -32
- package/dist/mcp/run-stdio.js +4 -4
- package/dist/oauth-K6CWZEQO.js +23 -0
- package/dist/oauth-S3M7B475.js +21 -0
- package/dist/{orchestrator-5VVR7LOU.js → orchestrator-P4KNHFHG.js} +4 -4
- package/dist/{orchestrator-LFC3V3AX.js → orchestrator-Y5IDLJ7D.js} +4 -4
- package/package.json +1 -1
- package/dist/chunk-AN3THU7F.js +0 -103
- package/dist/chunk-Y4MIXGPA.js +0 -104
- package/dist/oauth-6ZQ4Q4XN.js +0 -11
- package/dist/oauth-O7UGLZOY.js +0 -13
|
@@ -44,13 +44,13 @@ import {
|
|
|
44
44
|
resolveLoginCommand,
|
|
45
45
|
resolveQuotaFallbackAgent,
|
|
46
46
|
whichOnPath
|
|
47
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-YG2X34EF.js";
|
|
48
48
|
import "./chunk-N2VST3NL.js";
|
|
49
49
|
import "./chunk-KTZESHYH.js";
|
|
50
50
|
import "./chunk-6K64FJDD.js";
|
|
51
51
|
import "./chunk-PO4M4JAJ.js";
|
|
52
|
-
import "./chunk-
|
|
53
|
-
import "./chunk-
|
|
52
|
+
import "./chunk-QILV2BRW.js";
|
|
53
|
+
import "./chunk-ROPGQRPU.js";
|
|
54
54
|
import "./chunk-N7WZ6NIN.js";
|
|
55
55
|
import {
|
|
56
56
|
cursorSdkMessageToEvents,
|
|
@@ -50,10 +50,10 @@ import {
|
|
|
50
50
|
resolveQuotaFallbackAgent,
|
|
51
51
|
turnCostUsdFromCursorUsage,
|
|
52
52
|
whichOnPath
|
|
53
|
-
} from "./chunk-
|
|
53
|
+
} from "./chunk-3Y4FIARC.js";
|
|
54
54
|
import "./chunk-P2EAAM3H.js";
|
|
55
|
-
import "./chunk-
|
|
56
|
-
import "./chunk-
|
|
55
|
+
import "./chunk-UNMMKZ4Q.js";
|
|
56
|
+
import "./chunk-P2BSMNHB.js";
|
|
57
57
|
import "./chunk-VKQGAWHY.js";
|
|
58
58
|
import "./chunk-NFWPDEN4.js";
|
|
59
59
|
import "./chunk-D6JWYJ4P.js";
|
|
@@ -8,10 +8,10 @@ import {
|
|
|
8
8
|
brightsyMcpServerName,
|
|
9
9
|
ensureCliTeamTracked,
|
|
10
10
|
ensureConnectedBrightsyTeamTokens
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-UNMMKZ4Q.js";
|
|
12
12
|
import {
|
|
13
13
|
loadBrightsyConfig
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-P2BSMNHB.js";
|
|
15
15
|
import {
|
|
16
16
|
httpFetch
|
|
17
17
|
} from "./chunk-VKQGAWHY.js";
|
|
@@ -1313,6 +1313,7 @@ function teamEnv(team) {
|
|
|
1313
1313
|
return {
|
|
1314
1314
|
BRIGHTSY_API_TOKEN: team.access_token,
|
|
1315
1315
|
...team.refresh_token ? { BRIGHTSY_REFRESH_TOKEN: team.refresh_token } : {},
|
|
1316
|
+
...team.oauth_client_id ? { BRIGHTSY_OAUTH_CLIENT_ID: team.oauth_client_id } : {},
|
|
1316
1317
|
BRIGHTSY_ACCOUNT_ID: team.id,
|
|
1317
1318
|
BRIGHTSY_API_URL: endpoint
|
|
1318
1319
|
};
|
|
@@ -42,7 +42,7 @@ import {
|
|
|
42
42
|
summarizeTurnStderr,
|
|
43
43
|
toolDescription,
|
|
44
44
|
turnFailChatText
|
|
45
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-3Y4FIARC.js";
|
|
46
46
|
import {
|
|
47
47
|
SLACK_REPLY_FORMATTING,
|
|
48
48
|
addWorkspace,
|
|
@@ -418,7 +418,7 @@ async function continueSourceThread(threadId, prompt) {
|
|
|
418
418
|
await continueOnReply(threadId, prompt);
|
|
419
419
|
return;
|
|
420
420
|
}
|
|
421
|
-
const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-
|
|
421
|
+
const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-P4KNHFHG.js");
|
|
422
422
|
await getOrchestrator2().send(threadId, prompt);
|
|
423
423
|
} catch {
|
|
424
424
|
}
|
|
@@ -6957,7 +6957,7 @@ function formatScheduledPrompt(name, prompt) {
|
|
|
6957
6957
|
${prompt}`;
|
|
6958
6958
|
}
|
|
6959
6959
|
async function defaultDeps() {
|
|
6960
|
-
const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-
|
|
6960
|
+
const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-P4KNHFHG.js");
|
|
6961
6961
|
const orch = getOrchestrator2();
|
|
6962
6962
|
return {
|
|
6963
6963
|
findThread: (id) => findThreadByRef(id),
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
resolveQuotaFallbackAgent,
|
|
25
25
|
stripBrightsyNdjsonNoise,
|
|
26
26
|
toolDescription
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-YG2X34EF.js";
|
|
28
28
|
import {
|
|
29
29
|
SLACK_REPLY_FORMATTING,
|
|
30
30
|
addWorkspace,
|
|
@@ -482,7 +482,7 @@ async function continueSourceThread(threadId, prompt) {
|
|
|
482
482
|
await continueOnReply(threadId, prompt);
|
|
483
483
|
return;
|
|
484
484
|
}
|
|
485
|
-
const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-
|
|
485
|
+
const { getOrchestrator: getOrchestrator2 } = await import("./orchestrator-Y5IDLJ7D.js");
|
|
486
486
|
await getOrchestrator2().send(threadId, prompt);
|
|
487
487
|
} catch {
|
|
488
488
|
}
|
|
@@ -4178,7 +4178,7 @@ async function createThread(input, _onSetupLine) {
|
|
|
4178
4178
|
return readThread(thread.id) ?? thread;
|
|
4179
4179
|
}
|
|
4180
4180
|
async function listLinearIssues(agent, repoPath) {
|
|
4181
|
-
const { getAdapter: getAdapter2 } = await import("./agents-
|
|
4181
|
+
const { getAdapter: getAdapter2 } = await import("./agents-6UOWJLXX.js");
|
|
4182
4182
|
await requireAgent(agent, { requireLinear: true });
|
|
4183
4183
|
const adapter = getAdapter2(agent);
|
|
4184
4184
|
if (!adapter.listLinearIssues) {
|
|
@@ -7332,7 +7332,7 @@ function formatScheduledPrompt(name, prompt) {
|
|
|
7332
7332
|
${prompt}`;
|
|
7333
7333
|
}
|
|
7334
7334
|
async function defaultDeps() {
|
|
7335
|
-
const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-
|
|
7335
|
+
const { getOrchestrator: getOrchestrator2, startOrchestration: startOrchestration2 } = await import("./orchestrator-Y5IDLJ7D.js");
|
|
7336
7336
|
const orch = getOrchestrator2();
|
|
7337
7337
|
return {
|
|
7338
7338
|
findThread: (id) => findThreadByRef(id),
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
formatFetchError
|
|
5
|
+
} from "./chunk-VKQGAWHY.js";
|
|
6
|
+
|
|
7
|
+
// src/brightsy/config.ts
|
|
8
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
9
|
+
import { homedir } from "os";
|
|
10
|
+
import { join } from "path";
|
|
11
|
+
function brightsyConfigPath() {
|
|
12
|
+
const override = process.env.BRIGHTSY_CONFIG?.trim();
|
|
13
|
+
if (override) return override;
|
|
14
|
+
return join(homedir(), ".brightsy", "config.json");
|
|
15
|
+
}
|
|
16
|
+
function loadBrightsyConfig() {
|
|
17
|
+
const path = brightsyConfigPath();
|
|
18
|
+
if (!existsSync(path)) {
|
|
19
|
+
throw new Error("Brightsy not logged in \u2014 run `brightsy login` first");
|
|
20
|
+
}
|
|
21
|
+
const raw = JSON.parse(readFileSync(path, "utf8"));
|
|
22
|
+
if (!raw.access_token || !raw.account_id) {
|
|
23
|
+
throw new Error("Brightsy config incomplete \u2014 run `brightsy login`");
|
|
24
|
+
}
|
|
25
|
+
return raw;
|
|
26
|
+
}
|
|
27
|
+
function saveBrightsyConfig(cfg) {
|
|
28
|
+
writeFileSync(brightsyConfigPath(), `${JSON.stringify(cfg, null, 2)}
|
|
29
|
+
`, {
|
|
30
|
+
mode: 384
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// src/brightsy/oauth.ts
|
|
35
|
+
var REFRESH_SKEW_MS = 6e4;
|
|
36
|
+
var BRIGHTSY_OAUTH_CLIENT_ID = "sideboard";
|
|
37
|
+
var BRIGHTSY_OAUTH_REDIRECT = "http://127.0.0.1:19850/callback";
|
|
38
|
+
function defaultFetch(fetchImpl) {
|
|
39
|
+
return fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
40
|
+
}
|
|
41
|
+
function normalizeBrightsyEndpoint(endpoint) {
|
|
42
|
+
return (endpoint || "https://brightsy.ai").replace(/\/$/, "");
|
|
43
|
+
}
|
|
44
|
+
function brightsyAccessTokenNeedsRefresh(expiresAt, now = Date.now()) {
|
|
45
|
+
if (typeof expiresAt !== "number" || !Number.isFinite(expiresAt) || expiresAt <= 0) {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
return now >= expiresAt - REFRESH_SKEW_MS;
|
|
49
|
+
}
|
|
50
|
+
function applyGrant(current, grant) {
|
|
51
|
+
return {
|
|
52
|
+
access_token: grant.access_token,
|
|
53
|
+
refresh_token: grant.refresh_token || current.refresh_token,
|
|
54
|
+
expires_at: grant.expires_at ?? current.expires_at
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
async function registerBrightsyOAuthClient(opts) {
|
|
58
|
+
const endpoint = normalizeBrightsyEndpoint(opts.endpoint);
|
|
59
|
+
const url = `${endpoint}/oauth/register`;
|
|
60
|
+
const clientId = (opts.clientId || BRIGHTSY_OAUTH_CLIENT_ID).trim();
|
|
61
|
+
const redirectUri = opts.redirectUri || BRIGHTSY_OAUTH_REDIRECT;
|
|
62
|
+
const fetchImpl = defaultFetch(opts.fetchImpl);
|
|
63
|
+
let res;
|
|
64
|
+
try {
|
|
65
|
+
res = await fetchImpl(url, {
|
|
66
|
+
method: "POST",
|
|
67
|
+
headers: { "Content-Type": "application/json" },
|
|
68
|
+
body: JSON.stringify({
|
|
69
|
+
client_id: clientId,
|
|
70
|
+
client_name: "Sideboard",
|
|
71
|
+
description: "Sideboard desktop Brightsy MCP client",
|
|
72
|
+
redirect_uris: [redirectUri],
|
|
73
|
+
scope: "brightsy:api",
|
|
74
|
+
grant_types: ["authorization_code", "refresh_token"],
|
|
75
|
+
response_types: ["code"],
|
|
76
|
+
token_endpoint_auth_method: "none"
|
|
77
|
+
})
|
|
78
|
+
});
|
|
79
|
+
} catch (err) {
|
|
80
|
+
throw new Error(formatFetchError(err, url));
|
|
81
|
+
}
|
|
82
|
+
if (!res.ok) return null;
|
|
83
|
+
const data = await res.json();
|
|
84
|
+
if (!data.client_id?.trim()) return null;
|
|
85
|
+
return {
|
|
86
|
+
client_id: data.client_id.trim(),
|
|
87
|
+
client_secret: data.client_secret?.trim() || void 0
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
async function ensureBrightsyOAuthClientId(opts) {
|
|
91
|
+
const existing = opts.clientId?.trim();
|
|
92
|
+
if (existing) return existing;
|
|
93
|
+
const registered = await registerBrightsyOAuthClient({
|
|
94
|
+
endpoint: opts.endpoint,
|
|
95
|
+
fetchImpl: opts.fetchImpl
|
|
96
|
+
});
|
|
97
|
+
return registered?.client_id || BRIGHTSY_OAUTH_CLIENT_ID;
|
|
98
|
+
}
|
|
99
|
+
async function refreshBrightsyAccessToken(opts) {
|
|
100
|
+
const endpoint = normalizeBrightsyEndpoint(opts.endpoint);
|
|
101
|
+
const url = `${endpoint}/oauth/token`;
|
|
102
|
+
const fetchImpl = defaultFetch(opts.fetchImpl);
|
|
103
|
+
const clientId = opts.clientId?.trim() || BRIGHTSY_OAUTH_CLIENT_ID;
|
|
104
|
+
let res;
|
|
105
|
+
try {
|
|
106
|
+
res = await fetchImpl(url, {
|
|
107
|
+
method: "POST",
|
|
108
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
109
|
+
body: new URLSearchParams({
|
|
110
|
+
grant_type: "refresh_token",
|
|
111
|
+
refresh_token: opts.refreshToken,
|
|
112
|
+
client_id: clientId
|
|
113
|
+
})
|
|
114
|
+
});
|
|
115
|
+
} catch (err) {
|
|
116
|
+
throw new Error(formatFetchError(err, url));
|
|
117
|
+
}
|
|
118
|
+
if (!res.ok) return null;
|
|
119
|
+
const data = await res.json();
|
|
120
|
+
if (!data.access_token) return null;
|
|
121
|
+
return {
|
|
122
|
+
access_token: data.access_token,
|
|
123
|
+
refresh_token: data.refresh_token,
|
|
124
|
+
expires_at: data.expires_in ? Date.now() + data.expires_in * 1e3 : void 0
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
var refreshInFlight = null;
|
|
128
|
+
async function ensureBrightsyLocalConfigFreshOnce(opts) {
|
|
129
|
+
let cfg;
|
|
130
|
+
try {
|
|
131
|
+
cfg = loadBrightsyConfig();
|
|
132
|
+
} catch {
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
const refreshToken = cfg.refresh_token;
|
|
136
|
+
if (!refreshToken || !brightsyAccessTokenNeedsRefresh(cfg.expires_at)) {
|
|
137
|
+
return cfg;
|
|
138
|
+
}
|
|
139
|
+
const endpoint = normalizeBrightsyEndpoint(cfg.endpoint);
|
|
140
|
+
const clientId = await ensureBrightsyOAuthClientId({
|
|
141
|
+
endpoint,
|
|
142
|
+
clientId: cfg.oauth_client_id,
|
|
143
|
+
fetchImpl: opts?.fetchImpl
|
|
144
|
+
});
|
|
145
|
+
if (clientId !== cfg.oauth_client_id) {
|
|
146
|
+
cfg = { ...cfg, oauth_client_id: clientId };
|
|
147
|
+
saveBrightsyConfig(cfg);
|
|
148
|
+
}
|
|
149
|
+
const grant = await refreshBrightsyAccessToken({
|
|
150
|
+
endpoint,
|
|
151
|
+
refreshToken,
|
|
152
|
+
clientId,
|
|
153
|
+
fetchImpl: opts?.fetchImpl
|
|
154
|
+
});
|
|
155
|
+
if (!grant) return cfg;
|
|
156
|
+
const next = { ...cfg, ...applyGrant(cfg, grant), oauth_client_id: clientId };
|
|
157
|
+
saveBrightsyConfig(next);
|
|
158
|
+
return next;
|
|
159
|
+
}
|
|
160
|
+
async function ensureBrightsyLocalConfigFresh(opts) {
|
|
161
|
+
if (refreshInFlight) return refreshInFlight;
|
|
162
|
+
refreshInFlight = ensureBrightsyLocalConfigFreshOnce(opts).finally(() => {
|
|
163
|
+
refreshInFlight = null;
|
|
164
|
+
});
|
|
165
|
+
return refreshInFlight;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export {
|
|
169
|
+
loadBrightsyConfig,
|
|
170
|
+
saveBrightsyConfig,
|
|
171
|
+
BRIGHTSY_OAUTH_CLIENT_ID,
|
|
172
|
+
BRIGHTSY_OAUTH_REDIRECT,
|
|
173
|
+
normalizeBrightsyEndpoint,
|
|
174
|
+
brightsyAccessTokenNeedsRefresh,
|
|
175
|
+
registerBrightsyOAuthClient,
|
|
176
|
+
ensureBrightsyOAuthClientId,
|
|
177
|
+
refreshBrightsyAccessToken,
|
|
178
|
+
ensureBrightsyLocalConfigFresh
|
|
179
|
+
};
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
brightsyAccessTokenNeedsRefresh,
|
|
3
3
|
ensureBrightsyLocalConfigFresh,
|
|
4
|
+
ensureBrightsyOAuthClientId,
|
|
4
5
|
loadBrightsyConfig,
|
|
5
6
|
refreshBrightsyAccessToken,
|
|
6
7
|
saveBrightsyConfig
|
|
7
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-ROPGQRPU.js";
|
|
8
9
|
import {
|
|
9
10
|
appDataDir
|
|
10
11
|
} from "./chunk-WFUDY6PC.js";
|
|
@@ -18,7 +19,7 @@ import { join } from "path";
|
|
|
18
19
|
|
|
19
20
|
// src/brightsy/accounts.ts
|
|
20
21
|
async function loadConnectedTeams() {
|
|
21
|
-
const { listConnectedBrightsyTeams: listConnectedBrightsyTeams2 } = await import("./connected-teams-
|
|
22
|
+
const { listConnectedBrightsyTeams: listConnectedBrightsyTeams2 } = await import("./connected-teams-PRHZS6B4.js");
|
|
22
23
|
return listConnectedBrightsyTeams2();
|
|
23
24
|
}
|
|
24
25
|
async function runBrightsyTeamsJson(args) {
|
|
@@ -46,15 +47,15 @@ async function listBrightsyAccounts() {
|
|
|
46
47
|
}
|
|
47
48
|
async function getBrightsySession() {
|
|
48
49
|
try {
|
|
49
|
-
const { ensureBrightsyLocalConfigFresh: ensureBrightsyLocalConfigFresh2 } = await import("./oauth-
|
|
50
|
-
const { ensureConnectedBrightsyTeamTokens: ensureConnectedBrightsyTeamTokens2 } = await import("./connected-teams-
|
|
50
|
+
const { ensureBrightsyLocalConfigFresh: ensureBrightsyLocalConfigFresh2 } = await import("./oauth-S3M7B475.js");
|
|
51
|
+
const { ensureConnectedBrightsyTeamTokens: ensureConnectedBrightsyTeamTokens2 } = await import("./connected-teams-PRHZS6B4.js");
|
|
51
52
|
await ensureBrightsyLocalConfigFresh2();
|
|
52
53
|
await ensureConnectedBrightsyTeamTokens2();
|
|
53
54
|
loadBrightsyConfig();
|
|
54
55
|
const data = await runBrightsyTeamsJson([]);
|
|
55
56
|
const accounts = Array.isArray(data.teams) ? data.teams : [];
|
|
56
57
|
const active = data.active ?? accounts.find((a) => a.active) ?? null;
|
|
57
|
-
const { ensureCliTeamTracked: ensureCliTeamTracked2 } = await import("./connected-teams-
|
|
58
|
+
const { ensureCliTeamTracked: ensureCliTeamTracked2 } = await import("./connected-teams-PRHZS6B4.js");
|
|
58
59
|
const connectedTeams = ensureCliTeamTracked2(
|
|
59
60
|
active ? { id: active.id, slug: active.slug, name: active.name } : void 0
|
|
60
61
|
);
|
|
@@ -94,7 +95,7 @@ async function performBrightsyAccountSwitch(accountIdOrSlug) {
|
|
|
94
95
|
return { cfg, target: data.active };
|
|
95
96
|
}
|
|
96
97
|
async function switchBrightsyAccount(accountIdOrSlug) {
|
|
97
|
-
const { connectBrightsyTeam: connectBrightsyTeam2 } = await import("./connected-teams-
|
|
98
|
+
const { connectBrightsyTeam: connectBrightsyTeam2 } = await import("./connected-teams-PRHZS6B4.js");
|
|
98
99
|
await connectBrightsyTeam2(accountIdOrSlug);
|
|
99
100
|
return getBrightsySession();
|
|
100
101
|
}
|
|
@@ -147,10 +148,10 @@ function applyConnectedTeamToCli(team) {
|
|
|
147
148
|
account_id: team.id,
|
|
148
149
|
account_slug: team.slug,
|
|
149
150
|
endpoint: team.endpoint ?? base.endpoint ?? "https://brightsy.ai",
|
|
150
|
-
oauth_client_id: base.oauth_client_id
|
|
151
|
+
oauth_client_id: team.oauth_client_id ?? base.oauth_client_id
|
|
151
152
|
});
|
|
152
153
|
}
|
|
153
|
-
async function refreshTeamToken(team) {
|
|
154
|
+
async function refreshTeamToken(team, opts) {
|
|
154
155
|
if (!team.refresh_token) return team;
|
|
155
156
|
const cfg = (() => {
|
|
156
157
|
try {
|
|
@@ -159,21 +160,31 @@ async function refreshTeamToken(team) {
|
|
|
159
160
|
return null;
|
|
160
161
|
}
|
|
161
162
|
})();
|
|
163
|
+
const endpoint = team.endpoint || "https://brightsy.ai";
|
|
164
|
+
const clientId = await ensureBrightsyOAuthClientId({
|
|
165
|
+
endpoint,
|
|
166
|
+
clientId: team.oauth_client_id || cfg?.oauth_client_id,
|
|
167
|
+
fetchImpl: opts?.fetchImpl
|
|
168
|
+
});
|
|
162
169
|
const grant = await refreshBrightsyAccessToken({
|
|
163
|
-
endpoint
|
|
170
|
+
endpoint,
|
|
164
171
|
refreshToken: team.refresh_token,
|
|
165
|
-
clientId
|
|
172
|
+
clientId,
|
|
173
|
+
fetchImpl: opts?.fetchImpl
|
|
166
174
|
});
|
|
167
|
-
if (!grant) return team;
|
|
175
|
+
if (!grant) return { ...team, oauth_client_id: clientId || team.oauth_client_id };
|
|
168
176
|
return {
|
|
169
177
|
...team,
|
|
170
178
|
access_token: grant.access_token,
|
|
171
179
|
refresh_token: grant.refresh_token || team.refresh_token,
|
|
172
|
-
expires_at: grant.expires_at ?? team.expires_at
|
|
180
|
+
expires_at: grant.expires_at ?? team.expires_at,
|
|
181
|
+
oauth_client_id: clientId
|
|
173
182
|
};
|
|
174
183
|
}
|
|
175
|
-
async function ensureConnectedBrightsyTeamTokens() {
|
|
176
|
-
await ensureBrightsyLocalConfigFresh().catch(
|
|
184
|
+
async function ensureConnectedBrightsyTeamTokens(opts) {
|
|
185
|
+
await ensureBrightsyLocalConfigFresh({ fetchImpl: opts?.fetchImpl }).catch(
|
|
186
|
+
() => null
|
|
187
|
+
);
|
|
177
188
|
const teams = readStore();
|
|
178
189
|
if (teams.length === 0) return [];
|
|
179
190
|
const next = [];
|
|
@@ -183,8 +194,8 @@ async function ensureConnectedBrightsyTeamTokens() {
|
|
|
183
194
|
next.push(team);
|
|
184
195
|
continue;
|
|
185
196
|
}
|
|
186
|
-
const refreshed = await refreshTeamToken(team);
|
|
187
|
-
if (refreshed.access_token !== team.access_token || refreshed.refresh_token !== team.refresh_token || refreshed.expires_at !== team.expires_at) {
|
|
197
|
+
const refreshed = await refreshTeamToken(team, opts);
|
|
198
|
+
if (refreshed.access_token !== team.access_token || refreshed.refresh_token !== team.refresh_token || refreshed.expires_at !== team.expires_at || refreshed.oauth_client_id !== team.oauth_client_id) {
|
|
188
199
|
changed = true;
|
|
189
200
|
}
|
|
190
201
|
next.push(refreshed);
|
|
@@ -214,7 +225,8 @@ function ensureCliTeamTracked(meta) {
|
|
|
214
225
|
access_token: cfg.access_token,
|
|
215
226
|
refresh_token: cfg.refresh_token,
|
|
216
227
|
expires_at: cfg.expires_at,
|
|
217
|
-
endpoint: cfg.endpoint
|
|
228
|
+
endpoint: cfg.endpoint,
|
|
229
|
+
oauth_client_id: cfg.oauth_client_id
|
|
218
230
|
};
|
|
219
231
|
writeStore([...existing, team]);
|
|
220
232
|
} catch {
|
|
@@ -241,7 +253,8 @@ async function connectBrightsyTeam(accountIdOrSlug) {
|
|
|
241
253
|
access_token: minted.access_token,
|
|
242
254
|
refresh_token: minted.refresh_token,
|
|
243
255
|
expires_at: minted.expires_at,
|
|
244
|
-
endpoint: minted.endpoint
|
|
256
|
+
endpoint: minted.endpoint,
|
|
257
|
+
oauth_client_id: minted.oauth_client_id
|
|
245
258
|
};
|
|
246
259
|
writeStore([...existing, team]);
|
|
247
260
|
return listConnectedBrightsyTeams();
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import {
|
|
2
|
+
formatFetchError
|
|
3
|
+
} from "./chunk-N7WZ6NIN.js";
|
|
4
|
+
|
|
5
|
+
// src/brightsy/config.ts
|
|
6
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
7
|
+
import { homedir } from "os";
|
|
8
|
+
import { join } from "path";
|
|
9
|
+
function brightsyConfigPath() {
|
|
10
|
+
const override = process.env.BRIGHTSY_CONFIG?.trim();
|
|
11
|
+
if (override) return override;
|
|
12
|
+
return join(homedir(), ".brightsy", "config.json");
|
|
13
|
+
}
|
|
14
|
+
function loadBrightsyConfig() {
|
|
15
|
+
const path = brightsyConfigPath();
|
|
16
|
+
if (!existsSync(path)) {
|
|
17
|
+
throw new Error("Brightsy not logged in \u2014 run `brightsy login` first");
|
|
18
|
+
}
|
|
19
|
+
const raw = JSON.parse(readFileSync(path, "utf8"));
|
|
20
|
+
if (!raw.access_token || !raw.account_id) {
|
|
21
|
+
throw new Error("Brightsy config incomplete \u2014 run `brightsy login`");
|
|
22
|
+
}
|
|
23
|
+
return raw;
|
|
24
|
+
}
|
|
25
|
+
function saveBrightsyConfig(cfg) {
|
|
26
|
+
writeFileSync(brightsyConfigPath(), `${JSON.stringify(cfg, null, 2)}
|
|
27
|
+
`, {
|
|
28
|
+
mode: 384
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// src/brightsy/oauth.ts
|
|
33
|
+
var REFRESH_SKEW_MS = 6e4;
|
|
34
|
+
var BRIGHTSY_OAUTH_CLIENT_ID = "sideboard";
|
|
35
|
+
var BRIGHTSY_OAUTH_REDIRECT = "http://127.0.0.1:19850/callback";
|
|
36
|
+
function defaultFetch(fetchImpl) {
|
|
37
|
+
return fetchImpl ?? globalThis.fetch.bind(globalThis);
|
|
38
|
+
}
|
|
39
|
+
function normalizeBrightsyEndpoint(endpoint) {
|
|
40
|
+
return (endpoint || "https://brightsy.ai").replace(/\/$/, "");
|
|
41
|
+
}
|
|
42
|
+
function brightsyAccessTokenNeedsRefresh(expiresAt, now = Date.now()) {
|
|
43
|
+
if (typeof expiresAt !== "number" || !Number.isFinite(expiresAt) || expiresAt <= 0) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
return now >= expiresAt - REFRESH_SKEW_MS;
|
|
47
|
+
}
|
|
48
|
+
function applyGrant(current, grant) {
|
|
49
|
+
return {
|
|
50
|
+
access_token: grant.access_token,
|
|
51
|
+
refresh_token: grant.refresh_token || current.refresh_token,
|
|
52
|
+
expires_at: grant.expires_at ?? current.expires_at
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
async function registerBrightsyOAuthClient(opts) {
|
|
56
|
+
const endpoint = normalizeBrightsyEndpoint(opts.endpoint);
|
|
57
|
+
const url = `${endpoint}/oauth/register`;
|
|
58
|
+
const clientId = (opts.clientId || BRIGHTSY_OAUTH_CLIENT_ID).trim();
|
|
59
|
+
const redirectUri = opts.redirectUri || BRIGHTSY_OAUTH_REDIRECT;
|
|
60
|
+
const fetchImpl = defaultFetch(opts.fetchImpl);
|
|
61
|
+
let res;
|
|
62
|
+
try {
|
|
63
|
+
res = await fetchImpl(url, {
|
|
64
|
+
method: "POST",
|
|
65
|
+
headers: { "Content-Type": "application/json" },
|
|
66
|
+
body: JSON.stringify({
|
|
67
|
+
client_id: clientId,
|
|
68
|
+
client_name: "Sideboard",
|
|
69
|
+
description: "Sideboard desktop Brightsy MCP client",
|
|
70
|
+
redirect_uris: [redirectUri],
|
|
71
|
+
scope: "brightsy:api",
|
|
72
|
+
grant_types: ["authorization_code", "refresh_token"],
|
|
73
|
+
response_types: ["code"],
|
|
74
|
+
token_endpoint_auth_method: "none"
|
|
75
|
+
})
|
|
76
|
+
});
|
|
77
|
+
} catch (err) {
|
|
78
|
+
throw new Error(formatFetchError(err, url));
|
|
79
|
+
}
|
|
80
|
+
if (!res.ok) return null;
|
|
81
|
+
const data = await res.json();
|
|
82
|
+
if (!data.client_id?.trim()) return null;
|
|
83
|
+
return {
|
|
84
|
+
client_id: data.client_id.trim(),
|
|
85
|
+
client_secret: data.client_secret?.trim() || void 0
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
async function ensureBrightsyOAuthClientId(opts) {
|
|
89
|
+
const existing = opts.clientId?.trim();
|
|
90
|
+
if (existing) return existing;
|
|
91
|
+
const registered = await registerBrightsyOAuthClient({
|
|
92
|
+
endpoint: opts.endpoint,
|
|
93
|
+
fetchImpl: opts.fetchImpl
|
|
94
|
+
});
|
|
95
|
+
return registered?.client_id || BRIGHTSY_OAUTH_CLIENT_ID;
|
|
96
|
+
}
|
|
97
|
+
async function refreshBrightsyAccessToken(opts) {
|
|
98
|
+
const endpoint = normalizeBrightsyEndpoint(opts.endpoint);
|
|
99
|
+
const url = `${endpoint}/oauth/token`;
|
|
100
|
+
const fetchImpl = defaultFetch(opts.fetchImpl);
|
|
101
|
+
const clientId = opts.clientId?.trim() || BRIGHTSY_OAUTH_CLIENT_ID;
|
|
102
|
+
let res;
|
|
103
|
+
try {
|
|
104
|
+
res = await fetchImpl(url, {
|
|
105
|
+
method: "POST",
|
|
106
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
107
|
+
body: new URLSearchParams({
|
|
108
|
+
grant_type: "refresh_token",
|
|
109
|
+
refresh_token: opts.refreshToken,
|
|
110
|
+
client_id: clientId
|
|
111
|
+
})
|
|
112
|
+
});
|
|
113
|
+
} catch (err) {
|
|
114
|
+
throw new Error(formatFetchError(err, url));
|
|
115
|
+
}
|
|
116
|
+
if (!res.ok) return null;
|
|
117
|
+
const data = await res.json();
|
|
118
|
+
if (!data.access_token) return null;
|
|
119
|
+
return {
|
|
120
|
+
access_token: data.access_token,
|
|
121
|
+
refresh_token: data.refresh_token,
|
|
122
|
+
expires_at: data.expires_in ? Date.now() + data.expires_in * 1e3 : void 0
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
var refreshInFlight = null;
|
|
126
|
+
async function ensureBrightsyLocalConfigFreshOnce(opts) {
|
|
127
|
+
let cfg;
|
|
128
|
+
try {
|
|
129
|
+
cfg = loadBrightsyConfig();
|
|
130
|
+
} catch {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
const refreshToken = cfg.refresh_token;
|
|
134
|
+
if (!refreshToken || !brightsyAccessTokenNeedsRefresh(cfg.expires_at)) {
|
|
135
|
+
return cfg;
|
|
136
|
+
}
|
|
137
|
+
const endpoint = normalizeBrightsyEndpoint(cfg.endpoint);
|
|
138
|
+
const clientId = await ensureBrightsyOAuthClientId({
|
|
139
|
+
endpoint,
|
|
140
|
+
clientId: cfg.oauth_client_id,
|
|
141
|
+
fetchImpl: opts?.fetchImpl
|
|
142
|
+
});
|
|
143
|
+
if (clientId !== cfg.oauth_client_id) {
|
|
144
|
+
cfg = { ...cfg, oauth_client_id: clientId };
|
|
145
|
+
saveBrightsyConfig(cfg);
|
|
146
|
+
}
|
|
147
|
+
const grant = await refreshBrightsyAccessToken({
|
|
148
|
+
endpoint,
|
|
149
|
+
refreshToken,
|
|
150
|
+
clientId,
|
|
151
|
+
fetchImpl: opts?.fetchImpl
|
|
152
|
+
});
|
|
153
|
+
if (!grant) return cfg;
|
|
154
|
+
const next = { ...cfg, ...applyGrant(cfg, grant), oauth_client_id: clientId };
|
|
155
|
+
saveBrightsyConfig(next);
|
|
156
|
+
return next;
|
|
157
|
+
}
|
|
158
|
+
async function ensureBrightsyLocalConfigFresh(opts) {
|
|
159
|
+
if (refreshInFlight) return refreshInFlight;
|
|
160
|
+
refreshInFlight = ensureBrightsyLocalConfigFreshOnce(opts).finally(() => {
|
|
161
|
+
refreshInFlight = null;
|
|
162
|
+
});
|
|
163
|
+
return refreshInFlight;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export {
|
|
167
|
+
brightsyConfigPath,
|
|
168
|
+
loadBrightsyConfig,
|
|
169
|
+
saveBrightsyConfig,
|
|
170
|
+
BRIGHTSY_OAUTH_CLIENT_ID,
|
|
171
|
+
BRIGHTSY_OAUTH_REDIRECT,
|
|
172
|
+
normalizeBrightsyEndpoint,
|
|
173
|
+
brightsyAccessTokenNeedsRefresh,
|
|
174
|
+
registerBrightsyOAuthClient,
|
|
175
|
+
ensureBrightsyOAuthClientId,
|
|
176
|
+
refreshBrightsyAccessToken,
|
|
177
|
+
ensureBrightsyLocalConfigFresh
|
|
178
|
+
};
|