@rk0429/agentic-relay 22.3.0 → 22.4.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/agent-management-service.d.ts +47 -0
- package/dist/application/agent-management-service.js +106 -0
- package/dist/application/agent-management-service.js.map +1 -0
- package/dist/application/projection-service.js +3 -0
- package/dist/application/projection-service.js.map +1 -1
- package/dist/application/task-service.js +9 -0
- package/dist/application/task-service.js.map +1 -1
- package/dist/application/workflow-execution-service.d.ts +0 -1
- package/dist/application/workflow-execution-service.js +16 -45
- package/dist/application/workflow-execution-service.js.map +1 -1
- package/dist/bin/relay.d.ts +27 -2
- package/dist/bin/relay.js +358 -0
- package/dist/bin/relay.js.map +1 -1
- package/dist/core/types.d.ts +10 -1
- package/dist/interfaces/cli/relay-cli-args.d.ts +18 -1
- package/dist/interfaces/cli/relay-cli-args.js +144 -0
- package/dist/interfaces/cli/relay-cli-args.js.map +1 -1
- package/dist/interfaces/mcp/relay-mcp-server.d.ts +2 -0
- package/dist/interfaces/mcp/relay-mcp-server.js +179 -3
- package/dist/interfaces/mcp/relay-mcp-server.js.map +1 -1
- package/dist/interfaces/vscode/extension.d.ts +6 -0
- package/dist/interfaces/vscode/extension.js +145 -0
- package/dist/interfaces/vscode/extension.js.map +1 -1
- package/dist/interfaces/vscode/tree-data-providers.d.ts +4 -0
- package/dist/interfaces/vscode/tree-data-providers.js +2 -0
- package/dist/interfaces/vscode/tree-data-providers.js.map +1 -1
- package/dist/interfaces/vscode/view-model.js +25 -12
- package/dist/interfaces/vscode/view-model.js.map +1 -1
- package/package.json +52 -2
package/dist/bin/relay.js
CHANGED
|
@@ -7,6 +7,7 @@ import path from "node:path";
|
|
|
7
7
|
import { createInterface } from "node:readline";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
9
|
import { promisify } from "node:util";
|
|
10
|
+
import { AgentManagementService } from "../application/agent-management-service.js";
|
|
10
11
|
import { ChatDaemonService, } from "../application/chat/chat-daemon-service.js";
|
|
11
12
|
import { ChatInboundHandler } from "../application/chat/chat-inbound-handler.js";
|
|
12
13
|
import { ChatOutboundHandler } from "../application/chat/chat-outbound-handler.js";
|
|
@@ -306,6 +307,33 @@ async function main() {
|
|
|
306
307
|
}
|
|
307
308
|
return;
|
|
308
309
|
}
|
|
310
|
+
if (command.kind === "agent-list" ||
|
|
311
|
+
command.kind === "agent-show" ||
|
|
312
|
+
command.kind === "agent-stop" ||
|
|
313
|
+
command.kind === "agent-log") {
|
|
314
|
+
const agentService = new AgentManagementService({ store });
|
|
315
|
+
const exitCode = await handleAgentCommand(command, {
|
|
316
|
+
service: agentService,
|
|
317
|
+
stdout: process.stdout,
|
|
318
|
+
stderr: process.stderr,
|
|
319
|
+
now: () => new Date(),
|
|
320
|
+
fileExists: async (filePath) => {
|
|
321
|
+
try {
|
|
322
|
+
await access(filePath);
|
|
323
|
+
return true;
|
|
324
|
+
}
|
|
325
|
+
catch {
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
328
|
+
},
|
|
329
|
+
readTextFile: (filePath) => readFile(filePath, "utf8"),
|
|
330
|
+
readFileStream: createFollowReader(),
|
|
331
|
+
});
|
|
332
|
+
if (exitCode !== undefined) {
|
|
333
|
+
process.exitCode = exitCode;
|
|
334
|
+
}
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
309
337
|
if (command.kind === "chat-setup" ||
|
|
310
338
|
command.kind === "chat-start" ||
|
|
311
339
|
command.kind === "chat-stop" ||
|
|
@@ -351,6 +379,7 @@ async function main() {
|
|
|
351
379
|
const server = createRelayMcpServer({
|
|
352
380
|
service,
|
|
353
381
|
taskService,
|
|
382
|
+
agentManagementService: new AgentManagementService({ store }),
|
|
354
383
|
store,
|
|
355
384
|
routineStatusQueryService: buildRoutineStatusQueryService({
|
|
356
385
|
store,
|
|
@@ -633,6 +662,72 @@ export async function handleRoutineCommand(command, dependencies) {
|
|
|
633
662
|
}
|
|
634
663
|
}
|
|
635
664
|
}
|
|
665
|
+
export async function handleAgentCommand(command, dependencies) {
|
|
666
|
+
switch (command.kind) {
|
|
667
|
+
case "agent-list": {
|
|
668
|
+
const agents = await dependencies.service.listActiveAgents({
|
|
669
|
+
includeStale: command.includeStale,
|
|
670
|
+
});
|
|
671
|
+
if (command.format === "json") {
|
|
672
|
+
dependencies.stdout.write(`${JSON.stringify(agents, null, 2)}\n`);
|
|
673
|
+
return 0;
|
|
674
|
+
}
|
|
675
|
+
dependencies.stdout.write(renderAgentTable(agents));
|
|
676
|
+
return 0;
|
|
677
|
+
}
|
|
678
|
+
case "agent-show": {
|
|
679
|
+
const detail = await dependencies.service.getAgentDetail({
|
|
680
|
+
relaySessionId: command.relaySessionId,
|
|
681
|
+
});
|
|
682
|
+
if (!detail) {
|
|
683
|
+
writeLine(dependencies.stderr, `No session found for relay session id: ${command.relaySessionId}`);
|
|
684
|
+
return 1;
|
|
685
|
+
}
|
|
686
|
+
dependencies.stdout.write(renderAgentDetail(detail));
|
|
687
|
+
return 0;
|
|
688
|
+
}
|
|
689
|
+
case "agent-stop": {
|
|
690
|
+
const result = await dependencies.service.stopAgent({
|
|
691
|
+
relaySessionId: command.relaySessionId,
|
|
692
|
+
actorId: dependencies.actorId ?? "local-cli-operator",
|
|
693
|
+
gracePeriodMs: command.gracePeriodMs,
|
|
694
|
+
});
|
|
695
|
+
if (command.format === "json") {
|
|
696
|
+
dependencies.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
|
|
697
|
+
return 0;
|
|
698
|
+
}
|
|
699
|
+
writeLine(dependencies.stdout, renderStopAgentResult(result));
|
|
700
|
+
return 0;
|
|
701
|
+
}
|
|
702
|
+
case "agent-log": {
|
|
703
|
+
const logPath = dependencies.service.readSessionLogPath({
|
|
704
|
+
relaySessionId: command.relaySessionId,
|
|
705
|
+
now: dependencies.now(),
|
|
706
|
+
});
|
|
707
|
+
const exists = await dependencies.fileExists(logPath);
|
|
708
|
+
if (!exists) {
|
|
709
|
+
writeLine(dependencies.stderr, `No log file found for today at ${logPath}.`);
|
|
710
|
+
return 1;
|
|
711
|
+
}
|
|
712
|
+
const raw = await dependencies.readTextFile(logPath);
|
|
713
|
+
for (const line of filterAgentLogLines(raw, command.relaySessionId)) {
|
|
714
|
+
writeLine(dependencies.stdout, line);
|
|
715
|
+
}
|
|
716
|
+
if (!command.follow) {
|
|
717
|
+
return 0;
|
|
718
|
+
}
|
|
719
|
+
if (!dependencies.readFileStream) {
|
|
720
|
+
throw new ValidationError("Agent log follow is unavailable in this environment.");
|
|
721
|
+
}
|
|
722
|
+
await dependencies.readFileStream(logPath, {
|
|
723
|
+
follow: true,
|
|
724
|
+
sessionId: command.relaySessionId,
|
|
725
|
+
onLine: (line) => writeLine(dependencies.stdout, line),
|
|
726
|
+
});
|
|
727
|
+
return undefined;
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
636
731
|
async function suppressLoopStateWarnings(fn) {
|
|
637
732
|
const origWarn = console.warn;
|
|
638
733
|
console.warn = (...args) => {
|
|
@@ -655,6 +750,9 @@ export function renderHelpText(topic, scriptName = path.basename(fileURLToPath(i
|
|
|
655
750
|
if (isRoutineHelpTopic(topic)) {
|
|
656
751
|
return renderRoutineHelp(topic, scriptName);
|
|
657
752
|
}
|
|
753
|
+
if (isAgentHelpTopic(topic)) {
|
|
754
|
+
return renderAgentHelp(topic, scriptName);
|
|
755
|
+
}
|
|
658
756
|
if (isChatHelpTopic(topic)) {
|
|
659
757
|
return renderChatHelp(topic, scriptName);
|
|
660
758
|
}
|
|
@@ -669,6 +767,10 @@ export function renderHelpText(topic, scriptName = path.basename(fileURLToPath(i
|
|
|
669
767
|
` ${scriptName} routine cleanup [--config <dir>] [--dry-run]`,
|
|
670
768
|
` ${scriptName} routine list [--config <dir>] [--format <table|tsv|json>]`,
|
|
671
769
|
` ${scriptName} routine status [name] [--config <dir>] [--format <table|tsv|json>] [--limit <n>] [--run-id <uuid>]`,
|
|
770
|
+
` ${scriptName} agent list [--include-stale] [--format <table|json>]`,
|
|
771
|
+
` ${scriptName} agent show <session-id>`,
|
|
772
|
+
` ${scriptName} agent stop <session-id> [--grace-ms <n>] [--json]`,
|
|
773
|
+
` ${scriptName} agent log <session-id> [--follow]`,
|
|
672
774
|
` ${scriptName} chat setup (--slack | --discord) [--tokens-only]`,
|
|
673
775
|
` ${scriptName} chat start [--foreground]`,
|
|
674
776
|
` ${scriptName} chat stop`,
|
|
@@ -687,6 +789,10 @@ export function renderHelpText(topic, scriptName = path.basename(fileURLToPath(i
|
|
|
687
789
|
` ${commandName} routine cleanup [options] Remove stale routine metadata`,
|
|
688
790
|
` ${commandName} routine list [options] List known routines`,
|
|
689
791
|
` ${commandName} routine status [options] Show routine status details`,
|
|
792
|
+
` ${commandName} agent list [options] List active agent processes`,
|
|
793
|
+
` ${commandName} agent show [options] Show one agent session`,
|
|
794
|
+
` ${commandName} agent stop [options] Stop one running agent`,
|
|
795
|
+
` ${commandName} agent log [options] Show session logs for one agent`,
|
|
690
796
|
` ${commandName} chat setup [options] Configure Slack or Discord integration`,
|
|
691
797
|
` ${commandName} chat start [options] Start the chat daemon`,
|
|
692
798
|
` ${commandName} chat stop Stop the chat daemon`,
|
|
@@ -746,9 +852,71 @@ function renderCoreSubcommandHelp(topic, scriptName) {
|
|
|
746
852
|
function isRoutineHelpTopic(topic) {
|
|
747
853
|
return topic.startsWith("routine");
|
|
748
854
|
}
|
|
855
|
+
function isAgentHelpTopic(topic) {
|
|
856
|
+
return topic.startsWith("agent");
|
|
857
|
+
}
|
|
749
858
|
function isChatHelpTopic(topic) {
|
|
750
859
|
return topic.startsWith("chat");
|
|
751
860
|
}
|
|
861
|
+
function renderAgentHelp(topic, scriptName) {
|
|
862
|
+
if (topic === "agent") {
|
|
863
|
+
return renderAgentAggregateHelp(scriptName);
|
|
864
|
+
}
|
|
865
|
+
const commandName = path.parse(scriptName).name;
|
|
866
|
+
const allSubcommandsReference = `See '${commandName} agent --help' for all subcommands.`;
|
|
867
|
+
switch (topic) {
|
|
868
|
+
case "agent-list":
|
|
869
|
+
return [
|
|
870
|
+
`Usage: ${scriptName} agent list [options]`,
|
|
871
|
+
"",
|
|
872
|
+
"List active agent processes.",
|
|
873
|
+
"",
|
|
874
|
+
"Options:",
|
|
875
|
+
" --include-stale Include stale agent sessions",
|
|
876
|
+
" --format <fmt> Output format (table, json)",
|
|
877
|
+
" --json Alias for --format json",
|
|
878
|
+
" -h, --help Show this help",
|
|
879
|
+
"",
|
|
880
|
+
allSubcommandsReference,
|
|
881
|
+
].join("\n");
|
|
882
|
+
case "agent-show":
|
|
883
|
+
return [
|
|
884
|
+
`Usage: ${scriptName} agent show <session-id>`,
|
|
885
|
+
"",
|
|
886
|
+
"Show one agent session and its related task.",
|
|
887
|
+
"",
|
|
888
|
+
"Options:",
|
|
889
|
+
" -h, --help Show this help",
|
|
890
|
+
"",
|
|
891
|
+
allSubcommandsReference,
|
|
892
|
+
].join("\n");
|
|
893
|
+
case "agent-stop":
|
|
894
|
+
return [
|
|
895
|
+
`Usage: ${scriptName} agent stop <session-id> [options]`,
|
|
896
|
+
"",
|
|
897
|
+
"Stop one running agent process.",
|
|
898
|
+
"",
|
|
899
|
+
"Options:",
|
|
900
|
+
" --grace-ms <n> SIGTERM grace period before SIGKILL (0-600000)",
|
|
901
|
+
" --json Output StopAgentResult as JSON",
|
|
902
|
+
" -h, --help Show this help",
|
|
903
|
+
"",
|
|
904
|
+
allSubcommandsReference,
|
|
905
|
+
].join("\n");
|
|
906
|
+
case "agent-log":
|
|
907
|
+
return [
|
|
908
|
+
`Usage: ${scriptName} agent log <session-id> [options]`,
|
|
909
|
+
"",
|
|
910
|
+
"Show session logs for one agent.",
|
|
911
|
+
"",
|
|
912
|
+
"Options:",
|
|
913
|
+
" --follow Follow appended log lines",
|
|
914
|
+
" -h, --help Show this help",
|
|
915
|
+
"",
|
|
916
|
+
allSubcommandsReference,
|
|
917
|
+
].join("\n");
|
|
918
|
+
}
|
|
919
|
+
}
|
|
752
920
|
function renderChatHelp(topic, scriptName) {
|
|
753
921
|
if (topic === "chat") {
|
|
754
922
|
return [
|
|
@@ -952,9 +1120,199 @@ function renderRoutineAggregateHelp(scriptName) {
|
|
|
952
1120
|
" -h, --help Show this help",
|
|
953
1121
|
].join("\n");
|
|
954
1122
|
}
|
|
1123
|
+
function renderAgentAggregateHelp(scriptName) {
|
|
1124
|
+
return [
|
|
1125
|
+
`Usage: ${scriptName} agent list [options]`,
|
|
1126
|
+
` ${scriptName} agent show <session-id>`,
|
|
1127
|
+
` ${scriptName} agent stop <session-id> [options]`,
|
|
1128
|
+
` ${scriptName} agent log <session-id> [options]`,
|
|
1129
|
+
"",
|
|
1130
|
+
"Agent commands:",
|
|
1131
|
+
" list List active agent processes",
|
|
1132
|
+
" show Show one agent session",
|
|
1133
|
+
" stop Stop one running agent",
|
|
1134
|
+
" log Show session logs for one agent",
|
|
1135
|
+
"",
|
|
1136
|
+
"Options:",
|
|
1137
|
+
" --include-stale Include stale agent sessions",
|
|
1138
|
+
" --format <fmt> Output format for `agent list` (table, json)",
|
|
1139
|
+
" --json Alias for JSON output (`agent list`, `agent stop`)",
|
|
1140
|
+
" --grace-ms <n> SIGTERM grace period before SIGKILL (0-600000)",
|
|
1141
|
+
" --follow Follow appended log lines",
|
|
1142
|
+
" -h, --help Show this help",
|
|
1143
|
+
].join("\n");
|
|
1144
|
+
}
|
|
955
1145
|
function printHelp(topic) {
|
|
956
1146
|
console.error(renderHelpText(topic));
|
|
957
1147
|
}
|
|
1148
|
+
function renderAgentTable(agents) {
|
|
1149
|
+
if (agents.length === 0) {
|
|
1150
|
+
return "No active agents.\n";
|
|
1151
|
+
}
|
|
1152
|
+
const headers = ["agent_id", "session_id", "role", "backend", "running_for", "status"];
|
|
1153
|
+
const rows = agents.map((agent) => [
|
|
1154
|
+
agent.agent_id,
|
|
1155
|
+
agent.relay_session_id.slice(0, 8),
|
|
1156
|
+
agent.role ?? "-",
|
|
1157
|
+
agent.backend ?? "-",
|
|
1158
|
+
formatRunningFor(agent.running_for_seconds),
|
|
1159
|
+
agent.status,
|
|
1160
|
+
]);
|
|
1161
|
+
const widths = headers.map((header, column) => Math.max(header.length, ...rows.map((row) => row[column].length)));
|
|
1162
|
+
const lines = [
|
|
1163
|
+
headers.map((header, column) => header.padEnd(widths[column])).join(" "),
|
|
1164
|
+
...rows.map((row) => row.map((cell, column) => cell.padEnd(widths[column])).join(" ")),
|
|
1165
|
+
];
|
|
1166
|
+
return `${lines.join("\n")}\n`;
|
|
1167
|
+
}
|
|
1168
|
+
function formatRunningFor(totalSeconds) {
|
|
1169
|
+
const hours = Math.floor(totalSeconds / 3600);
|
|
1170
|
+
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
|
1171
|
+
const seconds = totalSeconds % 60;
|
|
1172
|
+
return [hours, minutes, seconds]
|
|
1173
|
+
.map((value) => String(value).padStart(2, "0"))
|
|
1174
|
+
.join(":");
|
|
1175
|
+
}
|
|
1176
|
+
function renderAgentDetail(detail) {
|
|
1177
|
+
const metadataOrder = [
|
|
1178
|
+
"relaySessionId",
|
|
1179
|
+
"agentId",
|
|
1180
|
+
"role",
|
|
1181
|
+
"parentRelaySessionId",
|
|
1182
|
+
"continuationKind",
|
|
1183
|
+
"backend",
|
|
1184
|
+
"backendSessionId",
|
|
1185
|
+
"ownerPid",
|
|
1186
|
+
"ownerStartedAt",
|
|
1187
|
+
"backendChildPid",
|
|
1188
|
+
"backendChildObservedAt",
|
|
1189
|
+
"status",
|
|
1190
|
+
"phase",
|
|
1191
|
+
"taskType",
|
|
1192
|
+
"cwd",
|
|
1193
|
+
"createdAt",
|
|
1194
|
+
"updatedAt",
|
|
1195
|
+
"lastHeartbeatAt",
|
|
1196
|
+
"lastProgressAt",
|
|
1197
|
+
"handoffFrom",
|
|
1198
|
+
];
|
|
1199
|
+
const lines = metadataOrder
|
|
1200
|
+
.flatMap((key) => {
|
|
1201
|
+
const value = detail.metadata[key];
|
|
1202
|
+
return value === undefined ? [] : [`${key}: ${formatAgentValue(value)}`];
|
|
1203
|
+
});
|
|
1204
|
+
if (detail.task) {
|
|
1205
|
+
lines.push("", "Task:");
|
|
1206
|
+
lines.push(` title: ${detail.task.title}`);
|
|
1207
|
+
lines.push(` status: ${detail.task.status}`);
|
|
1208
|
+
}
|
|
1209
|
+
if (detail.metadata.stopRequestedAt && detail.metadata.stopRequestedBy) {
|
|
1210
|
+
lines.push("", "Stop history:");
|
|
1211
|
+
lines.push(` requested_at: ${detail.metadata.stopRequestedAt}`);
|
|
1212
|
+
lines.push(` requested_by: ${detail.metadata.stopRequestedBy}`);
|
|
1213
|
+
if (detail.metadata.stopReason) {
|
|
1214
|
+
lines.push(` reason: ${detail.metadata.stopReason}`);
|
|
1215
|
+
}
|
|
1216
|
+
}
|
|
1217
|
+
return `${lines.join("\n")}\n`;
|
|
1218
|
+
}
|
|
1219
|
+
function formatAgentValue(value) {
|
|
1220
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
1221
|
+
return String(value);
|
|
1222
|
+
}
|
|
1223
|
+
return JSON.stringify(value);
|
|
1224
|
+
}
|
|
1225
|
+
function renderStopAgentResult(result) {
|
|
1226
|
+
switch (result.kind) {
|
|
1227
|
+
case "stopped":
|
|
1228
|
+
return `Stopped agent (pid ${result.pid}) after ${result.durationMs}ms.`;
|
|
1229
|
+
case "killed":
|
|
1230
|
+
return `Force-killed agent (pid ${result.pid}) after SIGTERM grace.`;
|
|
1231
|
+
case "no_child":
|
|
1232
|
+
return "No child process recorded — nothing to stop.";
|
|
1233
|
+
case "already_exited":
|
|
1234
|
+
return `Child process (pid ${result.pid}) has already exited.`;
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
function filterAgentLogLines(raw, relaySessionId) {
|
|
1238
|
+
return raw
|
|
1239
|
+
.split(/\r?\n/u)
|
|
1240
|
+
.filter((line) => line.length > 0)
|
|
1241
|
+
.filter((line) => matchesAgentLogLine(line, relaySessionId));
|
|
1242
|
+
}
|
|
1243
|
+
function matchesAgentLogLine(line, relaySessionId) {
|
|
1244
|
+
try {
|
|
1245
|
+
const parsed = JSON.parse(line);
|
|
1246
|
+
return parsed.session_id === relaySessionId;
|
|
1247
|
+
}
|
|
1248
|
+
catch {
|
|
1249
|
+
return false;
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
function createFollowReader() {
|
|
1253
|
+
return async (filePath, options) => {
|
|
1254
|
+
let cursor = 0;
|
|
1255
|
+
let buffer = "";
|
|
1256
|
+
try {
|
|
1257
|
+
cursor = (await readFile(filePath, "utf8")).length;
|
|
1258
|
+
}
|
|
1259
|
+
catch {
|
|
1260
|
+
cursor = 0;
|
|
1261
|
+
}
|
|
1262
|
+
const flushAppended = async () => {
|
|
1263
|
+
const content = await readFile(filePath, "utf8");
|
|
1264
|
+
if (content.length < cursor) {
|
|
1265
|
+
cursor = 0;
|
|
1266
|
+
buffer = "";
|
|
1267
|
+
}
|
|
1268
|
+
if (content.length === cursor) {
|
|
1269
|
+
return;
|
|
1270
|
+
}
|
|
1271
|
+
buffer += content.slice(cursor);
|
|
1272
|
+
cursor = content.length;
|
|
1273
|
+
const segments = buffer.split(/\r?\n/u);
|
|
1274
|
+
buffer = segments.pop() ?? "";
|
|
1275
|
+
for (const line of segments) {
|
|
1276
|
+
if (matchesAgentLogLine(line, options.sessionId)) {
|
|
1277
|
+
options.onLine(line);
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
};
|
|
1281
|
+
if (!options.follow) {
|
|
1282
|
+
return;
|
|
1283
|
+
}
|
|
1284
|
+
await new Promise((resolve, reject) => {
|
|
1285
|
+
let settled = false;
|
|
1286
|
+
const cleanup = () => {
|
|
1287
|
+
if (settled) {
|
|
1288
|
+
return;
|
|
1289
|
+
}
|
|
1290
|
+
settled = true;
|
|
1291
|
+
clearInterval(timer);
|
|
1292
|
+
process.off("SIGINT", onSigint);
|
|
1293
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
1294
|
+
resolve();
|
|
1295
|
+
};
|
|
1296
|
+
const fail = (error) => {
|
|
1297
|
+
if (settled) {
|
|
1298
|
+
return;
|
|
1299
|
+
}
|
|
1300
|
+
settled = true;
|
|
1301
|
+
clearInterval(timer);
|
|
1302
|
+
process.off("SIGINT", onSigint);
|
|
1303
|
+
options.signal?.removeEventListener("abort", onAbort);
|
|
1304
|
+
reject(error);
|
|
1305
|
+
};
|
|
1306
|
+
const onAbort = () => cleanup();
|
|
1307
|
+
const onSigint = () => cleanup();
|
|
1308
|
+
const timer = setInterval(() => {
|
|
1309
|
+
void flushAppended().catch(fail);
|
|
1310
|
+
}, 1000);
|
|
1311
|
+
process.once("SIGINT", onSigint);
|
|
1312
|
+
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
1313
|
+
});
|
|
1314
|
+
};
|
|
1315
|
+
}
|
|
958
1316
|
function createRoutineCatalogLoader(allowCommandRun) {
|
|
959
1317
|
return async (yamlPath, repo, vars) => {
|
|
960
1318
|
const loaded = await loadRoutine(yamlPath, repo, vars);
|