opencode-supertask 0.1.40 → 0.1.41
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/CHANGELOG.md +18 -1
- package/LICENSE +21 -0
- package/README.md +10 -3
- package/README.zh-CN.md +10 -3
- package/dist/cli/index.js +365 -124
- package/dist/cli/index.js.map +1 -1
- package/dist/daemon/gateway-diagnostic-runner.d.ts +2 -0
- package/dist/daemon/gateway-diagnostic-runner.js +461 -0
- package/dist/daemon/gateway-diagnostic-runner.js.map +1 -0
- package/dist/gateway/index.js +348 -572
- package/dist/gateway/index.js.map +1 -1
- package/dist/plugin/supertask.js +53 -24
- package/dist/plugin/supertask.js.map +1 -1
- package/dist/web/index.js +231 -487
- package/dist/web/index.js.map +1 -1
- package/dist/worker/index.d.ts +8 -0
- package/dist/worker/index.js +191 -66
- package/dist/worker/index.js.map +1 -1
- package/dist/worker/launcher.js.map +1 -1
- package/package.json +12 -3
package/dist/worker/index.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ import { G as GatewayConfig } from '../config-CCUuD6Az.js';
|
|
|
3
3
|
interface WorkerEngineOptions {
|
|
4
4
|
opencodeBin?: string;
|
|
5
5
|
maxOutputChars?: number;
|
|
6
|
+
settlementRetryDelaysMs?: number[];
|
|
7
|
+
settlementRetryIntervalMs?: number;
|
|
6
8
|
}
|
|
7
9
|
interface InterruptedTaskRun {
|
|
8
10
|
taskId: number;
|
|
@@ -17,9 +19,13 @@ declare class WorkerEngine {
|
|
|
17
19
|
private pollTimer;
|
|
18
20
|
private heartbeatTimer;
|
|
19
21
|
private pollCyclePromise;
|
|
22
|
+
private shutdownDeadlineMs;
|
|
23
|
+
private settlementRetryWakeups;
|
|
20
24
|
private cfg;
|
|
21
25
|
private opencodeBin;
|
|
22
26
|
private maxOutputChars;
|
|
27
|
+
private settlementRetryDelaysMs;
|
|
28
|
+
private settlementRetryIntervalMs;
|
|
23
29
|
constructor(cfg: GatewayConfig, options?: WorkerEngineOptions);
|
|
24
30
|
start(): void;
|
|
25
31
|
stop(gracePeriodMs?: number): Promise<InterruptedTaskRun[]>;
|
|
@@ -32,6 +38,8 @@ declare class WorkerEngine {
|
|
|
32
38
|
private spawnTask;
|
|
33
39
|
private handleChildClose;
|
|
34
40
|
private settleEntry;
|
|
41
|
+
private commitEntryWithRetry;
|
|
42
|
+
private waitForSettlementRetry;
|
|
35
43
|
private commitEntry;
|
|
36
44
|
private reconcileCancelledTasks;
|
|
37
45
|
private reconcileQuarantinedTasks;
|
package/dist/worker/index.js
CHANGED
|
@@ -5538,13 +5538,23 @@ var TaskService = class {
|
|
|
5538
5538
|
});
|
|
5539
5539
|
const maxRetries = normalizedData.maxRetries ?? task.maxRetries ?? 3;
|
|
5540
5540
|
const exhausted = task.status === "failed" && (task.retryCount ?? 0) > maxRetries;
|
|
5541
|
-
|
|
5541
|
+
const updated = tx.update(tasks2).set({
|
|
5542
5542
|
...normalizedData,
|
|
5543
5543
|
...exhausted ? {
|
|
5544
5544
|
status: "dead_letter",
|
|
5545
5545
|
retryAfter: null
|
|
5546
5546
|
} : {}
|
|
5547
5547
|
}).where(eq(tasks2.id, id)).returning().get() ?? null;
|
|
5548
|
+
if (exhausted && updated) {
|
|
5549
|
+
const finishedAt = /* @__PURE__ */ new Date();
|
|
5550
|
+
tx.update(tasks2).set({
|
|
5551
|
+
status: "dead_letter",
|
|
5552
|
+
finishedAt,
|
|
5553
|
+
retryAfter: null,
|
|
5554
|
+
resultLog: `\u4F9D\u8D56\u4EFB\u52A1 #${id} \u5DF2\u8FDB\u5165\u4E0D\u53EF\u6062\u590D\u7EC8\u6001`
|
|
5555
|
+
}).where(blockedDependentsOf(id)).run();
|
|
5556
|
+
}
|
|
5557
|
+
return updated;
|
|
5548
5558
|
}, { behavior: "immediate" });
|
|
5549
5559
|
}
|
|
5550
5560
|
static validateNewTask(data) {
|
|
@@ -5565,7 +5575,7 @@ var TaskService = class {
|
|
|
5565
5575
|
throw new Error(`${name} \u5FC5\u987B\u662F ${min} \u5230 ${max} \u4E4B\u95F4\u7684\u6574\u6570`);
|
|
5566
5576
|
}
|
|
5567
5577
|
}
|
|
5568
|
-
static
|
|
5578
|
+
static buildRunnableTaskWhere(scope) {
|
|
5569
5579
|
const baseConditions = [...this.buildScopeWhere(scope)];
|
|
5570
5580
|
const nowMs = Date.now();
|
|
5571
5581
|
const retryAfterFilter = or(
|
|
@@ -5600,40 +5610,43 @@ var TaskService = class {
|
|
|
5600
5610
|
if (batchFilter) {
|
|
5601
5611
|
conditions.push(batchFilter);
|
|
5602
5612
|
}
|
|
5603
|
-
|
|
5613
|
+
return and(
|
|
5604
5614
|
...conditions,
|
|
5605
5615
|
or(
|
|
5606
5616
|
isNull(tasks2.dependsOn),
|
|
5607
5617
|
sql`EXISTS (
|
|
5608
|
-
|
|
5609
|
-
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5618
|
+
SELECT 1 FROM tasks AS dependency_task
|
|
5619
|
+
WHERE dependency_task.id = ${tasks2.dependsOn}
|
|
5620
|
+
AND dependency_task.status = 'done'
|
|
5621
|
+
AND dependency_task.cwd IS ${tasks2.cwd}
|
|
5622
|
+
)`
|
|
5613
5623
|
),
|
|
5614
5624
|
or(
|
|
5615
5625
|
isNull(tasks2.batchId),
|
|
5616
5626
|
sql`trim(${tasks2.batchId}, ${TASK_BATCH_TRIM_CHARACTERS}) = ''`,
|
|
5617
5627
|
sql`NOT EXISTS (
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
)
|
|
5628
|
+
SELECT 1 FROM tasks AS running_batch_task
|
|
5629
|
+
WHERE trim(running_batch_task.batch_id, ${TASK_BATCH_TRIM_CHARACTERS})
|
|
5630
|
+
= trim(${tasks2.batchId}, ${TASK_BATCH_TRIM_CHARACTERS})
|
|
5631
|
+
AND (
|
|
5632
|
+
running_batch_task.status = 'running'
|
|
5633
|
+
OR EXISTS (
|
|
5634
|
+
SELECT 1 FROM task_runs AS running_batch_run
|
|
5635
|
+
WHERE running_batch_run.task_id = running_batch_task.id
|
|
5636
|
+
AND running_batch_run.status = 'running'
|
|
5628
5637
|
)
|
|
5629
|
-
|
|
5638
|
+
)
|
|
5639
|
+
)`
|
|
5630
5640
|
),
|
|
5631
5641
|
sql`NOT EXISTS (
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
)
|
|
5642
|
+
SELECT 1 FROM task_runs AS candidate_active_run
|
|
5643
|
+
WHERE candidate_active_run.task_id = ${tasks2.id}
|
|
5644
|
+
AND candidate_active_run.status = 'running'
|
|
5645
|
+
)`
|
|
5646
|
+
);
|
|
5647
|
+
}
|
|
5648
|
+
static async next(scope = {}) {
|
|
5649
|
+
const result = await db.select().from(tasks2).where(this.buildRunnableTaskWhere(scope)).orderBy(
|
|
5637
5650
|
desc(tasks2.urgency),
|
|
5638
5651
|
desc(tasks2.importance),
|
|
5639
5652
|
asc(tasks2.createdAt),
|
|
@@ -5641,6 +5654,22 @@ var TaskService = class {
|
|
|
5641
5654
|
).limit(1);
|
|
5642
5655
|
return result[0] ?? null;
|
|
5643
5656
|
}
|
|
5657
|
+
static async claimNext(scope = {}) {
|
|
5658
|
+
return db.transaction((tx) => {
|
|
5659
|
+
const candidate = tx.select().from(tasks2).where(this.buildRunnableTaskWhere(scope)).orderBy(
|
|
5660
|
+
desc(tasks2.urgency),
|
|
5661
|
+
desc(tasks2.importance),
|
|
5662
|
+
asc(tasks2.createdAt),
|
|
5663
|
+
asc(tasks2.id)
|
|
5664
|
+
).limit(1).get();
|
|
5665
|
+
if (!candidate) return null;
|
|
5666
|
+
return tx.update(tasks2).set({
|
|
5667
|
+
status: "running",
|
|
5668
|
+
startedAt: /* @__PURE__ */ new Date(),
|
|
5669
|
+
finishedAt: null
|
|
5670
|
+
}).where(eq(tasks2.id, candidate.id)).returning().get() ?? null;
|
|
5671
|
+
}, { behavior: "immediate" });
|
|
5672
|
+
}
|
|
5644
5673
|
static async countRunning(scope = {}) {
|
|
5645
5674
|
const scopeConditions = scope.legacyCwd ? [sql`${tasks2.cwd} IS NULL OR trim(${tasks2.cwd}) = ''`] : this.buildScopeWhere(scope);
|
|
5646
5675
|
if (scope.batchId !== void 0) {
|
|
@@ -6273,7 +6302,6 @@ var TaskService = class {
|
|
|
6273
6302
|
};
|
|
6274
6303
|
|
|
6275
6304
|
// src/core/process-control.ts
|
|
6276
|
-
import { spawnSync } from "child_process";
|
|
6277
6305
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
6278
6306
|
import { basename, dirname as dirname2, resolve } from "path";
|
|
6279
6307
|
|
|
@@ -6298,6 +6326,84 @@ function isMatchingDrainProof(message, launchIdentity) {
|
|
|
6298
6326
|
|
|
6299
6327
|
// src/core/process-control.ts
|
|
6300
6328
|
var OS_COMMAND_TIMEOUT_MS = 2e3;
|
|
6329
|
+
var OS_COMMAND_MAX_OUTPUT_BYTES = 4 * 1024 * 1024;
|
|
6330
|
+
var OS_COMMAND_RESULT_MARKER = "\n\0supertask-os-command-result:";
|
|
6331
|
+
var BOUNDED_OS_COMMAND_RUNNER = `
|
|
6332
|
+
const { writeSync } = await import('fs');
|
|
6333
|
+
let child;
|
|
6334
|
+
try {
|
|
6335
|
+
child = Bun.spawn(process.argv.slice(2), {
|
|
6336
|
+
stdin: 'ignore',
|
|
6337
|
+
stdout: 'pipe',
|
|
6338
|
+
stderr: 'ignore',
|
|
6339
|
+
});
|
|
6340
|
+
} catch {
|
|
6341
|
+
process.exit(127);
|
|
6342
|
+
}
|
|
6343
|
+
const terminateGroup = () => {
|
|
6344
|
+
if (process.platform !== 'win32') {
|
|
6345
|
+
try {
|
|
6346
|
+
process.kill(-process.pid, 'SIGKILL');
|
|
6347
|
+
} catch {
|
|
6348
|
+
}
|
|
6349
|
+
}
|
|
6350
|
+
try {
|
|
6351
|
+
child.kill('SIGKILL');
|
|
6352
|
+
} catch {
|
|
6353
|
+
}
|
|
6354
|
+
process.exit(1);
|
|
6355
|
+
};
|
|
6356
|
+
const timer = setTimeout(terminateGroup, ${OS_COMMAND_TIMEOUT_MS});
|
|
6357
|
+
try {
|
|
6358
|
+
const chunks = [];
|
|
6359
|
+
let outputBytes = 0;
|
|
6360
|
+
const readOutput = async () => {
|
|
6361
|
+
const reader = child.stdout.getReader();
|
|
6362
|
+
while (true) {
|
|
6363
|
+
const { done, value } = await reader.read();
|
|
6364
|
+
if (done) return;
|
|
6365
|
+
outputBytes += value.byteLength;
|
|
6366
|
+
if (outputBytes > ${OS_COMMAND_MAX_OUTPUT_BYTES}) terminateGroup();
|
|
6367
|
+
chunks.push(value);
|
|
6368
|
+
}
|
|
6369
|
+
};
|
|
6370
|
+
const [exitCode] = await Promise.all([child.exited, readOutput()]);
|
|
6371
|
+
clearTimeout(timer);
|
|
6372
|
+
for (const chunk of chunks) writeSync(1, chunk);
|
|
6373
|
+
writeSync(1, ${JSON.stringify(OS_COMMAND_RESULT_MARKER)} + String(exitCode));
|
|
6374
|
+
if (process.platform !== 'win32') terminateGroup();
|
|
6375
|
+
process.exit(0);
|
|
6376
|
+
} catch {
|
|
6377
|
+
clearTimeout(timer);
|
|
6378
|
+
terminateGroup();
|
|
6379
|
+
}
|
|
6380
|
+
`;
|
|
6381
|
+
function runBoundedOsCommand(command, args, captureOutput = true) {
|
|
6382
|
+
const result = Bun.spawnSync({
|
|
6383
|
+
cmd: [
|
|
6384
|
+
process.execPath,
|
|
6385
|
+
"-e",
|
|
6386
|
+
BOUNDED_OS_COMMAND_RUNNER,
|
|
6387
|
+
"supertask-os-command",
|
|
6388
|
+
command,
|
|
6389
|
+
...args
|
|
6390
|
+
],
|
|
6391
|
+
detached: process.platform !== "win32",
|
|
6392
|
+
maxBuffer: OS_COMMAND_MAX_OUTPUT_BYTES + 1024,
|
|
6393
|
+
stdin: "ignore",
|
|
6394
|
+
stdout: "pipe",
|
|
6395
|
+
stderr: "ignore"
|
|
6396
|
+
});
|
|
6397
|
+
const output = new TextDecoder().decode(result.stdout);
|
|
6398
|
+
const markerIndex = output.lastIndexOf(OS_COMMAND_RESULT_MARKER);
|
|
6399
|
+
if (markerIndex < 0) return { status: null, stdout: "" };
|
|
6400
|
+
const statusText = output.slice(markerIndex + OS_COMMAND_RESULT_MARKER.length);
|
|
6401
|
+
if (!/^\d+$/.test(statusText)) return { status: null, stdout: "" };
|
|
6402
|
+
return {
|
|
6403
|
+
status: Number(statusText),
|
|
6404
|
+
stdout: captureOutput ? output.slice(0, markerIndex) : ""
|
|
6405
|
+
};
|
|
6406
|
+
}
|
|
6301
6407
|
function isSafePid(pid) {
|
|
6302
6408
|
return Number.isInteger(pid) && pid > 1 && pid !== process.pid;
|
|
6303
6409
|
}
|
|
@@ -6331,12 +6437,10 @@ function inspectSpawnedProcessTreePresence(pid) {
|
|
|
6331
6437
|
}
|
|
6332
6438
|
}
|
|
6333
6439
|
function inspectUnixProcess(pid) {
|
|
6334
|
-
const result =
|
|
6335
|
-
|
|
6336
|
-
|
|
6337
|
-
|
|
6338
|
-
killSignal: "SIGKILL"
|
|
6339
|
-
});
|
|
6440
|
+
const result = runBoundedOsCommand(
|
|
6441
|
+
"ps",
|
|
6442
|
+
["-o", "pgid=", "-o", "command=", "-p", String(pid)]
|
|
6443
|
+
);
|
|
6340
6444
|
if (result.status !== 0) return null;
|
|
6341
6445
|
const match = result.stdout.trim().match(/^(\d+)\s+(.+)$/s);
|
|
6342
6446
|
if (!match) return null;
|
|
@@ -6344,30 +6448,18 @@ function inspectUnixProcess(pid) {
|
|
|
6344
6448
|
}
|
|
6345
6449
|
function inspectWindowsCommand(pid) {
|
|
6346
6450
|
const script = `(Get-CimInstance Win32_Process -Filter "ProcessId = ${pid}").CommandLine`;
|
|
6347
|
-
const result =
|
|
6451
|
+
const result = runBoundedOsCommand(
|
|
6348
6452
|
"powershell.exe",
|
|
6349
|
-
["-NoProfile", "-NonInteractive", "-Command", script]
|
|
6350
|
-
{
|
|
6351
|
-
encoding: "utf8",
|
|
6352
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
6353
|
-
timeout: OS_COMMAND_TIMEOUT_MS,
|
|
6354
|
-
killSignal: "SIGKILL"
|
|
6355
|
-
}
|
|
6453
|
+
["-NoProfile", "-NonInteractive", "-Command", script]
|
|
6356
6454
|
);
|
|
6357
6455
|
if (result.status !== 0) return null;
|
|
6358
6456
|
return result.stdout.trim() || null;
|
|
6359
6457
|
}
|
|
6360
6458
|
function inspectWindowsProcessTree(rootPid) {
|
|
6361
6459
|
const script = `$root=${rootPid}; $all=@(Get-CimInstance Win32_Process | Select-Object ProcessId,ParentProcessId); $ids=New-Object 'System.Collections.Generic.HashSet[int]'; [void]$ids.Add($root); do { $added=$false; foreach($p in $all) { if($ids.Contains([int]$p.ParentProcessId) -and $ids.Add([int]$p.ProcessId)) { $added=$true } } } while($added); $all | Where-Object { $ids.Contains([int]$_.ProcessId) } | Select-Object -ExpandProperty ProcessId`;
|
|
6362
|
-
const result =
|
|
6460
|
+
const result = runBoundedOsCommand(
|
|
6363
6461
|
"powershell.exe",
|
|
6364
|
-
["-NoProfile", "-NonInteractive", "-Command", script]
|
|
6365
|
-
{
|
|
6366
|
-
encoding: "utf8",
|
|
6367
|
-
stdio: ["ignore", "pipe", "ignore"],
|
|
6368
|
-
timeout: OS_COMMAND_TIMEOUT_MS,
|
|
6369
|
-
killSignal: "SIGKILL"
|
|
6370
|
-
}
|
|
6462
|
+
["-NoProfile", "-NonInteractive", "-Command", script]
|
|
6371
6463
|
);
|
|
6372
6464
|
if (result.status !== 0) return null;
|
|
6373
6465
|
return result.stdout.split(/\s+/).filter(Boolean).map(Number).filter((pid) => Number.isInteger(pid) && pid > 0);
|
|
@@ -6424,11 +6516,7 @@ function signalRecordedProcessTreeWithResult(pid, signal, expectedExecutable, la
|
|
|
6424
6516
|
}
|
|
6425
6517
|
const args = ["/PID", String(pid), "/T"];
|
|
6426
6518
|
if (signal === "SIGKILL") args.push("/F");
|
|
6427
|
-
const status =
|
|
6428
|
-
stdio: "ignore",
|
|
6429
|
-
timeout: OS_COMMAND_TIMEOUT_MS,
|
|
6430
|
-
killSignal: "SIGKILL"
|
|
6431
|
-
}).status;
|
|
6519
|
+
const status = runBoundedOsCommand("taskkill", args, false).status;
|
|
6432
6520
|
if (status === 0) return "signalled";
|
|
6433
6521
|
return isProcessAlive(pid) ? "signal-failed" : absentLeaderResult(pid);
|
|
6434
6522
|
}
|
|
@@ -6599,7 +6687,7 @@ var TaskRunService = class {
|
|
|
6599
6687
|
}
|
|
6600
6688
|
if (current.childPid != null) {
|
|
6601
6689
|
throw new LegacyRunAbandonConflictError(
|
|
6602
|
-
`run #${runId} \u5DF2\u8BB0\u5F55 child PID ${current.childPid}\uFF0C\u5FC5\u987B\u7531 Worker/Watchdog \u786E\u8BA4\u8FDB\u7A0B\
|
|
6690
|
+
`run #${runId} \u5DF2\u8BB0\u5F55 child PID ${current.childPid}\uFF0C\u5FC5\u987B\u7531 Worker/Watchdog \u786E\u8BA4\u53D7\u7BA1\u8FDB\u7A0B\u7EC4\u6392\u7A7A`
|
|
6603
6691
|
);
|
|
6604
6692
|
}
|
|
6605
6693
|
if (current.workerPid != null && isProcessAlive(current.workerPid)) {
|
|
@@ -6688,7 +6776,7 @@ function runCommandContext(executable, args, cwd) {
|
|
|
6688
6776
|
function assertWorkerProcessIsolationSupported(platform = process.platform) {
|
|
6689
6777
|
if (platform === "win32") {
|
|
6690
6778
|
throw new Error(
|
|
6691
|
-
"Windows Worker \u5DF2\u5B89\u5168\u7981\u7528\uFF1A\u5F53\u524D\u8FD0\u884C\u65F6\u65E0\u6CD5\u7528 Job Object \
|
|
6779
|
+
"Windows Worker \u5DF2\u5B89\u5168\u7981\u7528\uFF1A\u5F53\u524D\u8FD0\u884C\u65F6\u65E0\u6CD5\u7528 Job Object \u63D0\u4F9B\u7B49\u4EF7\u7684\u53D7\u7BA1\u8FDB\u7A0B\u9694\u79BB\u4E0E\u6392\u7A7A\u8BC1\u660E"
|
|
6692
6780
|
);
|
|
6693
6781
|
}
|
|
6694
6782
|
}
|
|
@@ -6699,17 +6787,24 @@ var WorkerEngine = class {
|
|
|
6699
6787
|
pollTimer = null;
|
|
6700
6788
|
heartbeatTimer = null;
|
|
6701
6789
|
pollCyclePromise = null;
|
|
6790
|
+
shutdownDeadlineMs = null;
|
|
6791
|
+
settlementRetryWakeups = /* @__PURE__ */ new Set();
|
|
6702
6792
|
cfg;
|
|
6703
6793
|
opencodeBin;
|
|
6704
6794
|
maxOutputChars;
|
|
6795
|
+
settlementRetryDelaysMs;
|
|
6796
|
+
settlementRetryIntervalMs;
|
|
6705
6797
|
constructor(cfg, options = {}) {
|
|
6706
6798
|
this.cfg = cfg.worker;
|
|
6707
6799
|
this.opencodeBin = options.opencodeBin ?? process.env.SUPERTASK_OPENCODE_BIN ?? "opencode";
|
|
6708
6800
|
this.maxOutputChars = options.maxOutputChars ?? DEFAULT_MAX_OUTPUT_CHARS;
|
|
6801
|
+
this.settlementRetryDelaysMs = options.settlementRetryDelaysMs ?? [250, 1e3, 4e3];
|
|
6802
|
+
this.settlementRetryIntervalMs = options.settlementRetryIntervalMs ?? 5e3;
|
|
6709
6803
|
}
|
|
6710
6804
|
start() {
|
|
6711
6805
|
assertWorkerProcessIsolationSupported();
|
|
6712
6806
|
this.stopped = false;
|
|
6807
|
+
this.shutdownDeadlineMs = null;
|
|
6713
6808
|
markGatewayActivity("worker");
|
|
6714
6809
|
this.poll();
|
|
6715
6810
|
this.heartbeatTimer = setInterval(() => {
|
|
@@ -6718,6 +6813,8 @@ var WorkerEngine = class {
|
|
|
6718
6813
|
}
|
|
6719
6814
|
async stop(gracePeriodMs = 0) {
|
|
6720
6815
|
this.stopped = true;
|
|
6816
|
+
this.shutdownDeadlineMs = Date.now() + Math.max(0, gracePeriodMs);
|
|
6817
|
+
for (const wake of [...this.settlementRetryWakeups]) wake();
|
|
6721
6818
|
if (this.pollTimer) {
|
|
6722
6819
|
clearTimeout(this.pollTimer);
|
|
6723
6820
|
this.pollTimer = null;
|
|
@@ -6787,21 +6884,18 @@ var WorkerEngine = class {
|
|
|
6787
6884
|
if (databaseRunningCount >= this.cfg.maxConcurrency) break;
|
|
6788
6885
|
let task;
|
|
6789
6886
|
try {
|
|
6790
|
-
task = await TaskService.
|
|
6887
|
+
task = await TaskService.claimNext({ excludedBatchIds: [...this.activeBatchIds] });
|
|
6791
6888
|
} catch (err) {
|
|
6792
6889
|
this.logError("task claim failed", err);
|
|
6793
6890
|
throw err;
|
|
6794
6891
|
}
|
|
6795
6892
|
if (!task) break;
|
|
6796
|
-
if (this.stopped) break;
|
|
6797
|
-
if (!await TaskService.start(task.id)) continue;
|
|
6798
|
-
const batchId = normalizeTaskBatchId(task.batchId);
|
|
6799
|
-
if (batchId) this.activeBatchIds.add(batchId);
|
|
6800
6893
|
if (this.stopped) {
|
|
6801
6894
|
await TaskService.resetRunningToPending([task.id]);
|
|
6802
|
-
this.releaseBatch(task);
|
|
6803
6895
|
break;
|
|
6804
6896
|
}
|
|
6897
|
+
const batchId = normalizeTaskBatchId(task.batchId);
|
|
6898
|
+
if (batchId) this.activeBatchIds.add(batchId);
|
|
6805
6899
|
let runId = null;
|
|
6806
6900
|
try {
|
|
6807
6901
|
const launchIdentity = `gateway-${process.pid}:launch:${randomUUID()}`;
|
|
@@ -7012,7 +7106,7 @@ var WorkerEngine = class {
|
|
|
7012
7106
|
if (!entry.guardianDrained) {
|
|
7013
7107
|
const pid = entry.child.pid;
|
|
7014
7108
|
const presence = pid == null ? spawnError == null ? "unknown" : "not-running" : inspectSpawnedProcessTreePresence(pid);
|
|
7015
|
-
const message = "guardian \u672A\u63D0\u4F9B\u8FDB\u7A0B\
|
|
7109
|
+
const message = "guardian \u672A\u63D0\u4F9B\u53D7\u7BA1\u8FDB\u7A0B\u7EC4\u6392\u7A7A\u8BC1\u660E";
|
|
7016
7110
|
if (presence !== "not-running") {
|
|
7017
7111
|
if (entry.timeoutTimer) {
|
|
7018
7112
|
clearTimeout(entry.timeoutTimer);
|
|
@@ -7048,17 +7142,48 @@ var WorkerEngine = class {
|
|
|
7048
7142
|
clearTimeout(entry.timeoutTimer);
|
|
7049
7143
|
entry.timeoutTimer = null;
|
|
7050
7144
|
}
|
|
7051
|
-
const settlement = this.
|
|
7052
|
-
markGatewayFailure("worker", error);
|
|
7053
|
-
this.logError("task settlement failed", error, entry.task.id);
|
|
7054
|
-
return false;
|
|
7055
|
-
}).finally(() => {
|
|
7145
|
+
const settlement = this.commitEntryWithRetry(entry, code, failure).finally(() => {
|
|
7056
7146
|
this.runningTasks.delete(entry.task.id);
|
|
7057
7147
|
this.releaseBatch(entry.task);
|
|
7058
7148
|
});
|
|
7059
7149
|
entry.settlementPromise = settlement;
|
|
7060
7150
|
return settlement;
|
|
7061
7151
|
}
|
|
7152
|
+
async commitEntryWithRetry(entry, code, failure) {
|
|
7153
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
7154
|
+
try {
|
|
7155
|
+
await this.commitEntry(entry, code, failure);
|
|
7156
|
+
return true;
|
|
7157
|
+
} catch (error) {
|
|
7158
|
+
markGatewayFailure("worker", error);
|
|
7159
|
+
const shortRetryDelayMs = this.settlementRetryDelaysMs[attempt];
|
|
7160
|
+
let retryDelayMs = shortRetryDelayMs ?? this.settlementRetryIntervalMs;
|
|
7161
|
+
if (this.stopped) {
|
|
7162
|
+
const remainingMs = Math.max(0, (this.shutdownDeadlineMs ?? 0) - Date.now());
|
|
7163
|
+
if (remainingMs === 0) return false;
|
|
7164
|
+
retryDelayMs = Math.min(retryDelayMs, remainingMs);
|
|
7165
|
+
}
|
|
7166
|
+
this.logError(
|
|
7167
|
+
`task settlement failed; retrying in ${retryDelayMs}ms`,
|
|
7168
|
+
error,
|
|
7169
|
+
entry.task.id
|
|
7170
|
+
);
|
|
7171
|
+
await this.waitForSettlementRetry(retryDelayMs);
|
|
7172
|
+
}
|
|
7173
|
+
}
|
|
7174
|
+
}
|
|
7175
|
+
waitForSettlementRetry(delayMs) {
|
|
7176
|
+
return new Promise((resolve2) => {
|
|
7177
|
+
let timer;
|
|
7178
|
+
const finish = () => {
|
|
7179
|
+
clearTimeout(timer);
|
|
7180
|
+
this.settlementRetryWakeups.delete(finish);
|
|
7181
|
+
resolve2();
|
|
7182
|
+
};
|
|
7183
|
+
timer = setTimeout(finish, delayMs);
|
|
7184
|
+
this.settlementRetryWakeups.add(finish);
|
|
7185
|
+
});
|
|
7186
|
+
}
|
|
7062
7187
|
async commitEntry(entry, code, failure) {
|
|
7063
7188
|
const termination = entry.termination;
|
|
7064
7189
|
if (termination?.kind === "shutdown") return;
|