@tarcisiopgs/lisa 1.26.2 → 1.27.0
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/README.md +17 -5
- package/dist/{chunk-CDI22S63.js → chunk-3EOEDL3T.js} +2 -2
- package/dist/{chunk-AGYOJQBR.js → chunk-JRCAT5PI.js} +20 -8
- package/dist/{chunk-235DMOXJ.js → chunk-SSSFEMF4.js} +143 -110
- package/dist/{detection-OWEDBW7B.js → detection-FUU6FUZZ.js} +1 -1
- package/dist/{guardrails-6IG2C4KJ.js → guardrails-IX3VVUO5.js} +1 -1
- package/dist/index.js +2006 -1466
- package/dist/{kanban-PSOTFEQI.js → kanban-MHKZIHS3.js} +55 -40
- package/package.json +5 -4
|
@@ -16,14 +16,18 @@ import { dirname } from "path";
|
|
|
16
16
|
import pc from "picocolors";
|
|
17
17
|
var logFilePath = null;
|
|
18
18
|
var outputMode = "default";
|
|
19
|
+
var logLevel = "default";
|
|
19
20
|
function setOutputMode(mode) {
|
|
20
21
|
outputMode = mode;
|
|
21
22
|
}
|
|
22
23
|
function getOutputMode() {
|
|
23
24
|
return outputMode;
|
|
24
25
|
}
|
|
26
|
+
function setLogLevel(level) {
|
|
27
|
+
logLevel = level;
|
|
28
|
+
}
|
|
25
29
|
function shouldPrintToConsole() {
|
|
26
|
-
return outputMode !== "tui";
|
|
30
|
+
return outputMode !== "tui" && logLevel !== "quiet";
|
|
27
31
|
}
|
|
28
32
|
function initLogFile(path) {
|
|
29
33
|
const dir = dirname(path);
|
|
@@ -45,7 +49,7 @@ function writeToFile(level, message) {
|
|
|
45
49
|
}
|
|
46
50
|
function log(message) {
|
|
47
51
|
if (shouldPrintToConsole()) {
|
|
48
|
-
console.
|
|
52
|
+
console.error(`${pc.cyan("[lisa]")} ${pc.dim(timestamp())} ${message}`);
|
|
49
53
|
}
|
|
50
54
|
writeToFile("info", message);
|
|
51
55
|
}
|
|
@@ -63,7 +67,7 @@ function error(message) {
|
|
|
63
67
|
}
|
|
64
68
|
function ok(message) {
|
|
65
69
|
if (shouldPrintToConsole()) {
|
|
66
|
-
console.
|
|
70
|
+
console.error(`${pc.green("[lisa]")} ${pc.dim(timestamp())} ${message}`);
|
|
67
71
|
}
|
|
68
72
|
writeToFile("ok", message);
|
|
69
73
|
}
|
|
@@ -71,34 +75,83 @@ function divider(session) {
|
|
|
71
75
|
log(`${"\u2501".repeat(3)} Session ${session} ${"\u2501".repeat(3)}`);
|
|
72
76
|
}
|
|
73
77
|
function banner() {
|
|
74
|
-
if (outputMode !== "default") return;
|
|
78
|
+
if (outputMode !== "default" || logLevel === "quiet") return;
|
|
75
79
|
const title = " lisa \u266A autonomous issue resolver ";
|
|
76
80
|
const border = "\u2500".repeat(title.length);
|
|
77
|
-
console.
|
|
81
|
+
console.error(pc.yellow(`
|
|
78
82
|
\u250C${border}\u2510`));
|
|
79
|
-
console.
|
|
80
|
-
console.
|
|
83
|
+
console.error(pc.yellow(` \u2502`) + pc.bold(pc.white(title)) + pc.yellow("\u2502"));
|
|
84
|
+
console.error(pc.yellow(` \u2514${border}\u2518
|
|
81
85
|
`));
|
|
82
86
|
}
|
|
83
87
|
function updateNotice(update) {
|
|
84
|
-
if (outputMode !== "default") return;
|
|
88
|
+
if (outputMode !== "default" || logLevel === "quiet") return;
|
|
85
89
|
const msg = `Update available ${pc.dim(update.currentVersion)} \u2192 ${pc.green(pc.bold(update.latestVersion))}`;
|
|
86
90
|
const cmd = `Run ${pc.cyan("npm i -g @tarcisiopgs/lisa")} to update`;
|
|
87
91
|
const lines = [msg, cmd];
|
|
88
92
|
const strip = (s) => s.replace(/\x1b\[[0-9;]*m/g, "");
|
|
89
93
|
const maxLen = Math.max(...lines.map((l) => strip(l).length));
|
|
90
94
|
const pad = (s) => s + " ".repeat(maxLen - strip(s).length);
|
|
91
|
-
console.
|
|
95
|
+
console.error(pc.yellow(` \u250C${"\u2500".repeat(maxLen + 2)}\u2510`));
|
|
92
96
|
for (const line of lines) {
|
|
93
|
-
console.
|
|
97
|
+
console.error(pc.yellow(" \u2502 ") + pad(line) + pc.yellow(" \u2502"));
|
|
94
98
|
}
|
|
95
|
-
console.
|
|
99
|
+
console.error(pc.yellow(` \u2514${"\u2500".repeat(maxLen + 2)}\u2518
|
|
96
100
|
`));
|
|
97
101
|
}
|
|
98
102
|
|
|
99
|
-
// src/sources/
|
|
100
|
-
var API_URL = "https://api.github.com";
|
|
103
|
+
// src/sources/base.ts
|
|
101
104
|
var REQUEST_TIMEOUT_MS = 3e4;
|
|
105
|
+
function normalizeLabels(config) {
|
|
106
|
+
return Array.isArray(config.label) ? config.label : config.label ? [config.label] : [];
|
|
107
|
+
}
|
|
108
|
+
function createApiClient(baseUrl, getHeaders, name) {
|
|
109
|
+
async function request(method, path, body) {
|
|
110
|
+
const url = `${baseUrl}${path}`;
|
|
111
|
+
const headers = {
|
|
112
|
+
...await getHeaders(),
|
|
113
|
+
"Content-Type": "application/json"
|
|
114
|
+
};
|
|
115
|
+
const res = await fetch(url, {
|
|
116
|
+
method,
|
|
117
|
+
headers,
|
|
118
|
+
body: body !== void 0 ? JSON.stringify(body) : void 0,
|
|
119
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
120
|
+
});
|
|
121
|
+
if (!res.ok) {
|
|
122
|
+
const text = await res.text();
|
|
123
|
+
throw new Error(`${name} API error (${res.status}): ${text}`);
|
|
124
|
+
}
|
|
125
|
+
if (method === "DELETE" || res.status === 204) return void 0;
|
|
126
|
+
return await res.json();
|
|
127
|
+
}
|
|
128
|
+
return {
|
|
129
|
+
get: (path) => request("GET", path),
|
|
130
|
+
post: (path, body) => request("POST", path, body),
|
|
131
|
+
put: (path, body) => request("PUT", path, body),
|
|
132
|
+
patch: (path, body) => request("PATCH", path, body),
|
|
133
|
+
delete: (path) => request("DELETE", path),
|
|
134
|
+
/** Raw request for non-JSON bodies (e.g. Trello form-encoded). */
|
|
135
|
+
raw: async (method, path, init) => {
|
|
136
|
+
const url = `${baseUrl}${path}`;
|
|
137
|
+
const headers = await getHeaders();
|
|
138
|
+
const res = await fetch(url, {
|
|
139
|
+
method,
|
|
140
|
+
headers: { ...headers, ...init?.headers },
|
|
141
|
+
body: init?.body,
|
|
142
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
143
|
+
});
|
|
144
|
+
if (!res.ok) {
|
|
145
|
+
const text = await res.text();
|
|
146
|
+
throw new Error(`${name} API error (${res.status}): ${text}`);
|
|
147
|
+
}
|
|
148
|
+
if (method === "DELETE" || res.status === 204) return void 0;
|
|
149
|
+
return await res.json();
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// src/sources/github-issues.ts
|
|
102
155
|
var PRIORITY_LABELS = ["p1", "p2", "p3"];
|
|
103
156
|
var DEPENDENCY_PATTERN = /(?:depends\s+on|blocked\s+by)\s+#(\d+)/gi;
|
|
104
157
|
async function getToken() {
|
|
@@ -118,36 +171,12 @@ async function getAuthHeaders() {
|
|
|
118
171
|
"X-GitHub-Api-Version": "2022-11-28"
|
|
119
172
|
};
|
|
120
173
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
"Content-Type": "application/json"
|
|
126
|
-
};
|
|
127
|
-
const res = await fetch(url, {
|
|
128
|
-
method,
|
|
129
|
-
headers,
|
|
130
|
-
body: body !== void 0 ? JSON.stringify(body) : void 0,
|
|
131
|
-
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS)
|
|
132
|
-
});
|
|
133
|
-
if (!res.ok) {
|
|
134
|
-
const text = await res.text();
|
|
135
|
-
throw new Error(`GitHub API error (${res.status}): ${text}`);
|
|
174
|
+
var _api;
|
|
175
|
+
function api() {
|
|
176
|
+
if (!_api) {
|
|
177
|
+
_api = createApiClient("https://api.github.com", getAuthHeaders, "GitHub");
|
|
136
178
|
}
|
|
137
|
-
|
|
138
|
-
return await res.json();
|
|
139
|
-
}
|
|
140
|
-
async function githubGet(path) {
|
|
141
|
-
return githubFetch("GET", path);
|
|
142
|
-
}
|
|
143
|
-
async function githubPost(path, body) {
|
|
144
|
-
return githubFetch("POST", path, body);
|
|
145
|
-
}
|
|
146
|
-
async function githubPatch(path, body) {
|
|
147
|
-
return githubFetch("PATCH", path, body);
|
|
148
|
-
}
|
|
149
|
-
async function githubDelete(path) {
|
|
150
|
-
await githubFetch("DELETE", path);
|
|
179
|
+
return _api;
|
|
151
180
|
}
|
|
152
181
|
function parseGitHubPrUrl(url) {
|
|
153
182
|
const match = url.match(/github\.com\/([^/]+)\/([^/]+)\/pull\/(\d+)/);
|
|
@@ -160,7 +189,7 @@ async function checkPrMerged(prUrl) {
|
|
|
160
189
|
const parsed = parseGitHubPrUrl(prUrl);
|
|
161
190
|
if (!parsed) return false;
|
|
162
191
|
try {
|
|
163
|
-
const pr = await
|
|
192
|
+
const pr = await api().get(
|
|
164
193
|
`/repos/${parsed.owner}/${parsed.repo}/pulls/${parsed.number}`
|
|
165
194
|
);
|
|
166
195
|
return pr.merged === true;
|
|
@@ -216,10 +245,10 @@ var GitHubIssuesSource = class {
|
|
|
216
245
|
const { owner, repo } = parseOwnerRepo(config.scope);
|
|
217
246
|
const validStates = ["open", "closed", "all"];
|
|
218
247
|
const isOrphanDetection = !!config.pick_from && !validStates.includes(config.pick_from);
|
|
219
|
-
const filterLabels = isOrphanDetection ? [config.pick_from] :
|
|
248
|
+
const filterLabels = isOrphanDetection ? [config.pick_from] : normalizeLabels(config);
|
|
220
249
|
const label = filterLabels.map((l) => encodeURIComponent(l)).join(",");
|
|
221
250
|
const path = `/repos/${owner}/${repo}/issues?labels=${label}&state=open&sort=created&direction=asc&per_page=100`;
|
|
222
|
-
const issues = (await
|
|
251
|
+
const issues = (await api().get(path)).filter((i) => !i.pull_request);
|
|
223
252
|
if (issues.length === 0) return null;
|
|
224
253
|
const unblocked = [];
|
|
225
254
|
const blocked = [];
|
|
@@ -234,7 +263,7 @@ var GitHubIssuesSource = class {
|
|
|
234
263
|
const closedBlockers = [];
|
|
235
264
|
for (const depNum of depNumbers) {
|
|
236
265
|
try {
|
|
237
|
-
const dep = await
|
|
266
|
+
const dep = await api().get(`/repos/${owner}/${repo}/issues/${depNum}`);
|
|
238
267
|
if (!dep.state || dep.state === "open") {
|
|
239
268
|
activeBlockers.push(depNum);
|
|
240
269
|
} else {
|
|
@@ -284,14 +313,15 @@ var GitHubIssuesSource = class {
|
|
|
284
313
|
async fetchIssueById(id) {
|
|
285
314
|
const ref = parseGitHubIssueNumber(id);
|
|
286
315
|
try {
|
|
287
|
-
const issue = await
|
|
316
|
+
const issue = await api().get(
|
|
288
317
|
`/repos/${ref.owner}/${ref.repo}/issues/${ref.number}`
|
|
289
318
|
);
|
|
290
319
|
return {
|
|
291
320
|
id: makeIssueId(ref.owner, ref.repo, issue.number),
|
|
292
321
|
title: issue.title,
|
|
293
322
|
description: issue.body ?? "",
|
|
294
|
-
url: issue.html_url
|
|
323
|
+
url: issue.html_url,
|
|
324
|
+
status: issue.state
|
|
295
325
|
};
|
|
296
326
|
} catch {
|
|
297
327
|
return null;
|
|
@@ -300,15 +330,15 @@ var GitHubIssuesSource = class {
|
|
|
300
330
|
async updateStatus(issueId, labelToAdd, config) {
|
|
301
331
|
const ref = parseGitHubIssueNumber(issueId);
|
|
302
332
|
if (config && config.in_progress !== config.pick_from) {
|
|
303
|
-
const filterLabels =
|
|
333
|
+
const filterLabels = normalizeLabels(config);
|
|
304
334
|
const isMovingToInProgress = labelToAdd === config.in_progress;
|
|
305
335
|
if (isMovingToInProgress) {
|
|
306
|
-
await
|
|
336
|
+
await api().post(`/repos/${ref.owner}/${ref.repo}/issues/${ref.number}/labels`, {
|
|
307
337
|
labels: [labelToAdd]
|
|
308
338
|
});
|
|
309
339
|
for (const label of filterLabels) {
|
|
310
340
|
try {
|
|
311
|
-
await
|
|
341
|
+
await api().delete(
|
|
312
342
|
`/repos/${ref.owner}/${ref.repo}/issues/${ref.number}/labels/${encodeURIComponent(label)}`
|
|
313
343
|
);
|
|
314
344
|
} catch {
|
|
@@ -316,30 +346,30 @@ var GitHubIssuesSource = class {
|
|
|
316
346
|
}
|
|
317
347
|
return;
|
|
318
348
|
}
|
|
319
|
-
await
|
|
349
|
+
await api().post(`/repos/${ref.owner}/${ref.repo}/issues/${ref.number}/labels`, {
|
|
320
350
|
labels: filterLabels
|
|
321
351
|
});
|
|
322
352
|
try {
|
|
323
|
-
await
|
|
353
|
+
await api().delete(
|
|
324
354
|
`/repos/${ref.owner}/${ref.repo}/issues/${ref.number}/labels/${encodeURIComponent(config.in_progress)}`
|
|
325
355
|
);
|
|
326
356
|
} catch {
|
|
327
357
|
}
|
|
328
358
|
return;
|
|
329
359
|
}
|
|
330
|
-
await
|
|
360
|
+
await api().post(`/repos/${ref.owner}/${ref.repo}/issues/${ref.number}/labels`, {
|
|
331
361
|
labels: [labelToAdd]
|
|
332
362
|
});
|
|
333
363
|
}
|
|
334
364
|
async attachPullRequest(issueId, prUrl) {
|
|
335
365
|
const ref = parseGitHubIssueNumber(issueId);
|
|
336
|
-
await
|
|
366
|
+
await api().post(`/repos/${ref.owner}/${ref.repo}/issues/${ref.number}/comments`, {
|
|
337
367
|
body: `Pull request: ${prUrl}`
|
|
338
368
|
});
|
|
339
369
|
}
|
|
340
370
|
async completeIssue(issueId, _status, labelToRemove, config) {
|
|
341
371
|
const ref = parseGitHubIssueNumber(issueId);
|
|
342
|
-
await
|
|
372
|
+
await api().patch(`/repos/${ref.owner}/${ref.repo}/issues/${ref.number}`, {
|
|
343
373
|
state: "closed"
|
|
344
374
|
});
|
|
345
375
|
if (labelToRemove) {
|
|
@@ -347,7 +377,7 @@ var GitHubIssuesSource = class {
|
|
|
347
377
|
}
|
|
348
378
|
if (config && config.in_progress !== config.pick_from) {
|
|
349
379
|
try {
|
|
350
|
-
await
|
|
380
|
+
await api().delete(
|
|
351
381
|
`/repos/${ref.owner}/${ref.repo}/issues/${ref.number}/labels/${encodeURIComponent(config.in_progress)}`
|
|
352
382
|
);
|
|
353
383
|
} catch {
|
|
@@ -356,10 +386,10 @@ var GitHubIssuesSource = class {
|
|
|
356
386
|
}
|
|
357
387
|
async listIssues(config) {
|
|
358
388
|
const { owner, repo } = parseOwnerRepo(config.scope);
|
|
359
|
-
const labels =
|
|
389
|
+
const labels = normalizeLabels(config);
|
|
360
390
|
const label = labels.map((l) => encodeURIComponent(l)).join(",");
|
|
361
391
|
const path = `/repos/${owner}/${repo}/issues?labels=${label}&state=open&sort=created&direction=asc&per_page=100`;
|
|
362
|
-
const issues = (await
|
|
392
|
+
const issues = (await api().get(path)).filter((i) => !i.pull_request);
|
|
363
393
|
return issues.map((issue) => ({
|
|
364
394
|
id: makeIssueId(owner, repo, issue.number),
|
|
365
395
|
title: issue.title,
|
|
@@ -372,7 +402,7 @@ var GitHubIssuesSource = class {
|
|
|
372
402
|
const results = [];
|
|
373
403
|
let page = 1;
|
|
374
404
|
while (true) {
|
|
375
|
-
const labels = await
|
|
405
|
+
const labels = await api().get(
|
|
376
406
|
`/repos/${owner}/${repo}/labels?per_page=100&page=${page}`
|
|
377
407
|
);
|
|
378
408
|
for (const l of labels) {
|
|
@@ -389,7 +419,7 @@ var GitHubIssuesSource = class {
|
|
|
389
419
|
async removeLabel(issueId, labelToRemove) {
|
|
390
420
|
const ref = parseGitHubIssueNumber(issueId);
|
|
391
421
|
try {
|
|
392
|
-
await
|
|
422
|
+
await api().delete(
|
|
393
423
|
`/repos/${ref.owner}/${ref.repo}/issues/${ref.number}/labels/${encodeURIComponent(labelToRemove)}`
|
|
394
424
|
);
|
|
395
425
|
} catch {
|
|
@@ -399,7 +429,6 @@ var GitHubIssuesSource = class {
|
|
|
399
429
|
|
|
400
430
|
// src/sources/gitlab-issues.ts
|
|
401
431
|
var DEFAULT_BASE_URL = "https://gitlab.com";
|
|
402
|
-
var REQUEST_TIMEOUT_MS2 = 3e4;
|
|
403
432
|
var PRIORITY_LABELS2 = ["p1", "p2", "p3"];
|
|
404
433
|
function getBaseUrl() {
|
|
405
434
|
return (process.env.GITLAB_BASE_URL ?? DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
@@ -409,33 +438,12 @@ function getAuthHeaders2() {
|
|
|
409
438
|
if (!token) throw new Error("GITLAB_TOKEN must be set");
|
|
410
439
|
return { "PRIVATE-TOKEN": token };
|
|
411
440
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
"Content-Type": "application/json"
|
|
417
|
-
};
|
|
418
|
-
const res = await fetch(url, {
|
|
419
|
-
method,
|
|
420
|
-
headers,
|
|
421
|
-
body: body !== void 0 ? JSON.stringify(body) : void 0,
|
|
422
|
-
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS2)
|
|
423
|
-
});
|
|
424
|
-
if (!res.ok) {
|
|
425
|
-
const text = await res.text();
|
|
426
|
-
throw new Error(`GitLab API error (${res.status}): ${text}`);
|
|
441
|
+
var _api2;
|
|
442
|
+
function api2() {
|
|
443
|
+
if (!_api2) {
|
|
444
|
+
_api2 = createApiClient(`${getBaseUrl()}/api/v4`, getAuthHeaders2, "GitLab");
|
|
427
445
|
}
|
|
428
|
-
|
|
429
|
-
return await res.json();
|
|
430
|
-
}
|
|
431
|
-
async function gitlabGet(path) {
|
|
432
|
-
return gitlabFetch("GET", path);
|
|
433
|
-
}
|
|
434
|
-
async function gitlabPost(path, body) {
|
|
435
|
-
return gitlabFetch("POST", path, body);
|
|
436
|
-
}
|
|
437
|
-
async function gitlabPut(path, body) {
|
|
438
|
-
return gitlabFetch("PUT", path, body);
|
|
446
|
+
return _api2;
|
|
439
447
|
}
|
|
440
448
|
function parseGitLabMrUrl(url) {
|
|
441
449
|
const match = url.match(/gitlab(?:\.com|[^/]*)\/(.+?)\/-\/merge_requests\/(\d+)/);
|
|
@@ -449,7 +457,7 @@ async function checkPrMerged2(prUrl) {
|
|
|
449
457
|
if (!parsed) return false;
|
|
450
458
|
try {
|
|
451
459
|
const encodedProject = parseGitLabProject(parsed.project);
|
|
452
|
-
const mr = await
|
|
460
|
+
const mr = await api2().get(
|
|
453
461
|
`/projects/${encodedProject}/merge_requests/${parsed.iid}`
|
|
454
462
|
);
|
|
455
463
|
return mr.state === "merged";
|
|
@@ -480,15 +488,15 @@ var GitLabIssuesSource = class {
|
|
|
480
488
|
const project = parseGitLabProject(config.scope);
|
|
481
489
|
const validStates = ["opened", "closed", "all"];
|
|
482
490
|
const isOrphanDetection = !!config.pick_from && !validStates.includes(config.pick_from);
|
|
483
|
-
const filterLabels = isOrphanDetection ? [config.pick_from] :
|
|
491
|
+
const filterLabels = isOrphanDetection ? [config.pick_from] : normalizeLabels(config);
|
|
484
492
|
const label = filterLabels.map((l) => encodeURIComponent(l)).join(",");
|
|
485
493
|
const path = `/projects/${project}/issues?labels=${label}&state=opened&per_page=100`;
|
|
486
|
-
const issues = await
|
|
494
|
+
const issues = await api2().get(path);
|
|
487
495
|
if (issues.length === 0) return null;
|
|
488
496
|
const unblocked = [];
|
|
489
497
|
const blocked = [];
|
|
490
498
|
for (const issue2 of issues) {
|
|
491
|
-
const links = await
|
|
499
|
+
const links = await api2().get(
|
|
492
500
|
`/projects/${project}/issues/${issue2.iid}/links`
|
|
493
501
|
);
|
|
494
502
|
const activeBlockers = links.filter((link) => {
|
|
@@ -533,12 +541,13 @@ var GitLabIssuesSource = class {
|
|
|
533
541
|
const ref = parseGitLabIssueRef(id);
|
|
534
542
|
try {
|
|
535
543
|
const project = parseGitLabProject(ref.project);
|
|
536
|
-
const issue = await
|
|
544
|
+
const issue = await api2().get(`/projects/${project}/issues/${ref.iid}`);
|
|
537
545
|
return {
|
|
538
546
|
id: makeIssueId2(ref.project, issue.iid),
|
|
539
547
|
title: issue.title,
|
|
540
548
|
description: issue.description ?? "",
|
|
541
|
-
url: issue.web_url
|
|
549
|
+
url: issue.web_url,
|
|
550
|
+
status: issue.state
|
|
542
551
|
};
|
|
543
552
|
} catch {
|
|
544
553
|
return null;
|
|
@@ -547,15 +556,15 @@ var GitLabIssuesSource = class {
|
|
|
547
556
|
async updateStatus(issueId, labelToAdd, config) {
|
|
548
557
|
const { project, iid } = splitIssueId(issueId);
|
|
549
558
|
const encodedProject = parseGitLabProject(project);
|
|
550
|
-
const issue = await
|
|
559
|
+
const issue = await api2().get(`/projects/${encodedProject}/issues/${iid}`);
|
|
551
560
|
if (config && config.in_progress !== config.pick_from) {
|
|
552
|
-
const filterLabels =
|
|
561
|
+
const filterLabels = normalizeLabels(config);
|
|
553
562
|
const isMovingToInProgress = labelToAdd === config.in_progress;
|
|
554
563
|
if (isMovingToInProgress) {
|
|
555
564
|
const updated2 = [.../* @__PURE__ */ new Set([...issue.labels, labelToAdd])].filter(
|
|
556
565
|
(l) => !filterLabels.includes(l)
|
|
557
566
|
);
|
|
558
|
-
await
|
|
567
|
+
await api2().put(`/projects/${encodedProject}/issues/${iid}`, {
|
|
559
568
|
labels: updated2.join(",")
|
|
560
569
|
});
|
|
561
570
|
return;
|
|
@@ -563,40 +572,40 @@ var GitLabIssuesSource = class {
|
|
|
563
572
|
const updated = [.../* @__PURE__ */ new Set([...issue.labels, ...filterLabels])].filter(
|
|
564
573
|
(l) => l !== config.in_progress
|
|
565
574
|
);
|
|
566
|
-
await
|
|
575
|
+
await api2().put(`/projects/${encodedProject}/issues/${iid}`, {
|
|
567
576
|
labels: updated.join(",")
|
|
568
577
|
});
|
|
569
578
|
return;
|
|
570
579
|
}
|
|
571
580
|
const labels = [.../* @__PURE__ */ new Set([...issue.labels, labelToAdd])];
|
|
572
|
-
await
|
|
581
|
+
await api2().put(`/projects/${encodedProject}/issues/${iid}`, { labels: labels.join(",") });
|
|
573
582
|
}
|
|
574
583
|
async attachPullRequest(issueId, prUrl) {
|
|
575
584
|
const { project, iid } = splitIssueId(issueId);
|
|
576
585
|
const encodedProject = parseGitLabProject(project);
|
|
577
|
-
await
|
|
586
|
+
await api2().post(`/projects/${encodedProject}/issues/${iid}/notes`, {
|
|
578
587
|
body: `Pull request: ${prUrl}`
|
|
579
588
|
});
|
|
580
589
|
}
|
|
581
590
|
async completeIssue(issueId, _status, labelToRemove, config) {
|
|
582
591
|
const { project, iid } = splitIssueId(issueId);
|
|
583
592
|
const encodedProject = parseGitLabProject(project);
|
|
584
|
-
const issue = await
|
|
593
|
+
const issue = await api2().get(`/projects/${encodedProject}/issues/${iid}`);
|
|
585
594
|
let labels = labelToRemove ? issue.labels.filter((l) => l.toLowerCase() !== labelToRemove.toLowerCase()) : issue.labels;
|
|
586
595
|
if (config && config.in_progress !== config.pick_from) {
|
|
587
596
|
labels = labels.filter((l) => l !== config.in_progress);
|
|
588
597
|
}
|
|
589
|
-
await
|
|
598
|
+
await api2().put(`/projects/${encodedProject}/issues/${iid}`, {
|
|
590
599
|
state_event: "close",
|
|
591
600
|
labels: labels.join(",")
|
|
592
601
|
});
|
|
593
602
|
}
|
|
594
603
|
async listIssues(config) {
|
|
595
604
|
const project = parseGitLabProject(config.scope);
|
|
596
|
-
const labelsArr =
|
|
605
|
+
const labelsArr = normalizeLabels(config);
|
|
597
606
|
const label = labelsArr.map((l) => encodeURIComponent(l)).join(",");
|
|
598
607
|
const path = `/projects/${project}/issues?labels=${label}&state=opened&per_page=100`;
|
|
599
|
-
const issues = await
|
|
608
|
+
const issues = await api2().get(path);
|
|
600
609
|
return issues.map((issue) => ({
|
|
601
610
|
id: makeIssueId2(config.scope, issue.iid),
|
|
602
611
|
title: issue.title,
|
|
@@ -609,7 +618,7 @@ var GitLabIssuesSource = class {
|
|
|
609
618
|
const results = [];
|
|
610
619
|
let page = 1;
|
|
611
620
|
while (true) {
|
|
612
|
-
const labels = await
|
|
621
|
+
const labels = await api2().get(
|
|
613
622
|
`/projects/${project}/labels?per_page=100&page=${page}`
|
|
614
623
|
);
|
|
615
624
|
for (const l of labels) {
|
|
@@ -626,10 +635,10 @@ var GitLabIssuesSource = class {
|
|
|
626
635
|
async removeLabel(issueId, labelToRemove) {
|
|
627
636
|
const { project, iid } = splitIssueId(issueId);
|
|
628
637
|
const encodedProject = parseGitLabProject(project);
|
|
629
|
-
const issue = await
|
|
638
|
+
const issue = await api2().get(`/projects/${encodedProject}/issues/${iid}`);
|
|
630
639
|
const filtered = issue.labels.filter((l) => l.toLowerCase() !== labelToRemove.toLowerCase());
|
|
631
640
|
if (filtered.length === issue.labels.length) return;
|
|
632
|
-
await
|
|
641
|
+
await api2().put(`/projects/${encodedProject}/issues/${iid}`, {
|
|
633
642
|
labels: filtered.join(",")
|
|
634
643
|
});
|
|
635
644
|
}
|
|
@@ -819,11 +828,18 @@ function useKanbanState(bellEnabled, initialCards = []) {
|
|
|
819
828
|
setCards((prev) => prev.map((c) => c.id === issueId ? { ...c, logFile } : c));
|
|
820
829
|
};
|
|
821
830
|
const MAX_OUTPUT_SIZE = 2e5;
|
|
822
|
-
const
|
|
831
|
+
const outputBuffer = /* @__PURE__ */ new Map();
|
|
832
|
+
let flushTimer = null;
|
|
833
|
+
const flushOutputBuffer = () => {
|
|
834
|
+
flushTimer = null;
|
|
835
|
+
if (outputBuffer.size === 0) return;
|
|
836
|
+
const buffered = new Map(outputBuffer);
|
|
837
|
+
outputBuffer.clear();
|
|
823
838
|
setCards(
|
|
824
839
|
(prev) => prev.map((c) => {
|
|
825
|
-
|
|
826
|
-
|
|
840
|
+
const chunk = buffered.get(c.id);
|
|
841
|
+
if (chunk === void 0) return c;
|
|
842
|
+
let newLog = c.outputLog + chunk;
|
|
827
843
|
if (newLog.length > MAX_OUTPUT_SIZE) {
|
|
828
844
|
const trimAt = newLog.indexOf("\n", newLog.length - MAX_OUTPUT_SIZE);
|
|
829
845
|
newLog = trimAt !== -1 ? newLog.slice(trimAt + 1) : newLog.slice(-MAX_OUTPUT_SIZE);
|
|
@@ -832,7 +848,15 @@ function useKanbanState(bellEnabled, initialCards = []) {
|
|
|
832
848
|
})
|
|
833
849
|
);
|
|
834
850
|
};
|
|
851
|
+
const onOutput = (issueId, text) => {
|
|
852
|
+
const existing = outputBuffer.get(issueId);
|
|
853
|
+
outputBuffer.set(issueId, existing !== void 0 ? existing + text : text);
|
|
854
|
+
if (flushTimer === null) {
|
|
855
|
+
flushTimer = setTimeout(flushOutputBuffer, 100);
|
|
856
|
+
}
|
|
857
|
+
};
|
|
835
858
|
const onReconcileRemove = (issueId) => {
|
|
859
|
+
outputBuffer.delete(issueId);
|
|
836
860
|
setCards((prev) => prev.filter((c) => c.id !== issueId));
|
|
837
861
|
};
|
|
838
862
|
kanbanEmitter.on("issue:queued", onQueued);
|
|
@@ -866,6 +890,11 @@ function useKanbanState(bellEnabled, initialCards = []) {
|
|
|
866
890
|
kanbanEmitter.on("work:watch-prompt-resolved", onWatchPromptResolved);
|
|
867
891
|
const cleanupBell = registerBellListeners(bellEnabled);
|
|
868
892
|
return () => {
|
|
893
|
+
if (flushTimer !== null) {
|
|
894
|
+
clearTimeout(flushTimer);
|
|
895
|
+
flushTimer = null;
|
|
896
|
+
}
|
|
897
|
+
outputBuffer.clear();
|
|
869
898
|
kanbanEmitter.off("issue:queued", onQueued);
|
|
870
899
|
kanbanEmitter.off("issue:started", onStarted);
|
|
871
900
|
kanbanEmitter.off("issue:done", onDone);
|
|
@@ -906,6 +935,7 @@ function useKanbanState(bellEnabled, initialCards = []) {
|
|
|
906
935
|
export {
|
|
907
936
|
setOutputMode,
|
|
908
937
|
getOutputMode,
|
|
938
|
+
setLogLevel,
|
|
909
939
|
initLogFile,
|
|
910
940
|
log,
|
|
911
941
|
warn,
|
|
@@ -914,6 +944,9 @@ export {
|
|
|
914
944
|
divider,
|
|
915
945
|
banner,
|
|
916
946
|
updateNotice,
|
|
947
|
+
REQUEST_TIMEOUT_MS,
|
|
948
|
+
normalizeLabels,
|
|
949
|
+
createApiClient,
|
|
917
950
|
GitHubIssuesSource,
|
|
918
951
|
GitLabIssuesSource,
|
|
919
952
|
kanbanEmitter,
|