@vimhead.dev/norn-cli 0.1.0-tip.35359392805.1 → 0.1.0-tip.35390859630.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/assets/README.md +17 -26
- package/assets/docs/README.md +3 -3
- package/assets/docs/agents.md +12 -1
- package/assets/docs/cli.md +5 -5
- package/assets/docs/composition.md +21 -21
- package/assets/docs/persistence.md +7 -10
- package/assets/docs/projects.md +30 -18
- package/assets/docs/providers.md +1 -1
- package/assets/docs/recovery.md +13 -9
- package/assets/docs/resources.md +4 -19
- package/assets/docs/schemas.md +1 -1
- package/assets/docs/workflows.md +95 -7
- package/assets/examples/agent-then-analysis/README.md +4 -4
- package/assets/examples/agent-then-analysis/input.json +1 -1
- package/assets/examples/agent-then-analysis/norn.project.json +1 -1
- package/assets/examples/agent-then-analysis/plugin.ts +55 -68
- package/assets/examples/caller-selected-continuation/README.md +5 -5
- package/assets/examples/caller-selected-continuation/caller.ts +35 -38
- package/assets/examples/caller-selected-continuation/input.json +2 -2
- package/assets/examples/caller-selected-continuation/norn.project.json +1 -1
- package/assets/examples/caller-selected-continuation/producer.ts +15 -23
- package/assets/examples/coordinating-multiple-agents/README.md +2 -2
- package/assets/examples/coordinating-multiple-agents/input.json +1 -1
- package/assets/examples/coordinating-multiple-agents/norn.project.json +1 -1
- package/assets/examples/coordinating-multiple-agents/plugin.ts +52 -56
- package/assets/examples/coordinating-multiple-agents/queue-adapter.ts +4 -4
- package/assets/examples/getting-started/norn.project.json +1 -1
- package/assets/examples/getting-started/plugin.ts +16 -26
- package/assets/examples/minimal-workflow/README.md +8 -8
- package/assets/examples/minimal-workflow/norn.project.json +1 -1
- package/assets/examples/minimal-workflow/plugin.ts +11 -25
- package/assets/examples/shared-state/README.md +10 -2
- package/assets/examples/shared-state/input.json +1 -1
- package/assets/examples/shared-state/norn.project.json +1 -1
- package/assets/examples/shared-state/plugin.ts +36 -41
- package/assets/examples/shared-state/shared-state.ts +75 -0
- package/assets/{packages/sdk/src → examples/shared-state}/state-adapter.ts +17 -16
- package/assets/examples/worktree-development-loop/README.md +6 -7
- package/assets/examples/worktree-development-loop/norn.project.json +1 -1
- package/assets/examples/worktree-development-loop/plugin.ts +6 -26
- package/assets/examples/worktree-development-loop/scope.ts +4 -0
- package/assets/examples/worktree-development-loop/workflows/development-loop/execute.ts +14 -15
- package/assets/examples/worktree-development-loop/workflows/development-loop/index.ts +3 -4
- package/assets/examples/worktree-development-loop/workflows/development-loop/schema.ts +2 -2
- package/assets/examples/worktree-development-loop/workflows/implementation/execute.ts +34 -33
- package/assets/examples/worktree-development-loop/workflows/implementation/index.ts +2 -3
- package/assets/examples/worktree-development-loop/workflows/implementation/schema.ts +7 -3
- package/assets/examples/worktree-development-loop/workflows/planning/execute.ts +23 -16
- package/assets/examples/worktree-development-loop/workflows/planning/index.ts +2 -3
- package/assets/examples/worktree-development-loop/workflows/planning/schema.ts +4 -2
- package/assets/examples/worktree-development-loop/workflows/review/execute.ts +40 -31
- package/assets/examples/worktree-development-loop/workflows/review/index.ts +3 -4
- package/assets/examples/worktree-development-loop/workflows/review/schema.ts +5 -4
- package/assets/examples/worktree-development-loop/workflows/review-router/execute.ts +49 -44
- package/assets/examples/worktree-development-loop/workflows/review-router/index.ts +2 -3
- package/assets/examples/worktree-development-loop/workflows/review-router/schema.ts +4 -3
- package/assets/package.json +1 -1
- package/assets/packages/cli/src/cli.ts +35 -34
- package/assets/packages/cli/src/client.ts +9 -16
- package/assets/packages/cli/src/documentation-intro.ts +1 -1
- package/assets/packages/cli/src/generated-build-info.ts +2 -2
- package/assets/packages/cli/src/internal/agent-response-tool.ts +3 -3
- package/assets/packages/cli/src/internal/engine.ts +38 -62
- package/assets/packages/cli/src/internal/errors.ts +4 -4
- package/assets/packages/cli/src/internal/launch-request.ts +6 -6
- package/assets/packages/cli/src/internal/run-state.ts +31 -21
- package/assets/packages/cli/src/internal/run.ts +2 -5
- package/assets/packages/cli/src/internal/workflow-registry.ts +114 -111
- package/assets/packages/cli/src/workflow-loader.ts +214 -0
- package/assets/packages/core/src/workflow-transition.ts +2 -2
- package/assets/packages/sdk/src/api.ts +133 -289
- package/assets/packages/sdk/src/index.ts +1 -1
- package/assets/packages/sdk/src/schema.ts +3 -3
- package/assets/tests/workflow-ref.test.ts +50 -51
- package/dist/cli.js +33 -32
- package/dist/client.d.ts +3 -4
- package/dist/client.js +3 -8
- package/dist/documentation-intro.js +1 -1
- package/dist/generated-build-info.d.ts +2 -2
- package/dist/generated-build-info.js +2 -2
- package/dist/internal/agent-response-tool.js +3 -3
- package/dist/internal/engine.d.ts +4 -8
- package/dist/internal/engine.js +36 -49
- package/dist/internal/errors.d.ts +3 -3
- package/dist/internal/errors.js +1 -1
- package/dist/internal/launch-request.d.ts +4 -4
- package/dist/internal/launch-request.js +2 -2
- package/dist/internal/run-state.d.ts +9 -8
- package/dist/internal/run-state.js +29 -20
- package/dist/internal/run.d.ts +1 -3
- package/dist/internal/run.js +3 -5
- package/dist/internal/workflow-registry.d.ts +25 -17
- package/dist/internal/workflow-registry.js +107 -72
- package/dist/{plugin-loader.d.ts → workflow-loader.d.ts} +7 -8
- package/dist/workflow-loader.js +235 -0
- package/package.json +2 -2
- package/assets/examples/worktree-development-loop/manifest.ts +0 -26
- package/assets/examples/worktree-development-loop/state.ts +0 -23
- package/assets/examples/worktree-development-loop/workflows/development-loop/declaration.ts +0 -8
- package/assets/examples/worktree-development-loop/workflows/implementation/declaration.ts +0 -8
- package/assets/examples/worktree-development-loop/workflows/planning/declaration.ts +0 -8
- package/assets/examples/worktree-development-loop/workflows/review/declaration.ts +0 -8
- package/assets/examples/worktree-development-loop/workflows/review-router/declaration.ts +0 -12
- package/assets/packages/cli/src/internal/run-resources.ts +0 -23
- package/assets/packages/cli/src/internal/state-store.ts +0 -83
- package/assets/packages/cli/src/plugin-loader.ts +0 -400
- package/dist/internal/run-resources.d.ts +0 -6
- package/dist/internal/run-resources.js +0 -26
- package/dist/internal/state-store.d.ts +0 -23
- package/dist/internal/state-store.js +0 -103
- package/dist/plugin-loader.js +0 -341
|
@@ -1,39 +1,38 @@
|
|
|
1
|
-
import { artifactRefSchema,
|
|
1
|
+
import { artifactRefSchema, workflow, isWorkflowDeclaration, workflowRefSchema, type NornWorkflowRefSchemaOptions } from "@vimhead.dev/norn";
|
|
2
2
|
import { inspectSchema } from "@vimhead.dev/norn/schema";
|
|
3
3
|
import assert from "node:assert/strict";
|
|
4
4
|
import { Type } from "typebox";
|
|
5
5
|
import { Value } from "typebox/value";
|
|
6
6
|
import { expect, expectTypeOf, test } from "vitest";
|
|
7
7
|
|
|
8
|
-
const
|
|
9
|
-
id: "refs",
|
|
8
|
+
const target = workflow({
|
|
9
|
+
id: "refs.finish", isEntrypoint: false, args: Type.Object({ report: Type.String() }),
|
|
10
|
+
execute() { throw new Error("A callable must select a transition, not execute it"); },
|
|
10
11
|
});
|
|
11
|
-
const target = manifest.workflows.finish;
|
|
12
12
|
|
|
13
13
|
test("callable declarations retain metadata and construct transitions without execution", () => {
|
|
14
14
|
expectTypeOf(target.id).toEqualTypeOf<"refs.finish">();
|
|
15
15
|
assert.equal(isWorkflowDeclaration(target), true);
|
|
16
|
-
assert.equal(isWorkflowPluginManifest(manifest), true);
|
|
17
16
|
assert.equal(isWorkflowDeclaration(() => undefined), false);
|
|
18
|
-
assert.equal(Value.Check(target.
|
|
17
|
+
assert.equal(Value.Check(target.args, { report: "done" }), true);
|
|
19
18
|
assert.deepEqual(target.isolation, { mode: "runWorkspace" });
|
|
20
|
-
assert.deepEqual(target({ report: "done" }), { type: "next", workflowId: target.id,
|
|
19
|
+
assert.deepEqual(target({ report: "done" }), { type: "next", workflowId: target.id, args: { report: "done" } });
|
|
21
20
|
});
|
|
22
21
|
|
|
23
22
|
for (const reference of ["refs.finish", target.id]) {
|
|
24
23
|
test("workflow references decode into contribution-taking transition builders", () => {
|
|
25
24
|
const contributions = Type.Object({ report: Type.String() });
|
|
26
|
-
const options: NornWorkflowRefSchemaOptions<typeof contributions> = {
|
|
25
|
+
const options: NornWorkflowRefSchemaOptions<typeof contributions> = { args: contributions };
|
|
27
26
|
const schema = workflowRefSchema(options);
|
|
28
|
-
assert.deepEqual(Value.Decode(workflowRefSchema({}), reference)({}), { type: "next", workflowId: "refs.finish",
|
|
27
|
+
assert.deepEqual(Value.Decode(workflowRefSchema({}), reference)({}), { type: "next", workflowId: "refs.finish", args: {} });
|
|
29
28
|
interface Report { report: string }
|
|
30
29
|
const report: Report = { report: "done" };
|
|
31
30
|
assert.deepEqual(Value.Decode(schema, reference)(report), target(report));
|
|
32
|
-
const input = { workflow: reference,
|
|
31
|
+
const input = { workflow: reference, forwardArgs: { task: "retain", report: "stale" } };
|
|
33
32
|
const next = Value.Decode(schema, input);
|
|
34
|
-
assert.deepEqual(next({ report: "done" }), { type: "next", workflowId: reference,
|
|
35
|
-
assert.deepEqual(next({ report: "again" }).
|
|
36
|
-
assert.deepEqual(input.
|
|
33
|
+
assert.deepEqual(next({ report: "done" }), { type: "next", workflowId: reference, args: { task: "retain", report: "done" } });
|
|
34
|
+
assert.deepEqual(next({ report: "again" }).args, { task: "retain", report: "again" });
|
|
35
|
+
assert.deepEqual(input.forwardArgs, { task: "retain", report: "stale" });
|
|
37
36
|
assert.throws(() => Reflect.apply(next, undefined, [{}]));
|
|
38
37
|
});
|
|
39
38
|
}
|
|
@@ -46,7 +45,7 @@ test("nullable reference inputs retain their required property and input union",
|
|
|
46
45
|
assert.equal(Value.Check(schema, { next: null }), true);
|
|
47
46
|
assert.equal(Value.Check(schema, { next: "refs.finish" }), true);
|
|
48
47
|
expect(inspected).toHaveProperty("properties.next.anyOf.0.anyOf.0.type", "string");
|
|
49
|
-
expect(inspected).toHaveProperty("properties.next.anyOf.0.anyOf.1.required", ["workflow", "
|
|
48
|
+
expect(inspected).toHaveProperty("properties.next.anyOf.0.anyOf.1.required", ["workflow", "forwardArgs"]);
|
|
50
49
|
});
|
|
51
50
|
|
|
52
51
|
test("optional reference properties remain optional in validation and inspection", () => {
|
|
@@ -62,35 +61,35 @@ test("inspection advertises encoded contributions and invocation does not decode
|
|
|
62
61
|
count: Type.Decode(Type.String(), value => { decodes++; return Number(value); }),
|
|
63
62
|
label: Type.Optional(Type.String({ default: "collected" })),
|
|
64
63
|
});
|
|
65
|
-
const reference = Type.With(workflowRefSchema({
|
|
64
|
+
const reference = Type.With(workflowRefSchema({ args: contributions }), { description: "Continue with the collected records" });
|
|
66
65
|
const schema = Type.Object({ next: reference });
|
|
67
66
|
const inspected = inspectSchema(schema);
|
|
68
|
-
expect(inspected).toHaveProperty("properties.next.x-norn-workflow-ref.
|
|
67
|
+
expect(inspected).toHaveProperty("properties.next.x-norn-workflow-ref.contributedArgsSchema", inspectSchema(contributions));
|
|
69
68
|
expect(inspected).toHaveProperty("properties.next.description", "Continue with the collected records");
|
|
70
69
|
expect(inspected).toHaveProperty("properties.next.anyOf", inspectSchema(workflowRefSchema()).anyOf);
|
|
71
|
-
expect(inspected).toHaveProperty("properties.next.anyOf.1.properties.
|
|
72
|
-
expect(inspected).toHaveProperty("properties.next.x-norn-workflow-ref.
|
|
73
|
-
expect(inspected).toHaveProperty("properties.next.x-norn-workflow-ref.
|
|
74
|
-
const
|
|
75
|
-
const next = Value.Decode(schema, { next: { workflow: "refs.finish",
|
|
70
|
+
expect(inspected).toHaveProperty("properties.next.anyOf.1.properties.forwardArgs.patternProperties", { "^.*$": {} });
|
|
71
|
+
expect(inspected).toHaveProperty("properties.next.x-norn-workflow-ref.contributedArgsSchema.required", ["records", "count"]);
|
|
72
|
+
expect(inspected).toHaveProperty("properties.next.x-norn-workflow-ref.contributedArgsSchema.properties.count.type", "string");
|
|
73
|
+
const forwardArgs = { taskId: "task-42", nested: { labels: ["one", "two"], enabled: false, absent: null } };
|
|
74
|
+
const next = Value.Decode(schema, { next: { workflow: "refs.finish", forwardArgs } }).next;
|
|
76
75
|
const contribution = { records: { path: "records.json" }, count: "2" };
|
|
77
|
-
assert.deepEqual(next(contribution).
|
|
76
|
+
assert.deepEqual(next(contribution).args, { ...forwardArgs, ...contribution });
|
|
78
77
|
assert.equal(decodes, 0);
|
|
79
78
|
assert.throws(() => Reflect.apply(next, undefined, [{ records: { path: "records.json" }, count: 2 }]));
|
|
80
79
|
});
|
|
81
80
|
|
|
82
81
|
test("references without contributions advertise an empty object schema", () => {
|
|
83
82
|
const reference = workflowRefSchema();
|
|
84
|
-
expect(inspectSchema(reference)).toHaveProperty("x-norn-workflow-ref.
|
|
85
|
-
assert.deepEqual(Value.Decode(reference, "refs.finish")({}), { type: "next", workflowId: "refs.finish",
|
|
83
|
+
expect(inspectSchema(reference)).toHaveProperty("x-norn-workflow-ref.contributedArgsSchema", inspectSchema(Type.Object({})));
|
|
84
|
+
assert.deepEqual(Value.Decode(reference, "refs.finish")({}), { type: "next", workflowId: "refs.finish", args: {} });
|
|
86
85
|
});
|
|
87
86
|
|
|
88
87
|
test("optional, nullable and array references retain annotations and callable codecs", () => {
|
|
89
|
-
const reference = workflowRefSchema({
|
|
88
|
+
const reference = workflowRefSchema({ args: Type.Object({ report: Type.String() }) });
|
|
90
89
|
const schema = Type.Object({ optional: Type.Optional(reference), nullable: Type.Union([reference, Type.Null()]), many: Type.Array(reference) });
|
|
91
90
|
const inspected = inspectSchema(schema);
|
|
92
91
|
for (const path of ["properties.optional", "properties.nullable.anyOf.0", "properties.many.items"]) {
|
|
93
|
-
expect(inspected).toHaveProperty(`${path}.x-norn-workflow-ref.
|
|
92
|
+
expect(inspected).toHaveProperty(`${path}.x-norn-workflow-ref.contributedArgsSchema`, inspectSchema(Type.Object({ report: Type.String() })));
|
|
94
93
|
}
|
|
95
94
|
assert.deepEqual(inspected.required, ["nullable", "many"]);
|
|
96
95
|
const decoded = Value.Decode(schema, { nullable: null, many: ["refs.finish"], optional: "refs.finish" });
|
|
@@ -101,56 +100,56 @@ test("optional, nullable and array references retain annotations and callable co
|
|
|
101
100
|
|
|
102
101
|
test("nested references retain encoded payloads in contributed parameters", () => {
|
|
103
102
|
const finalContributions = Type.Object({ summary: Type.String() });
|
|
104
|
-
const contributions = Type.Object({ records: artifactRefSchema, next: workflowRefSchema({
|
|
105
|
-
const reference = workflowRefSchema({
|
|
106
|
-
expect(inspectSchema(reference)).toHaveProperty("x-norn-workflow-ref.
|
|
107
|
-
const
|
|
108
|
-
const transition = Value.Decode(reference, "intermediate")(
|
|
109
|
-
assert.deepEqual(transition.
|
|
110
|
-
assert.deepEqual(Value.Decode(contributions, transition.
|
|
103
|
+
const contributions = Type.Object({ records: artifactRefSchema, next: workflowRefSchema({ args: finalContributions }) });
|
|
104
|
+
const reference = workflowRefSchema({ args: contributions });
|
|
105
|
+
expect(inspectSchema(reference)).toHaveProperty("x-norn-workflow-ref.contributedArgsSchema.properties.next.x-norn-workflow-ref.contributedArgsSchema", inspectSchema(finalContributions));
|
|
106
|
+
const args = { records: { path: "records.json" }, next: { workflow: "refs.finish", forwardArgs: { batch: 1 } } };
|
|
107
|
+
const transition = Value.Decode(reference, "intermediate")(args);
|
|
108
|
+
assert.deepEqual(transition.args, args);
|
|
109
|
+
assert.deepEqual(Value.Decode(contributions, transition.args).next({ summary: "done" }).args, { batch: 1, summary: "done" });
|
|
111
110
|
});
|
|
112
111
|
|
|
113
112
|
test("recursive contributions use native TypeBox definitions", () => {
|
|
114
113
|
const contributions = Type.Cyclic({ Contribution: Type.Object({ label: Type.String(), children: Type.Array(Type.Ref("Contribution")) }) }, "Contribution");
|
|
115
|
-
const reference = workflowRefSchema({
|
|
116
|
-
expect(inspectSchema(reference)).toHaveProperty("x-norn-workflow-ref.
|
|
117
|
-
const
|
|
118
|
-
assert.deepEqual(Value.Decode(reference, "refs.finish")(
|
|
114
|
+
const reference = workflowRefSchema({ args: contributions });
|
|
115
|
+
expect(inspectSchema(reference)).toHaveProperty("x-norn-workflow-ref.contributedArgsSchema", inspectSchema(contributions));
|
|
116
|
+
const args = { label: "root", children: [{ label: "leaf", children: [] }] };
|
|
117
|
+
assert.deepEqual(Value.Decode(reference, "refs.finish")(args).args, args);
|
|
119
118
|
});
|
|
120
119
|
|
|
121
120
|
test("object-valued unions, records, intersections and root codecs can contribute", () => {
|
|
122
|
-
const union = workflowRefSchema({
|
|
123
|
-
assert.deepEqual(Value.Decode(union, "target")({ kind: "error", reason: "failed" }).
|
|
124
|
-
const record = workflowRefSchema({
|
|
125
|
-
assert.deepEqual(Value.Decode(record, "target")({ first: 1 }).
|
|
126
|
-
const intersection = workflowRefSchema({
|
|
127
|
-
assert.deepEqual(Value.Decode(intersection, "target")({ left: "x", right: 2 }).
|
|
128
|
-
const codec = workflowRefSchema({
|
|
129
|
-
assert.deepEqual(Value.Decode(codec, "target")({ text: "encoded" }).
|
|
130
|
-
const unknown = Value.Decode(workflowRefSchema({
|
|
121
|
+
const union = workflowRefSchema({ args: Type.Union([Type.Object({ kind: Type.Literal("ok"), report: Type.String() }), Type.Object({ kind: Type.Literal("error"), reason: Type.String() })]) });
|
|
122
|
+
assert.deepEqual(Value.Decode(union, "target")({ kind: "error", reason: "failed" }).args, { kind: "error", reason: "failed" });
|
|
123
|
+
const record = workflowRefSchema({ args: Type.Record(Type.String(), Type.Integer()) });
|
|
124
|
+
assert.deepEqual(Value.Decode(record, "target")({ first: 1 }).args, { first: 1 });
|
|
125
|
+
const intersection = workflowRefSchema({ args: Type.Intersect([Type.Object({ left: Type.String() }), Type.Object({ right: Type.Integer() })]) });
|
|
126
|
+
assert.deepEqual(Value.Decode(intersection, "target")({ left: "x", right: 2 }).args, { left: "x", right: 2 });
|
|
127
|
+
const codec = workflowRefSchema({ args: Type.Decode(Type.Object({ text: Type.String() }), value => value.text) });
|
|
128
|
+
assert.deepEqual(Value.Decode(codec, "target")({ text: "encoded" }).args, { text: "encoded" });
|
|
129
|
+
const unknown = Value.Decode(workflowRefSchema({ args: Type.Unknown() }), "target");
|
|
131
130
|
for (const value of [null, [], "text", 1]) assert.throws(() => Reflect.apply(unknown, undefined, [value]), /contributions must be objects/);
|
|
132
131
|
for (const value of [new Date(), { callback: () => undefined }]) assert.throws(() => Reflect.apply(unknown, undefined, [value]));
|
|
133
132
|
});
|
|
134
133
|
|
|
135
134
|
test("non-JSON contributions fail explicit inspection without preventing construction", () => {
|
|
136
|
-
const reference = workflowRefSchema({
|
|
135
|
+
const reference = workflowRefSchema({ args: Type.BigInt() });
|
|
137
136
|
assert.equal(typeof Value.Decode(reference, "refs.finish"), "function");
|
|
138
137
|
assert.throws(() => inspectSchema(reference));
|
|
139
|
-
const nested = workflowRefSchema({
|
|
138
|
+
const nested = workflowRefSchema({ args: Type.Object({ next: reference }) });
|
|
140
139
|
assert.throws(() => inspectSchema(Type.Object({ next: Type.Optional(Type.Array(nested)) })));
|
|
141
140
|
});
|
|
142
141
|
|
|
143
142
|
test("inspection traverses schema positions, not defaults, examples or property-name maps", () => {
|
|
144
|
-
const payload = { "x-norn-workflow-ref": {
|
|
143
|
+
const payload = { "x-norn-workflow-ref": { contributedArgsSchema: { type: "bigint" } } };
|
|
145
144
|
const schema = Type.Object({ "x-norn-workflow-ref": Type.String() }, { default: payload, examples: [payload] });
|
|
146
145
|
assert.doesNotThrow(() => inspectSchema(schema));
|
|
147
|
-
const invalid = workflowRefSchema({
|
|
146
|
+
const invalid = workflowRefSchema({ args: Type.BigInt() });
|
|
148
147
|
for (const wrapper of [Type.Record(Type.String(), invalid), Type.Tuple([invalid]), Type.Object({}, { additionalProperties: invalid }), Type.Cyclic({ Root: Type.Object({ child: invalid }) }, "Root")]) {
|
|
149
148
|
assert.throws(() => inspectSchema(wrapper));
|
|
150
149
|
}
|
|
151
150
|
});
|
|
152
151
|
|
|
153
|
-
for (const input of [undefined, null, "", 3, {}, { workflow: "refs.finish" }, { workflow: "",
|
|
152
|
+
for (const input of [undefined, null, "", 3, {}, { workflow: "refs.finish" }, { workflow: "", forwardArgs: {} }, { workflow: "refs.finish", forwardArgs: null }, { workflow: target, forwardArgs: {} }]) {
|
|
154
153
|
test(`reference input schema rejects invalid shape without coercion: ${JSON.stringify(input)}`, () => {
|
|
155
154
|
assert.equal(Value.Check(workflowRefSchema(), input), false);
|
|
156
155
|
});
|
package/dist/cli.js
CHANGED
|
@@ -23,9 +23,9 @@ import { clearRunResumeRequest, readRunLaunchRequest, readRunResumeRequest, writ
|
|
|
23
23
|
import { readRunMetrics } from "./internal/metrics.js";
|
|
24
24
|
import { getRunLeaseOwner, NornRunLease } from "./internal/run-lease.js";
|
|
25
25
|
import { generateRunName } from "./internal/run-names.js";
|
|
26
|
-
import { getRunInfo, listRuns,
|
|
26
|
+
import { assertRunVersion, getRunInfo, listRuns, mergeInterruptedWorkflowArgs, resolveRunRoot } from "./internal/run-state.js";
|
|
27
27
|
import { NornRunStore } from "./internal/run-store.js";
|
|
28
|
-
import { discoverNornProject, findNornProject, inspectNornWorkflow, loadNornProject, NORN_PROJECT_FILE_NAME } from "./
|
|
28
|
+
import { discoverNornProject, findNornProject, inspectNornWorkflow, loadNornProject, NORN_PROJECT_FILE_NAME } from "./workflow-loader.js";
|
|
29
29
|
var RUNS_ROOT = join(".norn", "runs");
|
|
30
30
|
var RUN_WAIT_INTERVAL_MS = 1e3;
|
|
31
31
|
var CLI_DESCRIPTION = "Norn is a harness-agnostic runtime for agent-driven and code-driven workflows, built primarily for agents. Build workflows with the Norn SDK; use this JSON-native CLI to discover workflows, start or resume runs, inspect evidence, and manage the installed runtime.";
|
|
@@ -112,9 +112,9 @@ var COMMANDS = [
|
|
|
112
112
|
{
|
|
113
113
|
id: "project.inspect",
|
|
114
114
|
path: ["project", "inspect"],
|
|
115
|
-
description: "Use when discovering the active Norn project,
|
|
115
|
+
description: "Use when discovering the active Norn project, configuration, and workflow sources.",
|
|
116
116
|
usage: "norn project inspect",
|
|
117
|
-
output: "JSON object with project metadata under project, isComplete, and
|
|
117
|
+
output: "JSON object with project metadata under project, isComplete, and registration diagnostics. Incomplete discovery does not permit execution.",
|
|
118
118
|
examples: ["norn project inspect"],
|
|
119
119
|
execute: async (args) => {
|
|
120
120
|
assertNoExtraArgs("project inspect", args);
|
|
@@ -127,17 +127,17 @@ var COMMANDS = [
|
|
|
127
127
|
description: "Use when selecting a Norn workflow for a user task; defaults to entrypoint workflows.",
|
|
128
128
|
usage: "norn workflows list [--entrypoints|--all]",
|
|
129
129
|
options: ["--entrypoints: list entrypoint workflows", "--all: include internal workflow steps"],
|
|
130
|
-
output: "JSON object with workflow summaries under workflows, isComplete, and
|
|
130
|
+
output: "JSON object with workflow summaries under workflows, isComplete, and registration diagnostics. Successful discovery can be incomplete; start/resume remain strict.",
|
|
131
131
|
examples: ["norn workflows list", "norn workflows list --all"],
|
|
132
132
|
execute: listWorkflows
|
|
133
133
|
},
|
|
134
134
|
{
|
|
135
135
|
id: "workflows.inspect",
|
|
136
136
|
path: ["workflows", "inspect"],
|
|
137
|
-
description: "Use when reading a workflow's instructions,
|
|
137
|
+
description: "Use when reading a workflow's instructions, args schema, gate contract, and registration source before starting or editing it.",
|
|
138
138
|
usage: "norn workflows inspect <workflow-id>",
|
|
139
139
|
arguments: ["workflow-id: fully qualified workflow id"],
|
|
140
|
-
output: "JSON object with workflow details, isComplete, and
|
|
140
|
+
output: "JSON object with workflow details, isComplete, and registration diagnostics. workflow is null if unavailable in an incomplete catalog or its schema cannot be inspected; an unknown id in a complete catalog is an error.",
|
|
141
141
|
examples: ["norn workflows inspect example.plan"],
|
|
142
142
|
execute: async (args) => {
|
|
143
143
|
const workflowId = requiredArg("workflows inspect", args, 0, "workflow id");
|
|
@@ -148,12 +148,12 @@ var COMMANDS = [
|
|
|
148
148
|
{
|
|
149
149
|
id: "runs.start",
|
|
150
150
|
path: ["runs", "start"],
|
|
151
|
-
description: "Use when starting a Norn workflow run after the workflow id and
|
|
151
|
+
description: "Use when starting a Norn workflow run after the workflow id and args are known.",
|
|
152
152
|
usage: "norn runs start <workflow-id>",
|
|
153
153
|
arguments: ["workflow-id: fully qualified workflow id to start"],
|
|
154
|
-
stdin: 'Optional JSON object: {"
|
|
154
|
+
stdin: 'Optional JSON object: {"args":{...},"config":{"workflowOrScopeId":{...}}}.',
|
|
155
155
|
output: "JSON object with started run info under run.",
|
|
156
|
-
examples: [`printf '{"
|
|
156
|
+
examples: [`printf '{"args":{"task":"Add tests"}}' | norn runs start example.plan`],
|
|
157
157
|
execute: async (args) => {
|
|
158
158
|
const workflowId = requiredArg("runs start", args, 0, "workflow id");
|
|
159
159
|
await startRun(workflowId, args.slice(1));
|
|
@@ -165,9 +165,9 @@ var COMMANDS = [
|
|
|
165
165
|
description: "Use when resuming an interrupted gate or a checkpoint restored for retry.",
|
|
166
166
|
usage: "norn runs resume <run>",
|
|
167
167
|
arguments: ["run: run id, generated name, or run path"],
|
|
168
|
-
stdin: 'Optional JSON object: {"
|
|
168
|
+
stdin: 'Optional JSON object: {"args":{...}}. Interrupted runs require a patch containing only editable gate fields; pending-resume runs do not accept args.',
|
|
169
169
|
output: "JSON object with resumed run info under run.",
|
|
170
|
-
examples: [`printf '{"
|
|
170
|
+
examples: [`printf '{"args":{"decision":"accept"}}' | norn runs resume quiet-river-lantern`],
|
|
171
171
|
execute: async (args) => {
|
|
172
172
|
const run = requiredArg("runs resume", args, 0, "run");
|
|
173
173
|
await resumeRun(run, args.slice(1));
|
|
@@ -686,7 +686,7 @@ async function initProject() {
|
|
|
686
686
|
const projectPath = resolve(projectRoot, NORN_PROJECT_FILE_NAME);
|
|
687
687
|
if (await isFile(projectPath)) throw new Error(`Norn project already exists: ${projectPath}`);
|
|
688
688
|
await mkdir(resolve(projectRoot, RUNS_ROOT), { recursive: true });
|
|
689
|
-
await writeFile(projectPath, `${JSON.stringify({ version: 1,
|
|
689
|
+
await writeFile(projectPath, `${JSON.stringify({ version: 1, workflows: [], includes: [], config: {} }, null, 2)}
|
|
690
690
|
`, "utf8");
|
|
691
691
|
await ensureGitignoreExcludesRunState(projectRoot);
|
|
692
692
|
writeJson({ project: { path: projectPath, root: projectRoot, runsRoot: resolve(projectRoot, RUNS_ROOT) } });
|
|
@@ -699,21 +699,21 @@ async function inspectProject() {
|
|
|
699
699
|
const { project, isComplete, diagnostics } = await discoverNornProject(process.cwd());
|
|
700
700
|
writeJson({ project, isComplete, diagnostics });
|
|
701
701
|
}
|
|
702
|
-
async function startRun(workflowId,
|
|
703
|
-
assertNoStructuredInputArgs("runs start",
|
|
702
|
+
async function startRun(workflowId, commandArgs) {
|
|
703
|
+
assertNoStructuredInputArgs("runs start", commandArgs);
|
|
704
704
|
const project = await loadNornProject(process.cwd());
|
|
705
705
|
const workflow = project.registry.workflowById(workflowId);
|
|
706
706
|
if (!workflow) throw new Error(`Unknown workflow: ${workflowId}`);
|
|
707
707
|
const input = parseStartRunInput(await readStdinJson());
|
|
708
|
-
const
|
|
709
|
-
Value.Decode(workflow.
|
|
708
|
+
const args = input.args === void 0 ? {} : input.args;
|
|
709
|
+
Value.Decode(workflow.args, args);
|
|
710
710
|
const configOverride = input.config;
|
|
711
711
|
const id = randomUUID();
|
|
712
712
|
const name = generateRunName(new Set((await listRuns(project.projectRoot)).map((run) => run.name)));
|
|
713
713
|
const runRoot = resolve(project.projectRoot, RUNS_ROOT, id);
|
|
714
714
|
await mkdir(runRoot, { recursive: true });
|
|
715
715
|
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
716
|
-
await writeRunLaunchRequest(runRoot, { version:
|
|
716
|
+
await writeRunLaunchRequest(runRoot, { version: 2, type: "run", id, name, workflowId, args, configOverride, createdAt });
|
|
717
717
|
await startDetachedExecuteRun(id, project.projectRoot);
|
|
718
718
|
writeJson({ run: startedRunInfo({ id, name, workflow, runRoot, createdAt }) });
|
|
719
719
|
}
|
|
@@ -726,8 +726,9 @@ async function resumeRun(run, args) {
|
|
|
726
726
|
let request;
|
|
727
727
|
try {
|
|
728
728
|
const runInfo = await getRunInfo(runRoot);
|
|
729
|
-
|
|
730
|
-
|
|
729
|
+
assertRunVersion(runInfo.version);
|
|
730
|
+
const args2 = await parseResumeArgs(runInfo, input.args);
|
|
731
|
+
request = { version: 2, type: "resume", id: runInfo.id, requestId: randomUUID(), args: args2, createdAt: (/* @__PURE__ */ new Date()).toISOString() };
|
|
731
732
|
await writeRunResumeRequest(runRoot, request);
|
|
732
733
|
} finally {
|
|
733
734
|
await lease.release();
|
|
@@ -740,18 +741,18 @@ async function resumeRun(run, args) {
|
|
|
740
741
|
}
|
|
741
742
|
writeJson({ run: await getRunInfo(runRoot) });
|
|
742
743
|
}
|
|
743
|
-
async function
|
|
744
|
+
async function parseResumeArgs(runInfo, args) {
|
|
744
745
|
if (runInfo.status === "interrupted") {
|
|
745
|
-
if (
|
|
746
|
+
if (args === void 0) throw new Error(`Interrupted workflow resume requires args: ${runInfo.name}`);
|
|
746
747
|
if (!runInfo.currentWorkflowId) throw new Error(`Run has no current workflow: ${runInfo.name}`);
|
|
747
748
|
const project = await loadNornProject(process.cwd());
|
|
748
749
|
const workflow = project.registry.workflowById(runInfo.currentWorkflowId);
|
|
749
750
|
if (!workflow) throw new Error(`Unknown workflow for resumed run: ${runInfo.currentWorkflowId}`);
|
|
750
|
-
Value.Decode(workflow.
|
|
751
|
-
return
|
|
751
|
+
Value.Decode(workflow.args, mergeInterruptedWorkflowArgs(runInfo.interruption?.args, args, runInfo.interruption?.fields));
|
|
752
|
+
return args;
|
|
752
753
|
}
|
|
753
754
|
if (runInfo.status === "pendingResume") {
|
|
754
|
-
if (
|
|
755
|
+
if (args !== void 0) throw new Error(`Pending-resume workflows do not accept args: ${runInfo.name}`);
|
|
755
756
|
return void 0;
|
|
756
757
|
}
|
|
757
758
|
throw new Error(`Run must be rolled back before resuming: ${runInfo.name}`);
|
|
@@ -823,11 +824,11 @@ async function executeRun(runId) {
|
|
|
823
824
|
try {
|
|
824
825
|
const project = await loadNornProject(process.cwd());
|
|
825
826
|
const engine = new NornEngine({ cwd: project.projectRoot, signal: abortController.signal, gateMode: "pause", config: project.projectConfig });
|
|
826
|
-
|
|
827
|
+
engine.registerWorkflows(project.definitions);
|
|
827
828
|
if (request.type === "run") {
|
|
828
829
|
const workflow = project.registry.workflowById(request.workflowId);
|
|
829
830
|
if (!workflow) throw new Error(`Unknown workflow: ${request.workflowId}`);
|
|
830
|
-
await engine.runWorkflow(workflow, request.
|
|
831
|
+
await engine.runWorkflow(workflow, request.args, { id: request.id, name: request.name, configOverride: request.configOverride });
|
|
831
832
|
} else {
|
|
832
833
|
await engine.resumeRequestedWorkflow({ runRoot, request });
|
|
833
834
|
}
|
|
@@ -913,7 +914,7 @@ function detachedExecuteRunArgs(runId) {
|
|
|
913
914
|
}
|
|
914
915
|
function startedRunInfo(input) {
|
|
915
916
|
return {
|
|
916
|
-
version:
|
|
917
|
+
version: 2,
|
|
917
918
|
id: input.id,
|
|
918
919
|
name: input.name,
|
|
919
920
|
path: input.runRoot,
|
|
@@ -963,14 +964,14 @@ async function readStdin() {
|
|
|
963
964
|
function parseStartRunInput(value) {
|
|
964
965
|
if (value === void 0) return {};
|
|
965
966
|
const input = parseStructuredInputObject("runs start", value);
|
|
966
|
-
assertStructuredInputKeys("runs start", input, ["
|
|
967
|
-
return {
|
|
967
|
+
assertStructuredInputKeys("runs start", input, ["args", "config"]);
|
|
968
|
+
return { args: input.args, config: input.config };
|
|
968
969
|
}
|
|
969
970
|
function parseResumeRunInput(value) {
|
|
970
971
|
if (value === void 0) return {};
|
|
971
972
|
const input = parseStructuredInputObject("runs resume", value);
|
|
972
|
-
assertStructuredInputKeys("runs resume", input, ["
|
|
973
|
-
return {
|
|
973
|
+
assertStructuredInputKeys("runs resume", input, ["args"]);
|
|
974
|
+
return { args: input.args };
|
|
974
975
|
}
|
|
975
976
|
function parseStructuredInputObject(command, value) {
|
|
976
977
|
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error(`${command} reads a JSON object from stdin`);
|
package/dist/client.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DeletedNornRunInfo, NornProjectInspection, NornWorkflowCatalogInfo, NornWorkflowInspection, NornRunCheckpoint, NornRunInfo, NornRunMetrics
|
|
1
|
+
import type { DeletedNornRunInfo, NornProjectInspection, NornWorkflowCatalogInfo, NornWorkflowInspection, NornRunCheckpoint, NornRunInfo, NornRunMetrics } from "@vimhead.dev/norn";
|
|
2
2
|
import type { NornRegisteredWorkflow } from "./internal/workflow-registry.ts";
|
|
3
3
|
export { NornProjectLoadError } from "./internal/errors.ts";
|
|
4
4
|
export type NornClientInput = {
|
|
@@ -16,16 +16,15 @@ export type NornClient = {
|
|
|
16
16
|
inspect(workflowId: string): Promise<NornWorkflowInspection>;
|
|
17
17
|
entries(): Promise<readonly NornRegisteredWorkflow[]>;
|
|
18
18
|
};
|
|
19
|
-
readonly state: NornWorkflowStateReader;
|
|
20
19
|
readonly runs: {
|
|
21
20
|
start(input: {
|
|
22
21
|
readonly workflowId: string;
|
|
23
|
-
readonly
|
|
22
|
+
readonly args: unknown;
|
|
24
23
|
readonly configOverride?: unknown;
|
|
25
24
|
}): Promise<NornRunInfo>;
|
|
26
25
|
resume(input: {
|
|
27
26
|
readonly run: string;
|
|
28
|
-
readonly
|
|
27
|
+
readonly args?: unknown;
|
|
29
28
|
}): Promise<NornRunInfo>;
|
|
30
29
|
wait(run: string): Promise<NornRunInfo>;
|
|
31
30
|
list(): Promise<NornRunInfo[]>;
|
package/dist/client.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { spawn } from "node:child_process";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { createInterface } from "node:readline";
|
|
5
|
-
import { loadNornProject } from "./
|
|
5
|
+
import { loadNornProject } from "./workflow-loader.js";
|
|
6
6
|
import { NornProjectLoadError } from "./internal/errors.js";
|
|
7
7
|
import { NornProjectLoadError as NornProjectLoadError2 } from "./internal/errors.js";
|
|
8
8
|
function createNornClient(input = {}) {
|
|
@@ -17,7 +17,6 @@ function createNornClient(input = {}) {
|
|
|
17
17
|
inspect: async (workflowId) => processRunner.readJson(["workflows", "inspect", workflowId]),
|
|
18
18
|
entries: async () => (await catalog.load()).workflows
|
|
19
19
|
},
|
|
20
|
-
state: catalog.state,
|
|
21
20
|
runs: {
|
|
22
21
|
start: async (startInput) => (await processRunner.readJson(["runs", "start", startInput.workflowId], JSON.stringify(runStartInput(startInput)))).run,
|
|
23
22
|
resume: async (resumeInput) => (await processRunner.readJson(["runs", "resume", resumeInput.run], runResumeStdin(resumeInput))).run,
|
|
@@ -41,10 +40,6 @@ var NornWorkflowCatalog = class {
|
|
|
41
40
|
}
|
|
42
41
|
cwd;
|
|
43
42
|
loaded;
|
|
44
|
-
state = {
|
|
45
|
-
get: async (state) => (await this.load()).state.get(state),
|
|
46
|
-
getOptional: async (state) => (await this.load()).state.getOptional(state)
|
|
47
|
-
};
|
|
48
43
|
load() {
|
|
49
44
|
this.loaded ??= loadNornProject(this.cwd).catch((error) => {
|
|
50
45
|
this.loaded = void 0;
|
|
@@ -104,10 +99,10 @@ var NornProcessRunner = class {
|
|
|
104
99
|
}
|
|
105
100
|
};
|
|
106
101
|
function runStartInput(input) {
|
|
107
|
-
return input.configOverride === void 0 ? {
|
|
102
|
+
return input.configOverride === void 0 ? { args: input.args } : { args: input.args, config: input.configOverride };
|
|
108
103
|
}
|
|
109
104
|
function runResumeStdin(input) {
|
|
110
|
-
return input.
|
|
105
|
+
return input.args === void 0 ? void 0 : JSON.stringify({ args: input.args });
|
|
111
106
|
}
|
|
112
107
|
export {
|
|
113
108
|
NornProjectLoadError2 as NornProjectLoadError,
|
|
@@ -20,7 +20,7 @@ function renderNornDocumentationIntro(input) {
|
|
|
20
20
|
"",
|
|
21
21
|
"Live CLI contracts (argument arrays appended to the runtime argv above):",
|
|
22
22
|
'- ["workflows", "list"] \u2014 currently registered entrypoints.',
|
|
23
|
-
'- ["workflows", "inspect", "<workflow-id>"] \u2014 current instructions,
|
|
23
|
+
'- ["workflows", "inspect", "<workflow-id>"] \u2014 current instructions, args schema, and source.',
|
|
24
24
|
'- ["help"] \u2014 command usage.'
|
|
25
25
|
].join("\n");
|
|
26
26
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export declare const NORN_GENERATED_BUILD_INFO: {
|
|
2
2
|
readonly kind: "npm-registry";
|
|
3
|
-
readonly version: "0.1.0-tip.
|
|
4
|
-
readonly commit: "
|
|
3
|
+
readonly version: "0.1.0-tip.35390859630.1";
|
|
4
|
+
readonly commit: "7a7976170d62c342c29c6479444394d542c3f22b";
|
|
5
5
|
readonly packageSpec: "@vimhead.dev/norn-cli@tip";
|
|
6
6
|
readonly upgrade: {
|
|
7
7
|
readonly supported: false;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// src/generated-build-info.ts
|
|
2
2
|
var NORN_GENERATED_BUILD_INFO = {
|
|
3
3
|
"kind": "npm-registry",
|
|
4
|
-
"version": "0.1.0-tip.
|
|
5
|
-
"commit": "
|
|
4
|
+
"version": "0.1.0-tip.35390859630.1",
|
|
5
|
+
"commit": "7a7976170d62c342c29c6479444394d542c3f22b",
|
|
6
6
|
"packageSpec": "@vimhead.dev/norn-cli@tip",
|
|
7
7
|
"upgrade": {
|
|
8
8
|
"supported": false,
|
|
@@ -52,12 +52,12 @@ var NornAgentResponseToolFactory = class {
|
|
|
52
52
|
label: Type.String({ description: "The exact workflow agent label provided in the prompt." }),
|
|
53
53
|
response: Type.Unknown({ description: "The structured response object requested by the workflow." })
|
|
54
54
|
}),
|
|
55
|
-
async execute(_toolCallId,
|
|
55
|
+
async execute(_toolCallId, args) {
|
|
56
56
|
try {
|
|
57
|
-
const captured = responseCollector.capture(
|
|
57
|
+
const captured = responseCollector.capture(args.runId, args.label, args.response);
|
|
58
58
|
return {
|
|
59
59
|
content: [{ type: "text", text: `Recorded workflow agent response for ${captured.label}.` }],
|
|
60
|
-
details: { valid: true, runId:
|
|
60
|
+
details: { valid: true, runId: args.runId, label: captured.label, response: captured.response },
|
|
61
61
|
terminate: true
|
|
62
62
|
};
|
|
63
63
|
} catch (error) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type NornAnyWorkflowDeclaration, type
|
|
1
|
+
import { type NornAnyWorkflowDeclaration, type NornDispose, type NornRunCheckpoint, type NornRunInfo, type NornRunResult, type NornRunStartOptions } from "@vimhead.dev/norn";
|
|
2
2
|
import { NornAgentResponseCollector } from "./agent-response-tool.ts";
|
|
3
3
|
import { type NornRunResumeRequest } from "./launch-request.ts";
|
|
4
4
|
import { type NornRegisteredWorkflow } from "./workflow-registry.ts";
|
|
@@ -13,19 +13,16 @@ export type NornEngineInput = {
|
|
|
13
13
|
export declare class NornEngine {
|
|
14
14
|
private readonly input;
|
|
15
15
|
private readonly registry;
|
|
16
|
-
private readonly registrarState;
|
|
17
|
-
private readonly disposersByPlugin;
|
|
18
16
|
private readonly responseCollector;
|
|
19
17
|
private readonly activeRuns;
|
|
20
18
|
constructor(input: NornEngineInput);
|
|
21
|
-
|
|
19
|
+
registerWorkflows(workflows: readonly NornAnyWorkflowDeclaration[]): NornDispose;
|
|
22
20
|
listWorkflows(): import("@vimhead.dev/norn").NornRegisteredWorkflowInfo[];
|
|
23
21
|
visibleWorkflowEntries(): NornRegisteredWorkflow[];
|
|
24
|
-
|
|
25
|
-
runWorkflow<TWorkflow extends NornAnyWorkflowDeclaration>(workflow: TWorkflow, params: unknown, options: NornRunStartOptions | undefined): Promise<NornRunResult>;
|
|
22
|
+
runWorkflow<TWorkflow extends NornAnyWorkflowDeclaration>(workflow: TWorkflow, args: unknown, options: NornRunStartOptions | undefined): Promise<NornRunResult>;
|
|
26
23
|
listRunCheckpoints(path: string): Promise<NornRunCheckpoint[]>;
|
|
27
24
|
rollbackRun(path: string, checkpointId: string): Promise<NornRunInfo>;
|
|
28
|
-
resumeWorkflow(path: string,
|
|
25
|
+
resumeWorkflow(path: string, args?: unknown): Promise<NornRunResult>;
|
|
29
26
|
resumeRequestedWorkflow(input: {
|
|
30
27
|
readonly runRoot: string;
|
|
31
28
|
readonly request: NornRunResumeRequest;
|
|
@@ -40,5 +37,4 @@ export declare class NornEngine {
|
|
|
40
37
|
private startActiveRun;
|
|
41
38
|
private finishActiveRun;
|
|
42
39
|
private failActiveRun;
|
|
43
|
-
private disposePlugin;
|
|
44
40
|
}
|