@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
@@ -1,198 +1,16 @@
1
1
  import {
2
- httpFetch
3
- } from "./chunk-N7WZ6NIN.js";
2
+ textFromAbleTimeDoc
3
+ } from "./chunk-CYLDJWRK.js";
4
+ import {
5
+ assertAbleTimeCredential,
6
+ callAbleTimeTool,
7
+ normalizeAbleTimeHost,
8
+ rewriteAbleTimeError
9
+ } from "./chunk-W6VM36YO.js";
4
10
  import {
5
- getAbleTimeAccessToken,
6
- getAbleTimeHost,
7
11
  isAbleTimeConnected,
8
12
  saveAbleTimeConnection
9
- } from "./chunk-CXFW5LIH.js";
10
-
11
- // src/integrations/abletime-mcp.ts
12
- var DEFAULT_ABLETIME_HOST = "https://track.abletime.com";
13
- var ABLETIME_MCP_PATH = "/api/public/v2/mcp";
14
- var ABLETIME_MCP_PM_PATH = "/api/public/v2/mcp/pm";
15
- var MCP_PROTOCOL_VERSION = "2025-03-26";
16
- var ABLETIME_PM_TOOL_NAMES = [
17
- "set_task_assignee",
18
- "set_task_priority",
19
- "set_task_stage",
20
- "set_task_dependency",
21
- "set_task_blocked",
22
- "set_task_locked",
23
- "set_task_schedule",
24
- "clear_task_schedule",
25
- "archive_task",
26
- "delete_task"
27
- ];
28
- function abletimeToolUsesPm(name) {
29
- return ABLETIME_PM_TOOL_NAMES.includes(name);
30
- }
31
- function normalizeAbleTimeHost(raw) {
32
- const trimmed = raw?.trim() || DEFAULT_ABLETIME_HOST;
33
- const withScheme = /^https?:\/\//i.test(trimmed) ? trimmed : `https://${trimmed}`;
34
- return withScheme.replace(/\/+$/, "");
35
- }
36
- function abletimeMcpUrl(host, opts) {
37
- const path = opts?.pm ? ABLETIME_MCP_PM_PATH : ABLETIME_MCP_PATH;
38
- return `${normalizeAbleTimeHost(host)}${path}`;
39
- }
40
- function rewriteAbleTimeError(message) {
41
- if (/INTEGRATION_AGENT_ACCESS_DISABLED|agent access/i.test(message)) {
42
- return `${message} \u2014 Enable Agent access (MCP) in AbleTime Settings \u2192 Integrations \u2192 API Keys.`;
43
- }
44
- if (/INTEGRATION_PAT_REQUIRED|organization api key/i.test(message)) {
45
- return `${message} \u2014 AbleTime MCP needs a personal access token (apt_\u2026), not an organization API key.`;
46
- }
47
- if (/INTEGRATION_PLAN_REQUIRED/i.test(message)) {
48
- return `${message} \u2014 AbleTime plan does not include API access.`;
49
- }
50
- if (/INTEGRATION_KEY_INVALID|INTEGRATION_KEY_MISSING|401|unauthorized|invalid token/i.test(message)) {
51
- return `${message} \u2014 Reconnect AbleTime from Account settings (Profile \u2192 API Access).`;
52
- }
53
- return message;
54
- }
55
- function asRecord(value) {
56
- return value && typeof value === "object" && !Array.isArray(value) ? value : null;
57
- }
58
- function parseSseJson(body) {
59
- const blocks = body.split(/\n\n+/);
60
- let last;
61
- for (const block of blocks) {
62
- const data = block.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trim()).filter((line) => line && line !== "[DONE]").join("\n");
63
- if (!data) continue;
64
- try {
65
- last = JSON.parse(data);
66
- } catch {
67
- }
68
- }
69
- if (last !== void 0) return last;
70
- try {
71
- return JSON.parse(body);
72
- } catch {
73
- throw new Error(`AbleTime MCP returned a non-JSON response: ${body.slice(0, 200)}`);
74
- }
75
- }
76
- function unwrapToolResult(result) {
77
- const record = asRecord(result);
78
- if (!record) return result;
79
- const content = record.content;
80
- if (Array.isArray(content)) {
81
- const texts = content.map((part) => {
82
- const item = asRecord(part);
83
- if (!item) return "";
84
- return typeof item.text === "string" ? item.text : "";
85
- }).filter(Boolean);
86
- if (texts.length === 1) {
87
- const text = texts[0];
88
- try {
89
- return JSON.parse(text);
90
- } catch {
91
- return text;
92
- }
93
- }
94
- if (texts.length > 1) return texts.join("\n");
95
- }
96
- if (record.structuredContent != null) return record.structuredContent;
97
- if (record.isError === true) {
98
- const message = typeof record.message === "string" ? record.message : textsFromUnknown(result) || "AbleTime tool failed";
99
- throw new Error(message);
100
- }
101
- return result;
102
- }
103
- function textsFromUnknown(value) {
104
- const record = asRecord(value);
105
- if (!record) return "";
106
- if (typeof record.message === "string") return record.message;
107
- if (typeof record.error === "string") return record.error;
108
- return "";
109
- }
110
- async function abletimeMcpRequest(method, params, opts) {
111
- const token = (opts?.token ?? getAbleTimeAccessToken())?.trim();
112
- if (!token) {
113
- throw new Error("AbleTime is not connected \u2014 paste a personal access token in Account settings");
114
- }
115
- const url = abletimeMcpUrl(
116
- opts?.host ?? (opts?.token ? DEFAULT_ABLETIME_HOST : getAbleTimeHost()),
117
- { pm: opts?.pm }
118
- );
119
- const res = await httpFetch(url, {
120
- method: "POST",
121
- headers: {
122
- "Content-Type": "application/json",
123
- Accept: "application/json, text/event-stream",
124
- Authorization: `Bearer ${token}`,
125
- "MCP-Protocol-Version": MCP_PROTOCOL_VERSION
126
- },
127
- body: JSON.stringify({
128
- jsonrpc: "2.0",
129
- id: 1,
130
- method,
131
- ...params === void 0 ? {} : { params }
132
- })
133
- });
134
- const body = await res.text().catch(() => "");
135
- if (!res.ok) {
136
- throw new Error(
137
- rewriteAbleTimeError(
138
- `AbleTime MCP error ${res.status}${body ? `: ${body.slice(0, 240)}` : ""}`
139
- )
140
- );
141
- }
142
- const contentType = res.headers.get("content-type") ?? "";
143
- const parsed = contentType.includes("text/event-stream") ? parseSseJson(body) : (() => {
144
- try {
145
- return JSON.parse(body);
146
- } catch {
147
- return parseSseJson(body);
148
- }
149
- })();
150
- const rpc = asRecord(parsed);
151
- if (rpc?.error) {
152
- const detail = typeof rpc.error.message === "string" && rpc.error.message.trim() ? rpc.error.message : `AbleTime MCP error ${rpc.error.code ?? ""}`.trim();
153
- throw new Error(rewriteAbleTimeError(detail));
154
- }
155
- if (rpc && "result" in rpc) return rpc.result;
156
- return parsed;
157
- }
158
- async function initializeIfNeeded(opts) {
159
- try {
160
- await abletimeMcpRequest(
161
- "initialize",
162
- {
163
- protocolVersion: MCP_PROTOCOL_VERSION,
164
- capabilities: {},
165
- clientInfo: { name: "sideboard", version: "1.0.0" }
166
- },
167
- opts
168
- );
169
- } catch (err) {
170
- const message = err instanceof Error ? err.message : String(err);
171
- if (/already initialized|not (required|supported)|method not found|-32601/i.test(message)) {
172
- return;
173
- }
174
- throw err;
175
- }
176
- }
177
- async function callAbleTimeTool(name, args = {}, opts) {
178
- if (!opts?.token && !isAbleTimeConnected()) {
179
- throw new Error("AbleTime is not connected \u2014 paste a personal access token in Account settings");
180
- }
181
- const requestOpts = { ...opts, pm: opts?.pm ?? abletimeToolUsesPm(name) };
182
- const call = () => abletimeMcpRequest("tools/call", { name, arguments: args }, requestOpts).then((result) => {
183
- return unwrapToolResult(result);
184
- });
185
- try {
186
- return await call();
187
- } catch (err) {
188
- const message = err instanceof Error ? err.message : String(err);
189
- if (/initialize|session|not initialized|-32000/i.test(message)) {
190
- await initializeIfNeeded(requestOpts);
191
- return await call();
192
- }
193
- throw err instanceof Error ? new Error(rewriteAbleTimeError(err.message)) : err;
194
- }
195
- }
13
+ } from "./chunk-QFBBUFD4.js";
196
14
 
197
15
  // src/integrations/abletime.ts
198
16
  var CLOSED_STATES = /* @__PURE__ */ new Set([
@@ -203,7 +21,7 @@ var CLOSED_STATES = /* @__PURE__ */ new Set([
203
21
  "archived",
204
22
  "closed"
205
23
  ]);
206
- function asRecord2(value) {
24
+ function asRecord(value) {
207
25
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
208
26
  }
209
27
  function asString(value) {
@@ -220,14 +38,14 @@ function firstString(record, keys) {
220
38
  function firstRecord(record, keys) {
221
39
  if (!record) return null;
222
40
  for (const key of keys) {
223
- const value = asRecord2(record[key]);
41
+ const value = asRecord(record[key]);
224
42
  if (value) return value;
225
43
  }
226
44
  return null;
227
45
  }
228
46
  function asList(value) {
229
47
  if (Array.isArray(value)) return value;
230
- const record = asRecord2(value);
48
+ const record = asRecord(value);
231
49
  if (!record) return [];
232
50
  for (const key of ["data", "tasks", "projects", "items", "results", "nodes"]) {
233
51
  if (Array.isArray(record[key])) return record[key];
@@ -239,7 +57,7 @@ function labelsOf(record) {
239
57
  if (!Array.isArray(raw)) return [];
240
58
  return raw.map((item) => {
241
59
  if (typeof item === "string") return item.trim();
242
- const rec = asRecord2(item);
60
+ const rec = asRecord(item);
243
61
  return rec ? firstString(rec, ["name", "title", "label"]) : "";
244
62
  }).filter(Boolean);
245
63
  }
@@ -247,13 +65,13 @@ function commentsOf(record) {
247
65
  const raw = record.comments ?? record.notes ?? record.discussion;
248
66
  const out = [];
249
67
  for (const item of asList(raw)) {
250
- const rec = asRecord2(item);
68
+ const rec = asRecord(item);
251
69
  if (!rec) continue;
252
- const body = firstString(rec, ["body", "text", "comment", "content", "message"]);
70
+ const body = firstString(rec, ["body", "text", "comment", "message", "contentSanitized"]) || textFromAbleTimeDoc(rec.content);
253
71
  if (!body) continue;
254
- const user = firstString(asRecord2(rec.user) ?? asRecord2(rec.author), ["name", "display_name"]) || firstString(rec, ["user_name", "author"]);
72
+ const user = firstString(asRecord(rec.user) ?? asRecord(rec.author), ["name", "display_name", "username"]) || firstString(rec, ["user_name", "author", "username"]);
255
73
  const comment = { body };
256
- const id = firstString(rec, ["id", "comment_id"]);
74
+ const id = firstString(rec, ["id", "comment_id", "commentId"]);
257
75
  if (id) comment.id = id;
258
76
  const url = firstString(rec, ["url", "permalink"]);
259
77
  if (url) comment.url = url;
@@ -277,11 +95,11 @@ function taskUrl(task, host) {
277
95
  return `${base}/tasks/${slug}`;
278
96
  }
279
97
  function mapAbleTimeTask(raw, host) {
280
- const record = asRecord2(raw);
98
+ const record = asRecord(raw);
281
99
  if (!record) return null;
282
100
  const nested = firstRecord(record, ["task", "data"]) ?? record;
283
- const id = firstString(nested, ["id", "task_id", "taskId"]);
284
- const identifier = firstString(nested, ["reference", "ref", "identifier", "key", "code", "number"]) || id;
101
+ const id = firstString(nested, ["id", "task_id", "taskId", "timeflowTaskId"]);
102
+ const identifier = firstString(nested, ["reference", "ref", "identifier", "key", "code", "number", "taskRef"]) || id;
285
103
  const title = firstString(nested, ["title", "name"]);
286
104
  if (!id && !identifier && !title) return null;
287
105
  const resolvedId = id || identifier;
@@ -292,21 +110,22 @@ function mapAbleTimeTask(raw, host) {
292
110
  identifier: resolvedIdentifier,
293
111
  title: title || resolvedIdentifier,
294
112
  url,
295
- description: firstString(nested, ["description", "body", "details"]) || void 0,
296
- state: firstString(nested, ["state", "board_state", "boardState", "status"]) || void 0,
297
- projectId: firstString(nested, ["project_id", "projectId"]) || firstString(asRecord2(nested.project), ["id"]) || void 0,
298
- categoryId: firstString(nested, ["category_id", "categoryId"]) || firstString(asRecord2(nested.category), ["id"]) || void 0,
113
+ description: firstString(nested, ["description", "body", "details", "descriptionSanitized"]) || textFromAbleTimeDoc(nested.description) || void 0,
114
+ state: firstString(nested, ["state", "board_state", "boardState", "status", "taskState"]) || void 0,
115
+ projectId: firstString(nested, ["project_id", "projectId"]) || firstString(asRecord(nested.project), ["id"]) || void 0,
116
+ categoryId: firstString(nested, ["category_id", "categoryId", "projectCategoryId"]) || firstString(asRecord(nested.category), ["id"]) || void 0,
299
117
  assignee: assigneeOf(nested),
300
118
  labels: labelsOf(nested),
301
119
  comments: commentsOf(nested),
302
- createdAt: firstString(nested, ["created_at", "createdAt", "created"]) || void 0,
120
+ createdAt: firstString(nested, ["created_at", "createdAt", "created", "dateCreated"]) || void 0,
303
121
  updatedAt: firstString(nested, [
304
122
  "updated_at",
305
123
  "updatedAt",
306
124
  "modified_at",
307
125
  "modifiedAt",
308
126
  "last_activity_at",
309
- "lastActivityAt"
127
+ "lastActivityAt",
128
+ "lastUpdate"
310
129
  ]) || void 0
311
130
  };
312
131
  }
@@ -337,16 +156,16 @@ function issueAttachmentForAbleTimeTask(task) {
337
156
  };
338
157
  }
339
158
  function mapProject(raw) {
340
- const record = asRecord2(raw);
159
+ const record = asRecord(raw);
341
160
  if (!record) return null;
342
- const id = firstString(record, ["id", "project_id"]);
343
- const name = firstString(record, ["name", "title"]);
161
+ const id = firstString(record, ["id", "project_id", "projectId"]);
162
+ const name = firstString(record, ["name", "title", "projectName"]);
344
163
  if (!id && !name) return null;
345
164
  const categories = asList(record.categories ?? record.category).map((item) => {
346
- const rec = asRecord2(item);
165
+ const rec = asRecord(item);
347
166
  if (!rec) return null;
348
- const categoryId = firstString(rec, ["id", "category_id"]);
349
- const categoryName = firstString(rec, ["name", "title"]);
167
+ const categoryId = firstString(rec, ["id", "category_id", "projectCategoryId", "categoryId"]);
168
+ const categoryName = firstString(rec, ["name", "title", "categoryName"]);
350
169
  if (!categoryId && !categoryName) return null;
351
170
  return { id: categoryId || categoryName, name: categoryName || categoryId };
352
171
  }).filter((item) => Boolean(item));
@@ -358,8 +177,8 @@ function isOpenTask(task) {
358
177
  }
359
178
  async function getAbleTimeOrientation(opts) {
360
179
  const raw = await callAbleTimeTool("orientation", {}, opts);
361
- const record = asRecord2(raw);
362
- const viewerRec = firstRecord(record, ["viewer", "user", "actor", "me", "person"]) ?? record;
180
+ const record = asRecord(raw);
181
+ const viewerRec = firstRecord(record, ["viewer", "principal", "user", "actor", "me", "person"]) ?? record;
363
182
  const viewer = {
364
183
  id: firstString(viewerRec, ["id", "user_id"]) || void 0,
365
184
  name: firstString(viewerRec, ["name", "display_name", "full_name"]) || firstString(record, ["name"]) || void 0
@@ -437,7 +256,7 @@ async function commentAbleTimeTask(input, opts) {
437
256
  { id, task_id: id, task: id, body, comment: body, text: body },
438
257
  opts
439
258
  );
440
- const rec = asRecord2(raw);
259
+ const rec = asRecord(raw);
441
260
  return {
442
261
  id: rec ? firstString(rec, ["id", "comment_id"]) || void 0 : void 0,
443
262
  body: rec ? firstString(rec, ["body", "text", "comment"]) || body : body
@@ -627,8 +446,7 @@ async function listAbleTimeAssignedIssues(opts) {
627
446
  };
628
447
  }
629
448
  async function verifyAbleTimeConnection(input) {
630
- const token = input.token.trim();
631
- if (!token) throw new Error("AbleTime personal access token is required");
449
+ const token = assertAbleTimeCredential(input.token);
632
450
  const host = input.host?.trim() || void 0;
633
451
  const orientation = await getAbleTimeOrientation({ token, host });
634
452
  saveAbleTimeConnection({
@@ -640,16 +458,6 @@ async function verifyAbleTimeConnection(input) {
640
458
  }
641
459
 
642
460
  export {
643
- DEFAULT_ABLETIME_HOST,
644
- ABLETIME_MCP_PATH,
645
- ABLETIME_MCP_PM_PATH,
646
- ABLETIME_PM_TOOL_NAMES,
647
- abletimeToolUsesPm,
648
- normalizeAbleTimeHost,
649
- abletimeMcpUrl,
650
- rewriteAbleTimeError,
651
- abletimeMcpRequest,
652
- callAbleTimeTool,
653
461
  taskUrl,
654
462
  mapAbleTimeTask,
655
463
  toAbleTimeIssueInfo,
@@ -328,6 +328,9 @@ function normalizeVault(raw) {
328
328
  if (typeof raw.abletimeAccessToken === "string" && raw.abletimeAccessToken.trim()) {
329
329
  out.abletimeAccessToken = raw.abletimeAccessToken.trim();
330
330
  }
331
+ if (typeof raw.abletimeRefreshToken === "string" && raw.abletimeRefreshToken.trim()) {
332
+ out.abletimeRefreshToken = raw.abletimeRefreshToken.trim();
333
+ }
331
334
  if (typeof raw.vercelToken === "string" && raw.vercelToken.trim()) {
332
335
  out.vercelToken = raw.vercelToken.trim();
333
336
  }
@@ -409,6 +412,7 @@ function emptyPublicIntegrations() {
409
412
  hasSlackAppToken: false,
410
413
  hasGithubPat: false,
411
414
  hasAbleTimeToken: false,
415
+ hasAbleTimeOAuth: false,
412
416
  hasVercelToken: false,
413
417
  hasSupabaseToken: false,
414
418
  hasPosthogToken: false,
@@ -433,6 +437,7 @@ function toPublicAppSettings(settings) {
433
437
  const slackAppToken = integrations.slackAppToken;
434
438
  const githubPat = integrations.githubPat;
435
439
  const abletimeAccessToken = integrations.abletimeAccessToken;
440
+ const abletimeRefreshToken = integrations.abletimeRefreshToken;
436
441
  const vercelToken = integrations.vercelToken;
437
442
  const supabaseAccessToken = integrations.supabaseAccessToken;
438
443
  const posthogPersonalApiKey = integrations.posthogPersonalApiKey;
@@ -445,6 +450,7 @@ function toPublicAppSettings(settings) {
445
450
  delete integrations.slackAppToken;
446
451
  delete integrations.githubPat;
447
452
  delete integrations.abletimeAccessToken;
453
+ delete integrations.abletimeRefreshToken;
448
454
  delete integrations.vercelToken;
449
455
  delete integrations.supabaseAccessToken;
450
456
  delete integrations.posthogPersonalApiKey;
@@ -459,7 +465,8 @@ function toPublicAppSettings(settings) {
459
465
  hasSlackClientSecret: Boolean(slackClientSecret?.trim()),
460
466
  hasSlackAppToken: Boolean(slackAppToken?.trim()),
461
467
  hasGithubPat: Boolean(githubPat?.trim()),
462
- hasAbleTimeToken: Boolean(abletimeAccessToken?.trim()),
468
+ hasAbleTimeToken: Boolean(abletimeAccessToken?.trim() || abletimeRefreshToken?.trim()),
469
+ hasAbleTimeOAuth: Boolean(abletimeRefreshToken?.trim()),
463
470
  hasVercelToken: Boolean(vercelToken?.trim()),
464
471
  hasSupabaseToken: Boolean(supabaseAccessToken?.trim()),
465
472
  hasPosthogToken: Boolean(posthogPersonalApiKey?.trim()),
@@ -605,6 +612,16 @@ function normalizeIntegrations(raw) {
605
612
  const token = source.abletimeAccessToken.trim();
606
613
  if (token) out.abletimeAccessToken = token;
607
614
  }
615
+ if (typeof source.abletimeRefreshToken === "string") {
616
+ const token = source.abletimeRefreshToken.trim();
617
+ if (token) out.abletimeRefreshToken = token;
618
+ }
619
+ if (typeof source.abletimeTokenExpiresAt === "number" && Number.isFinite(source.abletimeTokenExpiresAt)) {
620
+ out.abletimeTokenExpiresAt = source.abletimeTokenExpiresAt;
621
+ } else if (typeof source.abletimeTokenExpiresAt === "string") {
622
+ const n = Number(source.abletimeTokenExpiresAt);
623
+ if (Number.isFinite(n)) out.abletimeTokenExpiresAt = n;
624
+ }
608
625
  if (typeof source.abletimeHost === "string") {
609
626
  const host = source.abletimeHost.trim();
610
627
  if (host) out.abletimeHost = host;
@@ -893,7 +910,7 @@ function readSettingsFile() {
893
910
  }
894
911
  function diskHoldsSecrets(disk) {
895
912
  return Boolean(
896
- 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
913
+ 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
897
914
  );
898
915
  }
899
916
  function mergeVault(disk) {
@@ -924,6 +941,9 @@ function mergeVault(disk) {
924
941
  if (vault.abletimeAccessToken && !integrations.abletimeAccessToken) {
925
942
  integrations.abletimeAccessToken = vault.abletimeAccessToken;
926
943
  }
944
+ if (vault.abletimeRefreshToken && !integrations.abletimeRefreshToken) {
945
+ integrations.abletimeRefreshToken = vault.abletimeRefreshToken;
946
+ }
927
947
  if (vault.vercelToken && !integrations.vercelToken) {
928
948
  integrations.vercelToken = vault.vercelToken;
929
949
  }
@@ -948,6 +968,7 @@ function persistSplit(settings) {
948
968
  const slackAppToken = integrations.slackAppToken;
949
969
  const githubPat = integrations.githubPat;
950
970
  const abletimeAccessToken = integrations.abletimeAccessToken;
971
+ const abletimeRefreshToken = integrations.abletimeRefreshToken;
951
972
  const vercelToken = integrations.vercelToken;
952
973
  const supabaseAccessToken = integrations.supabaseAccessToken;
953
974
  const posthogPersonalApiKey = integrations.posthogPersonalApiKey;
@@ -960,6 +981,7 @@ function persistSplit(settings) {
960
981
  delete integrations.slackAppToken;
961
982
  delete integrations.githubPat;
962
983
  delete integrations.abletimeAccessToken;
984
+ delete integrations.abletimeRefreshToken;
963
985
  delete integrations.vercelToken;
964
986
  delete integrations.supabaseAccessToken;
965
987
  delete integrations.posthogPersonalApiKey;
@@ -978,6 +1000,7 @@ function persistSplit(settings) {
978
1000
  slackAppToken,
979
1001
  githubPat,
980
1002
  abletimeAccessToken,
1003
+ abletimeRefreshToken,
981
1004
  vercelToken,
982
1005
  supabaseAccessToken,
983
1006
  posthogPersonalApiKey,
@@ -1164,6 +1187,8 @@ function updateIntegrationsSettings(patch) {
1164
1187
  if ("abletimeAccessToken" in patch) {
1165
1188
  if (patch.abletimeAccessToken == null || patch.abletimeAccessToken.trim() === "") {
1166
1189
  delete integrations.abletimeAccessToken;
1190
+ delete integrations.abletimeRefreshToken;
1191
+ delete integrations.abletimeTokenExpiresAt;
1167
1192
  delete integrations.abletimeViewerName;
1168
1193
  } else {
1169
1194
  integrations.abletimeAccessToken = patch.abletimeAccessToken.trim();
@@ -1516,7 +1541,9 @@ function disconnectLinearConnection() {
1516
1541
  return saveAppSettings({ ...current, integrations });
1517
1542
  }
1518
1543
  function isAbleTimeConnected(settings = loadAppSettings()) {
1519
- return Boolean(settings.integrations.abletimeAccessToken?.trim());
1544
+ return Boolean(
1545
+ settings.integrations.abletimeAccessToken?.trim() || settings.integrations.abletimeRefreshToken?.trim()
1546
+ );
1520
1547
  }
1521
1548
  function getAbleTimeAccessToken(settings = loadAppSettings()) {
1522
1549
  const token = settings.integrations.abletimeAccessToken?.trim();
@@ -1529,6 +1556,12 @@ function saveAbleTimeConnection(input) {
1529
1556
  const current = loadAppSettings();
1530
1557
  const integrations = { ...current.integrations };
1531
1558
  integrations.abletimeAccessToken = input.accessToken.trim();
1559
+ if (input.refreshToken?.trim()) {
1560
+ integrations.abletimeRefreshToken = input.refreshToken.trim();
1561
+ }
1562
+ if (typeof input.expiresIn === "number" && Number.isFinite(input.expiresIn)) {
1563
+ integrations.abletimeTokenExpiresAt = Date.now() + Math.max(0, input.expiresIn) * 1e3;
1564
+ }
1532
1565
  if (input.host?.trim()) {
1533
1566
  integrations.abletimeHost = input.host.trim();
1534
1567
  }
@@ -1541,6 +1574,8 @@ function disconnectAbleTimeConnection() {
1541
1574
  const current = loadAppSettings();
1542
1575
  const integrations = { ...current.integrations };
1543
1576
  delete integrations.abletimeAccessToken;
1577
+ delete integrations.abletimeRefreshToken;
1578
+ delete integrations.abletimeTokenExpiresAt;
1544
1579
  delete integrations.abletimeViewerName;
1545
1580
  return saveAppSettings({ ...current, integrations });
1546
1581
  }
@@ -22,7 +22,7 @@ import {
22
22
  import {
23
23
  getGithubGitAuthMode,
24
24
  getGithubPat
25
- } from "./chunk-ISV5IFH3.js";
25
+ } from "./chunk-X743YN5Y.js";
26
26
  import {
27
27
  worktreesRoot
28
28
  } from "./chunk-EQNRWDLN.js";
@@ -2117,26 +2117,21 @@ function resolvePrSelectors(thread) {
2117
2117
  function resolvePrSelector(thread) {
2118
2118
  return resolvePrSelectors(thread)[0] ?? null;
2119
2119
  }
2120
+ function connectedPrSelectors(thread, currentHeadPrUrl) {
2121
+ const out = [];
2122
+ const push = (value) => {
2123
+ const v = value?.trim();
2124
+ if (!v || out.includes(v)) return;
2125
+ out.push(v);
2126
+ };
2127
+ push(currentHeadPrUrl);
2128
+ for (const selector of resolvePrSelectors(thread)) push(selector);
2129
+ return out;
2130
+ }
2120
2131
  async function getPrForHeadBranch(repoPath, branch) {
2121
2132
  const head = normalizeGitBranchName(branch);
2122
2133
  if (!head || isDefaultishSourceRef(head)) return null;
2123
2134
  const slug = await resolveGithubRepoSlug(repoPath);
2124
- const viewArgs = [
2125
- "pr",
2126
- "view",
2127
- head,
2128
- "--json",
2129
- "number,title,headRefName,url,isCrossRepository,author"
2130
- ];
2131
- if (slug) viewArgs.push("--repo", slug);
2132
- const viewed = await gh(viewArgs, repoPath, { reject: false });
2133
- if (viewed.exitCode === 0 && viewed.stdout.trim()) {
2134
- try {
2135
- return JSON.parse(viewed.stdout);
2136
- } catch {
2137
- return null;
2138
- }
2139
- }
2140
2135
  const listHead = slug ? ghHeadRef(slug, head) : head;
2141
2136
  const listArgs = [
2142
2137
  "pr",
@@ -2152,10 +2147,28 @@ async function getPrForHeadBranch(repoPath, branch) {
2152
2147
  ];
2153
2148
  if (slug) listArgs.push("--repo", slug);
2154
2149
  const listed = await gh(listArgs, repoPath, { reject: false });
2155
- if (listed.exitCode !== 0 || !listed.stdout.trim()) return null;
2150
+ if (listed.exitCode === 0 && listed.stdout.trim()) {
2151
+ try {
2152
+ const rows = JSON.parse(listed.stdout);
2153
+ if (rows[0]) return rows[0];
2154
+ } catch {
2155
+ }
2156
+ }
2157
+ const viewArgs = [
2158
+ "pr",
2159
+ "view",
2160
+ head,
2161
+ "--json",
2162
+ "number,title,headRefName,url,isCrossRepository,author,state"
2163
+ ];
2164
+ if (slug) viewArgs.push("--repo", slug);
2165
+ const viewed = await gh(viewArgs, repoPath, { reject: false });
2166
+ if (viewed.exitCode !== 0 || !viewed.stdout.trim()) return null;
2156
2167
  try {
2157
- const rows = JSON.parse(listed.stdout);
2158
- return rows[0] ?? null;
2168
+ const info = JSON.parse(viewed.stdout);
2169
+ const state = (info.state ?? "OPEN").trim().toUpperCase();
2170
+ if (state !== "OPEN") return null;
2171
+ return info;
2159
2172
  } catch {
2160
2173
  return null;
2161
2174
  }
@@ -3251,6 +3264,7 @@ export {
3251
3264
  isDefaultishSourceRef,
3252
3265
  resolvePrSelectors,
3253
3266
  resolvePrSelector,
3267
+ connectedPrSelectors,
3254
3268
  getPrForHeadBranch,
3255
3269
  detectLocalMergeConflicts,
3256
3270
  getPrChecks,