@sideboard-ai/core 0.1.202 → 0.1.203
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/{abletime-KGPD3UUJ.js → abletime-JFGZBDHH.js} +4 -2
- package/dist/{abletime-345SE3B4.js → abletime-SISJEPJ3.js} +4 -2
- package/dist/abletime-oauth-7TD5Z2QV.js +265 -0
- package/dist/abletime-oauth-OSQYFB3E.js +40 -0
- package/dist/abletime-rest-QPJTDLKX.js +25 -0
- package/dist/abletime-rest-ZJGWYALM.js +24 -0
- package/dist/{agents-OXX3ONVY.js → agents-3755WYQM.js} +4 -4
- package/dist/{agents-573ZDIX6.js → agents-ZMWL43SP.js} +4 -4
- package/dist/{app-settings-V6IHESEQ.js → app-settings-LGHMNHSP.js} +1 -1
- package/dist/{app-settings-AXCURTZJ.js → app-settings-YEPF46IJ.js} +1 -1
- package/dist/{chunk-7NKY2OX7.js → chunk-2DVCCLII.js} +78 -62
- package/dist/chunk-3PYH2RLA.js +388 -0
- package/dist/{chunk-A2TAKYSG.js → chunk-5IXJTUST.js} +4 -4
- package/dist/chunk-6VEIKW5C.js +390 -0
- package/dist/chunk-BFEBFGCS.js +250 -0
- package/dist/{chunk-STPZQR5B.js → chunk-CMGRV36E.js} +1 -1
- package/dist/{chunk-PGQGP4FD.js → chunk-FZ6IBUKY.js} +36 -218
- package/dist/{chunk-7AHAABKN.js → chunk-FZYYEMET.js} +36 -228
- package/dist/{chunk-54RSAMBH.js → chunk-LCU6GBZP.js} +2 -2
- package/dist/chunk-MHPINQKY.js +260 -0
- package/dist/chunk-PJHFNFXA.js +243 -0
- package/dist/{chunk-CXFW5LIH.js → chunk-QFBBUFD4.js} +38 -3
- package/dist/{chunk-RPX6VDW7.js → chunk-QSEF2YVQ.js} +1 -1
- package/dist/{chunk-LJHC62LZ.js → chunk-TNDUTGDV.js} +2 -2
- package/dist/{chunk-27GGXX27.js → chunk-TZNJKZ2A.js} +82 -66
- package/dist/{chunk-NIVF5NDN.js → chunk-X2TFUFIS.js} +4 -4
- package/dist/{chunk-ISV5IFH3.js → chunk-X743YN5Y.js} +38 -3
- package/dist/{coordinator-prompt-TY5FHAXC.js → coordinator-prompt-2B2JA2IE.js} +3 -3
- package/dist/{coordinator-prompt-BH4EHFOO.js → coordinator-prompt-U4PEZDAW.js} +3 -3
- package/dist/{global-workspace-NLLAPQJI.js → global-workspace-JPJ3FKKY.js} +3 -3
- package/dist/{global-workspace-5AZEEQRU.js → global-workspace-PU7ZS3LS.js} +3 -3
- package/dist/index.cjs +912 -142
- package/dist/index.d.cts +61 -9
- package/dist/index.d.ts +61 -9
- package/dist/index.js +63 -18
- package/dist/mcp/run-stdio.cjs +843 -114
- package/dist/mcp/run-stdio.js +22 -20
- package/dist/{orchestrator-JNSQDRZI.js → orchestrator-3A6KUTNI.js} +5 -5
- package/dist/{orchestrator-NCYKEI3E.js → orchestrator-Q33M24ZJ.js} +6 -6
- package/dist/{workspaces-RP76JH3J.js → workspaces-IMG7ZRUQ.js} +3 -3
- package/dist/{workspaces-PEWYWVHZ.js → workspaces-U6P6DB44.js} +3 -3
- package/dist/{worktree-ZQEUKSNE.js → worktree-FL3UIUKQ.js} +2 -2
- package/dist/{worktree-72TZWUSH.js → worktree-JSCDJPBU.js} +2 -2
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -929,6 +929,9 @@ function normalizeVault(raw) {
|
|
|
929
929
|
if (typeof raw.abletimeAccessToken === "string" && raw.abletimeAccessToken.trim()) {
|
|
930
930
|
out.abletimeAccessToken = raw.abletimeAccessToken.trim();
|
|
931
931
|
}
|
|
932
|
+
if (typeof raw.abletimeRefreshToken === "string" && raw.abletimeRefreshToken.trim()) {
|
|
933
|
+
out.abletimeRefreshToken = raw.abletimeRefreshToken.trim();
|
|
934
|
+
}
|
|
932
935
|
if (typeof raw.vercelToken === "string" && raw.vercelToken.trim()) {
|
|
933
936
|
out.vercelToken = raw.vercelToken.trim();
|
|
934
937
|
}
|
|
@@ -1134,6 +1137,7 @@ function emptyPublicIntegrations() {
|
|
|
1134
1137
|
hasSlackAppToken: false,
|
|
1135
1138
|
hasGithubPat: false,
|
|
1136
1139
|
hasAbleTimeToken: false,
|
|
1140
|
+
hasAbleTimeOAuth: false,
|
|
1137
1141
|
hasVercelToken: false,
|
|
1138
1142
|
hasSupabaseToken: false,
|
|
1139
1143
|
hasPosthogToken: false,
|
|
@@ -1158,6 +1162,7 @@ function toPublicAppSettings(settings) {
|
|
|
1158
1162
|
const slackAppToken = integrations.slackAppToken;
|
|
1159
1163
|
const githubPat = integrations.githubPat;
|
|
1160
1164
|
const abletimeAccessToken = integrations.abletimeAccessToken;
|
|
1165
|
+
const abletimeRefreshToken = integrations.abletimeRefreshToken;
|
|
1161
1166
|
const vercelToken = integrations.vercelToken;
|
|
1162
1167
|
const supabaseAccessToken = integrations.supabaseAccessToken;
|
|
1163
1168
|
const posthogPersonalApiKey = integrations.posthogPersonalApiKey;
|
|
@@ -1170,6 +1175,7 @@ function toPublicAppSettings(settings) {
|
|
|
1170
1175
|
delete integrations.slackAppToken;
|
|
1171
1176
|
delete integrations.githubPat;
|
|
1172
1177
|
delete integrations.abletimeAccessToken;
|
|
1178
|
+
delete integrations.abletimeRefreshToken;
|
|
1173
1179
|
delete integrations.vercelToken;
|
|
1174
1180
|
delete integrations.supabaseAccessToken;
|
|
1175
1181
|
delete integrations.posthogPersonalApiKey;
|
|
@@ -1184,7 +1190,8 @@ function toPublicAppSettings(settings) {
|
|
|
1184
1190
|
hasSlackClientSecret: Boolean(slackClientSecret?.trim()),
|
|
1185
1191
|
hasSlackAppToken: Boolean(slackAppToken?.trim()),
|
|
1186
1192
|
hasGithubPat: Boolean(githubPat?.trim()),
|
|
1187
|
-
hasAbleTimeToken: Boolean(abletimeAccessToken?.trim()),
|
|
1193
|
+
hasAbleTimeToken: Boolean(abletimeAccessToken?.trim() || abletimeRefreshToken?.trim()),
|
|
1194
|
+
hasAbleTimeOAuth: Boolean(abletimeRefreshToken?.trim()),
|
|
1188
1195
|
hasVercelToken: Boolean(vercelToken?.trim()),
|
|
1189
1196
|
hasSupabaseToken: Boolean(supabaseAccessToken?.trim()),
|
|
1190
1197
|
hasPosthogToken: Boolean(posthogPersonalApiKey?.trim()),
|
|
@@ -1322,6 +1329,16 @@ function normalizeIntegrations(raw) {
|
|
|
1322
1329
|
const token = source.abletimeAccessToken.trim();
|
|
1323
1330
|
if (token) out.abletimeAccessToken = token;
|
|
1324
1331
|
}
|
|
1332
|
+
if (typeof source.abletimeRefreshToken === "string") {
|
|
1333
|
+
const token = source.abletimeRefreshToken.trim();
|
|
1334
|
+
if (token) out.abletimeRefreshToken = token;
|
|
1335
|
+
}
|
|
1336
|
+
if (typeof source.abletimeTokenExpiresAt === "number" && Number.isFinite(source.abletimeTokenExpiresAt)) {
|
|
1337
|
+
out.abletimeTokenExpiresAt = source.abletimeTokenExpiresAt;
|
|
1338
|
+
} else if (typeof source.abletimeTokenExpiresAt === "string") {
|
|
1339
|
+
const n = Number(source.abletimeTokenExpiresAt);
|
|
1340
|
+
if (Number.isFinite(n)) out.abletimeTokenExpiresAt = n;
|
|
1341
|
+
}
|
|
1325
1342
|
if (typeof source.abletimeHost === "string") {
|
|
1326
1343
|
const host = source.abletimeHost.trim();
|
|
1327
1344
|
if (host) out.abletimeHost = host;
|
|
@@ -1599,7 +1616,7 @@ function readSettingsFile() {
|
|
|
1599
1616
|
}
|
|
1600
1617
|
function diskHoldsSecrets(disk) {
|
|
1601
1618
|
return Boolean(
|
|
1602
|
-
disk.integrations.linearApiKey || disk.integrations.linearAccessToken || disk.integrations.linearRefreshToken || disk.integrations.linearClientSecret || disk.integrations.slackClientSecret || disk.integrations.slackAppToken || disk.integrations.githubPat || disk.integrations.abletimeAccessToken || disk.integrations.vercelToken || disk.integrations.supabaseAccessToken || disk.integrations.posthogPersonalApiKey || disk.integrations.sentryAuthToken || Object.keys(disk.environment).length > 0
|
|
1619
|
+
disk.integrations.linearApiKey || disk.integrations.linearAccessToken || disk.integrations.linearRefreshToken || disk.integrations.linearClientSecret || disk.integrations.slackClientSecret || disk.integrations.slackAppToken || disk.integrations.githubPat || disk.integrations.abletimeAccessToken || disk.integrations.abletimeRefreshToken || disk.integrations.vercelToken || disk.integrations.supabaseAccessToken || disk.integrations.posthogPersonalApiKey || disk.integrations.sentryAuthToken || Object.keys(disk.environment).length > 0
|
|
1603
1620
|
);
|
|
1604
1621
|
}
|
|
1605
1622
|
function mergeVault(disk) {
|
|
@@ -1630,6 +1647,9 @@ function mergeVault(disk) {
|
|
|
1630
1647
|
if (vault.abletimeAccessToken && !integrations.abletimeAccessToken) {
|
|
1631
1648
|
integrations.abletimeAccessToken = vault.abletimeAccessToken;
|
|
1632
1649
|
}
|
|
1650
|
+
if (vault.abletimeRefreshToken && !integrations.abletimeRefreshToken) {
|
|
1651
|
+
integrations.abletimeRefreshToken = vault.abletimeRefreshToken;
|
|
1652
|
+
}
|
|
1633
1653
|
if (vault.vercelToken && !integrations.vercelToken) {
|
|
1634
1654
|
integrations.vercelToken = vault.vercelToken;
|
|
1635
1655
|
}
|
|
@@ -1654,6 +1674,7 @@ function persistSplit(settings) {
|
|
|
1654
1674
|
const slackAppToken = integrations.slackAppToken;
|
|
1655
1675
|
const githubPat = integrations.githubPat;
|
|
1656
1676
|
const abletimeAccessToken = integrations.abletimeAccessToken;
|
|
1677
|
+
const abletimeRefreshToken = integrations.abletimeRefreshToken;
|
|
1657
1678
|
const vercelToken = integrations.vercelToken;
|
|
1658
1679
|
const supabaseAccessToken = integrations.supabaseAccessToken;
|
|
1659
1680
|
const posthogPersonalApiKey = integrations.posthogPersonalApiKey;
|
|
@@ -1666,6 +1687,7 @@ function persistSplit(settings) {
|
|
|
1666
1687
|
delete integrations.slackAppToken;
|
|
1667
1688
|
delete integrations.githubPat;
|
|
1668
1689
|
delete integrations.abletimeAccessToken;
|
|
1690
|
+
delete integrations.abletimeRefreshToken;
|
|
1669
1691
|
delete integrations.vercelToken;
|
|
1670
1692
|
delete integrations.supabaseAccessToken;
|
|
1671
1693
|
delete integrations.posthogPersonalApiKey;
|
|
@@ -1684,6 +1706,7 @@ function persistSplit(settings) {
|
|
|
1684
1706
|
slackAppToken,
|
|
1685
1707
|
githubPat,
|
|
1686
1708
|
abletimeAccessToken,
|
|
1709
|
+
abletimeRefreshToken,
|
|
1687
1710
|
vercelToken,
|
|
1688
1711
|
supabaseAccessToken,
|
|
1689
1712
|
posthogPersonalApiKey,
|
|
@@ -1870,6 +1893,8 @@ function updateIntegrationsSettings(patch) {
|
|
|
1870
1893
|
if ("abletimeAccessToken" in patch) {
|
|
1871
1894
|
if (patch.abletimeAccessToken == null || patch.abletimeAccessToken.trim() === "") {
|
|
1872
1895
|
delete integrations.abletimeAccessToken;
|
|
1896
|
+
delete integrations.abletimeRefreshToken;
|
|
1897
|
+
delete integrations.abletimeTokenExpiresAt;
|
|
1873
1898
|
delete integrations.abletimeViewerName;
|
|
1874
1899
|
} else {
|
|
1875
1900
|
integrations.abletimeAccessToken = patch.abletimeAccessToken.trim();
|
|
@@ -2220,7 +2245,9 @@ function disconnectLinearConnection() {
|
|
|
2220
2245
|
return saveAppSettings({ ...current, integrations });
|
|
2221
2246
|
}
|
|
2222
2247
|
function isAbleTimeConnected(settings = loadAppSettings()) {
|
|
2223
|
-
return Boolean(
|
|
2248
|
+
return Boolean(
|
|
2249
|
+
settings.integrations.abletimeAccessToken?.trim() || settings.integrations.abletimeRefreshToken?.trim()
|
|
2250
|
+
);
|
|
2224
2251
|
}
|
|
2225
2252
|
function getAbleTimeAccessToken(settings = loadAppSettings()) {
|
|
2226
2253
|
const token = settings.integrations.abletimeAccessToken?.trim();
|
|
@@ -2233,6 +2260,12 @@ function saveAbleTimeConnection(input) {
|
|
|
2233
2260
|
const current = loadAppSettings();
|
|
2234
2261
|
const integrations = { ...current.integrations };
|
|
2235
2262
|
integrations.abletimeAccessToken = input.accessToken.trim();
|
|
2263
|
+
if (input.refreshToken?.trim()) {
|
|
2264
|
+
integrations.abletimeRefreshToken = input.refreshToken.trim();
|
|
2265
|
+
}
|
|
2266
|
+
if (typeof input.expiresIn === "number" && Number.isFinite(input.expiresIn)) {
|
|
2267
|
+
integrations.abletimeTokenExpiresAt = Date.now() + Math.max(0, input.expiresIn) * 1e3;
|
|
2268
|
+
}
|
|
2236
2269
|
if (input.host?.trim()) {
|
|
2237
2270
|
integrations.abletimeHost = input.host.trim();
|
|
2238
2271
|
}
|
|
@@ -2245,6 +2278,8 @@ function disconnectAbleTimeConnection() {
|
|
|
2245
2278
|
const current = loadAppSettings();
|
|
2246
2279
|
const integrations = { ...current.integrations };
|
|
2247
2280
|
delete integrations.abletimeAccessToken;
|
|
2281
|
+
delete integrations.abletimeRefreshToken;
|
|
2282
|
+
delete integrations.abletimeTokenExpiresAt;
|
|
2248
2283
|
delete integrations.abletimeViewerName;
|
|
2249
2284
|
return saveAppSettings({ ...current, integrations });
|
|
2250
2285
|
}
|
|
@@ -17459,6 +17494,645 @@ var init_detect = __esm({
|
|
|
17459
17494
|
}
|
|
17460
17495
|
});
|
|
17461
17496
|
|
|
17497
|
+
// src/integrations/abletime-oauth.ts
|
|
17498
|
+
var abletime_oauth_exports = {};
|
|
17499
|
+
__export(abletime_oauth_exports, {
|
|
17500
|
+
ABLETIME_OAUTH_CANCELLED: () => ABLETIME_OAUTH_CANCELLED,
|
|
17501
|
+
ABLETIME_OAUTH_PORT: () => ABLETIME_OAUTH_PORT,
|
|
17502
|
+
ABLETIME_OAUTH_REDIRECT: () => ABLETIME_OAUTH_REDIRECT,
|
|
17503
|
+
ABLETIME_OAUTH_RESOURCE: () => ABLETIME_OAUTH_RESOURCE,
|
|
17504
|
+
AbleTimeOAuthCancelledError: () => AbleTimeOAuthCancelledError,
|
|
17505
|
+
BAKED_ABLETIME_OAUTH_CLIENT_ID: () => BAKED_ABLETIME_OAUTH_CLIENT_ID,
|
|
17506
|
+
ableTimeOAuthAuthorizeUrl: () => ableTimeOAuthAuthorizeUrl,
|
|
17507
|
+
ableTimeOAuthClientId: () => ableTimeOAuthClientId,
|
|
17508
|
+
ableTimeOAuthResource: () => ableTimeOAuthResource,
|
|
17509
|
+
createAbleTimePkce: () => createAbleTimePkce,
|
|
17510
|
+
ensureAbleTimeAccessToken: () => ensureAbleTimeAccessToken,
|
|
17511
|
+
hasBakedAbleTimeOAuth: () => hasBakedAbleTimeOAuth,
|
|
17512
|
+
isAbleTimeOAuthCancelled: () => isAbleTimeOAuthCancelled,
|
|
17513
|
+
startAbleTimeOAuth: () => startAbleTimeOAuth
|
|
17514
|
+
});
|
|
17515
|
+
function hasBakedAbleTimeOAuth() {
|
|
17516
|
+
return Boolean(BAKED_ABLETIME_OAUTH_CLIENT_ID.trim());
|
|
17517
|
+
}
|
|
17518
|
+
function ableTimeOAuthClientId() {
|
|
17519
|
+
return process.env.SIDEBOARD_ABLETIME_OAUTH_CLIENT_ID?.trim() || BAKED_ABLETIME_OAUTH_CLIENT_ID.trim();
|
|
17520
|
+
}
|
|
17521
|
+
function ableTimeOAuthResource(host) {
|
|
17522
|
+
return `${normalizeAbleTimeHost(host)}/api/public/v2/mcp`;
|
|
17523
|
+
}
|
|
17524
|
+
function createAbleTimePkce() {
|
|
17525
|
+
const verifier = (0, import_node_crypto10.randomBytes)(32).toString("base64url");
|
|
17526
|
+
const challenge = (0, import_node_crypto10.createHash)("sha256").update(verifier).digest("base64url");
|
|
17527
|
+
return { verifier, challenge };
|
|
17528
|
+
}
|
|
17529
|
+
function ableTimeOAuthAuthorizeUrl(clientId, state, codeChallenge, host) {
|
|
17530
|
+
const base = normalizeAbleTimeHost(host);
|
|
17531
|
+
const params = new URLSearchParams({
|
|
17532
|
+
response_type: "code",
|
|
17533
|
+
client_id: clientId,
|
|
17534
|
+
redirect_uri: ABLETIME_OAUTH_REDIRECT,
|
|
17535
|
+
code_challenge: codeChallenge,
|
|
17536
|
+
code_challenge_method: "S256",
|
|
17537
|
+
state,
|
|
17538
|
+
resource: ableTimeOAuthResource(host)
|
|
17539
|
+
});
|
|
17540
|
+
return `${base}/oauth/authorize?${params.toString()}`;
|
|
17541
|
+
}
|
|
17542
|
+
function htmlPage(title, body) {
|
|
17543
|
+
return `<!doctype html><html><head><meta charset="utf-8"><title>${title}</title>
|
|
17544
|
+
<style>body{font-family:ui-sans-serif,system-ui,sans-serif;padding:48px 24px;max-width:36rem;margin:0 auto;color:#1a1a1a}
|
|
17545
|
+
h1{font-size:1.25rem}p{line-height:1.5;color:#444}</style></head><body>${body}</body></html>`;
|
|
17546
|
+
}
|
|
17547
|
+
async function exchangeAbleTimeToken(body, host) {
|
|
17548
|
+
const url = `${normalizeAbleTimeHost(host)}/api/oauth/token`;
|
|
17549
|
+
const res = await httpFetch(url, {
|
|
17550
|
+
method: "POST",
|
|
17551
|
+
headers: {
|
|
17552
|
+
Accept: "application/json",
|
|
17553
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
17554
|
+
},
|
|
17555
|
+
body
|
|
17556
|
+
});
|
|
17557
|
+
const data = await res.json().catch(() => ({}));
|
|
17558
|
+
if (!res.ok || data.error || !data.access_token?.trim()) {
|
|
17559
|
+
const detail = data.error_description || data.error || `HTTP ${res.status}`;
|
|
17560
|
+
throw new Error(`AbleTime OAuth token exchange failed: ${detail}`);
|
|
17561
|
+
}
|
|
17562
|
+
return data;
|
|
17563
|
+
}
|
|
17564
|
+
function persistAbleTimeTokens(data, input) {
|
|
17565
|
+
return saveAbleTimeConnection({
|
|
17566
|
+
accessToken: data.access_token.trim(),
|
|
17567
|
+
refreshToken: data.refresh_token?.trim(),
|
|
17568
|
+
expiresIn: data.expires_in,
|
|
17569
|
+
host: input?.host,
|
|
17570
|
+
viewerName: input?.viewerName
|
|
17571
|
+
});
|
|
17572
|
+
}
|
|
17573
|
+
async function refreshAbleTimeAccessToken(refreshToken) {
|
|
17574
|
+
const host = getAbleTimeHost();
|
|
17575
|
+
const body = new URLSearchParams({
|
|
17576
|
+
grant_type: "refresh_token",
|
|
17577
|
+
refresh_token: refreshToken,
|
|
17578
|
+
client_id: ableTimeOAuthClientId(),
|
|
17579
|
+
resource: ableTimeOAuthResource(host)
|
|
17580
|
+
});
|
|
17581
|
+
try {
|
|
17582
|
+
const data = await exchangeAbleTimeToken(body, host);
|
|
17583
|
+
persistAbleTimeTokens(data, { host });
|
|
17584
|
+
return data.access_token.trim();
|
|
17585
|
+
} catch (err) {
|
|
17586
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
17587
|
+
if (/invalid_grant|invalid_token|unauthorized/i.test(message)) {
|
|
17588
|
+
disconnectAbleTimeConnection();
|
|
17589
|
+
throw new Error("AbleTime sign-in expired \u2014 connect again in Account settings");
|
|
17590
|
+
}
|
|
17591
|
+
throw err;
|
|
17592
|
+
}
|
|
17593
|
+
}
|
|
17594
|
+
async function ensureAbleTimeAccessToken() {
|
|
17595
|
+
const settings = loadAppSettings();
|
|
17596
|
+
const access = settings.integrations.abletimeAccessToken?.trim() || "";
|
|
17597
|
+
const refresh = settings.integrations.abletimeRefreshToken?.trim() || "";
|
|
17598
|
+
const expiresAt = settings.integrations.abletimeTokenExpiresAt ?? 0;
|
|
17599
|
+
if (access && (!refresh || Date.now() < expiresAt - REFRESH_SKEW_MS2)) {
|
|
17600
|
+
return access;
|
|
17601
|
+
}
|
|
17602
|
+
if (refresh) {
|
|
17603
|
+
if (!refreshInFlight) {
|
|
17604
|
+
refreshInFlight = refreshAbleTimeAccessToken(refresh).finally(() => {
|
|
17605
|
+
refreshInFlight = null;
|
|
17606
|
+
});
|
|
17607
|
+
}
|
|
17608
|
+
return refreshInFlight;
|
|
17609
|
+
}
|
|
17610
|
+
return access || null;
|
|
17611
|
+
}
|
|
17612
|
+
function isAbleTimeOAuthCancelled(err) {
|
|
17613
|
+
if (err instanceof AbleTimeOAuthCancelledError) return true;
|
|
17614
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
17615
|
+
return message.includes(ABLETIME_OAUTH_CANCELLED);
|
|
17616
|
+
}
|
|
17617
|
+
async function startAbleTimeOAuth(opts) {
|
|
17618
|
+
if (opts?.signal?.aborted) {
|
|
17619
|
+
throw new AbleTimeOAuthCancelledError();
|
|
17620
|
+
}
|
|
17621
|
+
const clientId = ableTimeOAuthClientId();
|
|
17622
|
+
if (!clientId) {
|
|
17623
|
+
throw new Error(
|
|
17624
|
+
`AbleTime browser sign-in needs a Client ID Metadata Document URL. Host ${BAKED_ABLETIME_OAUTH_CLIENT_ID} (see site/oauth/abletime-client.json), or set SIDEBOARD_ABLETIME_OAUTH_CLIENT_ID.`
|
|
17625
|
+
);
|
|
17626
|
+
}
|
|
17627
|
+
const host = opts?.host?.trim() || void 0;
|
|
17628
|
+
const state = (0, import_node_crypto10.randomBytes)(16).toString("hex");
|
|
17629
|
+
const pkce = createAbleTimePkce();
|
|
17630
|
+
const authorizeUrl = ableTimeOAuthAuthorizeUrl(clientId, state, pkce.challenge, host);
|
|
17631
|
+
const timeoutMs = opts?.timeoutMs ?? 5 * 6e4;
|
|
17632
|
+
const code = await new Promise((resolve, reject) => {
|
|
17633
|
+
let settled = false;
|
|
17634
|
+
const finish = (err, value) => {
|
|
17635
|
+
if (settled) return;
|
|
17636
|
+
settled = true;
|
|
17637
|
+
cleanup();
|
|
17638
|
+
if (err) reject(err);
|
|
17639
|
+
else resolve(value);
|
|
17640
|
+
};
|
|
17641
|
+
const server = (0, import_node_http.createServer)((req, res) => {
|
|
17642
|
+
try {
|
|
17643
|
+
const url = new URL(req.url || "/", `http://127.0.0.1:${ABLETIME_OAUTH_PORT}`);
|
|
17644
|
+
if (url.pathname !== "/callback") {
|
|
17645
|
+
res.writeHead(404);
|
|
17646
|
+
res.end("Not found");
|
|
17647
|
+
return;
|
|
17648
|
+
}
|
|
17649
|
+
const err = url.searchParams.get("error");
|
|
17650
|
+
const gotState = url.searchParams.get("state");
|
|
17651
|
+
const gotCode = url.searchParams.get("code");
|
|
17652
|
+
if (err) {
|
|
17653
|
+
res.writeHead(400, { "Content-Type": "text/html" });
|
|
17654
|
+
res.end(htmlPage("AbleTime", `<h1>Authorization cancelled</h1><p>${err}</p>`));
|
|
17655
|
+
finish(new AbleTimeOAuthCancelledError(`AbleTime OAuth: ${err}`));
|
|
17656
|
+
return;
|
|
17657
|
+
}
|
|
17658
|
+
if (gotState !== state || !gotCode) {
|
|
17659
|
+
res.writeHead(400, { "Content-Type": "text/html" });
|
|
17660
|
+
res.end(htmlPage("AbleTime", "<h1>Invalid callback</h1><p>State or code missing.</p>"));
|
|
17661
|
+
finish(new Error("AbleTime OAuth callback was invalid"));
|
|
17662
|
+
return;
|
|
17663
|
+
}
|
|
17664
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
17665
|
+
res.end(
|
|
17666
|
+
htmlPage(
|
|
17667
|
+
"AbleTime connected",
|
|
17668
|
+
"<h1>AbleTime connected</h1><p>You can close this tab and return to Sideboard.</p>"
|
|
17669
|
+
)
|
|
17670
|
+
);
|
|
17671
|
+
finish(void 0, gotCode);
|
|
17672
|
+
} catch (e) {
|
|
17673
|
+
finish(e instanceof Error ? e : new Error(String(e)));
|
|
17674
|
+
}
|
|
17675
|
+
});
|
|
17676
|
+
const timer = setTimeout(() => {
|
|
17677
|
+
finish(new Error("AbleTime sign-in timed out \u2014 try again"));
|
|
17678
|
+
}, timeoutMs);
|
|
17679
|
+
const onAbort = () => finish(new AbleTimeOAuthCancelledError());
|
|
17680
|
+
const cleanup = () => {
|
|
17681
|
+
clearTimeout(timer);
|
|
17682
|
+
opts?.signal?.removeEventListener("abort", onAbort);
|
|
17683
|
+
server.close();
|
|
17684
|
+
};
|
|
17685
|
+
opts?.signal?.addEventListener("abort", onAbort, { once: true });
|
|
17686
|
+
if (opts?.signal?.aborted) {
|
|
17687
|
+
onAbort();
|
|
17688
|
+
return;
|
|
17689
|
+
}
|
|
17690
|
+
server.on("error", (e) => {
|
|
17691
|
+
finish(
|
|
17692
|
+
e instanceof Error && e.code === "EADDRINUSE" ? new Error(
|
|
17693
|
+
`Port ${ABLETIME_OAUTH_PORT} is in use. Close whatever is bound there, or paste an AbleTime token instead.`
|
|
17694
|
+
) : e instanceof Error ? e : new Error(String(e))
|
|
17695
|
+
);
|
|
17696
|
+
});
|
|
17697
|
+
server.listen(ABLETIME_OAUTH_PORT, "127.0.0.1", () => {
|
|
17698
|
+
void Promise.resolve(opts?.openUrl?.(authorizeUrl)).catch((e) => {
|
|
17699
|
+
finish(e instanceof Error ? e : new Error(String(e)));
|
|
17700
|
+
});
|
|
17701
|
+
});
|
|
17702
|
+
});
|
|
17703
|
+
const body = new URLSearchParams({
|
|
17704
|
+
grant_type: "authorization_code",
|
|
17705
|
+
code,
|
|
17706
|
+
redirect_uri: ABLETIME_OAUTH_REDIRECT,
|
|
17707
|
+
client_id: clientId,
|
|
17708
|
+
code_verifier: pkce.verifier,
|
|
17709
|
+
resource: ableTimeOAuthResource(host)
|
|
17710
|
+
});
|
|
17711
|
+
const data = await exchangeAbleTimeToken(body, host);
|
|
17712
|
+
persistAbleTimeTokens(data, { host });
|
|
17713
|
+
const token = data.access_token.trim();
|
|
17714
|
+
try {
|
|
17715
|
+
const { getAbleTimeOrientation: getAbleTimeOrientation2 } = await Promise.resolve().then(() => (init_abletime(), abletime_exports));
|
|
17716
|
+
const orientation = await getAbleTimeOrientation2({ token, host });
|
|
17717
|
+
return saveAbleTimeConnection({
|
|
17718
|
+
accessToken: token,
|
|
17719
|
+
refreshToken: data.refresh_token?.trim(),
|
|
17720
|
+
expiresIn: data.expires_in,
|
|
17721
|
+
host,
|
|
17722
|
+
viewerName: orientation.viewer.name
|
|
17723
|
+
});
|
|
17724
|
+
} catch {
|
|
17725
|
+
return loadAppSettings();
|
|
17726
|
+
}
|
|
17727
|
+
}
|
|
17728
|
+
var import_node_crypto10, import_node_http, ABLETIME_OAUTH_PORT, ABLETIME_OAUTH_REDIRECT, BAKED_ABLETIME_OAUTH_CLIENT_ID, ABLETIME_OAUTH_RESOURCE, REFRESH_SKEW_MS2, refreshInFlight, ABLETIME_OAUTH_CANCELLED, AbleTimeOAuthCancelledError;
|
|
17729
|
+
var init_abletime_oauth = __esm({
|
|
17730
|
+
"src/integrations/abletime-oauth.ts"() {
|
|
17731
|
+
"use strict";
|
|
17732
|
+
import_node_crypto10 = require("crypto");
|
|
17733
|
+
import_node_http = require("http");
|
|
17734
|
+
init_fetch();
|
|
17735
|
+
init_app_settings();
|
|
17736
|
+
init_abletime_mcp();
|
|
17737
|
+
ABLETIME_OAUTH_PORT = 19849;
|
|
17738
|
+
ABLETIME_OAUTH_REDIRECT = `http://127.0.0.1:${ABLETIME_OAUTH_PORT}/callback`;
|
|
17739
|
+
BAKED_ABLETIME_OAUTH_CLIENT_ID = "https://www.sideboard.cloud/oauth/abletime-client.json";
|
|
17740
|
+
ABLETIME_OAUTH_RESOURCE = `${DEFAULT_ABLETIME_HOST}/api/public/v2/mcp`;
|
|
17741
|
+
REFRESH_SKEW_MS2 = 5 * 6e4;
|
|
17742
|
+
refreshInFlight = null;
|
|
17743
|
+
ABLETIME_OAUTH_CANCELLED = "AbleTime sign-in cancelled";
|
|
17744
|
+
AbleTimeOAuthCancelledError = class extends Error {
|
|
17745
|
+
constructor(message = ABLETIME_OAUTH_CANCELLED) {
|
|
17746
|
+
super(message);
|
|
17747
|
+
this.name = "AbleTimeOAuthCancelledError";
|
|
17748
|
+
}
|
|
17749
|
+
};
|
|
17750
|
+
}
|
|
17751
|
+
});
|
|
17752
|
+
|
|
17753
|
+
// src/integrations/abletime-rest.ts
|
|
17754
|
+
var abletime_rest_exports = {};
|
|
17755
|
+
__export(abletime_rest_exports, {
|
|
17756
|
+
ABLETIME_REST_PREFIX: () => ABLETIME_REST_PREFIX,
|
|
17757
|
+
ableTimeDocFromText: () => ableTimeDocFromText,
|
|
17758
|
+
abletimeRestUrl: () => abletimeRestUrl,
|
|
17759
|
+
callAbleTimeRestTool: () => callAbleTimeRestTool,
|
|
17760
|
+
looksLikeAbleTimeUlid: () => looksLikeAbleTimeUlid,
|
|
17761
|
+
textFromAbleTimeDoc: () => textFromAbleTimeDoc
|
|
17762
|
+
});
|
|
17763
|
+
function asRecord5(value) {
|
|
17764
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
17765
|
+
}
|
|
17766
|
+
function asString(value) {
|
|
17767
|
+
return typeof value === "string" ? value.trim() : "";
|
|
17768
|
+
}
|
|
17769
|
+
function asList(value) {
|
|
17770
|
+
if (Array.isArray(value)) return value;
|
|
17771
|
+
const record = asRecord5(value);
|
|
17772
|
+
if (!record) return [];
|
|
17773
|
+
for (const key of ["data", "tasks", "projects", "users", "items", "results"]) {
|
|
17774
|
+
if (Array.isArray(record[key])) return record[key];
|
|
17775
|
+
}
|
|
17776
|
+
return [];
|
|
17777
|
+
}
|
|
17778
|
+
function ableTimeDocFromText(text7) {
|
|
17779
|
+
const trimmed = text7.trim();
|
|
17780
|
+
if (!trimmed) return { type: "doc", content: [] };
|
|
17781
|
+
return {
|
|
17782
|
+
type: "doc",
|
|
17783
|
+
content: [{ type: "paragraph", content: [{ type: "text", text: trimmed }] }]
|
|
17784
|
+
};
|
|
17785
|
+
}
|
|
17786
|
+
function textFromAbleTimeDoc(value) {
|
|
17787
|
+
if (typeof value === "string") return value.trim();
|
|
17788
|
+
const record = asRecord5(value);
|
|
17789
|
+
if (!record) return "";
|
|
17790
|
+
if (typeof record.text === "string" && record.text.trim()) return record.text.trim();
|
|
17791
|
+
const chunks = [];
|
|
17792
|
+
const walk = (node) => {
|
|
17793
|
+
const rec = asRecord5(node);
|
|
17794
|
+
if (!rec) return;
|
|
17795
|
+
if (typeof rec.text === "string" && rec.text) chunks.push(rec.text);
|
|
17796
|
+
if (Array.isArray(rec.content)) rec.content.forEach(walk);
|
|
17797
|
+
};
|
|
17798
|
+
walk(record);
|
|
17799
|
+
return chunks.join("").trim();
|
|
17800
|
+
}
|
|
17801
|
+
function abletimeRestUrl(path2, host, query) {
|
|
17802
|
+
const base = `${normalizeAbleTimeHost(host)}${ABLETIME_REST_PREFIX}`;
|
|
17803
|
+
const suffix = path2.startsWith("/") ? path2 : `/${path2}`;
|
|
17804
|
+
const url = new URL(`${base}${suffix}`);
|
|
17805
|
+
for (const [key, value] of Object.entries(query ?? {})) {
|
|
17806
|
+
if (value) url.searchParams.set(key, value);
|
|
17807
|
+
}
|
|
17808
|
+
return url.href;
|
|
17809
|
+
}
|
|
17810
|
+
function looksLikeAbleTimeUlid(id) {
|
|
17811
|
+
return ULID_RE.test(id.trim());
|
|
17812
|
+
}
|
|
17813
|
+
async function abletimeRestRequest(path2, opts = {}) {
|
|
17814
|
+
const raw = opts.token ?? getAbleTimeAccessToken();
|
|
17815
|
+
const token = normalizeAbleTimeCredential(raw ?? "");
|
|
17816
|
+
if (!token) {
|
|
17817
|
+
throw new Error("AbleTime is not connected \u2014 paste an API key or personal access token in Account settings");
|
|
17818
|
+
}
|
|
17819
|
+
const host = opts.host ?? (opts.token ? DEFAULT_ABLETIME_HOST : getAbleTimeHost());
|
|
17820
|
+
const url = abletimeRestUrl(path2, host, opts.query);
|
|
17821
|
+
const method = (opts.method ?? "GET").toUpperCase();
|
|
17822
|
+
const res = await httpFetch(url, {
|
|
17823
|
+
method,
|
|
17824
|
+
headers: {
|
|
17825
|
+
Accept: "application/json",
|
|
17826
|
+
Authorization: `Bearer ${token}`,
|
|
17827
|
+
...opts.body !== void 0 ? { "Content-Type": "application/json" } : {}
|
|
17828
|
+
},
|
|
17829
|
+
...opts.body !== void 0 ? { body: JSON.stringify(opts.body) } : {}
|
|
17830
|
+
});
|
|
17831
|
+
const text7 = await res.text().catch(() => "");
|
|
17832
|
+
if (!res.ok) {
|
|
17833
|
+
throw new Error(
|
|
17834
|
+
rewriteAbleTimeError(
|
|
17835
|
+
`AbleTime API error ${res.status}${text7 ? `: ${text7.slice(0, 240)}` : ""}`
|
|
17836
|
+
)
|
|
17837
|
+
);
|
|
17838
|
+
}
|
|
17839
|
+
if (!text7.trim()) return null;
|
|
17840
|
+
try {
|
|
17841
|
+
return JSON.parse(text7);
|
|
17842
|
+
} catch {
|
|
17843
|
+
throw new Error(`AbleTime API returned a non-JSON response: ${text7.slice(0, 200)}`);
|
|
17844
|
+
}
|
|
17845
|
+
}
|
|
17846
|
+
async function restPages(path2, opts, maxPages = 20) {
|
|
17847
|
+
const items = [];
|
|
17848
|
+
let cursor;
|
|
17849
|
+
for (let i = 0; i < maxPages; i += 1) {
|
|
17850
|
+
const raw = await abletimeRestRequest(path2, {
|
|
17851
|
+
...opts,
|
|
17852
|
+
query: { ...opts.query, ...cursor ? { cursor } : {} }
|
|
17853
|
+
});
|
|
17854
|
+
items.push(...asList(raw));
|
|
17855
|
+
const page = asRecord5(asRecord5(raw)?.page);
|
|
17856
|
+
const next = page ? asString(page.nextCursor) : "";
|
|
17857
|
+
if (!next) break;
|
|
17858
|
+
cursor = next;
|
|
17859
|
+
}
|
|
17860
|
+
return items;
|
|
17861
|
+
}
|
|
17862
|
+
function userDisplayName(raw) {
|
|
17863
|
+
const rec = asRecord5(raw);
|
|
17864
|
+
if (!rec) return null;
|
|
17865
|
+
const id = asString(rec.userId) || asString(rec.id);
|
|
17866
|
+
const name = [asString(rec.firstName), asString(rec.lastName)].filter(Boolean).join(" ") || asString(rec.username) || asString(rec.email) || asString(rec.name);
|
|
17867
|
+
if (!name) return null;
|
|
17868
|
+
return { id: id || void 0, name };
|
|
17869
|
+
}
|
|
17870
|
+
function restTaskToRaw(raw, users) {
|
|
17871
|
+
const rec = asRecord5(raw);
|
|
17872
|
+
if (!rec) return null;
|
|
17873
|
+
const id = asString(rec.timeflowTaskId) || asString(rec.taskId) || asString(rec.id);
|
|
17874
|
+
const identifier = asString(rec.taskRef) || asString(rec.reference) || id;
|
|
17875
|
+
const title = asString(rec.title);
|
|
17876
|
+
if (!id && !identifier && !title) return null;
|
|
17877
|
+
const assigned = asString(rec.assignedUserId);
|
|
17878
|
+
const assignee = assigned ? users.get(assigned) : void 0;
|
|
17879
|
+
const comments = asList(rec.comments).map((item) => {
|
|
17880
|
+
const comment = asRecord5(item) ?? {};
|
|
17881
|
+
const user = userDisplayName(comment);
|
|
17882
|
+
return {
|
|
17883
|
+
id: asString(comment.commentId) || asString(comment.id),
|
|
17884
|
+
body: textFromAbleTimeDoc(comment.content) || asString(comment.contentSanitized) || asString(comment.body),
|
|
17885
|
+
createdAt: asString(comment.createdAt) || asString(comment.lastUpdate),
|
|
17886
|
+
user: user?.name || asString(comment.username)
|
|
17887
|
+
};
|
|
17888
|
+
});
|
|
17889
|
+
return {
|
|
17890
|
+
id,
|
|
17891
|
+
taskId: id,
|
|
17892
|
+
timeflowTaskId: id,
|
|
17893
|
+
reference: identifier,
|
|
17894
|
+
taskRef: identifier,
|
|
17895
|
+
title: title || identifier,
|
|
17896
|
+
state: asString(rec.taskState) || asString(rec.state),
|
|
17897
|
+
description: textFromAbleTimeDoc(rec.description) || asString(rec.descriptionSanitized),
|
|
17898
|
+
projectId: asString(rec.projectId),
|
|
17899
|
+
categoryId: asString(rec.projectCategoryId) || asString(rec.categoryId),
|
|
17900
|
+
tags: rec.tags,
|
|
17901
|
+
comments,
|
|
17902
|
+
createdAt: asString(rec.dateCreated) || asString(rec.createdAt),
|
|
17903
|
+
updatedAt: asString(rec.lastUpdate) || asString(rec.updatedAt),
|
|
17904
|
+
...assignee ? { assignee } : {}
|
|
17905
|
+
};
|
|
17906
|
+
}
|
|
17907
|
+
function restProjectToRaw(raw) {
|
|
17908
|
+
const rec = asRecord5(raw);
|
|
17909
|
+
if (!rec) return null;
|
|
17910
|
+
const id = asString(rec.projectId) || asString(rec.id);
|
|
17911
|
+
const name = asString(rec.projectName) || asString(rec.name);
|
|
17912
|
+
if (!id && !name) return null;
|
|
17913
|
+
const categories = asList(rec.categories).map((item) => {
|
|
17914
|
+
const cat = asRecord5(item) ?? {};
|
|
17915
|
+
return {
|
|
17916
|
+
id: asString(cat.projectCategoryId) || asString(cat.categoryId) || asString(cat.id),
|
|
17917
|
+
name: asString(cat.categoryName) || asString(cat.name)
|
|
17918
|
+
};
|
|
17919
|
+
});
|
|
17920
|
+
return { id, name, projectId: id, projectName: name, categories };
|
|
17921
|
+
}
|
|
17922
|
+
async function listUsers(opts) {
|
|
17923
|
+
const users = /* @__PURE__ */ new Map();
|
|
17924
|
+
for (const item of await restPages("/users", opts).catch(() => [])) {
|
|
17925
|
+
const user = userDisplayName(item);
|
|
17926
|
+
const rec = asRecord5(item);
|
|
17927
|
+
const id = user?.id || (rec ? asString(rec.userId) : "");
|
|
17928
|
+
if (user && id) users.set(id, user);
|
|
17929
|
+
}
|
|
17930
|
+
return users;
|
|
17931
|
+
}
|
|
17932
|
+
async function listProjectsRaw(opts) {
|
|
17933
|
+
const items = await restPages("/projects", { ...opts, query: { expand: "categories" } });
|
|
17934
|
+
return items.map(restProjectToRaw).filter((item) => Boolean(item));
|
|
17935
|
+
}
|
|
17936
|
+
async function listTasksRaw(opts) {
|
|
17937
|
+
const [users, items] = await Promise.all([
|
|
17938
|
+
listUsers(opts),
|
|
17939
|
+
restPages("/tasks", {
|
|
17940
|
+
...opts,
|
|
17941
|
+
query: {
|
|
17942
|
+
...opts.projectId ? { projectId: opts.projectId } : {}
|
|
17943
|
+
}
|
|
17944
|
+
})
|
|
17945
|
+
]);
|
|
17946
|
+
let tasks = items.map((item) => restTaskToRaw(item, users)).filter((item) => Boolean(item));
|
|
17947
|
+
const needle = opts.query?.trim().toLowerCase();
|
|
17948
|
+
if (needle) {
|
|
17949
|
+
tasks = tasks.filter((task) => {
|
|
17950
|
+
const hay = [task.title, task.reference, task.taskRef, task.id].map((value) => String(value ?? "").toLowerCase()).join(" ");
|
|
17951
|
+
const tags = Array.isArray(task.tags) ? task.tags.join(" ").toLowerCase() : "";
|
|
17952
|
+
return hay.includes(needle) || tags.includes(needle);
|
|
17953
|
+
});
|
|
17954
|
+
}
|
|
17955
|
+
return tasks;
|
|
17956
|
+
}
|
|
17957
|
+
async function resolveTaskId(id, opts) {
|
|
17958
|
+
const wanted = id.trim();
|
|
17959
|
+
if (!wanted) throw new Error("AbleTime task id is required");
|
|
17960
|
+
if (looksLikeAbleTimeUlid(wanted)) return wanted;
|
|
17961
|
+
const tasks = await listTasksRaw(opts);
|
|
17962
|
+
const match = tasks.find((task) => {
|
|
17963
|
+
const ref = String(task.reference ?? task.taskRef ?? "").toLowerCase();
|
|
17964
|
+
const taskId = String(task.id ?? "").toLowerCase();
|
|
17965
|
+
return ref === wanted.toLowerCase() || taskId === wanted.toLowerCase();
|
|
17966
|
+
});
|
|
17967
|
+
if (!match?.id) throw new Error(`AbleTime task not found: ${id}`);
|
|
17968
|
+
return String(match.id);
|
|
17969
|
+
}
|
|
17970
|
+
async function getTaskRaw(id, opts) {
|
|
17971
|
+
const taskId = await resolveTaskId(id, opts);
|
|
17972
|
+
const [users, raw] = await Promise.all([
|
|
17973
|
+
listUsers(opts),
|
|
17974
|
+
abletimeRestRequest(`/tasks/${encodeURIComponent(taskId)}`, {
|
|
17975
|
+
...opts,
|
|
17976
|
+
query: { expand: "comments" }
|
|
17977
|
+
})
|
|
17978
|
+
]);
|
|
17979
|
+
const task = restTaskToRaw(raw, users);
|
|
17980
|
+
if (!task) throw new Error(`AbleTime task not found: ${id}`);
|
|
17981
|
+
return task;
|
|
17982
|
+
}
|
|
17983
|
+
function firstStringArg(args, keys) {
|
|
17984
|
+
for (const key of keys) {
|
|
17985
|
+
const value = asString(args[key]);
|
|
17986
|
+
if (value) return value;
|
|
17987
|
+
}
|
|
17988
|
+
return "";
|
|
17989
|
+
}
|
|
17990
|
+
async function callAbleTimeRestTool(name, args = {}, opts) {
|
|
17991
|
+
const token = normalizeAbleTimeCredential(opts?.token ?? getAbleTimeAccessToken() ?? "");
|
|
17992
|
+
if (ableTimeCredentialKind(token) !== "org") {
|
|
17993
|
+
throw new Error("AbleTime REST transport is for organization API keys (atk_\u2026)");
|
|
17994
|
+
}
|
|
17995
|
+
const requestOpts = { ...opts, token };
|
|
17996
|
+
switch (name) {
|
|
17997
|
+
case "orientation": {
|
|
17998
|
+
const [users, projects, tasks] = await Promise.all([
|
|
17999
|
+
listUsers(requestOpts),
|
|
18000
|
+
listProjectsRaw(requestOpts),
|
|
18001
|
+
listTasksRaw(requestOpts)
|
|
18002
|
+
]);
|
|
18003
|
+
const viewer = [...users.values()].find((user) => user.name) ?? [...users.values()][0] ?? { name: "AbleTime" };
|
|
18004
|
+
return { viewer, projects, tasks };
|
|
18005
|
+
}
|
|
18006
|
+
case "list_projects":
|
|
18007
|
+
return { data: await listProjectsRaw(requestOpts) };
|
|
18008
|
+
case "list_users":
|
|
18009
|
+
return { data: [...(await listUsers(requestOpts)).values()] };
|
|
18010
|
+
case "list_tasks":
|
|
18011
|
+
return {
|
|
18012
|
+
data: await listTasksRaw({
|
|
18013
|
+
...requestOpts,
|
|
18014
|
+
projectId: firstStringArg(args, ["project_id", "projectId", "project"])
|
|
18015
|
+
})
|
|
18016
|
+
};
|
|
18017
|
+
case "search_tasks":
|
|
18018
|
+
return {
|
|
18019
|
+
data: await listTasksRaw({
|
|
18020
|
+
...requestOpts,
|
|
18021
|
+
query: firstStringArg(args, ["query", "q"])
|
|
18022
|
+
})
|
|
18023
|
+
};
|
|
18024
|
+
case "get_task":
|
|
18025
|
+
return await getTaskRaw(firstStringArg(args, ["id", "task_id", "task"]), requestOpts);
|
|
18026
|
+
case "create_task": {
|
|
18027
|
+
const title = firstStringArg(args, ["title"]);
|
|
18028
|
+
if (!title) throw new Error("AbleTime task title is required");
|
|
18029
|
+
const projectId = firstStringArg(args, ["project_id", "projectId", "project"]);
|
|
18030
|
+
if (!projectId) throw new Error("AbleTime project is required");
|
|
18031
|
+
const projects = await listProjectsRaw(requestOpts);
|
|
18032
|
+
const project = projects.find(
|
|
18033
|
+
(item) => String(item.id).toLowerCase() === projectId.toLowerCase() || String(item.name).toLowerCase() === projectId.toLowerCase()
|
|
18034
|
+
) ?? projects[0];
|
|
18035
|
+
if (!project) throw new Error("AbleTime has no projects to create a task in");
|
|
18036
|
+
const categories = asList(project.categories);
|
|
18037
|
+
const wantedCategory = firstStringArg(args, ["category_id", "categoryId", "category"]);
|
|
18038
|
+
const category = categories.map((item) => asRecord5(item)).find((item) => {
|
|
18039
|
+
if (!item || !wantedCategory) return false;
|
|
18040
|
+
return asString(item.id).toLowerCase() === wantedCategory.toLowerCase() || asString(item.name).toLowerCase() === wantedCategory.toLowerCase();
|
|
18041
|
+
}) ?? asRecord5(categories[0]);
|
|
18042
|
+
const created = await abletimeRestRequest("/tasks", {
|
|
18043
|
+
...requestOpts,
|
|
18044
|
+
method: "POST",
|
|
18045
|
+
body: {
|
|
18046
|
+
projectId: project.id,
|
|
18047
|
+
projectCategoryId: category ? asString(category.id) : void 0,
|
|
18048
|
+
title
|
|
18049
|
+
}
|
|
18050
|
+
});
|
|
18051
|
+
const state = firstStringArg(args, ["state"]);
|
|
18052
|
+
const createdId = asString(asRecord5(created)?.timeflowTaskId);
|
|
18053
|
+
const description = asString(args.description);
|
|
18054
|
+
if (createdId && description) {
|
|
18055
|
+
await abletimeRestRequest(`/tasks/${encodeURIComponent(createdId)}`, {
|
|
18056
|
+
...requestOpts,
|
|
18057
|
+
method: "PATCH",
|
|
18058
|
+
body: { description: ableTimeDocFromText(description) }
|
|
18059
|
+
}).catch(() => void 0);
|
|
18060
|
+
}
|
|
18061
|
+
if (createdId && state && state !== "todo") {
|
|
18062
|
+
await abletimeRestRequest(`/tasks/${encodeURIComponent(createdId)}/state`, {
|
|
18063
|
+
...requestOpts,
|
|
18064
|
+
method: "POST",
|
|
18065
|
+
body: { taskState: state }
|
|
18066
|
+
}).catch(() => void 0);
|
|
18067
|
+
}
|
|
18068
|
+
return createdId ? await getTaskRaw(createdId, requestOpts) : restTaskToRaw(created, await listUsers(requestOpts));
|
|
18069
|
+
}
|
|
18070
|
+
case "update_task": {
|
|
18071
|
+
const id = await resolveTaskId(firstStringArg(args, ["id", "task_id", "task"]), requestOpts);
|
|
18072
|
+
const patch = {};
|
|
18073
|
+
const title = firstStringArg(args, ["title"]);
|
|
18074
|
+
if (title) patch.title = title;
|
|
18075
|
+
if (args.description !== void 0) {
|
|
18076
|
+
patch.description = args.description == null || args.description === "" ? null : ableTimeDocFromText(String(args.description));
|
|
18077
|
+
}
|
|
18078
|
+
if (args.tags !== void 0 || args.labels !== void 0) {
|
|
18079
|
+
patch.tags = args.tags ?? args.labels ?? args.tag_names;
|
|
18080
|
+
}
|
|
18081
|
+
if (Object.keys(patch).length > 0) {
|
|
18082
|
+
await abletimeRestRequest(`/tasks/${encodeURIComponent(id)}`, {
|
|
18083
|
+
...requestOpts,
|
|
18084
|
+
method: "PATCH",
|
|
18085
|
+
body: patch
|
|
18086
|
+
});
|
|
18087
|
+
}
|
|
18088
|
+
return await getTaskRaw(id, requestOpts);
|
|
18089
|
+
}
|
|
18090
|
+
case "set_task_state": {
|
|
18091
|
+
const id = await resolveTaskId(firstStringArg(args, ["id", "task_id", "task"]), requestOpts);
|
|
18092
|
+
const state = firstStringArg(args, ["state", "taskState"]);
|
|
18093
|
+
if (!state) throw new Error("AbleTime task state is required");
|
|
18094
|
+
await abletimeRestRequest(`/tasks/${encodeURIComponent(id)}/state`, {
|
|
18095
|
+
...requestOpts,
|
|
18096
|
+
method: "POST",
|
|
18097
|
+
body: { taskState: state }
|
|
18098
|
+
});
|
|
18099
|
+
return await getTaskRaw(id, requestOpts);
|
|
18100
|
+
}
|
|
18101
|
+
case "create_comment": {
|
|
18102
|
+
const id = await resolveTaskId(firstStringArg(args, ["id", "task_id", "task"]), requestOpts);
|
|
18103
|
+
const body = firstStringArg(args, ["body", "comment", "text"]);
|
|
18104
|
+
if (!body) throw new Error("AbleTime comment body is required");
|
|
18105
|
+
const created = await abletimeRestRequest(`/tasks/${encodeURIComponent(id)}/comments`, {
|
|
18106
|
+
...requestOpts,
|
|
18107
|
+
method: "POST",
|
|
18108
|
+
body: { content: ableTimeDocFromText(body) }
|
|
18109
|
+
});
|
|
18110
|
+
const rec = asRecord5(created) ?? {};
|
|
18111
|
+
return {
|
|
18112
|
+
id: asString(rec.commentId) || void 0,
|
|
18113
|
+
body: textFromAbleTimeDoc(rec.content) || body
|
|
18114
|
+
};
|
|
18115
|
+
}
|
|
18116
|
+
case "set_task_dependency":
|
|
18117
|
+
throw new Error(
|
|
18118
|
+
"AbleTime organization API keys cannot set task dependencies on REST yet \u2014 use a personal access token (apt_\u2026) for MCP project-management tools."
|
|
18119
|
+
);
|
|
18120
|
+
default:
|
|
18121
|
+
throw new Error(`AbleTime organization API keys do not support ${name} on REST`);
|
|
18122
|
+
}
|
|
18123
|
+
}
|
|
18124
|
+
var ABLETIME_REST_PREFIX, ULID_RE;
|
|
18125
|
+
var init_abletime_rest = __esm({
|
|
18126
|
+
"src/integrations/abletime-rest.ts"() {
|
|
18127
|
+
"use strict";
|
|
18128
|
+
init_fetch();
|
|
18129
|
+
init_app_settings();
|
|
18130
|
+
init_abletime_mcp();
|
|
18131
|
+
ABLETIME_REST_PREFIX = "/api/public/v2";
|
|
18132
|
+
ULID_RE = /^[0-9A-HJKMNP-TV-Z]{26}$/i;
|
|
18133
|
+
}
|
|
18134
|
+
});
|
|
18135
|
+
|
|
17462
18136
|
// src/integrations/abletime-mcp.ts
|
|
17463
18137
|
function abletimeToolUsesPm(name) {
|
|
17464
18138
|
return ABLETIME_PM_TOOL_NAMES.includes(name);
|
|
@@ -17472,22 +18146,49 @@ function abletimeMcpUrl(host, opts) {
|
|
|
17472
18146
|
const path2 = opts?.pm ? ABLETIME_MCP_PM_PATH : ABLETIME_MCP_PATH;
|
|
17473
18147
|
return `${normalizeAbleTimeHost(host)}${path2}`;
|
|
17474
18148
|
}
|
|
18149
|
+
function normalizeAbleTimeCredential(raw) {
|
|
18150
|
+
return raw.trim().replace(/^bearer\s+/i, "").trim();
|
|
18151
|
+
}
|
|
18152
|
+
function ableTimeCredentialKind(raw) {
|
|
18153
|
+
const token = normalizeAbleTimeCredential(raw).toLowerCase();
|
|
18154
|
+
if (token.startsWith(ABLETIME_PAT_PREFIX)) return "pat";
|
|
18155
|
+
if (token.startsWith(ABLETIME_ORG_KEY_PREFIX)) return "org";
|
|
18156
|
+
return "unknown";
|
|
18157
|
+
}
|
|
18158
|
+
function assertAbleTimeMcpCredential(raw) {
|
|
18159
|
+
const token = normalizeAbleTimeCredential(raw);
|
|
18160
|
+
if (!token) {
|
|
18161
|
+
throw new Error("AbleTime personal access token is required");
|
|
18162
|
+
}
|
|
18163
|
+
if (ableTimeCredentialKind(token) === "org") {
|
|
18164
|
+
throw new Error(ORG_KEY_HINT);
|
|
18165
|
+
}
|
|
18166
|
+
return token;
|
|
18167
|
+
}
|
|
18168
|
+
function assertAbleTimeCredential(raw) {
|
|
18169
|
+
const token = normalizeAbleTimeCredential(raw);
|
|
18170
|
+
if (!token) {
|
|
18171
|
+
throw new Error("AbleTime API key or personal access token is required");
|
|
18172
|
+
}
|
|
18173
|
+
return token;
|
|
18174
|
+
}
|
|
17475
18175
|
function rewriteAbleTimeError(message) {
|
|
17476
|
-
if (
|
|
18176
|
+
if (REWRITE_APPLIED.test(message)) return message;
|
|
18177
|
+
if (/INTEGRATION_AGENT_ACCESS_DISABLED/i.test(message)) {
|
|
17477
18178
|
return `${message} \u2014 Enable Agent access (MCP) in AbleTime Settings \u2192 Integrations \u2192 API Keys.`;
|
|
17478
18179
|
}
|
|
17479
|
-
if (/INTEGRATION_PAT_REQUIRED
|
|
17480
|
-
return `${message} \u2014
|
|
18180
|
+
if (/INTEGRATION_PAT_REQUIRED/i.test(message)) {
|
|
18181
|
+
return `${message} \u2014 ${ORG_KEY_HINT}`;
|
|
17481
18182
|
}
|
|
17482
18183
|
if (/INTEGRATION_PLAN_REQUIRED/i.test(message)) {
|
|
17483
18184
|
return `${message} \u2014 AbleTime plan does not include API access.`;
|
|
17484
18185
|
}
|
|
17485
18186
|
if (/INTEGRATION_KEY_INVALID|INTEGRATION_KEY_MISSING|401|unauthorized|invalid token/i.test(message)) {
|
|
17486
|
-
return `${message} \u2014
|
|
18187
|
+
return `${message} \u2014 ${REJECTED_CREDENTIAL_HINT}`;
|
|
17487
18188
|
}
|
|
17488
18189
|
return message;
|
|
17489
18190
|
}
|
|
17490
|
-
function
|
|
18191
|
+
function asRecord6(value) {
|
|
17491
18192
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
17492
18193
|
}
|
|
17493
18194
|
function parseSseJson(body) {
|
|
@@ -17509,12 +18210,12 @@ function parseSseJson(body) {
|
|
|
17509
18210
|
}
|
|
17510
18211
|
}
|
|
17511
18212
|
function unwrapToolResult(result) {
|
|
17512
|
-
const record =
|
|
18213
|
+
const record = asRecord6(result);
|
|
17513
18214
|
if (!record) return result;
|
|
17514
18215
|
const content = record.content;
|
|
17515
18216
|
if (Array.isArray(content)) {
|
|
17516
18217
|
const texts = content.map((part) => {
|
|
17517
|
-
const item =
|
|
18218
|
+
const item = asRecord6(part);
|
|
17518
18219
|
if (!item) return "";
|
|
17519
18220
|
return typeof item.text === "string" ? item.text : "";
|
|
17520
18221
|
}).filter(Boolean);
|
|
@@ -17536,17 +18237,18 @@ function unwrapToolResult(result) {
|
|
|
17536
18237
|
return result;
|
|
17537
18238
|
}
|
|
17538
18239
|
function textsFromUnknown(value) {
|
|
17539
|
-
const record =
|
|
18240
|
+
const record = asRecord6(value);
|
|
17540
18241
|
if (!record) return "";
|
|
17541
18242
|
if (typeof record.message === "string") return record.message;
|
|
17542
18243
|
if (typeof record.error === "string") return record.error;
|
|
17543
18244
|
return "";
|
|
17544
18245
|
}
|
|
17545
18246
|
async function abletimeMcpRequest(method, params, opts) {
|
|
17546
|
-
const
|
|
17547
|
-
if (!
|
|
18247
|
+
const rawToken = opts?.token ?? await Promise.resolve().then(() => (init_abletime_oauth(), abletime_oauth_exports)).then((m) => m.ensureAbleTimeAccessToken());
|
|
18248
|
+
if (!rawToken?.trim()) {
|
|
17548
18249
|
throw new Error("AbleTime is not connected \u2014 paste a personal access token in Account settings");
|
|
17549
18250
|
}
|
|
18251
|
+
const token = assertAbleTimeMcpCredential(rawToken);
|
|
17550
18252
|
const url = abletimeMcpUrl(
|
|
17551
18253
|
opts?.host ?? (opts?.token ? DEFAULT_ABLETIME_HOST : getAbleTimeHost()),
|
|
17552
18254
|
{ pm: opts?.pm }
|
|
@@ -17582,7 +18284,7 @@ async function abletimeMcpRequest(method, params, opts) {
|
|
|
17582
18284
|
return parseSseJson(body);
|
|
17583
18285
|
}
|
|
17584
18286
|
})();
|
|
17585
|
-
const rpc =
|
|
18287
|
+
const rpc = asRecord6(parsed);
|
|
17586
18288
|
if (rpc?.error) {
|
|
17587
18289
|
const detail = typeof rpc.error.message === "string" && rpc.error.message.trim() ? rpc.error.message : `AbleTime MCP error ${rpc.error.code ?? ""}`.trim();
|
|
17588
18290
|
throw new Error(rewriteAbleTimeError(detail));
|
|
@@ -17613,6 +18315,11 @@ async function callAbleTimeTool(name, args = {}, opts) {
|
|
|
17613
18315
|
if (!opts?.token && !isAbleTimeConnected()) {
|
|
17614
18316
|
throw new Error("AbleTime is not connected \u2014 paste a personal access token in Account settings");
|
|
17615
18317
|
}
|
|
18318
|
+
const rawToken = opts?.token ?? await Promise.resolve().then(() => (init_abletime_oauth(), abletime_oauth_exports)).then((m) => m.ensureAbleTimeAccessToken());
|
|
18319
|
+
if (rawToken && ableTimeCredentialKind(rawToken) === "org") {
|
|
18320
|
+
const { callAbleTimeRestTool: callAbleTimeRestTool2 } = await Promise.resolve().then(() => (init_abletime_rest(), abletime_rest_exports));
|
|
18321
|
+
return callAbleTimeRestTool2(name, args, { ...opts, token: rawToken });
|
|
18322
|
+
}
|
|
17616
18323
|
const requestOpts = { ...opts, pm: opts?.pm ?? abletimeToolUsesPm(name) };
|
|
17617
18324
|
const call = () => abletimeMcpRequest("tools/call", { name, arguments: args }, requestOpts).then((result) => {
|
|
17618
18325
|
return unwrapToolResult(result);
|
|
@@ -17628,7 +18335,7 @@ async function callAbleTimeTool(name, args = {}, opts) {
|
|
|
17628
18335
|
throw err instanceof Error ? new Error(rewriteAbleTimeError(err.message)) : err;
|
|
17629
18336
|
}
|
|
17630
18337
|
}
|
|
17631
|
-
var DEFAULT_ABLETIME_HOST, ABLETIME_MCP_PATH, ABLETIME_MCP_PM_PATH, MCP_PROTOCOL_VERSION, ABLETIME_PM_TOOL_NAMES;
|
|
18338
|
+
var DEFAULT_ABLETIME_HOST, ABLETIME_MCP_PATH, ABLETIME_MCP_PM_PATH, MCP_PROTOCOL_VERSION, ABLETIME_PM_TOOL_NAMES, ABLETIME_PAT_PREFIX, ABLETIME_ORG_KEY_PREFIX, ORG_KEY_HINT, REJECTED_CREDENTIAL_HINT, REWRITE_APPLIED;
|
|
17632
18339
|
var init_abletime_mcp = __esm({
|
|
17633
18340
|
"src/integrations/abletime-mcp.ts"() {
|
|
17634
18341
|
"use strict";
|
|
@@ -17650,6 +18357,11 @@ var init_abletime_mcp = __esm({
|
|
|
17650
18357
|
"archive_task",
|
|
17651
18358
|
"delete_task"
|
|
17652
18359
|
];
|
|
18360
|
+
ABLETIME_PAT_PREFIX = "apt_";
|
|
18361
|
+
ABLETIME_ORG_KEY_PREFIX = "atk_";
|
|
18362
|
+
ORG_KEY_HINT = "AbleTime MCP needs a personal access token (apt_\u2026) from Edit Profile \u2192 API Access, not an organization API key (atk_\u2026) from Settings \u2192 Integrations \u2192 API Keys.";
|
|
18363
|
+
REJECTED_CREDENTIAL_HINT = "AbleTime rejected this credential (unknown, revoked, or expired). Use a personal access token (apt_\u2026) from Edit Profile \u2192 API Access \u2014 organization API keys (atk_\u2026) cannot call MCP. Tokens are shown once; rotating replaces the previous one.";
|
|
18364
|
+
REWRITE_APPLIED = /— Enable Agent access \(MCP\)|— AbleTime MCP needs a personal access token|— AbleTime plan does not include API access|— AbleTime rejected this credential/;
|
|
17653
18365
|
}
|
|
17654
18366
|
});
|
|
17655
18367
|
|
|
@@ -17673,16 +18385,16 @@ __export(abletime_exports, {
|
|
|
17673
18385
|
updateAbleTimeTask: () => updateAbleTimeTask,
|
|
17674
18386
|
verifyAbleTimeConnection: () => verifyAbleTimeConnection
|
|
17675
18387
|
});
|
|
17676
|
-
function
|
|
18388
|
+
function asRecord7(value) {
|
|
17677
18389
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
17678
18390
|
}
|
|
17679
|
-
function
|
|
18391
|
+
function asString2(value) {
|
|
17680
18392
|
return typeof value === "string" ? value.trim() : "";
|
|
17681
18393
|
}
|
|
17682
18394
|
function firstString(record, keys) {
|
|
17683
18395
|
if (!record) return "";
|
|
17684
18396
|
for (const key of keys) {
|
|
17685
|
-
const value =
|
|
18397
|
+
const value = asString2(record[key]);
|
|
17686
18398
|
if (value) return value;
|
|
17687
18399
|
}
|
|
17688
18400
|
return "";
|
|
@@ -17690,14 +18402,14 @@ function firstString(record, keys) {
|
|
|
17690
18402
|
function firstRecord(record, keys) {
|
|
17691
18403
|
if (!record) return null;
|
|
17692
18404
|
for (const key of keys) {
|
|
17693
|
-
const value =
|
|
18405
|
+
const value = asRecord7(record[key]);
|
|
17694
18406
|
if (value) return value;
|
|
17695
18407
|
}
|
|
17696
18408
|
return null;
|
|
17697
18409
|
}
|
|
17698
|
-
function
|
|
18410
|
+
function asList2(value) {
|
|
17699
18411
|
if (Array.isArray(value)) return value;
|
|
17700
|
-
const record =
|
|
18412
|
+
const record = asRecord7(value);
|
|
17701
18413
|
if (!record) return [];
|
|
17702
18414
|
for (const key of ["data", "tasks", "projects", "items", "results", "nodes"]) {
|
|
17703
18415
|
if (Array.isArray(record[key])) return record[key];
|
|
@@ -17709,21 +18421,21 @@ function labelsOf(record) {
|
|
|
17709
18421
|
if (!Array.isArray(raw)) return [];
|
|
17710
18422
|
return raw.map((item) => {
|
|
17711
18423
|
if (typeof item === "string") return item.trim();
|
|
17712
|
-
const rec =
|
|
18424
|
+
const rec = asRecord7(item);
|
|
17713
18425
|
return rec ? firstString(rec, ["name", "title", "label"]) : "";
|
|
17714
18426
|
}).filter(Boolean);
|
|
17715
18427
|
}
|
|
17716
18428
|
function commentsOf(record) {
|
|
17717
18429
|
const raw = record.comments ?? record.notes ?? record.discussion;
|
|
17718
18430
|
const out = [];
|
|
17719
|
-
for (const item of
|
|
17720
|
-
const rec =
|
|
18431
|
+
for (const item of asList2(raw)) {
|
|
18432
|
+
const rec = asRecord7(item);
|
|
17721
18433
|
if (!rec) continue;
|
|
17722
|
-
const body = firstString(rec, ["body", "text", "comment", "
|
|
18434
|
+
const body = firstString(rec, ["body", "text", "comment", "message", "contentSanitized"]) || textFromAbleTimeDoc(rec.content);
|
|
17723
18435
|
if (!body) continue;
|
|
17724
|
-
const user = firstString(
|
|
18436
|
+
const user = firstString(asRecord7(rec.user) ?? asRecord7(rec.author), ["name", "display_name", "username"]) || firstString(rec, ["user_name", "author", "username"]);
|
|
17725
18437
|
const comment = { body };
|
|
17726
|
-
const id = firstString(rec, ["id", "comment_id"]);
|
|
18438
|
+
const id = firstString(rec, ["id", "comment_id", "commentId"]);
|
|
17727
18439
|
if (id) comment.id = id;
|
|
17728
18440
|
const url = firstString(rec, ["url", "permalink"]);
|
|
17729
18441
|
if (url) comment.url = url;
|
|
@@ -17747,11 +18459,11 @@ function taskUrl(task, host) {
|
|
|
17747
18459
|
return `${base}/tasks/${slug}`;
|
|
17748
18460
|
}
|
|
17749
18461
|
function mapAbleTimeTask(raw, host) {
|
|
17750
|
-
const record =
|
|
18462
|
+
const record = asRecord7(raw);
|
|
17751
18463
|
if (!record) return null;
|
|
17752
18464
|
const nested = firstRecord(record, ["task", "data"]) ?? record;
|
|
17753
|
-
const id = firstString(nested, ["id", "task_id", "taskId"]);
|
|
17754
|
-
const identifier = firstString(nested, ["reference", "ref", "identifier", "key", "code", "number"]) || id;
|
|
18465
|
+
const id = firstString(nested, ["id", "task_id", "taskId", "timeflowTaskId"]);
|
|
18466
|
+
const identifier = firstString(nested, ["reference", "ref", "identifier", "key", "code", "number", "taskRef"]) || id;
|
|
17755
18467
|
const title = firstString(nested, ["title", "name"]);
|
|
17756
18468
|
if (!id && !identifier && !title) return null;
|
|
17757
18469
|
const resolvedId = id || identifier;
|
|
@@ -17762,21 +18474,22 @@ function mapAbleTimeTask(raw, host) {
|
|
|
17762
18474
|
identifier: resolvedIdentifier,
|
|
17763
18475
|
title: title || resolvedIdentifier,
|
|
17764
18476
|
url,
|
|
17765
|
-
description: firstString(nested, ["description", "body", "details"]) || void 0,
|
|
17766
|
-
state: firstString(nested, ["state", "board_state", "boardState", "status"]) || void 0,
|
|
17767
|
-
projectId: firstString(nested, ["project_id", "projectId"]) || firstString(
|
|
17768
|
-
categoryId: firstString(nested, ["category_id", "categoryId"]) || firstString(
|
|
18477
|
+
description: firstString(nested, ["description", "body", "details", "descriptionSanitized"]) || textFromAbleTimeDoc(nested.description) || void 0,
|
|
18478
|
+
state: firstString(nested, ["state", "board_state", "boardState", "status", "taskState"]) || void 0,
|
|
18479
|
+
projectId: firstString(nested, ["project_id", "projectId"]) || firstString(asRecord7(nested.project), ["id"]) || void 0,
|
|
18480
|
+
categoryId: firstString(nested, ["category_id", "categoryId", "projectCategoryId"]) || firstString(asRecord7(nested.category), ["id"]) || void 0,
|
|
17769
18481
|
assignee: assigneeOf(nested),
|
|
17770
18482
|
labels: labelsOf(nested),
|
|
17771
18483
|
comments: commentsOf(nested),
|
|
17772
|
-
createdAt: firstString(nested, ["created_at", "createdAt", "created"]) || void 0,
|
|
18484
|
+
createdAt: firstString(nested, ["created_at", "createdAt", "created", "dateCreated"]) || void 0,
|
|
17773
18485
|
updatedAt: firstString(nested, [
|
|
17774
18486
|
"updated_at",
|
|
17775
18487
|
"updatedAt",
|
|
17776
18488
|
"modified_at",
|
|
17777
18489
|
"modifiedAt",
|
|
17778
18490
|
"last_activity_at",
|
|
17779
|
-
"lastActivityAt"
|
|
18491
|
+
"lastActivityAt",
|
|
18492
|
+
"lastUpdate"
|
|
17780
18493
|
]) || void 0
|
|
17781
18494
|
};
|
|
17782
18495
|
}
|
|
@@ -17807,16 +18520,16 @@ function issueAttachmentForAbleTimeTask(task) {
|
|
|
17807
18520
|
};
|
|
17808
18521
|
}
|
|
17809
18522
|
function mapProject(raw) {
|
|
17810
|
-
const record =
|
|
18523
|
+
const record = asRecord7(raw);
|
|
17811
18524
|
if (!record) return null;
|
|
17812
|
-
const id = firstString(record, ["id", "project_id"]);
|
|
17813
|
-
const name = firstString(record, ["name", "title"]);
|
|
18525
|
+
const id = firstString(record, ["id", "project_id", "projectId"]);
|
|
18526
|
+
const name = firstString(record, ["name", "title", "projectName"]);
|
|
17814
18527
|
if (!id && !name) return null;
|
|
17815
|
-
const categories =
|
|
17816
|
-
const rec =
|
|
18528
|
+
const categories = asList2(record.categories ?? record.category).map((item) => {
|
|
18529
|
+
const rec = asRecord7(item);
|
|
17817
18530
|
if (!rec) return null;
|
|
17818
|
-
const categoryId = firstString(rec, ["id", "category_id"]);
|
|
17819
|
-
const categoryName = firstString(rec, ["name", "title"]);
|
|
18531
|
+
const categoryId = firstString(rec, ["id", "category_id", "projectCategoryId", "categoryId"]);
|
|
18532
|
+
const categoryName = firstString(rec, ["name", "title", "categoryName"]);
|
|
17820
18533
|
if (!categoryId && !categoryName) return null;
|
|
17821
18534
|
return { id: categoryId || categoryName, name: categoryName || categoryId };
|
|
17822
18535
|
}).filter((item) => Boolean(item));
|
|
@@ -17828,21 +18541,21 @@ function isOpenTask(task) {
|
|
|
17828
18541
|
}
|
|
17829
18542
|
async function getAbleTimeOrientation(opts) {
|
|
17830
18543
|
const raw = await callAbleTimeTool("orientation", {}, opts);
|
|
17831
|
-
const record =
|
|
18544
|
+
const record = asRecord7(raw);
|
|
17832
18545
|
const viewerRec = firstRecord(record, ["viewer", "user", "actor", "me", "person"]) ?? record;
|
|
17833
18546
|
const viewer = {
|
|
17834
18547
|
id: firstString(viewerRec, ["id", "user_id"]) || void 0,
|
|
17835
18548
|
name: firstString(viewerRec, ["name", "display_name", "full_name"]) || firstString(record, ["name"]) || void 0
|
|
17836
18549
|
};
|
|
17837
|
-
const projects =
|
|
18550
|
+
const projects = asList2(
|
|
17838
18551
|
record?.projects ?? record?.current_projects ?? record?.currentProjects
|
|
17839
18552
|
).map(mapProject).filter((item) => Boolean(item));
|
|
17840
|
-
const tasks =
|
|
18553
|
+
const tasks = asList2(record?.tasks ?? record?.active_tasks ?? record?.activeTasks).map((item) => mapAbleTimeTask(item, opts?.host)).filter((item) => Boolean(item));
|
|
17841
18554
|
return { viewer, projects, tasks, raw };
|
|
17842
18555
|
}
|
|
17843
18556
|
async function listAbleTimeProjects(opts) {
|
|
17844
18557
|
const raw = await callAbleTimeTool("list_projects", { include_categories: true }, opts);
|
|
17845
|
-
return
|
|
18558
|
+
return asList2(raw).map(mapProject).filter((item) => Boolean(item));
|
|
17846
18559
|
}
|
|
17847
18560
|
async function listAbleTimeTasks(opts) {
|
|
17848
18561
|
const args = {};
|
|
@@ -17852,14 +18565,14 @@ async function listAbleTimeTasks(opts) {
|
|
|
17852
18565
|
}
|
|
17853
18566
|
if (opts?.query) args.query = opts.query;
|
|
17854
18567
|
const raw = await callAbleTimeTool("list_tasks", args, opts);
|
|
17855
|
-
const tasks =
|
|
18568
|
+
const tasks = asList2(raw).map((item) => mapAbleTimeTask(item, opts?.host)).filter((item) => Boolean(item));
|
|
17856
18569
|
return opts?.includeClosed ? tasks : tasks.filter(isOpenTask);
|
|
17857
18570
|
}
|
|
17858
18571
|
async function searchAbleTimeTasks(query, opts) {
|
|
17859
18572
|
const q = query.trim();
|
|
17860
18573
|
if (!q) return [];
|
|
17861
18574
|
const raw = await callAbleTimeTool("search_tasks", { query: q, q }, opts);
|
|
17862
|
-
return
|
|
18575
|
+
return asList2(raw).map((item) => mapAbleTimeTask(item, opts?.host)).filter((item) => Boolean(item)).filter(isOpenTask);
|
|
17863
18576
|
}
|
|
17864
18577
|
async function getAbleTimeTask(id, opts) {
|
|
17865
18578
|
const raw = await callAbleTimeTool("get_task", { id, task_id: id }, opts);
|
|
@@ -17907,7 +18620,7 @@ async function commentAbleTimeTask(input, opts) {
|
|
|
17907
18620
|
{ id, task_id: id, task: id, body, comment: body, text: body },
|
|
17908
18621
|
opts
|
|
17909
18622
|
);
|
|
17910
|
-
const rec =
|
|
18623
|
+
const rec = asRecord7(raw);
|
|
17911
18624
|
return {
|
|
17912
18625
|
id: rec ? firstString(rec, ["id", "comment_id"]) || void 0 : void 0,
|
|
17913
18626
|
body: rec ? firstString(rec, ["body", "text", "comment"]) || body : body
|
|
@@ -18097,8 +18810,7 @@ async function listAbleTimeAssignedIssues(opts) {
|
|
|
18097
18810
|
};
|
|
18098
18811
|
}
|
|
18099
18812
|
async function verifyAbleTimeConnection(input) {
|
|
18100
|
-
const token = input.token
|
|
18101
|
-
if (!token) throw new Error("AbleTime personal access token is required");
|
|
18813
|
+
const token = assertAbleTimeCredential(input.token);
|
|
18102
18814
|
const host = input.host?.trim() || void 0;
|
|
18103
18815
|
const orientation = await getAbleTimeOrientation({ token, host });
|
|
18104
18816
|
saveAbleTimeConnection({
|
|
@@ -18114,6 +18826,7 @@ var init_abletime = __esm({
|
|
|
18114
18826
|
"use strict";
|
|
18115
18827
|
init_app_settings();
|
|
18116
18828
|
init_abletime_mcp();
|
|
18829
|
+
init_abletime_rest();
|
|
18117
18830
|
CLOSED_STATES = /* @__PURE__ */ new Set([
|
|
18118
18831
|
"done",
|
|
18119
18832
|
"completed",
|
|
@@ -18697,12 +19410,19 @@ function buildQuotaHandoffAttachment(from, limitText, fallbackAgent) {
|
|
|
18697
19410
|
...instructions
|
|
18698
19411
|
].join("\n");
|
|
18699
19412
|
return {
|
|
18700
|
-
id: (0,
|
|
19413
|
+
id: (0, import_node_crypto11.randomUUID)(),
|
|
18701
19414
|
name: orch ? "Orchestration quota handoff.md" : "Quota handoff.md",
|
|
18702
19415
|
kind: "transcript",
|
|
18703
19416
|
content: body
|
|
18704
19417
|
};
|
|
18705
19418
|
}
|
|
19419
|
+
async function isolateQuotaFailover(run2) {
|
|
19420
|
+
try {
|
|
19421
|
+
return await run2();
|
|
19422
|
+
} catch {
|
|
19423
|
+
return true;
|
|
19424
|
+
}
|
|
19425
|
+
}
|
|
18706
19426
|
function createQuotaFailoverChat(from, fallbackAgent, limitText) {
|
|
18707
19427
|
const handoff = buildQuotaHandoffAttachment(from, limitText, fallbackAgent);
|
|
18708
19428
|
const tab = createChatTab({
|
|
@@ -18721,11 +19441,11 @@ function createQuotaFailoverChat(from, fallbackAgent, limitText) {
|
|
|
18721
19441
|
}
|
|
18722
19442
|
return updateThread(tab.id, patch);
|
|
18723
19443
|
}
|
|
18724
|
-
var
|
|
19444
|
+
var import_node_crypto11, QUOTA_CONTINUE_PROMPT, QUOTA_RESUME_PROMPT;
|
|
18725
19445
|
var init_quota_failover = __esm({
|
|
18726
19446
|
"src/orchestrator/quota-failover.ts"() {
|
|
18727
19447
|
"use strict";
|
|
18728
|
-
|
|
19448
|
+
import_node_crypto11 = require("crypto");
|
|
18729
19449
|
init_session_quota();
|
|
18730
19450
|
init_app_settings();
|
|
18731
19451
|
init_global_workspace();
|
|
@@ -20978,16 +21698,16 @@ function normalizeServiceOrigin(raw, fallback) {
|
|
|
20978
21698
|
return fallback;
|
|
20979
21699
|
}
|
|
20980
21700
|
}
|
|
20981
|
-
function
|
|
21701
|
+
function asRecord8(value) {
|
|
20982
21702
|
return value && typeof value === "object" && !Array.isArray(value) ? value : null;
|
|
20983
21703
|
}
|
|
20984
|
-
function
|
|
21704
|
+
function asString3(value) {
|
|
20985
21705
|
return typeof value === "string" ? value.trim() : "";
|
|
20986
21706
|
}
|
|
20987
21707
|
function firstString2(record, keys) {
|
|
20988
21708
|
if (!record) return "";
|
|
20989
21709
|
for (const key of keys) {
|
|
20990
|
-
const value =
|
|
21710
|
+
const value = asString3(record[key]);
|
|
20991
21711
|
if (value) return value;
|
|
20992
21712
|
}
|
|
20993
21713
|
return "";
|
|
@@ -21011,7 +21731,7 @@ async function authorizedGet(url, token) {
|
|
|
21011
21731
|
});
|
|
21012
21732
|
const body = await readJson(res);
|
|
21013
21733
|
if (!res.ok) {
|
|
21014
|
-
const record =
|
|
21734
|
+
const record = asRecord8(body);
|
|
21015
21735
|
const message = firstString2(record, ["message", "error", "error_description", "detail"]) || (typeof body === "string" ? body.trim().slice(0, 160) : "");
|
|
21016
21736
|
throw new Error(
|
|
21017
21737
|
message ? `${res.status}: ${message}` : `Request failed (${res.status})`
|
|
@@ -21025,28 +21745,28 @@ async function verifyOptionalService(input) {
|
|
|
21025
21745
|
const spec = optionalServiceSpec(input.id);
|
|
21026
21746
|
try {
|
|
21027
21747
|
if (input.id === "vercel") {
|
|
21028
|
-
const body2 =
|
|
21029
|
-
const user =
|
|
21748
|
+
const body2 = asRecord8(await authorizedGet("https://api.vercel.com/v2/user", token));
|
|
21749
|
+
const user = asRecord8(body2?.user) ?? body2;
|
|
21030
21750
|
const name2 = firstString2(user, ["username", "name", "email"]) || "Vercel user";
|
|
21031
21751
|
return { viewerName: name2 };
|
|
21032
21752
|
}
|
|
21033
21753
|
if (input.id === "supabase") {
|
|
21034
21754
|
const body2 = await authorizedGet("https://api.supabase.com/v1/organizations", token);
|
|
21035
21755
|
const list2 = Array.isArray(body2) ? body2 : [];
|
|
21036
|
-
const first2 =
|
|
21756
|
+
const first2 = asRecord8(list2[0]);
|
|
21037
21757
|
const name2 = firstString2(first2, ["name", "slug"]) || "Supabase";
|
|
21038
21758
|
return { viewerName: list2.length > 1 ? `${name2} +${list2.length - 1}` : name2 };
|
|
21039
21759
|
}
|
|
21040
21760
|
if (input.id === "posthog") {
|
|
21041
21761
|
const host2 = normalizeServiceOrigin(input.host, spec.defaultHost ?? "https://us.posthog.com");
|
|
21042
|
-
const body2 =
|
|
21762
|
+
const body2 = asRecord8(await authorizedGet(`${host2}/api/users/@me/`, token));
|
|
21043
21763
|
const name2 = firstString2(body2, ["email", "distinct_id"]) || [firstString2(body2, ["first_name"]), firstString2(body2, ["last_name"])].filter(Boolean).join(" ") || "PostHog user";
|
|
21044
21764
|
return { viewerName: name2, host: host2 };
|
|
21045
21765
|
}
|
|
21046
21766
|
const host = normalizeServiceOrigin(input.host, spec.defaultHost ?? "https://sentry.io");
|
|
21047
21767
|
const body = await authorizedGet(`${host}/api/0/organizations/`, token);
|
|
21048
21768
|
const list = Array.isArray(body) ? body : [];
|
|
21049
|
-
const first =
|
|
21769
|
+
const first = asRecord8(list[0]);
|
|
21050
21770
|
const name = firstString2(first, ["slug", "name"]) || "Sentry";
|
|
21051
21771
|
return { viewerName: list.length > 1 ? `${name} +${list.length - 1}` : name, host };
|
|
21052
21772
|
} catch (err) {
|
|
@@ -21233,7 +21953,7 @@ var init_optional_services = __esm({
|
|
|
21233
21953
|
});
|
|
21234
21954
|
|
|
21235
21955
|
// src/plan/plan-present.ts
|
|
21236
|
-
function
|
|
21956
|
+
function asRecord9(v) {
|
|
21237
21957
|
return v != null && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
21238
21958
|
}
|
|
21239
21959
|
function isPresentPlanToolName(name) {
|
|
@@ -21245,7 +21965,7 @@ function extractPresentedPlan(parts) {
|
|
|
21245
21965
|
for (let i = parts.length - 1; i >= 0; i--) {
|
|
21246
21966
|
const p = parts[i];
|
|
21247
21967
|
if (p.type !== "tool" || !isPresentPlanToolName(p.name)) continue;
|
|
21248
|
-
const input =
|
|
21968
|
+
const input = asRecord9(p.input) ?? {};
|
|
21249
21969
|
const content = typeof input.content === "string" ? input.content : typeof input.plan === "string" ? input.plan : typeof input.markdown === "string" ? input.markdown : "";
|
|
21250
21970
|
if (!content.trim()) continue;
|
|
21251
21971
|
const title = typeof input.title === "string" && input.title.trim() ? input.title.trim() : "Plan";
|
|
@@ -21928,51 +22648,62 @@ var init_orchestrator = __esm({
|
|
|
21928
22648
|
if (plan.action === "wait_reset" && plan.resumeAt) {
|
|
21929
22649
|
this.haltDrain.add(threadId);
|
|
21930
22650
|
this.scheduleQuotaResume(threadId, plan.resumeAt);
|
|
21931
|
-
|
|
21932
|
-
|
|
21933
|
-
|
|
21934
|
-
|
|
21935
|
-
|
|
21936
|
-
|
|
21937
|
-
|
|
21938
|
-
|
|
21939
|
-
|
|
21940
|
-
|
|
21941
|
-
|
|
21942
|
-
|
|
21943
|
-
|
|
21944
|
-
|
|
22651
|
+
try {
|
|
22652
|
+
setStatus(threadId, "idle", null);
|
|
22653
|
+
appendMessage(threadId, {
|
|
22654
|
+
role: "agent",
|
|
22655
|
+
text: `Sideboard will auto-retry this chat around ${plan.resumeAt.toLocaleString()} when the session limit resets.`,
|
|
22656
|
+
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
22657
|
+
});
|
|
22658
|
+
this.emit({
|
|
22659
|
+
type: "quota_failover",
|
|
22660
|
+
threadId,
|
|
22661
|
+
action: "wait_reset",
|
|
22662
|
+
message: plan.reason,
|
|
22663
|
+
resumeAt: plan.resumeAt.toISOString()
|
|
22664
|
+
});
|
|
22665
|
+
this.emit({ type: "status_changed", threadId, status: "idle" });
|
|
22666
|
+
} catch {
|
|
22667
|
+
}
|
|
21945
22668
|
return true;
|
|
21946
22669
|
}
|
|
21947
22670
|
if (plan.action === "switch_agent" && plan.fallbackAgent) {
|
|
21948
22671
|
this.haltDrain.add(threadId);
|
|
21949
|
-
|
|
21950
|
-
|
|
21951
|
-
|
|
21952
|
-
|
|
21953
|
-
|
|
22672
|
+
let next;
|
|
22673
|
+
try {
|
|
22674
|
+
next = createQuotaFailoverChat(
|
|
22675
|
+
thread,
|
|
22676
|
+
plan.fallbackAgent,
|
|
22677
|
+
plan.limitText
|
|
22678
|
+
);
|
|
22679
|
+
} catch {
|
|
22680
|
+
return false;
|
|
22681
|
+
}
|
|
21954
22682
|
this.clearQuotaResumeTimer(threadId);
|
|
21955
22683
|
try {
|
|
21956
22684
|
updateThread(threadId, { quotaResumeAt: null });
|
|
21957
22685
|
} catch {
|
|
21958
22686
|
}
|
|
21959
|
-
|
|
21960
|
-
|
|
21961
|
-
|
|
21962
|
-
|
|
21963
|
-
|
|
21964
|
-
|
|
21965
|
-
|
|
21966
|
-
|
|
21967
|
-
|
|
21968
|
-
|
|
21969
|
-
|
|
21970
|
-
|
|
21971
|
-
|
|
21972
|
-
|
|
21973
|
-
|
|
21974
|
-
|
|
21975
|
-
|
|
22687
|
+
try {
|
|
22688
|
+
appendMessage(threadId, {
|
|
22689
|
+
role: "agent",
|
|
22690
|
+
text: `Session limit on ${thread.agent}. Sideboard continued on ${plan.fallbackAgent} (Auto) in [${next.title}](sideboard://thread/${next.id}).`,
|
|
22691
|
+
ts: (/* @__PURE__ */ new Date()).toISOString()
|
|
22692
|
+
});
|
|
22693
|
+
this.emit({
|
|
22694
|
+
type: "quota_failover",
|
|
22695
|
+
threadId,
|
|
22696
|
+
action: "switch_agent",
|
|
22697
|
+
toThreadId: next.id,
|
|
22698
|
+
message: plan.reason
|
|
22699
|
+
});
|
|
22700
|
+
this.emit({
|
|
22701
|
+
type: "status_changed",
|
|
22702
|
+
threadId: next.id,
|
|
22703
|
+
status: next.status
|
|
22704
|
+
});
|
|
22705
|
+
} catch {
|
|
22706
|
+
}
|
|
21976
22707
|
void this.send(
|
|
21977
22708
|
next.id,
|
|
21978
22709
|
QUOTA_CONTINUE_PROMPT(thread.agent, plan.fallbackAgent)
|
|
@@ -22703,9 +23434,8 @@ var init_orchestrator = __esm({
|
|
|
22703
23434
|
this.maybeEnqueueJobContinue(threadId, chatText, parts);
|
|
22704
23435
|
} else {
|
|
22705
23436
|
const blob = [chatText, detail].filter(Boolean).join("\n");
|
|
22706
|
-
const failedOver = await
|
|
22707
|
-
threadId,
|
|
22708
|
-
blob
|
|
23437
|
+
const failedOver = await isolateQuotaFailover(
|
|
23438
|
+
() => this.maybeHandleOrchestrationQuotaFailover(threadId, blob)
|
|
22709
23439
|
);
|
|
22710
23440
|
this.maybeEnqueueCrashContinue(threadId, {
|
|
22711
23441
|
detail: detail || failDetail,
|
|
@@ -22739,9 +23469,8 @@ var init_orchestrator = __esm({
|
|
|
22739
23469
|
this.emit({ type: "status_changed", threadId, status: "error" });
|
|
22740
23470
|
}
|
|
22741
23471
|
this.emit({ type: "turn_finished", threadId, exitCode: 1 });
|
|
22742
|
-
const failedOver = await
|
|
22743
|
-
threadId,
|
|
22744
|
-
message
|
|
23472
|
+
const failedOver = await isolateQuotaFailover(
|
|
23473
|
+
() => this.maybeHandleOrchestrationQuotaFailover(threadId, message)
|
|
22745
23474
|
);
|
|
22746
23475
|
this.maybeEnqueueCrashContinue(threadId, {
|
|
22747
23476
|
detail: message,
|
|
@@ -23953,10 +24682,18 @@ var index_exports = {};
|
|
|
23953
24682
|
__export(index_exports, {
|
|
23954
24683
|
ABLETIME_MCP_PATH: () => ABLETIME_MCP_PATH,
|
|
23955
24684
|
ABLETIME_MCP_PM_PATH: () => ABLETIME_MCP_PM_PATH,
|
|
24685
|
+
ABLETIME_OAUTH_CANCELLED: () => ABLETIME_OAUTH_CANCELLED,
|
|
24686
|
+
ABLETIME_OAUTH_PORT: () => ABLETIME_OAUTH_PORT,
|
|
24687
|
+
ABLETIME_OAUTH_REDIRECT: () => ABLETIME_OAUTH_REDIRECT,
|
|
24688
|
+
ABLETIME_OAUTH_RESOURCE: () => ABLETIME_OAUTH_RESOURCE,
|
|
24689
|
+
ABLETIME_ORG_KEY_PREFIX: () => ABLETIME_ORG_KEY_PREFIX,
|
|
24690
|
+
ABLETIME_PAT_PREFIX: () => ABLETIME_PAT_PREFIX,
|
|
23956
24691
|
ABLETIME_PM_TOOL_NAMES: () => ABLETIME_PM_TOOL_NAMES,
|
|
23957
24692
|
AGENT_GIT_ACTIONS: () => AGENT_GIT_ACTIONS,
|
|
23958
24693
|
AGENT_RUNNER_MAX_OLD_SPACE_MB: () => AGENT_RUNNER_MAX_OLD_SPACE_MB,
|
|
23959
24694
|
ATTACHMENTS_DIR: () => ATTACHMENTS_DIR,
|
|
24695
|
+
AbleTimeOAuthCancelledError: () => AbleTimeOAuthCancelledError,
|
|
24696
|
+
BAKED_ABLETIME_OAUTH_CLIENT_ID: () => BAKED_ABLETIME_OAUTH_CLIENT_ID,
|
|
23960
24697
|
BAKED_SLACK_RELAY_URL: () => BAKED_SLACK_RELAY_URL,
|
|
23961
24698
|
BRIGHTSY_MCP_ALLOWED_TOOLS: () => BRIGHTSY_MCP_ALLOWED_TOOLS,
|
|
23962
24699
|
BRIGHTSY_SUMMARIZE_CONTEXT_TOOL: () => BRIGHTSY_SUMMARIZE_CONTEXT_TOOL,
|
|
@@ -24045,7 +24782,11 @@ __export(index_exports, {
|
|
|
24045
24782
|
WORKTREE_GITHUB_MCP_TOOLS: () => WORKTREE_GITHUB_MCP_TOOLS,
|
|
24046
24783
|
WORKTREE_LINEAR_MCP_TOOLS: () => WORKTREE_LINEAR_MCP_TOOLS,
|
|
24047
24784
|
WORKTREE_MCP_TOOLS: () => WORKTREE_MCP_TOOLS,
|
|
24785
|
+
ableTimeCredentialKind: () => ableTimeCredentialKind,
|
|
24048
24786
|
ableTimeDependencyTarget: () => ableTimeDependencyTarget,
|
|
24787
|
+
ableTimeOAuthAuthorizeUrl: () => ableTimeOAuthAuthorizeUrl,
|
|
24788
|
+
ableTimeOAuthClientId: () => ableTimeOAuthClientId,
|
|
24789
|
+
ableTimeOAuthResource: () => ableTimeOAuthResource,
|
|
24049
24790
|
abletimeMcpRequest: () => abletimeMcpRequest,
|
|
24050
24791
|
abletimeMcpUrl: () => abletimeMcpUrl,
|
|
24051
24792
|
abletimeToolUsesPm: () => abletimeToolUsesPm,
|
|
@@ -24076,6 +24817,8 @@ __export(index_exports, {
|
|
|
24076
24817
|
applyThreadIntoMain: () => applyThreadIntoMain,
|
|
24077
24818
|
applyTurnUsage: () => applyTurnUsage,
|
|
24078
24819
|
armSchedules: () => armSchedules,
|
|
24820
|
+
assertAbleTimeCredential: () => assertAbleTimeCredential,
|
|
24821
|
+
assertAbleTimeMcpCredential: () => assertAbleTimeMcpCredential,
|
|
24079
24822
|
assertOrchestratorCapableAgent: () => assertOrchestratorCapableAgent,
|
|
24080
24823
|
attachmentFromAbsolutePath: () => attachmentFromAbsolutePath,
|
|
24081
24824
|
attachmentsFromBuffers: () => attachmentsFromBuffers,
|
|
@@ -24155,6 +24898,7 @@ __export(index_exports, {
|
|
|
24155
24898
|
countCacheControlBlocks: () => countCacheControlBlocks,
|
|
24156
24899
|
countUnpushedVsOrigin: () => countUnpushedVsOrigin,
|
|
24157
24900
|
cowboyModeEnabled: () => cowboyModeEnabled,
|
|
24901
|
+
createAbleTimePkce: () => createAbleTimePkce,
|
|
24158
24902
|
createAbleTimeTask: () => createAbleTimeTask,
|
|
24159
24903
|
createChatTab: () => createChatTab,
|
|
24160
24904
|
createEmptyThread: () => createEmptyThread,
|
|
@@ -24194,6 +24938,7 @@ __export(index_exports, {
|
|
|
24194
24938
|
encodeBrightsyTarget: () => encodeBrightsyTarget,
|
|
24195
24939
|
enrichPathWithNpmGlobalBin: () => enrichPathWithNpmGlobalBin,
|
|
24196
24940
|
enrichWorkspacesWithGithub: () => enrichWorkspacesWithGithub,
|
|
24941
|
+
ensureAbleTimeAccessToken: () => ensureAbleTimeAccessToken,
|
|
24197
24942
|
ensureAbleTimeTask: () => ensureAbleTimeTask,
|
|
24198
24943
|
ensureAgentPath: () => ensureAgentPath,
|
|
24199
24944
|
ensureBrightsyLocalConfigFresh: () => ensureBrightsyLocalConfigFresh,
|
|
@@ -24340,6 +25085,7 @@ __export(index_exports, {
|
|
|
24340
25085
|
groupHomeBoardWorktrees: () => groupHomeBoardWorktrees,
|
|
24341
25086
|
handleSlackInbound: () => handleSlackInbound,
|
|
24342
25087
|
harnessEnvKey: () => harnessEnvKey,
|
|
25088
|
+
hasBakedAbleTimeOAuth: () => hasBakedAbleTimeOAuth,
|
|
24343
25089
|
hasBakedLinearOAuth: () => hasBakedLinearOAuth,
|
|
24344
25090
|
hasBakedSlackOAuth: () => hasBakedSlackOAuth,
|
|
24345
25091
|
hasConductorHook: () => hasConductorHook,
|
|
@@ -24364,6 +25110,7 @@ __export(index_exports, {
|
|
|
24364
25110
|
invalidateThreadListCache: () => invalidateThreadListCache,
|
|
24365
25111
|
invalidateThreadRecord: () => invalidateThreadRecord,
|
|
24366
25112
|
isAbleTimeConnected: () => isAbleTimeConnected,
|
|
25113
|
+
isAbleTimeOAuthCancelled: () => isAbleTimeOAuthCancelled,
|
|
24367
25114
|
isAskUserToolName: () => isAskUserToolName,
|
|
24368
25115
|
isBrightsyConnected: () => isBrightsyConnected,
|
|
24369
25116
|
isBrightsyNdjsonLine: () => isBrightsyNdjsonLine,
|
|
@@ -24493,6 +25240,7 @@ __export(index_exports, {
|
|
|
24493
25240
|
nextPastedTextName: () => nextPastedTextName,
|
|
24494
25241
|
nextThinkingEffort: () => nextThinkingEffort,
|
|
24495
25242
|
nonInteractiveGitProcessEnv: () => nonInteractiveGitProcessEnv,
|
|
25243
|
+
normalizeAbleTimeCredential: () => normalizeAbleTimeCredential,
|
|
24496
25244
|
normalizeAbleTimeHost: () => normalizeAbleTimeHost,
|
|
24497
25245
|
normalizeCodeSelection: () => normalizeCodeSelection,
|
|
24498
25246
|
normalizeLinearRelationType: () => normalizeLinearRelationType,
|
|
@@ -24674,6 +25422,7 @@ __export(index_exports, {
|
|
|
24674
25422
|
stackMergeReadiness: () => stackMergeReadiness,
|
|
24675
25423
|
stageAbsolutePathsAsAttachments: () => stageAbsolutePathsAsAttachments,
|
|
24676
25424
|
stageBuffersAsAttachments: () => stageBuffersAsAttachments,
|
|
25425
|
+
startAbleTimeOAuth: () => startAbleTimeOAuth,
|
|
24677
25426
|
startDevServer: () => startDevServer,
|
|
24678
25427
|
startLinearOAuth: () => startLinearOAuth,
|
|
24679
25428
|
startMcpServer: () => startMcpServer,
|
|
@@ -24976,8 +25725,8 @@ function issueActivityKind(issue, sinceIso) {
|
|
|
24976
25725
|
}
|
|
24977
25726
|
|
|
24978
25727
|
// src/integrations/linear-oauth.ts
|
|
24979
|
-
var
|
|
24980
|
-
var
|
|
25728
|
+
var import_node_crypto12 = require("crypto");
|
|
25729
|
+
var import_node_http2 = require("http");
|
|
24981
25730
|
init_app_settings();
|
|
24982
25731
|
init_fetch();
|
|
24983
25732
|
|
|
@@ -24995,7 +25744,7 @@ var LINEAR_TOKEN = "https://api.linear.app/oauth/token";
|
|
|
24995
25744
|
var LINEAR_REVOKE = "https://api.linear.app/oauth/revoke";
|
|
24996
25745
|
var LINEAR_GRAPHQL = "https://api.linear.app/graphql";
|
|
24997
25746
|
var LINEAR_OAUTH_SCOPES = "read,write";
|
|
24998
|
-
var
|
|
25747
|
+
var REFRESH_SKEW_MS3 = 5 * 6e4;
|
|
24999
25748
|
function linearOAuthCredentials() {
|
|
25000
25749
|
const settings = loadAppSettings();
|
|
25001
25750
|
const clientId = process.env.SIDEBOARD_LINEAR_CLIENT_ID?.trim() || settings.integrations.linearClientId?.trim() || BAKED_LINEAR_CLIENT_ID.trim();
|
|
@@ -25008,8 +25757,8 @@ function linearOAuthCredentials() {
|
|
|
25008
25757
|
return { clientId, clientSecret };
|
|
25009
25758
|
}
|
|
25010
25759
|
function createLinearPkce() {
|
|
25011
|
-
const verifier = (0,
|
|
25012
|
-
const challenge = (0,
|
|
25760
|
+
const verifier = (0, import_node_crypto12.randomBytes)(32).toString("base64url");
|
|
25761
|
+
const challenge = (0, import_node_crypto12.createHash)("sha256").update(verifier).digest("base64url");
|
|
25013
25762
|
return { verifier, challenge };
|
|
25014
25763
|
}
|
|
25015
25764
|
function linearOAuthAuthorizeUrl(clientId, state, codeChallenge) {
|
|
@@ -25033,7 +25782,7 @@ function linearAuthorizationHeader(token) {
|
|
|
25033
25782
|
if (t.startsWith("lin_api_")) return t;
|
|
25034
25783
|
return `Bearer ${t}`;
|
|
25035
25784
|
}
|
|
25036
|
-
function
|
|
25785
|
+
function htmlPage2(title, body) {
|
|
25037
25786
|
return `<!doctype html><html><head><meta charset="utf-8"><title>${title}</title>
|
|
25038
25787
|
<style>body{font-family:ui-sans-serif,system-ui,sans-serif;padding:48px 24px;max-width:36rem;margin:0 auto;color:#1a1a1a}
|
|
25039
25788
|
h1{font-size:1.25rem}p{line-height:1.5;color:#444}</style></head><body>${body}</body></html>`;
|
|
@@ -25079,7 +25828,7 @@ async function fetchLinearViewer(accessToken) {
|
|
|
25079
25828
|
organizationName: viewer?.organization?.name?.trim() || void 0
|
|
25080
25829
|
};
|
|
25081
25830
|
}
|
|
25082
|
-
var
|
|
25831
|
+
var refreshInFlight2 = null;
|
|
25083
25832
|
async function refreshLinearAccessToken(refreshToken) {
|
|
25084
25833
|
const { clientId, clientSecret } = linearOAuthCredentials();
|
|
25085
25834
|
const body = new URLSearchParams({
|
|
@@ -25106,16 +25855,16 @@ async function getLinearAuthToken(settings = loadAppSettings()) {
|
|
|
25106
25855
|
const refresh = settings.integrations.linearRefreshToken?.trim() || "";
|
|
25107
25856
|
const expiresAt = settings.integrations.linearTokenExpiresAt ?? 0;
|
|
25108
25857
|
const apiKey = settings.integrations.linearApiKey?.trim() || "";
|
|
25109
|
-
if (access && (!refresh || Date.now() < expiresAt -
|
|
25858
|
+
if (access && (!refresh || Date.now() < expiresAt - REFRESH_SKEW_MS3)) {
|
|
25110
25859
|
return access;
|
|
25111
25860
|
}
|
|
25112
25861
|
if (refresh) {
|
|
25113
|
-
if (!
|
|
25114
|
-
|
|
25115
|
-
|
|
25862
|
+
if (!refreshInFlight2) {
|
|
25863
|
+
refreshInFlight2 = refreshLinearAccessToken(refresh).finally(() => {
|
|
25864
|
+
refreshInFlight2 = null;
|
|
25116
25865
|
});
|
|
25117
25866
|
}
|
|
25118
|
-
return
|
|
25867
|
+
return refreshInFlight2.catch((err) => {
|
|
25119
25868
|
if (apiKey) return apiKey;
|
|
25120
25869
|
throw err;
|
|
25121
25870
|
});
|
|
@@ -25140,7 +25889,7 @@ async function startLinearOAuth(opts) {
|
|
|
25140
25889
|
throw new LinearOAuthCancelledError();
|
|
25141
25890
|
}
|
|
25142
25891
|
const { clientId, clientSecret } = linearOAuthCredentials();
|
|
25143
|
-
const state = (0,
|
|
25892
|
+
const state = (0, import_node_crypto12.randomBytes)(16).toString("hex");
|
|
25144
25893
|
const pkce = createLinearPkce();
|
|
25145
25894
|
const authorizeUrl = linearOAuthAuthorizeUrl(clientId, state, pkce.challenge);
|
|
25146
25895
|
const timeoutMs = opts?.timeoutMs ?? 5 * 6e4;
|
|
@@ -25153,7 +25902,7 @@ async function startLinearOAuth(opts) {
|
|
|
25153
25902
|
if (err) reject(err);
|
|
25154
25903
|
else resolve(value);
|
|
25155
25904
|
};
|
|
25156
|
-
const server = (0,
|
|
25905
|
+
const server = (0, import_node_http2.createServer)((req, res) => {
|
|
25157
25906
|
try {
|
|
25158
25907
|
const url = new URL(req.url || "/", `http://127.0.0.1:${LINEAR_OAUTH_PORT}`);
|
|
25159
25908
|
if (url.pathname !== "/callback") {
|
|
@@ -25166,19 +25915,19 @@ async function startLinearOAuth(opts) {
|
|
|
25166
25915
|
const gotCode = url.searchParams.get("code");
|
|
25167
25916
|
if (err) {
|
|
25168
25917
|
res.writeHead(400, { "Content-Type": "text/html" });
|
|
25169
|
-
res.end(
|
|
25918
|
+
res.end(htmlPage2("Linear", `<h1>Authorization cancelled</h1><p>${err}</p>`));
|
|
25170
25919
|
finish(new LinearOAuthCancelledError(`Linear OAuth: ${err}`));
|
|
25171
25920
|
return;
|
|
25172
25921
|
}
|
|
25173
25922
|
if (gotState !== state || !gotCode) {
|
|
25174
25923
|
res.writeHead(400, { "Content-Type": "text/html" });
|
|
25175
|
-
res.end(
|
|
25924
|
+
res.end(htmlPage2("Linear", "<h1>Invalid callback</h1><p>State or code missing.</p>"));
|
|
25176
25925
|
finish(new Error("Linear OAuth callback was invalid"));
|
|
25177
25926
|
return;
|
|
25178
25927
|
}
|
|
25179
25928
|
res.writeHead(200, { "Content-Type": "text/html" });
|
|
25180
25929
|
res.end(
|
|
25181
|
-
|
|
25930
|
+
htmlPage2(
|
|
25182
25931
|
"Linear connected",
|
|
25183
25932
|
"<h1>Linear connected</h1><p>You can close this tab and return to Sideboard.</p>"
|
|
25184
25933
|
)
|
|
@@ -26591,6 +27340,7 @@ async function createGitHubIssue(input, opts) {
|
|
|
26591
27340
|
// src/index.ts
|
|
26592
27341
|
init_abletime();
|
|
26593
27342
|
init_abletime_mcp();
|
|
27343
|
+
init_abletime_oauth();
|
|
26594
27344
|
|
|
26595
27345
|
// src/integrations/issues.ts
|
|
26596
27346
|
init_worktree();
|
|
@@ -26993,7 +27743,7 @@ function buildPathRefAttachment(input) {
|
|
|
26993
27743
|
init_stage_files();
|
|
26994
27744
|
|
|
26995
27745
|
// src/composer/pasted-text.ts
|
|
26996
|
-
var
|
|
27746
|
+
var import_node_crypto13 = require("crypto");
|
|
26997
27747
|
var PASTE_ATTACH_MIN_CHARS = 1200;
|
|
26998
27748
|
var PASTE_ATTACH_MIN_LINES = 15;
|
|
26999
27749
|
var PASTED_NAME_RE = /^Pasted text #(\d+)\.txt$/i;
|
|
@@ -27020,7 +27770,7 @@ function nextPastedTextName(existing) {
|
|
|
27020
27770
|
}
|
|
27021
27771
|
function buildPastedTextAttachment(text7, opts) {
|
|
27022
27772
|
return {
|
|
27023
|
-
id: opts?.id ?? (0,
|
|
27773
|
+
id: opts?.id ?? (0, import_node_crypto13.randomUUID)(),
|
|
27024
27774
|
name: opts?.name ?? "Pasted text #1.txt",
|
|
27025
27775
|
kind: "file",
|
|
27026
27776
|
path: opts?.path,
|
|
@@ -27051,7 +27801,7 @@ function planQuestionsSignature(questions) {
|
|
|
27051
27801
|
return `${q.question}${opts}`;
|
|
27052
27802
|
}).join("");
|
|
27053
27803
|
}
|
|
27054
|
-
function
|
|
27804
|
+
function asRecord10(v) {
|
|
27055
27805
|
return v != null && typeof v === "object" && !Array.isArray(v) ? v : null;
|
|
27056
27806
|
}
|
|
27057
27807
|
function parseOptions(raw) {
|
|
@@ -27062,7 +27812,7 @@ function parseOptions(raw) {
|
|
|
27062
27812
|
out.push({ label: item.trim() });
|
|
27063
27813
|
continue;
|
|
27064
27814
|
}
|
|
27065
|
-
const o =
|
|
27815
|
+
const o = asRecord10(item);
|
|
27066
27816
|
if (!o) continue;
|
|
27067
27817
|
const label = typeof o.label === "string" ? o.label.trim() : typeof o.text === "string" ? o.text.trim() : typeof o.title === "string" ? o.title.trim() : "";
|
|
27068
27818
|
if (!label) continue;
|
|
@@ -27072,7 +27822,7 @@ function parseOptions(raw) {
|
|
|
27072
27822
|
return out;
|
|
27073
27823
|
}
|
|
27074
27824
|
function parseOneQuestion(raw) {
|
|
27075
|
-
const o =
|
|
27825
|
+
const o = asRecord10(raw);
|
|
27076
27826
|
if (!o) return null;
|
|
27077
27827
|
const question = typeof o.question === "string" ? o.question.trim() : typeof o.text === "string" ? o.text.trim() : typeof o.prompt === "string" ? o.prompt.trim() : "";
|
|
27078
27828
|
if (!question) return null;
|
|
@@ -27083,7 +27833,7 @@ function parseOneQuestion(raw) {
|
|
|
27083
27833
|
return { question, header, multiSelect, options };
|
|
27084
27834
|
}
|
|
27085
27835
|
function parsePlanQuestionsInput(input) {
|
|
27086
|
-
const root =
|
|
27836
|
+
const root = asRecord10(input);
|
|
27087
27837
|
if (!root) return [];
|
|
27088
27838
|
const list = Array.isArray(root.questions) ? root.questions : Array.isArray(root.question) ? root.question : root.question || root.prompt ? [root] : [];
|
|
27089
27839
|
const out = [];
|
|
@@ -27174,7 +27924,7 @@ init_orphan_cleanup();
|
|
|
27174
27924
|
var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
27175
27925
|
var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
27176
27926
|
var import_zod9 = require("zod");
|
|
27177
|
-
var
|
|
27927
|
+
var import_node_crypto15 = require("crypto");
|
|
27178
27928
|
var import_node_path53 = require("path");
|
|
27179
27929
|
init_orchestrator();
|
|
27180
27930
|
init_worktree();
|
|
@@ -28901,7 +29651,7 @@ init_worktree();
|
|
|
28901
29651
|
init_paths();
|
|
28902
29652
|
|
|
28903
29653
|
// src/board/board-pins.ts
|
|
28904
|
-
var
|
|
29654
|
+
var import_node_crypto14 = require("crypto");
|
|
28905
29655
|
var import_node_fs55 = require("fs");
|
|
28906
29656
|
var import_node_path51 = require("path");
|
|
28907
29657
|
init_paths();
|
|
@@ -28933,7 +29683,7 @@ function addBoardPin(input) {
|
|
|
28933
29683
|
const ref = input.ref.trim();
|
|
28934
29684
|
if (!ref) throw new Error("Board item needs a ref");
|
|
28935
29685
|
const pin = {
|
|
28936
|
-
id: (0,
|
|
29686
|
+
id: (0, import_node_crypto14.randomUUID)(),
|
|
28937
29687
|
kind: input.kind,
|
|
28938
29688
|
ref,
|
|
28939
29689
|
repoPath: input.repoPath.trim(),
|
|
@@ -29767,7 +30517,7 @@ async function startMcpServer() {
|
|
|
29767
30517
|
const ident = issue?.identifier ?? ref.trim();
|
|
29768
30518
|
const attachments = issue ? [
|
|
29769
30519
|
{
|
|
29770
|
-
id: (0,
|
|
30520
|
+
id: (0, import_node_crypto15.randomUUID)(),
|
|
29771
30521
|
name: issue.identifier,
|
|
29772
30522
|
kind: "issue",
|
|
29773
30523
|
content: [
|
|
@@ -30722,7 +31472,7 @@ init_orch_mcp_isolation();
|
|
|
30722
31472
|
init_workspaces2();
|
|
30723
31473
|
|
|
30724
31474
|
// src/slack/oauth.ts
|
|
30725
|
-
var
|
|
31475
|
+
var import_node_crypto16 = require("crypto");
|
|
30726
31476
|
init_app_settings();
|
|
30727
31477
|
init_workspaces2();
|
|
30728
31478
|
|
|
@@ -30946,7 +31696,7 @@ async function startSlackOAuth(opts) {
|
|
|
30946
31696
|
throw new SlackOAuthCancelledError();
|
|
30947
31697
|
}
|
|
30948
31698
|
const { clientId } = slackOAuthCredentials();
|
|
30949
|
-
const state = (0,
|
|
31699
|
+
const state = (0, import_node_crypto16.randomBytes)(16).toString("hex");
|
|
30950
31700
|
const authorizeUrl = slackOAuthAuthorizeUrl(clientId, state);
|
|
30951
31701
|
const timeoutMs = opts?.timeoutMs ?? 5 * 6e4;
|
|
30952
31702
|
const pollIntervalMs = opts?.pollIntervalMs ?? 400;
|
|
@@ -31228,7 +31978,7 @@ init_workspaces2();
|
|
|
31228
31978
|
init_reply_target();
|
|
31229
31979
|
|
|
31230
31980
|
// src/slack/relay-protocol.ts
|
|
31231
|
-
var
|
|
31981
|
+
var import_node_crypto17 = require("crypto");
|
|
31232
31982
|
var SLACK_RELAY_PING_INTERVAL_MS = 2e4;
|
|
31233
31983
|
var SLACK_RELAY_PONG_TIMEOUT_MS = 15e3;
|
|
31234
31984
|
function slackRelaySessionKey(teamId, userId, deviceId) {
|
|
@@ -31238,7 +31988,7 @@ function slackRelayUserKey(teamId, userId) {
|
|
|
31238
31988
|
return `${teamId.trim()}:${userId.trim()}`;
|
|
31239
31989
|
}
|
|
31240
31990
|
function newSlackRelayEventId() {
|
|
31241
|
-
return (0,
|
|
31991
|
+
return (0, import_node_crypto17.randomBytes)(12).toString("hex");
|
|
31242
31992
|
}
|
|
31243
31993
|
function parseSlackRelayClientMessage(raw) {
|
|
31244
31994
|
try {
|
|
@@ -31311,7 +32061,7 @@ function parseSlackRelayServerMessage(raw) {
|
|
|
31311
32061
|
|
|
31312
32062
|
// src/slack/public-dns.ts
|
|
31313
32063
|
var import_node_dns = require("dns");
|
|
31314
|
-
var
|
|
32064
|
+
var import_node_http3 = require("http");
|
|
31315
32065
|
var import_node_https = require("https");
|
|
31316
32066
|
var PUBLIC_DNS = ["1.1.1.1", "8.8.8.8"];
|
|
31317
32067
|
function finishLookup(options, callback, address, family) {
|
|
@@ -32355,7 +33105,7 @@ var SlackRelayHub = class {
|
|
|
32355
33105
|
};
|
|
32356
33106
|
|
|
32357
33107
|
// src/slack/relay-server.ts
|
|
32358
|
-
var
|
|
33108
|
+
var import_node_http4 = require("http");
|
|
32359
33109
|
var import_ws3 = require("ws");
|
|
32360
33110
|
|
|
32361
33111
|
// src/slack/relay-static.ts
|
|
@@ -32527,7 +33277,7 @@ async function startSlackRelayServer(opts) {
|
|
|
32527
33277
|
sendJson(res, 200, { ok: true, ...value.payload });
|
|
32528
33278
|
return true;
|
|
32529
33279
|
};
|
|
32530
|
-
const httpServer = (0,
|
|
33280
|
+
const httpServer = (0, import_node_http4.createServer)((req, res) => {
|
|
32531
33281
|
void (async () => {
|
|
32532
33282
|
const reqUrl = req.url || "/";
|
|
32533
33283
|
if (await handleCallback(reqUrl, res)) return;
|
|
@@ -32664,10 +33414,18 @@ init_outbound_watch();
|
|
|
32664
33414
|
0 && (module.exports = {
|
|
32665
33415
|
ABLETIME_MCP_PATH,
|
|
32666
33416
|
ABLETIME_MCP_PM_PATH,
|
|
33417
|
+
ABLETIME_OAUTH_CANCELLED,
|
|
33418
|
+
ABLETIME_OAUTH_PORT,
|
|
33419
|
+
ABLETIME_OAUTH_REDIRECT,
|
|
33420
|
+
ABLETIME_OAUTH_RESOURCE,
|
|
33421
|
+
ABLETIME_ORG_KEY_PREFIX,
|
|
33422
|
+
ABLETIME_PAT_PREFIX,
|
|
32667
33423
|
ABLETIME_PM_TOOL_NAMES,
|
|
32668
33424
|
AGENT_GIT_ACTIONS,
|
|
32669
33425
|
AGENT_RUNNER_MAX_OLD_SPACE_MB,
|
|
32670
33426
|
ATTACHMENTS_DIR,
|
|
33427
|
+
AbleTimeOAuthCancelledError,
|
|
33428
|
+
BAKED_ABLETIME_OAUTH_CLIENT_ID,
|
|
32671
33429
|
BAKED_SLACK_RELAY_URL,
|
|
32672
33430
|
BRIGHTSY_MCP_ALLOWED_TOOLS,
|
|
32673
33431
|
BRIGHTSY_SUMMARIZE_CONTEXT_TOOL,
|
|
@@ -32756,7 +33514,11 @@ init_outbound_watch();
|
|
|
32756
33514
|
WORKTREE_GITHUB_MCP_TOOLS,
|
|
32757
33515
|
WORKTREE_LINEAR_MCP_TOOLS,
|
|
32758
33516
|
WORKTREE_MCP_TOOLS,
|
|
33517
|
+
ableTimeCredentialKind,
|
|
32759
33518
|
ableTimeDependencyTarget,
|
|
33519
|
+
ableTimeOAuthAuthorizeUrl,
|
|
33520
|
+
ableTimeOAuthClientId,
|
|
33521
|
+
ableTimeOAuthResource,
|
|
32760
33522
|
abletimeMcpRequest,
|
|
32761
33523
|
abletimeMcpUrl,
|
|
32762
33524
|
abletimeToolUsesPm,
|
|
@@ -32787,6 +33549,8 @@ init_outbound_watch();
|
|
|
32787
33549
|
applyThreadIntoMain,
|
|
32788
33550
|
applyTurnUsage,
|
|
32789
33551
|
armSchedules,
|
|
33552
|
+
assertAbleTimeCredential,
|
|
33553
|
+
assertAbleTimeMcpCredential,
|
|
32790
33554
|
assertOrchestratorCapableAgent,
|
|
32791
33555
|
attachmentFromAbsolutePath,
|
|
32792
33556
|
attachmentsFromBuffers,
|
|
@@ -32866,6 +33630,7 @@ init_outbound_watch();
|
|
|
32866
33630
|
countCacheControlBlocks,
|
|
32867
33631
|
countUnpushedVsOrigin,
|
|
32868
33632
|
cowboyModeEnabled,
|
|
33633
|
+
createAbleTimePkce,
|
|
32869
33634
|
createAbleTimeTask,
|
|
32870
33635
|
createChatTab,
|
|
32871
33636
|
createEmptyThread,
|
|
@@ -32905,6 +33670,7 @@ init_outbound_watch();
|
|
|
32905
33670
|
encodeBrightsyTarget,
|
|
32906
33671
|
enrichPathWithNpmGlobalBin,
|
|
32907
33672
|
enrichWorkspacesWithGithub,
|
|
33673
|
+
ensureAbleTimeAccessToken,
|
|
32908
33674
|
ensureAbleTimeTask,
|
|
32909
33675
|
ensureAgentPath,
|
|
32910
33676
|
ensureBrightsyLocalConfigFresh,
|
|
@@ -33051,6 +33817,7 @@ init_outbound_watch();
|
|
|
33051
33817
|
groupHomeBoardWorktrees,
|
|
33052
33818
|
handleSlackInbound,
|
|
33053
33819
|
harnessEnvKey,
|
|
33820
|
+
hasBakedAbleTimeOAuth,
|
|
33054
33821
|
hasBakedLinearOAuth,
|
|
33055
33822
|
hasBakedSlackOAuth,
|
|
33056
33823
|
hasConductorHook,
|
|
@@ -33075,6 +33842,7 @@ init_outbound_watch();
|
|
|
33075
33842
|
invalidateThreadListCache,
|
|
33076
33843
|
invalidateThreadRecord,
|
|
33077
33844
|
isAbleTimeConnected,
|
|
33845
|
+
isAbleTimeOAuthCancelled,
|
|
33078
33846
|
isAskUserToolName,
|
|
33079
33847
|
isBrightsyConnected,
|
|
33080
33848
|
isBrightsyNdjsonLine,
|
|
@@ -33204,6 +33972,7 @@ init_outbound_watch();
|
|
|
33204
33972
|
nextPastedTextName,
|
|
33205
33973
|
nextThinkingEffort,
|
|
33206
33974
|
nonInteractiveGitProcessEnv,
|
|
33975
|
+
normalizeAbleTimeCredential,
|
|
33207
33976
|
normalizeAbleTimeHost,
|
|
33208
33977
|
normalizeCodeSelection,
|
|
33209
33978
|
normalizeLinearRelationType,
|
|
@@ -33385,6 +34154,7 @@ init_outbound_watch();
|
|
|
33385
34154
|
stackMergeReadiness,
|
|
33386
34155
|
stageAbsolutePathsAsAttachments,
|
|
33387
34156
|
stageBuffersAsAttachments,
|
|
34157
|
+
startAbleTimeOAuth,
|
|
33388
34158
|
startDevServer,
|
|
33389
34159
|
startLinearOAuth,
|
|
33390
34160
|
startMcpServer,
|