@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.
- package/dist/{abletime-KGPD3UUJ.js → abletime-S3UHQZVW.js} +4 -2
- package/dist/{abletime-345SE3B4.js → abletime-TONBQP4O.js} +4 -2
- package/dist/abletime-oauth-AVCEO2DY.js +314 -0
- package/dist/abletime-oauth-SD4SZY5U.js +52 -0
- package/dist/abletime-rest-6ODSXV7G.js +25 -0
- package/dist/abletime-rest-J2OBB4VA.js +24 -0
- package/dist/agents/cursor-runner.js +1 -1
- package/dist/{agents-573ZDIX6.js → agents-67K4BQFJ.js} +4 -4
- package/dist/{agents-OXX3ONVY.js → agents-BVOJNHV6.js} +5 -5
- package/dist/{app-settings-V6IHESEQ.js → app-settings-LGHMNHSP.js} +1 -1
- package/dist/{app-settings-AXCURTZJ.js → app-settings-YEPF46IJ.js} +1 -1
- package/dist/chunk-3K257PCS.js +393 -0
- package/dist/{chunk-54RSAMBH.js → chunk-5SMS6QTO.js} +2 -2
- package/dist/chunk-6Q7DFVDQ.js +309 -0
- package/dist/chunk-CYLDJWRK.js +391 -0
- package/dist/{chunk-PGQGP4FD.js → chunk-FO2NTJ7L.js} +37 -219
- package/dist/{chunk-7NKY2OX7.js → chunk-FO45DMJ5.js} +125 -109
- package/dist/{chunk-CQ5R3VPP.js → chunk-KZ7PI7FE.js} +18 -0
- package/dist/{chunk-7AHAABKN.js → chunk-QAVWH2RQ.js} +37 -229
- package/dist/{chunk-CXFW5LIH.js → chunk-QFBBUFD4.js} +38 -3
- package/dist/{chunk-RPX6VDW7.js → chunk-QUW5D62C.js} +34 -20
- package/dist/chunk-SGUVAK66.js +262 -0
- package/dist/{chunk-27GGXX27.js → chunk-ULZLWQBS.js} +128 -112
- package/dist/{chunk-LJHC62LZ.js → chunk-VIPA75ZA.js} +2 -2
- package/dist/chunk-W6VM36YO.js +272 -0
- package/dist/{chunk-NIVF5NDN.js → chunk-WF33LOP4.js} +44 -6
- package/dist/{chunk-STPZQR5B.js → chunk-WVATAIF4.js} +34 -20
- package/dist/{chunk-ISV5IFH3.js → chunk-X743YN5Y.js} +38 -3
- package/dist/{chunk-A2TAKYSG.js → chunk-YOSTWSNC.js} +27 -7
- package/dist/{coordinator-prompt-BH4EHFOO.js → coordinator-prompt-NPT555BO.js} +3 -3
- package/dist/{coordinator-prompt-TY5FHAXC.js → coordinator-prompt-WEPXXLLX.js} +3 -3
- package/dist/{global-workspace-NLLAPQJI.js → global-workspace-2I2T7FL5.js} +3 -3
- package/dist/{global-workspace-5AZEEQRU.js → global-workspace-NSILADOM.js} +3 -3
- package/dist/index.cjs +1137 -213
- package/dist/index.d.cts +87 -13
- package/dist/index.d.ts +87 -13
- package/dist/index.js +94 -19
- package/dist/mcp/run-stdio.cjs +1023 -180
- package/dist/mcp/run-stdio.js +22 -20
- package/dist/{orchestrator-JNSQDRZI.js → orchestrator-CMAVNELS.js} +6 -6
- package/dist/{orchestrator-NCYKEI3E.js → orchestrator-I7GFXLHF.js} +6 -6
- package/dist/{workspaces-RP76JH3J.js → workspaces-BOC6M7BW.js} +3 -3
- package/dist/{workspaces-PEWYWVHZ.js → workspaces-RFGZBUAR.js} +3 -3
- package/dist/{worktree-72TZWUSH.js → worktree-G3ZLM4DC.js} +4 -2
- package/dist/{worktree-ZQEUKSNE.js → worktree-WSUQMMK4.js} +4 -2
- package/package.json +1 -1
|
@@ -1,198 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
} from "./chunk-
|
|
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-
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
68
|
+
const rec = asRecord(item);
|
|
251
69
|
if (!rec) continue;
|
|
252
|
-
const body = firstString(rec, ["body", "text", "comment", "
|
|
70
|
+
const body = firstString(rec, ["body", "text", "comment", "message", "contentSanitized"]) || textFromAbleTimeDoc(rec.content);
|
|
253
71
|
if (!body) continue;
|
|
254
|
-
const user = firstString(
|
|
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 =
|
|
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(
|
|
298
|
-
categoryId: firstString(nested, ["category_id", "categoryId"]) || firstString(
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
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(
|
|
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-
|
|
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
|
|
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
|
|
2158
|
-
|
|
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,
|