@sideboard-ai/core 0.1.202 → 0.1.204

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/{abletime-KGPD3UUJ.js → abletime-S3UHQZVW.js} +4 -2
  2. package/dist/{abletime-345SE3B4.js → abletime-TONBQP4O.js} +4 -2
  3. package/dist/abletime-oauth-AVCEO2DY.js +314 -0
  4. package/dist/abletime-oauth-SD4SZY5U.js +52 -0
  5. package/dist/abletime-rest-6ODSXV7G.js +25 -0
  6. package/dist/abletime-rest-J2OBB4VA.js +24 -0
  7. package/dist/agents/cursor-runner.js +1 -1
  8. package/dist/{agents-573ZDIX6.js → agents-67K4BQFJ.js} +4 -4
  9. package/dist/{agents-OXX3ONVY.js → agents-BVOJNHV6.js} +5 -5
  10. package/dist/{app-settings-V6IHESEQ.js → app-settings-LGHMNHSP.js} +1 -1
  11. package/dist/{app-settings-AXCURTZJ.js → app-settings-YEPF46IJ.js} +1 -1
  12. package/dist/chunk-3K257PCS.js +393 -0
  13. package/dist/{chunk-54RSAMBH.js → chunk-5SMS6QTO.js} +2 -2
  14. package/dist/chunk-6Q7DFVDQ.js +309 -0
  15. package/dist/chunk-CYLDJWRK.js +391 -0
  16. package/dist/{chunk-PGQGP4FD.js → chunk-FO2NTJ7L.js} +37 -219
  17. package/dist/{chunk-7NKY2OX7.js → chunk-FO45DMJ5.js} +125 -109
  18. package/dist/{chunk-CQ5R3VPP.js → chunk-KZ7PI7FE.js} +18 -0
  19. package/dist/{chunk-7AHAABKN.js → chunk-QAVWH2RQ.js} +37 -229
  20. package/dist/{chunk-CXFW5LIH.js → chunk-QFBBUFD4.js} +38 -3
  21. package/dist/{chunk-RPX6VDW7.js → chunk-QUW5D62C.js} +34 -20
  22. package/dist/chunk-SGUVAK66.js +262 -0
  23. package/dist/{chunk-27GGXX27.js → chunk-ULZLWQBS.js} +128 -112
  24. package/dist/{chunk-LJHC62LZ.js → chunk-VIPA75ZA.js} +2 -2
  25. package/dist/chunk-W6VM36YO.js +272 -0
  26. package/dist/{chunk-NIVF5NDN.js → chunk-WF33LOP4.js} +44 -6
  27. package/dist/{chunk-STPZQR5B.js → chunk-WVATAIF4.js} +34 -20
  28. package/dist/{chunk-ISV5IFH3.js → chunk-X743YN5Y.js} +38 -3
  29. package/dist/{chunk-A2TAKYSG.js → chunk-YOSTWSNC.js} +27 -7
  30. package/dist/{coordinator-prompt-BH4EHFOO.js → coordinator-prompt-NPT555BO.js} +3 -3
  31. package/dist/{coordinator-prompt-TY5FHAXC.js → coordinator-prompt-WEPXXLLX.js} +3 -3
  32. package/dist/{global-workspace-NLLAPQJI.js → global-workspace-2I2T7FL5.js} +3 -3
  33. package/dist/{global-workspace-5AZEEQRU.js → global-workspace-NSILADOM.js} +3 -3
  34. package/dist/index.cjs +1137 -213
  35. package/dist/index.d.cts +87 -13
  36. package/dist/index.d.ts +87 -13
  37. package/dist/index.js +94 -19
  38. package/dist/mcp/run-stdio.cjs +1023 -180
  39. package/dist/mcp/run-stdio.js +22 -20
  40. package/dist/{orchestrator-JNSQDRZI.js → orchestrator-CMAVNELS.js} +6 -6
  41. package/dist/{orchestrator-NCYKEI3E.js → orchestrator-I7GFXLHF.js} +6 -6
  42. package/dist/{workspaces-RP76JH3J.js → workspaces-BOC6M7BW.js} +3 -3
  43. package/dist/{workspaces-PEWYWVHZ.js → workspaces-RFGZBUAR.js} +3 -3
  44. package/dist/{worktree-72TZWUSH.js → worktree-G3ZLM4DC.js} +4 -2
  45. package/dist/{worktree-ZQEUKSNE.js → worktree-WSUQMMK4.js} +4 -2
  46. package/package.json +1 -1
@@ -1792,6 +1792,9 @@ function humanizeAgentFailDetail(detail) {
1792
1792
  if (/\[resource_exhausted\]|resource_exhausted|findfileswithripgrep/.test(lower)) {
1793
1793
  return "Cursor local file search failed (ripgrep / resource_exhausted). Wait a minute and retry; if it keeps happening, check Cursor usage.";
1794
1794
  }
1795
+ if (looksLikeCodexMcpError(raw)) {
1796
+ return `${raw} \u2014 skip that vendor MCP and continue (PostHog: Settings \u2192 Connectors + HTTP API).`;
1797
+ }
1795
1798
  if (/invalid user api key|invalid api key|not logged in|not authenticated|unauthorized|authentication|please run.*login|codex login|claude auth|cursor api/.test(
1796
1799
  lower
1797
1800
  )) {
@@ -1849,9 +1852,24 @@ function turnFailChatText(opts) {
1849
1852
 
1850
1853
  ${fail6}`;
1851
1854
  }
1855
+ function looksLikeCodexMcpError(text7) {
1856
+ const t = text7.trim().toLowerCase();
1857
+ if (!t) return false;
1858
+ if (/authrequired|authorization required/.test(t) && /mcp|posthog|rmcp|connector|plugin/.test(t)) {
1859
+ return true;
1860
+ }
1861
+ if (/\bposthog\b/.test(t) && /mcp|oauth|auth|unauthorized|401|plugin|app/.test(t)) {
1862
+ return true;
1863
+ }
1864
+ if (/\brmcp\b/.test(t) && /fatal|quit|transport|error|died|kill|auth/.test(t)) {
1865
+ return true;
1866
+ }
1867
+ return /mcp[_ -]?(server|tool|error|transport|client)/.test(t) && /fail|error|timeout|disconnect|auth|unauthorized/.test(t);
1868
+ }
1852
1869
  function shouldFeedErrorBackToAgent(opts) {
1853
1870
  const detail = opts.detail.trim();
1854
1871
  const chat = (opts.assistantText ?? "").trim();
1872
+ if (looksLikeCodexMcpError(detail)) return true;
1855
1873
  if (looksLikeAgentFailureMessage(detail) || looksLikeAgentFailureMessage(chat)) {
1856
1874
  return false;
1857
1875
  }
@@ -3424,6 +3442,9 @@ function normalizeVault(raw) {
3424
3442
  if (typeof raw.abletimeAccessToken === "string" && raw.abletimeAccessToken.trim()) {
3425
3443
  out.abletimeAccessToken = raw.abletimeAccessToken.trim();
3426
3444
  }
3445
+ if (typeof raw.abletimeRefreshToken === "string" && raw.abletimeRefreshToken.trim()) {
3446
+ out.abletimeRefreshToken = raw.abletimeRefreshToken.trim();
3447
+ }
3427
3448
  if (typeof raw.vercelToken === "string" && raw.vercelToken.trim()) {
3428
3449
  out.vercelToken = raw.vercelToken.trim();
3429
3450
  }
@@ -3629,6 +3650,7 @@ function emptyPublicIntegrations() {
3629
3650
  hasSlackAppToken: false,
3630
3651
  hasGithubPat: false,
3631
3652
  hasAbleTimeToken: false,
3653
+ hasAbleTimeOAuth: false,
3632
3654
  hasVercelToken: false,
3633
3655
  hasSupabaseToken: false,
3634
3656
  hasPosthogToken: false,
@@ -3653,6 +3675,7 @@ function toPublicAppSettings(settings) {
3653
3675
  const slackAppToken = integrations.slackAppToken;
3654
3676
  const githubPat = integrations.githubPat;
3655
3677
  const abletimeAccessToken = integrations.abletimeAccessToken;
3678
+ const abletimeRefreshToken = integrations.abletimeRefreshToken;
3656
3679
  const vercelToken = integrations.vercelToken;
3657
3680
  const supabaseAccessToken = integrations.supabaseAccessToken;
3658
3681
  const posthogPersonalApiKey = integrations.posthogPersonalApiKey;
@@ -3665,6 +3688,7 @@ function toPublicAppSettings(settings) {
3665
3688
  delete integrations.slackAppToken;
3666
3689
  delete integrations.githubPat;
3667
3690
  delete integrations.abletimeAccessToken;
3691
+ delete integrations.abletimeRefreshToken;
3668
3692
  delete integrations.vercelToken;
3669
3693
  delete integrations.supabaseAccessToken;
3670
3694
  delete integrations.posthogPersonalApiKey;
@@ -3679,7 +3703,8 @@ function toPublicAppSettings(settings) {
3679
3703
  hasSlackClientSecret: Boolean(slackClientSecret?.trim()),
3680
3704
  hasSlackAppToken: Boolean(slackAppToken?.trim()),
3681
3705
  hasGithubPat: Boolean(githubPat?.trim()),
3682
- hasAbleTimeToken: Boolean(abletimeAccessToken?.trim()),
3706
+ hasAbleTimeToken: Boolean(abletimeAccessToken?.trim() || abletimeRefreshToken?.trim()),
3707
+ hasAbleTimeOAuth: Boolean(abletimeRefreshToken?.trim()),
3683
3708
  hasVercelToken: Boolean(vercelToken?.trim()),
3684
3709
  hasSupabaseToken: Boolean(supabaseAccessToken?.trim()),
3685
3710
  hasPosthogToken: Boolean(posthogPersonalApiKey?.trim()),
@@ -3817,6 +3842,16 @@ function normalizeIntegrations(raw) {
3817
3842
  const token = source.abletimeAccessToken.trim();
3818
3843
  if (token) out.abletimeAccessToken = token;
3819
3844
  }
3845
+ if (typeof source.abletimeRefreshToken === "string") {
3846
+ const token = source.abletimeRefreshToken.trim();
3847
+ if (token) out.abletimeRefreshToken = token;
3848
+ }
3849
+ if (typeof source.abletimeTokenExpiresAt === "number" && Number.isFinite(source.abletimeTokenExpiresAt)) {
3850
+ out.abletimeTokenExpiresAt = source.abletimeTokenExpiresAt;
3851
+ } else if (typeof source.abletimeTokenExpiresAt === "string") {
3852
+ const n = Number(source.abletimeTokenExpiresAt);
3853
+ if (Number.isFinite(n)) out.abletimeTokenExpiresAt = n;
3854
+ }
3820
3855
  if (typeof source.abletimeHost === "string") {
3821
3856
  const host = source.abletimeHost.trim();
3822
3857
  if (host) out.abletimeHost = host;
@@ -4094,7 +4129,7 @@ function readSettingsFile() {
4094
4129
  }
4095
4130
  function diskHoldsSecrets(disk) {
4096
4131
  return Boolean(
4097
- 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
4132
+ 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
4098
4133
  );
4099
4134
  }
4100
4135
  function mergeVault(disk) {
@@ -4125,6 +4160,9 @@ function mergeVault(disk) {
4125
4160
  if (vault.abletimeAccessToken && !integrations.abletimeAccessToken) {
4126
4161
  integrations.abletimeAccessToken = vault.abletimeAccessToken;
4127
4162
  }
4163
+ if (vault.abletimeRefreshToken && !integrations.abletimeRefreshToken) {
4164
+ integrations.abletimeRefreshToken = vault.abletimeRefreshToken;
4165
+ }
4128
4166
  if (vault.vercelToken && !integrations.vercelToken) {
4129
4167
  integrations.vercelToken = vault.vercelToken;
4130
4168
  }
@@ -4149,6 +4187,7 @@ function persistSplit(settings) {
4149
4187
  const slackAppToken = integrations.slackAppToken;
4150
4188
  const githubPat = integrations.githubPat;
4151
4189
  const abletimeAccessToken = integrations.abletimeAccessToken;
4190
+ const abletimeRefreshToken = integrations.abletimeRefreshToken;
4152
4191
  const vercelToken = integrations.vercelToken;
4153
4192
  const supabaseAccessToken = integrations.supabaseAccessToken;
4154
4193
  const posthogPersonalApiKey = integrations.posthogPersonalApiKey;
@@ -4161,6 +4200,7 @@ function persistSplit(settings) {
4161
4200
  delete integrations.slackAppToken;
4162
4201
  delete integrations.githubPat;
4163
4202
  delete integrations.abletimeAccessToken;
4203
+ delete integrations.abletimeRefreshToken;
4164
4204
  delete integrations.vercelToken;
4165
4205
  delete integrations.supabaseAccessToken;
4166
4206
  delete integrations.posthogPersonalApiKey;
@@ -4179,6 +4219,7 @@ function persistSplit(settings) {
4179
4219
  slackAppToken,
4180
4220
  githubPat,
4181
4221
  abletimeAccessToken,
4222
+ abletimeRefreshToken,
4182
4223
  vercelToken,
4183
4224
  supabaseAccessToken,
4184
4225
  posthogPersonalApiKey,
@@ -4365,6 +4406,8 @@ function updateIntegrationsSettings(patch) {
4365
4406
  if ("abletimeAccessToken" in patch) {
4366
4407
  if (patch.abletimeAccessToken == null || patch.abletimeAccessToken.trim() === "") {
4367
4408
  delete integrations.abletimeAccessToken;
4409
+ delete integrations.abletimeRefreshToken;
4410
+ delete integrations.abletimeTokenExpiresAt;
4368
4411
  delete integrations.abletimeViewerName;
4369
4412
  } else {
4370
4413
  integrations.abletimeAccessToken = patch.abletimeAccessToken.trim();
@@ -4715,7 +4758,9 @@ function disconnectLinearConnection() {
4715
4758
  return saveAppSettings({ ...current, integrations });
4716
4759
  }
4717
4760
  function isAbleTimeConnected(settings = loadAppSettings()) {
4718
- return Boolean(settings.integrations.abletimeAccessToken?.trim());
4761
+ return Boolean(
4762
+ settings.integrations.abletimeAccessToken?.trim() || settings.integrations.abletimeRefreshToken?.trim()
4763
+ );
4719
4764
  }
4720
4765
  function getAbleTimeAccessToken(settings = loadAppSettings()) {
4721
4766
  const token = settings.integrations.abletimeAccessToken?.trim();
@@ -4728,6 +4773,12 @@ function saveAbleTimeConnection(input) {
4728
4773
  const current = loadAppSettings();
4729
4774
  const integrations = { ...current.integrations };
4730
4775
  integrations.abletimeAccessToken = input.accessToken.trim();
4776
+ if (input.refreshToken?.trim()) {
4777
+ integrations.abletimeRefreshToken = input.refreshToken.trim();
4778
+ }
4779
+ if (typeof input.expiresIn === "number" && Number.isFinite(input.expiresIn)) {
4780
+ integrations.abletimeTokenExpiresAt = Date.now() + Math.max(0, input.expiresIn) * 1e3;
4781
+ }
4731
4782
  if (input.host?.trim()) {
4732
4783
  integrations.abletimeHost = input.host.trim();
4733
4784
  }
@@ -4740,6 +4791,8 @@ function disconnectAbleTimeConnection() {
4740
4791
  const current = loadAppSettings();
4741
4792
  const integrations = { ...current.integrations };
4742
4793
  delete integrations.abletimeAccessToken;
4794
+ delete integrations.abletimeRefreshToken;
4795
+ delete integrations.abletimeTokenExpiresAt;
4743
4796
  delete integrations.abletimeViewerName;
4744
4797
  return saveAppSettings({ ...current, integrations });
4745
4798
  }
@@ -6034,6 +6087,7 @@ __export(worktree_exports, {
6034
6087
  canonicalizeRepoPath: () => canonicalizeRepoPath,
6035
6088
  collectTakenTeamSlugs: () => collectTakenTeamSlugs,
6036
6089
  commitAll: () => commitAll,
6090
+ connectedPrSelectors: () => connectedPrSelectors,
6037
6091
  countUnpushedVsOrigin: () => countUnpushedVsOrigin,
6038
6092
  createExistingBranchWorktree: () => createExistingBranchWorktree,
6039
6093
  createOrUpdatePr: () => createOrUpdatePr,
@@ -6367,26 +6421,21 @@ function resolvePrSelectors(thread) {
6367
6421
  function resolvePrSelector(thread) {
6368
6422
  return resolvePrSelectors(thread)[0] ?? null;
6369
6423
  }
6424
+ function connectedPrSelectors(thread, currentHeadPrUrl) {
6425
+ const out = [];
6426
+ const push = (value) => {
6427
+ const v = value?.trim();
6428
+ if (!v || out.includes(v)) return;
6429
+ out.push(v);
6430
+ };
6431
+ push(currentHeadPrUrl);
6432
+ for (const selector of resolvePrSelectors(thread)) push(selector);
6433
+ return out;
6434
+ }
6370
6435
  async function getPrForHeadBranch(repoPath, branch) {
6371
6436
  const head = normalizeGitBranchName(branch);
6372
6437
  if (!head || isDefaultishSourceRef(head)) return null;
6373
6438
  const slug = await resolveGithubRepoSlug(repoPath);
6374
- const viewArgs = [
6375
- "pr",
6376
- "view",
6377
- head,
6378
- "--json",
6379
- "number,title,headRefName,url,isCrossRepository,author"
6380
- ];
6381
- if (slug) viewArgs.push("--repo", slug);
6382
- const viewed = await gh(viewArgs, repoPath, { reject: false });
6383
- if (viewed.exitCode === 0 && viewed.stdout.trim()) {
6384
- try {
6385
- return JSON.parse(viewed.stdout);
6386
- } catch {
6387
- return null;
6388
- }
6389
- }
6390
6439
  const listHead = slug ? ghHeadRef(slug, head) : head;
6391
6440
  const listArgs = [
6392
6441
  "pr",
@@ -6402,10 +6451,28 @@ async function getPrForHeadBranch(repoPath, branch) {
6402
6451
  ];
6403
6452
  if (slug) listArgs.push("--repo", slug);
6404
6453
  const listed = await gh(listArgs, repoPath, { reject: false });
6405
- if (listed.exitCode !== 0 || !listed.stdout.trim()) return null;
6454
+ if (listed.exitCode === 0 && listed.stdout.trim()) {
6455
+ try {
6456
+ const rows = JSON.parse(listed.stdout);
6457
+ if (rows[0]) return rows[0];
6458
+ } catch {
6459
+ }
6460
+ }
6461
+ const viewArgs = [
6462
+ "pr",
6463
+ "view",
6464
+ head,
6465
+ "--json",
6466
+ "number,title,headRefName,url,isCrossRepository,author,state"
6467
+ ];
6468
+ if (slug) viewArgs.push("--repo", slug);
6469
+ const viewed = await gh(viewArgs, repoPath, { reject: false });
6470
+ if (viewed.exitCode !== 0 || !viewed.stdout.trim()) return null;
6406
6471
  try {
6407
- const rows = JSON.parse(listed.stdout);
6408
- return rows[0] ?? null;
6472
+ const info = JSON.parse(viewed.stdout);
6473
+ const state = (info.state ?? "OPEN").trim().toUpperCase();
6474
+ if (state !== "OPEN") return null;
6475
+ return info;
6409
6476
  } catch {
6410
6477
  return null;
6411
6478
  }
@@ -10988,7 +11055,18 @@ function toCodexDisableUserMcpArgs(names) {
10988
11055
  return args;
10989
11056
  }
10990
11057
  function toCodexUnattendedAppsArgs() {
10991
- return ["-c", "features.apps=false", "-c", "apps._default.enabled=false"];
11058
+ return [
11059
+ "-c",
11060
+ "features.apps=false",
11061
+ "-c",
11062
+ "apps._default.enabled=false",
11063
+ "-c",
11064
+ "apps.posthog.enabled=false",
11065
+ "-c",
11066
+ "features.plugins=false",
11067
+ "-c",
11068
+ "mcp_servers.posthog.enabled=false"
11069
+ ];
10992
11070
  }
10993
11071
  var import_node_fs28, import_node_os11, import_node_path29, import_smol_toml2;
10994
11072
  var init_orch_mcp_isolation = __esm({
@@ -12698,6 +12776,33 @@ var init_agent_git_actions = __esm({
12698
12776
  function normalizePrState(state) {
12699
12777
  return (state ?? "").trim().toUpperCase();
12700
12778
  }
12779
+ function sameLoginList(a, b) {
12780
+ const left = [...a ?? []].map((s) => s.trim().toLowerCase()).filter(Boolean).sort();
12781
+ const right = [...b ?? []].map((s) => s.trim().toLowerCase()).filter(Boolean).sort();
12782
+ return left.length === right.length && left.every((login, i) => login === right[i]);
12783
+ }
12784
+ function threadPrMetaPatch(thread, meta) {
12785
+ const prevState = normalizePrState(thread.prState);
12786
+ const nextState = normalizePrState(meta.state);
12787
+ const patch = {};
12788
+ if (meta.url && meta.url !== thread.prUrl) patch.prUrl = meta.url;
12789
+ if (meta.title && meta.title !== thread.prTitle) patch.prTitle = meta.title;
12790
+ if (nextState && nextState !== prevState) patch.prState = nextState;
12791
+ const nextDraft = Boolean(meta.isDraft) && nextState !== "MERGED" && nextState !== "CLOSED";
12792
+ if (nextDraft !== Boolean(thread.prIsDraft)) patch.prIsDraft = nextDraft;
12793
+ const nextAuthor = meta.authorLogin?.trim() || "";
12794
+ if (nextAuthor && nextAuthor !== (thread.prAuthorLogin ?? "").trim()) {
12795
+ patch.prAuthorLogin = nextAuthor;
12796
+ }
12797
+ const nextReviewers = meta.reviewerLogins ?? [];
12798
+ if (!sameLoginList(nextReviewers, thread.prReviewerLogins)) {
12799
+ patch.prReviewerLogins = nextReviewers;
12800
+ }
12801
+ if (thread.skipAutoArchiveOnMerge && nextState && nextState !== "MERGED" && nextState !== "CLOSED") {
12802
+ patch.skipAutoArchiveOnMerge = false;
12803
+ }
12804
+ return patch;
12805
+ }
12701
12806
  function shouldAutoArchiveOnPrMerge(opts) {
12702
12807
  if (!opts.autoArchiveEnabled) return false;
12703
12808
  if (opts.isGlobal) return false;
@@ -15373,6 +15478,697 @@ var init_home_board = __esm({
15373
15478
  }
15374
15479
  });
15375
15480
 
15481
+ // src/integrations/abletime-oauth.ts
15482
+ var abletime_oauth_exports = {};
15483
+ __export(abletime_oauth_exports, {
15484
+ ABLETIME_OAUTH_CALLBACK_PATH: () => ABLETIME_OAUTH_CALLBACK_PATH,
15485
+ ABLETIME_OAUTH_CANCELLED: () => ABLETIME_OAUTH_CANCELLED,
15486
+ ABLETIME_OAUTH_LOCAL_CALLBACK: () => ABLETIME_OAUTH_LOCAL_CALLBACK,
15487
+ ABLETIME_OAUTH_PORT: () => ABLETIME_OAUTH_PORT,
15488
+ ABLETIME_OAUTH_REDIRECT: () => ABLETIME_OAUTH_REDIRECT,
15489
+ ABLETIME_OAUTH_RESOURCE: () => ABLETIME_OAUTH_RESOURCE,
15490
+ AbleTimeOAuthCancelledError: () => AbleTimeOAuthCancelledError,
15491
+ BAKED_ABLETIME_OAUTH_CLIENT_ID: () => BAKED_ABLETIME_OAUTH_CLIENT_ID,
15492
+ ableTimeOAuthAuthorizeUrl: () => ableTimeOAuthAuthorizeUrl,
15493
+ ableTimeOAuthBouncePage: () => ableTimeOAuthBouncePage,
15494
+ ableTimeOAuthClientId: () => ableTimeOAuthClientId,
15495
+ ableTimeOAuthLocalBounceUrl: () => ableTimeOAuthLocalBounceUrl,
15496
+ ableTimeOAuthRedirectUri: () => ableTimeOAuthRedirectUri,
15497
+ ableTimeOAuthResource: () => ableTimeOAuthResource,
15498
+ createAbleTimePkce: () => createAbleTimePkce,
15499
+ ensureAbleTimeAccessToken: () => ensureAbleTimeAccessToken,
15500
+ hasBakedAbleTimeOAuth: () => hasBakedAbleTimeOAuth,
15501
+ isAbleTimeOAuthCancelled: () => isAbleTimeOAuthCancelled,
15502
+ parseAbleTimeOAuthCallbackUrl: () => parseAbleTimeOAuthCallbackUrl,
15503
+ startAbleTimeOAuth: () => startAbleTimeOAuth
15504
+ });
15505
+ function ableTimeOAuthRedirectUri() {
15506
+ return process.env.SIDEBOARD_ABLETIME_OAUTH_REDIRECT?.trim() || ABLETIME_OAUTH_REDIRECT;
15507
+ }
15508
+ function parseAbleTimeOAuthCallbackUrl(reqUrl) {
15509
+ let url;
15510
+ try {
15511
+ url = new URL(reqUrl, ABLETIME_OAUTH_REDIRECT);
15512
+ } catch {
15513
+ return null;
15514
+ }
15515
+ if (url.pathname !== ABLETIME_OAUTH_CALLBACK_PATH) return null;
15516
+ return url;
15517
+ }
15518
+ function ableTimeOAuthLocalBounceUrl(reqUrl) {
15519
+ const url = parseAbleTimeOAuthCallbackUrl(reqUrl);
15520
+ if (!url) return null;
15521
+ const dest = new URL(ABLETIME_OAUTH_LOCAL_CALLBACK);
15522
+ for (const key of BOUNCE_QUERY_KEYS) {
15523
+ const value = url.searchParams.get(key);
15524
+ if (value) dest.searchParams.set(key, value);
15525
+ }
15526
+ return dest.toString();
15527
+ }
15528
+ function escapeHtml(value) {
15529
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
15530
+ }
15531
+ function ableTimeOAuthBouncePage(localUrl) {
15532
+ const safe = escapeHtml(localUrl);
15533
+ return `<!doctype html><html><head><meta charset="utf-8">
15534
+ <meta http-equiv="refresh" content="0;url=${safe}">
15535
+ <title>Returning to Sideboard</title>
15536
+ <script>location.replace(${JSON.stringify(localUrl)})</script>
15537
+ <style>body{font-family:ui-sans-serif,system-ui,sans-serif;padding:48px 24px;max-width:36rem;margin:0 auto;color:#1a1a1a}
15538
+ h1{font-size:1.25rem}p{line-height:1.5;color:#444}a{color:#0f7ed4}</style>
15539
+ </head><body>
15540
+ <h1>Returning to Sideboard</h1>
15541
+ <p>AbleTime authorized. Opening the app\u2026</p>
15542
+ <p><a href="${safe}">Open Sideboard</a> if it does not appear.</p>
15543
+ </body></html>`;
15544
+ }
15545
+ function hasBakedAbleTimeOAuth() {
15546
+ return Boolean(BAKED_ABLETIME_OAUTH_CLIENT_ID.trim());
15547
+ }
15548
+ function ableTimeOAuthClientId() {
15549
+ return process.env.SIDEBOARD_ABLETIME_OAUTH_CLIENT_ID?.trim() || BAKED_ABLETIME_OAUTH_CLIENT_ID.trim();
15550
+ }
15551
+ function ableTimeOAuthResource(host) {
15552
+ return `${normalizeAbleTimeHost(host)}/api/public/v2/mcp`;
15553
+ }
15554
+ function createAbleTimePkce() {
15555
+ const verifier = (0, import_node_crypto9.randomBytes)(32).toString("base64url");
15556
+ const challenge = (0, import_node_crypto9.createHash)("sha256").update(verifier).digest("base64url");
15557
+ return { verifier, challenge };
15558
+ }
15559
+ function ableTimeOAuthAuthorizeUrl(clientId, state, codeChallenge, host) {
15560
+ const base = normalizeAbleTimeHost(host);
15561
+ const params = new URLSearchParams({
15562
+ response_type: "code",
15563
+ client_id: clientId,
15564
+ redirect_uri: ableTimeOAuthRedirectUri(),
15565
+ code_challenge: codeChallenge,
15566
+ code_challenge_method: "S256",
15567
+ state,
15568
+ resource: ableTimeOAuthResource(host)
15569
+ });
15570
+ return `${base}/oauth/authorize?${params.toString()}`;
15571
+ }
15572
+ function htmlPage(title, body) {
15573
+ return `<!doctype html><html><head><meta charset="utf-8"><title>${title}</title>
15574
+ <style>body{font-family:ui-sans-serif,system-ui,sans-serif;padding:48px 24px;max-width:36rem;margin:0 auto;color:#1a1a1a}
15575
+ h1{font-size:1.25rem}p{line-height:1.5;color:#444}</style></head><body>${body}</body></html>`;
15576
+ }
15577
+ async function exchangeAbleTimeToken(body, host) {
15578
+ const url = `${normalizeAbleTimeHost(host)}/api/oauth/token`;
15579
+ const res = await httpFetch(url, {
15580
+ method: "POST",
15581
+ headers: {
15582
+ Accept: "application/json",
15583
+ "Content-Type": "application/x-www-form-urlencoded"
15584
+ },
15585
+ body
15586
+ });
15587
+ const data = await res.json().catch(() => ({}));
15588
+ if (!res.ok || data.error || !data.access_token?.trim()) {
15589
+ const detail = data.error_description || data.error || `HTTP ${res.status}`;
15590
+ throw new Error(`AbleTime OAuth token exchange failed: ${detail}`);
15591
+ }
15592
+ return data;
15593
+ }
15594
+ function persistAbleTimeTokens(data, input) {
15595
+ return saveAbleTimeConnection({
15596
+ accessToken: data.access_token.trim(),
15597
+ refreshToken: data.refresh_token?.trim(),
15598
+ expiresIn: data.expires_in,
15599
+ host: input?.host,
15600
+ viewerName: input?.viewerName
15601
+ });
15602
+ }
15603
+ async function refreshAbleTimeAccessToken(refreshToken) {
15604
+ const host = getAbleTimeHost();
15605
+ const body = new URLSearchParams({
15606
+ grant_type: "refresh_token",
15607
+ refresh_token: refreshToken,
15608
+ client_id: ableTimeOAuthClientId(),
15609
+ resource: ableTimeOAuthResource(host)
15610
+ });
15611
+ try {
15612
+ const data = await exchangeAbleTimeToken(body, host);
15613
+ persistAbleTimeTokens(data, { host });
15614
+ return data.access_token.trim();
15615
+ } catch (err) {
15616
+ const message = err instanceof Error ? err.message : String(err);
15617
+ if (/invalid_grant|invalid_token|unauthorized/i.test(message)) {
15618
+ disconnectAbleTimeConnection();
15619
+ throw new Error("AbleTime sign-in expired \u2014 connect again in Account settings");
15620
+ }
15621
+ throw err;
15622
+ }
15623
+ }
15624
+ async function ensureAbleTimeAccessToken() {
15625
+ const settings = loadAppSettings();
15626
+ const access = settings.integrations.abletimeAccessToken?.trim() || "";
15627
+ const refresh = settings.integrations.abletimeRefreshToken?.trim() || "";
15628
+ const expiresAt = settings.integrations.abletimeTokenExpiresAt ?? 0;
15629
+ if (access && (!refresh || Date.now() < expiresAt - REFRESH_SKEW_MS2)) {
15630
+ return access;
15631
+ }
15632
+ if (refresh) {
15633
+ if (!refreshInFlight) {
15634
+ refreshInFlight = refreshAbleTimeAccessToken(refresh).finally(() => {
15635
+ refreshInFlight = null;
15636
+ });
15637
+ }
15638
+ return refreshInFlight;
15639
+ }
15640
+ return access || null;
15641
+ }
15642
+ function isAbleTimeOAuthCancelled(err) {
15643
+ if (err instanceof AbleTimeOAuthCancelledError) return true;
15644
+ const message = err instanceof Error ? err.message : String(err);
15645
+ return message.includes(ABLETIME_OAUTH_CANCELLED);
15646
+ }
15647
+ async function startAbleTimeOAuth(opts) {
15648
+ if (opts?.signal?.aborted) {
15649
+ throw new AbleTimeOAuthCancelledError();
15650
+ }
15651
+ const clientId = ableTimeOAuthClientId();
15652
+ if (!clientId) {
15653
+ throw new Error(
15654
+ `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.`
15655
+ );
15656
+ }
15657
+ const host = opts?.host?.trim() || void 0;
15658
+ const state = (0, import_node_crypto9.randomBytes)(16).toString("hex");
15659
+ const pkce = createAbleTimePkce();
15660
+ const authorizeUrl = ableTimeOAuthAuthorizeUrl(clientId, state, pkce.challenge, host);
15661
+ const timeoutMs = opts?.timeoutMs ?? 5 * 6e4;
15662
+ const code = await new Promise((resolve, reject) => {
15663
+ let settled = false;
15664
+ const finish = (err, value) => {
15665
+ if (settled) return;
15666
+ settled = true;
15667
+ cleanup();
15668
+ if (err) reject(err);
15669
+ else resolve(value);
15670
+ };
15671
+ const server = (0, import_node_http.createServer)((req, res) => {
15672
+ try {
15673
+ const url = new URL(req.url || "/", `http://127.0.0.1:${ABLETIME_OAUTH_PORT}`);
15674
+ if (url.pathname !== "/callback") {
15675
+ res.writeHead(404);
15676
+ res.end("Not found");
15677
+ return;
15678
+ }
15679
+ const err = url.searchParams.get("error");
15680
+ const gotState = url.searchParams.get("state");
15681
+ const gotCode = url.searchParams.get("code");
15682
+ if (err) {
15683
+ res.writeHead(400, { "Content-Type": "text/html" });
15684
+ res.end(htmlPage("AbleTime", `<h1>Authorization cancelled</h1><p>${err}</p>`));
15685
+ finish(new AbleTimeOAuthCancelledError(`AbleTime OAuth: ${err}`));
15686
+ return;
15687
+ }
15688
+ if (gotState !== state || !gotCode) {
15689
+ res.writeHead(400, { "Content-Type": "text/html" });
15690
+ res.end(htmlPage("AbleTime", "<h1>Invalid callback</h1><p>State or code missing.</p>"));
15691
+ finish(new Error("AbleTime OAuth callback was invalid"));
15692
+ return;
15693
+ }
15694
+ res.writeHead(200, { "Content-Type": "text/html" });
15695
+ res.end(
15696
+ htmlPage(
15697
+ "AbleTime connected",
15698
+ "<h1>AbleTime connected</h1><p>You can close this tab and return to Sideboard.</p>"
15699
+ )
15700
+ );
15701
+ finish(void 0, gotCode);
15702
+ } catch (e) {
15703
+ finish(e instanceof Error ? e : new Error(String(e)));
15704
+ }
15705
+ });
15706
+ const timer = setTimeout(() => {
15707
+ finish(new Error("AbleTime sign-in timed out \u2014 try again"));
15708
+ }, timeoutMs);
15709
+ const onAbort = () => finish(new AbleTimeOAuthCancelledError());
15710
+ const cleanup = () => {
15711
+ clearTimeout(timer);
15712
+ opts?.signal?.removeEventListener("abort", onAbort);
15713
+ server.close();
15714
+ };
15715
+ opts?.signal?.addEventListener("abort", onAbort, { once: true });
15716
+ if (opts?.signal?.aborted) {
15717
+ onAbort();
15718
+ return;
15719
+ }
15720
+ server.on("error", (e) => {
15721
+ finish(
15722
+ e instanceof Error && e.code === "EADDRINUSE" ? new Error(
15723
+ `Port ${ABLETIME_OAUTH_PORT} is in use. Close whatever is bound there, or paste an AbleTime token instead.`
15724
+ ) : e instanceof Error ? e : new Error(String(e))
15725
+ );
15726
+ });
15727
+ server.listen(ABLETIME_OAUTH_PORT, "127.0.0.1", () => {
15728
+ void Promise.resolve(opts?.openUrl?.(authorizeUrl)).catch((e) => {
15729
+ finish(e instanceof Error ? e : new Error(String(e)));
15730
+ });
15731
+ });
15732
+ });
15733
+ const body = new URLSearchParams({
15734
+ grant_type: "authorization_code",
15735
+ code,
15736
+ redirect_uri: ableTimeOAuthRedirectUri(),
15737
+ client_id: clientId,
15738
+ code_verifier: pkce.verifier,
15739
+ resource: ableTimeOAuthResource(host)
15740
+ });
15741
+ const data = await exchangeAbleTimeToken(body, host);
15742
+ persistAbleTimeTokens(data, { host });
15743
+ const token = data.access_token.trim();
15744
+ try {
15745
+ const { getAbleTimeOrientation: getAbleTimeOrientation2 } = await Promise.resolve().then(() => (init_abletime(), abletime_exports));
15746
+ const orientation = await getAbleTimeOrientation2({ token, host });
15747
+ return saveAbleTimeConnection({
15748
+ accessToken: token,
15749
+ refreshToken: data.refresh_token?.trim(),
15750
+ expiresIn: data.expires_in,
15751
+ host,
15752
+ viewerName: orientation.viewer.name
15753
+ });
15754
+ } catch {
15755
+ return loadAppSettings();
15756
+ }
15757
+ }
15758
+ var import_node_crypto9, import_node_http, ABLETIME_OAUTH_CALLBACK_PATH, ABLETIME_OAUTH_PORT, ABLETIME_OAUTH_LOCAL_CALLBACK, ABLETIME_OAUTH_REDIRECT, BOUNCE_QUERY_KEYS, BAKED_ABLETIME_OAUTH_CLIENT_ID, ABLETIME_OAUTH_RESOURCE, REFRESH_SKEW_MS2, refreshInFlight, ABLETIME_OAUTH_CANCELLED, AbleTimeOAuthCancelledError;
15759
+ var init_abletime_oauth = __esm({
15760
+ "src/integrations/abletime-oauth.ts"() {
15761
+ "use strict";
15762
+ import_node_crypto9 = require("crypto");
15763
+ import_node_http = require("http");
15764
+ init_fetch();
15765
+ init_app_settings();
15766
+ init_abletime_mcp();
15767
+ ABLETIME_OAUTH_CALLBACK_PATH = "/oauth/abletime/callback";
15768
+ ABLETIME_OAUTH_PORT = 19849;
15769
+ ABLETIME_OAUTH_LOCAL_CALLBACK = `http://127.0.0.1:${ABLETIME_OAUTH_PORT}/callback`;
15770
+ ABLETIME_OAUTH_REDIRECT = `https://www.sideboard.cloud${ABLETIME_OAUTH_CALLBACK_PATH}`;
15771
+ BOUNCE_QUERY_KEYS = ["code", "state", "error", "error_description"];
15772
+ BAKED_ABLETIME_OAUTH_CLIENT_ID = "https://www.sideboard.cloud/oauth/abletime-client.json";
15773
+ ABLETIME_OAUTH_RESOURCE = `${DEFAULT_ABLETIME_HOST}/api/public/v2/mcp`;
15774
+ REFRESH_SKEW_MS2 = 5 * 6e4;
15775
+ refreshInFlight = null;
15776
+ ABLETIME_OAUTH_CANCELLED = "AbleTime sign-in cancelled";
15777
+ AbleTimeOAuthCancelledError = class extends Error {
15778
+ constructor(message = ABLETIME_OAUTH_CANCELLED) {
15779
+ super(message);
15780
+ this.name = "AbleTimeOAuthCancelledError";
15781
+ }
15782
+ };
15783
+ }
15784
+ });
15785
+
15786
+ // src/integrations/abletime-rest.ts
15787
+ var abletime_rest_exports = {};
15788
+ __export(abletime_rest_exports, {
15789
+ ABLETIME_REST_PREFIX: () => ABLETIME_REST_PREFIX,
15790
+ ableTimeDocFromText: () => ableTimeDocFromText,
15791
+ abletimeRestUrl: () => abletimeRestUrl,
15792
+ callAbleTimeRestTool: () => callAbleTimeRestTool,
15793
+ looksLikeAbleTimeUlid: () => looksLikeAbleTimeUlid,
15794
+ textFromAbleTimeDoc: () => textFromAbleTimeDoc
15795
+ });
15796
+ function asRecord4(value) {
15797
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
15798
+ }
15799
+ function asString(value) {
15800
+ return typeof value === "string" ? value.trim() : "";
15801
+ }
15802
+ function asList(value) {
15803
+ if (Array.isArray(value)) return value;
15804
+ const record = asRecord4(value);
15805
+ if (!record) return [];
15806
+ for (const key of ["data", "tasks", "projects", "users", "items", "results"]) {
15807
+ if (Array.isArray(record[key])) return record[key];
15808
+ }
15809
+ return [];
15810
+ }
15811
+ function ableTimeDocFromText(text7) {
15812
+ const trimmed = text7.trim();
15813
+ if (!trimmed) return { type: "doc", content: [] };
15814
+ return {
15815
+ type: "doc",
15816
+ content: [{ type: "paragraph", content: [{ type: "text", text: trimmed }] }]
15817
+ };
15818
+ }
15819
+ function textFromAbleTimeDoc(value) {
15820
+ if (typeof value === "string") return value.trim();
15821
+ const record = asRecord4(value);
15822
+ if (!record) return "";
15823
+ if (typeof record.text === "string" && record.text.trim()) return record.text.trim();
15824
+ const chunks = [];
15825
+ const walk = (node) => {
15826
+ const rec = asRecord4(node);
15827
+ if (!rec) return;
15828
+ if (typeof rec.text === "string" && rec.text) chunks.push(rec.text);
15829
+ if (Array.isArray(rec.content)) rec.content.forEach(walk);
15830
+ };
15831
+ walk(record);
15832
+ return chunks.join("").trim();
15833
+ }
15834
+ function abletimeRestUrl(path, host, query) {
15835
+ const base = `${normalizeAbleTimeHost(host)}${ABLETIME_REST_PREFIX}`;
15836
+ const suffix = path.startsWith("/") ? path : `/${path}`;
15837
+ const url = new URL(`${base}${suffix}`);
15838
+ for (const [key, value] of Object.entries(query ?? {})) {
15839
+ if (value) url.searchParams.set(key, value);
15840
+ }
15841
+ return url.href;
15842
+ }
15843
+ function looksLikeAbleTimeUlid(id) {
15844
+ return ULID_RE.test(id.trim());
15845
+ }
15846
+ async function abletimeRestRequest(path, opts = {}) {
15847
+ const raw = opts.token ?? getAbleTimeAccessToken();
15848
+ const token = normalizeAbleTimeCredential(raw ?? "");
15849
+ if (!token) {
15850
+ throw new Error("AbleTime is not connected \u2014 paste an API key or personal access token in Account settings");
15851
+ }
15852
+ const host = opts.host ?? (opts.token ? DEFAULT_ABLETIME_HOST : getAbleTimeHost());
15853
+ const url = abletimeRestUrl(path, host, opts.query);
15854
+ const method = (opts.method ?? "GET").toUpperCase();
15855
+ const res = await httpFetch(url, {
15856
+ method,
15857
+ headers: {
15858
+ Accept: "application/json",
15859
+ Authorization: `Bearer ${token}`,
15860
+ ...opts.body !== void 0 ? { "Content-Type": "application/json" } : {}
15861
+ },
15862
+ ...opts.body !== void 0 ? { body: JSON.stringify(opts.body) } : {}
15863
+ });
15864
+ const text7 = await res.text().catch(() => "");
15865
+ if (!res.ok) {
15866
+ throw new Error(
15867
+ rewriteAbleTimeError(
15868
+ `AbleTime API error ${res.status}${text7 ? `: ${text7.slice(0, 240)}` : ""}`
15869
+ )
15870
+ );
15871
+ }
15872
+ if (!text7.trim()) return null;
15873
+ try {
15874
+ return JSON.parse(text7);
15875
+ } catch {
15876
+ throw new Error(`AbleTime API returned a non-JSON response: ${text7.slice(0, 200)}`);
15877
+ }
15878
+ }
15879
+ async function restPages(path, opts, maxPages = 20) {
15880
+ const items = [];
15881
+ let cursor;
15882
+ for (let i = 0; i < maxPages; i += 1) {
15883
+ const raw = await abletimeRestRequest(path, {
15884
+ ...opts,
15885
+ query: { ...opts.query, ...cursor ? { cursor } : {} }
15886
+ });
15887
+ items.push(...asList(raw));
15888
+ const page = asRecord4(asRecord4(raw)?.page);
15889
+ const next = page ? asString(page.nextCursor) : "";
15890
+ if (!next) break;
15891
+ cursor = next;
15892
+ }
15893
+ return items;
15894
+ }
15895
+ function userDisplayName(raw) {
15896
+ const rec = asRecord4(raw);
15897
+ if (!rec) return null;
15898
+ const id = asString(rec.userId) || asString(rec.id);
15899
+ const name = [asString(rec.firstName), asString(rec.lastName)].filter(Boolean).join(" ") || asString(rec.username) || asString(rec.email) || asString(rec.name);
15900
+ if (!name) return null;
15901
+ return { id: id || void 0, name };
15902
+ }
15903
+ function restTaskToRaw(raw, users) {
15904
+ const rec = asRecord4(raw);
15905
+ if (!rec) return null;
15906
+ const id = asString(rec.timeflowTaskId) || asString(rec.taskId) || asString(rec.id);
15907
+ const identifier = asString(rec.taskRef) || asString(rec.reference) || id;
15908
+ const title = asString(rec.title);
15909
+ if (!id && !identifier && !title) return null;
15910
+ const assigned = asString(rec.assignedUserId);
15911
+ const assignee = assigned ? users.get(assigned) : void 0;
15912
+ const comments = asList(rec.comments).map((item) => {
15913
+ const comment = asRecord4(item) ?? {};
15914
+ const user = userDisplayName(comment);
15915
+ return {
15916
+ id: asString(comment.commentId) || asString(comment.id),
15917
+ body: textFromAbleTimeDoc(comment.content) || asString(comment.contentSanitized) || asString(comment.body),
15918
+ createdAt: asString(comment.createdAt) || asString(comment.lastUpdate),
15919
+ user: user?.name || asString(comment.username)
15920
+ };
15921
+ });
15922
+ return {
15923
+ id,
15924
+ taskId: id,
15925
+ timeflowTaskId: id,
15926
+ reference: identifier,
15927
+ taskRef: identifier,
15928
+ title: title || identifier,
15929
+ state: asString(rec.taskState) || asString(rec.state),
15930
+ description: textFromAbleTimeDoc(rec.description) || asString(rec.descriptionSanitized),
15931
+ projectId: asString(rec.projectId),
15932
+ categoryId: asString(rec.projectCategoryId) || asString(rec.categoryId),
15933
+ tags: rec.tags,
15934
+ comments,
15935
+ createdAt: asString(rec.dateCreated) || asString(rec.createdAt),
15936
+ updatedAt: asString(rec.lastUpdate) || asString(rec.updatedAt),
15937
+ ...assignee ? { assignee } : {}
15938
+ };
15939
+ }
15940
+ function restProjectToRaw(raw) {
15941
+ const rec = asRecord4(raw);
15942
+ if (!rec) return null;
15943
+ const id = asString(rec.projectId) || asString(rec.id);
15944
+ const name = asString(rec.projectName) || asString(rec.name);
15945
+ if (!id && !name) return null;
15946
+ const categories = asList(rec.categories).map((item) => {
15947
+ const cat = asRecord4(item) ?? {};
15948
+ return {
15949
+ id: asString(cat.projectCategoryId) || asString(cat.categoryId) || asString(cat.id),
15950
+ name: asString(cat.categoryName) || asString(cat.name)
15951
+ };
15952
+ });
15953
+ return { id, name, projectId: id, projectName: name, categories };
15954
+ }
15955
+ async function listUsers(opts) {
15956
+ const users = /* @__PURE__ */ new Map();
15957
+ for (const item of await restPages("/users", opts).catch(() => [])) {
15958
+ const user = userDisplayName(item);
15959
+ const rec = asRecord4(item);
15960
+ const id = user?.id || (rec ? asString(rec.userId) : "");
15961
+ if (user && id) users.set(id, user);
15962
+ }
15963
+ return users;
15964
+ }
15965
+ async function listProjectsRaw(opts) {
15966
+ const items = await restPages("/projects", { ...opts, query: { expand: "categories" } });
15967
+ return items.map(restProjectToRaw).filter((item) => Boolean(item));
15968
+ }
15969
+ async function listTasksRaw(opts) {
15970
+ const [users, items] = await Promise.all([
15971
+ listUsers(opts),
15972
+ restPages("/tasks", {
15973
+ ...opts,
15974
+ query: {
15975
+ ...opts.projectId ? { projectId: opts.projectId } : {}
15976
+ }
15977
+ })
15978
+ ]);
15979
+ let tasks = items.map((item) => restTaskToRaw(item, users)).filter((item) => Boolean(item));
15980
+ const needle = opts.query?.trim().toLowerCase();
15981
+ if (needle) {
15982
+ tasks = tasks.filter((task) => {
15983
+ const hay = [task.title, task.reference, task.taskRef, task.id].map((value) => String(value ?? "").toLowerCase()).join(" ");
15984
+ const tags = Array.isArray(task.tags) ? task.tags.join(" ").toLowerCase() : "";
15985
+ return hay.includes(needle) || tags.includes(needle);
15986
+ });
15987
+ }
15988
+ return tasks;
15989
+ }
15990
+ async function resolveTaskId(id, opts) {
15991
+ const wanted = id.trim();
15992
+ if (!wanted) throw new Error("AbleTime task id is required");
15993
+ if (looksLikeAbleTimeUlid(wanted)) return wanted;
15994
+ const tasks = await listTasksRaw(opts);
15995
+ const match = tasks.find((task) => {
15996
+ const ref = String(task.reference ?? task.taskRef ?? "").toLowerCase();
15997
+ const taskId = String(task.id ?? "").toLowerCase();
15998
+ return ref === wanted.toLowerCase() || taskId === wanted.toLowerCase();
15999
+ });
16000
+ if (!match?.id) throw new Error(`AbleTime task not found: ${id}`);
16001
+ return String(match.id);
16002
+ }
16003
+ async function getTaskRaw(id, opts) {
16004
+ const taskId = await resolveTaskId(id, opts);
16005
+ const [users, raw] = await Promise.all([
16006
+ listUsers(opts),
16007
+ abletimeRestRequest(`/tasks/${encodeURIComponent(taskId)}`, {
16008
+ ...opts,
16009
+ query: { expand: "comments" }
16010
+ })
16011
+ ]);
16012
+ const task = restTaskToRaw(raw, users);
16013
+ if (!task) throw new Error(`AbleTime task not found: ${id}`);
16014
+ return task;
16015
+ }
16016
+ function firstStringArg(args, keys) {
16017
+ for (const key of keys) {
16018
+ const value = asString(args[key]);
16019
+ if (value) return value;
16020
+ }
16021
+ return "";
16022
+ }
16023
+ async function callAbleTimeRestTool(name, args = {}, opts) {
16024
+ const token = normalizeAbleTimeCredential(opts?.token ?? getAbleTimeAccessToken() ?? "");
16025
+ if (!token) {
16026
+ throw new Error("AbleTime is not connected \u2014 paste an API key or connect via browser");
16027
+ }
16028
+ if (ableTimeCredentialKind(token) === "pat") {
16029
+ throw new Error("AbleTime REST transport is for organization keys and OAuth tokens, not apt_\u2026");
16030
+ }
16031
+ const requestOpts = { ...opts, token };
16032
+ switch (name) {
16033
+ case "orientation": {
16034
+ const [users, projects, tasks] = await Promise.all([
16035
+ listUsers(requestOpts),
16036
+ listProjectsRaw(requestOpts),
16037
+ listTasksRaw(requestOpts)
16038
+ ]);
16039
+ const viewer = [...users.values()].find((user) => user.name) ?? [...users.values()][0] ?? { name: "AbleTime" };
16040
+ return { viewer, projects, tasks };
16041
+ }
16042
+ case "list_projects":
16043
+ return { data: await listProjectsRaw(requestOpts) };
16044
+ case "list_users":
16045
+ return { data: [...(await listUsers(requestOpts)).values()] };
16046
+ case "list_tasks":
16047
+ return {
16048
+ data: await listTasksRaw({
16049
+ ...requestOpts,
16050
+ projectId: firstStringArg(args, ["project_id", "projectId", "project"])
16051
+ })
16052
+ };
16053
+ case "search_tasks":
16054
+ return {
16055
+ data: await listTasksRaw({
16056
+ ...requestOpts,
16057
+ query: firstStringArg(args, ["query", "q"])
16058
+ })
16059
+ };
16060
+ case "get_task":
16061
+ return await getTaskRaw(firstStringArg(args, ["id", "task_id", "task"]), requestOpts);
16062
+ case "create_task": {
16063
+ const title = firstStringArg(args, ["title"]);
16064
+ if (!title) throw new Error("AbleTime task title is required");
16065
+ const projectId = firstStringArg(args, ["project_id", "projectId", "project"]);
16066
+ if (!projectId) throw new Error("AbleTime project is required");
16067
+ const projects = await listProjectsRaw(requestOpts);
16068
+ const project = projects.find(
16069
+ (item) => String(item.id).toLowerCase() === projectId.toLowerCase() || String(item.name).toLowerCase() === projectId.toLowerCase()
16070
+ ) ?? projects[0];
16071
+ if (!project) throw new Error("AbleTime has no projects to create a task in");
16072
+ const categories = asList(project.categories);
16073
+ const wantedCategory = firstStringArg(args, ["category_id", "categoryId", "category"]);
16074
+ const category = categories.map((item) => asRecord4(item)).find((item) => {
16075
+ if (!item || !wantedCategory) return false;
16076
+ return asString(item.id).toLowerCase() === wantedCategory.toLowerCase() || asString(item.name).toLowerCase() === wantedCategory.toLowerCase();
16077
+ }) ?? asRecord4(categories[0]);
16078
+ const created = await abletimeRestRequest("/tasks", {
16079
+ ...requestOpts,
16080
+ method: "POST",
16081
+ body: {
16082
+ projectId: project.id,
16083
+ projectCategoryId: category ? asString(category.id) : void 0,
16084
+ title
16085
+ }
16086
+ });
16087
+ const state = firstStringArg(args, ["state"]);
16088
+ const createdId = asString(asRecord4(created)?.timeflowTaskId);
16089
+ const description = asString(args.description);
16090
+ if (createdId && description) {
16091
+ await abletimeRestRequest(`/tasks/${encodeURIComponent(createdId)}`, {
16092
+ ...requestOpts,
16093
+ method: "PATCH",
16094
+ body: { description: ableTimeDocFromText(description) }
16095
+ }).catch(() => void 0);
16096
+ }
16097
+ if (createdId && state && state !== "todo") {
16098
+ await abletimeRestRequest(`/tasks/${encodeURIComponent(createdId)}/state`, {
16099
+ ...requestOpts,
16100
+ method: "POST",
16101
+ body: { taskState: state }
16102
+ }).catch(() => void 0);
16103
+ }
16104
+ return createdId ? await getTaskRaw(createdId, requestOpts) : restTaskToRaw(created, await listUsers(requestOpts));
16105
+ }
16106
+ case "update_task": {
16107
+ const id = await resolveTaskId(firstStringArg(args, ["id", "task_id", "task"]), requestOpts);
16108
+ const patch = {};
16109
+ const title = firstStringArg(args, ["title"]);
16110
+ if (title) patch.title = title;
16111
+ if (args.description !== void 0) {
16112
+ patch.description = args.description == null || args.description === "" ? null : ableTimeDocFromText(String(args.description));
16113
+ }
16114
+ if (args.tags !== void 0 || args.labels !== void 0) {
16115
+ patch.tags = args.tags ?? args.labels ?? args.tag_names;
16116
+ }
16117
+ if (Object.keys(patch).length > 0) {
16118
+ await abletimeRestRequest(`/tasks/${encodeURIComponent(id)}`, {
16119
+ ...requestOpts,
16120
+ method: "PATCH",
16121
+ body: patch
16122
+ });
16123
+ }
16124
+ return await getTaskRaw(id, requestOpts);
16125
+ }
16126
+ case "set_task_state": {
16127
+ const id = await resolveTaskId(firstStringArg(args, ["id", "task_id", "task"]), requestOpts);
16128
+ const state = firstStringArg(args, ["state", "taskState"]);
16129
+ if (!state) throw new Error("AbleTime task state is required");
16130
+ await abletimeRestRequest(`/tasks/${encodeURIComponent(id)}/state`, {
16131
+ ...requestOpts,
16132
+ method: "POST",
16133
+ body: { taskState: state }
16134
+ });
16135
+ return await getTaskRaw(id, requestOpts);
16136
+ }
16137
+ case "create_comment": {
16138
+ const id = await resolveTaskId(firstStringArg(args, ["id", "task_id", "task"]), requestOpts);
16139
+ const body = firstStringArg(args, ["body", "comment", "text"]);
16140
+ if (!body) throw new Error("AbleTime comment body is required");
16141
+ const created = await abletimeRestRequest(`/tasks/${encodeURIComponent(id)}/comments`, {
16142
+ ...requestOpts,
16143
+ method: "POST",
16144
+ body: { content: ableTimeDocFromText(body) }
16145
+ });
16146
+ const rec = asRecord4(created) ?? {};
16147
+ return {
16148
+ id: asString(rec.commentId) || void 0,
16149
+ body: textFromAbleTimeDoc(rec.content) || body
16150
+ };
16151
+ }
16152
+ case "set_task_dependency":
16153
+ throw new Error(
16154
+ "AbleTime organization API keys cannot set task dependencies on REST yet \u2014 use a personal access token (apt_\u2026) for MCP project-management tools."
16155
+ );
16156
+ default:
16157
+ throw new Error(`AbleTime organization API keys do not support ${name} on REST`);
16158
+ }
16159
+ }
16160
+ var ABLETIME_REST_PREFIX, ULID_RE;
16161
+ var init_abletime_rest = __esm({
16162
+ "src/integrations/abletime-rest.ts"() {
16163
+ "use strict";
16164
+ init_fetch();
16165
+ init_app_settings();
16166
+ init_abletime_mcp();
16167
+ ABLETIME_REST_PREFIX = "/api/public/v2";
16168
+ ULID_RE = /^[0-9A-HJKMNP-TV-Z]{26}$/i;
16169
+ }
16170
+ });
16171
+
15376
16172
  // src/integrations/abletime-mcp.ts
15377
16173
  function abletimeToolUsesPm(name) {
15378
16174
  return ABLETIME_PM_TOOL_NAMES.includes(name);
@@ -15386,22 +16182,63 @@ function abletimeMcpUrl(host, opts) {
15386
16182
  const path = opts?.pm ? ABLETIME_MCP_PM_PATH : ABLETIME_MCP_PATH;
15387
16183
  return `${normalizeAbleTimeHost(host)}${path}`;
15388
16184
  }
16185
+ function normalizeAbleTimeCredential(raw) {
16186
+ return raw.trim().replace(/^bearer\s+/i, "").trim();
16187
+ }
16188
+ function ableTimeCredentialKind(raw) {
16189
+ const token = normalizeAbleTimeCredential(raw);
16190
+ const lower = token.toLowerCase();
16191
+ if (lower.startsWith(ABLETIME_PAT_PREFIX)) return "pat";
16192
+ if (lower.startsWith(ABLETIME_ORG_KEY_PREFIX)) return "org";
16193
+ if (lower.startsWith(ABLETIME_OAUTH_TOKEN_PREFIX) || token.startsWith("eyJ")) return "oauth";
16194
+ return "unknown";
16195
+ }
16196
+ function ableTimeUsesRest(raw) {
16197
+ const token = normalizeAbleTimeCredential(raw);
16198
+ const kind = ableTimeCredentialKind(token);
16199
+ if (kind === "org") return true;
16200
+ if (kind === "oauth") return token.startsWith("eyJ");
16201
+ return kind === "unknown";
16202
+ }
16203
+ function isAbleTimeRejectedCredential(message) {
16204
+ return /INTEGRATION_KEY_INVALID|INTEGRATION_KEY_MISSING|INTEGRATION_PAT_REQUIRED|\b401\b/i.test(
16205
+ message
16206
+ );
16207
+ }
16208
+ function assertAbleTimeMcpCredential(raw) {
16209
+ const token = normalizeAbleTimeCredential(raw);
16210
+ if (!token) {
16211
+ throw new Error("AbleTime personal access token is required");
16212
+ }
16213
+ if (ableTimeCredentialKind(token) === "org") {
16214
+ throw new Error(ORG_KEY_HINT);
16215
+ }
16216
+ return token;
16217
+ }
16218
+ function assertAbleTimeCredential(raw) {
16219
+ const token = normalizeAbleTimeCredential(raw);
16220
+ if (!token) {
16221
+ throw new Error("AbleTime API key or personal access token is required");
16222
+ }
16223
+ return token;
16224
+ }
15389
16225
  function rewriteAbleTimeError(message) {
15390
- if (/INTEGRATION_AGENT_ACCESS_DISABLED|agent access/i.test(message)) {
16226
+ if (REWRITE_APPLIED.test(message)) return message;
16227
+ if (/INTEGRATION_AGENT_ACCESS_DISABLED/i.test(message)) {
15391
16228
  return `${message} \u2014 Enable Agent access (MCP) in AbleTime Settings \u2192 Integrations \u2192 API Keys.`;
15392
16229
  }
15393
- if (/INTEGRATION_PAT_REQUIRED|organization api key/i.test(message)) {
15394
- return `${message} \u2014 AbleTime MCP needs a personal access token (apt_\u2026), not an organization API key.`;
16230
+ if (/INTEGRATION_PAT_REQUIRED/i.test(message)) {
16231
+ return `${message} \u2014 ${ORG_KEY_HINT}`;
15395
16232
  }
15396
16233
  if (/INTEGRATION_PLAN_REQUIRED/i.test(message)) {
15397
16234
  return `${message} \u2014 AbleTime plan does not include API access.`;
15398
16235
  }
15399
16236
  if (/INTEGRATION_KEY_INVALID|INTEGRATION_KEY_MISSING|401|unauthorized|invalid token/i.test(message)) {
15400
- return `${message} \u2014 Reconnect AbleTime from Account settings (Profile \u2192 API Access).`;
16237
+ return `${message} \u2014 ${REJECTED_CREDENTIAL_HINT}`;
15401
16238
  }
15402
16239
  return message;
15403
16240
  }
15404
- function asRecord4(value) {
16241
+ function asRecord5(value) {
15405
16242
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
15406
16243
  }
15407
16244
  function parseSseJson(body) {
@@ -15423,12 +16260,12 @@ function parseSseJson(body) {
15423
16260
  }
15424
16261
  }
15425
16262
  function unwrapToolResult(result) {
15426
- const record = asRecord4(result);
16263
+ const record = asRecord5(result);
15427
16264
  if (!record) return result;
15428
16265
  const content = record.content;
15429
16266
  if (Array.isArray(content)) {
15430
16267
  const texts = content.map((part) => {
15431
- const item = asRecord4(part);
16268
+ const item = asRecord5(part);
15432
16269
  if (!item) return "";
15433
16270
  return typeof item.text === "string" ? item.text : "";
15434
16271
  }).filter(Boolean);
@@ -15450,17 +16287,18 @@ function unwrapToolResult(result) {
15450
16287
  return result;
15451
16288
  }
15452
16289
  function textsFromUnknown(value) {
15453
- const record = asRecord4(value);
16290
+ const record = asRecord5(value);
15454
16291
  if (!record) return "";
15455
16292
  if (typeof record.message === "string") return record.message;
15456
16293
  if (typeof record.error === "string") return record.error;
15457
16294
  return "";
15458
16295
  }
15459
16296
  async function abletimeMcpRequest(method, params, opts) {
15460
- const token = (opts?.token ?? getAbleTimeAccessToken())?.trim();
15461
- if (!token) {
16297
+ const rawToken = opts?.token ?? await Promise.resolve().then(() => (init_abletime_oauth(), abletime_oauth_exports)).then((m) => m.ensureAbleTimeAccessToken());
16298
+ if (!rawToken?.trim()) {
15462
16299
  throw new Error("AbleTime is not connected \u2014 paste a personal access token in Account settings");
15463
16300
  }
16301
+ const token = assertAbleTimeMcpCredential(rawToken);
15464
16302
  const url = abletimeMcpUrl(
15465
16303
  opts?.host ?? (opts?.token ? DEFAULT_ABLETIME_HOST : getAbleTimeHost()),
15466
16304
  { pm: opts?.pm }
@@ -15496,7 +16334,7 @@ async function abletimeMcpRequest(method, params, opts) {
15496
16334
  return parseSseJson(body);
15497
16335
  }
15498
16336
  })();
15499
- const rpc = asRecord4(parsed);
16337
+ const rpc = asRecord5(parsed);
15500
16338
  if (rpc?.error) {
15501
16339
  const detail = typeof rpc.error.message === "string" && rpc.error.message.trim() ? rpc.error.message : `AbleTime MCP error ${rpc.error.code ?? ""}`.trim();
15502
16340
  throw new Error(rewriteAbleTimeError(detail));
@@ -15527,6 +16365,11 @@ async function callAbleTimeTool(name, args = {}, opts) {
15527
16365
  if (!opts?.token && !isAbleTimeConnected()) {
15528
16366
  throw new Error("AbleTime is not connected \u2014 paste a personal access token in Account settings");
15529
16367
  }
16368
+ const rawToken = opts?.token ?? await Promise.resolve().then(() => (init_abletime_oauth(), abletime_oauth_exports)).then((m) => m.ensureAbleTimeAccessToken());
16369
+ if (rawToken && ableTimeUsesRest(rawToken)) {
16370
+ const { callAbleTimeRestTool: callAbleTimeRestTool2 } = await Promise.resolve().then(() => (init_abletime_rest(), abletime_rest_exports));
16371
+ return callAbleTimeRestTool2(name, args, { ...opts, token: rawToken });
16372
+ }
15530
16373
  const requestOpts = { ...opts, pm: opts?.pm ?? abletimeToolUsesPm(name) };
15531
16374
  const call = () => abletimeMcpRequest("tools/call", { name, arguments: args }, requestOpts).then((result) => {
15532
16375
  return unwrapToolResult(result);
@@ -15539,10 +16382,14 @@ async function callAbleTimeTool(name, args = {}, opts) {
15539
16382
  await initializeIfNeeded(requestOpts);
15540
16383
  return await call();
15541
16384
  }
16385
+ if (rawToken && ableTimeUsesRest(rawToken) && isAbleTimeRejectedCredential(message)) {
16386
+ const { callAbleTimeRestTool: callAbleTimeRestTool2 } = await Promise.resolve().then(() => (init_abletime_rest(), abletime_rest_exports));
16387
+ return callAbleTimeRestTool2(name, args, { ...opts, token: rawToken });
16388
+ }
15542
16389
  throw err instanceof Error ? new Error(rewriteAbleTimeError(err.message)) : err;
15543
16390
  }
15544
16391
  }
15545
- var DEFAULT_ABLETIME_HOST, ABLETIME_MCP_PATH, ABLETIME_MCP_PM_PATH, MCP_PROTOCOL_VERSION, ABLETIME_PM_TOOL_NAMES;
16392
+ 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, ABLETIME_OAUTH_TOKEN_PREFIX, ORG_KEY_HINT, REJECTED_CREDENTIAL_HINT, REWRITE_APPLIED;
15546
16393
  var init_abletime_mcp = __esm({
15547
16394
  "src/integrations/abletime-mcp.ts"() {
15548
16395
  "use strict";
@@ -15564,6 +16411,12 @@ var init_abletime_mcp = __esm({
15564
16411
  "archive_task",
15565
16412
  "delete_task"
15566
16413
  ];
16414
+ ABLETIME_PAT_PREFIX = "apt_";
16415
+ ABLETIME_ORG_KEY_PREFIX = "atk_";
16416
+ ABLETIME_OAUTH_TOKEN_PREFIX = "acn_";
16417
+ 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.";
16418
+ REJECTED_CREDENTIAL_HINT = "AbleTime rejected this credential (unknown, revoked, or expired). Reconnect via browser, or paste a personal access token (apt_\u2026) from Edit Profile \u2192 API Access. Organization API keys (atk_\u2026) use REST, not MCP.";
16419
+ REWRITE_APPLIED = /— Enable Agent access \(MCP\)|— AbleTime MCP needs a personal access token|— AbleTime plan does not include API access|— AbleTime rejected this credential/;
15567
16420
  }
15568
16421
  });
15569
16422
 
@@ -15587,16 +16440,16 @@ __export(abletime_exports, {
15587
16440
  updateAbleTimeTask: () => updateAbleTimeTask,
15588
16441
  verifyAbleTimeConnection: () => verifyAbleTimeConnection
15589
16442
  });
15590
- function asRecord5(value) {
16443
+ function asRecord6(value) {
15591
16444
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
15592
16445
  }
15593
- function asString(value) {
16446
+ function asString2(value) {
15594
16447
  return typeof value === "string" ? value.trim() : "";
15595
16448
  }
15596
16449
  function firstString(record, keys) {
15597
16450
  if (!record) return "";
15598
16451
  for (const key of keys) {
15599
- const value = asString(record[key]);
16452
+ const value = asString2(record[key]);
15600
16453
  if (value) return value;
15601
16454
  }
15602
16455
  return "";
@@ -15604,14 +16457,14 @@ function firstString(record, keys) {
15604
16457
  function firstRecord(record, keys) {
15605
16458
  if (!record) return null;
15606
16459
  for (const key of keys) {
15607
- const value = asRecord5(record[key]);
16460
+ const value = asRecord6(record[key]);
15608
16461
  if (value) return value;
15609
16462
  }
15610
16463
  return null;
15611
16464
  }
15612
- function asList(value) {
16465
+ function asList2(value) {
15613
16466
  if (Array.isArray(value)) return value;
15614
- const record = asRecord5(value);
16467
+ const record = asRecord6(value);
15615
16468
  if (!record) return [];
15616
16469
  for (const key of ["data", "tasks", "projects", "items", "results", "nodes"]) {
15617
16470
  if (Array.isArray(record[key])) return record[key];
@@ -15623,21 +16476,21 @@ function labelsOf(record) {
15623
16476
  if (!Array.isArray(raw)) return [];
15624
16477
  return raw.map((item) => {
15625
16478
  if (typeof item === "string") return item.trim();
15626
- const rec = asRecord5(item);
16479
+ const rec = asRecord6(item);
15627
16480
  return rec ? firstString(rec, ["name", "title", "label"]) : "";
15628
16481
  }).filter(Boolean);
15629
16482
  }
15630
16483
  function commentsOf(record) {
15631
16484
  const raw = record.comments ?? record.notes ?? record.discussion;
15632
16485
  const out = [];
15633
- for (const item of asList(raw)) {
15634
- const rec = asRecord5(item);
16486
+ for (const item of asList2(raw)) {
16487
+ const rec = asRecord6(item);
15635
16488
  if (!rec) continue;
15636
- const body = firstString(rec, ["body", "text", "comment", "content", "message"]);
16489
+ const body = firstString(rec, ["body", "text", "comment", "message", "contentSanitized"]) || textFromAbleTimeDoc(rec.content);
15637
16490
  if (!body) continue;
15638
- const user = firstString(asRecord5(rec.user) ?? asRecord5(rec.author), ["name", "display_name"]) || firstString(rec, ["user_name", "author"]);
16491
+ const user = firstString(asRecord6(rec.user) ?? asRecord6(rec.author), ["name", "display_name", "username"]) || firstString(rec, ["user_name", "author", "username"]);
15639
16492
  const comment = { body };
15640
- const id = firstString(rec, ["id", "comment_id"]);
16493
+ const id = firstString(rec, ["id", "comment_id", "commentId"]);
15641
16494
  if (id) comment.id = id;
15642
16495
  const url = firstString(rec, ["url", "permalink"]);
15643
16496
  if (url) comment.url = url;
@@ -15661,11 +16514,11 @@ function taskUrl(task, host) {
15661
16514
  return `${base}/tasks/${slug}`;
15662
16515
  }
15663
16516
  function mapAbleTimeTask(raw, host) {
15664
- const record = asRecord5(raw);
16517
+ const record = asRecord6(raw);
15665
16518
  if (!record) return null;
15666
16519
  const nested = firstRecord(record, ["task", "data"]) ?? record;
15667
- const id = firstString(nested, ["id", "task_id", "taskId"]);
15668
- const identifier = firstString(nested, ["reference", "ref", "identifier", "key", "code", "number"]) || id;
16520
+ const id = firstString(nested, ["id", "task_id", "taskId", "timeflowTaskId"]);
16521
+ const identifier = firstString(nested, ["reference", "ref", "identifier", "key", "code", "number", "taskRef"]) || id;
15669
16522
  const title = firstString(nested, ["title", "name"]);
15670
16523
  if (!id && !identifier && !title) return null;
15671
16524
  const resolvedId = id || identifier;
@@ -15676,21 +16529,22 @@ function mapAbleTimeTask(raw, host) {
15676
16529
  identifier: resolvedIdentifier,
15677
16530
  title: title || resolvedIdentifier,
15678
16531
  url,
15679
- description: firstString(nested, ["description", "body", "details"]) || void 0,
15680
- state: firstString(nested, ["state", "board_state", "boardState", "status"]) || void 0,
15681
- projectId: firstString(nested, ["project_id", "projectId"]) || firstString(asRecord5(nested.project), ["id"]) || void 0,
15682
- categoryId: firstString(nested, ["category_id", "categoryId"]) || firstString(asRecord5(nested.category), ["id"]) || void 0,
16532
+ description: firstString(nested, ["description", "body", "details", "descriptionSanitized"]) || textFromAbleTimeDoc(nested.description) || void 0,
16533
+ state: firstString(nested, ["state", "board_state", "boardState", "status", "taskState"]) || void 0,
16534
+ projectId: firstString(nested, ["project_id", "projectId"]) || firstString(asRecord6(nested.project), ["id"]) || void 0,
16535
+ categoryId: firstString(nested, ["category_id", "categoryId", "projectCategoryId"]) || firstString(asRecord6(nested.category), ["id"]) || void 0,
15683
16536
  assignee: assigneeOf(nested),
15684
16537
  labels: labelsOf(nested),
15685
16538
  comments: commentsOf(nested),
15686
- createdAt: firstString(nested, ["created_at", "createdAt", "created"]) || void 0,
16539
+ createdAt: firstString(nested, ["created_at", "createdAt", "created", "dateCreated"]) || void 0,
15687
16540
  updatedAt: firstString(nested, [
15688
16541
  "updated_at",
15689
16542
  "updatedAt",
15690
16543
  "modified_at",
15691
16544
  "modifiedAt",
15692
16545
  "last_activity_at",
15693
- "lastActivityAt"
16546
+ "lastActivityAt",
16547
+ "lastUpdate"
15694
16548
  ]) || void 0
15695
16549
  };
15696
16550
  }
@@ -15721,16 +16575,16 @@ function issueAttachmentForAbleTimeTask(task) {
15721
16575
  };
15722
16576
  }
15723
16577
  function mapProject(raw) {
15724
- const record = asRecord5(raw);
16578
+ const record = asRecord6(raw);
15725
16579
  if (!record) return null;
15726
- const id = firstString(record, ["id", "project_id"]);
15727
- const name = firstString(record, ["name", "title"]);
16580
+ const id = firstString(record, ["id", "project_id", "projectId"]);
16581
+ const name = firstString(record, ["name", "title", "projectName"]);
15728
16582
  if (!id && !name) return null;
15729
- const categories = asList(record.categories ?? record.category).map((item) => {
15730
- const rec = asRecord5(item);
16583
+ const categories = asList2(record.categories ?? record.category).map((item) => {
16584
+ const rec = asRecord6(item);
15731
16585
  if (!rec) return null;
15732
- const categoryId = firstString(rec, ["id", "category_id"]);
15733
- const categoryName = firstString(rec, ["name", "title"]);
16586
+ const categoryId = firstString(rec, ["id", "category_id", "projectCategoryId", "categoryId"]);
16587
+ const categoryName = firstString(rec, ["name", "title", "categoryName"]);
15734
16588
  if (!categoryId && !categoryName) return null;
15735
16589
  return { id: categoryId || categoryName, name: categoryName || categoryId };
15736
16590
  }).filter((item) => Boolean(item));
@@ -15742,21 +16596,21 @@ function isOpenTask(task) {
15742
16596
  }
15743
16597
  async function getAbleTimeOrientation(opts) {
15744
16598
  const raw = await callAbleTimeTool("orientation", {}, opts);
15745
- const record = asRecord5(raw);
15746
- const viewerRec = firstRecord(record, ["viewer", "user", "actor", "me", "person"]) ?? record;
16599
+ const record = asRecord6(raw);
16600
+ const viewerRec = firstRecord(record, ["viewer", "principal", "user", "actor", "me", "person"]) ?? record;
15747
16601
  const viewer = {
15748
16602
  id: firstString(viewerRec, ["id", "user_id"]) || void 0,
15749
16603
  name: firstString(viewerRec, ["name", "display_name", "full_name"]) || firstString(record, ["name"]) || void 0
15750
16604
  };
15751
- const projects = asList(
16605
+ const projects = asList2(
15752
16606
  record?.projects ?? record?.current_projects ?? record?.currentProjects
15753
16607
  ).map(mapProject).filter((item) => Boolean(item));
15754
- const tasks = asList(record?.tasks ?? record?.active_tasks ?? record?.activeTasks).map((item) => mapAbleTimeTask(item, opts?.host)).filter((item) => Boolean(item));
16608
+ const tasks = asList2(record?.tasks ?? record?.active_tasks ?? record?.activeTasks).map((item) => mapAbleTimeTask(item, opts?.host)).filter((item) => Boolean(item));
15755
16609
  return { viewer, projects, tasks, raw };
15756
16610
  }
15757
16611
  async function listAbleTimeProjects(opts) {
15758
16612
  const raw = await callAbleTimeTool("list_projects", { include_categories: true }, opts);
15759
- return asList(raw).map(mapProject).filter((item) => Boolean(item));
16613
+ return asList2(raw).map(mapProject).filter((item) => Boolean(item));
15760
16614
  }
15761
16615
  async function listAbleTimeTasks(opts) {
15762
16616
  const args = {};
@@ -15766,14 +16620,14 @@ async function listAbleTimeTasks(opts) {
15766
16620
  }
15767
16621
  if (opts?.query) args.query = opts.query;
15768
16622
  const raw = await callAbleTimeTool("list_tasks", args, opts);
15769
- const tasks = asList(raw).map((item) => mapAbleTimeTask(item, opts?.host)).filter((item) => Boolean(item));
16623
+ const tasks = asList2(raw).map((item) => mapAbleTimeTask(item, opts?.host)).filter((item) => Boolean(item));
15770
16624
  return opts?.includeClosed ? tasks : tasks.filter(isOpenTask);
15771
16625
  }
15772
16626
  async function searchAbleTimeTasks(query, opts) {
15773
16627
  const q = query.trim();
15774
16628
  if (!q) return [];
15775
16629
  const raw = await callAbleTimeTool("search_tasks", { query: q, q }, opts);
15776
- return asList(raw).map((item) => mapAbleTimeTask(item, opts?.host)).filter((item) => Boolean(item)).filter(isOpenTask);
16630
+ return asList2(raw).map((item) => mapAbleTimeTask(item, opts?.host)).filter((item) => Boolean(item)).filter(isOpenTask);
15777
16631
  }
15778
16632
  async function getAbleTimeTask(id, opts) {
15779
16633
  const raw = await callAbleTimeTool("get_task", { id, task_id: id }, opts);
@@ -15821,7 +16675,7 @@ async function commentAbleTimeTask(input, opts) {
15821
16675
  { id, task_id: id, task: id, body, comment: body, text: body },
15822
16676
  opts
15823
16677
  );
15824
- const rec = asRecord5(raw);
16678
+ const rec = asRecord6(raw);
15825
16679
  return {
15826
16680
  id: rec ? firstString(rec, ["id", "comment_id"]) || void 0 : void 0,
15827
16681
  body: rec ? firstString(rec, ["body", "text", "comment"]) || body : body
@@ -16011,8 +16865,7 @@ async function listAbleTimeAssignedIssues(opts) {
16011
16865
  };
16012
16866
  }
16013
16867
  async function verifyAbleTimeConnection(input) {
16014
- const token = input.token.trim();
16015
- if (!token) throw new Error("AbleTime personal access token is required");
16868
+ const token = assertAbleTimeCredential(input.token);
16016
16869
  const host = input.host?.trim() || void 0;
16017
16870
  const orientation = await getAbleTimeOrientation({ token, host });
16018
16871
  saveAbleTimeConnection({
@@ -16028,6 +16881,7 @@ var init_abletime = __esm({
16028
16881
  "use strict";
16029
16882
  init_app_settings();
16030
16883
  init_abletime_mcp();
16884
+ init_abletime_rest();
16031
16885
  CLOSED_STATES = /* @__PURE__ */ new Set([
16032
16886
  "done",
16033
16887
  "completed",
@@ -16599,12 +17453,19 @@ function buildQuotaHandoffAttachment(from, limitText, fallbackAgent) {
16599
17453
  ...instructions
16600
17454
  ].join("\n");
16601
17455
  return {
16602
- id: (0, import_node_crypto9.randomUUID)(),
17456
+ id: (0, import_node_crypto10.randomUUID)(),
16603
17457
  name: orch ? "Orchestration quota handoff.md" : "Quota handoff.md",
16604
17458
  kind: "transcript",
16605
17459
  content: body
16606
17460
  };
16607
17461
  }
17462
+ async function isolateQuotaFailover(run2) {
17463
+ try {
17464
+ return await run2();
17465
+ } catch {
17466
+ return true;
17467
+ }
17468
+ }
16608
17469
  function createQuotaFailoverChat(from, fallbackAgent, limitText) {
16609
17470
  const handoff = buildQuotaHandoffAttachment(from, limitText, fallbackAgent);
16610
17471
  const tab = createChatTab({
@@ -16623,11 +17484,11 @@ function createQuotaFailoverChat(from, fallbackAgent, limitText) {
16623
17484
  }
16624
17485
  return updateThread(tab.id, patch);
16625
17486
  }
16626
- var import_node_crypto9, QUOTA_CONTINUE_PROMPT, QUOTA_RESUME_PROMPT;
17487
+ var import_node_crypto10, QUOTA_CONTINUE_PROMPT, QUOTA_RESUME_PROMPT;
16627
17488
  var init_quota_failover = __esm({
16628
17489
  "src/orchestrator/quota-failover.ts"() {
16629
17490
  "use strict";
16630
- import_node_crypto9 = require("crypto");
17491
+ import_node_crypto10 = require("crypto");
16631
17492
  init_session_quota();
16632
17493
  init_app_settings();
16633
17494
  init_global_workspace();
@@ -18938,7 +19799,7 @@ var init_optional_services = __esm({
18938
19799
  });
18939
19800
 
18940
19801
  // src/plan/plan-present.ts
18941
- function asRecord6(v) {
19802
+ function asRecord7(v) {
18942
19803
  return v != null && typeof v === "object" && !Array.isArray(v) ? v : null;
18943
19804
  }
18944
19805
  function isPresentPlanToolName(name) {
@@ -18950,7 +19811,7 @@ function extractPresentedPlan(parts) {
18950
19811
  for (let i = parts.length - 1; i >= 0; i--) {
18951
19812
  const p = parts[i];
18952
19813
  if (p.type !== "tool" || !isPresentPlanToolName(p.name)) continue;
18953
- const input = asRecord6(p.input) ?? {};
19814
+ const input = asRecord7(p.input) ?? {};
18954
19815
  const content = typeof input.content === "string" ? input.content : typeof input.plan === "string" ? input.plan : typeof input.markdown === "string" ? input.markdown : "";
18955
19816
  if (!content.trim()) continue;
18956
19817
  const title = typeof input.title === "string" && input.title.trim() ? input.title.trim() : "Plan";
@@ -19385,7 +20246,7 @@ function buildTask(input, now) {
19385
20246
  const ts = nowIso2(now);
19386
20247
  const agent = input.agent ? coerceOrchestratorAgent(input.agent) : null;
19387
20248
  return {
19388
- id: (0, import_node_crypto10.randomUUID)(),
20249
+ id: (0, import_node_crypto11.randomUUID)(),
19389
20250
  name: input.name?.trim() || defaultScheduleName(prompt),
19390
20251
  prompt,
19391
20252
  enabled: input.enabled !== false,
@@ -19472,11 +20333,11 @@ function recordScheduleRun(id, result) {
19472
20333
  writeAll2(rows);
19473
20334
  return next;
19474
20335
  }
19475
- var import_node_crypto10, import_node_fs52, import_node_path49, import_croner, DURATION_RE, UNIT_MS;
20336
+ var import_node_crypto11, import_node_fs52, import_node_path49, import_croner, DURATION_RE, UNIT_MS;
19476
20337
  var init_schedules = __esm({
19477
20338
  "src/store/schedules.ts"() {
19478
20339
  "use strict";
19479
- import_node_crypto10 = require("crypto");
20340
+ import_node_crypto11 = require("crypto");
19480
20341
  import_node_fs52 = require("fs");
19481
20342
  import_node_path49 = require("path");
19482
20343
  import_croner = require("croner");
@@ -19754,11 +20615,6 @@ function isPidAlive(pid) {
19754
20615
  return false;
19755
20616
  }
19756
20617
  }
19757
- function sameLoginList(a, b) {
19758
- const left = [...a ?? []].map((s) => s.trim().toLowerCase()).filter(Boolean).sort();
19759
- const right = [...b ?? []].map((s) => s.trim().toLowerCase()).filter(Boolean).sort();
19760
- return left.length === right.length && left.every((login, i) => login === right[i]);
19761
- }
19762
20618
  async function waitForPidExit(pid, timeoutMs) {
19763
20619
  const start = Date.now();
19764
20620
  while (isPidAlive(pid)) {
@@ -20165,51 +21021,62 @@ var init_orchestrator = __esm({
20165
21021
  if (plan.action === "wait_reset" && plan.resumeAt) {
20166
21022
  this.haltDrain.add(threadId);
20167
21023
  this.scheduleQuotaResume(threadId, plan.resumeAt);
20168
- setStatus(threadId, "idle", null);
20169
- appendMessage(threadId, {
20170
- role: "agent",
20171
- text: `Sideboard will auto-retry this chat around ${plan.resumeAt.toLocaleString()} when the session limit resets.`,
20172
- ts: (/* @__PURE__ */ new Date()).toISOString()
20173
- });
20174
- this.emit({
20175
- type: "quota_failover",
20176
- threadId,
20177
- action: "wait_reset",
20178
- message: plan.reason,
20179
- resumeAt: plan.resumeAt.toISOString()
20180
- });
20181
- this.emit({ type: "status_changed", threadId, status: "idle" });
21024
+ try {
21025
+ setStatus(threadId, "idle", null);
21026
+ appendMessage(threadId, {
21027
+ role: "agent",
21028
+ text: `Sideboard will auto-retry this chat around ${plan.resumeAt.toLocaleString()} when the session limit resets.`,
21029
+ ts: (/* @__PURE__ */ new Date()).toISOString()
21030
+ });
21031
+ this.emit({
21032
+ type: "quota_failover",
21033
+ threadId,
21034
+ action: "wait_reset",
21035
+ message: plan.reason,
21036
+ resumeAt: plan.resumeAt.toISOString()
21037
+ });
21038
+ this.emit({ type: "status_changed", threadId, status: "idle" });
21039
+ } catch {
21040
+ }
20182
21041
  return true;
20183
21042
  }
20184
21043
  if (plan.action === "switch_agent" && plan.fallbackAgent) {
20185
21044
  this.haltDrain.add(threadId);
20186
- const next = createQuotaFailoverChat(
20187
- thread,
20188
- plan.fallbackAgent,
20189
- plan.limitText
20190
- );
21045
+ let next;
21046
+ try {
21047
+ next = createQuotaFailoverChat(
21048
+ thread,
21049
+ plan.fallbackAgent,
21050
+ plan.limitText
21051
+ );
21052
+ } catch {
21053
+ return false;
21054
+ }
20191
21055
  this.clearQuotaResumeTimer(threadId);
20192
21056
  try {
20193
21057
  updateThread(threadId, { quotaResumeAt: null });
20194
21058
  } catch {
20195
21059
  }
20196
- appendMessage(threadId, {
20197
- role: "agent",
20198
- text: `Session limit on ${thread.agent}. Sideboard continued on ${plan.fallbackAgent} (Auto) in [${next.title}](sideboard://thread/${next.id}).`,
20199
- ts: (/* @__PURE__ */ new Date()).toISOString()
20200
- });
20201
- this.emit({
20202
- type: "quota_failover",
20203
- threadId,
20204
- action: "switch_agent",
20205
- toThreadId: next.id,
20206
- message: plan.reason
20207
- });
20208
- this.emit({
20209
- type: "status_changed",
20210
- threadId: next.id,
20211
- status: next.status
20212
- });
21060
+ try {
21061
+ appendMessage(threadId, {
21062
+ role: "agent",
21063
+ text: `Session limit on ${thread.agent}. Sideboard continued on ${plan.fallbackAgent} (Auto) in [${next.title}](sideboard://thread/${next.id}).`,
21064
+ ts: (/* @__PURE__ */ new Date()).toISOString()
21065
+ });
21066
+ this.emit({
21067
+ type: "quota_failover",
21068
+ threadId,
21069
+ action: "switch_agent",
21070
+ toThreadId: next.id,
21071
+ message: plan.reason
21072
+ });
21073
+ this.emit({
21074
+ type: "status_changed",
21075
+ threadId: next.id,
21076
+ status: next.status
21077
+ });
21078
+ } catch {
21079
+ }
20213
21080
  void this.send(
20214
21081
  next.id,
20215
21082
  QUOTA_CONTINUE_PROMPT(thread.agent, plan.fallbackAgent)
@@ -20940,9 +21807,8 @@ var init_orchestrator = __esm({
20940
21807
  this.maybeEnqueueJobContinue(threadId, chatText, parts);
20941
21808
  } else {
20942
21809
  const blob = [chatText, detail].filter(Boolean).join("\n");
20943
- const failedOver = await this.maybeHandleOrchestrationQuotaFailover(
20944
- threadId,
20945
- blob
21810
+ const failedOver = await isolateQuotaFailover(
21811
+ () => this.maybeHandleOrchestrationQuotaFailover(threadId, blob)
20946
21812
  );
20947
21813
  this.maybeEnqueueCrashContinue(threadId, {
20948
21814
  detail: detail || failDetail,
@@ -20976,9 +21842,8 @@ var init_orchestrator = __esm({
20976
21842
  this.emit({ type: "status_changed", threadId, status: "error" });
20977
21843
  }
20978
21844
  this.emit({ type: "turn_finished", threadId, exitCode: 1 });
20979
- const failedOver = await this.maybeHandleOrchestrationQuotaFailover(
20980
- threadId,
20981
- message
21845
+ const failedOver = await isolateQuotaFailover(
21846
+ () => this.maybeHandleOrchestrationQuotaFailover(threadId, message)
20982
21847
  );
20983
21848
  this.maybeEnqueueCrashContinue(threadId, {
20984
21849
  detail: message,
@@ -21588,12 +22453,18 @@ var init_orchestrator = __esm({
21588
22453
  /** Resolve PR selectors and optionally persist `prUrl` when found. */
21589
22454
  async withPrSelector(threadRef) {
21590
22455
  const thread = this.requireThread(threadRef);
21591
- const selectors = resolvePrSelectors(thread);
21592
22456
  const cwd = thread.worktreePath;
21593
22457
  if (!cwd?.trim()) {
21594
22458
  throw new Error(`Thread ${threadRef} has no worktreePath`);
21595
22459
  }
21596
- return { thread, selectors, cwd };
22460
+ let headUrl = null;
22461
+ try {
22462
+ const head = await getPrForHeadBranch(cwd, thread.branchName);
22463
+ headUrl = head?.url?.trim() || null;
22464
+ } catch {
22465
+ headUrl = null;
22466
+ }
22467
+ return { thread, selectors: connectedPrSelectors(thread, headUrl), cwd };
21597
22468
  }
21598
22469
  async getPrChecks(threadRef) {
21599
22470
  const { selectors, cwd } = await this.withPrSelector(threadRef);
@@ -21621,29 +22492,16 @@ var init_orchestrator = __esm({
21621
22492
  async persistPrMetaAndMaybeArchive(thread, meta) {
21622
22493
  const prevState = normalizePrState(thread.prState);
21623
22494
  const nextState = normalizePrState(meta.state);
21624
- const patch = {};
21625
- if (meta.url && meta.url !== thread.prUrl) patch.prUrl = meta.url;
21626
- if (meta.title && meta.title !== thread.prTitle) patch.prTitle = meta.title;
21627
- if (nextState && nextState !== prevState) patch.prState = nextState;
21628
- const nextDraft = Boolean(meta.isDraft) && nextState !== "MERGED" && nextState !== "CLOSED";
21629
- if (nextDraft !== Boolean(thread.prIsDraft)) patch.prIsDraft = nextDraft;
21630
- const nextAuthor = meta.authorLogin?.trim() || "";
21631
- if (nextAuthor && nextAuthor !== (thread.prAuthorLogin ?? "").trim()) {
21632
- patch.prAuthorLogin = nextAuthor;
21633
- }
21634
- const nextReviewers = meta.reviewerLogins ?? [];
21635
- if (!sameLoginList(nextReviewers, thread.prReviewerLogins)) {
21636
- patch.prReviewerLogins = nextReviewers;
22495
+ const siblings = threadsSharingWorktree(thread.worktreePath);
22496
+ const targets = siblings.length > 0 ? siblings : [thread];
22497
+ for (const t of targets) {
22498
+ const patch = threadPrMetaPatch(t, meta);
22499
+ if (Object.keys(patch).length === 0) continue;
22500
+ updateThread(t.id, patch);
21637
22501
  }
21638
- if (thread.skipAutoArchiveOnMerge && nextState && nextState !== "MERGED" && nextState !== "CLOSED") {
21639
- patch.skipAutoArchiveOnMerge = false;
21640
- }
21641
- if (Object.keys(patch).length > 0) {
21642
- updateThread(thread.id, patch);
21643
- const latest2 = this.requireThread(thread.id);
21644
- if (!latest2.userSetTitle && meta.title && latest2.title !== meta.title) {
21645
- updateThread(thread.id, { title: meta.title });
21646
- }
22502
+ const titled = this.requireThread(thread.id);
22503
+ if (!titled.userSetTitle && meta.title && titled.title !== meta.title) {
22504
+ updateThread(thread.id, { title: meta.title });
21647
22505
  }
21648
22506
  const { autoArchiveOnMergeEnabled: autoArchiveOnMergeEnabled2 } = await Promise.resolve().then(() => (init_app_settings(), app_settings_exports));
21649
22507
  const latest = this.requireThread(thread.id);
@@ -21657,22 +22515,7 @@ var init_orchestrator = __esm({
21657
22515
  })) {
21658
22516
  return;
21659
22517
  }
21660
- const siblings = threadsSharingWorktree(latest.worktreePath);
21661
- for (const t of siblings) {
21662
- const sibPatch = { prState: "MERGED", prIsDraft: false };
21663
- if (meta.url && meta.url !== t.prUrl) sibPatch.prUrl = meta.url;
21664
- if (meta.title && meta.title !== t.prTitle) sibPatch.prTitle = meta.title;
21665
- const sibAuthor = meta.authorLogin?.trim() || "";
21666
- if (sibAuthor && sibAuthor !== (t.prAuthorLogin ?? "").trim()) {
21667
- sibPatch.prAuthorLogin = sibAuthor;
21668
- }
21669
- const sibReviewers = meta.reviewerLogins ?? [];
21670
- if (!sameLoginList(sibReviewers, t.prReviewerLogins)) {
21671
- sibPatch.prReviewerLogins = sibReviewers;
21672
- }
21673
- if (Object.keys(sibPatch).length > 0) updateThread(t.id, sibPatch);
21674
- }
21675
- for (const t of siblings) {
22518
+ for (const t of threadsSharingWorktree(latest.worktreePath)) {
21676
22519
  if (this.requireThread(t.id).status === "archived") continue;
21677
22520
  await this.archive(t.id);
21678
22521
  }
@@ -22068,7 +22911,7 @@ init_system_ca();
22068
22911
  var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
22069
22912
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
22070
22913
  var import_zod9 = require("zod");
22071
- var import_node_crypto13 = require("crypto");
22914
+ var import_node_crypto14 = require("crypto");
22072
22915
  var import_node_path53 = require("path");
22073
22916
  init_orchestrator();
22074
22917
  init_worktree();
@@ -22495,8 +23338,8 @@ async function createGitHubIssue(input, opts) {
22495
23338
  init_fetch();
22496
23339
 
22497
23340
  // src/integrations/linear-oauth.ts
22498
- var import_node_crypto11 = require("crypto");
22499
- var import_node_http = require("http");
23341
+ var import_node_crypto12 = require("crypto");
23342
+ var import_node_http2 = require("http");
22500
23343
  init_app_settings();
22501
23344
  init_fetch();
22502
23345
 
@@ -22507,7 +23350,7 @@ var BAKED_LINEAR_CLIENT_ID = "39a15abc7ea5bea17c47f47f85ff5fd0";
22507
23350
  var LINEAR_OAUTH_PORT = 19848;
22508
23351
  var LINEAR_OAUTH_REDIRECT = `http://127.0.0.1:${LINEAR_OAUTH_PORT}/callback`;
22509
23352
  var LINEAR_TOKEN = "https://api.linear.app/oauth/token";
22510
- var REFRESH_SKEW_MS2 = 5 * 6e4;
23353
+ var REFRESH_SKEW_MS3 = 5 * 6e4;
22511
23354
  function linearOAuthCredentials() {
22512
23355
  const settings = loadAppSettings();
22513
23356
  const clientId = process.env.SIDEBOARD_LINEAR_CLIENT_ID?.trim() || settings.integrations.linearClientId?.trim() || BAKED_LINEAR_CLIENT_ID.trim();
@@ -22548,7 +23391,7 @@ async function persistLinearTokens(data, viewer) {
22548
23391
  organizationName: viewer?.organizationName
22549
23392
  });
22550
23393
  }
22551
- var refreshInFlight = null;
23394
+ var refreshInFlight2 = null;
22552
23395
  async function refreshLinearAccessToken(refreshToken) {
22553
23396
  const { clientId, clientSecret } = linearOAuthCredentials();
22554
23397
  const body = new URLSearchParams({
@@ -22575,16 +23418,16 @@ async function getLinearAuthToken(settings = loadAppSettings()) {
22575
23418
  const refresh = settings.integrations.linearRefreshToken?.trim() || "";
22576
23419
  const expiresAt = settings.integrations.linearTokenExpiresAt ?? 0;
22577
23420
  const apiKey = settings.integrations.linearApiKey?.trim() || "";
22578
- if (access && (!refresh || Date.now() < expiresAt - REFRESH_SKEW_MS2)) {
23421
+ if (access && (!refresh || Date.now() < expiresAt - REFRESH_SKEW_MS3)) {
22579
23422
  return access;
22580
23423
  }
22581
23424
  if (refresh) {
22582
- if (!refreshInFlight) {
22583
- refreshInFlight = refreshLinearAccessToken(refresh).finally(() => {
22584
- refreshInFlight = null;
23425
+ if (!refreshInFlight2) {
23426
+ refreshInFlight2 = refreshLinearAccessToken(refresh).finally(() => {
23427
+ refreshInFlight2 = null;
22585
23428
  });
22586
23429
  }
22587
- return refreshInFlight.catch((err) => {
23430
+ return refreshInFlight2.catch((err) => {
22588
23431
  if (apiKey) return apiKey;
22589
23432
  throw err;
22590
23433
  });
@@ -25478,7 +26321,7 @@ init_worktree();
25478
26321
  init_paths();
25479
26322
 
25480
26323
  // src/board/board-pins.ts
25481
- var import_node_crypto12 = require("crypto");
26324
+ var import_node_crypto13 = require("crypto");
25482
26325
  var import_node_fs55 = require("fs");
25483
26326
  var import_node_path51 = require("path");
25484
26327
  init_paths();
@@ -26327,7 +27170,7 @@ async function startMcpServer() {
26327
27170
  const ident = issue?.identifier ?? ref.trim();
26328
27171
  const attachments = issue ? [
26329
27172
  {
26330
- id: (0, import_node_crypto13.randomUUID)(),
27173
+ id: (0, import_node_crypto14.randomUUID)(),
26331
27174
  name: issue.identifier,
26332
27175
  kind: "issue",
26333
27176
  content: [