@rk0429/agentic-relay 20.1.1 → 21.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/application/event-log-sink.d.ts +13 -9
- package/dist/application/event-log-sink.js +29 -22
- package/dist/application/event-log-sink.js.map +1 -1
- package/dist/application/projection-service.js +3 -0
- package/dist/application/projection-service.js.map +1 -1
- package/dist/application/routine-catalog-service.d.ts +36 -0
- package/dist/application/routine-catalog-service.js +90 -0
- package/dist/application/routine-catalog-service.js.map +1 -0
- package/dist/application/routine-daemon-service.d.ts +57 -0
- package/dist/application/routine-daemon-service.js +242 -0
- package/dist/application/routine-daemon-service.js.map +1 -0
- package/dist/application/routine-event-log-sink.d.ts +42 -0
- package/dist/application/routine-event-log-sink.js +82 -0
- package/dist/application/routine-event-log-sink.js.map +1 -0
- package/dist/application/routine-run-service.d.ts +60 -0
- package/dist/application/routine-run-service.js +392 -0
- package/dist/application/routine-run-service.js.map +1 -0
- package/dist/application/routine-scheduler.d.ts +34 -0
- package/dist/application/routine-scheduler.js +82 -0
- package/dist/application/routine-scheduler.js.map +1 -0
- package/dist/application/routine-status-query-service.d.ts +58 -0
- package/dist/application/routine-status-query-service.js +161 -0
- package/dist/application/routine-status-query-service.js.map +1 -0
- package/dist/application/spawn-agents-service.js +32 -5
- package/dist/application/spawn-agents-service.js.map +1 -1
- package/dist/application/workflow-execution-service.d.ts +5 -1
- package/dist/application/workflow-execution-service.js +82 -23
- package/dist/application/workflow-execution-service.js.map +1 -1
- package/dist/bin/relay.d.ts +60 -1
- package/dist/bin/relay.js +551 -49
- package/dist/bin/relay.js.map +1 -1
- package/dist/core/types.d.ts +2 -2
- package/dist/domain/loop-execution.d.ts +1 -1
- package/dist/domain/loop-execution.js +8 -4
- package/dist/domain/loop-execution.js.map +1 -1
- package/dist/domain/routine-definition.d.ts +18 -0
- package/dist/domain/routine-definition.js +19 -0
- package/dist/domain/routine-definition.js.map +1 -0
- package/dist/domain/routine-execution-state.d.ts +35 -0
- package/dist/domain/routine-execution-state.js +97 -0
- package/dist/domain/routine-execution-state.js.map +1 -0
- package/dist/domain/routine-firing.d.ts +23 -0
- package/dist/domain/routine-firing.js +47 -0
- package/dist/domain/routine-firing.js.map +1 -0
- package/dist/domain/routine-loader.d.ts +9 -0
- package/dist/domain/routine-loader.js +129 -0
- package/dist/domain/routine-loader.js.map +1 -0
- package/dist/domain/routine-name.d.ts +6 -0
- package/dist/domain/routine-name.js +31 -0
- package/dist/domain/routine-name.js.map +1 -0
- package/dist/domain/routine-schedule.d.ts +28 -0
- package/dist/domain/routine-schedule.js +142 -0
- package/dist/domain/routine-schedule.js.map +1 -0
- package/dist/domain/trigger-config.d.ts +25 -0
- package/dist/domain/trigger-config.js +86 -0
- package/dist/domain/trigger-config.js.map +1 -0
- package/dist/domain/workflow-loader.d.ts +2 -0
- package/dist/domain/workflow-loader.js +12 -2
- package/dist/domain/workflow-loader.js.map +1 -1
- package/dist/domain/workflow-schema.d.ts +20 -1
- package/dist/domain/workflow-schema.js +43 -7
- package/dist/domain/workflow-schema.js.map +1 -1
- package/dist/infrastructure/command-execution-gateway.d.ts +21 -0
- package/dist/infrastructure/command-execution-gateway.js +24 -0
- package/dist/infrastructure/command-execution-gateway.js.map +1 -0
- package/dist/infrastructure/store/file-utils.d.ts +2 -0
- package/dist/infrastructure/store/file-utils.js +23 -0
- package/dist/infrastructure/store/file-utils.js.map +1 -0
- package/dist/infrastructure/store/relay-store.d.ts +1 -0
- package/dist/infrastructure/store/relay-store.js +6 -22
- package/dist/infrastructure/store/relay-store.js.map +1 -1
- package/dist/infrastructure/store/routine-execution-lease-repository.d.ts +31 -0
- package/dist/infrastructure/store/routine-execution-lease-repository.js +191 -0
- package/dist/infrastructure/store/routine-execution-lease-repository.js.map +1 -0
- package/dist/infrastructure/store/routine-runtime-projection-repository.d.ts +27 -0
- package/dist/infrastructure/store/routine-runtime-projection-repository.js +142 -0
- package/dist/infrastructure/store/routine-runtime-projection-repository.js.map +1 -0
- package/dist/infrastructure/store/routine-state-repository.d.ts +21 -0
- package/dist/infrastructure/store/routine-state-repository.js +139 -0
- package/dist/infrastructure/store/routine-state-repository.js.map +1 -0
- package/dist/interfaces/cli/loop-progress-renderer.js +3 -1
- package/dist/interfaces/cli/loop-progress-renderer.js.map +1 -1
- package/dist/interfaces/cli/relay-cli-args.d.ts +19 -2
- package/dist/interfaces/cli/relay-cli-args.js +184 -50
- package/dist/interfaces/cli/relay-cli-args.js.map +1 -1
- package/dist/interfaces/mcp/relay-mcp-server.js +2 -2
- package/dist/interfaces/mcp/relay-mcp-server.js.map +1 -1
- package/package.json +2 -1
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { mkdir, readdir, readFile, rm, } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { ValidationError } from "../../core/errors.js";
|
|
4
|
+
import { restoreRoutineRunId, } from "../../domain/routine-execution-state.js";
|
|
5
|
+
import { isMissingFileError, writeJsonAtomic } from "./file-utils.js";
|
|
6
|
+
export class RoutineRuntimeProjectionRepository {
|
|
7
|
+
store;
|
|
8
|
+
constructor(store) {
|
|
9
|
+
this.store = store;
|
|
10
|
+
}
|
|
11
|
+
async publish(name, projection) {
|
|
12
|
+
await this.ensureRoutinesDir();
|
|
13
|
+
await writeJsonAtomic(this.buildProjectionPath(name), serializeProjection(projection));
|
|
14
|
+
return this.buildProjectionPath(name);
|
|
15
|
+
}
|
|
16
|
+
async heartbeat(name, heartbeatAt = new Date()) {
|
|
17
|
+
const projection = await this.get(name);
|
|
18
|
+
if (projection === null) {
|
|
19
|
+
throw new ValidationError(`Runtime projection not found: ${name.value}`, {
|
|
20
|
+
recoveryHint: "Publish the runtime projection before updating its heartbeat.",
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
const updated = {
|
|
24
|
+
...projection,
|
|
25
|
+
lastHeartbeatAt: heartbeatAt.toISOString(),
|
|
26
|
+
};
|
|
27
|
+
await writeJsonAtomic(this.buildProjectionPath(name), serializeProjection(updated));
|
|
28
|
+
return updated;
|
|
29
|
+
}
|
|
30
|
+
async get(name) {
|
|
31
|
+
try {
|
|
32
|
+
const raw = await readFile(this.buildProjectionPath(name), "utf8");
|
|
33
|
+
return parseProjection(JSON.parse(raw));
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
if (isMissingFileError(error)) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
if (error instanceof SyntaxError) {
|
|
40
|
+
throw new ValidationError(`Invalid runtime projection JSON for "${name.value}": ${error.message}`, {
|
|
41
|
+
recoveryHint: "Repair or delete the corrupted runtime projection file.",
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async list() {
|
|
48
|
+
try {
|
|
49
|
+
const entries = await readdir(this.routinesDir(), { withFileTypes: true });
|
|
50
|
+
const filenames = entries
|
|
51
|
+
.filter((entry) => entry.isFile() && entry.name.endsWith(".runtime.json"))
|
|
52
|
+
.map((entry) => entry.name)
|
|
53
|
+
.sort();
|
|
54
|
+
const projections = await Promise.all(filenames.map(async (filename) => {
|
|
55
|
+
const raw = await readFile(path.join(this.routinesDir(), filename), "utf8");
|
|
56
|
+
return {
|
|
57
|
+
name: filename.replace(/\.runtime\.json$/, ""),
|
|
58
|
+
projection: parseProjection(JSON.parse(raw)),
|
|
59
|
+
};
|
|
60
|
+
}));
|
|
61
|
+
return projections;
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
if (isMissingFileError(error)) {
|
|
65
|
+
return [];
|
|
66
|
+
}
|
|
67
|
+
throw error;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async remove(name) {
|
|
71
|
+
await rm(this.buildProjectionPath(name), { force: true });
|
|
72
|
+
}
|
|
73
|
+
async ensureRoutinesDir() {
|
|
74
|
+
await mkdir(this.store.routinesDir(), { recursive: true });
|
|
75
|
+
}
|
|
76
|
+
routinesDir() {
|
|
77
|
+
return this.store.routinesDir();
|
|
78
|
+
}
|
|
79
|
+
buildProjectionPath(name) {
|
|
80
|
+
return path.join(this.routinesDir(), `${name.value}.runtime.json`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
function parseProjection(value) {
|
|
84
|
+
if (!isRecord(value)) {
|
|
85
|
+
throw new ValidationError("Invalid runtime projection: expected an object.", {
|
|
86
|
+
recoveryHint: "Repair or delete the corrupted runtime projection file.",
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
routineRunId: restoreRoutineRunId(asNonEmptyString(value.routineRunId, "routineRunId")),
|
|
91
|
+
source: asSource(value.source),
|
|
92
|
+
startedAt: asIsoTimestamp(value.startedAt, "startedAt"),
|
|
93
|
+
lastHeartbeatAt: asIsoTimestamp(value.lastHeartbeatAt, "lastHeartbeatAt"),
|
|
94
|
+
pid: asPositiveInteger(value.pid, "pid"),
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function serializeProjection(projection) {
|
|
98
|
+
return {
|
|
99
|
+
routineRunId: projection.routineRunId,
|
|
100
|
+
source: projection.source,
|
|
101
|
+
startedAt: projection.startedAt,
|
|
102
|
+
lastHeartbeatAt: projection.lastHeartbeatAt,
|
|
103
|
+
pid: projection.pid,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function asSource(value) {
|
|
107
|
+
if (value === "scheduled" || value === "manual" || value === "catch_up") {
|
|
108
|
+
return value;
|
|
109
|
+
}
|
|
110
|
+
throw new ValidationError(`Invalid source: ${String(value)}`, {
|
|
111
|
+
recoveryHint: "source must be scheduled, manual, or catch_up.",
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
function asPositiveInteger(value, field) {
|
|
115
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
|
|
116
|
+
throw new ValidationError(`Invalid ${field}: ${String(value)}`, {
|
|
117
|
+
recoveryHint: `${field} must be a positive integer.`,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
return value;
|
|
121
|
+
}
|
|
122
|
+
function asNonEmptyString(value, field) {
|
|
123
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
124
|
+
throw new ValidationError(`Invalid ${field}: ${String(value)}`, {
|
|
125
|
+
recoveryHint: `${field} must be a non-empty string.`,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
return value;
|
|
129
|
+
}
|
|
130
|
+
function asIsoTimestamp(value, field) {
|
|
131
|
+
const timestamp = asNonEmptyString(value, field);
|
|
132
|
+
if (Number.isNaN(new Date(timestamp).getTime())) {
|
|
133
|
+
throw new ValidationError(`Invalid ${field}: ${timestamp}`, {
|
|
134
|
+
recoveryHint: `${field} must be an ISO 8601 timestamp.`,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
return timestamp;
|
|
138
|
+
}
|
|
139
|
+
function isRecord(value) {
|
|
140
|
+
return typeof value === "object" && value !== null;
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=routine-runtime-projection-repository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routine-runtime-projection-repository.js","sourceRoot":"","sources":["../../../src/infrastructure/store/routine-runtime-projection-repository.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { RoutineExecutionState } from "../../domain/routine-execution-state.js";
|
|
2
|
+
import { RoutineName } from "../../domain/routine-name.js";
|
|
3
|
+
import { RelayStore } from "./relay-store.js";
|
|
4
|
+
export interface PersistedRoutineState {
|
|
5
|
+
workflowPath: string;
|
|
6
|
+
state: RoutineExecutionState;
|
|
7
|
+
}
|
|
8
|
+
export interface NamedPersistedRoutineState extends PersistedRoutineState {
|
|
9
|
+
name: RoutineName;
|
|
10
|
+
}
|
|
11
|
+
export declare class RoutineStateRepository {
|
|
12
|
+
private readonly store;
|
|
13
|
+
constructor(store: RelayStore);
|
|
14
|
+
save(name: RoutineName, workflowPath: string, state: RoutineExecutionState): Promise<string>;
|
|
15
|
+
load(name: RoutineName): Promise<PersistedRoutineState | null>;
|
|
16
|
+
delete(name: RoutineName): Promise<void>;
|
|
17
|
+
list(): Promise<NamedPersistedRoutineState[]>;
|
|
18
|
+
private ensureRoutinesDir;
|
|
19
|
+
private routinesDir;
|
|
20
|
+
private buildStatePath;
|
|
21
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { mkdir, readFile, rm } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { ValidationError } from "../../core/errors.js";
|
|
4
|
+
import { RoutineExecutionState, } from "../../domain/routine-execution-state.js";
|
|
5
|
+
import { RoutineName } from "../../domain/routine-name.js";
|
|
6
|
+
import { isMissingFileError, writeJsonAtomic } from "./file-utils.js";
|
|
7
|
+
export class RoutineStateRepository {
|
|
8
|
+
store;
|
|
9
|
+
constructor(store) {
|
|
10
|
+
this.store = store;
|
|
11
|
+
}
|
|
12
|
+
async save(name, workflowPath, state) {
|
|
13
|
+
assertNonEmptyString(workflowPath, "workflowPath");
|
|
14
|
+
await this.ensureRoutinesDir();
|
|
15
|
+
const targetPath = this.buildStatePath(name);
|
|
16
|
+
await writeJsonAtomic(targetPath, {
|
|
17
|
+
routineName: name.value,
|
|
18
|
+
workflowPath,
|
|
19
|
+
...state.toJSON(),
|
|
20
|
+
});
|
|
21
|
+
return targetPath;
|
|
22
|
+
}
|
|
23
|
+
async load(name) {
|
|
24
|
+
const targetPath = this.buildStatePath(name);
|
|
25
|
+
try {
|
|
26
|
+
const raw = await readFile(targetPath, "utf8");
|
|
27
|
+
return parsePersistedRoutineState(JSON.parse(raw), name);
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
if (isMissingFileError(error)) {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
if (error instanceof SyntaxError) {
|
|
34
|
+
throw new ValidationError(`Invalid routine state JSON for "${name.value}": ${error.message}`, {
|
|
35
|
+
recoveryHint: "Repair or delete the corrupted routine state file.",
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
async delete(name) {
|
|
42
|
+
await rm(this.buildStatePath(name), { force: true });
|
|
43
|
+
}
|
|
44
|
+
async list() {
|
|
45
|
+
try {
|
|
46
|
+
const entries = await import("node:fs/promises").then(({ readdir }) => readdir(this.store.routinesDir(), { withFileTypes: true }));
|
|
47
|
+
const files = entries
|
|
48
|
+
.filter((entry) => entry.isFile() && entry.name.endsWith(".state.json"))
|
|
49
|
+
.map((entry) => entry.name)
|
|
50
|
+
.sort();
|
|
51
|
+
const states = await Promise.all(files.map(async (filename) => {
|
|
52
|
+
const raw = await readFile(path.join(this.store.routinesDir(), filename), "utf8");
|
|
53
|
+
const name = new RoutineName(filename.replace(/\.state\.json$/, ""));
|
|
54
|
+
const persisted = parsePersistedRoutineState(JSON.parse(raw), name);
|
|
55
|
+
return {
|
|
56
|
+
name,
|
|
57
|
+
...persisted,
|
|
58
|
+
};
|
|
59
|
+
}));
|
|
60
|
+
return states;
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
if (isMissingFileError(error)) {
|
|
64
|
+
return [];
|
|
65
|
+
}
|
|
66
|
+
throw error;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async ensureRoutinesDir() {
|
|
70
|
+
await mkdir(this.store.routinesDir(), { recursive: true });
|
|
71
|
+
}
|
|
72
|
+
routinesDir() {
|
|
73
|
+
return this.store.routinesDir();
|
|
74
|
+
}
|
|
75
|
+
buildStatePath(name) {
|
|
76
|
+
return path.join(this.routinesDir(), `${name.value}.state.json`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function parsePersistedRoutineState(value, expectedName) {
|
|
80
|
+
if (!isRecord(value)) {
|
|
81
|
+
throw new ValidationError("Invalid routine state file: expected an object.", {
|
|
82
|
+
recoveryHint: "Repair or delete the corrupted routine state file.",
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
const routineName = asNonEmptyString(value.routineName, "routineName");
|
|
86
|
+
assertNonEmptyString(value.workflowPath, "workflowPath");
|
|
87
|
+
if (expectedName && routineName !== expectedName.value) {
|
|
88
|
+
throw new ValidationError(`Invalid routineName: ${routineName}`, {
|
|
89
|
+
recoveryHint: `routineName must match the state filename (${expectedName.value}).`,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
workflowPath: value.workflowPath,
|
|
94
|
+
state: RoutineExecutionState.fromJSON({
|
|
95
|
+
lastExecutedAt: asNullableString(value.lastExecutedAt, "lastExecutedAt"),
|
|
96
|
+
lastScheduledAt: asNullableString(value.lastScheduledAt, "lastScheduledAt"),
|
|
97
|
+
nextScheduledAt: asNullableString(value.nextScheduledAt, "nextScheduledAt"),
|
|
98
|
+
lastStatus: asExecutionStatus(value.lastStatus),
|
|
99
|
+
}),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function asExecutionStatus(value) {
|
|
103
|
+
if (value === null) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
if (value === "completed" ||
|
|
107
|
+
value === "failed" ||
|
|
108
|
+
value === "skipped") {
|
|
109
|
+
return value;
|
|
110
|
+
}
|
|
111
|
+
throw new ValidationError(`Invalid lastStatus: ${String(value)}`, {
|
|
112
|
+
recoveryHint: "lastStatus must be completed, failed, skipped, or null.",
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
function asNullableString(value, field) {
|
|
116
|
+
if (value === null) {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
return asNonEmptyString(value, field);
|
|
120
|
+
}
|
|
121
|
+
function asNonEmptyString(value, field) {
|
|
122
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
123
|
+
throw new ValidationError(`Invalid ${field}: ${String(value)}`, {
|
|
124
|
+
recoveryHint: `${field} must be a non-empty string.`,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
return value;
|
|
128
|
+
}
|
|
129
|
+
function assertNonEmptyString(value, field) {
|
|
130
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
131
|
+
throw new ValidationError(`Invalid ${field}: ${String(value)}`, {
|
|
132
|
+
recoveryHint: `${field} must be a non-empty string.`,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
function isRecord(value) {
|
|
137
|
+
return typeof value === "object" && value !== null;
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=routine-state-repository.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routine-state-repository.js","sourceRoot":"","sources":["../../../src/infrastructure/store/routine-state-repository.ts"],"names":[],"mappings":"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"}
|
|
@@ -80,7 +80,9 @@ function renderStepLine(step, state, now) {
|
|
|
80
80
|
const icon = STATUS_ICONS[status];
|
|
81
81
|
const meta = step.kind === "workflow"
|
|
82
82
|
? "sub-workflow"
|
|
83
|
-
:
|
|
83
|
+
: step.kind === "command"
|
|
84
|
+
? "command"
|
|
85
|
+
: [step.backend, step.role].filter(Boolean).join(", ");
|
|
84
86
|
const durationText = status === "running"
|
|
85
87
|
? state.activeStepStartedAt
|
|
86
88
|
? formatDurationMs(Math.max(0, now.getTime() - new Date(state.activeStepStartedAt).getTime()))
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"loop-progress-renderer.js","sourceRoot":"","sources":["../../../src/interfaces/cli/loop-progress-renderer.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"loop-progress-renderer.js","sourceRoot":"","sources":["../../../src/interfaces/cli/loop-progress-renderer.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type BackendType } from "../../core/types.js";
|
|
2
2
|
export type RelayCliCommand = {
|
|
3
3
|
kind: "help";
|
|
4
|
-
topic?: "core-setup" | "core-update" | "core-remove";
|
|
4
|
+
topic?: "core-setup" | "core-update" | "core-remove" | "routine";
|
|
5
5
|
} | {
|
|
6
6
|
kind: "version";
|
|
7
7
|
} | {
|
|
@@ -32,13 +32,30 @@ export type RelayCliCommand = {
|
|
|
32
32
|
target?: "code" | "code-insiders" | "cursor";
|
|
33
33
|
uninstall?: boolean;
|
|
34
34
|
} | {
|
|
35
|
-
kind: "
|
|
35
|
+
kind: "routine-run";
|
|
36
36
|
workflowPath: string;
|
|
37
|
+
configDir?: string;
|
|
37
38
|
resumeStateFile?: string;
|
|
38
39
|
forceResume?: boolean;
|
|
39
40
|
allowCommandRun?: boolean;
|
|
40
41
|
dryRun?: boolean;
|
|
41
42
|
vars?: Record<string, string>;
|
|
43
|
+
} | {
|
|
44
|
+
kind: "routine-start";
|
|
45
|
+
configDir?: string;
|
|
46
|
+
daemon?: boolean;
|
|
47
|
+
allowCommandRun?: boolean;
|
|
48
|
+
} | {
|
|
49
|
+
kind: "routine-stop";
|
|
50
|
+
} | {
|
|
51
|
+
kind: "routine-list";
|
|
52
|
+
configDir?: string;
|
|
53
|
+
format?: "table" | "tsv" | "json";
|
|
54
|
+
} | {
|
|
55
|
+
kind: "routine-status";
|
|
56
|
+
name?: string;
|
|
57
|
+
configDir?: string;
|
|
58
|
+
limit?: number;
|
|
42
59
|
} | {
|
|
43
60
|
kind: "relay";
|
|
44
61
|
backend?: BackendType;
|
|
@@ -36,67 +36,198 @@ export function parseRelayCliArgs(argv) {
|
|
|
36
36
|
...(uninstall ? { uninstall } : {}),
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
|
-
if (argv[0] === "
|
|
40
|
-
const
|
|
41
|
-
if (
|
|
42
|
-
return { kind: "help" };
|
|
43
|
-
}
|
|
44
|
-
if (!workflowPath) {
|
|
45
|
-
throw new ValidationError("Usage: relay loop <workflow.yaml> [--var key=value ...] [--dry-run] [--resume-state <state_file>] [--force-resume] [--allow-command-run]");
|
|
39
|
+
if (argv[0] === "routine") {
|
|
40
|
+
const subcommand = argv[1];
|
|
41
|
+
if (!subcommand || subcommand === "-h" || subcommand === "--help") {
|
|
42
|
+
return { kind: "help", topic: "routine" };
|
|
46
43
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
let dryRun = false;
|
|
52
|
-
const vars = {};
|
|
53
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
54
|
-
const current = args[index];
|
|
55
|
-
if (current === "-h" || current === "--help") {
|
|
56
|
-
return { kind: "help" };
|
|
44
|
+
if (subcommand === "run") {
|
|
45
|
+
const workflowPath = argv[2];
|
|
46
|
+
if (workflowPath === "-h" || workflowPath === "--help") {
|
|
47
|
+
return { kind: "help", topic: "routine" };
|
|
57
48
|
}
|
|
58
|
-
if (
|
|
59
|
-
|
|
60
|
-
continue;
|
|
49
|
+
if (!workflowPath) {
|
|
50
|
+
throw new ValidationError("Usage: relay routine run <workflow.yaml> [--config <dir>] [--var key=value ...] [--dry-run] [--resume-state <state_file>] [--force-resume] [--allow-command-run]");
|
|
61
51
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
52
|
+
const args = argv.slice(3);
|
|
53
|
+
let configDir;
|
|
54
|
+
let resumeStateFile;
|
|
55
|
+
let forceResume = false;
|
|
56
|
+
let allowCommandRun = false;
|
|
57
|
+
let dryRun = false;
|
|
58
|
+
const vars = {};
|
|
59
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
60
|
+
const current = args[index];
|
|
61
|
+
if (current === "-h" || current === "--help") {
|
|
62
|
+
return { kind: "help", topic: "routine" };
|
|
63
|
+
}
|
|
64
|
+
if (current === "--config") {
|
|
65
|
+
configDir = readOptionValue(args, ++index, current);
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (current === "--resume-state") {
|
|
69
|
+
resumeStateFile = readOptionValue(args, ++index, current);
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (current === "--force-resume") {
|
|
73
|
+
forceResume = true;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (current === "--allow-command-run") {
|
|
77
|
+
allowCommandRun = true;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (current === "--dry-run") {
|
|
81
|
+
dryRun = true;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (current === "--var") {
|
|
85
|
+
const pair = readOptionValue(args, ++index, current);
|
|
86
|
+
const eqIndex = pair.indexOf("=");
|
|
87
|
+
if (eqIndex < 1) {
|
|
88
|
+
throw new ValidationError(`--var requires key=value format, got: "${pair}"`);
|
|
89
|
+
}
|
|
90
|
+
vars[pair.slice(0, eqIndex)] = pair.slice(eqIndex + 1);
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (current?.startsWith("-")) {
|
|
94
|
+
throw new ValidationError(`Unknown option: ${current}`);
|
|
95
|
+
}
|
|
96
|
+
throw new ValidationError(`Unknown command or argument: ${current}`);
|
|
65
97
|
}
|
|
66
|
-
if (
|
|
67
|
-
|
|
68
|
-
continue;
|
|
98
|
+
if (forceResume && !resumeStateFile) {
|
|
99
|
+
throw new ValidationError("--force-resume requires --resume-state <state_file>.");
|
|
69
100
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
101
|
+
return {
|
|
102
|
+
kind: "routine-run",
|
|
103
|
+
workflowPath,
|
|
104
|
+
...(configDir ? { configDir } : {}),
|
|
105
|
+
...(resumeStateFile ? { resumeStateFile } : {}),
|
|
106
|
+
...(forceResume ? { forceResume } : {}),
|
|
107
|
+
...(allowCommandRun ? { allowCommandRun } : {}),
|
|
108
|
+
...(dryRun ? { dryRun } : {}),
|
|
109
|
+
...(Object.keys(vars).length > 0 ? { vars } : {}),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
if (subcommand === "start") {
|
|
113
|
+
const args = argv.slice(2);
|
|
114
|
+
let configDir;
|
|
115
|
+
let daemon = false;
|
|
116
|
+
let allowCommandRun = false;
|
|
117
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
118
|
+
const current = args[index];
|
|
119
|
+
if (current === "-h" || current === "--help") {
|
|
120
|
+
return { kind: "help", topic: "routine" };
|
|
121
|
+
}
|
|
122
|
+
if (current === "--config") {
|
|
123
|
+
configDir = readOptionValue(args, ++index, current);
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
if (current === "--daemon") {
|
|
127
|
+
daemon = true;
|
|
128
|
+
continue;
|
|
129
|
+
}
|
|
130
|
+
if (current === "--allow-command-run") {
|
|
131
|
+
allowCommandRun = true;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (current?.startsWith("-")) {
|
|
135
|
+
throw new ValidationError(`Unknown option: ${current}`);
|
|
136
|
+
}
|
|
137
|
+
throw new ValidationError(`Unknown command or argument: ${current}`);
|
|
73
138
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
139
|
+
return {
|
|
140
|
+
kind: "routine-start",
|
|
141
|
+
...(configDir ? { configDir } : {}),
|
|
142
|
+
...(daemon ? { daemon } : {}),
|
|
143
|
+
...(allowCommandRun ? { allowCommandRun } : {}),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
if (subcommand === "stop") {
|
|
147
|
+
const args = argv.slice(2);
|
|
148
|
+
for (const current of args) {
|
|
149
|
+
if (current === "-h" || current === "--help") {
|
|
150
|
+
return { kind: "help", topic: "routine" };
|
|
79
151
|
}
|
|
80
|
-
|
|
81
|
-
|
|
152
|
+
if (current?.startsWith("-")) {
|
|
153
|
+
throw new ValidationError(`Unknown option: ${current}`);
|
|
154
|
+
}
|
|
155
|
+
throw new ValidationError(`Unknown command or argument: ${current}`);
|
|
82
156
|
}
|
|
83
|
-
|
|
84
|
-
|
|
157
|
+
return { kind: "routine-stop" };
|
|
158
|
+
}
|
|
159
|
+
if (subcommand === "list") {
|
|
160
|
+
const args = argv.slice(2);
|
|
161
|
+
let configDir;
|
|
162
|
+
let format;
|
|
163
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
164
|
+
const current = args[index];
|
|
165
|
+
if (current === "-h" || current === "--help") {
|
|
166
|
+
return { kind: "help", topic: "routine" };
|
|
167
|
+
}
|
|
168
|
+
if (current === "--config") {
|
|
169
|
+
configDir = readOptionValue(args, ++index, current);
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
if (current === "--format") {
|
|
173
|
+
const value = readOptionValue(args, ++index, current);
|
|
174
|
+
if (!isRoutineListFormat(value)) {
|
|
175
|
+
throw new ValidationError(`--format must be one of: table, tsv, json. Got: "${value}"`);
|
|
176
|
+
}
|
|
177
|
+
format = value;
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
if (current?.startsWith("-")) {
|
|
181
|
+
throw new ValidationError(`Unknown option: ${current}`);
|
|
182
|
+
}
|
|
183
|
+
throw new ValidationError(`Unknown command or argument: ${current}`);
|
|
85
184
|
}
|
|
86
|
-
|
|
185
|
+
return {
|
|
186
|
+
kind: "routine-list",
|
|
187
|
+
...(configDir ? { configDir } : {}),
|
|
188
|
+
...(format ? { format } : {}),
|
|
189
|
+
};
|
|
87
190
|
}
|
|
88
|
-
if (
|
|
89
|
-
|
|
191
|
+
if (subcommand === "status") {
|
|
192
|
+
const args = argv.slice(2);
|
|
193
|
+
let name;
|
|
194
|
+
let configDir;
|
|
195
|
+
let limit;
|
|
196
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
197
|
+
const current = args[index];
|
|
198
|
+
if (current === "-h" || current === "--help") {
|
|
199
|
+
return { kind: "help", topic: "routine" };
|
|
200
|
+
}
|
|
201
|
+
if (current === "--config") {
|
|
202
|
+
configDir = readOptionValue(args, ++index, current);
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
if (current === "--limit") {
|
|
206
|
+
const raw = readOptionValue(args, ++index, current);
|
|
207
|
+
const parsed = Number.parseInt(raw, 10);
|
|
208
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
209
|
+
throw new ValidationError(`--limit must be a positive integer. Got: "${raw}"`);
|
|
210
|
+
}
|
|
211
|
+
limit = parsed;
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
if (current?.startsWith("-")) {
|
|
215
|
+
throw new ValidationError(`Unknown option: ${current}`);
|
|
216
|
+
}
|
|
217
|
+
if (!name) {
|
|
218
|
+
name = current;
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
throw new ValidationError(`Unknown command or argument: ${current}`);
|
|
222
|
+
}
|
|
223
|
+
return {
|
|
224
|
+
kind: "routine-status",
|
|
225
|
+
...(name ? { name } : {}),
|
|
226
|
+
...(configDir ? { configDir } : {}),
|
|
227
|
+
...(limit !== undefined ? { limit } : {}),
|
|
228
|
+
};
|
|
90
229
|
}
|
|
91
|
-
|
|
92
|
-
kind: "loop",
|
|
93
|
-
workflowPath,
|
|
94
|
-
...(resumeStateFile ? { resumeStateFile } : {}),
|
|
95
|
-
...(forceResume ? { forceResume } : {}),
|
|
96
|
-
...(allowCommandRun ? { allowCommandRun } : {}),
|
|
97
|
-
...(dryRun ? { dryRun } : {}),
|
|
98
|
-
...(Object.keys(vars).length > 0 ? { vars } : {}),
|
|
99
|
-
};
|
|
230
|
+
throw new ValidationError(`Unknown routine subcommand: ${subcommand}. Expected one of: run, start, stop, list, status.`);
|
|
100
231
|
}
|
|
101
232
|
if (argv[0] === "core") {
|
|
102
233
|
const subcommand = argv[1];
|
|
@@ -247,6 +378,9 @@ function isBackendType(value) {
|
|
|
247
378
|
function isVscodeTarget(value) {
|
|
248
379
|
return value === "code" || value === "code-insiders" || value === "cursor";
|
|
249
380
|
}
|
|
381
|
+
function isRoutineListFormat(value) {
|
|
382
|
+
return value === "table" || value === "tsv" || value === "json";
|
|
383
|
+
}
|
|
250
384
|
function readOptionValue(args, index, option) {
|
|
251
385
|
const value = args[index];
|
|
252
386
|
if (!value) {
|