@serviceme/devtools-core 0.1.3 → 0.1.4
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/index.d.mts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +493 -226
- package/dist/index.mjs +490 -225
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -57,7 +57,9 @@ __export(index_exports, {
|
|
|
57
57
|
moveFiles: () => moveFiles,
|
|
58
58
|
noopLogger: () => noopLogger,
|
|
59
59
|
parseIntervalMs: () => parseIntervalMs,
|
|
60
|
-
|
|
60
|
+
resolveTaskExecutionPayload: () => resolveTaskExecutionPayload,
|
|
61
|
+
unzipFile: () => unzipFile,
|
|
62
|
+
validateTaskPayload: () => validateTaskPayload
|
|
61
63
|
});
|
|
62
64
|
module.exports = __toCommonJS(index_exports);
|
|
63
65
|
|
|
@@ -92,7 +94,8 @@ async function runCommand(command, options = {}) {
|
|
|
92
94
|
cwd: options.cwd,
|
|
93
95
|
env: options.env,
|
|
94
96
|
shell: options.shell,
|
|
95
|
-
stdio: "pipe"
|
|
97
|
+
stdio: "pipe",
|
|
98
|
+
windowsHide: true
|
|
96
99
|
});
|
|
97
100
|
let stdout = "";
|
|
98
101
|
let stderr = "";
|
|
@@ -1113,12 +1116,51 @@ var PidManager = class {
|
|
|
1113
1116
|
};
|
|
1114
1117
|
|
|
1115
1118
|
// src/scheduled-tasks/daemon/SchedulerDaemon.ts
|
|
1116
|
-
var
|
|
1119
|
+
var fs9 = __toESM(require("fs"));
|
|
1120
|
+
var os = __toESM(require("os"));
|
|
1117
1121
|
|
|
1118
1122
|
// src/scheduled-tasks/executors/GithubCopilotCliExecutor.ts
|
|
1119
1123
|
var import_node_child_process2 = require("child_process");
|
|
1120
|
-
var
|
|
1124
|
+
var fs5 = __toESM(require("fs"));
|
|
1125
|
+
|
|
1126
|
+
// src/scheduled-tasks/executors/timeout.ts
|
|
1127
|
+
function resolveConfiguredTimeoutMs(timeoutSeconds, defaultTimeoutMs) {
|
|
1128
|
+
if (timeoutSeconds === 0) {
|
|
1129
|
+
return void 0;
|
|
1130
|
+
}
|
|
1131
|
+
if (typeof timeoutSeconds !== "number" || !Number.isFinite(timeoutSeconds) || timeoutSeconds < 0) {
|
|
1132
|
+
return defaultTimeoutMs;
|
|
1133
|
+
}
|
|
1134
|
+
return timeoutSeconds * 1e3;
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
// src/scheduled-tasks/executors/GithubCopilotCliExecutor.ts
|
|
1121
1138
|
var MAX_OUTPUT_BYTES = 2 * 1024 * 1024;
|
|
1139
|
+
var DEFAULT_TIMEOUT_MS2 = 3e5;
|
|
1140
|
+
function redactArgs(args) {
|
|
1141
|
+
const redacted = [];
|
|
1142
|
+
let redactNext = false;
|
|
1143
|
+
for (const arg of args) {
|
|
1144
|
+
if (redactNext) {
|
|
1145
|
+
redacted.push("<redacted>");
|
|
1146
|
+
redactNext = false;
|
|
1147
|
+
continue;
|
|
1148
|
+
}
|
|
1149
|
+
redacted.push(arg);
|
|
1150
|
+
if (arg === "--prompt") {
|
|
1151
|
+
redactNext = true;
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
return redacted;
|
|
1155
|
+
}
|
|
1156
|
+
function writeDiagnostic(message) {
|
|
1157
|
+
const logPath = process.env.SERVICEME_SCHEDULER_LOG_PATH;
|
|
1158
|
+
if (logPath) {
|
|
1159
|
+
fs5.appendFileSync(logPath, message);
|
|
1160
|
+
return;
|
|
1161
|
+
}
|
|
1162
|
+
process.stderr.write(message);
|
|
1163
|
+
}
|
|
1122
1164
|
var GithubCopilotCliExecutor = class {
|
|
1123
1165
|
async execute(payload, abortSignal) {
|
|
1124
1166
|
const authenticated = await isCopilotAuthenticated();
|
|
@@ -1141,7 +1183,7 @@ var GithubCopilotCliExecutor = class {
|
|
|
1141
1183
|
}
|
|
1142
1184
|
executeStreaming(payload, onOutput, abortSignal) {
|
|
1143
1185
|
const p = payload;
|
|
1144
|
-
const
|
|
1186
|
+
const timeoutMs = resolveConfiguredTimeoutMs(p.timeout, DEFAULT_TIMEOUT_MS2);
|
|
1145
1187
|
let resolve;
|
|
1146
1188
|
const resultPromise = new Promise((r) => {
|
|
1147
1189
|
resolve = r;
|
|
@@ -1150,7 +1192,7 @@ var GithubCopilotCliExecutor = class {
|
|
|
1150
1192
|
const settle = (result) => {
|
|
1151
1193
|
if (settled) return;
|
|
1152
1194
|
settled = true;
|
|
1153
|
-
clearTimeout(timer);
|
|
1195
|
+
if (timer) clearTimeout(timer);
|
|
1154
1196
|
resolve?.(result);
|
|
1155
1197
|
};
|
|
1156
1198
|
if (abortSignal?.aborted) {
|
|
@@ -1176,23 +1218,32 @@ var GithubCopilotCliExecutor = class {
|
|
|
1176
1218
|
if (p.agent) {
|
|
1177
1219
|
args.push("--agent", p.agent);
|
|
1178
1220
|
}
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1221
|
+
args.push("--timeout", String(p.timeout ?? 0));
|
|
1222
|
+
writeDiagnostic(
|
|
1223
|
+
`[GithubCopilotCliExecutor] spawn: command=serviceme, args=${JSON.stringify(redactArgs(args))}, promptLen=${p.prompt.length}, cwd=${p.workspace ?? "(default)"}, platform=${process.platform}, windowsHide=true, pid=${process.pid}
|
|
1224
|
+
`
|
|
1225
|
+
);
|
|
1182
1226
|
const child = (0, import_node_child_process2.spawn)("serviceme", args, {
|
|
1183
1227
|
cwd: p.workspace,
|
|
1184
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
1228
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
1229
|
+
windowsHide: true
|
|
1185
1230
|
});
|
|
1186
|
-
|
|
1231
|
+
if (child.pid) {
|
|
1232
|
+
writeDiagnostic(
|
|
1233
|
+
`[GithubCopilotCliExecutor] spawned child PID=${child.pid}
|
|
1234
|
+
`
|
|
1235
|
+
);
|
|
1236
|
+
}
|
|
1237
|
+
const timer = timeoutMs != null ? setTimeout(() => {
|
|
1187
1238
|
child.kill("SIGTERM");
|
|
1188
1239
|
setTimeout(() => {
|
|
1189
1240
|
if (!child.killed) child.kill("SIGKILL");
|
|
1190
1241
|
}, 5e3);
|
|
1191
1242
|
settle({
|
|
1192
1243
|
status: "timeout",
|
|
1193
|
-
error: `Copilot CLI execution timed out after ${
|
|
1244
|
+
error: `Copilot CLI execution timed out after ${timeoutMs / 1e3}s`
|
|
1194
1245
|
});
|
|
1195
|
-
},
|
|
1246
|
+
}, timeoutMs) : void 0;
|
|
1196
1247
|
let stdoutBuf = "";
|
|
1197
1248
|
let stderrBuf = "";
|
|
1198
1249
|
child.stdout.on("data", (chunk) => {
|
|
@@ -1236,19 +1287,19 @@ var GithubCopilotCliExecutor = class {
|
|
|
1236
1287
|
};
|
|
1237
1288
|
|
|
1238
1289
|
// src/scheduled-tasks/executors/HttpRequestExecutor.ts
|
|
1239
|
-
var
|
|
1290
|
+
var DEFAULT_TIMEOUT_MS3 = 3e4;
|
|
1240
1291
|
var HttpRequestExecutor = class {
|
|
1241
1292
|
async execute(payload, abortSignal) {
|
|
1242
1293
|
const p = payload;
|
|
1243
|
-
const
|
|
1294
|
+
const timeoutMs = resolveConfiguredTimeoutMs(p.timeout, DEFAULT_TIMEOUT_MS3);
|
|
1244
1295
|
const ac = new AbortController();
|
|
1245
|
-
let
|
|
1246
|
-
const timer = setTimeout(() => {
|
|
1247
|
-
|
|
1296
|
+
let timedOut = false;
|
|
1297
|
+
const timer = timeoutMs != null ? setTimeout(() => {
|
|
1298
|
+
timedOut = true;
|
|
1248
1299
|
ac.abort();
|
|
1249
|
-
},
|
|
1300
|
+
}, timeoutMs) : void 0;
|
|
1250
1301
|
if (abortSignal?.aborted) {
|
|
1251
|
-
clearTimeout(timer);
|
|
1302
|
+
if (timer) clearTimeout(timer);
|
|
1252
1303
|
return { status: "cancelled", error: "Execution aborted" };
|
|
1253
1304
|
}
|
|
1254
1305
|
let externalAbort = false;
|
|
@@ -1256,7 +1307,7 @@ var HttpRequestExecutor = class {
|
|
|
1256
1307
|
"abort",
|
|
1257
1308
|
() => {
|
|
1258
1309
|
externalAbort = true;
|
|
1259
|
-
clearTimeout(timer);
|
|
1310
|
+
if (timer) clearTimeout(timer);
|
|
1260
1311
|
ac.abort();
|
|
1261
1312
|
},
|
|
1262
1313
|
{ once: true }
|
|
@@ -1268,7 +1319,7 @@ var HttpRequestExecutor = class {
|
|
|
1268
1319
|
body: p.body,
|
|
1269
1320
|
signal: ac.signal
|
|
1270
1321
|
});
|
|
1271
|
-
clearTimeout(timer);
|
|
1322
|
+
if (timer) clearTimeout(timer);
|
|
1272
1323
|
const body = await response.text();
|
|
1273
1324
|
if (response.ok) {
|
|
1274
1325
|
return {
|
|
@@ -1283,14 +1334,17 @@ ${body}`.trim()
|
|
|
1283
1334
|
${body}`.trim()
|
|
1284
1335
|
};
|
|
1285
1336
|
} catch (err) {
|
|
1286
|
-
clearTimeout(timer);
|
|
1337
|
+
if (timer) clearTimeout(timer);
|
|
1287
1338
|
if (err instanceof Error && err.name === "AbortError") {
|
|
1288
1339
|
if (externalAbort) {
|
|
1289
1340
|
return { status: "cancelled", error: "Execution cancelled" };
|
|
1290
1341
|
}
|
|
1342
|
+
if (!timedOut || timeoutMs == null) {
|
|
1343
|
+
return { status: "failure", error: err.message };
|
|
1344
|
+
}
|
|
1291
1345
|
return {
|
|
1292
1346
|
status: "timeout",
|
|
1293
|
-
error: `HTTP request timed out after ${
|
|
1347
|
+
error: `HTTP request timed out after ${timeoutMs / 1e3}s`
|
|
1294
1348
|
};
|
|
1295
1349
|
}
|
|
1296
1350
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -1301,8 +1355,82 @@ ${body}`.trim()
|
|
|
1301
1355
|
|
|
1302
1356
|
// src/scheduled-tasks/executors/ShellExecutor.ts
|
|
1303
1357
|
var import_node_child_process3 = require("child_process");
|
|
1304
|
-
var
|
|
1358
|
+
var fs6 = __toESM(require("fs"));
|
|
1359
|
+
var path5 = __toESM(require("path"));
|
|
1305
1360
|
var MAX_OUTPUT_BYTES2 = 1024 * 1024;
|
|
1361
|
+
var DEFAULT_TIMEOUT_MS4 = 6e4;
|
|
1362
|
+
var POSIX_SHELL_CANDIDATES = ["bash.exe", "sh.exe"];
|
|
1363
|
+
function resolveShellExecution(script, options = {}) {
|
|
1364
|
+
const platform = options.platform ?? process.platform;
|
|
1365
|
+
const env = options.env ?? process.env;
|
|
1366
|
+
const fileExists = options.fileExists ?? fs6.existsSync;
|
|
1367
|
+
if (platform === "win32") {
|
|
1368
|
+
const posixShell = usesPosixShellSyntax(script) ? findWindowsPosixShell(env, fileExists) : null;
|
|
1369
|
+
if (posixShell) {
|
|
1370
|
+
return {
|
|
1371
|
+
command: posixShell,
|
|
1372
|
+
args: ["-s"],
|
|
1373
|
+
stdinScript: script,
|
|
1374
|
+
outputEncoding: "utf8",
|
|
1375
|
+
shellKind: "posix"
|
|
1376
|
+
};
|
|
1377
|
+
}
|
|
1378
|
+
return {
|
|
1379
|
+
command: env.ComSpec ?? env.COMSPEC ?? "cmd.exe",
|
|
1380
|
+
args: ["/d", "/s", "/c", script],
|
|
1381
|
+
outputEncoding: "utf8",
|
|
1382
|
+
shellKind: "cmd"
|
|
1383
|
+
};
|
|
1384
|
+
}
|
|
1385
|
+
return {
|
|
1386
|
+
command: "sh",
|
|
1387
|
+
args: ["-s"],
|
|
1388
|
+
stdinScript: script,
|
|
1389
|
+
outputEncoding: "utf8",
|
|
1390
|
+
shellKind: "posix"
|
|
1391
|
+
};
|
|
1392
|
+
}
|
|
1393
|
+
function usesPosixShellSyntax(script) {
|
|
1394
|
+
return /\$\([^)]*\)|`[^`]*`/.test(script);
|
|
1395
|
+
}
|
|
1396
|
+
function findWindowsPosixShell(env, fileExists) {
|
|
1397
|
+
const explicitShell = env.SERVICEME_POSIX_SHELL;
|
|
1398
|
+
if (explicitShell && fileExists(explicitShell)) {
|
|
1399
|
+
return explicitShell;
|
|
1400
|
+
}
|
|
1401
|
+
const knownGitBashPaths = [
|
|
1402
|
+
"C:\\Program Files\\Git\\bin\\bash.exe",
|
|
1403
|
+
"C:\\Program Files\\Git\\usr\\bin\\bash.exe",
|
|
1404
|
+
"C:\\Program Files (x86)\\Git\\bin\\bash.exe",
|
|
1405
|
+
"C:\\Program Files (x86)\\Git\\usr\\bin\\bash.exe"
|
|
1406
|
+
];
|
|
1407
|
+
for (const candidate of knownGitBashPaths) {
|
|
1408
|
+
if (fileExists(candidate)) return candidate;
|
|
1409
|
+
}
|
|
1410
|
+
const pathValue = env.Path ?? env.PATH ?? "";
|
|
1411
|
+
for (const dir of pathValue.split(path5.win32.delimiter)) {
|
|
1412
|
+
if (!dir) continue;
|
|
1413
|
+
for (const executable of POSIX_SHELL_CANDIDATES) {
|
|
1414
|
+
const candidate = path5.win32.join(dir, executable);
|
|
1415
|
+
if (fileExists(candidate) && !isWindowsWslLauncher(candidate)) {
|
|
1416
|
+
return candidate;
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
return null;
|
|
1421
|
+
}
|
|
1422
|
+
function isWindowsWslLauncher(candidate) {
|
|
1423
|
+
const normalized = path5.win32.normalize(candidate).toLowerCase();
|
|
1424
|
+
return normalized.endsWith("\\windows\\system32\\bash.exe") || normalized.endsWith("\\windows\\syswow64\\bash.exe");
|
|
1425
|
+
}
|
|
1426
|
+
function writeDiagnostic2(message) {
|
|
1427
|
+
const logPath = process.env.SERVICEME_SCHEDULER_LOG_PATH;
|
|
1428
|
+
if (logPath) {
|
|
1429
|
+
fs6.appendFileSync(logPath, message);
|
|
1430
|
+
return;
|
|
1431
|
+
}
|
|
1432
|
+
process.stderr.write(message);
|
|
1433
|
+
}
|
|
1306
1434
|
var ShellExecutor = class {
|
|
1307
1435
|
async execute(payload, abortSignal) {
|
|
1308
1436
|
let output = "";
|
|
@@ -1318,7 +1446,7 @@ var ShellExecutor = class {
|
|
|
1318
1446
|
}
|
|
1319
1447
|
executeStreaming(payload, onOutput, abortSignal) {
|
|
1320
1448
|
const p = payload;
|
|
1321
|
-
const
|
|
1449
|
+
const timeoutMs = resolveConfiguredTimeoutMs(p.timeout, DEFAULT_TIMEOUT_MS4);
|
|
1322
1450
|
let resolve;
|
|
1323
1451
|
const resultPromise = new Promise((r) => {
|
|
1324
1452
|
resolve = r;
|
|
@@ -1327,7 +1455,7 @@ var ShellExecutor = class {
|
|
|
1327
1455
|
const settle = (result) => {
|
|
1328
1456
|
if (settled) return;
|
|
1329
1457
|
settled = true;
|
|
1330
|
-
clearTimeout(timer);
|
|
1458
|
+
if (timer) clearTimeout(timer);
|
|
1331
1459
|
resolve?.(result);
|
|
1332
1460
|
};
|
|
1333
1461
|
if (abortSignal?.aborted) {
|
|
@@ -1340,37 +1468,65 @@ var ShellExecutor = class {
|
|
|
1340
1468
|
}
|
|
1341
1469
|
};
|
|
1342
1470
|
}
|
|
1343
|
-
const
|
|
1471
|
+
const shellExecution = resolveShellExecution(p.script);
|
|
1472
|
+
const spawnOpts = {
|
|
1344
1473
|
cwd: p.cwd,
|
|
1345
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
1346
|
-
|
|
1347
|
-
|
|
1474
|
+
stdio: [shellExecution.stdinScript ? "pipe" : "ignore", "pipe", "pipe"],
|
|
1475
|
+
windowsHide: true
|
|
1476
|
+
};
|
|
1477
|
+
writeDiagnostic2(
|
|
1478
|
+
`[ShellExecutor] spawn:
|
|
1479
|
+
platform=${process.platform}
|
|
1480
|
+
shell=${shellExecution.shellKind}
|
|
1481
|
+
command=${shellExecution.command}
|
|
1482
|
+
args=${JSON.stringify(shellExecution.args.filter((a) => a !== p.script))}
|
|
1483
|
+
stdin=${shellExecution.stdinScript ? "true" : "false"}
|
|
1484
|
+
cwd=${p.cwd ?? "(default)"}
|
|
1485
|
+
timeout=${timeoutMs != null ? `${timeoutMs}ms` : "unlimited"}
|
|
1486
|
+
scriptLen=${p.script.length}
|
|
1487
|
+
windowsHide=true
|
|
1488
|
+
daemonPid=${process.pid}
|
|
1489
|
+
`
|
|
1490
|
+
);
|
|
1491
|
+
const child = (0, import_node_child_process3.spawn)(shellExecution.command, shellExecution.args, spawnOpts);
|
|
1492
|
+
if (shellExecution.stdinScript && child.stdin) {
|
|
1493
|
+
child.stdin.end(shellExecution.stdinScript);
|
|
1494
|
+
}
|
|
1495
|
+
if (child.pid) {
|
|
1496
|
+
writeDiagnostic2(`[ShellExecutor] spawned child PID=${child.pid}
|
|
1497
|
+
`);
|
|
1498
|
+
}
|
|
1499
|
+
const timer = timeoutMs != null ? setTimeout(() => {
|
|
1348
1500
|
child.kill("SIGTERM");
|
|
1349
1501
|
setTimeout(() => {
|
|
1350
1502
|
if (!child.killed) child.kill("SIGKILL");
|
|
1351
1503
|
}, 5e3);
|
|
1352
1504
|
settle({
|
|
1353
1505
|
status: "timeout",
|
|
1354
|
-
error: `Shell execution timed out after ${
|
|
1506
|
+
error: `Shell execution timed out after ${timeoutMs / 1e3}s`
|
|
1355
1507
|
});
|
|
1356
|
-
},
|
|
1508
|
+
}, timeoutMs) : void 0;
|
|
1357
1509
|
let stdoutBuf = "";
|
|
1358
1510
|
let stderrBuf = "";
|
|
1359
|
-
child.stdout
|
|
1360
|
-
const data = chunk.toString();
|
|
1511
|
+
child.stdout?.on("data", (chunk) => {
|
|
1512
|
+
const data = chunk.toString(shellExecution.outputEncoding);
|
|
1361
1513
|
stdoutBuf += data;
|
|
1362
1514
|
if (stdoutBuf.length > MAX_OUTPUT_BYTES2)
|
|
1363
1515
|
stdoutBuf = stdoutBuf.slice(-MAX_OUTPUT_BYTES2);
|
|
1364
1516
|
onOutput("stdout", data);
|
|
1365
1517
|
});
|
|
1366
|
-
child.stderr
|
|
1367
|
-
const data = chunk.toString();
|
|
1518
|
+
child.stderr?.on("data", (chunk) => {
|
|
1519
|
+
const data = chunk.toString(shellExecution.outputEncoding);
|
|
1368
1520
|
stderrBuf += data;
|
|
1369
1521
|
if (stderrBuf.length > MAX_OUTPUT_BYTES2)
|
|
1370
1522
|
stderrBuf = stderrBuf.slice(-MAX_OUTPUT_BYTES2);
|
|
1371
1523
|
onOutput("stderr", data);
|
|
1372
1524
|
});
|
|
1373
1525
|
child.on("close", (code) => {
|
|
1526
|
+
writeDiagnostic2(
|
|
1527
|
+
`[ShellExecutor] child PID=${child.pid} exited: code=${code}
|
|
1528
|
+
`
|
|
1529
|
+
);
|
|
1374
1530
|
if (code === 0) {
|
|
1375
1531
|
settle({ status: "success", output: stdoutBuf || void 0 });
|
|
1376
1532
|
} else {
|
|
@@ -1417,36 +1573,65 @@ function getExecutor(taskType) {
|
|
|
1417
1573
|
|
|
1418
1574
|
// src/scheduled-tasks/TaskConfigManager.ts
|
|
1419
1575
|
var import_node_crypto = require("crypto");
|
|
1420
|
-
var
|
|
1421
|
-
var
|
|
1576
|
+
var fs7 = __toESM(require("fs"));
|
|
1577
|
+
var path6 = __toESM(require("path"));
|
|
1578
|
+
var import_devtools_protocol7 = require("@serviceme/devtools-protocol");
|
|
1422
1579
|
var CONFIG_DIR3 = ".serviceme";
|
|
1423
1580
|
var CONFIG_FILE = "scheduled-tasks.json";
|
|
1424
1581
|
function emptyConfig() {
|
|
1425
1582
|
return { version: 1, tasks: [] };
|
|
1426
1583
|
}
|
|
1584
|
+
function isRecord(value) {
|
|
1585
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1586
|
+
}
|
|
1587
|
+
function requireNonEmptyString(payload, field, taskType) {
|
|
1588
|
+
if (!isRecord(payload) || typeof payload[field] !== "string" || !payload[field].trim()) {
|
|
1589
|
+
throw (0, import_devtools_protocol7.createServicemeError)(
|
|
1590
|
+
"invalid_payload",
|
|
1591
|
+
`${taskType} payload ${field} is required`
|
|
1592
|
+
);
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
function validateTaskPayload(taskType, payload) {
|
|
1596
|
+
switch (taskType) {
|
|
1597
|
+
case "command":
|
|
1598
|
+
requireNonEmptyString(payload, "command", taskType);
|
|
1599
|
+
return;
|
|
1600
|
+
case "shell":
|
|
1601
|
+
requireNonEmptyString(payload, "script", taskType);
|
|
1602
|
+
return;
|
|
1603
|
+
case "http_request":
|
|
1604
|
+
requireNonEmptyString(payload, "url", taskType);
|
|
1605
|
+
requireNonEmptyString(payload, "method", taskType);
|
|
1606
|
+
return;
|
|
1607
|
+
case "github_copilot_cli":
|
|
1608
|
+
requireNonEmptyString(payload, "prompt", taskType);
|
|
1609
|
+
return;
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1427
1612
|
var TaskConfigManager = class {
|
|
1428
1613
|
constructor(workspacePath) {
|
|
1429
|
-
this.configPath =
|
|
1614
|
+
this.configPath = path6.join(workspacePath, CONFIG_DIR3, CONFIG_FILE);
|
|
1430
1615
|
}
|
|
1431
1616
|
getConfigPath() {
|
|
1432
1617
|
return this.configPath;
|
|
1433
1618
|
}
|
|
1434
1619
|
readConfig() {
|
|
1435
|
-
if (!
|
|
1620
|
+
if (!fs7.existsSync(this.configPath)) {
|
|
1436
1621
|
return emptyConfig();
|
|
1437
1622
|
}
|
|
1438
|
-
const raw =
|
|
1623
|
+
const raw = fs7.readFileSync(this.configPath, "utf-8");
|
|
1439
1624
|
const parsed = JSON.parse(raw);
|
|
1440
1625
|
return parsed;
|
|
1441
1626
|
}
|
|
1442
1627
|
writeConfig(config) {
|
|
1443
|
-
const dir =
|
|
1444
|
-
if (!
|
|
1445
|
-
|
|
1628
|
+
const dir = path6.dirname(this.configPath);
|
|
1629
|
+
if (!fs7.existsSync(dir)) {
|
|
1630
|
+
fs7.mkdirSync(dir, { recursive: true });
|
|
1446
1631
|
}
|
|
1447
1632
|
const tmp = `${this.configPath}.tmp`;
|
|
1448
|
-
|
|
1449
|
-
|
|
1633
|
+
fs7.writeFileSync(tmp, JSON.stringify(config, null, " "), "utf-8");
|
|
1634
|
+
fs7.renameSync(tmp, this.configPath);
|
|
1450
1635
|
}
|
|
1451
1636
|
listTasks() {
|
|
1452
1637
|
return this.readConfig().tasks;
|
|
@@ -1458,6 +1643,7 @@ var TaskConfigManager = class {
|
|
|
1458
1643
|
return this.readConfig().tasks.find((t) => t.name === name);
|
|
1459
1644
|
}
|
|
1460
1645
|
createTask(input) {
|
|
1646
|
+
validateTaskPayload(input.taskType, input.payload);
|
|
1461
1647
|
const config = this.readConfig();
|
|
1462
1648
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1463
1649
|
const task = {
|
|
@@ -1486,6 +1672,9 @@ var TaskConfigManager = class {
|
|
|
1486
1672
|
if (!existing) {
|
|
1487
1673
|
throw new Error(`Task '${id}' not found`);
|
|
1488
1674
|
}
|
|
1675
|
+
const nextTaskType = input.taskType ?? existing.taskType;
|
|
1676
|
+
const nextPayload = input.payload ?? existing.payload;
|
|
1677
|
+
validateTaskPayload(nextTaskType, nextPayload);
|
|
1489
1678
|
const updated = {
|
|
1490
1679
|
...existing,
|
|
1491
1680
|
...input.name !== void 0 && { name: input.name },
|
|
@@ -1524,10 +1713,197 @@ var TaskConfigManager = class {
|
|
|
1524
1713
|
}
|
|
1525
1714
|
};
|
|
1526
1715
|
|
|
1716
|
+
// src/scheduled-tasks/TaskExecutionEngine.ts
|
|
1717
|
+
function hasNonEmptyString(value) {
|
|
1718
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
1719
|
+
}
|
|
1720
|
+
function resolveTaskExecutionPayload(taskType, payload, workspacePath) {
|
|
1721
|
+
if (!hasNonEmptyString(workspacePath)) {
|
|
1722
|
+
return payload;
|
|
1723
|
+
}
|
|
1724
|
+
if (taskType === "shell") {
|
|
1725
|
+
const shellPayload = payload;
|
|
1726
|
+
if (hasNonEmptyString(shellPayload.cwd)) {
|
|
1727
|
+
return shellPayload;
|
|
1728
|
+
}
|
|
1729
|
+
return { ...shellPayload, cwd: workspacePath };
|
|
1730
|
+
}
|
|
1731
|
+
if (taskType === "github_copilot_cli") {
|
|
1732
|
+
const copilotPayload = payload;
|
|
1733
|
+
if (hasNonEmptyString(copilotPayload.workspace)) {
|
|
1734
|
+
return copilotPayload;
|
|
1735
|
+
}
|
|
1736
|
+
return { ...copilotPayload, workspace: workspacePath };
|
|
1737
|
+
}
|
|
1738
|
+
return payload;
|
|
1739
|
+
}
|
|
1740
|
+
var TaskExecutionEngine = class {
|
|
1741
|
+
constructor(getExecutor2) {
|
|
1742
|
+
this.getExecutor = getExecutor2;
|
|
1743
|
+
this.running = /* @__PURE__ */ new Map();
|
|
1744
|
+
this.taskExecutions = /* @__PURE__ */ new Map();
|
|
1745
|
+
this.listener = null;
|
|
1746
|
+
}
|
|
1747
|
+
setListener(listener) {
|
|
1748
|
+
this.listener = listener;
|
|
1749
|
+
}
|
|
1750
|
+
async execute(snapshot) {
|
|
1751
|
+
const policy = snapshot.concurrencyPolicy ?? "reject";
|
|
1752
|
+
if (policy === "reject") {
|
|
1753
|
+
const existingIds = this.taskExecutions.get(snapshot.taskId);
|
|
1754
|
+
if (existingIds && existingIds.size > 0) {
|
|
1755
|
+
throw new Error(
|
|
1756
|
+
`TASK_ALREADY_RUNNING: Task ${snapshot.taskId} is already running`
|
|
1757
|
+
);
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1761
|
+
if (!this.taskExecutions.has(snapshot.taskId)) {
|
|
1762
|
+
this.taskExecutions.set(snapshot.taskId, /* @__PURE__ */ new Set());
|
|
1763
|
+
}
|
|
1764
|
+
this.taskExecutions.get(snapshot.taskId)?.add(snapshot.executionId);
|
|
1765
|
+
const ac = new AbortController();
|
|
1766
|
+
const runningExec = {
|
|
1767
|
+
executionId: snapshot.executionId,
|
|
1768
|
+
taskId: snapshot.taskId,
|
|
1769
|
+
startedAt,
|
|
1770
|
+
cancel: () => ac.abort()
|
|
1771
|
+
};
|
|
1772
|
+
this.running.set(snapshot.executionId, runningExec);
|
|
1773
|
+
this.listener?.onStarted({
|
|
1774
|
+
executionId: snapshot.executionId,
|
|
1775
|
+
taskId: snapshot.taskId,
|
|
1776
|
+
startedAt
|
|
1777
|
+
});
|
|
1778
|
+
try {
|
|
1779
|
+
const executionPayload = resolveTaskExecutionPayload(
|
|
1780
|
+
snapshot.type,
|
|
1781
|
+
snapshot.payload,
|
|
1782
|
+
snapshot.workspacePath
|
|
1783
|
+
);
|
|
1784
|
+
validateTaskPayload(snapshot.type, executionPayload);
|
|
1785
|
+
const executor = this.getExecutor(snapshot.type);
|
|
1786
|
+
let result;
|
|
1787
|
+
if (isStreamingTaskExecutor(executor)) {
|
|
1788
|
+
const handle = executor.executeStreaming(
|
|
1789
|
+
executionPayload,
|
|
1790
|
+
(stream, data) => {
|
|
1791
|
+
this.listener?.onOutput({
|
|
1792
|
+
executionId: snapshot.executionId,
|
|
1793
|
+
stream,
|
|
1794
|
+
data
|
|
1795
|
+
});
|
|
1796
|
+
},
|
|
1797
|
+
ac.signal
|
|
1798
|
+
);
|
|
1799
|
+
runningExec.cancel = () => {
|
|
1800
|
+
handle.cancel();
|
|
1801
|
+
ac.abort();
|
|
1802
|
+
};
|
|
1803
|
+
result = await handle.result;
|
|
1804
|
+
} else {
|
|
1805
|
+
result = await executor.execute(executionPayload, ac.signal);
|
|
1806
|
+
}
|
|
1807
|
+
const log = {
|
|
1808
|
+
id: snapshot.executionId,
|
|
1809
|
+
taskId: snapshot.taskId,
|
|
1810
|
+
taskName: snapshot.name,
|
|
1811
|
+
startedAt,
|
|
1812
|
+
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1813
|
+
status: result.status === "running" ? "failure" : result.status,
|
|
1814
|
+
output: result.output,
|
|
1815
|
+
error: result.error
|
|
1816
|
+
};
|
|
1817
|
+
switch (log.status) {
|
|
1818
|
+
case "success":
|
|
1819
|
+
this.listener?.onCompleted({
|
|
1820
|
+
executionId: snapshot.executionId,
|
|
1821
|
+
log
|
|
1822
|
+
});
|
|
1823
|
+
break;
|
|
1824
|
+
case "cancelled":
|
|
1825
|
+
this.listener?.onCancelled({
|
|
1826
|
+
executionId: snapshot.executionId,
|
|
1827
|
+
log
|
|
1828
|
+
});
|
|
1829
|
+
break;
|
|
1830
|
+
case "timeout":
|
|
1831
|
+
this.listener?.onFailed({
|
|
1832
|
+
executionId: snapshot.executionId,
|
|
1833
|
+
status: "timeout",
|
|
1834
|
+
reason: "timeout",
|
|
1835
|
+
log
|
|
1836
|
+
});
|
|
1837
|
+
break;
|
|
1838
|
+
default:
|
|
1839
|
+
this.listener?.onFailed({
|
|
1840
|
+
executionId: snapshot.executionId,
|
|
1841
|
+
status: "failure",
|
|
1842
|
+
reason: "error",
|
|
1843
|
+
log
|
|
1844
|
+
});
|
|
1845
|
+
break;
|
|
1846
|
+
}
|
|
1847
|
+
} catch (err) {
|
|
1848
|
+
const log = {
|
|
1849
|
+
id: snapshot.executionId,
|
|
1850
|
+
taskId: snapshot.taskId,
|
|
1851
|
+
taskName: snapshot.name,
|
|
1852
|
+
startedAt,
|
|
1853
|
+
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1854
|
+
status: ac.signal.aborted ? "cancelled" : "failure",
|
|
1855
|
+
error: err instanceof Error ? err.message : String(err)
|
|
1856
|
+
};
|
|
1857
|
+
if (ac.signal.aborted) {
|
|
1858
|
+
this.listener?.onCancelled({
|
|
1859
|
+
executionId: snapshot.executionId,
|
|
1860
|
+
log
|
|
1861
|
+
});
|
|
1862
|
+
} else {
|
|
1863
|
+
this.listener?.onFailed({
|
|
1864
|
+
executionId: snapshot.executionId,
|
|
1865
|
+
status: "failure",
|
|
1866
|
+
reason: "error",
|
|
1867
|
+
log
|
|
1868
|
+
});
|
|
1869
|
+
}
|
|
1870
|
+
} finally {
|
|
1871
|
+
this.running.delete(snapshot.executionId);
|
|
1872
|
+
const taskExecIds = this.taskExecutions.get(snapshot.taskId);
|
|
1873
|
+
if (taskExecIds) {
|
|
1874
|
+
taskExecIds.delete(snapshot.executionId);
|
|
1875
|
+
if (taskExecIds.size === 0) {
|
|
1876
|
+
this.taskExecutions.delete(snapshot.taskId);
|
|
1877
|
+
}
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
cancel(executionId) {
|
|
1882
|
+
const exec = this.running.get(executionId);
|
|
1883
|
+
if (!exec) return false;
|
|
1884
|
+
exec.cancel();
|
|
1885
|
+
return true;
|
|
1886
|
+
}
|
|
1887
|
+
listRunning() {
|
|
1888
|
+
return Array.from(this.running.values()).map((e) => ({
|
|
1889
|
+
executionId: e.executionId,
|
|
1890
|
+
taskId: e.taskId,
|
|
1891
|
+
startedAt: e.startedAt
|
|
1892
|
+
}));
|
|
1893
|
+
}
|
|
1894
|
+
dispose() {
|
|
1895
|
+
for (const exec of this.running.values()) {
|
|
1896
|
+
exec.cancel();
|
|
1897
|
+
}
|
|
1898
|
+
this.running.clear();
|
|
1899
|
+
this.taskExecutions.clear();
|
|
1900
|
+
}
|
|
1901
|
+
};
|
|
1902
|
+
|
|
1527
1903
|
// src/scheduled-tasks/TaskLogManager.ts
|
|
1528
1904
|
var import_node_crypto2 = require("crypto");
|
|
1529
|
-
var
|
|
1530
|
-
var
|
|
1905
|
+
var fs8 = __toESM(require("fs"));
|
|
1906
|
+
var path7 = __toESM(require("path"));
|
|
1531
1907
|
var CONFIG_DIR4 = ".serviceme";
|
|
1532
1908
|
var LOG_FILE2 = "scheduled-tasks-log.json";
|
|
1533
1909
|
var MAX_LOGS = 200;
|
|
@@ -1552,17 +1928,17 @@ function validateAndRepairLogFile(raw) {
|
|
|
1552
1928
|
}
|
|
1553
1929
|
var TaskLogManager = class {
|
|
1554
1930
|
constructor(workspacePath) {
|
|
1555
|
-
this.logPath =
|
|
1931
|
+
this.logPath = path7.join(workspacePath, CONFIG_DIR4, LOG_FILE2);
|
|
1556
1932
|
}
|
|
1557
1933
|
getLogPath() {
|
|
1558
1934
|
return this.logPath;
|
|
1559
1935
|
}
|
|
1560
1936
|
readLogFile() {
|
|
1561
|
-
if (!
|
|
1937
|
+
if (!fs8.existsSync(this.logPath)) {
|
|
1562
1938
|
return emptyLogFile();
|
|
1563
1939
|
}
|
|
1564
1940
|
try {
|
|
1565
|
-
const raw =
|
|
1941
|
+
const raw = fs8.readFileSync(this.logPath, "utf-8");
|
|
1566
1942
|
const parsed = JSON.parse(raw);
|
|
1567
1943
|
const file = validateAndRepairLogFile(parsed);
|
|
1568
1944
|
if (!parsed || typeof parsed !== "object" || !Array.isArray(parsed.logs) || parsed.logs.length !== file.logs.length) {
|
|
@@ -1578,21 +1954,21 @@ var TaskLogManager = class {
|
|
|
1578
1954
|
}
|
|
1579
1955
|
backupCorruptedFile() {
|
|
1580
1956
|
try {
|
|
1581
|
-
if (
|
|
1957
|
+
if (fs8.existsSync(this.logPath)) {
|
|
1582
1958
|
const backupPath = `${this.logPath}.corrupted.${Date.now()}`;
|
|
1583
|
-
|
|
1959
|
+
fs8.copyFileSync(this.logPath, backupPath);
|
|
1584
1960
|
}
|
|
1585
1961
|
} catch {
|
|
1586
1962
|
}
|
|
1587
1963
|
}
|
|
1588
1964
|
writeLogFile(file) {
|
|
1589
|
-
const dir =
|
|
1590
|
-
if (!
|
|
1591
|
-
|
|
1965
|
+
const dir = path7.dirname(this.logPath);
|
|
1966
|
+
if (!fs8.existsSync(dir)) {
|
|
1967
|
+
fs8.mkdirSync(dir, { recursive: true });
|
|
1592
1968
|
}
|
|
1593
1969
|
const tmp = `${this.logPath}.tmp`;
|
|
1594
|
-
|
|
1595
|
-
|
|
1970
|
+
fs8.writeFileSync(tmp, JSON.stringify(file, null, " "), "utf-8");
|
|
1971
|
+
fs8.renameSync(tmp, this.logPath);
|
|
1596
1972
|
}
|
|
1597
1973
|
appendLog(input) {
|
|
1598
1974
|
const file = this.readLogFile();
|
|
@@ -1662,7 +2038,23 @@ var SchedulerDaemon = class {
|
|
|
1662
2038
|
this.running = true;
|
|
1663
2039
|
this.startTime = Date.now();
|
|
1664
2040
|
this.pidManager.writePid(process.pid);
|
|
1665
|
-
this.
|
|
2041
|
+
const configPath = this.configManager.getConfigPath();
|
|
2042
|
+
const logPath = this.logger.getLogPath();
|
|
2043
|
+
this.logger.log("info", "=".repeat(60));
|
|
2044
|
+
this.logger.log(
|
|
2045
|
+
"info",
|
|
2046
|
+
`Scheduler daemon started
|
|
2047
|
+
PID: ${process.pid}
|
|
2048
|
+
Node: ${process.version}
|
|
2049
|
+
OS: ${os.type()} ${os.release()} (${process.arch})
|
|
2050
|
+
Platform: ${process.platform}
|
|
2051
|
+
Hostname: ${os.hostname()}
|
|
2052
|
+
User: ${os.userInfo().username}
|
|
2053
|
+
Workspace: ${this.workspacePath}
|
|
2054
|
+
Config: ${configPath}
|
|
2055
|
+
LogPath: ${logPath}
|
|
2056
|
+
ExecPath: ${process.execPath}`
|
|
2057
|
+
);
|
|
1666
2058
|
this.tickTimer = setInterval(() => this.tick(), TICK_INTERVAL);
|
|
1667
2059
|
this.setupConfigWatch();
|
|
1668
2060
|
process.on("SIGTERM", () => this.stop());
|
|
@@ -1687,8 +2079,8 @@ var SchedulerDaemon = class {
|
|
|
1687
2079
|
const configPath = this.configManager.getConfigPath();
|
|
1688
2080
|
const dir = configPath.substring(0, configPath.lastIndexOf("/"));
|
|
1689
2081
|
try {
|
|
1690
|
-
if (
|
|
1691
|
-
this.watcher =
|
|
2082
|
+
if (fs9.existsSync(dir)) {
|
|
2083
|
+
this.watcher = fs9.watch(dir, (_eventType, filename) => {
|
|
1692
2084
|
if (filename === "scheduled-tasks.json") {
|
|
1693
2085
|
this.logger.log("info", "Config file changed, reconciling...");
|
|
1694
2086
|
}
|
|
@@ -1704,7 +2096,11 @@ var SchedulerDaemon = class {
|
|
|
1704
2096
|
for (const task of config.tasks) {
|
|
1705
2097
|
if (!task.enabled) continue;
|
|
1706
2098
|
if (this.taskRunning.has(task.id)) continue;
|
|
1707
|
-
|
|
2099
|
+
let lastExec = this.lastRun.get(task.id);
|
|
2100
|
+
if (lastExec === void 0) {
|
|
2101
|
+
this.lastRun.set(task.id, now);
|
|
2102
|
+
lastExec = now;
|
|
2103
|
+
}
|
|
1708
2104
|
if (this.shouldRun(task, lastExec, now)) {
|
|
1709
2105
|
this.executeTask(task, now);
|
|
1710
2106
|
}
|
|
@@ -1726,11 +2122,25 @@ var SchedulerDaemon = class {
|
|
|
1726
2122
|
this.taskRunning.add(task.id);
|
|
1727
2123
|
this.lastRun.set(task.id, now);
|
|
1728
2124
|
const startedAt = new Date(now).toISOString();
|
|
1729
|
-
|
|
2125
|
+
const startMs = Date.now();
|
|
2126
|
+
this.logger.log(
|
|
2127
|
+
"info",
|
|
2128
|
+
`Executing task: ${task.name} (${task.id})
|
|
2129
|
+
type: ${task.taskType}
|
|
2130
|
+
schedule: ${task.schedule} (${task.scheduleType})
|
|
2131
|
+
enabled: ${task.enabled}`
|
|
2132
|
+
);
|
|
1730
2133
|
try {
|
|
2134
|
+
const executionPayload = resolveTaskExecutionPayload(
|
|
2135
|
+
task.taskType,
|
|
2136
|
+
task.payload,
|
|
2137
|
+
this.workspacePath
|
|
2138
|
+
);
|
|
2139
|
+
validateTaskPayload(task.taskType, executionPayload);
|
|
1731
2140
|
const executor = getExecutor(task.taskType);
|
|
1732
|
-
const result = await executor.execute(
|
|
2141
|
+
const result = await executor.execute(executionPayload);
|
|
1733
2142
|
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2143
|
+
const durationMs = Date.now() - startMs;
|
|
1734
2144
|
this.logManager.appendLog({
|
|
1735
2145
|
taskId: task.id,
|
|
1736
2146
|
taskName: task.name,
|
|
@@ -1740,9 +2150,17 @@ var SchedulerDaemon = class {
|
|
|
1740
2150
|
output: result.output,
|
|
1741
2151
|
error: result.error
|
|
1742
2152
|
});
|
|
1743
|
-
|
|
2153
|
+
const outputBytes = result.output ? Buffer.byteLength(result.output) : 0;
|
|
2154
|
+
this.logger.log(
|
|
2155
|
+
"info",
|
|
2156
|
+
`Task completed: ${task.name} (${task.id})
|
|
2157
|
+
status: ${result.status}
|
|
2158
|
+
duration: ${durationMs}ms
|
|
2159
|
+
outputBytes: ${outputBytes}`
|
|
2160
|
+
);
|
|
1744
2161
|
} catch (err) {
|
|
1745
2162
|
const finishedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2163
|
+
const durationMs = Date.now() - startMs;
|
|
1746
2164
|
const message = err instanceof Error ? err.message : String(err);
|
|
1747
2165
|
this.logManager.appendLog({
|
|
1748
2166
|
taskId: task.id,
|
|
@@ -1752,7 +2170,12 @@ var SchedulerDaemon = class {
|
|
|
1752
2170
|
status: "failure",
|
|
1753
2171
|
error: message
|
|
1754
2172
|
});
|
|
1755
|
-
this.logger.log(
|
|
2173
|
+
this.logger.log(
|
|
2174
|
+
"error",
|
|
2175
|
+
`Task failed: ${task.name} (${task.id})
|
|
2176
|
+
duration: ${durationMs}ms
|
|
2177
|
+
error: ${message}`
|
|
2178
|
+
);
|
|
1756
2179
|
} finally {
|
|
1757
2180
|
this.taskRunning.delete(task.id);
|
|
1758
2181
|
}
|
|
@@ -1809,164 +2232,6 @@ function matchCronField(field, value) {
|
|
|
1809
2232
|
const values = field.split(",");
|
|
1810
2233
|
return values.some((v) => Number.parseInt(v, 10) === value);
|
|
1811
2234
|
}
|
|
1812
|
-
|
|
1813
|
-
// src/scheduled-tasks/TaskExecutionEngine.ts
|
|
1814
|
-
var TaskExecutionEngine = class {
|
|
1815
|
-
constructor(getExecutor2) {
|
|
1816
|
-
this.getExecutor = getExecutor2;
|
|
1817
|
-
this.running = /* @__PURE__ */ new Map();
|
|
1818
|
-
this.taskExecutions = /* @__PURE__ */ new Map();
|
|
1819
|
-
this.listener = null;
|
|
1820
|
-
}
|
|
1821
|
-
setListener(listener) {
|
|
1822
|
-
this.listener = listener;
|
|
1823
|
-
}
|
|
1824
|
-
async execute(snapshot) {
|
|
1825
|
-
const policy = snapshot.concurrencyPolicy ?? "reject";
|
|
1826
|
-
if (policy === "reject") {
|
|
1827
|
-
const existingIds = this.taskExecutions.get(snapshot.taskId);
|
|
1828
|
-
if (existingIds && existingIds.size > 0) {
|
|
1829
|
-
throw new Error(
|
|
1830
|
-
`TASK_ALREADY_RUNNING: Task ${snapshot.taskId} is already running`
|
|
1831
|
-
);
|
|
1832
|
-
}
|
|
1833
|
-
}
|
|
1834
|
-
const executor = this.getExecutor(snapshot.type);
|
|
1835
|
-
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
1836
|
-
if (!this.taskExecutions.has(snapshot.taskId)) {
|
|
1837
|
-
this.taskExecutions.set(snapshot.taskId, /* @__PURE__ */ new Set());
|
|
1838
|
-
}
|
|
1839
|
-
this.taskExecutions.get(snapshot.taskId)?.add(snapshot.executionId);
|
|
1840
|
-
const ac = new AbortController();
|
|
1841
|
-
const runningExec = {
|
|
1842
|
-
executionId: snapshot.executionId,
|
|
1843
|
-
taskId: snapshot.taskId,
|
|
1844
|
-
startedAt,
|
|
1845
|
-
cancel: () => ac.abort()
|
|
1846
|
-
};
|
|
1847
|
-
this.running.set(snapshot.executionId, runningExec);
|
|
1848
|
-
this.listener?.onStarted({
|
|
1849
|
-
executionId: snapshot.executionId,
|
|
1850
|
-
taskId: snapshot.taskId,
|
|
1851
|
-
startedAt
|
|
1852
|
-
});
|
|
1853
|
-
try {
|
|
1854
|
-
let result;
|
|
1855
|
-
if (isStreamingTaskExecutor(executor)) {
|
|
1856
|
-
const handle = executor.executeStreaming(
|
|
1857
|
-
snapshot.payload,
|
|
1858
|
-
(stream, data) => {
|
|
1859
|
-
this.listener?.onOutput({
|
|
1860
|
-
executionId: snapshot.executionId,
|
|
1861
|
-
stream,
|
|
1862
|
-
data
|
|
1863
|
-
});
|
|
1864
|
-
},
|
|
1865
|
-
ac.signal
|
|
1866
|
-
);
|
|
1867
|
-
runningExec.cancel = () => {
|
|
1868
|
-
handle.cancel();
|
|
1869
|
-
ac.abort();
|
|
1870
|
-
};
|
|
1871
|
-
result = await handle.result;
|
|
1872
|
-
} else {
|
|
1873
|
-
result = await executor.execute(snapshot.payload, ac.signal);
|
|
1874
|
-
}
|
|
1875
|
-
const log = {
|
|
1876
|
-
id: snapshot.executionId,
|
|
1877
|
-
taskId: snapshot.taskId,
|
|
1878
|
-
taskName: snapshot.name,
|
|
1879
|
-
startedAt,
|
|
1880
|
-
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1881
|
-
status: result.status === "running" ? "failure" : result.status,
|
|
1882
|
-
output: result.output,
|
|
1883
|
-
error: result.error
|
|
1884
|
-
};
|
|
1885
|
-
switch (log.status) {
|
|
1886
|
-
case "success":
|
|
1887
|
-
this.listener?.onCompleted({
|
|
1888
|
-
executionId: snapshot.executionId,
|
|
1889
|
-
log
|
|
1890
|
-
});
|
|
1891
|
-
break;
|
|
1892
|
-
case "cancelled":
|
|
1893
|
-
this.listener?.onCancelled({
|
|
1894
|
-
executionId: snapshot.executionId,
|
|
1895
|
-
log
|
|
1896
|
-
});
|
|
1897
|
-
break;
|
|
1898
|
-
case "timeout":
|
|
1899
|
-
this.listener?.onFailed({
|
|
1900
|
-
executionId: snapshot.executionId,
|
|
1901
|
-
status: "timeout",
|
|
1902
|
-
reason: "timeout",
|
|
1903
|
-
log
|
|
1904
|
-
});
|
|
1905
|
-
break;
|
|
1906
|
-
default:
|
|
1907
|
-
this.listener?.onFailed({
|
|
1908
|
-
executionId: snapshot.executionId,
|
|
1909
|
-
status: "failure",
|
|
1910
|
-
reason: "error",
|
|
1911
|
-
log
|
|
1912
|
-
});
|
|
1913
|
-
break;
|
|
1914
|
-
}
|
|
1915
|
-
} catch (err) {
|
|
1916
|
-
const log = {
|
|
1917
|
-
id: snapshot.executionId,
|
|
1918
|
-
taskId: snapshot.taskId,
|
|
1919
|
-
taskName: snapshot.name,
|
|
1920
|
-
startedAt,
|
|
1921
|
-
finishedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1922
|
-
status: ac.signal.aborted ? "cancelled" : "failure",
|
|
1923
|
-
error: err instanceof Error ? err.message : String(err)
|
|
1924
|
-
};
|
|
1925
|
-
if (ac.signal.aborted) {
|
|
1926
|
-
this.listener?.onCancelled({
|
|
1927
|
-
executionId: snapshot.executionId,
|
|
1928
|
-
log
|
|
1929
|
-
});
|
|
1930
|
-
} else {
|
|
1931
|
-
this.listener?.onFailed({
|
|
1932
|
-
executionId: snapshot.executionId,
|
|
1933
|
-
status: "failure",
|
|
1934
|
-
reason: "error",
|
|
1935
|
-
log
|
|
1936
|
-
});
|
|
1937
|
-
}
|
|
1938
|
-
} finally {
|
|
1939
|
-
this.running.delete(snapshot.executionId);
|
|
1940
|
-
const taskExecIds = this.taskExecutions.get(snapshot.taskId);
|
|
1941
|
-
if (taskExecIds) {
|
|
1942
|
-
taskExecIds.delete(snapshot.executionId);
|
|
1943
|
-
if (taskExecIds.size === 0) {
|
|
1944
|
-
this.taskExecutions.delete(snapshot.taskId);
|
|
1945
|
-
}
|
|
1946
|
-
}
|
|
1947
|
-
}
|
|
1948
|
-
}
|
|
1949
|
-
cancel(executionId) {
|
|
1950
|
-
const exec = this.running.get(executionId);
|
|
1951
|
-
if (!exec) return false;
|
|
1952
|
-
exec.cancel();
|
|
1953
|
-
return true;
|
|
1954
|
-
}
|
|
1955
|
-
listRunning() {
|
|
1956
|
-
return Array.from(this.running.values()).map((e) => ({
|
|
1957
|
-
executionId: e.executionId,
|
|
1958
|
-
taskId: e.taskId,
|
|
1959
|
-
startedAt: e.startedAt
|
|
1960
|
-
}));
|
|
1961
|
-
}
|
|
1962
|
-
dispose() {
|
|
1963
|
-
for (const exec of this.running.values()) {
|
|
1964
|
-
exec.cancel();
|
|
1965
|
-
}
|
|
1966
|
-
this.running.clear();
|
|
1967
|
-
this.taskExecutions.clear();
|
|
1968
|
-
}
|
|
1969
|
-
};
|
|
1970
2235
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1971
2236
|
0 && (module.exports = {
|
|
1972
2237
|
DaemonLogger,
|
|
@@ -1996,5 +2261,7 @@ var TaskExecutionEngine = class {
|
|
|
1996
2261
|
moveFiles,
|
|
1997
2262
|
noopLogger,
|
|
1998
2263
|
parseIntervalMs,
|
|
1999
|
-
|
|
2264
|
+
resolveTaskExecutionPayload,
|
|
2265
|
+
unzipFile,
|
|
2266
|
+
validateTaskPayload
|
|
2000
2267
|
});
|