@jwillert/forgeflow 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 +94 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +119 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/init.d.ts +11 -0
- package/dist/cli/init.js +198 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/lock.d.ts +4 -0
- package/dist/cli/lock.js +55 -0
- package/dist/cli/lock.js.map +1 -0
- package/dist/cli/templates/Dockerfile +38 -0
- package/dist/cli/templates/build-image.sh +11 -0
- package/dist/core/capabilities.d.ts +75 -0
- package/dist/core/capabilities.js +2 -0
- package/dist/core/capabilities.js.map +1 -0
- package/dist/core/config.d.ts +38 -0
- package/dist/core/config.js +25 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/engine.d.ts +44 -0
- package/dist/core/engine.js +225 -0
- package/dist/core/engine.js.map +1 -0
- package/dist/core/state.d.ts +42 -0
- package/dist/core/state.js +8 -0
- package/dist/core/state.js.map +1 -0
- package/dist/core/types.d.ts +141 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/workflow.d.ts +72 -0
- package/dist/core/workflow.js +38 -0
- package/dist/core/workflow.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/process/index.d.ts +14 -0
- package/dist/process/index.js +46 -0
- package/dist/process/index.js.map +1 -0
- package/dist/providers/github/index.d.ts +81 -0
- package/dist/providers/github/index.js +271 -0
- package/dist/providers/github/index.js.map +1 -0
- package/dist/providers/gitlab/index.d.ts +79 -0
- package/dist/providers/gitlab/index.js +263 -0
- package/dist/providers/gitlab/index.js.map +1 -0
- package/dist/state/sqlite/index.d.ts +2 -0
- package/dist/state/sqlite/index.js +138 -0
- package/dist/state/sqlite/index.js.map +1 -0
- package/dist/workflows/implement.d.ts +14 -0
- package/dist/workflows/implement.js +154 -0
- package/dist/workflows/implement.js.map +1 -0
- package/dist/workflows/index.d.ts +9 -0
- package/dist/workflows/index.js +5 -0
- package/dist/workflows/index.js.map +1 -0
- package/dist/workflows/prompts/implement.md +43 -0
- package/dist/workflows/prompts/review/extraction.md +21 -0
- package/dist/workflows/prompts/review/prompt.md +49 -0
- package/dist/workflows/prompts/update-branch/extraction.md +13 -0
- package/dist/workflows/prompts/update-branch/prompt.md +41 -0
- package/dist/workflows/review.d.ts +14 -0
- package/dist/workflows/review.js +134 -0
- package/dist/workflows/review.js.map +1 -0
- package/dist/workflows/shared/git.d.ts +17 -0
- package/dist/workflows/shared/git.js +28 -0
- package/dist/workflows/shared/git.js.map +1 -0
- package/dist/workflows/shared/lifecycle.d.ts +21 -0
- package/dist/workflows/shared/lifecycle.js +29 -0
- package/dist/workflows/shared/lifecycle.js.map +1 -0
- package/dist/workflows/shared/review-output.d.ts +44 -0
- package/dist/workflows/shared/review-output.js +100 -0
- package/dist/workflows/shared/review-output.js.map +1 -0
- package/dist/workflows/shared/sandcastle.d.ts +26 -0
- package/dist/workflows/shared/sandcastle.js +112 -0
- package/dist/workflows/shared/sandcastle.js.map +1 -0
- package/dist/workflows/shared/schema.d.ts +4 -0
- package/dist/workflows/shared/schema.js +17 -0
- package/dist/workflows/shared/schema.js.map +1 -0
- package/dist/workflows/update-branch.d.ts +12 -0
- package/dist/workflows/update-branch.js +148 -0
- package/dist/workflows/update-branch.js.map +1 -0
- package/package.json +46 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { AgentCommand, ChangeRequestDetails, ChangeRequestRef, CheckoutSpec, CodeTargetRef, CommentRef, GeneralComment, InlineReviewComment, ReviewThread, WorkTargetRef, WorkTargetSnapshot } from "./types.js";
|
|
2
|
+
export interface PollingEventSource {
|
|
3
|
+
readonly provider: string;
|
|
4
|
+
readonly targetId: string;
|
|
5
|
+
poll(input: {
|
|
6
|
+
cursor?: {
|
|
7
|
+
value?: string;
|
|
8
|
+
};
|
|
9
|
+
maxEvents: number;
|
|
10
|
+
}): Promise<{
|
|
11
|
+
events: import("./types.js").NormalizedEvent[];
|
|
12
|
+
nextCursor?: {
|
|
13
|
+
value?: string;
|
|
14
|
+
};
|
|
15
|
+
}>;
|
|
16
|
+
}
|
|
17
|
+
export interface WorkReader {
|
|
18
|
+
getTarget(target: WorkTargetRef): Promise<WorkTargetSnapshot>;
|
|
19
|
+
/** Other issues/change requests that block this target from proceeding (native forge relationships and/or a "Blocked by #N" text convention in the body). */
|
|
20
|
+
listBlockingIssues(target: WorkTargetRef): Promise<WorkTargetRef[]>;
|
|
21
|
+
}
|
|
22
|
+
export interface WorkTracker extends WorkReader {
|
|
23
|
+
addLabel(target: WorkTargetRef, label: string): Promise<void>;
|
|
24
|
+
removeLabel(target: WorkTargetRef, label: string): Promise<void>;
|
|
25
|
+
addComment(target: WorkTargetRef, body: string): Promise<CommentRef>;
|
|
26
|
+
}
|
|
27
|
+
export interface CodeReader {
|
|
28
|
+
resolveCheckout(target: CodeTargetRef, command: AgentCommand): Promise<CheckoutSpec>;
|
|
29
|
+
}
|
|
30
|
+
export interface CodeHost extends CodeReader {
|
|
31
|
+
openOrUpdateChangeRequest(input: {
|
|
32
|
+
existing?: ChangeRequestRef;
|
|
33
|
+
sourceBranch: import("./types.js").BranchRef;
|
|
34
|
+
targetBranch: string;
|
|
35
|
+
title: string;
|
|
36
|
+
body: string;
|
|
37
|
+
}): Promise<ChangeRequestRef>;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Forge-agnostic surface for reviewing a change request (GitHub pull request /
|
|
41
|
+
* GitLab merge request) or discussing an issue. `listGeneralComments` works for
|
|
42
|
+
* any WorkTargetRef (issue or change request); the rest are change-request only.
|
|
43
|
+
*/
|
|
44
|
+
export interface CodeReviewHost {
|
|
45
|
+
getChangeRequestDetails(target: WorkTargetRef): Promise<ChangeRequestDetails>;
|
|
46
|
+
listGeneralComments(target: WorkTargetRef): Promise<GeneralComment[]>;
|
|
47
|
+
listReviewThreads(target: WorkTargetRef): Promise<ReviewThread[]>;
|
|
48
|
+
postReview(target: WorkTargetRef, input: {
|
|
49
|
+
commitSha: string;
|
|
50
|
+
summary: string;
|
|
51
|
+
inlineComments: InlineReviewComment[];
|
|
52
|
+
}): Promise<void>;
|
|
53
|
+
replyToThread(target: WorkTargetRef, input: {
|
|
54
|
+
threadId: string;
|
|
55
|
+
body: string;
|
|
56
|
+
}): Promise<void>;
|
|
57
|
+
markReady(target: WorkTargetRef): Promise<void>;
|
|
58
|
+
}
|
|
59
|
+
export interface CapabilityRegistry {
|
|
60
|
+
optional<T = unknown>(name: string): T | undefined;
|
|
61
|
+
}
|
|
62
|
+
export interface TrustPolicy {
|
|
63
|
+
canProcessEvent(input: {
|
|
64
|
+
event: import("./types.js").NormalizedEvent;
|
|
65
|
+
workflowKind?: "read" | "write";
|
|
66
|
+
}): Promise<{
|
|
67
|
+
allowed: boolean;
|
|
68
|
+
reason?: string;
|
|
69
|
+
}>;
|
|
70
|
+
}
|
|
71
|
+
export interface CommandState {
|
|
72
|
+
getLinkedChangeRequest(): Promise<ChangeRequestRef | undefined>;
|
|
73
|
+
linkChangeRequest(ref: ChangeRequestRef): Promise<void>;
|
|
74
|
+
getLastExecutionResult(): Promise<import("./types.js").ExecutionResult | undefined>;
|
|
75
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capabilities.js","sourceRoot":"","sources":["../../src/core/capabilities.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { TrustPolicy } from "./capabilities.js";
|
|
2
|
+
import type { StateStore } from "./state.js";
|
|
3
|
+
import type { Workflow } from "./workflow.js";
|
|
4
|
+
export type EnabledTarget = {
|
|
5
|
+
provider: string;
|
|
6
|
+
id: string;
|
|
7
|
+
codeRepo: string;
|
|
8
|
+
baseBranch?: string;
|
|
9
|
+
workflows: Workflow[];
|
|
10
|
+
pollingSource: import("./capabilities.js").PollingEventSource;
|
|
11
|
+
workReader: import("./capabilities.js").WorkReader;
|
|
12
|
+
workTracker: import("./capabilities.js").WorkTracker;
|
|
13
|
+
codeReader: import("./capabilities.js").CodeReader;
|
|
14
|
+
codeHost: import("./capabilities.js").CodeHost;
|
|
15
|
+
codeReviewHost: import("./capabilities.js").CodeReviewHost;
|
|
16
|
+
};
|
|
17
|
+
export type ForgeflowConfig = {
|
|
18
|
+
state: StateStore;
|
|
19
|
+
enabledTargets: EnabledTarget[];
|
|
20
|
+
trustPolicy?: TrustPolicy;
|
|
21
|
+
defaultDeferInterval?: string;
|
|
22
|
+
eventAudit?: {
|
|
23
|
+
storeRawPayloads?: boolean | "redacted";
|
|
24
|
+
};
|
|
25
|
+
capabilities?: Record<string, unknown>;
|
|
26
|
+
};
|
|
27
|
+
export type EnvReader = {
|
|
28
|
+
required(name: string): string;
|
|
29
|
+
optional(name: string): string | undefined;
|
|
30
|
+
optional(name: string, defaultValue: string): string;
|
|
31
|
+
};
|
|
32
|
+
export declare function createEnvReader(source?: NodeJS.ProcessEnv): EnvReader;
|
|
33
|
+
export declare function defineConfig(factory: (ctx: {
|
|
34
|
+
env: EnvReader;
|
|
35
|
+
}) => ForgeflowConfig | Promise<ForgeflowConfig>): (ctx: {
|
|
36
|
+
env: EnvReader;
|
|
37
|
+
}) => ForgeflowConfig | Promise<ForgeflowConfig>;
|
|
38
|
+
export declare function defaultTrustPolicy(): TrustPolicy;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export function createEnvReader(source = process.env) {
|
|
2
|
+
function optional(name, defaultValue) {
|
|
3
|
+
return source[name] ?? defaultValue;
|
|
4
|
+
}
|
|
5
|
+
return {
|
|
6
|
+
required(name) {
|
|
7
|
+
const value = source[name];
|
|
8
|
+
if (!value)
|
|
9
|
+
throw new Error(`Missing required environment variable ${name}`);
|
|
10
|
+
return value;
|
|
11
|
+
},
|
|
12
|
+
optional,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export function defineConfig(factory) {
|
|
16
|
+
return factory;
|
|
17
|
+
}
|
|
18
|
+
export function defaultTrustPolicy() {
|
|
19
|
+
return {
|
|
20
|
+
async canProcessEvent() {
|
|
21
|
+
return { allowed: true };
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/core/config.ts"],"names":[],"mappings":"AAiCA,MAAM,UAAU,eAAe,CAAC,SAA4B,OAAO,CAAC,GAAG;IAGrE,SAAS,QAAQ,CAAC,IAAY,EAAE,YAAqB;QACnD,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,YAAY,CAAA;IACrC,CAAC;IAED,OAAO;QACL,QAAQ,CAAC,IAAY;YACnB,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAA;YAC1B,IAAI,CAAC,KAAK;gBAAE,MAAM,IAAI,KAAK,CAAC,yCAAyC,IAAI,EAAE,CAAC,CAAA;YAC5E,OAAO,KAAK,CAAA;QACd,CAAC;QACD,QAAQ;KACT,CAAA;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,OAAgF;IAC3G,OAAO,OAAO,CAAA;AAChB,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,OAAO;QACL,KAAK,CAAC,eAAe;YACnB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAA;QAC1B,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { ForgeflowConfig } from "./config.js";
|
|
2
|
+
export declare class ForgeflowGateway {
|
|
3
|
+
private readonly config;
|
|
4
|
+
constructor(config: ForgeflowConfig);
|
|
5
|
+
runOnce(options?: {
|
|
6
|
+
maxEvents?: number;
|
|
7
|
+
limit?: number;
|
|
8
|
+
parallel?: number;
|
|
9
|
+
}): Promise<{
|
|
10
|
+
poll: {
|
|
11
|
+
events: number;
|
|
12
|
+
commands: number;
|
|
13
|
+
deferred: number;
|
|
14
|
+
rejected: number;
|
|
15
|
+
};
|
|
16
|
+
worker: {
|
|
17
|
+
processed: number;
|
|
18
|
+
succeeded: number;
|
|
19
|
+
failed: number;
|
|
20
|
+
};
|
|
21
|
+
}>;
|
|
22
|
+
poll(options?: {
|
|
23
|
+
maxEvents?: number;
|
|
24
|
+
}): Promise<{
|
|
25
|
+
events: number;
|
|
26
|
+
commands: number;
|
|
27
|
+
deferred: number;
|
|
28
|
+
rejected: number;
|
|
29
|
+
}>;
|
|
30
|
+
/** Re-runs match() for a deferred command against its original triggering event, so a still-blocked condition reschedules instead of blindly proceeding. */
|
|
31
|
+
private recheckDeferred;
|
|
32
|
+
private handleEvent;
|
|
33
|
+
worker(options?: {
|
|
34
|
+
limit?: number;
|
|
35
|
+
parallel?: number;
|
|
36
|
+
}): Promise<{
|
|
37
|
+
processed: number;
|
|
38
|
+
succeeded: number;
|
|
39
|
+
failed: number;
|
|
40
|
+
}>;
|
|
41
|
+
private runCommand;
|
|
42
|
+
private findTargetForCommand;
|
|
43
|
+
}
|
|
44
|
+
export declare function createGateway(config: ForgeflowConfig): ForgeflowGateway;
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
import { createCommandState } from "./state.js";
|
|
2
|
+
import { codeTargetFromEnabledTarget, defaultCommandId } from "./workflow.js";
|
|
3
|
+
import { defaultTrustPolicy } from "./config.js";
|
|
4
|
+
function nowIso() { return new Date().toISOString(); }
|
|
5
|
+
function isActiveStatus(status) {
|
|
6
|
+
return status === "queued" || status === "deferred" || status === "running";
|
|
7
|
+
}
|
|
8
|
+
function workTargetKey(target) {
|
|
9
|
+
return `${target.provider}:${target.repo ?? target.project ?? ""}:${target.kind}:${target.id}`;
|
|
10
|
+
}
|
|
11
|
+
function addInterval(from, interval = "15m") {
|
|
12
|
+
const match = /^(\d+)\s*([smhd])$/.exec(interval);
|
|
13
|
+
if (!match)
|
|
14
|
+
return new Date(from.getTime() + 15 * 60_000).toISOString();
|
|
15
|
+
const n = Number(match[1]);
|
|
16
|
+
const unit = match[2];
|
|
17
|
+
const ms = unit === "s" ? n * 1000 : unit === "m" ? n * 60_000 : unit === "h" ? n * 3_600_000 : n * 86_400_000;
|
|
18
|
+
return new Date(from.getTime() + ms).toISOString();
|
|
19
|
+
}
|
|
20
|
+
export class ForgeflowGateway {
|
|
21
|
+
config;
|
|
22
|
+
constructor(config) {
|
|
23
|
+
this.config = config;
|
|
24
|
+
}
|
|
25
|
+
async runOnce(options = {}) {
|
|
26
|
+
const poll = await this.poll({ maxEvents: options.maxEvents });
|
|
27
|
+
const worker = await this.worker({ limit: options.limit, parallel: options.parallel });
|
|
28
|
+
return { poll, worker };
|
|
29
|
+
}
|
|
30
|
+
async poll(options = {}) {
|
|
31
|
+
const maxEvents = options.maxEvents ?? 100;
|
|
32
|
+
let budget = maxEvents;
|
|
33
|
+
let eventsSeen = 0, commands = 0, deferred = 0, rejected = 0;
|
|
34
|
+
for (const target of this.config.enabledTargets) {
|
|
35
|
+
if (budget <= 0)
|
|
36
|
+
break;
|
|
37
|
+
const sourceKey = `${target.provider}:${target.id}`;
|
|
38
|
+
const cursor = await this.config.state.getCursor(sourceKey);
|
|
39
|
+
const batch = await target.pollingSource.poll({ cursor, maxEvents: budget });
|
|
40
|
+
for (const event of batch.events) {
|
|
41
|
+
if (budget <= 0)
|
|
42
|
+
break;
|
|
43
|
+
budget--;
|
|
44
|
+
eventsSeen++;
|
|
45
|
+
const result = await this.handleEvent(target, event);
|
|
46
|
+
if (result === "accepted")
|
|
47
|
+
commands++;
|
|
48
|
+
if (result === "deferred")
|
|
49
|
+
deferred++;
|
|
50
|
+
if (result === "rejected")
|
|
51
|
+
rejected++;
|
|
52
|
+
}
|
|
53
|
+
await this.config.state.setCursor(sourceKey, batch.nextCursor);
|
|
54
|
+
}
|
|
55
|
+
if (budget > 0) {
|
|
56
|
+
const due = await this.config.state.listDueDeferred(nowIso(), budget);
|
|
57
|
+
for (const command of due) {
|
|
58
|
+
budget--;
|
|
59
|
+
const outcome = await this.recheckDeferred(command);
|
|
60
|
+
if (outcome === "queued")
|
|
61
|
+
commands++;
|
|
62
|
+
if (outcome === "deferred")
|
|
63
|
+
deferred++;
|
|
64
|
+
if (outcome === "rejected")
|
|
65
|
+
rejected++;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return { events: eventsSeen, commands, deferred, rejected };
|
|
69
|
+
}
|
|
70
|
+
/** Re-runs match() for a deferred command against its original triggering event, so a still-blocked condition reschedules instead of blindly proceeding. */
|
|
71
|
+
async recheckDeferred(command) {
|
|
72
|
+
const target = this.findTargetForCommand(command);
|
|
73
|
+
const workflow = target?.workflows.find(w => w.id === command.workflowId);
|
|
74
|
+
if (!target || !workflow) {
|
|
75
|
+
await this.config.state.updateCommandStatus(command.id, "failed", { reason: "No enabled target/workflow found" });
|
|
76
|
+
return "rejected";
|
|
77
|
+
}
|
|
78
|
+
const decision = await workflow.match({
|
|
79
|
+
event: command.triggerEvent,
|
|
80
|
+
workReader: target.workReader,
|
|
81
|
+
codeReader: target.codeReader,
|
|
82
|
+
state: this.config.state,
|
|
83
|
+
trustPolicy: this.config.trustPolicy ?? defaultTrustPolicy(),
|
|
84
|
+
});
|
|
85
|
+
if (decision.type === "ignore") {
|
|
86
|
+
await this.config.state.updateCommandStatus(command.id, "rejected", { reason: "No longer applicable on recheck" });
|
|
87
|
+
return "rejected";
|
|
88
|
+
}
|
|
89
|
+
if (decision.type === "reject") {
|
|
90
|
+
await this.config.state.updateCommandStatus(command.id, "rejected", { reason: decision.reason });
|
|
91
|
+
return "rejected";
|
|
92
|
+
}
|
|
93
|
+
if (decision.type === "defer") {
|
|
94
|
+
await this.config.state.updateCommandStatus(command.id, "deferred", {
|
|
95
|
+
reason: decision.reason,
|
|
96
|
+
nextCheckAt: decision.nextCheckAt ?? addInterval(new Date(), this.config.defaultDeferInterval).toString(),
|
|
97
|
+
});
|
|
98
|
+
return "deferred";
|
|
99
|
+
}
|
|
100
|
+
await this.config.state.updateCommandStatus(command.id, "queued");
|
|
101
|
+
return "queued";
|
|
102
|
+
}
|
|
103
|
+
async handleEvent(target, event) {
|
|
104
|
+
if (await this.config.state.hasProcessedEvent(event.id))
|
|
105
|
+
return "ignored";
|
|
106
|
+
await this.config.state.recordEvent(event, { rawPayload: this.config.eventAudit?.storeRawPayloads ? event.raw : undefined });
|
|
107
|
+
if (event.kind === "comment_created" && event.comment?.body.trim() === "/agent retry") {
|
|
108
|
+
const failed = await this.config.state.findLatestFailedCommandForTarget(workTargetKey(event.workTarget));
|
|
109
|
+
if (failed)
|
|
110
|
+
await this.config.state.updateCommandStatus(failed.id, "queued");
|
|
111
|
+
await this.config.state.markEventProcessed(event.id);
|
|
112
|
+
return failed ? "accepted" : "ignored";
|
|
113
|
+
}
|
|
114
|
+
const trust = await (this.config.trustPolicy ?? defaultTrustPolicy()).canProcessEvent({ event });
|
|
115
|
+
if (!trust.allowed) {
|
|
116
|
+
await this.config.state.markEventProcessed(event.id);
|
|
117
|
+
return "rejected";
|
|
118
|
+
}
|
|
119
|
+
for (const workflow of target.workflows) {
|
|
120
|
+
const decision = await workflow.match({
|
|
121
|
+
event,
|
|
122
|
+
workReader: target.workReader,
|
|
123
|
+
codeReader: target.codeReader,
|
|
124
|
+
state: this.config.state,
|
|
125
|
+
trustPolicy: this.config.trustPolicy ?? defaultTrustPolicy(),
|
|
126
|
+
});
|
|
127
|
+
if (decision.type === "ignore")
|
|
128
|
+
continue;
|
|
129
|
+
const commandId = defaultCommandId({ event, workflowId: workflow.id });
|
|
130
|
+
if (decision.type === "reject") {
|
|
131
|
+
await this.config.state.markEventProcessed(event.id);
|
|
132
|
+
return "rejected";
|
|
133
|
+
}
|
|
134
|
+
const existing = await this.config.state.getCommand(commandId);
|
|
135
|
+
if (existing && isActiveStatus(existing.status)) {
|
|
136
|
+
await this.config.state.markEventProcessed(event.id);
|
|
137
|
+
return "ignored";
|
|
138
|
+
}
|
|
139
|
+
if (existing) {
|
|
140
|
+
// Prior run for this trigger reached a terminal state (succeeded/failed/rejected);
|
|
141
|
+
// re-queue it in place rather than creating a duplicate command with the same id.
|
|
142
|
+
await this.config.state.updateCommandStatus(commandId, decision.type === "defer" ? "deferred" : "queued", {
|
|
143
|
+
reason: decision.type === "defer" ? decision.reason : undefined,
|
|
144
|
+
nextCheckAt: decision.type === "defer" ? decision.nextCheckAt ?? addInterval(new Date(), this.config.defaultDeferInterval).toString() : undefined,
|
|
145
|
+
});
|
|
146
|
+
await this.config.state.markEventProcessed(event.id);
|
|
147
|
+
return decision.type === "defer" ? "deferred" : "accepted";
|
|
148
|
+
}
|
|
149
|
+
const snapshot = await target.workReader.getTarget(event.workTarget).catch(() => undefined);
|
|
150
|
+
const createdAt = nowIso();
|
|
151
|
+
const command = {
|
|
152
|
+
id: commandId,
|
|
153
|
+
workflowId: workflow.id,
|
|
154
|
+
triggerEvent: event,
|
|
155
|
+
workTarget: event.workTarget,
|
|
156
|
+
codeTarget: codeTargetFromEnabledTarget(target),
|
|
157
|
+
title: decision.type === "accept" && decision.title ? decision.title : snapshot?.title ?? `${workflow.id} for ${event.workTarget.kind} ${event.workTarget.id}`,
|
|
158
|
+
body: decision.type === "accept" && decision.body ? decision.body : snapshot?.body,
|
|
159
|
+
status: decision.type === "defer" ? "deferred" : "queued",
|
|
160
|
+
deferReason: decision.type === "defer" ? decision.reason : undefined,
|
|
161
|
+
nextCheckAt: decision.type === "defer" ? decision.nextCheckAt ?? addInterval(new Date(), this.config.defaultDeferInterval).toString() : undefined,
|
|
162
|
+
createdAt,
|
|
163
|
+
updatedAt: createdAt,
|
|
164
|
+
};
|
|
165
|
+
await this.config.state.createCommand(command);
|
|
166
|
+
await this.config.state.markEventProcessed(event.id);
|
|
167
|
+
return decision.type === "defer" ? "deferred" : "accepted";
|
|
168
|
+
}
|
|
169
|
+
await this.config.state.markEventProcessed(event.id);
|
|
170
|
+
return "ignored";
|
|
171
|
+
}
|
|
172
|
+
async worker(options = {}) {
|
|
173
|
+
const parallel = Math.max(1, options.parallel ?? 3);
|
|
174
|
+
const limit = options.limit ?? parallel;
|
|
175
|
+
const commands = await this.config.state.claimQueued(limit);
|
|
176
|
+
let succeeded = 0, failed = 0;
|
|
177
|
+
for (let index = 0; index < commands.length; index += parallel) {
|
|
178
|
+
const batch = commands.slice(index, index + parallel);
|
|
179
|
+
const settled = await Promise.allSettled(batch.map(command => this.runCommand(command)));
|
|
180
|
+
for (const outcome of settled) {
|
|
181
|
+
if (outcome.status === "fulfilled" && outcome.value === "succeeded")
|
|
182
|
+
succeeded++;
|
|
183
|
+
else
|
|
184
|
+
failed++;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return { processed: commands.length, succeeded, failed };
|
|
188
|
+
}
|
|
189
|
+
async runCommand(command) {
|
|
190
|
+
const target = this.findTargetForCommand(command);
|
|
191
|
+
const workflow = target?.workflows.find(w => w.id === command.workflowId);
|
|
192
|
+
if (!target || !workflow) {
|
|
193
|
+
await this.config.state.updateCommandStatus(command.id, "failed", { reason: "No enabled target/workflow found" });
|
|
194
|
+
return "failed";
|
|
195
|
+
}
|
|
196
|
+
const startedAt = nowIso();
|
|
197
|
+
await this.config.state.recordWorkflowRun({ commandId: command.id, workflowId: command.workflowId, status: "running", startedAt });
|
|
198
|
+
try {
|
|
199
|
+
const result = await workflow.run({
|
|
200
|
+
command,
|
|
201
|
+
workReader: target.workReader,
|
|
202
|
+
workTracker: target.workTracker,
|
|
203
|
+
codeHost: target.codeHost,
|
|
204
|
+
codeReviewHost: target.codeReviewHost,
|
|
205
|
+
commandState: createCommandState(this.config.state, command.id),
|
|
206
|
+
capabilities: { optional: (name) => this.config.capabilities?.[name] },
|
|
207
|
+
});
|
|
208
|
+
await this.config.state.updateCommandStatus(command.id, "succeeded");
|
|
209
|
+
await this.config.state.recordWorkflowRun({ commandId: command.id, workflowId: command.workflowId, status: "succeeded", startedAt, completedAt: nowIso(), summary: result?.summary });
|
|
210
|
+
return "succeeded";
|
|
211
|
+
}
|
|
212
|
+
catch (error) {
|
|
213
|
+
await this.config.state.updateCommandStatus(command.id, "failed", { reason: error instanceof Error ? error.message : String(error) });
|
|
214
|
+
await this.config.state.recordWorkflowRun({ commandId: command.id, workflowId: command.workflowId, status: "failed", startedAt, completedAt: nowIso(), error: error instanceof Error ? error.stack ?? error.message : String(error) });
|
|
215
|
+
return "failed";
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
findTargetForCommand(command) {
|
|
219
|
+
return this.config.enabledTargets.find(t => t.provider === command.codeTarget.provider && t.codeRepo === command.codeTarget.repo);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
export function createGateway(config) {
|
|
223
|
+
return new ForgeflowGateway(config);
|
|
224
|
+
}
|
|
225
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/core/engine.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { AgentCommand, AgentCommandStatus, ChangeRequestRef, ExecutionResult, NormalizedEvent } from "./types.js";
|
|
2
|
+
export interface StateStore {
|
|
3
|
+
getCursor(sourceKey: string): Promise<{
|
|
4
|
+
value?: string;
|
|
5
|
+
} | undefined>;
|
|
6
|
+
setCursor(sourceKey: string, cursor?: {
|
|
7
|
+
value?: string;
|
|
8
|
+
}): Promise<void>;
|
|
9
|
+
hasProcessedEvent(eventId: string): Promise<boolean>;
|
|
10
|
+
recordEvent(event: NormalizedEvent, options?: {
|
|
11
|
+
rawPayload?: unknown;
|
|
12
|
+
}): Promise<void>;
|
|
13
|
+
markEventProcessed(eventId: string): Promise<void>;
|
|
14
|
+
hasCommand(commandId: string): Promise<boolean>;
|
|
15
|
+
createCommand(command: AgentCommand): Promise<void>;
|
|
16
|
+
updateCommandStatus(commandId: string, status: AgentCommandStatus, input?: {
|
|
17
|
+
reason?: string;
|
|
18
|
+
nextCheckAt?: string;
|
|
19
|
+
}): Promise<void>;
|
|
20
|
+
getCommand(commandId: string): Promise<AgentCommand | undefined>;
|
|
21
|
+
findLatestFailedCommandForTarget(targetKey: string): Promise<AgentCommand | undefined>;
|
|
22
|
+
listDueDeferred(now: string, limit: number): Promise<AgentCommand[]>;
|
|
23
|
+
claimQueued(limit: number): Promise<AgentCommand[]>;
|
|
24
|
+
getLinkedChangeRequest(commandId: string): Promise<ChangeRequestRef | undefined>;
|
|
25
|
+
linkChangeRequest(commandId: string, ref: ChangeRequestRef): Promise<void>;
|
|
26
|
+
getLastExecutionResult(commandId: string): Promise<ExecutionResult | undefined>;
|
|
27
|
+
recordWorkflowRun(input: {
|
|
28
|
+
commandId: string;
|
|
29
|
+
workflowId: string;
|
|
30
|
+
status: "running" | "succeeded" | "failed";
|
|
31
|
+
startedAt?: string;
|
|
32
|
+
completedAt?: string;
|
|
33
|
+
summary?: string;
|
|
34
|
+
execution?: ExecutionResult;
|
|
35
|
+
error?: string;
|
|
36
|
+
}): Promise<void>;
|
|
37
|
+
}
|
|
38
|
+
export declare function createCommandState(state: StateStore, commandId: string): {
|
|
39
|
+
getLinkedChangeRequest: () => Promise<ChangeRequestRef | undefined>;
|
|
40
|
+
linkChangeRequest: (ref: ChangeRequestRef) => Promise<void>;
|
|
41
|
+
getLastExecutionResult: () => Promise<ExecutionResult | undefined>;
|
|
42
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export function createCommandState(state, commandId) {
|
|
2
|
+
return {
|
|
3
|
+
getLinkedChangeRequest: () => state.getLinkedChangeRequest(commandId),
|
|
4
|
+
linkChangeRequest: (ref) => state.linkChangeRequest(commandId, ref),
|
|
5
|
+
getLastExecutionResult: () => state.getLastExecutionResult(commandId),
|
|
6
|
+
};
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["../../src/core/state.ts"],"names":[],"mappings":"AAkCA,MAAM,UAAU,kBAAkB,CAAC,KAAiB,EAAE,SAAiB;IACrE,OAAO;QACL,sBAAsB,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,sBAAsB,CAAC,SAAS,CAAC;QACrE,iBAAiB,EAAE,CAAC,GAAqB,EAAE,EAAE,CAAC,KAAK,CAAC,iBAAiB,CAAC,SAAS,EAAE,GAAG,CAAC;QACrF,sBAAsB,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,sBAAsB,CAAC,SAAS,CAAC;KACtE,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
export type ProviderId = string;
|
|
2
|
+
export type TargetKind = "issue" | "pull_request" | "merge_request" | "project" | "repo" | "tag";
|
|
3
|
+
export type WorkTargetRef = {
|
|
4
|
+
provider: ProviderId;
|
|
5
|
+
id: string;
|
|
6
|
+
kind: TargetKind;
|
|
7
|
+
repo?: string;
|
|
8
|
+
project?: string;
|
|
9
|
+
url?: string;
|
|
10
|
+
};
|
|
11
|
+
export type CodeTargetRef = {
|
|
12
|
+
provider: ProviderId;
|
|
13
|
+
repo: string;
|
|
14
|
+
baseBranch?: string;
|
|
15
|
+
};
|
|
16
|
+
export type ActorRef = {
|
|
17
|
+
provider: ProviderId;
|
|
18
|
+
id: string;
|
|
19
|
+
login: string;
|
|
20
|
+
};
|
|
21
|
+
export type NormalizedEvent = {
|
|
22
|
+
id: string;
|
|
23
|
+
provider: ProviderId;
|
|
24
|
+
source: "poll" | "webhook";
|
|
25
|
+
provenance: "historical" | "synthetic";
|
|
26
|
+
kind: "label_added" | "label_removed" | "comment_created" | "comment_command" | "target_updated" | "target_opened" | "tag_created";
|
|
27
|
+
workTarget: WorkTargetRef;
|
|
28
|
+
actor?: ActorRef;
|
|
29
|
+
label?: string;
|
|
30
|
+
comment?: {
|
|
31
|
+
id: string;
|
|
32
|
+
body: string;
|
|
33
|
+
url?: string;
|
|
34
|
+
};
|
|
35
|
+
command?: string;
|
|
36
|
+
tag?: string;
|
|
37
|
+
occurredAt: string;
|
|
38
|
+
observedAt: string;
|
|
39
|
+
raw?: unknown;
|
|
40
|
+
};
|
|
41
|
+
export type PollCursor = {
|
|
42
|
+
value?: string;
|
|
43
|
+
};
|
|
44
|
+
export type PollBatch = {
|
|
45
|
+
events: NormalizedEvent[];
|
|
46
|
+
nextCursor?: PollCursor;
|
|
47
|
+
};
|
|
48
|
+
export type WorkTargetSnapshot = {
|
|
49
|
+
target: WorkTargetRef;
|
|
50
|
+
title?: string;
|
|
51
|
+
body?: string;
|
|
52
|
+
labels: string[];
|
|
53
|
+
author?: ActorRef;
|
|
54
|
+
url?: string;
|
|
55
|
+
updatedAt?: string;
|
|
56
|
+
state?: "open" | "closed";
|
|
57
|
+
};
|
|
58
|
+
export type CheckoutSpec = {
|
|
59
|
+
cloneUrl: string;
|
|
60
|
+
baseRef: string;
|
|
61
|
+
workBranch: string;
|
|
62
|
+
authRef: string;
|
|
63
|
+
};
|
|
64
|
+
export type BranchRef = {
|
|
65
|
+
provider: ProviderId;
|
|
66
|
+
repo: string;
|
|
67
|
+
name: string;
|
|
68
|
+
url?: string;
|
|
69
|
+
};
|
|
70
|
+
export type CommentRef = {
|
|
71
|
+
id: string;
|
|
72
|
+
url?: string;
|
|
73
|
+
};
|
|
74
|
+
export type ChangeRequestRef = {
|
|
75
|
+
provider: ProviderId;
|
|
76
|
+
id: string;
|
|
77
|
+
url?: string;
|
|
78
|
+
kind: TargetKind;
|
|
79
|
+
};
|
|
80
|
+
export type ChangeRequestDetails = {
|
|
81
|
+
sourceBranch: string;
|
|
82
|
+
sourceSha: string;
|
|
83
|
+
targetBranch: string;
|
|
84
|
+
title: string;
|
|
85
|
+
isCrossRepository: boolean;
|
|
86
|
+
linkedIssueId?: string;
|
|
87
|
+
};
|
|
88
|
+
export type GeneralComment = {
|
|
89
|
+
author?: string;
|
|
90
|
+
body: string;
|
|
91
|
+
createdAt?: string;
|
|
92
|
+
};
|
|
93
|
+
export type ReviewThreadComment = {
|
|
94
|
+
id: string;
|
|
95
|
+
author?: string;
|
|
96
|
+
body: string;
|
|
97
|
+
};
|
|
98
|
+
export type ReviewThread = {
|
|
99
|
+
id: string;
|
|
100
|
+
path?: string;
|
|
101
|
+
line?: number;
|
|
102
|
+
isResolved: boolean;
|
|
103
|
+
comments: ReviewThreadComment[];
|
|
104
|
+
};
|
|
105
|
+
export type InlineReviewComment = {
|
|
106
|
+
path: string;
|
|
107
|
+
line: number;
|
|
108
|
+
body: string;
|
|
109
|
+
};
|
|
110
|
+
export type ExecutionResult = {
|
|
111
|
+
status: "success";
|
|
112
|
+
mode: "pushed_branch";
|
|
113
|
+
summary: string;
|
|
114
|
+
branch: BranchRef;
|
|
115
|
+
commitSha?: string;
|
|
116
|
+
logsUrl?: string;
|
|
117
|
+
artifacts?: Array<{
|
|
118
|
+
name: string;
|
|
119
|
+
url: string;
|
|
120
|
+
}>;
|
|
121
|
+
} | {
|
|
122
|
+
status: "failed";
|
|
123
|
+
summary: string;
|
|
124
|
+
logsUrl?: string;
|
|
125
|
+
error?: string;
|
|
126
|
+
};
|
|
127
|
+
export type AgentCommandStatus = "queued" | "deferred" | "running" | "succeeded" | "failed" | "rejected";
|
|
128
|
+
export type AgentCommand = {
|
|
129
|
+
id: string;
|
|
130
|
+
workflowId: string;
|
|
131
|
+
triggerEvent: NormalizedEvent;
|
|
132
|
+
workTarget: WorkTargetRef;
|
|
133
|
+
codeTarget: CodeTargetRef;
|
|
134
|
+
title: string;
|
|
135
|
+
body?: string;
|
|
136
|
+
status: AgentCommandStatus;
|
|
137
|
+
deferReason?: string;
|
|
138
|
+
nextCheckAt?: string;
|
|
139
|
+
createdAt: string;
|
|
140
|
+
updatedAt: string;
|
|
141
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/core/types.ts"],"names":[],"mappings":""}
|