@usine/config 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -18
- package/dist/agent.d.ts +4 -2
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +13 -4
- package/dist/agent.js.map +1 -1
- package/dist/board.d.ts +8 -6
- package/dist/board.d.ts.map +1 -1
- package/dist/board.js +13 -6
- package/dist/board.js.map +1 -1
- package/dist/checks.d.ts +3 -0
- package/dist/checks.d.ts.map +1 -1
- package/dist/checks.js +3 -0
- package/dist/checks.js.map +1 -1
- package/dist/factory.d.ts +75 -22
- package/dist/factory.d.ts.map +1 -1
- package/dist/factory.js +84 -29
- package/dist/factory.js.map +1 -1
- package/dist/file.d.ts +3 -3
- package/dist/file.d.ts.map +1 -1
- package/dist/file.js +3 -3
- package/dist/file.js.map +1 -1
- package/dist/frontmatter.d.ts +1 -1
- package/dist/frontmatter.d.ts.map +1 -1
- package/dist/frontmatter.js +1 -1
- package/dist/frontmatter.js.map +1 -1
- package/dist/instance/diagnostics.d.ts +1 -1
- package/dist/instance/diagnostics.d.ts.map +1 -1
- package/dist/instance/diagnostics.js +1 -1
- package/dist/instance/diagnostics.js.map +1 -1
- package/dist/instance/instance.d.ts +1 -1
- package/dist/instance/instance.d.ts.map +1 -1
- package/dist/instance/instance.js +2 -2
- package/dist/instance/instance.js.map +1 -1
- package/dist/instance/references.d.ts +5 -6
- package/dist/instance/references.d.ts.map +1 -1
- package/dist/instance/references.js +9 -9
- package/dist/instance/references.js.map +1 -1
- package/dist/instance-folder.d.ts +1 -1
- package/dist/instance-folder.d.ts.map +1 -1
- package/dist/instance-folder.js +13 -1
- package/dist/instance-folder.js.map +1 -1
- package/dist/label.d.ts +5 -5
- package/dist/label.d.ts.map +1 -1
- package/dist/label.js +5 -5
- package/dist/label.js.map +1 -1
- package/dist/loop.d.ts +4 -3
- package/dist/loop.d.ts.map +1 -1
- package/dist/loop.js +6 -5
- package/dist/loop.js.map +1 -1
- package/dist/parser.d.ts +6 -6
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +27 -10
- package/dist/parser.js.map +1 -1
- package/dist/payload-slices.d.ts +4 -7
- package/dist/payload-slices.d.ts.map +1 -1
- package/dist/payload-slices.js +30 -25
- package/dist/payload-slices.js.map +1 -1
- package/dist/selector.d.ts +6 -3
- package/dist/selector.d.ts.map +1 -1
- package/dist/selector.js +10 -6
- package/dist/selector.js.map +1 -1
- package/dist/trigger.d.ts +4 -7
- package/dist/trigger.d.ts.map +1 -1
- package/dist/trigger.js +6 -9
- package/dist/trigger.js.map +1 -1
- package/dist/yaml/document.d.ts +2 -2
- package/dist/yaml/document.js +3 -3
- package/dist/yaml/document.js.map +1 -1
- package/dist/yaml/writer.d.ts +4 -4
- package/dist/yaml/writer.d.ts.map +1 -1
- package/dist/yaml/writer.js +5 -6
- package/dist/yaml/writer.js.map +1 -1
- package/package.json +6 -5
package/README.md
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
# @usine/config
|
|
2
2
|
|
|
3
|
-
Reads and edits the
|
|
3
|
+
Reads and edits the files of a [usine](https://github.com/basaltbytes/usine) instance folder. The [`usine.yaml`](https://docs.usine.dev/reference/configuration/usine-yaml/), [loop file](https://docs.usine.dev/reference/configuration/loop-files/), and [agent file](https://docs.usine.dev/reference/configuration/agent-files/) references describe every key.
|
|
4
4
|
|
|
5
|
-
- `usine.yaml
|
|
6
|
-
- `loops/*.md
|
|
7
|
-
- `agents/*.md
|
|
8
|
-
- `updateYaml` and `updateFrontmatter` change one path through the YAML document tree while retaining comments, key order, and the Markdown body.
|
|
5
|
+
- `usine.yaml`: schema version, factory-wide GitHub labels, the managed-repo registry, settings, workers, webhook mode, and operator login.
|
|
6
|
+
- `loops/*.md`: strict YAML frontmatter and a Markdown prompt. The parser type-checks the trigger and every `{{ CEL expression }}` against the trigger's payload.
|
|
7
|
+
- `agents/*.md`: harness, model, effort, optional parent model and parent effort, and persona.
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
`updateYaml` and `updateFrontmatter` change one path in the YAML tree and keep comments, key order, and the Markdown body. Files use snake_case keys (`poll_interval`, `max_concurrent_attempts`); decoded types use camelCase (`pollInterval`, `maxConcurrentAttempts`). The schema does the rename, so edits keep snake_case.
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
## Labels and lanes
|
|
12
|
+
|
|
13
|
+
Declare the labels that loops and lanes use once. usine creates missing labels in every managed repo and never edits or deletes labels:
|
|
13
14
|
|
|
14
15
|
```yaml
|
|
15
16
|
labels:
|
|
@@ -19,11 +20,9 @@ labels:
|
|
|
19
20
|
- name: ready-for-review
|
|
20
21
|
```
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
Set `settings.max_children` to the number of child sessions one Attempt may spawn. The default is 4, and the count includes sequential and concurrent child sessions.
|
|
23
|
+
An undeclared label in a lane source or a trigger expression string literal adds a non-blocking entry to `LoadedInstance.warnings`. Prompt prose isn't checked.
|
|
25
24
|
|
|
26
|
-
Each managed
|
|
25
|
+
Each managed repo can define an exclusive board lane map of label and item state sources:
|
|
27
26
|
|
|
28
27
|
```yaml
|
|
29
28
|
repos:
|
|
@@ -38,16 +37,29 @@ repos:
|
|
|
38
37
|
- state: pr-open
|
|
39
38
|
```
|
|
40
39
|
|
|
41
|
-
|
|
40
|
+
`isSettledItemState` reports the settled states, `issue-closed`, `pr-closed`, and `pr-merged`; the board places such an item by state alone, whatever its labels. A repo without a lane map gets proposed lanes from the labels that polling finds.
|
|
41
|
+
|
|
42
|
+
## Settings and workers
|
|
43
|
+
|
|
44
|
+
`settings.max_children` (default 4) limits the child sessions, sequential and concurrent, that one attempt can start. Without a `workers` key, the parser adds a local worker named `local` that runs one attempt at a time. A worker's `kind` (`local`, `exe.dev`, or `ssh`) selects its executor.
|
|
45
|
+
|
|
46
|
+
Issue and pull request loops accept `max_cycles`, a positive integer (default 3): the number of runs each loop can open on an item between operator resets. The first admission past it blocks runs from every loop on the item. Schedule loops are exempt. See [Write review and fix loops](https://docs.usine.dev/guides/review-fix-loops/).
|
|
47
|
+
|
|
48
|
+
## Load an instance
|
|
49
|
+
|
|
50
|
+
`InstanceFolder.load` takes an `InstanceDirectory` (`@usine/core/instance`) and returns one atomic `LoadedInstance` snapshot:
|
|
42
51
|
|
|
43
|
-
|
|
52
|
+
- An invalid `usine.yaml` fails the load with `FactoryConfigError`.
|
|
53
|
+
- An invalid loop goes to `quarantinedLoops` with a `ConfigFileError` naming the file and stage. Valid loops still load.
|
|
54
|
+
- Invalid agents go to `invalidAgents`, and loops that use them are quarantined.
|
|
55
|
+
- Non-blocking reference diagnostics go to `warnings`.
|
|
44
56
|
|
|
45
|
-
`
|
|
57
|
+
`ConfigError` is the union of both errors. The `LoadedInstance` constructor enforces that every loop references a rostered agent and registered selector entries.
|
|
46
58
|
|
|
47
|
-
|
|
59
|
+
## Imports
|
|
48
60
|
|
|
49
|
-
|
|
61
|
+
Subpaths: `/factory` (settings and registry), `/board` (lanes), `/label`, `/loop`, `/trigger`, `/agent`, `/selector` (schema and matching), `/instance` (`LoadedInstance` and warnings), and `/file` (file paths and loading failures). Shared identities and agent personas come from `@usine/core`.
|
|
50
62
|
|
|
51
|
-
|
|
63
|
+
`InstanceFolder` (`/instance-folder`) and `ConfigParser` (`/parser`) are Effect services. `InstanceFolder.layer` needs `FileSystem.FileSystem` and `Path.Path`; `ConfigParser.layer` needs `ExpressionEngine` from `@usine/expression/expression-engine`.
|
|
52
64
|
|
|
53
|
-
|
|
65
|
+
The 0.x APIs can change between minor releases.
|
package/dist/agent.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Agent } from "@usine/core/agent";
|
|
1
2
|
import { Schema } from "effect";
|
|
2
3
|
import { ConfigFileError } from "./file.ts";
|
|
3
4
|
declare const AgentFile_base: Schema.Class<AgentFile, Schema.Struct<{
|
|
@@ -9,14 +10,15 @@ declare const AgentFile_base: Schema.Class<AgentFile, Schema.Struct<{
|
|
|
9
10
|
readonly parentEffort: Schema.Option<Schema.Literals<readonly ["off", "minimal", "low", "medium", "high", "xhigh", "max"]>>;
|
|
10
11
|
readonly persona: Schema.brand<Schema.String, "AgentPersona">;
|
|
11
12
|
}>, {}>;
|
|
12
|
-
/**
|
|
13
|
+
/** A parsed Agent Markdown file; `name` comes from the filename. */
|
|
13
14
|
export declare class AgentFile extends AgentFile_base {
|
|
15
|
+
get agent(): Agent;
|
|
14
16
|
}
|
|
15
17
|
declare const InvalidAgent_base: Schema.Class<InvalidAgent, Schema.Struct<{
|
|
16
18
|
readonly file: Schema.brand<Schema.NonEmptyString, "InstancePath">;
|
|
17
19
|
readonly error: typeof ConfigFileError;
|
|
18
20
|
}>, {}>;
|
|
19
|
-
/** An Agent file
|
|
21
|
+
/** An Agent file that failed to load and is left out of the roster. */
|
|
20
22
|
export declare class InvalidAgent extends InvalidAgent_base {
|
|
21
23
|
}
|
|
22
24
|
export {};
|
package/dist/agent.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAA2B,MAAM,mBAAmB,CAAC;AAEnE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAgB,eAAe,EAAE,MAAM,WAAW,CAAC;;;;;;;;;;AAE1D,oEAAoE;AACpE,qBAAa,SAAU,SAAQ,cAQ7B;IACA,IAAI,KAAK,IAAI,KAAK,CAQjB;CACF;;;;;AAED,uEAAuE;AACvE,qBAAa,YAAa,SAAQ,iBAGhC;CAAG"}
|
package/dist/agent.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { AgentPersona, AgentName } from "@usine/core/agent";
|
|
2
|
-
import { Effort, Harness, ModelId } from "@usine/core/agent/brain";
|
|
1
|
+
import { Agent, AgentPersona, AgentName } from "@usine/core/agent";
|
|
2
|
+
import { BrainSpec, Effort, Harness, ModelId } from "@usine/core/agent/brain";
|
|
3
3
|
import { Schema } from "effect";
|
|
4
4
|
import { InstancePath, ConfigFileError } from "./file.js";
|
|
5
|
-
/**
|
|
5
|
+
/** A parsed Agent Markdown file; `name` comes from the filename. */
|
|
6
6
|
export class AgentFile extends Schema.Class("@usine/config/AgentFile")({
|
|
7
7
|
name: AgentName,
|
|
8
8
|
harness: Harness,
|
|
@@ -12,8 +12,17 @@ export class AgentFile extends Schema.Class("@usine/config/AgentFile")({
|
|
|
12
12
|
parentEffort: Schema.Option(Effort),
|
|
13
13
|
persona: AgentPersona,
|
|
14
14
|
}) {
|
|
15
|
+
get agent() {
|
|
16
|
+
return new Agent({
|
|
17
|
+
name: this.name,
|
|
18
|
+
persona: this.persona,
|
|
19
|
+
brain: new BrainSpec({ harness: this.harness, model: this.model, effort: this.effort }),
|
|
20
|
+
parentModel: this.parentModel,
|
|
21
|
+
parentEffort: this.parentEffort,
|
|
22
|
+
});
|
|
23
|
+
}
|
|
15
24
|
}
|
|
16
|
-
/** An Agent file
|
|
25
|
+
/** An Agent file that failed to load and is left out of the roster. */
|
|
17
26
|
export class InvalidAgent extends Schema.Class("@usine/config/InvalidAgent")({
|
|
18
27
|
file: InstancePath,
|
|
19
28
|
error: ConfigFileError,
|
package/dist/agent.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAC9E,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAE1D,oEAAoE;AACpE,MAAM,OAAO,SAAU,SAAQ,MAAM,CAAC,KAAK,CAAY,yBAAyB,CAAC,CAAC;IAChF,IAAI,EAAE,SAAS;IACf,OAAO,EAAE,OAAO;IAChB,KAAK,EAAE,OAAO;IACd,MAAM,EAAE,MAAM;IACd,WAAW,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;IACnC,YAAY,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC;IACnC,OAAO,EAAE,YAAY;CACtB,CAAC;IACA,IAAI,KAAK;QACP,OAAO,IAAI,KAAK,CAAC;YACf,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,KAAK,EAAE,IAAI,SAAS,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;YACvF,WAAW,EAAE,IAAI,CAAC,WAAW;YAC7B,YAAY,EAAE,IAAI,CAAC,YAAY;SAChC,CAAC,CAAC;IACL,CAAC;CACF;AAED,uEAAuE;AACvE,MAAM,OAAO,YAAa,SAAQ,MAAM,CAAC,KAAK,CAAe,4BAA4B,CAAC,CAAC;IACzF,IAAI,EAAE,YAAY;IAClB,KAAK,EAAE,eAAe;CACvB,CAAC;CAAG"}
|
package/dist/board.d.ts
CHANGED
|
@@ -1,30 +1,32 @@
|
|
|
1
1
|
import { Schema } from "effect";
|
|
2
|
-
/** The operator-visible name of one lane on a Project's Board. */
|
|
3
2
|
export declare const LaneName: Schema.brand<Schema.String, "LaneName">;
|
|
4
3
|
export type LaneName = typeof LaneName.Type;
|
|
5
|
-
/** An issue or pull-request state that can place an Item in a Board lane. */
|
|
6
4
|
export declare const LaneItemState: Schema.Literals<readonly ["issue-open", "issue-closed", "pr-open", "pr-closed", "pr-merged"]>;
|
|
7
5
|
export type LaneItemState = typeof LaneItemState.Type;
|
|
6
|
+
/**
|
|
7
|
+
* Returns whether `state` is a closed issue or a closed or merged pull request, which the Board
|
|
8
|
+
* places by state regardless of labels.
|
|
9
|
+
*/
|
|
10
|
+
export declare const isSettledItemState: (state: LaneItemState) => boolean;
|
|
8
11
|
declare const LabelLaneSource_base: Schema.Class<LabelLaneSource, Schema.Struct<{
|
|
9
12
|
readonly label: Schema.brand<Schema.String, "GitHubLabelName">;
|
|
10
13
|
}>, {}>;
|
|
11
|
-
/**
|
|
14
|
+
/** A lane source that matches Items with this GitHub label, which Board moves can set. */
|
|
12
15
|
export declare class LabelLaneSource extends LabelLaneSource_base {
|
|
13
16
|
}
|
|
14
17
|
declare const StateLaneSource_base: Schema.Class<StateLaneSource, Schema.Struct<{
|
|
15
18
|
readonly state: Schema.Literals<readonly ["issue-open", "issue-closed", "pr-open", "pr-closed", "pr-merged"]>;
|
|
16
19
|
}>, {}>;
|
|
17
|
-
/**
|
|
20
|
+
/** A lane source that matches Items in this state, which Board moves can't change. */
|
|
18
21
|
export declare class StateLaneSource extends StateLaneSource_base {
|
|
19
22
|
}
|
|
20
|
-
/** A GitHub label or Item state that routes matching Items to a Board lane. */
|
|
21
23
|
export declare const LaneSource: Schema.Union<readonly [typeof LabelLaneSource, typeof StateLaneSource]>;
|
|
22
24
|
export type LaneSource = typeof LaneSource.Type;
|
|
23
25
|
declare const Lane_base: Schema.Class<Lane, Schema.Struct<{
|
|
24
26
|
readonly name: Schema.brand<Schema.String, "LaneName">;
|
|
25
27
|
readonly sources: Schema.$Array<Schema.Union<readonly [typeof LabelLaneSource, typeof StateLaneSource]>>;
|
|
26
28
|
}>, {}>;
|
|
27
|
-
/** A
|
|
29
|
+
/** A Board column that contains the Items matching any of `sources`. */
|
|
28
30
|
export declare class Lane extends Lane_base {
|
|
29
31
|
}
|
|
30
32
|
export {};
|
package/dist/board.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"board.d.ts","sourceRoot":"","sources":["../src/board.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAIhC,
|
|
1
|
+
{"version":3,"file":"board.d.ts","sourceRoot":"","sources":["../src/board.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAIhC,eAAO,MAAM,QAAQ,yCAEpB,CAAC;AAEF,MAAM,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAC,IAAI,CAAC;AAE5C,eAAO,MAAM,aAAa,+FAMxB,CAAC;AAEH,MAAM,MAAM,aAAa,GAAG,OAAO,aAAa,CAAC,IAAI,CAAC;AAQtD;;;GAGG;AACH,eAAO,MAAM,kBAAkB,UAAW,aAAa,KAAG,OAAyC,CAAC;;;;AAEpG,0FAA0F;AAC1F,qBAAa,eAAgB,SAAQ,oBAIpC;CAAG;;;;AAEJ,sFAAsF;AACtF,qBAAa,eAAgB,SAAQ,oBAIpC;CAAG;AAEJ,eAAO,MAAM,UAAU,yEAAmD,CAAC;AAE3E,MAAM,MAAM,UAAU,GAAG,OAAO,UAAU,CAAC,IAAI,CAAC;;;;;AAEhD,wEAAwE;AACxE,qBAAa,IAAK,SAAQ,SAGxB;CAAG"}
|
package/dist/board.js
CHANGED
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { Schema } from "effect";
|
|
2
2
|
import { nonBlankString } from "./checks.js";
|
|
3
3
|
import { GitHubLabelName } from "./label.js";
|
|
4
|
-
/** The operator-visible name of one lane on a Project's Board. */
|
|
5
4
|
export const LaneName = nonBlankString("Expected a non-blank lane name").pipe(Schema.brand("LaneName"));
|
|
6
|
-
/** An issue or pull-request state that can place an Item in a Board lane. */
|
|
7
5
|
export const LaneItemState = Schema.Literals([
|
|
8
6
|
"issue-open",
|
|
9
7
|
"issue-closed",
|
|
@@ -11,19 +9,28 @@ export const LaneItemState = Schema.Literals([
|
|
|
11
9
|
"pr-closed",
|
|
12
10
|
"pr-merged",
|
|
13
11
|
]);
|
|
14
|
-
|
|
12
|
+
const SETTLED_ITEM_STATES = new Set([
|
|
13
|
+
"issue-closed",
|
|
14
|
+
"pr-closed",
|
|
15
|
+
"pr-merged",
|
|
16
|
+
]);
|
|
17
|
+
/**
|
|
18
|
+
* Returns whether `state` is a closed issue or a closed or merged pull request, which the Board
|
|
19
|
+
* places by state regardless of labels.
|
|
20
|
+
*/
|
|
21
|
+
export const isSettledItemState = (state) => SETTLED_ITEM_STATES.has(state);
|
|
22
|
+
/** A lane source that matches Items with this GitHub label, which Board moves can set. */
|
|
15
23
|
export class LabelLaneSource extends Schema.Class("@usine/config/LabelLaneSource")({
|
|
16
24
|
label: GitHubLabelName,
|
|
17
25
|
}) {
|
|
18
26
|
}
|
|
19
|
-
/**
|
|
27
|
+
/** A lane source that matches Items in this state, which Board moves can't change. */
|
|
20
28
|
export class StateLaneSource extends Schema.Class("@usine/config/StateLaneSource")({
|
|
21
29
|
state: LaneItemState,
|
|
22
30
|
}) {
|
|
23
31
|
}
|
|
24
|
-
/** A GitHub label or Item state that routes matching Items to a Board lane. */
|
|
25
32
|
export const LaneSource = Schema.Union([LabelLaneSource, StateLaneSource]);
|
|
26
|
-
/** A
|
|
33
|
+
/** A Board column that contains the Items matching any of `sources`. */
|
|
27
34
|
export class Lane extends Schema.Class("@usine/config/Lane")({
|
|
28
35
|
name: LaneName,
|
|
29
36
|
sources: Schema.Array(LaneSource).check(Schema.isNonEmpty()),
|
package/dist/board.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"board.js","sourceRoot":"","sources":["../src/board.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,
|
|
1
|
+
{"version":3,"file":"board.js","sourceRoot":"","sources":["../src/board.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7C,MAAM,CAAC,MAAM,QAAQ,GAAG,cAAc,CAAC,gCAAgC,CAAC,CAAC,IAAI,CAC3E,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CACzB,CAAC;AAIF,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC,QAAQ,CAAC;IAC3C,YAAY;IACZ,cAAc;IACd,SAAS;IACT,WAAW;IACX,WAAW;CACZ,CAAC,CAAC;AAIH,MAAM,mBAAmB,GAA+B,IAAI,GAAG,CAAC;IAC9D,cAAc;IACd,WAAW;IACX,WAAW;CACZ,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,KAAoB,EAAW,EAAE,CAAC,mBAAmB,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AAEpG,0FAA0F;AAC1F,MAAM,OAAO,eAAgB,SAAQ,MAAM,CAAC,KAAK,CAAkB,+BAA+B,CAAC,CACjG;IACE,KAAK,EAAE,eAAe;CACvB,CACF;CAAG;AAEJ,sFAAsF;AACtF,MAAM,OAAO,eAAgB,SAAQ,MAAM,CAAC,KAAK,CAAkB,+BAA+B,CAAC,CACjG;IACE,KAAK,EAAE,aAAa;CACrB,CACF;CAAG;AAEJ,MAAM,CAAC,MAAM,UAAU,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,eAAe,EAAE,eAAe,CAAC,CAAC,CAAC;AAI3E,wEAAwE;AACxE,MAAM,OAAO,IAAK,SAAQ,MAAM,CAAC,KAAK,CAAO,oBAAoB,CAAC,CAAC;IACjE,IAAI,EAAE,QAAQ;IACd,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC;CAC7D,CAAC;CAAG"}
|
package/dist/checks.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { type RepositoryName } from "@usine/core/repository";
|
|
1
2
|
import { Schema } from "effect";
|
|
2
3
|
export declare const nonBlankString: (message: string) => Schema.String;
|
|
4
|
+
/** Returns a filter that rejects two names GitHub treats as the same repository. */
|
|
5
|
+
export declare const uniqueRepositoryNames: <A>(nameOf: (value: A) => RepositoryName) => import("effect/SchemaAST").Filter<readonly A[]>;
|
|
3
6
|
//# sourceMappingURL=checks.d.ts.map
|
package/dist/checks.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checks.d.ts","sourceRoot":"","sources":["../src/checks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,eAAO,MAAM,cAAc,YAAa,MAAM,kBACa,CAAC"}
|
|
1
|
+
{"version":3,"file":"checks.d.ts","sourceRoot":"","sources":["../src/checks.ts"],"names":[],"mappings":"AAAA,OAAO,EAA2B,KAAK,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACtF,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,eAAO,MAAM,cAAc,YAAa,MAAM,kBACa,CAAC;AAE5D,oFAAoF;AACpF,eAAO,MAAM,qBAAqB,GAAI,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC,KAAK,cAAc,oDAK1E,CAAC"}
|
package/dist/checks.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { canonicalRepositoryName } from "@usine/core/repository";
|
|
1
2
|
import { Schema } from "effect";
|
|
2
3
|
export const nonBlankString = (message) => Schema.String.check(Schema.isPattern(/\S/u, { message }));
|
|
4
|
+
/** Returns a filter that rejects two names GitHub treats as the same repository. */
|
|
5
|
+
export const uniqueRepositoryNames = (nameOf) => Schema.makeFilter((values) => new Set(values.map((value) => canonicalRepositoryName(nameOf(value)))).size === values.length, { message: "Expected unique repository names, ignoring case" });
|
|
3
6
|
//# sourceMappingURL=checks.js.map
|
package/dist/checks.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checks.js","sourceRoot":"","sources":["../src/checks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,OAAe,EAAE,EAAE,CAChD,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"checks.js","sourceRoot":"","sources":["../src/checks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAuB,MAAM,wBAAwB,CAAC;AACtF,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,OAAe,EAAE,EAAE,CAChD,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC;AAE5D,oFAAoF;AACpF,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAI,MAAoC,EAAE,EAAE,CAC/E,MAAM,CAAC,UAAU,CACf,CAAC,MAAM,EAAE,EAAE,CACT,IAAI,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,uBAAuB,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,MAAM,EAC/F,EAAE,OAAO,EAAE,iDAAiD,EAAE,CAC/D,CAAC"}
|
package/dist/factory.d.ts
CHANGED
|
@@ -1,46 +1,94 @@
|
|
|
1
1
|
import { Schema } from "effect";
|
|
2
2
|
import { Lane } from "./board.ts";
|
|
3
3
|
import { DeclaredLabel } from "./label.ts";
|
|
4
|
-
/** A
|
|
4
|
+
/** A repository tag that Loop selectors match. */
|
|
5
5
|
export declare const RegistryTag: Schema.brand<Schema.String, "RegistryTag">;
|
|
6
6
|
export type RegistryTag = typeof RegistryTag.Type;
|
|
7
|
-
/** GitHub login identifying the human operator for this factory. */
|
|
8
|
-
export declare const OperatorLogin: Schema.brand<Schema.String, "OperatorLogin">;
|
|
9
|
-
export type OperatorLogin = typeof OperatorLogin.Type;
|
|
10
7
|
declare const ManagedRepo_base: Schema.Class<ManagedRepo, Schema.Struct<{
|
|
11
8
|
readonly name: Schema.brand<Schema.String, "RepositoryName">;
|
|
12
9
|
readonly tags: Schema.withDecodingDefaultKey<Schema.$Array<Schema.brand<Schema.String, "RegistryTag">>, never>;
|
|
13
10
|
readonly lanes: Schema.withDecodingDefaultKey<Schema.$Array<typeof Lane>, never>;
|
|
14
11
|
}>, {}>;
|
|
15
|
-
/** A repository the factory manages, with its selector tags and configured Board lanes. */
|
|
16
12
|
export declare class ManagedRepo extends ManagedRepo_base {
|
|
17
13
|
}
|
|
18
|
-
declare const
|
|
14
|
+
declare const LocalWorker_base: Schema.Class<LocalWorker, Schema.Struct<{
|
|
19
15
|
readonly name: Schema.brand<Schema.String, "WorkerName">;
|
|
20
|
-
readonly kind: Schema.
|
|
16
|
+
readonly kind: Schema.Literal<"local">;
|
|
21
17
|
readonly maxConcurrentAttempts: Schema.withDecodingDefaultKey<Schema.Int, never>;
|
|
22
18
|
}>, {}>;
|
|
23
|
-
/** A
|
|
24
|
-
export declare class
|
|
19
|
+
/** A Worker that runs Attempts on the orchestrator's machine. */
|
|
20
|
+
export declare class LocalWorker extends LocalWorker_base {
|
|
25
21
|
}
|
|
22
|
+
declare const ExeDevWorker_base: Schema.Class<ExeDevWorker, Schema.Struct<{
|
|
23
|
+
readonly name: Schema.brand<Schema.String, "WorkerName">;
|
|
24
|
+
readonly kind: Schema.Literal<"exe.dev">;
|
|
25
|
+
readonly maxConcurrentAttempts: Schema.withDecodingDefaultKey<Schema.Int, never>;
|
|
26
|
+
}>, {}>;
|
|
27
|
+
/** A Worker on the exe.dev VM with the same name. */
|
|
28
|
+
export declare class ExeDevWorker extends ExeDevWorker_base {
|
|
29
|
+
}
|
|
30
|
+
declare const WorkerGateway_base: Schema.Class<WorkerGateway, Schema.Struct<{
|
|
31
|
+
readonly domain: Schema.brand<Schema.String, "GatewayDomain">;
|
|
32
|
+
readonly destination: Schema.brand<Schema.String, "SshDestination">;
|
|
33
|
+
readonly adminPort: Schema.withDecodingDefaultKey<Schema.brand<Schema.Int, "GatewayAdminPort">, never>;
|
|
34
|
+
readonly adminTokenEnv: Schema.brand<Schema.String, "EnvironmentVariableName">;
|
|
35
|
+
readonly llmProviders: Schema.withDecodingDefaultKey<Schema.$Array<Schema.Literals<readonly ["anthropic"]>>, never>;
|
|
36
|
+
}>, {}>;
|
|
37
|
+
/**
|
|
38
|
+
* The Gateway in front of an SSH Worker's machine on a private network (ADR 0027).
|
|
39
|
+
*
|
|
40
|
+
* - `domain`: the Gateway's private DNS domain.
|
|
41
|
+
* - `destination`: the Gateway's `ssh` destination, usually the Host alias in the Worker's
|
|
42
|
+
* `ProxyJump`. The executor forwards through it to the admin API at `adminPort` on the
|
|
43
|
+
* Gateway's loopback interface.
|
|
44
|
+
* - `adminTokenEnv`: the orchestrator environment variable with the admin token, read when
|
|
45
|
+
* `usine up` starts or a reload adds the Worker.
|
|
46
|
+
* - `llmProviders`: the providers whose credential the Gateway injects. Harnesses for other
|
|
47
|
+
* providers, and Codex, use the operator's login on the Worker through the forward proxy.
|
|
48
|
+
*/
|
|
49
|
+
export declare class WorkerGateway extends WorkerGateway_base {
|
|
50
|
+
}
|
|
51
|
+
declare const SshWorker_base: Schema.Class<SshWorker, Schema.Struct<{
|
|
52
|
+
readonly name: Schema.brand<Schema.String, "WorkerName">;
|
|
53
|
+
readonly kind: Schema.Literal<"ssh">;
|
|
54
|
+
readonly maxConcurrentAttempts: Schema.withDecodingDefaultKey<Schema.Int, never>;
|
|
55
|
+
readonly destination: Schema.brand<Schema.String, "SshDestination">;
|
|
56
|
+
readonly identityFile: Schema.OptionFromOptionalKey<Schema.brand<Schema.String, "SshIdentityFile">>;
|
|
57
|
+
readonly port: Schema.OptionFromOptionalKey<Schema.brand<Schema.Int, "SshPort">>;
|
|
58
|
+
readonly root: Schema.brand<Schema.String, "WorkerRoot">;
|
|
59
|
+
readonly gateway: Schema.OptionFromOptionalKey<Schema.encodeKeys<typeof WorkerGateway, {
|
|
60
|
+
readonly adminPort: "admin_port";
|
|
61
|
+
readonly adminTokenEnv: "admin_token_env";
|
|
62
|
+
readonly llmProviders: "llm_providers";
|
|
63
|
+
}>>;
|
|
64
|
+
}>, {}>;
|
|
65
|
+
/**
|
|
66
|
+
* A Linux machine reached with `ssh`, with Worker state under `<root>/workers/<name>`. If
|
|
67
|
+
* `gateway` is set, the machine stores no GitHub token; the executor sends each Attempt's token
|
|
68
|
+
* to the Gateway.
|
|
69
|
+
*/
|
|
70
|
+
export declare class SshWorker extends SshWorker_base {
|
|
71
|
+
}
|
|
72
|
+
/** A configured Worker; `kind` selects the adapter. */
|
|
73
|
+
export declare const Worker: Schema.Union<readonly [typeof LocalWorker, typeof ExeDevWorker, typeof SshWorker]>;
|
|
74
|
+
export type Worker = typeof Worker.Type;
|
|
26
75
|
declare const Settings_base: Schema.Class<Settings, Schema.Struct<{
|
|
27
|
-
/**
|
|
76
|
+
/** The interval between GitHub polling cycles, in seconds. */
|
|
28
77
|
readonly pollInterval: Schema.withDecodingDefaultKey<Schema.Int, never>;
|
|
29
|
-
/**
|
|
78
|
+
/** The maximum number of child sessions per Attempt, counting sequential spawns. */
|
|
30
79
|
readonly maxChildren: Schema.withDecodingDefaultKey<Schema.brand<Schema.Int, "ChildSessionBudget">, never>;
|
|
31
|
-
/**
|
|
32
|
-
readonly maxAttemptMinutes: Schema.
|
|
33
|
-
/**
|
|
34
|
-
readonly maxAttemptTokens: Schema.
|
|
35
|
-
/**
|
|
80
|
+
/** The wall-clock limit per Attempt, in minutes, or `None` for no limit. */
|
|
81
|
+
readonly maxAttemptMinutes: Schema.OptionFromOptionalKey<Schema.Int>;
|
|
82
|
+
/** The token limit per Attempt, counting the parent and every child, or `None` for no limit. */
|
|
83
|
+
readonly maxAttemptTokens: Schema.OptionFromOptionalKey<Schema.Int>;
|
|
84
|
+
/** The parent model for Agents that set none, or `None` to let Pi select it. */
|
|
36
85
|
readonly parentModel: Schema.OptionFromOptionalKey<Schema.brand<Schema.String, "ModelId">>;
|
|
37
|
-
/**
|
|
86
|
+
/** The parent reasoning effort for Agents that set none, or `None` to use the child's effort. */
|
|
38
87
|
readonly parentEffort: Schema.OptionFromOptionalKey<Schema.Literals<readonly ["off", "minimal", "low", "medium", "high", "xhigh", "max"]>>;
|
|
39
88
|
}>, {}>;
|
|
40
|
-
/** Factory-wide polling, Attempt limits, and defaults for the coordinating parent. */
|
|
41
89
|
export declare class Settings extends Settings_base {
|
|
42
90
|
}
|
|
43
|
-
/**
|
|
91
|
+
/** The source of GitHub webhook events that trigger an early poll; polling continues in every mode. */
|
|
44
92
|
export declare const Webhooks: Schema.Union<readonly [Schema.Struct<{
|
|
45
93
|
readonly mode: Schema.Literal<"off">;
|
|
46
94
|
}>, Schema.Struct<{
|
|
@@ -64,9 +112,14 @@ declare const UsineConfig_base: Schema.Class<UsineConfig, Schema.Struct<{
|
|
|
64
112
|
readonly parentModel: "parent_model";
|
|
65
113
|
readonly parentEffort: "parent_effort";
|
|
66
114
|
}>, never>;
|
|
67
|
-
readonly workers: Schema.withDecodingDefaultKey<Schema.$Array<Schema.encodeKeys<typeof
|
|
115
|
+
readonly workers: Schema.withDecodingDefaultKey<Schema.$Array<Schema.Union<readonly [Schema.encodeKeys<typeof LocalWorker, {
|
|
116
|
+
readonly maxConcurrentAttempts: "max_concurrent_attempts";
|
|
117
|
+
}>, Schema.encodeKeys<typeof ExeDevWorker, {
|
|
118
|
+
readonly maxConcurrentAttempts: "max_concurrent_attempts";
|
|
119
|
+
}>, Schema.encodeKeys<typeof SshWorker, {
|
|
68
120
|
readonly maxConcurrentAttempts: "max_concurrent_attempts";
|
|
69
|
-
|
|
121
|
+
readonly identityFile: "identity_file";
|
|
122
|
+
}>]>>, never>;
|
|
70
123
|
readonly webhooks: Schema.withDecodingDefaultKey<Schema.Union<readonly [Schema.Struct<{
|
|
71
124
|
readonly mode: Schema.Literal<"off">;
|
|
72
125
|
}>, Schema.Struct<{
|
|
@@ -79,7 +132,7 @@ declare const UsineConfig_base: Schema.Class<UsineConfig, Schema.Struct<{
|
|
|
79
132
|
}>]>, never>;
|
|
80
133
|
readonly operator: Schema.OptionFromOptionalKey<Schema.brand<Schema.String, "OperatorLogin">>;
|
|
81
134
|
}>, {}>;
|
|
82
|
-
/** The
|
|
135
|
+
/** The decoded `usine.yaml`. */
|
|
83
136
|
export declare class UsineConfig extends UsineConfig_base {
|
|
84
137
|
}
|
|
85
138
|
export {};
|
package/dist/factory.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AAcA,OAAO,EAAU,MAAM,EAAE,MAAM,QAAQ,CAAC;AAExC,OAAO,EAAc,IAAI,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAsB,aAAa,EAAE,MAAM,YAAY,CAAC;AAE/D,kDAAkD;AAClD,eAAO,MAAM,WAAW,4CAEvB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,OAAO,WAAW,CAAC,IAAI,CAAC;;;;;;AA6BlD,qBAAa,WAAY,SAAQ,gBAM/B;CAAG;;;;;;AAIL,iEAAiE;AACjE,qBAAa,WAAY,SAAQ,gBAI/B;CAAG;;;;;;AAEL,qDAAqD;AACrD,qBAAa,YAAa,SAAQ,iBAIhC;CAAG;;;;;;;;AAQL;;;;;;;;;;;GAWG;AACH,qBAAa,aAAc,SAAQ,kBAQjC;CAAG;;;;;;;;;;;;;;;AAEL;;;;GAIG;AACH,qBAAa,SAAU,SAAQ,cAiB7B;CAAG;AAEL,uDAAuD;AACvD,eAAO,MAAM,MAAM,oFAAuD,CAAC;AAC3E,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC,IAAI,CAAC;;IAGtC,8DAA8D;;IAE9D,oFAAoF;;IAEpF,4EAA4E;;IAE5E,gGAAgG;;IAEhG,gFAAgF;;IAEhF,iGAAiG;;;AAXnG,qBAAa,QAAS,SAAQ,aAa5B;CAAG;AAEL,uGAAuG;AACvG,eAAO,MAAM,QAAQ;;;;;;;;;IAMnB,CAAC;AAEH,MAAM,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAC,IAAI,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8B5C,gCAAgC;AAChC,qBAAa,WAAY,SAAQ,gBAU/B;CAAG"}
|
package/dist/factory.js
CHANGED
|
@@ -1,28 +1,24 @@
|
|
|
1
1
|
import { Effort, ModelId } from "@usine/core/agent/brain";
|
|
2
2
|
import { ChildSessionBudget } from "@usine/core/agent/session";
|
|
3
|
+
import { OperatorLogin } from "@usine/core/operator";
|
|
3
4
|
import { RepositoryName } from "@usine/core/repository";
|
|
4
|
-
import { WorkerName,
|
|
5
|
+
import { GatewayAdminPort, GatewayDomain, GatewayLlmProvider, SshDestination, SshIdentityFile, SshPort, WorkerName, WorkerRoot, } from "@usine/core/worker";
|
|
5
6
|
import { Effect, Schema } from "effect";
|
|
6
|
-
import { nonBlankString } from "./checks.js";
|
|
7
|
+
import { nonBlankString, uniqueRepositoryNames } from "./checks.js";
|
|
7
8
|
import { LaneSource, Lane } from "./board.js";
|
|
8
9
|
import { canonicalLabelName, DeclaredLabel } from "./label.js";
|
|
9
|
-
/** A
|
|
10
|
+
/** A repository tag that Loop selectors match. */
|
|
10
11
|
export const RegistryTag = nonBlankString("Expected a non-blank registry tag").pipe(Schema.brand("RegistryTag"));
|
|
11
|
-
/** GitHub login identifying the human operator for this factory. */
|
|
12
|
-
export const OperatorLogin = Schema.String.check(Schema.isPattern(/^[A-Za-z0-9](?:[A-Za-z0-9-]{0,37}[A-Za-z0-9])?$/u, {
|
|
13
|
-
message: "Expected a GitHub login",
|
|
14
|
-
})).pipe(Schema.brand("OperatorLogin"));
|
|
15
12
|
const PositiveInt = Schema.Int.check(Schema.isGreaterThanOrEqualTo(1));
|
|
16
13
|
const uniqueNames = Schema.makeFilter((values) => new Set(values.map(({ name }) => name)).size === values.length, { message: "Expected unique names" });
|
|
17
|
-
const laneSourceKey = (source) => "label" in source ? `label:${source.label}` : `state:${source.state}`;
|
|
14
|
+
const laneSourceKey = (source) => "label" in source ? `label:${canonicalLabelName(source.label)}` : `state:${source.state}`;
|
|
18
15
|
const uniqueLaneSources = Schema.makeFilter((lanes) => {
|
|
19
16
|
const keys = lanes.flatMap(({ sources }) => sources.map(laneSourceKey));
|
|
20
17
|
return new Set(keys).size === keys.length;
|
|
21
|
-
}, { message: "Expected
|
|
18
|
+
}, { message: "Expected each lane source in only one lane" });
|
|
22
19
|
const Lanes = Schema.Array(Lane).check(uniqueNames, uniqueLaneSources);
|
|
23
20
|
const uniqueLabelNames = Schema.makeFilter((values) => new Set(values.map(({ name }) => canonicalLabelName(name))).size === values.length, { message: "Expected unique GitHub label names" });
|
|
24
21
|
const DeclaredLabels = Schema.Array(DeclaredLabel).check(uniqueLabelNames);
|
|
25
|
-
/** A repository the factory manages, with its selector tags and configured Board lanes. */
|
|
26
22
|
export class ManagedRepo extends Schema.Class("@usine/config/ManagedRepo")({
|
|
27
23
|
name: RepositoryName,
|
|
28
24
|
tags: Schema.Array(RegistryTag)
|
|
@@ -31,37 +27,89 @@ export class ManagedRepo extends Schema.Class("@usine/config/ManagedRepo")({
|
|
|
31
27
|
lanes: Lanes.pipe(Schema.withDecodingDefaultKey(Effect.succeed([]))),
|
|
32
28
|
}) {
|
|
33
29
|
}
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
const MaxConcurrentAttempts = PositiveInt.pipe(Schema.withDecodingDefaultKey(Effect.succeed(1)));
|
|
31
|
+
/** A Worker that runs Attempts on the orchestrator's machine. */
|
|
32
|
+
export class LocalWorker extends Schema.Class("@usine/config/LocalWorker")({
|
|
36
33
|
name: WorkerName,
|
|
37
|
-
kind:
|
|
38
|
-
maxConcurrentAttempts:
|
|
34
|
+
kind: Schema.Literal("local"),
|
|
35
|
+
maxConcurrentAttempts: MaxConcurrentAttempts,
|
|
39
36
|
}) {
|
|
40
37
|
}
|
|
41
|
-
/**
|
|
38
|
+
/** A Worker on the exe.dev VM with the same name. */
|
|
39
|
+
export class ExeDevWorker extends Schema.Class("@usine/config/ExeDevWorker")({
|
|
40
|
+
name: WorkerName,
|
|
41
|
+
kind: Schema.Literal("exe.dev"),
|
|
42
|
+
maxConcurrentAttempts: MaxConcurrentAttempts,
|
|
43
|
+
}) {
|
|
44
|
+
}
|
|
45
|
+
const EnvironmentVariableName = Schema.String.check(Schema.isPattern(/^[A-Za-z_][A-Za-z0-9_]*$/u, {
|
|
46
|
+
message: "Expected an environment variable name",
|
|
47
|
+
})).pipe(Schema.brand("EnvironmentVariableName"));
|
|
48
|
+
/**
|
|
49
|
+
* The Gateway in front of an SSH Worker's machine on a private network (ADR 0027).
|
|
50
|
+
*
|
|
51
|
+
* - `domain`: the Gateway's private DNS domain.
|
|
52
|
+
* - `destination`: the Gateway's `ssh` destination, usually the Host alias in the Worker's
|
|
53
|
+
* `ProxyJump`. The executor forwards through it to the admin API at `adminPort` on the
|
|
54
|
+
* Gateway's loopback interface.
|
|
55
|
+
* - `adminTokenEnv`: the orchestrator environment variable with the admin token, read when
|
|
56
|
+
* `usine up` starts or a reload adds the Worker.
|
|
57
|
+
* - `llmProviders`: the providers whose credential the Gateway injects. Harnesses for other
|
|
58
|
+
* providers, and Codex, use the operator's login on the Worker through the forward proxy.
|
|
59
|
+
*/
|
|
60
|
+
export class WorkerGateway extends Schema.Class("@usine/config/WorkerGateway")({
|
|
61
|
+
domain: GatewayDomain,
|
|
62
|
+
destination: SshDestination,
|
|
63
|
+
adminPort: GatewayAdminPort.pipe(Schema.withDecodingDefaultKey(Effect.succeed(7070))),
|
|
64
|
+
adminTokenEnv: EnvironmentVariableName,
|
|
65
|
+
llmProviders: Schema.Array(GatewayLlmProvider)
|
|
66
|
+
.check(Schema.isUnique())
|
|
67
|
+
.pipe(Schema.withDecodingDefaultKey(Effect.succeed([]))),
|
|
68
|
+
}) {
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* A Linux machine reached with `ssh`, with Worker state under `<root>/workers/<name>`. If
|
|
72
|
+
* `gateway` is set, the machine stores no GitHub token; the executor sends each Attempt's token
|
|
73
|
+
* to the Gateway.
|
|
74
|
+
*/
|
|
75
|
+
export class SshWorker extends Schema.Class("@usine/config/SshWorker")({
|
|
76
|
+
name: WorkerName,
|
|
77
|
+
kind: Schema.Literal("ssh"),
|
|
78
|
+
maxConcurrentAttempts: MaxConcurrentAttempts,
|
|
79
|
+
destination: SshDestination,
|
|
80
|
+
identityFile: Schema.OptionFromOptionalKey(SshIdentityFile),
|
|
81
|
+
port: Schema.OptionFromOptionalKey(SshPort),
|
|
82
|
+
root: WorkerRoot,
|
|
83
|
+
gateway: Schema.OptionFromOptionalKey(WorkerGateway.pipe(Schema.encodeKeys({
|
|
84
|
+
adminPort: "admin_port",
|
|
85
|
+
adminTokenEnv: "admin_token_env",
|
|
86
|
+
llmProviders: "llm_providers",
|
|
87
|
+
}))),
|
|
88
|
+
}) {
|
|
89
|
+
}
|
|
90
|
+
/** A configured Worker; `kind` selects the adapter. */
|
|
91
|
+
export const Worker = Schema.Union([LocalWorker, ExeDevWorker, SshWorker]);
|
|
42
92
|
export class Settings extends Schema.Class("@usine/config/Settings")({
|
|
43
|
-
/**
|
|
93
|
+
/** The interval between GitHub polling cycles, in seconds. */
|
|
44
94
|
pollInterval: PositiveInt.pipe(Schema.withDecodingDefaultKey(Effect.succeed(60))),
|
|
45
|
-
/**
|
|
95
|
+
/** The maximum number of child sessions per Attempt, counting sequential spawns. */
|
|
46
96
|
maxChildren: ChildSessionBudget.pipe(Schema.withDecodingDefaultKey(Effect.succeed(4))),
|
|
47
|
-
/**
|
|
48
|
-
maxAttemptMinutes:
|
|
49
|
-
/**
|
|
50
|
-
maxAttemptTokens:
|
|
51
|
-
/**
|
|
97
|
+
/** The wall-clock limit per Attempt, in minutes, or `None` for no limit. */
|
|
98
|
+
maxAttemptMinutes: Schema.OptionFromOptionalKey(PositiveInt),
|
|
99
|
+
/** The token limit per Attempt, counting the parent and every child, or `None` for no limit. */
|
|
100
|
+
maxAttemptTokens: Schema.OptionFromOptionalKey(PositiveInt),
|
|
101
|
+
/** The parent model for Agents that set none, or `None` to let Pi select it. */
|
|
52
102
|
parentModel: Schema.OptionFromOptionalKey(ModelId),
|
|
53
|
-
/**
|
|
103
|
+
/** The parent reasoning effort for Agents that set none, or `None` to use the child's effort. */
|
|
54
104
|
parentEffort: Schema.OptionFromOptionalKey(Effort),
|
|
55
105
|
}) {
|
|
56
106
|
}
|
|
57
|
-
/**
|
|
107
|
+
/** The source of GitHub webhook events that trigger an early poll; polling continues in every mode. */
|
|
58
108
|
export const Webhooks = Schema.Union([
|
|
59
109
|
Schema.Struct({ mode: Schema.Literal("off") }),
|
|
60
110
|
Schema.Struct({ mode: Schema.Literal("deliveries") }),
|
|
61
111
|
Schema.Struct({ mode: Schema.Literal("endpoint"), publicUrl: Schema.URLFromString }).pipe(Schema.encodeKeys({ publicUrl: "public_url" })),
|
|
62
112
|
]);
|
|
63
|
-
// Authored YAML keys are snake_case and domain fields camelCase. The rename
|
|
64
|
-
// happens once here and encodes back for edits.
|
|
65
113
|
const SettingsFromYaml = Settings.pipe(Schema.encodeKeys({
|
|
66
114
|
pollInterval: "poll_interval",
|
|
67
115
|
maxChildren: "max_children",
|
|
@@ -70,10 +118,17 @@ const SettingsFromYaml = Settings.pipe(Schema.encodeKeys({
|
|
|
70
118
|
parentModel: "parent_model",
|
|
71
119
|
parentEffort: "parent_effort",
|
|
72
120
|
}));
|
|
73
|
-
const WorkerFromYaml =
|
|
74
|
-
|
|
121
|
+
const WorkerFromYaml = Schema.Union([
|
|
122
|
+
LocalWorker.pipe(Schema.encodeKeys({ maxConcurrentAttempts: "max_concurrent_attempts" })),
|
|
123
|
+
ExeDevWorker.pipe(Schema.encodeKeys({ maxConcurrentAttempts: "max_concurrent_attempts" })),
|
|
124
|
+
SshWorker.pipe(Schema.encodeKeys({
|
|
125
|
+
maxConcurrentAttempts: "max_concurrent_attempts",
|
|
126
|
+
identityFile: "identity_file",
|
|
127
|
+
})),
|
|
128
|
+
]);
|
|
129
|
+
const ManagedRepos = Schema.Array(ManagedRepo).check(uniqueRepositoryNames((repo) => repo.name));
|
|
75
130
|
const Workers = Schema.Array(WorkerFromYaml).check(Schema.isNonEmpty(), uniqueNames);
|
|
76
|
-
/** The
|
|
131
|
+
/** The decoded `usine.yaml`. */
|
|
77
132
|
export class UsineConfig extends Schema.Class("@usine/config/UsineConfig")({
|
|
78
133
|
version: Schema.Literal(1),
|
|
79
134
|
labels: DeclaredLabels.pipe(Schema.withDecodingDefaultKey(Effect.succeed([]))),
|