@laurentenhoor/devclaw 0.1.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/LICENSE +21 -0
- package/README.md +406 -0
- package/dist/index.d.ts +88 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +107 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/audit.d.ts +2 -0
- package/dist/lib/audit.d.ts.map +1 -0
- package/dist/lib/audit.js +42 -0
- package/dist/lib/audit.js.map +1 -0
- package/dist/lib/binding-manager.d.ts +35 -0
- package/dist/lib/binding-manager.d.ts.map +1 -0
- package/dist/lib/binding-manager.js +88 -0
- package/dist/lib/binding-manager.js.map +1 -0
- package/dist/lib/cli.d.ts +12 -0
- package/dist/lib/cli.d.ts.map +1 -0
- package/dist/lib/cli.js +69 -0
- package/dist/lib/cli.js.map +1 -0
- package/dist/lib/dispatch.d.ts +58 -0
- package/dist/lib/dispatch.d.ts.map +1 -0
- package/dist/lib/dispatch.js +163 -0
- package/dist/lib/dispatch.js.map +1 -0
- package/dist/lib/model-selector.d.ts +21 -0
- package/dist/lib/model-selector.d.ts.map +1 -0
- package/dist/lib/model-selector.js +74 -0
- package/dist/lib/model-selector.js.map +1 -0
- package/dist/lib/notify.d.ts +54 -0
- package/dist/lib/notify.d.ts.map +1 -0
- package/dist/lib/notify.js +143 -0
- package/dist/lib/notify.js.map +1 -0
- package/dist/lib/onboarding.d.ts +5 -0
- package/dist/lib/onboarding.d.ts.map +1 -0
- package/dist/lib/onboarding.js +124 -0
- package/dist/lib/onboarding.js.map +1 -0
- package/dist/lib/projects.d.ts +64 -0
- package/dist/lib/projects.d.ts.map +1 -0
- package/dist/lib/projects.js +127 -0
- package/dist/lib/projects.js.map +1 -0
- package/dist/lib/providers/github.d.ts +23 -0
- package/dist/lib/providers/github.d.ts.map +1 -0
- package/dist/lib/providers/github.js +130 -0
- package/dist/lib/providers/github.js.map +1 -0
- package/dist/lib/providers/gitlab.d.ts +23 -0
- package/dist/lib/providers/gitlab.d.ts.map +1 -0
- package/dist/lib/providers/gitlab.js +133 -0
- package/dist/lib/providers/gitlab.js.map +1 -0
- package/dist/lib/providers/index.d.ts +12 -0
- package/dist/lib/providers/index.d.ts.map +1 -0
- package/dist/lib/providers/index.js +25 -0
- package/dist/lib/providers/index.js.map +1 -0
- package/dist/lib/providers/provider.d.ts +35 -0
- package/dist/lib/providers/provider.d.ts.map +1 -0
- package/dist/lib/providers/provider.js +13 -0
- package/dist/lib/providers/provider.js.map +1 -0
- package/dist/lib/services/health.d.ts +38 -0
- package/dist/lib/services/health.d.ts.map +1 -0
- package/dist/lib/services/health.js +100 -0
- package/dist/lib/services/health.js.map +1 -0
- package/dist/lib/services/heartbeat.d.ts +38 -0
- package/dist/lib/services/heartbeat.d.ts.map +1 -0
- package/dist/lib/services/heartbeat.js +199 -0
- package/dist/lib/services/heartbeat.js.map +1 -0
- package/dist/lib/services/pipeline.d.ts +36 -0
- package/dist/lib/services/pipeline.d.ts.map +1 -0
- package/dist/lib/services/pipeline.js +90 -0
- package/dist/lib/services/pipeline.js.map +1 -0
- package/dist/lib/services/queue.d.ts +14 -0
- package/dist/lib/services/queue.d.ts.map +1 -0
- package/dist/lib/services/queue.js +31 -0
- package/dist/lib/services/queue.js.map +1 -0
- package/dist/lib/services/tick.d.ts +62 -0
- package/dist/lib/services/tick.d.ts.map +1 -0
- package/dist/lib/services/tick.js +160 -0
- package/dist/lib/services/tick.js.map +1 -0
- package/dist/lib/setup/agent.d.ts +14 -0
- package/dist/lib/setup/agent.d.ts.map +1 -0
- package/dist/lib/setup/agent.js +72 -0
- package/dist/lib/setup/agent.js.map +1 -0
- package/dist/lib/setup/config.d.ts +22 -0
- package/dist/lib/setup/config.d.ts.map +1 -0
- package/dist/lib/setup/config.js +67 -0
- package/dist/lib/setup/config.js.map +1 -0
- package/dist/lib/setup/index.d.ts +53 -0
- package/dist/lib/setup/index.d.ts.map +1 -0
- package/dist/lib/setup/index.js +68 -0
- package/dist/lib/setup/index.js.map +1 -0
- package/dist/lib/setup/workspace.d.ts +6 -0
- package/dist/lib/setup/workspace.d.ts.map +1 -0
- package/dist/lib/setup/workspace.js +69 -0
- package/dist/lib/setup/workspace.js.map +1 -0
- package/dist/lib/templates.d.ts +9 -0
- package/dist/lib/templates.d.ts.map +1 -0
- package/dist/lib/templates.js +163 -0
- package/dist/lib/templates.js.map +1 -0
- package/dist/lib/tiers.d.ts +55 -0
- package/dist/lib/tiers.d.ts.map +1 -0
- package/dist/lib/tiers.js +74 -0
- package/dist/lib/tiers.js.map +1 -0
- package/dist/lib/tool-helpers.d.ts +44 -0
- package/dist/lib/tool-helpers.d.ts.map +1 -0
- package/dist/lib/tool-helpers.js +65 -0
- package/dist/lib/tool-helpers.js.map +1 -0
- package/dist/lib/tools/health.d.ts +28 -0
- package/dist/lib/tools/health.d.ts.map +1 -0
- package/dist/lib/tools/health.js +61 -0
- package/dist/lib/tools/health.js.map +1 -0
- package/dist/lib/tools/onboard.d.ts +24 -0
- package/dist/lib/tools/onboard.d.ts.map +1 -0
- package/dist/lib/tools/onboard.js +27 -0
- package/dist/lib/tools/onboard.js.map +1 -0
- package/dist/lib/tools/project-register.d.ts +51 -0
- package/dist/lib/tools/project-register.d.ts.map +1 -0
- package/dist/lib/tools/project-register.js +172 -0
- package/dist/lib/tools/project-register.js.map +1 -0
- package/dist/lib/tools/queue-status.test.d.ts +2 -0
- package/dist/lib/tools/queue-status.test.d.ts.map +1 -0
- package/dist/lib/tools/queue-status.test.js +48 -0
- package/dist/lib/tools/queue-status.test.js.map +1 -0
- package/dist/lib/tools/setup.d.ts +76 -0
- package/dist/lib/tools/setup.d.ts.map +1 -0
- package/dist/lib/tools/setup.js +102 -0
- package/dist/lib/tools/setup.js.map +1 -0
- package/dist/lib/tools/status.d.ts +24 -0
- package/dist/lib/tools/status.d.ts.map +1 -0
- package/dist/lib/tools/status.js +53 -0
- package/dist/lib/tools/status.js.map +1 -0
- package/dist/lib/tools/task-comment.d.ts +40 -0
- package/dist/lib/tools/task-comment.d.ts.map +1 -0
- package/dist/lib/tools/task-comment.js +84 -0
- package/dist/lib/tools/task-comment.js.map +1 -0
- package/dist/lib/tools/task-create.d.ts +54 -0
- package/dist/lib/tools/task-create.d.ts.map +1 -0
- package/dist/lib/tools/task-create.js +77 -0
- package/dist/lib/tools/task-create.js.map +1 -0
- package/dist/lib/tools/task-update.d.ts +40 -0
- package/dist/lib/tools/task-update.d.ts.map +1 -0
- package/dist/lib/tools/task-update.js +79 -0
- package/dist/lib/tools/task-update.js.map +1 -0
- package/dist/lib/tools/task-update.test.d.ts +7 -0
- package/dist/lib/tools/task-update.test.d.ts.map +1 -0
- package/dist/lib/tools/task-update.test.js +55 -0
- package/dist/lib/tools/task-update.test.js.map +1 -0
- package/dist/lib/tools/work-finish.d.ts +43 -0
- package/dist/lib/tools/work-finish.d.ts.map +1 -0
- package/dist/lib/tools/work-finish.js +77 -0
- package/dist/lib/tools/work-finish.js.map +1 -0
- package/dist/lib/tools/work-start.d.ts +39 -0
- package/dist/lib/tools/work-start.d.ts.map +1 -0
- package/dist/lib/tools/work-start.js +129 -0
- package/dist/lib/tools/work-start.js.map +1 -0
- package/dist/lib/types.d.ts +17 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/types.js +8 -0
- package/dist/lib/types.js.map +1 -0
- package/docs/ARCHITECTURE.md +662 -0
- package/docs/CONFIGURATION.md +336 -0
- package/docs/MANAGEMENT.md +120 -0
- package/docs/ONBOARDING.md +251 -0
- package/docs/QA_WORKFLOW.md +120 -0
- package/docs/ROADMAP.md +96 -0
- package/docs/TESTING.md +339 -0
- package/docs/TOOLS.md +361 -0
- package/package.json +55 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHubProvider — IssueProvider implementation using gh CLI.
|
|
3
|
+
*/
|
|
4
|
+
import { execFile } from "node:child_process";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
import { writeFile, unlink } from "node:fs/promises";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { tmpdir } from "node:os";
|
|
9
|
+
import { STATE_LABELS, LABEL_COLORS, } from "./provider.js";
|
|
10
|
+
const execFileAsync = promisify(execFile);
|
|
11
|
+
function toIssue(gh) {
|
|
12
|
+
return {
|
|
13
|
+
iid: gh.number, title: gh.title, description: gh.body ?? "",
|
|
14
|
+
labels: gh.labels.map((l) => l.name), state: gh.state, web_url: gh.url,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
export class GitHubProvider {
|
|
18
|
+
repoPath;
|
|
19
|
+
constructor(opts) { this.repoPath = opts.repoPath; }
|
|
20
|
+
async gh(args) {
|
|
21
|
+
const { stdout } = await execFileAsync("gh", args, { cwd: this.repoPath, timeout: 30_000 });
|
|
22
|
+
return stdout.trim();
|
|
23
|
+
}
|
|
24
|
+
async ensureLabel(name, color) {
|
|
25
|
+
try {
|
|
26
|
+
await this.gh(["label", "create", name, "--color", color.replace(/^#/, "")]);
|
|
27
|
+
}
|
|
28
|
+
catch (err) {
|
|
29
|
+
if (!err.message?.includes("already exists"))
|
|
30
|
+
throw err;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async ensureAllStateLabels() {
|
|
34
|
+
for (const label of STATE_LABELS)
|
|
35
|
+
await this.ensureLabel(label, LABEL_COLORS[label]);
|
|
36
|
+
}
|
|
37
|
+
async createIssue(title, description, label, assignees) {
|
|
38
|
+
const tempFile = join(tmpdir(), `devclaw-issue-${Date.now()}.md`);
|
|
39
|
+
await writeFile(tempFile, description, "utf-8");
|
|
40
|
+
try {
|
|
41
|
+
const args = ["issue", "create", "--title", title, "--body-file", tempFile, "--label", label];
|
|
42
|
+
if (assignees?.length)
|
|
43
|
+
args.push("--assignee", assignees.join(","));
|
|
44
|
+
const url = await this.gh(args);
|
|
45
|
+
const match = url.match(/\/issues\/(\d+)$/);
|
|
46
|
+
if (!match)
|
|
47
|
+
throw new Error(`Failed to parse issue URL: ${url}`);
|
|
48
|
+
return this.getIssue(parseInt(match[1], 10));
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
try {
|
|
52
|
+
await unlink(tempFile);
|
|
53
|
+
}
|
|
54
|
+
catch { /* ignore */ }
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async listIssuesByLabel(label) {
|
|
58
|
+
try {
|
|
59
|
+
const raw = await this.gh(["issue", "list", "--label", label, "--state", "open", "--json", "number,title,body,labels,state,url"]);
|
|
60
|
+
return JSON.parse(raw).map(toIssue);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return [];
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async getIssue(issueId) {
|
|
67
|
+
const raw = await this.gh(["issue", "view", String(issueId), "--json", "number,title,body,labels,state,url"]);
|
|
68
|
+
return toIssue(JSON.parse(raw));
|
|
69
|
+
}
|
|
70
|
+
async transitionLabel(issueId, from, to) {
|
|
71
|
+
const issue = await this.getIssue(issueId);
|
|
72
|
+
const stateLabels = issue.labels.filter((l) => STATE_LABELS.includes(l));
|
|
73
|
+
const args = ["issue", "edit", String(issueId)];
|
|
74
|
+
for (const l of stateLabels)
|
|
75
|
+
args.push("--remove-label", l);
|
|
76
|
+
args.push("--add-label", to);
|
|
77
|
+
await this.gh(args);
|
|
78
|
+
}
|
|
79
|
+
async closeIssue(issueId) { await this.gh(["issue", "close", String(issueId)]); }
|
|
80
|
+
async reopenIssue(issueId) { await this.gh(["issue", "reopen", String(issueId)]); }
|
|
81
|
+
hasStateLabel(issue, expected) { return issue.labels.includes(expected); }
|
|
82
|
+
getCurrentStateLabel(issue) {
|
|
83
|
+
return STATE_LABELS.find((l) => issue.labels.includes(l)) ?? null;
|
|
84
|
+
}
|
|
85
|
+
async hasMergedMR(issueId) {
|
|
86
|
+
try {
|
|
87
|
+
const raw = await this.gh(["pr", "list", "--state", "merged", "--json", "title,body"]);
|
|
88
|
+
const prs = JSON.parse(raw);
|
|
89
|
+
const pat = `#${issueId}`;
|
|
90
|
+
return prs.some((pr) => pr.title.includes(pat) || (pr.body ?? "").includes(pat));
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
async getMergedMRUrl(issueId) {
|
|
97
|
+
try {
|
|
98
|
+
const raw = await this.gh(["pr", "list", "--state", "merged", "--json", "number,title,body,url,mergedAt", "--limit", "20"]);
|
|
99
|
+
const prs = JSON.parse(raw);
|
|
100
|
+
const pat = `#${issueId}`;
|
|
101
|
+
return prs.find((pr) => pr.title.includes(pat) || (pr.body ?? "").includes(pat))?.url ?? null;
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async addComment(issueId, body) {
|
|
108
|
+
const tempFile = join(tmpdir(), `devclaw-comment-${Date.now()}.md`);
|
|
109
|
+
await writeFile(tempFile, body, "utf-8");
|
|
110
|
+
try {
|
|
111
|
+
await this.gh(["issue", "comment", String(issueId), "--body-file", tempFile]);
|
|
112
|
+
}
|
|
113
|
+
finally {
|
|
114
|
+
try {
|
|
115
|
+
await unlink(tempFile);
|
|
116
|
+
}
|
|
117
|
+
catch { /* ignore */ }
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
async healthCheck() {
|
|
121
|
+
try {
|
|
122
|
+
await this.gh(["auth", "status"]);
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=github.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github.js","sourceRoot":"","sources":["../../../lib/providers/github.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type IssueProvider, type Issue, type StateLabel } from "./provider.js";
|
|
2
|
+
export declare class GitLabProvider implements IssueProvider {
|
|
3
|
+
private repoPath;
|
|
4
|
+
constructor(opts: {
|
|
5
|
+
repoPath: string;
|
|
6
|
+
});
|
|
7
|
+
private glab;
|
|
8
|
+
ensureLabel(name: string, color: string): Promise<void>;
|
|
9
|
+
ensureAllStateLabels(): Promise<void>;
|
|
10
|
+
createIssue(title: string, description: string, label: StateLabel, assignees?: string[]): Promise<Issue>;
|
|
11
|
+
listIssuesByLabel(label: StateLabel): Promise<Issue[]>;
|
|
12
|
+
getIssue(issueId: number): Promise<Issue>;
|
|
13
|
+
transitionLabel(issueId: number, from: StateLabel, to: StateLabel): Promise<void>;
|
|
14
|
+
closeIssue(issueId: number): Promise<void>;
|
|
15
|
+
reopenIssue(issueId: number): Promise<void>;
|
|
16
|
+
hasStateLabel(issue: Issue, expected: StateLabel): boolean;
|
|
17
|
+
getCurrentStateLabel(issue: Issue): StateLabel | null;
|
|
18
|
+
hasMergedMR(issueId: number): Promise<boolean>;
|
|
19
|
+
getMergedMRUrl(issueId: number): Promise<string | null>;
|
|
20
|
+
addComment(issueId: number, body: string): Promise<void>;
|
|
21
|
+
healthCheck(): Promise<boolean>;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=gitlab.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gitlab.d.ts","sourceRoot":"","sources":["../../../lib/providers/gitlab.ts"],"names":[],"mappings":"AAQA,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,KAAK,EACV,KAAK,UAAU,EAGhB,MAAM,eAAe,CAAC;AAIvB,qBAAa,cAAe,YAAW,aAAa;IAClD,OAAO,CAAC,QAAQ,CAAS;gBACb,IAAI,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE;YAExB,IAAI;IAKZ,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKvD,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC;IAIrC,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC;IAgBxG,iBAAiB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;IAOtD,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC;IAKzC,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IASjF,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAC1C,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAEjD,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,GAAG,OAAO;IAC1D,oBAAoB,CAAC,KAAK,EAAE,KAAK,GAAG,UAAU,GAAG,IAAI;IAI/C,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAS9C,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAYvD,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAUxD,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;CAGtC"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitLabProvider — IssueProvider implementation using glab CLI.
|
|
3
|
+
*/
|
|
4
|
+
import { execFile } from "node:child_process";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
import { writeFile, unlink } from "node:fs/promises";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { tmpdir } from "node:os";
|
|
9
|
+
import { STATE_LABELS, LABEL_COLORS, } from "./provider.js";
|
|
10
|
+
const execFileAsync = promisify(execFile);
|
|
11
|
+
export class GitLabProvider {
|
|
12
|
+
repoPath;
|
|
13
|
+
constructor(opts) { this.repoPath = opts.repoPath; }
|
|
14
|
+
async glab(args) {
|
|
15
|
+
const { stdout } = await execFileAsync("glab", args, { cwd: this.repoPath, timeout: 30_000 });
|
|
16
|
+
return stdout.trim();
|
|
17
|
+
}
|
|
18
|
+
async ensureLabel(name, color) {
|
|
19
|
+
try {
|
|
20
|
+
await this.glab(["label", "create", "--name", name, "--color", color]);
|
|
21
|
+
}
|
|
22
|
+
catch (err) {
|
|
23
|
+
const msg = err.message ?? "";
|
|
24
|
+
if (!msg.includes("already exists") && !msg.includes("409"))
|
|
25
|
+
throw err;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
async ensureAllStateLabels() {
|
|
29
|
+
for (const label of STATE_LABELS)
|
|
30
|
+
await this.ensureLabel(label, LABEL_COLORS[label]);
|
|
31
|
+
}
|
|
32
|
+
async createIssue(title, description, label, assignees) {
|
|
33
|
+
const tempFile = join(tmpdir(), `devclaw-issue-${Date.now()}.md`);
|
|
34
|
+
await writeFile(tempFile, description, "utf-8");
|
|
35
|
+
try {
|
|
36
|
+
const { exec } = await import("node:child_process");
|
|
37
|
+
const execAsync = promisify(exec);
|
|
38
|
+
let cmd = `glab issue create --title "${title.replace(/"/g, '\\"')}" --description "$(cat ${tempFile})" --label "${label}"`;
|
|
39
|
+
if (assignees?.length)
|
|
40
|
+
cmd += ` --assignee "${assignees.join(",")}"`;
|
|
41
|
+
const { stdout } = await execAsync(cmd, { cwd: this.repoPath, timeout: 30_000 });
|
|
42
|
+
// glab issue create returns the issue URL
|
|
43
|
+
const match = stdout.trim().match(/\/issues\/(\d+)/);
|
|
44
|
+
if (!match)
|
|
45
|
+
throw new Error(`Failed to parse issue URL: ${stdout.trim()}`);
|
|
46
|
+
return this.getIssue(parseInt(match[1], 10));
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
try {
|
|
50
|
+
await unlink(tempFile);
|
|
51
|
+
}
|
|
52
|
+
catch { /* ignore */ }
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async listIssuesByLabel(label) {
|
|
56
|
+
try {
|
|
57
|
+
const raw = await this.glab(["issue", "list", "--label", label, "--output", "json"]);
|
|
58
|
+
return JSON.parse(raw);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
async getIssue(issueId) {
|
|
65
|
+
const raw = await this.glab(["issue", "view", String(issueId), "--output", "json"]);
|
|
66
|
+
return JSON.parse(raw);
|
|
67
|
+
}
|
|
68
|
+
async transitionLabel(issueId, from, to) {
|
|
69
|
+
const issue = await this.getIssue(issueId);
|
|
70
|
+
const stateLabels = issue.labels.filter((l) => STATE_LABELS.includes(l));
|
|
71
|
+
const args = ["issue", "update", String(issueId)];
|
|
72
|
+
for (const l of stateLabels)
|
|
73
|
+
args.push("--unlabel", l);
|
|
74
|
+
args.push("--label", to);
|
|
75
|
+
await this.glab(args);
|
|
76
|
+
}
|
|
77
|
+
async closeIssue(issueId) { await this.glab(["issue", "close", String(issueId)]); }
|
|
78
|
+
async reopenIssue(issueId) { await this.glab(["issue", "reopen", String(issueId)]); }
|
|
79
|
+
hasStateLabel(issue, expected) { return issue.labels.includes(expected); }
|
|
80
|
+
getCurrentStateLabel(issue) {
|
|
81
|
+
return STATE_LABELS.find((l) => issue.labels.includes(l)) ?? null;
|
|
82
|
+
}
|
|
83
|
+
async hasMergedMR(issueId) {
|
|
84
|
+
try {
|
|
85
|
+
const raw = await this.glab(["mr", "list", "--output", "json", "--state", "merged"]);
|
|
86
|
+
const mrs = JSON.parse(raw);
|
|
87
|
+
const pat = `#${issueId}`;
|
|
88
|
+
return mrs.some((mr) => mr.title.includes(pat) || (mr.description ?? "").includes(pat));
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
async getMergedMRUrl(issueId) {
|
|
95
|
+
try {
|
|
96
|
+
const raw = await this.glab(["mr", "list", "--output", "json", "--state", "merged"]);
|
|
97
|
+
const mrs = JSON.parse(raw);
|
|
98
|
+
const pat = `#${issueId}`;
|
|
99
|
+
const mr = mrs
|
|
100
|
+
.filter((mr) => mr.title.includes(pat) || (mr.description ?? "").includes(pat))
|
|
101
|
+
.sort((a, b) => new Date(b.merged_at).getTime() - new Date(a.merged_at).getTime())[0];
|
|
102
|
+
return mr?.web_url ?? null;
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async addComment(issueId, body) {
|
|
109
|
+
const tempFile = join(tmpdir(), `devclaw-comment-${Date.now()}.md`);
|
|
110
|
+
await writeFile(tempFile, body, "utf-8");
|
|
111
|
+
try {
|
|
112
|
+
const { exec } = await import("node:child_process");
|
|
113
|
+
const execAsync = promisify(exec);
|
|
114
|
+
await execAsync(`glab issue note ${issueId} --message "$(cat ${tempFile})"`, { cwd: this.repoPath, timeout: 30_000 });
|
|
115
|
+
}
|
|
116
|
+
finally {
|
|
117
|
+
try {
|
|
118
|
+
await unlink(tempFile);
|
|
119
|
+
}
|
|
120
|
+
catch { /* ignore */ }
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
async healthCheck() {
|
|
124
|
+
try {
|
|
125
|
+
await this.glab(["auth", "status"]);
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=gitlab.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"gitlab.js","sourceRoot":"","sources":["../../../lib/providers/gitlab.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { IssueProvider } from "./provider.js";
|
|
2
|
+
export type ProviderOptions = {
|
|
3
|
+
provider?: "gitlab" | "github";
|
|
4
|
+
repo?: string;
|
|
5
|
+
repoPath?: string;
|
|
6
|
+
};
|
|
7
|
+
export type ProviderWithType = {
|
|
8
|
+
provider: IssueProvider;
|
|
9
|
+
type: "github" | "gitlab";
|
|
10
|
+
};
|
|
11
|
+
export declare function createProvider(opts: ProviderOptions): ProviderWithType;
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../lib/providers/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAKnD,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAC;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,aAAa,CAAC;IACxB,IAAI,EAAE,QAAQ,GAAG,QAAQ,CAAC;CAC3B,CAAC;AAWF,wBAAgB,cAAc,CAAC,IAAI,EAAE,eAAe,GAAG,gBAAgB,CAMtE"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider factory — auto-detects GitHub vs GitLab from git remote.
|
|
3
|
+
*/
|
|
4
|
+
import { execFileSync } from "node:child_process";
|
|
5
|
+
import { GitLabProvider } from "./gitlab.js";
|
|
6
|
+
import { GitHubProvider } from "./github.js";
|
|
7
|
+
import { resolveRepoPath } from "../projects.js";
|
|
8
|
+
function detectProvider(repoPath) {
|
|
9
|
+
try {
|
|
10
|
+
const url = execFileSync("git", ["remote", "get-url", "origin"], { cwd: repoPath, timeout: 5_000 }).toString().trim();
|
|
11
|
+
return url.includes("github.com") ? "github" : "gitlab";
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
return "gitlab";
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
export function createProvider(opts) {
|
|
18
|
+
const repoPath = opts.repoPath ?? (opts.repo ? resolveRepoPath(opts.repo) : null);
|
|
19
|
+
if (!repoPath)
|
|
20
|
+
throw new Error("Either repoPath or repo must be provided");
|
|
21
|
+
const type = opts.provider ?? detectProvider(repoPath);
|
|
22
|
+
const provider = type === "github" ? new GitHubProvider({ repoPath }) : new GitLabProvider({ repoPath });
|
|
23
|
+
return { provider, type };
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../lib/providers/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAajD,SAAS,cAAc,CAAC,QAAgB;IACtC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,KAAK,EAAE,CAAC,QAAQ,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,CAAC;QACtH,OAAO,GAAG,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC;IAC1D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,QAAQ,CAAC;IAClB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAqB;IAClD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IAClF,IAAI,CAAC,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAC3E,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,IAAI,cAAc,CAAC,QAAQ,CAAC,CAAC;IACvD,MAAM,QAAQ,GAAG,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,cAAc,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,cAAc,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC;IACzG,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;AAC5B,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IssueProvider — Abstract interface for issue tracker operations.
|
|
3
|
+
*
|
|
4
|
+
* Implementations: GitHub (gh CLI), GitLab (glab CLI).
|
|
5
|
+
*/
|
|
6
|
+
export declare const STATE_LABELS: readonly ["Planning", "To Do", "Doing", "To Test", "Testing", "Done", "To Improve", "Refining"];
|
|
7
|
+
export type StateLabel = (typeof STATE_LABELS)[number];
|
|
8
|
+
export declare const LABEL_COLORS: Record<StateLabel, string>;
|
|
9
|
+
export type Issue = {
|
|
10
|
+
iid: number;
|
|
11
|
+
title: string;
|
|
12
|
+
description: string;
|
|
13
|
+
labels: string[];
|
|
14
|
+
state: string;
|
|
15
|
+
web_url: string;
|
|
16
|
+
};
|
|
17
|
+
export interface IssueProvider {
|
|
18
|
+
ensureLabel(name: string, color: string): Promise<void>;
|
|
19
|
+
ensureAllStateLabels(): Promise<void>;
|
|
20
|
+
createIssue(title: string, description: string, label: StateLabel, assignees?: string[]): Promise<Issue>;
|
|
21
|
+
listIssuesByLabel(label: StateLabel): Promise<Issue[]>;
|
|
22
|
+
getIssue(issueId: number): Promise<Issue>;
|
|
23
|
+
transitionLabel(issueId: number, from: StateLabel, to: StateLabel): Promise<void>;
|
|
24
|
+
closeIssue(issueId: number): Promise<void>;
|
|
25
|
+
reopenIssue(issueId: number): Promise<void>;
|
|
26
|
+
hasStateLabel(issue: Issue, expected: StateLabel): boolean;
|
|
27
|
+
getCurrentStateLabel(issue: Issue): StateLabel | null;
|
|
28
|
+
hasMergedMR(issueId: number): Promise<boolean>;
|
|
29
|
+
getMergedMRUrl(issueId: number): Promise<string | null>;
|
|
30
|
+
addComment(issueId: number, body: string): Promise<void>;
|
|
31
|
+
healthCheck(): Promise<boolean>;
|
|
32
|
+
}
|
|
33
|
+
/** @deprecated Use IssueProvider */
|
|
34
|
+
export type TaskManager = IssueProvider;
|
|
35
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../../lib/providers/provider.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,eAAO,MAAM,YAAY,iGAEf,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAEvD,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,CAGnD,CAAC;AAEF,MAAM,MAAM,KAAK,GAAG;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxD,oBAAoB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IACzG,iBAAiB,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IACvD,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC;IAC1C,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClF,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3C,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,GAAG,OAAO,CAAC;IAC3D,oBAAoB,CAAC,KAAK,EAAE,KAAK,GAAG,UAAU,GAAG,IAAI,CAAC;IACtD,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/C,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACxD,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;CACjC;AAED,oCAAoC;AACpC,MAAM,MAAM,WAAW,GAAG,aAAa,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IssueProvider — Abstract interface for issue tracker operations.
|
|
3
|
+
*
|
|
4
|
+
* Implementations: GitHub (gh CLI), GitLab (glab CLI).
|
|
5
|
+
*/
|
|
6
|
+
export const STATE_LABELS = [
|
|
7
|
+
"Planning", "To Do", "Doing", "To Test", "Testing", "Done", "To Improve", "Refining",
|
|
8
|
+
];
|
|
9
|
+
export const LABEL_COLORS = {
|
|
10
|
+
Planning: "#6699cc", "To Do": "#428bca", Doing: "#f0ad4e", "To Test": "#5bc0de",
|
|
11
|
+
Testing: "#9b59b6", Done: "#5cb85c", "To Improve": "#d9534f", Refining: "#f39c12",
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../../../lib/providers/provider.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU;CAC5E,CAAC;AAIX,MAAM,CAAC,MAAM,YAAY,GAA+B;IACtD,QAAQ,EAAE,SAAS,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS;IAC/E,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,YAAY,EAAE,SAAS,EAAE,QAAQ,EAAE,SAAS;CAClF,CAAC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Health service — worker health checks and auto-fix.
|
|
3
|
+
*
|
|
4
|
+
* Detects: active_no_session, zombie_session, stale_worker, inactive_with_issue.
|
|
5
|
+
* Used by both `status` (read-only) and `auto_pickup` (auto-fix).
|
|
6
|
+
*/
|
|
7
|
+
import type { StateLabel } from "../providers/provider.js";
|
|
8
|
+
import { type Project } from "../projects.js";
|
|
9
|
+
export type HealthIssue = {
|
|
10
|
+
type: "active_no_session" | "zombie_session" | "stale_worker" | "inactive_with_issue";
|
|
11
|
+
severity: "critical" | "warning";
|
|
12
|
+
project: string;
|
|
13
|
+
groupId: string;
|
|
14
|
+
role: "dev" | "qa";
|
|
15
|
+
message: string;
|
|
16
|
+
level?: string | null;
|
|
17
|
+
sessionKey?: string | null;
|
|
18
|
+
hoursActive?: number;
|
|
19
|
+
issueId?: string | null;
|
|
20
|
+
};
|
|
21
|
+
export type HealthFix = {
|
|
22
|
+
issue: HealthIssue;
|
|
23
|
+
fixed: boolean;
|
|
24
|
+
labelReverted?: string;
|
|
25
|
+
labelRevertFailed?: boolean;
|
|
26
|
+
};
|
|
27
|
+
export declare function checkWorkerHealth(opts: {
|
|
28
|
+
workspaceDir: string;
|
|
29
|
+
groupId: string;
|
|
30
|
+
project: Project;
|
|
31
|
+
role: "dev" | "qa";
|
|
32
|
+
activeSessions: string[];
|
|
33
|
+
autoFix: boolean;
|
|
34
|
+
provider: {
|
|
35
|
+
transitionLabel(id: number, from: StateLabel, to: StateLabel): Promise<void>;
|
|
36
|
+
};
|
|
37
|
+
}): Promise<HealthFix[]>;
|
|
38
|
+
//# sourceMappingURL=health.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../../lib/services/health.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAIL,KAAK,OAAO,EACb,MAAM,gBAAgB,CAAC;AAExB,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,mBAAmB,GAAG,gBAAgB,GAAG,cAAc,GAAG,qBAAqB,CAAC;IACtF,QAAQ,EAAE,UAAU,GAAG,SAAS,CAAC;IACjC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,KAAK,GAAG,IAAI,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG;IACtB,KAAK,EAAE,WAAW,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;IACf,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B,CAAC;AAEF,wBAAsB,iBAAiB,CAAC,IAAI,EAAE;IAC5C,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,KAAK,GAAG,IAAI,CAAC;IACnB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE;QACR,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;KAC9E,CAAC;CACH,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAqGvB"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { getSessionForLevel, getWorker, updateWorker, } from "../projects.js";
|
|
2
|
+
export async function checkWorkerHealth(opts) {
|
|
3
|
+
const { workspaceDir, groupId, project, role, activeSessions, autoFix, provider } = opts;
|
|
4
|
+
const fixes = [];
|
|
5
|
+
const worker = getWorker(project, role);
|
|
6
|
+
const sessionKey = worker.level ? getSessionForLevel(worker, worker.level) : null;
|
|
7
|
+
const revertLabel = role === "dev" ? "To Do" : "To Test";
|
|
8
|
+
const currentLabel = role === "dev" ? "Doing" : "Testing";
|
|
9
|
+
async function revertIssueLabel(fix) {
|
|
10
|
+
if (!worker.issueId)
|
|
11
|
+
return;
|
|
12
|
+
try {
|
|
13
|
+
const id = Number(worker.issueId.split(",")[0]);
|
|
14
|
+
await provider.transitionLabel(id, currentLabel, revertLabel);
|
|
15
|
+
fix.labelReverted = `${currentLabel} → ${revertLabel}`;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
fix.labelRevertFailed = true;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
// Check 1: Active but no session key for current level
|
|
22
|
+
if (worker.active && !sessionKey) {
|
|
23
|
+
const fix = {
|
|
24
|
+
issue: {
|
|
25
|
+
type: "active_no_session", severity: "critical",
|
|
26
|
+
project: project.name, groupId, role,
|
|
27
|
+
level: worker.level,
|
|
28
|
+
message: `${role.toUpperCase()} active but no session for level "${worker.level}"`,
|
|
29
|
+
},
|
|
30
|
+
fixed: false,
|
|
31
|
+
};
|
|
32
|
+
if (autoFix) {
|
|
33
|
+
await updateWorker(workspaceDir, groupId, role, { active: false, issueId: null, startTime: null });
|
|
34
|
+
fix.fixed = true;
|
|
35
|
+
}
|
|
36
|
+
fixes.push(fix);
|
|
37
|
+
}
|
|
38
|
+
// Check 2: Active with session but session is dead (zombie)
|
|
39
|
+
if (worker.active && sessionKey && activeSessions.length > 0 && !activeSessions.includes(sessionKey)) {
|
|
40
|
+
const fix = {
|
|
41
|
+
issue: {
|
|
42
|
+
type: "zombie_session", severity: "critical",
|
|
43
|
+
project: project.name, groupId, role,
|
|
44
|
+
sessionKey, level: worker.level,
|
|
45
|
+
message: `${role.toUpperCase()} session not in active sessions list`,
|
|
46
|
+
},
|
|
47
|
+
fixed: false,
|
|
48
|
+
};
|
|
49
|
+
if (autoFix) {
|
|
50
|
+
await revertIssueLabel(fix);
|
|
51
|
+
const sessions = { ...worker.sessions };
|
|
52
|
+
if (worker.level)
|
|
53
|
+
sessions[worker.level] = null;
|
|
54
|
+
await updateWorker(workspaceDir, groupId, role, { active: false, issueId: null, startTime: null, sessions });
|
|
55
|
+
fix.fixed = true;
|
|
56
|
+
}
|
|
57
|
+
fixes.push(fix);
|
|
58
|
+
}
|
|
59
|
+
// Check 3: Inactive but still has issueId
|
|
60
|
+
if (!worker.active && worker.issueId) {
|
|
61
|
+
const fix = {
|
|
62
|
+
issue: {
|
|
63
|
+
type: "inactive_with_issue", severity: "warning",
|
|
64
|
+
project: project.name, groupId, role,
|
|
65
|
+
issueId: worker.issueId,
|
|
66
|
+
message: `${role.toUpperCase()} inactive but still has issueId "${worker.issueId}"`,
|
|
67
|
+
},
|
|
68
|
+
fixed: false,
|
|
69
|
+
};
|
|
70
|
+
if (autoFix) {
|
|
71
|
+
await updateWorker(workspaceDir, groupId, role, { issueId: null });
|
|
72
|
+
fix.fixed = true;
|
|
73
|
+
}
|
|
74
|
+
fixes.push(fix);
|
|
75
|
+
}
|
|
76
|
+
// Check 4: Active for >2 hours (stale)
|
|
77
|
+
if (worker.active && worker.startTime && sessionKey) {
|
|
78
|
+
const hours = (Date.now() - new Date(worker.startTime).getTime()) / 3_600_000;
|
|
79
|
+
if (hours > 2) {
|
|
80
|
+
const fix = {
|
|
81
|
+
issue: {
|
|
82
|
+
type: "stale_worker", severity: "warning",
|
|
83
|
+
project: project.name, groupId, role,
|
|
84
|
+
hoursActive: Math.round(hours * 10) / 10,
|
|
85
|
+
sessionKey, issueId: worker.issueId,
|
|
86
|
+
message: `${role.toUpperCase()} active for ${Math.round(hours * 10) / 10}h — may need attention`,
|
|
87
|
+
},
|
|
88
|
+
fixed: false,
|
|
89
|
+
};
|
|
90
|
+
if (autoFix) {
|
|
91
|
+
await revertIssueLabel(fix);
|
|
92
|
+
await updateWorker(workspaceDir, groupId, role, { active: false, issueId: null, startTime: null });
|
|
93
|
+
fix.fixed = true;
|
|
94
|
+
}
|
|
95
|
+
fixes.push(fix);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return fixes;
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=health.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.js","sourceRoot":"","sources":["../../../lib/services/health.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Heartbeat tick — token-free queue processing.
|
|
3
|
+
*
|
|
4
|
+
* Runs automatically via plugin service (periodic execution).
|
|
5
|
+
*
|
|
6
|
+
* Logic:
|
|
7
|
+
* 1. Health pass: auto-fix zombies, stale workers, orphaned state
|
|
8
|
+
* 2. Tick pass: fill free worker slots by priority
|
|
9
|
+
*
|
|
10
|
+
* Zero LLM tokens — all logic is deterministic code + CLI calls.
|
|
11
|
+
* Workers only consume tokens when they start processing dispatched tasks.
|
|
12
|
+
*/
|
|
13
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
14
|
+
export type HeartbeatConfig = {
|
|
15
|
+
enabled: boolean;
|
|
16
|
+
intervalSeconds: number;
|
|
17
|
+
maxPickupsPerTick: number;
|
|
18
|
+
};
|
|
19
|
+
type TickResult = {
|
|
20
|
+
totalPickups: number;
|
|
21
|
+
totalHealthFixes: number;
|
|
22
|
+
totalSkipped: number;
|
|
23
|
+
};
|
|
24
|
+
export declare const HEARTBEAT_DEFAULTS: HeartbeatConfig;
|
|
25
|
+
export declare function resolveHeartbeatConfig(pluginConfig?: Record<string, unknown>): HeartbeatConfig;
|
|
26
|
+
export declare function registerHeartbeatService(api: OpenClawPluginApi): void;
|
|
27
|
+
export declare function tick(opts: {
|
|
28
|
+
workspaceDir: string;
|
|
29
|
+
agentId?: string;
|
|
30
|
+
config: HeartbeatConfig;
|
|
31
|
+
pluginConfig?: Record<string, unknown>;
|
|
32
|
+
logger: {
|
|
33
|
+
info(msg: string): void;
|
|
34
|
+
warn(msg: string): void;
|
|
35
|
+
};
|
|
36
|
+
}): Promise<TickResult>;
|
|
37
|
+
export {};
|
|
38
|
+
//# sourceMappingURL=heartbeat.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"heartbeat.d.ts","sourceRoot":"","sources":["../../../lib/services/heartbeat.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAa7D,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,iBAAiB,EAAE,MAAM,CAAC;CAC3B,CAAC;AAOF,KAAK,UAAU,GAAG;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAaF,eAAO,MAAM,kBAAkB,EAAE,eAIhC,CAAC;AAEF,wBAAgB,sBAAsB,CACpC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACrC,eAAe,CAGjB;AAMD,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,iBAAiB,QAuC9D;AAyFD,wBAAsB,IAAI,CAAC,IAAI,EAAE;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,eAAe,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,MAAM,EAAE;QAAE,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;CAC9D,GAAG,OAAO,CAAC,UAAU,CAAC,CA+DtB"}
|