@usine/orchestrator 0.1.0 → 0.2.1
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 +46 -21
- package/dist/board/items.d.ts +18 -0
- package/dist/board/items.d.ts.map +1 -0
- package/dist/board/items.js +46 -0
- package/dist/board/items.js.map +1 -0
- package/dist/board/projection.d.ts +6 -5
- package/dist/board/projection.d.ts.map +1 -1
- package/dist/board/projection.js +25 -65
- package/dist/board/projection.js.map +1 -1
- package/dist/board/tasks.d.ts +8 -0
- package/dist/board/tasks.d.ts.map +1 -0
- package/dist/board/tasks.js +86 -0
- package/dist/board/tasks.js.map +1 -0
- package/dist/credentials.d.ts +6 -6
- package/dist/credentials.d.ts.map +1 -1
- package/dist/credentials.js +6 -6
- package/dist/credentials.js.map +1 -1
- package/dist/exe-dev.d.ts +5 -4
- package/dist/exe-dev.d.ts.map +1 -1
- package/dist/exe-dev.js +5 -4
- package/dist/exe-dev.js.map +1 -1
- package/dist/idle-triggers.d.ts +1 -1
- package/dist/idle-triggers.d.ts.map +1 -1
- package/dist/idle-triggers.js +3 -1
- package/dist/idle-triggers.js.map +1 -1
- package/dist/label-reconciler.d.ts +30 -0
- package/dist/label-reconciler.d.ts.map +1 -0
- package/dist/label-reconciler.js +119 -0
- package/dist/label-reconciler.js.map +1 -0
- package/dist/local.d.ts +17 -12
- package/dist/local.d.ts.map +1 -1
- package/dist/local.js +20 -13
- package/dist/local.js.map +1 -1
- package/dist/operator/attempt.d.ts +69 -0
- package/dist/operator/attempt.d.ts.map +1 -0
- package/dist/operator/attempt.js +62 -0
- package/dist/operator/attempt.js.map +1 -0
- package/dist/operator/board.d.ts +119 -0
- package/dist/operator/board.d.ts.map +1 -0
- package/dist/operator/board.js +101 -0
- package/dist/operator/board.js.map +1 -0
- package/dist/operator/http.d.ts +180 -0
- package/dist/operator/http.d.ts.map +1 -0
- package/dist/operator/http.js +150 -0
- package/dist/operator/http.js.map +1 -0
- package/dist/operator/need.d.ts +66 -0
- package/dist/operator/need.d.ts.map +1 -0
- package/dist/operator/need.js +70 -0
- package/dist/operator/need.js.map +1 -0
- package/dist/operator/operator.d.ts +68 -0
- package/dist/operator/operator.d.ts.map +1 -0
- package/dist/operator/operator.js +402 -0
- package/dist/operator/operator.js.map +1 -0
- package/dist/operator/overview.d.ts +51 -0
- package/dist/operator/overview.d.ts.map +1 -0
- package/dist/operator/overview.js +48 -0
- package/dist/operator/overview.js.map +1 -0
- package/dist/operator/projection.d.ts +12 -9
- package/dist/operator/projection.d.ts.map +1 -1
- package/dist/operator/projection.js +44 -17
- package/dist/operator/projection.js.map +1 -1
- package/dist/operator/run.d.ts +62 -0
- package/dist/operator/run.d.ts.map +1 -0
- package/dist/operator/run.js +61 -0
- package/dist/operator/run.js.map +1 -0
- package/dist/operator/transcript.d.ts +59 -0
- package/dist/operator/transcript.d.ts.map +1 -0
- package/dist/operator/transcript.js +66 -0
- package/dist/operator/transcript.js.map +1 -0
- package/dist/orchestrator.d.ts +3 -2
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +1 -1
- package/dist/orchestrator.js.map +1 -1
- package/dist/polling/cycle.d.ts +51 -0
- package/dist/polling/cycle.d.ts.map +1 -0
- package/dist/polling/cycle.js +44 -0
- package/dist/polling/cycle.js.map +1 -0
- package/dist/polling/hints.d.ts +20 -0
- package/dist/polling/hints.d.ts.map +1 -0
- package/dist/polling/hints.js +54 -0
- package/dist/polling/hints.js.map +1 -0
- package/dist/polling/loop-evaluation.d.ts +21 -0
- package/dist/polling/loop-evaluation.d.ts.map +1 -0
- package/dist/polling/loop-evaluation.js +131 -0
- package/dist/polling/loop-evaluation.js.map +1 -0
- package/dist/polling/polling.d.ts +36 -0
- package/dist/polling/polling.d.ts.map +1 -0
- package/dist/polling/polling.js +296 -0
- package/dist/polling/polling.js.map +1 -0
- package/dist/polling/repository-sync.d.ts +45 -0
- package/dist/polling/repository-sync.d.ts.map +1 -0
- package/dist/polling/repository-sync.js +169 -0
- package/dist/polling/repository-sync.js.map +1 -0
- package/dist/polling-triggers.d.ts +3 -39
- package/dist/polling-triggers.d.ts.map +1 -1
- package/dist/polling-triggers.js +3 -393
- package/dist/polling-triggers.js.map +1 -1
- package/dist/schedule-triggers.d.ts +17 -15
- package/dist/schedule-triggers.d.ts.map +1 -1
- package/dist/schedule-triggers.js +53 -43
- package/dist/schedule-triggers.js.map +1 -1
- package/dist/scheduler.d.ts +5 -5
- package/dist/scheduler.d.ts.map +1 -1
- package/dist/scheduler.js +30 -44
- package/dist/scheduler.js.map +1 -1
- package/dist/trigger-submission.d.ts +33 -24
- package/dist/trigger-submission.d.ts.map +1 -1
- package/dist/trigger-submission.js +30 -37
- package/dist/trigger-submission.js.map +1 -1
- package/package.json +50 -14
- package/dist/index.d.ts +0 -11
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -11
- package/dist/index.js.map +0 -1
- package/dist/operator/model.d.ts +0 -280
- package/dist/operator/model.d.ts.map +0 -1
- package/dist/operator/model.js +0 -242
- package/dist/operator/model.js.map +0 -1
- package/dist/operator/service.d.ts +0 -36
- package/dist/operator/service.d.ts.map +0 -1
- package/dist/operator/service.js +0 -248
- package/dist/operator/service.js.map +0 -1
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { CycleBudgetAsk } from "@usine/core/cycle-budget";
|
|
2
|
+
import { Schema } from "effect";
|
|
3
|
+
import { LabelPreflightFinding } from "../label-reconciler.ts";
|
|
4
|
+
import { RepositoryPollFailure } from "../polling-triggers.ts";
|
|
5
|
+
import { BoardProjectionError } from "./board.ts";
|
|
6
|
+
declare const ApprovalNeed_base: Schema.Class<ApprovalNeed, Schema.TaggedStruct<"ApprovalNeed", {
|
|
7
|
+
readonly id: Schema.String;
|
|
8
|
+
readonly repository: Schema.brand<Schema.String, "RepositoryName">;
|
|
9
|
+
readonly runId: Schema.brand<Schema.NonEmptyString, "RunId">;
|
|
10
|
+
readonly loop: Schema.brand<Schema.NonEmptyString, "LoopName">;
|
|
11
|
+
}>, {}>;
|
|
12
|
+
/** An operator approval is required before this Run can proceed. */
|
|
13
|
+
export declare class ApprovalNeed extends ApprovalNeed_base {
|
|
14
|
+
}
|
|
15
|
+
declare const RunFailureNeed_base: Schema.Class<RunFailureNeed, Schema.TaggedStruct<"RunFailureNeed", {
|
|
16
|
+
readonly id: Schema.String;
|
|
17
|
+
readonly repository: Schema.brand<Schema.String, "RepositoryName">;
|
|
18
|
+
readonly runId: Schema.brand<Schema.NonEmptyString, "RunId">;
|
|
19
|
+
readonly loop: Schema.brand<Schema.NonEmptyString, "LoopName">;
|
|
20
|
+
readonly failure: Schema.Union<readonly [typeof import("./attempt.ts").InfraFailureView, typeof import("./attempt.ts").DefectFailureView, typeof import("./attempt.ts").AgentFailureView, typeof import("./attempt.ts").RunawayFailureView, typeof import("./attempt.ts").ChildSessionBudgetFailureView]>;
|
|
21
|
+
}>, {}>;
|
|
22
|
+
/** A failed Run requires an operator decision, with its displayable failure details. */
|
|
23
|
+
export declare class RunFailureNeed extends RunFailureNeed_base {
|
|
24
|
+
}
|
|
25
|
+
declare const PollFailureNeed_base: Schema.Class<PollFailureNeed, Schema.TaggedStruct<"PollFailureNeed", {
|
|
26
|
+
readonly id: Schema.String;
|
|
27
|
+
readonly failure: typeof RepositoryPollFailure;
|
|
28
|
+
}>, {}>;
|
|
29
|
+
/** Repository polling failed and needs operator attention. */
|
|
30
|
+
export declare class PollFailureNeed extends PollFailureNeed_base {
|
|
31
|
+
}
|
|
32
|
+
declare const PreflightFindingNeed_base: Schema.Class<PreflightFindingNeed, Schema.TaggedStruct<"PreflightFindingNeed", {
|
|
33
|
+
readonly id: Schema.String;
|
|
34
|
+
readonly finding: typeof LabelPreflightFinding;
|
|
35
|
+
}>, {}>;
|
|
36
|
+
/** A declared repository label could not be provisioned or confirmed. */
|
|
37
|
+
export declare class PreflightFindingNeed extends PreflightFindingNeed_base {
|
|
38
|
+
}
|
|
39
|
+
declare const BoardProjectionNeed_base: Schema.Class<BoardProjectionNeed, Schema.TaggedStruct<"BoardProjectionNeed", {
|
|
40
|
+
readonly id: Schema.String;
|
|
41
|
+
readonly failure: typeof BoardProjectionError;
|
|
42
|
+
}>, {}>;
|
|
43
|
+
/** An Item could not be displayed on its Board and needs operator attention. */
|
|
44
|
+
export declare class BoardProjectionNeed extends BoardProjectionNeed_base {
|
|
45
|
+
}
|
|
46
|
+
declare const ScheduleFailureNeed_base: Schema.Class<ScheduleFailureNeed, Schema.TaggedStruct<"ScheduleFailureNeed", {
|
|
47
|
+
readonly id: Schema.String;
|
|
48
|
+
readonly failure: Schema.Union<readonly [typeof import("../schedule-triggers.ts").ScheduleAgentFailure, typeof import("../schedule-triggers.ts").ScheduleCronFailure, typeof import("../schedule-triggers.ts").ScheduleTickFailure]>;
|
|
49
|
+
}>, {}>;
|
|
50
|
+
/** A scheduled Loop could not evaluate or submit its work. */
|
|
51
|
+
export declare class ScheduleFailureNeed extends ScheduleFailureNeed_base {
|
|
52
|
+
}
|
|
53
|
+
declare const CycleBudgetNeed_base: Schema.Class<CycleBudgetNeed, Schema.TaggedStruct<"CycleBudgetNeed", {
|
|
54
|
+
readonly id: Schema.String;
|
|
55
|
+
readonly ask: typeof CycleBudgetAsk;
|
|
56
|
+
readonly itemLabel: Schema.String;
|
|
57
|
+
readonly itemUrl: Schema.NullOr<Schema.String>;
|
|
58
|
+
}>, {}>;
|
|
59
|
+
/** An Item stopped opening Runs and requires an explicit reset of its shared cycle budget. */
|
|
60
|
+
export declare class CycleBudgetNeed extends CycleBudgetNeed_base {
|
|
61
|
+
}
|
|
62
|
+
/** One reason the factory asks for operator attention in the overview. */
|
|
63
|
+
export declare const NeedView: Schema.Union<readonly [typeof CycleBudgetNeed, typeof ApprovalNeed, typeof BoardProjectionNeed, typeof RunFailureNeed, typeof PollFailureNeed, typeof PreflightFindingNeed, typeof ScheduleFailureNeed]>;
|
|
64
|
+
export type NeedView = typeof NeedView.Type;
|
|
65
|
+
export {};
|
|
66
|
+
//# sourceMappingURL=need.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"need.d.ts","sourceRoot":"","sources":["../../src/operator/need.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAG1D,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAG/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;;;;;;;AAElD,oEAAoE;AACpE,qBAAa,YAAa,SAAQ,iBAOhC;CAAG;;;;;;;;AAEL,wFAAwF;AACxF,qBAAa,cAAe,SAAQ,mBAQlC;CAAG;;;;;AAEL,8DAA8D;AAC9D,qBAAa,eAAgB,SAAQ,oBAKnC;CAAG;;;;;AAEL,yEAAyE;AACzE,qBAAa,oBAAqB,SAAQ,yBAKxC;CAAG;;;;;AAEL,gFAAgF;AAChF,qBAAa,mBAAoB,SAAQ,wBAKvC;CAAG;;;;;AAEL,8DAA8D;AAC9D,qBAAa,mBAAoB,SAAQ,wBAKvC;CAAG;;;;;;;AAEL,8FAA8F;AAC9F,qBAAa,eAAgB,SAAQ,oBAQpC;CAAG;AAEJ,0EAA0E;AAC1E,eAAO,MAAM,QAAQ,0MAQnB,CAAC;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAC,IAAI,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { LoopName } from "@usine/core/loop";
|
|
2
|
+
import { CycleBudgetAsk } from "@usine/core/cycle-budget";
|
|
3
|
+
import { RepositoryName } from "@usine/core/repository";
|
|
4
|
+
import { RunId } from "@usine/core/run";
|
|
5
|
+
import { Schema } from "effect";
|
|
6
|
+
import { LabelPreflightFinding } from "../label-reconciler.js";
|
|
7
|
+
import { RepositoryPollFailure } from "../polling-triggers.js";
|
|
8
|
+
import { ScheduleFailure } from "../schedule-triggers.js";
|
|
9
|
+
import { FailureView } from "./attempt.js";
|
|
10
|
+
import { BoardProjectionError } from "./board.js";
|
|
11
|
+
/** An operator approval is required before this Run can proceed. */
|
|
12
|
+
export class ApprovalNeed extends Schema.TaggedClass("@usine/orchestrator/ApprovalNeed")("ApprovalNeed", {
|
|
13
|
+
id: Schema.String,
|
|
14
|
+
repository: RepositoryName,
|
|
15
|
+
runId: RunId,
|
|
16
|
+
loop: LoopName,
|
|
17
|
+
}) {
|
|
18
|
+
}
|
|
19
|
+
/** A failed Run requires an operator decision, with its displayable failure details. */
|
|
20
|
+
export class RunFailureNeed extends Schema.TaggedClass("@usine/orchestrator/RunFailureNeed")("RunFailureNeed", {
|
|
21
|
+
id: Schema.String,
|
|
22
|
+
repository: RepositoryName,
|
|
23
|
+
runId: RunId,
|
|
24
|
+
loop: LoopName,
|
|
25
|
+
failure: FailureView,
|
|
26
|
+
}) {
|
|
27
|
+
}
|
|
28
|
+
/** Repository polling failed and needs operator attention. */
|
|
29
|
+
export class PollFailureNeed extends Schema.TaggedClass("@usine/orchestrator/PollFailureNeed")("PollFailureNeed", {
|
|
30
|
+
id: Schema.String,
|
|
31
|
+
failure: RepositoryPollFailure,
|
|
32
|
+
}) {
|
|
33
|
+
}
|
|
34
|
+
/** A declared repository label could not be provisioned or confirmed. */
|
|
35
|
+
export class PreflightFindingNeed extends Schema.TaggedClass("@usine/orchestrator/PreflightFindingNeed")("PreflightFindingNeed", {
|
|
36
|
+
id: Schema.String,
|
|
37
|
+
finding: LabelPreflightFinding,
|
|
38
|
+
}) {
|
|
39
|
+
}
|
|
40
|
+
/** An Item could not be displayed on its Board and needs operator attention. */
|
|
41
|
+
export class BoardProjectionNeed extends Schema.TaggedClass("@usine/orchestrator/BoardProjectionNeed")("BoardProjectionNeed", {
|
|
42
|
+
id: Schema.String,
|
|
43
|
+
failure: BoardProjectionError,
|
|
44
|
+
}) {
|
|
45
|
+
}
|
|
46
|
+
/** A scheduled Loop could not evaluate or submit its work. */
|
|
47
|
+
export class ScheduleFailureNeed extends Schema.TaggedClass("@usine/orchestrator/ScheduleFailureNeed")("ScheduleFailureNeed", {
|
|
48
|
+
id: Schema.String,
|
|
49
|
+
failure: ScheduleFailure,
|
|
50
|
+
}) {
|
|
51
|
+
}
|
|
52
|
+
/** An Item stopped opening Runs and requires an explicit reset of its shared cycle budget. */
|
|
53
|
+
export class CycleBudgetNeed extends Schema.TaggedClass("CycleBudgetNeed")("CycleBudgetNeed", {
|
|
54
|
+
id: Schema.String,
|
|
55
|
+
ask: CycleBudgetAsk,
|
|
56
|
+
itemLabel: Schema.String,
|
|
57
|
+
itemUrl: Schema.NullOr(Schema.String),
|
|
58
|
+
}) {
|
|
59
|
+
}
|
|
60
|
+
/** One reason the factory asks for operator attention in the overview. */
|
|
61
|
+
export const NeedView = Schema.Union([
|
|
62
|
+
CycleBudgetNeed,
|
|
63
|
+
ApprovalNeed,
|
|
64
|
+
BoardProjectionNeed,
|
|
65
|
+
RunFailureNeed,
|
|
66
|
+
PollFailureNeed,
|
|
67
|
+
PreflightFindingNeed,
|
|
68
|
+
ScheduleFailureNeed,
|
|
69
|
+
]);
|
|
70
|
+
//# sourceMappingURL=need.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"need.js","sourceRoot":"","sources":["../../src/operator/need.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AACxC,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAElD,oEAAoE;AACpE,MAAM,OAAO,YAAa,SAAQ,MAAM,CAAC,WAAW,CAClD,kCAAkC,CACnC,CAAC,cAAc,EAAE;IAChB,EAAE,EAAE,MAAM,CAAC,MAAM;IACjB,UAAU,EAAE,cAAc;IAC1B,KAAK,EAAE,KAAK;IACZ,IAAI,EAAE,QAAQ;CACf,CAAC;CAAG;AAEL,wFAAwF;AACxF,MAAM,OAAO,cAAe,SAAQ,MAAM,CAAC,WAAW,CACpD,oCAAoC,CACrC,CAAC,gBAAgB,EAAE;IAClB,EAAE,EAAE,MAAM,CAAC,MAAM;IACjB,UAAU,EAAE,cAAc;IAC1B,KAAK,EAAE,KAAK;IACZ,IAAI,EAAE,QAAQ;IACd,OAAO,EAAE,WAAW;CACrB,CAAC;CAAG;AAEL,8DAA8D;AAC9D,MAAM,OAAO,eAAgB,SAAQ,MAAM,CAAC,WAAW,CACrD,qCAAqC,CACtC,CAAC,iBAAiB,EAAE;IACnB,EAAE,EAAE,MAAM,CAAC,MAAM;IACjB,OAAO,EAAE,qBAAqB;CAC/B,CAAC;CAAG;AAEL,yEAAyE;AACzE,MAAM,OAAO,oBAAqB,SAAQ,MAAM,CAAC,WAAW,CAC1D,0CAA0C,CAC3C,CAAC,sBAAsB,EAAE;IACxB,EAAE,EAAE,MAAM,CAAC,MAAM;IACjB,OAAO,EAAE,qBAAqB;CAC/B,CAAC;CAAG;AAEL,gFAAgF;AAChF,MAAM,OAAO,mBAAoB,SAAQ,MAAM,CAAC,WAAW,CACzD,yCAAyC,CAC1C,CAAC,qBAAqB,EAAE;IACvB,EAAE,EAAE,MAAM,CAAC,MAAM;IACjB,OAAO,EAAE,oBAAoB;CAC9B,CAAC;CAAG;AAEL,8DAA8D;AAC9D,MAAM,OAAO,mBAAoB,SAAQ,MAAM,CAAC,WAAW,CACzD,yCAAyC,CAC1C,CAAC,qBAAqB,EAAE;IACvB,EAAE,EAAE,MAAM,CAAC,MAAM;IACjB,OAAO,EAAE,eAAe;CACzB,CAAC;CAAG;AAEL,8FAA8F;AAC9F,MAAM,OAAO,eAAgB,SAAQ,MAAM,CAAC,WAAW,CAAkB,iBAAiB,CAAC,CACzF,iBAAiB,EACjB;IACE,EAAE,EAAE,MAAM,CAAC,MAAM;IACjB,GAAG,EAAE,cAAc;IACnB,SAAS,EAAE,MAAM,CAAC,MAAM;IACxB,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;CACtC,CACF;CAAG;AAEJ,0EAA0E;AAC1E,MAAM,CAAC,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC;IACnC,eAAe;IACf,YAAY;IACZ,mBAAmB;IACnB,cAAc;IACd,eAAe;IACf,oBAAoB;IACpB,mBAAmB;CACpB,CAAC,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { LoadedInstance } from "@usine/config/instance";
|
|
2
|
+
import { CycleBudgetAskId } from "@usine/core/cycle-budget";
|
|
3
|
+
import { CycleBudgets, type CycleBudgetResetRejectedError } from "@usine/engine/cycle-budget";
|
|
4
|
+
import { LoopName } from "@usine/core/loop";
|
|
5
|
+
import { RunId } from "@usine/core/run";
|
|
6
|
+
import { RunEngine, type EnginePersistenceError, type RunEngineError, type RunStoreError } from "@usine/engine/engine";
|
|
7
|
+
import { ItemProjectionReader } from "@usine/engine/item-projection";
|
|
8
|
+
import { LoopControl, type LoopRuntimeState } from "@usine/engine/loop-control";
|
|
9
|
+
import { RunReader } from "@usine/engine/run-reader";
|
|
10
|
+
import { RepositoryLabelProjectionReader } from "@usine/engine/repository-label-projection";
|
|
11
|
+
import { GitHubItemWriter } from "@usine/github/item-writer";
|
|
12
|
+
import type { GitHubAppError } from "@usine/github/github-app";
|
|
13
|
+
import { Context, Effect, Layer } from "effect";
|
|
14
|
+
import { PollingTriggers } from "../polling-triggers.ts";
|
|
15
|
+
import { ScheduleTriggers } from "../schedule-triggers.ts";
|
|
16
|
+
import { type BoardItemNotFoundError, type BoardLaneNotFoundError, type BoardLaneReadOnlyError, type BoardMove, type BoardProjectNotFoundError } from "./board.ts";
|
|
17
|
+
import { type TranscriptAttemptNotFoundError, type TranscriptRequest } from "./transcript.ts";
|
|
18
|
+
import { ReadyOverview } from "./overview.ts";
|
|
19
|
+
import { RunDetailView, EditAndRetry, SessionIntervention, RosterAgentNotFoundError } from "./run.ts";
|
|
20
|
+
export type BoardMoveError = BoardItemNotFoundError | BoardLaneNotFoundError | BoardLaneReadOnlyError | BoardProjectNotFoundError | EnginePersistenceError | GitHubAppError;
|
|
21
|
+
export type OperatorError = RosterAgentNotFoundError | RunEngineError | TranscriptAttemptNotFoundError | BoardMoveError | CycleBudgetResetRejectedError;
|
|
22
|
+
export type OperatorObservationError = RunStoreError | TranscriptAttemptNotFoundError;
|
|
23
|
+
declare const OperatorObservation_base: Context.ServiceClass<OperatorObservation, "@usine/orchestrator/OperatorObservation", {
|
|
24
|
+
/** Builds the supplied instance's Projects, Boards, Runs, and items needing operator attention. */
|
|
25
|
+
readonly overview: (instance: LoadedInstance) => Effect.Effect<ReadyOverview, OperatorObservationError>;
|
|
26
|
+
/** Reads one Run and a bounded transcript page; `request` selects the latest, newer, or earlier activity. */
|
|
27
|
+
readonly detail: (runId: RunId, request?: TranscriptRequest) => Effect.Effect<RunDetailView, OperatorObservationError>;
|
|
28
|
+
}>;
|
|
29
|
+
/** The read-only operator surface. It needs no trigger execution to build. */
|
|
30
|
+
export declare class OperatorObservation extends OperatorObservation_base {
|
|
31
|
+
static readonly layer: Layer.Layer<OperatorObservation, never, CycleBudgets | ItemProjectionReader | LoopControl | PollingTriggers | RepositoryLabelProjectionReader | RunReader | ScheduleTriggers>;
|
|
32
|
+
}
|
|
33
|
+
declare const Operator_base: Context.ServiceClass<Operator, "@usine/orchestrator/Operator", {
|
|
34
|
+
/** Builds the supplied instance's Projects, Boards, Runs, and items needing operator attention. */
|
|
35
|
+
readonly overview: (instance: LoadedInstance) => Effect.Effect<ReadyOverview, OperatorObservationError>;
|
|
36
|
+
/** Reads one Run and a bounded transcript page; `request` selects the latest, newer, or earlier activity. */
|
|
37
|
+
readonly detail: (runId: RunId, request?: TranscriptRequest) => Effect.Effect<RunDetailView, OperatorObservationError>;
|
|
38
|
+
} & {
|
|
39
|
+
/** Answers the Item's current cycle Ask; polling refreshes its state before more Runs open. */
|
|
40
|
+
readonly resetCycleBudget: (askId: CycleBudgetAskId) => Effect.Effect<void, OperatorError>;
|
|
41
|
+
/** Records approval for a waiting Run; execution still waits for Worker capacity. */
|
|
42
|
+
readonly approve: (runId: RunId) => Effect.Effect<void, OperatorError>;
|
|
43
|
+
/** Requests a new Attempt for a failed Run using its effective task and Agent. */
|
|
44
|
+
readonly retry: (runId: RunId) => Effect.Effect<void, OperatorError>;
|
|
45
|
+
/** Atomically amends a failed Run before requesting its next Attempt. */
|
|
46
|
+
readonly editAndRetry: (instance: LoadedInstance, runId: RunId, edit: EditAndRetry) => Effect.Effect<void, OperatorError>;
|
|
47
|
+
/** Delivers an attributed action through a live session's Attempt channel. */
|
|
48
|
+
readonly intervene: (instance: LoadedInstance, runId: RunId, input: SessionIntervention) => Effect.Effect<void, OperatorError>;
|
|
49
|
+
/** Cancels an open Run and interrupts its current Attempt; an already closed Run is unchanged. */
|
|
50
|
+
readonly cancel: (runId: RunId) => Effect.Effect<void, OperatorError>;
|
|
51
|
+
/** Swaps lane labels on GitHub and hints an authoritative refresh of its Board position and Loop matches. */
|
|
52
|
+
readonly moveBoardItem: (instance: LoadedInstance, move: BoardMove) => Effect.Effect<void, BoardMoveError>;
|
|
53
|
+
/** Pauses new work for a Loop while its existing Runs continue. */
|
|
54
|
+
readonly pause: (loop: LoopName) => Effect.Effect<LoopRuntimeState, EnginePersistenceError>;
|
|
55
|
+
/** Allows the Loop's triggers to open new Runs again. */
|
|
56
|
+
readonly unpause: (loop: LoopName) => Effect.Effect<LoopRuntimeState, EnginePersistenceError>;
|
|
57
|
+
}>;
|
|
58
|
+
/** The operator's view of the factory and actions for approving, retrying, canceling, and directing work. */
|
|
59
|
+
export declare class Operator extends Operator_base {
|
|
60
|
+
static readonly layer: Layer.Layer<Operator, never, CycleBudgets | GitHubItemWriter | ItemProjectionReader | LoopControl | PollingTriggers | RepositoryLabelProjectionReader | RunEngine | RunReader | ScheduleTriggers>;
|
|
61
|
+
}
|
|
62
|
+
export { ProjectStatus, ProjectView, LoopView, ReadyOverview, NeedsSetupOverview, FactoryOverview, } from "./overview.ts";
|
|
63
|
+
export { RunStateTag, RunView, RunDetailView, EditAndRetry, SessionIntervention, RosterAgentNotFoundError, } from "./run.ts";
|
|
64
|
+
export { InfraFailureView, DefectFailureView, AgentFailureView, RunawayFailureView, ChildSessionBudgetFailureView, FailureView, SucceededAttemptView, FailedAttemptView, CanceledAttemptView, AttemptOutcomeView, AttemptView, } from "./attempt.ts";
|
|
65
|
+
export { BoardLaneId, BoardLaneOrigin, BoardItemLink, BoardTaskView, WritableBoardLaneView, ReadOnlyBoardLaneView, BoardLaneView, BoardProjectionError, BoardView, LabelView, BoardMove, BoardProjectNotFoundError, BoardItemNotFoundError, BoardLaneNotFoundError, BoardLaneReadOnlyError, } from "./board.ts";
|
|
66
|
+
export { ApprovalNeed, CycleBudgetNeed, RunFailureNeed, PollFailureNeed, PreflightFindingNeed, BoardProjectionNeed, ScheduleFailureNeed, NeedView, } from "./need.ts";
|
|
67
|
+
export { FailedTranscriptView, TranscriptPayload, TranscriptEventView, TranscriptPageCursor, ColdTranscriptRequest, TailTranscriptRequest, EarlierTranscriptRequest, TranscriptRequest, TranscriptAttemptNotFoundError, } from "./transcript.ts";
|
|
68
|
+
//# sourceMappingURL=operator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"operator.d.ts","sourceRoot":"","sources":["../../src/operator/operator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
import { transcriptEvent } from "./projection.js";
|
|
2
|
+
import { TranscriptSessionsQuery } from "@usine/engine/transcript";
|
|
3
|
+
import { GitHubLabelName } from "@usine/config/label";
|
|
4
|
+
import { resolveParentModel } from "@usine/core/agent";
|
|
5
|
+
import { AttemptContext } from "@usine/core/attempt";
|
|
6
|
+
import { Intervention } from "@usine/core/attempt/channel";
|
|
7
|
+
import { Event, DedupKey, EventId } from "@usine/core/event";
|
|
8
|
+
import { CycleBudgetAskId } from "@usine/core/cycle-budget";
|
|
9
|
+
import { CycleBudgets } from "@usine/engine/cycle-budget";
|
|
10
|
+
import { LoopName } from "@usine/core/loop";
|
|
11
|
+
import { RunId } from "@usine/core/run";
|
|
12
|
+
import { RunEngine, RunOperationRejectedError, } from "@usine/engine/engine";
|
|
13
|
+
import { ItemProjectionReader } from "@usine/engine/item-projection";
|
|
14
|
+
import { LoopControl } from "@usine/engine/loop-control";
|
|
15
|
+
import { RunReader } from "@usine/engine/run-reader";
|
|
16
|
+
import { RepositoryLabelProjectionReader } from "@usine/engine/repository-label-projection";
|
|
17
|
+
import { GitHubItemWriter } from "@usine/github/item-writer";
|
|
18
|
+
import { ChangedIssue, ChangedPullRequest } from "@usine/github/polling";
|
|
19
|
+
import { Context, DateTime, Effect, Exit, Layer, Option, Schema, SynchronizedRef } from "effect";
|
|
20
|
+
import { labelMutationForBoardMove, projectBoard } from "../board/projection.js";
|
|
21
|
+
import { GitHubItemKey, PollingTriggers } from "../polling-triggers.js";
|
|
22
|
+
import { ScheduleTriggers } from "../schedule-triggers.js";
|
|
23
|
+
import { ApprovalNeed, CycleBudgetNeed, BoardProjectionNeed, PollFailureNeed, PreflightFindingNeed, RunFailureNeed, ScheduleFailureNeed, } from "./need.js";
|
|
24
|
+
import { BoardItemNotFoundError as BoardItemNotFound, BoardLaneNotFoundError as BoardLaneNotFound, BoardLaneReadOnlyError as BoardLaneReadOnly, BoardProjectNotFoundError as BoardProjectNotFound, LabelView, } from "./board.js";
|
|
25
|
+
import { ColdTranscriptRequest, } from "./transcript.js";
|
|
26
|
+
import { LoopView, ProjectView, ReadyOverview } from "./overview.js";
|
|
27
|
+
import { RunDetailView, EditAndRetry, SessionIntervention, RosterAgentNotFoundError, } from "./run.js";
|
|
28
|
+
import { readTranscript, runView } from "./projection.js";
|
|
29
|
+
/** How many settled Runs the overview keeps beside the open ones. */
|
|
30
|
+
const OVERVIEW_CLOSED_RUNS = 100;
|
|
31
|
+
/** How many newest Items a Board shows; older ones drop off. */
|
|
32
|
+
const OVERVIEW_BOARD_ITEMS = 500;
|
|
33
|
+
const makeOperatorObservation = Effect.gen(function* () {
|
|
34
|
+
const budgets = yield* CycleBudgets;
|
|
35
|
+
const reader = yield* RunReader;
|
|
36
|
+
const controls = yield* LoopControl;
|
|
37
|
+
const itemReader = yield* ItemProjectionReader;
|
|
38
|
+
const repositoryLabels = yield* RepositoryLabelProjectionReader;
|
|
39
|
+
const polling = yield* PollingTriggers;
|
|
40
|
+
const schedules = yield* ScheduleTriggers;
|
|
41
|
+
const cache = yield* SynchronizedRef.make(Option.none());
|
|
42
|
+
const projectOverview = Effect.fn("Operator.projectOverview")(function* (instance, pollFailures, preflightFindings, scheduleFailures, revision) {
|
|
43
|
+
const [snapshots, states, cycleAsks] = yield* Effect.all([
|
|
44
|
+
reader.list({ closedLimit: OVERVIEW_CLOSED_RUNS }),
|
|
45
|
+
controls.states,
|
|
46
|
+
budgets.asks(),
|
|
47
|
+
], { concurrency: "unbounded" });
|
|
48
|
+
const paused = new Set(states.filter((state) => state.paused).map((state) => state.loop));
|
|
49
|
+
const views = snapshots.map((snapshot) => runView(snapshot, paused));
|
|
50
|
+
const runsByRepository = new Map();
|
|
51
|
+
for (const view of views) {
|
|
52
|
+
const projectRuns = runsByRepository.get(view.repository) ?? [];
|
|
53
|
+
projectRuns.push(view);
|
|
54
|
+
runsByRepository.set(view.repository, projectRuns);
|
|
55
|
+
}
|
|
56
|
+
const projects = yield* Effect.forEach(instance.config.repos, (repository) => Effect.gen(function* () {
|
|
57
|
+
const { name } = repository;
|
|
58
|
+
const projectFindings = preflightFindings.filter(({ repository: findingRepository }) => findingRepository === name);
|
|
59
|
+
const [boardItems, labelProjection] = yield* Effect.all([
|
|
60
|
+
itemReader.list(name, { limit: OVERVIEW_BOARD_ITEMS }),
|
|
61
|
+
repositoryLabels.get(name),
|
|
62
|
+
], { concurrency: "unbounded" });
|
|
63
|
+
const history = yield* reader.forItems(boardItems.map(({ itemKey }) => itemKey));
|
|
64
|
+
const merged = new Map((runsByRepository.get(name) ?? []).map((run) => [run.id, run]));
|
|
65
|
+
for (const saved of history) {
|
|
66
|
+
const view = runView(saved, paused);
|
|
67
|
+
merged.set(view.id, view);
|
|
68
|
+
}
|
|
69
|
+
const projectRuns = [...merged.values()].sort((a, b) => b.openedAt.localeCompare(a.openedAt) || b.id.localeCompare(a.id));
|
|
70
|
+
const catalog = Option.match(labelProjection, {
|
|
71
|
+
onNone: () => [],
|
|
72
|
+
onSome: ({ labels }) => labels,
|
|
73
|
+
});
|
|
74
|
+
const board = yield* projectBoard(repository, catalog.map(({ name }) => name), boardItems, projectRuns, cycleAsks.map(({ itemKey }) => itemKey));
|
|
75
|
+
const status = cycleAsks.some((ask) => ask.repository === name) ||
|
|
76
|
+
board.failures.length > 0 ||
|
|
77
|
+
projectFindings.length > 0 ||
|
|
78
|
+
projectRuns.some((run) => run.state === "Waiting" || run.state === "Failed")
|
|
79
|
+
? "needs-you"
|
|
80
|
+
: projectRuns.some((run) => run.canCancel)
|
|
81
|
+
? "active"
|
|
82
|
+
: "clear";
|
|
83
|
+
return new ProjectView({
|
|
84
|
+
repository: name,
|
|
85
|
+
status,
|
|
86
|
+
runs: projectRuns,
|
|
87
|
+
board,
|
|
88
|
+
labels: catalog.map(({ name: label, color, description }) => new LabelView({
|
|
89
|
+
name: GitHubLabelName.make(label),
|
|
90
|
+
color: Option.getOrNull(color),
|
|
91
|
+
description: Option.getOrNull(description),
|
|
92
|
+
})),
|
|
93
|
+
preflightFindings: projectFindings,
|
|
94
|
+
});
|
|
95
|
+
}), { concurrency: 4 });
|
|
96
|
+
const loops = instance.loops.map((loop) => new LoopView({
|
|
97
|
+
name: loop.name,
|
|
98
|
+
trigger: loop.trigger.on,
|
|
99
|
+
agent: loop.agent,
|
|
100
|
+
paused: paused.has(loop.name),
|
|
101
|
+
}));
|
|
102
|
+
const needsYou = [];
|
|
103
|
+
for (const ask of cycleAsks) {
|
|
104
|
+
const item = Schema.decodeUnknownOption(GitHubItemKey)(ask.itemKey);
|
|
105
|
+
needsYou.push(new CycleBudgetNeed({
|
|
106
|
+
id: `cycle-budget:${ask.id}`,
|
|
107
|
+
ask,
|
|
108
|
+
itemLabel: Option.match(item, {
|
|
109
|
+
onNone: () => ask.itemKey,
|
|
110
|
+
onSome: ([, repository, number]) => `${repository}#${number}`,
|
|
111
|
+
}),
|
|
112
|
+
itemUrl: Option.match(item, {
|
|
113
|
+
onNone: () => null,
|
|
114
|
+
onSome: ([, repository, number]) => `https://github.com/${repository}/issues/${number}`,
|
|
115
|
+
}),
|
|
116
|
+
}));
|
|
117
|
+
}
|
|
118
|
+
const allRuns = new Map(views.map((run) => [run.id, run]));
|
|
119
|
+
for (const project of projects)
|
|
120
|
+
for (const run of project.runs)
|
|
121
|
+
allRuns.set(run.id, run);
|
|
122
|
+
for (const run of allRuns.values()) {
|
|
123
|
+
if (run.state === "Waiting") {
|
|
124
|
+
needsYou.push(new ApprovalNeed({
|
|
125
|
+
id: `approval:${run.id}`,
|
|
126
|
+
repository: run.repository,
|
|
127
|
+
runId: run.id,
|
|
128
|
+
loop: run.loop,
|
|
129
|
+
}));
|
|
130
|
+
}
|
|
131
|
+
else if (run.state === "Failed") {
|
|
132
|
+
const attempt = run.attempts.at(-1);
|
|
133
|
+
if (attempt?.outcome?._tag !== "Failed") {
|
|
134
|
+
return yield* Effect.die(new Error(`Failed Run ${run.id} has no failed Attempt`));
|
|
135
|
+
}
|
|
136
|
+
needsYou.push(new RunFailureNeed({
|
|
137
|
+
id: `run-failure:${run.id}`,
|
|
138
|
+
repository: run.repository,
|
|
139
|
+
runId: run.id,
|
|
140
|
+
loop: run.loop,
|
|
141
|
+
failure: attempt.outcome.failure,
|
|
142
|
+
}));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
for (const project of projects) {
|
|
146
|
+
for (const failure of project.board.failures) {
|
|
147
|
+
needsYou.push(new BoardProjectionNeed({
|
|
148
|
+
id: `board-projection:${failure.repository}:${failure.itemKey}`,
|
|
149
|
+
failure,
|
|
150
|
+
}));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
pollFailures.forEach((failure, index) => {
|
|
154
|
+
needsYou.push(new PollFailureNeed({
|
|
155
|
+
id: `poll-failure:${failure.repository}:${failure.errorTag}:${index}`,
|
|
156
|
+
failure,
|
|
157
|
+
}));
|
|
158
|
+
});
|
|
159
|
+
preflightFindings.forEach((finding) => {
|
|
160
|
+
needsYou.push(new PreflightFindingNeed({
|
|
161
|
+
id: `preflight:${finding.repository}:${finding.label}`,
|
|
162
|
+
finding,
|
|
163
|
+
}));
|
|
164
|
+
});
|
|
165
|
+
scheduleFailures.forEach((failure, index) => {
|
|
166
|
+
needsYou.push(new ScheduleFailureNeed({
|
|
167
|
+
id: `schedule-failure:${failure.loop}:${failure._tag}:${index}`,
|
|
168
|
+
failure,
|
|
169
|
+
}));
|
|
170
|
+
});
|
|
171
|
+
const projected = new ReadyOverview({ projects, loops, needsYou });
|
|
172
|
+
yield* Effect.logDebug("Refreshed operator overview", {
|
|
173
|
+
revision,
|
|
174
|
+
runs: views.length,
|
|
175
|
+
needsYou: needsYou.length,
|
|
176
|
+
});
|
|
177
|
+
return projected;
|
|
178
|
+
});
|
|
179
|
+
const overview = Effect.fn("Operator.overview")((instance) => SynchronizedRef.modifyEffect(cache, (cached) => Effect.gen(function* () {
|
|
180
|
+
const [revision, latestPoll, latestSchedule] = yield* Effect.all([reader.revision(), polling.latest, schedules.latest], { concurrency: "unbounded" });
|
|
181
|
+
const pollFailures = Option.match(latestPoll, {
|
|
182
|
+
onNone: () => [],
|
|
183
|
+
onSome: ({ failures }) => failures,
|
|
184
|
+
});
|
|
185
|
+
const preflightFindings = Option.match(latestPoll, {
|
|
186
|
+
onNone: () => [],
|
|
187
|
+
onSome: ({ preflightFindings }) => preflightFindings,
|
|
188
|
+
});
|
|
189
|
+
const scheduleFailures = Option.match(latestSchedule, {
|
|
190
|
+
onNone: () => [],
|
|
191
|
+
onSome: ({ failures }) => failures,
|
|
192
|
+
});
|
|
193
|
+
// Without the invalidation singleton staleness cannot be detected, so the
|
|
194
|
+
// cache is bypassed.
|
|
195
|
+
const token = Option.map(revision, (value) => JSON.stringify([
|
|
196
|
+
value,
|
|
197
|
+
instance.config.repos.map(({ name, lanes }) => [name, lanes]),
|
|
198
|
+
instance.loops.map(({ name, agent, trigger }) => [name, agent, trigger.on]),
|
|
199
|
+
pollFailures,
|
|
200
|
+
preflightFindings,
|
|
201
|
+
scheduleFailures,
|
|
202
|
+
]));
|
|
203
|
+
if (Option.isNone(token)) {
|
|
204
|
+
yield* Effect.logWarning("Projection revision singleton is missing; the overview cache is bypassed");
|
|
205
|
+
const projected = yield* projectOverview(instance, pollFailures, preflightFindings, scheduleFailures, revision);
|
|
206
|
+
return [projected, Option.none()];
|
|
207
|
+
}
|
|
208
|
+
if (Option.isSome(cached) && cached.value.token === token.value) {
|
|
209
|
+
yield* Effect.logDebug("Reusing unchanged operator overview", {
|
|
210
|
+
revision,
|
|
211
|
+
});
|
|
212
|
+
return [cached.value.overview, cached];
|
|
213
|
+
}
|
|
214
|
+
const projected = yield* projectOverview(instance, pollFailures, preflightFindings, scheduleFailures, revision);
|
|
215
|
+
return [projected, Option.some({ token: token.value, overview: projected })];
|
|
216
|
+
})));
|
|
217
|
+
const detail = Effect.fn("Operator.detail")(function* (runId, request = new ColdTranscriptRequest()) {
|
|
218
|
+
yield* Effect.annotateCurrentSpan({ runId });
|
|
219
|
+
const [snapshot, states] = yield* Effect.all([reader.inspect(runId), controls.states], { concurrency: "unbounded" });
|
|
220
|
+
const paused = new Set(states.filter((state) => state.paused).map((state) => state.loop));
|
|
221
|
+
const [transcript, sessionState] = yield* Effect.all([
|
|
222
|
+
readTranscript(reader, snapshot, request),
|
|
223
|
+
Effect.forEach(snapshot.attempts.filter((attempt) => Option.isNone(attempt.outcome)), (attempt) => reader.transcript(new TranscriptSessionsQuery({ attemptId: attempt.id })), { concurrency: "unbounded" }),
|
|
224
|
+
], { concurrency: "unbounded" });
|
|
225
|
+
return new RunDetailView({
|
|
226
|
+
run: runView(snapshot, paused),
|
|
227
|
+
eventDedupKey: snapshot.event.dedupKey,
|
|
228
|
+
eventPayload: snapshot.event.payload,
|
|
229
|
+
transcript: transcript.events,
|
|
230
|
+
sessionState: sessionState.flat().map(transcriptEvent),
|
|
231
|
+
earlierTranscript: transcript.earlier,
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
return { detail, overview };
|
|
235
|
+
});
|
|
236
|
+
/** The read-only operator surface. It needs no trigger execution to build. */
|
|
237
|
+
export class OperatorObservation extends Context.Service()("@usine/orchestrator/OperatorObservation") {
|
|
238
|
+
static layer = Layer.effect(OperatorObservation, makeOperatorObservation);
|
|
239
|
+
}
|
|
240
|
+
const makeOperator = Effect.gen(function* () {
|
|
241
|
+
const budgets = yield* CycleBudgets;
|
|
242
|
+
const observation = yield* OperatorObservation;
|
|
243
|
+
const runs = yield* RunEngine;
|
|
244
|
+
const controls = yield* LoopControl;
|
|
245
|
+
const items = yield* ItemProjectionReader;
|
|
246
|
+
const repositoryLabels = yield* RepositoryLabelProjectionReader;
|
|
247
|
+
const github = yield* GitHubItemWriter;
|
|
248
|
+
const polling = yield* PollingTriggers;
|
|
249
|
+
const approve = Effect.fn("Operator.approve")(function* (runId) {
|
|
250
|
+
yield* Effect.annotateCurrentSpan({ runId });
|
|
251
|
+
const current = yield* runs.inspect(runId);
|
|
252
|
+
if (current.run.state._tag !== "Waiting") {
|
|
253
|
+
yield* Effect.logInfo("Operator approval rejected; the Run is past its gate", {
|
|
254
|
+
runId,
|
|
255
|
+
state: current.run.state._tag,
|
|
256
|
+
});
|
|
257
|
+
return yield* new RunOperationRejectedError({
|
|
258
|
+
runId,
|
|
259
|
+
operation: "approve",
|
|
260
|
+
state: current.run.state._tag,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
const receivedAt = yield* DateTime.now;
|
|
264
|
+
const dedupKey = DedupKey.make(JSON.stringify(["operator-approval", runId]));
|
|
265
|
+
yield* runs.approve(runId, new Event({
|
|
266
|
+
id: EventId.make(dedupKey),
|
|
267
|
+
dedupKey,
|
|
268
|
+
payload: { runId, decision: "approved" },
|
|
269
|
+
receivedAt,
|
|
270
|
+
}));
|
|
271
|
+
yield* Effect.logInfo("Operator approved Run", { runId });
|
|
272
|
+
});
|
|
273
|
+
const retry = Effect.fn("Operator.retry")(function* (runId) {
|
|
274
|
+
yield* Effect.annotateCurrentSpan({ runId });
|
|
275
|
+
yield* runs.retry(runId);
|
|
276
|
+
yield* Effect.logInfo("Operator retried Run", { runId });
|
|
277
|
+
});
|
|
278
|
+
const editAndRetry = Effect.fn("Operator.editAndRetry")(function* (instance, runId, edit) {
|
|
279
|
+
yield* Effect.annotateCurrentSpan({ runId, agent: edit.agent });
|
|
280
|
+
const file = instance.agents.find(({ name }) => name === edit.agent);
|
|
281
|
+
if (file === undefined)
|
|
282
|
+
return yield* new RosterAgentNotFoundError({
|
|
283
|
+
agent: edit.agent,
|
|
284
|
+
message: `Agent ${edit.agent} is not in the roster`,
|
|
285
|
+
});
|
|
286
|
+
const agent = file.agent;
|
|
287
|
+
yield* runs.retry(runId, Option.some(new AttemptContext({
|
|
288
|
+
task: edit.task,
|
|
289
|
+
agent,
|
|
290
|
+
parentModel: resolveParentModel(agent, instance.config.settings.parentModel),
|
|
291
|
+
})));
|
|
292
|
+
yield* Effect.logInfo("Operator amended and retried Run", { runId, agent: agent.name });
|
|
293
|
+
});
|
|
294
|
+
const intervene = Effect.fn("Operator.intervene")(function* (instance, runId, input) {
|
|
295
|
+
yield* Effect.annotateCurrentSpan({
|
|
296
|
+
runId,
|
|
297
|
+
attemptId: input.attemptId,
|
|
298
|
+
action: input.action._tag,
|
|
299
|
+
});
|
|
300
|
+
yield* runs.intervene(runId, input.attemptId, new Intervention({
|
|
301
|
+
operator: Option.getOrElse(instance.config.operator, () => "local operator"),
|
|
302
|
+
target: input.target,
|
|
303
|
+
action: input.action,
|
|
304
|
+
}));
|
|
305
|
+
});
|
|
306
|
+
const cancel = Effect.fn("Operator.cancel")(function* (runId) {
|
|
307
|
+
yield* Effect.annotateCurrentSpan({ runId });
|
|
308
|
+
yield* runs.cancel(runId);
|
|
309
|
+
yield* Effect.logInfo("Operator canceled Run", { runId });
|
|
310
|
+
});
|
|
311
|
+
const pause = Effect.fn("Operator.pause")(function* (loop) {
|
|
312
|
+
yield* Effect.annotateCurrentSpan({ loop });
|
|
313
|
+
return yield* controls.pause(loop);
|
|
314
|
+
});
|
|
315
|
+
const unpause = Effect.fn("Operator.unpause")(function* (loop) {
|
|
316
|
+
yield* Effect.annotateCurrentSpan({ loop });
|
|
317
|
+
return yield* controls.unpause(loop);
|
|
318
|
+
});
|
|
319
|
+
const moveBoardItem = Effect.fn("Operator.moveBoardItem")(function* (instance, move) {
|
|
320
|
+
yield* Effect.annotateCurrentSpan({
|
|
321
|
+
repository: move.repository,
|
|
322
|
+
item: move.item,
|
|
323
|
+
lane: move.toLane,
|
|
324
|
+
});
|
|
325
|
+
const repository = instance.config.repos.find(({ name }) => name === move.repository);
|
|
326
|
+
if (repository === undefined) {
|
|
327
|
+
return yield* new BoardProjectNotFound({ repository: move.repository });
|
|
328
|
+
}
|
|
329
|
+
const [storedItems, labelProjection] = yield* Effect.all([
|
|
330
|
+
items.list(move.repository, { limit: OVERVIEW_BOARD_ITEMS }),
|
|
331
|
+
repositoryLabels.get(move.repository),
|
|
332
|
+
], { concurrency: "unbounded" });
|
|
333
|
+
const board = yield* projectBoard(repository, Option.match(labelProjection, {
|
|
334
|
+
onNone: () => [],
|
|
335
|
+
onSome: ({ labels }) => labels.map(({ name }) => name),
|
|
336
|
+
}), storedItems, []);
|
|
337
|
+
const source = board.lanes.find(({ items: laneItems }) => laneItems.some(({ items }) => items.some(({ number }) => number === move.item)));
|
|
338
|
+
const movedItem = storedItems.find(({ number }) => number === move.item);
|
|
339
|
+
if (source === undefined || movedItem === undefined) {
|
|
340
|
+
return yield* new BoardItemNotFound({ repository: move.repository, item: move.item });
|
|
341
|
+
}
|
|
342
|
+
const target = board.lanes.find(({ id }) => id === move.toLane);
|
|
343
|
+
if (target === undefined) {
|
|
344
|
+
return yield* new BoardLaneNotFound({ repository: move.repository, lane: move.toLane });
|
|
345
|
+
}
|
|
346
|
+
if (target._tag === "ReadOnlyBoardLane") {
|
|
347
|
+
return yield* new BoardLaneReadOnly({ repository: move.repository, lane: move.toLane });
|
|
348
|
+
}
|
|
349
|
+
const mutation = labelMutationForBoardMove(board, target);
|
|
350
|
+
const changed = { number: move.item, updatedAt: yield* DateTime.now };
|
|
351
|
+
const item = movedItem.source === "pr" ? new ChangedPullRequest(changed) : new ChangedIssue(changed);
|
|
352
|
+
const applied = yield* github.updateLabels(move.repository, move.item, mutation).pipe(
|
|
353
|
+
// A failed or interrupted request may have applied part of the label
|
|
354
|
+
// swap. Reconcile it without replacing the writer's original outcome.
|
|
355
|
+
Effect.onExit((exit) => polling.hint(move.repository, item, Option.flatMap(Exit.findErrorOption(exit), (error) => error._tag === "GitHubRateLimitError" ? Option.some(error.retryAt) : Option.none()))));
|
|
356
|
+
yield* Effect.logInfo("Operator moved a Board card and queued its authoritative refresh", {
|
|
357
|
+
repository: move.repository,
|
|
358
|
+
item: move.item,
|
|
359
|
+
fromLane: source.id,
|
|
360
|
+
toLane: target.id,
|
|
361
|
+
addedLabels: mutation.add.join(", "),
|
|
362
|
+
removedLabels: mutation.remove.join(", "),
|
|
363
|
+
labels: applied.join(", "),
|
|
364
|
+
});
|
|
365
|
+
}, Effect.tapError((error) => Effect.logWarning("Board move rejected", {
|
|
366
|
+
errorTag: error._tag,
|
|
367
|
+
message: error.message,
|
|
368
|
+
})));
|
|
369
|
+
const resetCycleBudget = Effect.fn("Operator.resetCycleBudget")(function* (askId) {
|
|
370
|
+
yield* Effect.annotateCurrentSpan({ askId });
|
|
371
|
+
const key = `cycle-budget-reset:${askId}`;
|
|
372
|
+
yield* budgets.reset(askId, new Event({
|
|
373
|
+
id: EventId.make(key),
|
|
374
|
+
dedupKey: DedupKey.make(key),
|
|
375
|
+
payload: { action: "reset-cycle-budget", askId },
|
|
376
|
+
receivedAt: yield* DateTime.now,
|
|
377
|
+
}));
|
|
378
|
+
});
|
|
379
|
+
return {
|
|
380
|
+
...observation,
|
|
381
|
+
approve,
|
|
382
|
+
cancel,
|
|
383
|
+
moveBoardItem,
|
|
384
|
+
pause,
|
|
385
|
+
retry,
|
|
386
|
+
editAndRetry,
|
|
387
|
+
intervene,
|
|
388
|
+
unpause,
|
|
389
|
+
resetCycleBudget,
|
|
390
|
+
};
|
|
391
|
+
});
|
|
392
|
+
/** The operator's view of the factory and actions for approving, retrying, canceling, and directing work. */
|
|
393
|
+
export class Operator extends Context.Service()("@usine/orchestrator/Operator") {
|
|
394
|
+
static layer = Layer.effect(Operator, makeOperator).pipe(Layer.provide(OperatorObservation.layer));
|
|
395
|
+
}
|
|
396
|
+
export { ProjectStatus, ProjectView, LoopView, ReadyOverview, NeedsSetupOverview, FactoryOverview, } from "./overview.js";
|
|
397
|
+
export { RunStateTag, RunView, RunDetailView, EditAndRetry, SessionIntervention, RosterAgentNotFoundError, } from "./run.js";
|
|
398
|
+
export { InfraFailureView, DefectFailureView, AgentFailureView, RunawayFailureView, ChildSessionBudgetFailureView, FailureView, SucceededAttemptView, FailedAttemptView, CanceledAttemptView, AttemptOutcomeView, AttemptView, } from "./attempt.js";
|
|
399
|
+
export { BoardLaneId, BoardLaneOrigin, BoardItemLink, BoardTaskView, WritableBoardLaneView, ReadOnlyBoardLaneView, BoardLaneView, BoardProjectionError, BoardView, LabelView, BoardMove, BoardProjectNotFoundError, BoardItemNotFoundError, BoardLaneNotFoundError, BoardLaneReadOnlyError, } from "./board.js";
|
|
400
|
+
export { ApprovalNeed, CycleBudgetNeed, RunFailureNeed, PollFailureNeed, PreflightFindingNeed, BoardProjectionNeed, ScheduleFailureNeed, NeedView, } from "./need.js";
|
|
401
|
+
export { FailedTranscriptView, TranscriptPayload, TranscriptEventView, TranscriptPageCursor, ColdTranscriptRequest, TailTranscriptRequest, EarlierTranscriptRequest, TranscriptRequest, TranscriptAttemptNotFoundError, } from "./transcript.js";
|
|
402
|
+
//# sourceMappingURL=operator.js.map
|