@biaoo/tiangong-wiki 0.3.2 → 0.3.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/README.md +14 -0
- package/README.zh-CN.md +14 -0
- package/assets/sqlite-extensions/darwin-arm64/libsimple.dylib +0 -0
- package/assets/sqlite-extensions/darwin-x64/libsimple.dylib +0 -0
- package/assets/sqlite-extensions/linux-x64/libsimple.so +0 -0
- package/assets/sqlite-extensions/win32-x64/simple.dll +0 -0
- package/assets/wiki.config.default.json +3 -0
- package/dist/commands/rebuild-fts.js +41 -0
- package/dist/core/config.js +12 -0
- package/dist/core/db.js +92 -62
- package/dist/core/fts.js +68 -0
- package/dist/core/indexer.js +2 -2
- package/dist/core/runtime.js +1 -1
- package/dist/core/sqlite-extensions.js +78 -0
- package/dist/core/sync.js +4 -4
- package/dist/core/vault-processing.js +396 -176
- package/dist/core/vault.js +10 -0
- package/dist/daemon/server.js +51 -1
- package/dist/index.js +2 -0
- package/dist/operations/dashboard.js +5 -0
- package/dist/operations/query.js +4 -4
- package/dist/operations/write.js +74 -1
- package/package.json +1 -1
- package/references/cli-interface.md +26 -0
- package/references/troubleshooting.md +3 -1
|
@@ -9,8 +9,12 @@ import { buildVaultWorkflowPrompt, ensureWorkflowArtifactSet, getWorkflowArtifac
|
|
|
9
9
|
import { readWorkflowResult } from "./workflow-result.js";
|
|
10
10
|
import { AppError } from "../utils/errors.js";
|
|
11
11
|
import { readTextFileSync } from "../utils/fs.js";
|
|
12
|
-
import { toOffsetIso } from "../utils/time.js";
|
|
12
|
+
import { addSeconds, toOffsetIso } from "../utils/time.js";
|
|
13
13
|
const INLINE_WORKFLOW_ATTEMPTS = 2;
|
|
14
|
+
const MAX_QUEUE_ERROR_RETRIES = 3;
|
|
15
|
+
const QUEUE_FULL_RETRY_DELAY_SECONDS = 300;
|
|
16
|
+
const WORKFLOW_TIMEOUT_RETRY_DELAY_SECONDS = 120;
|
|
17
|
+
const NON_RETRYABLE_QUEUE_ERROR_CODES = new Set(["config_error", "invalid_request"]);
|
|
14
18
|
function buildFileIdFilterClause(filterFileIds) {
|
|
15
19
|
if (!filterFileIds || filterFileIds.length === 0) {
|
|
16
20
|
return { clause: "", params: [] };
|
|
@@ -20,6 +24,16 @@ function buildFileIdFilterClause(filterFileIds) {
|
|
|
20
24
|
params: filterFileIds,
|
|
21
25
|
};
|
|
22
26
|
}
|
|
27
|
+
function buildExcludedFileIdClause(excludedFileIds) {
|
|
28
|
+
const params = Array.from(excludedFileIds ?? []).filter((value) => value.trim().length > 0);
|
|
29
|
+
if (params.length === 0) {
|
|
30
|
+
return { clause: "", params: [] };
|
|
31
|
+
}
|
|
32
|
+
return {
|
|
33
|
+
clause: ` AND vault_processing_queue.file_id NOT IN (${params.map(() => "?").join(", ")})`,
|
|
34
|
+
params,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
23
37
|
function parseOptionalStringArray(value) {
|
|
24
38
|
if (Array.isArray(value)) {
|
|
25
39
|
return value
|
|
@@ -43,9 +57,11 @@ function parseOptionalStringArray(value) {
|
|
|
43
57
|
}
|
|
44
58
|
}
|
|
45
59
|
function mapQueueRow(row) {
|
|
60
|
+
const attempts = Number(row.attempts ?? 0);
|
|
61
|
+
const status = row.status;
|
|
46
62
|
return {
|
|
47
63
|
fileId: String(row.fileId),
|
|
48
|
-
status
|
|
64
|
+
status,
|
|
49
65
|
priority: Number(row.priority ?? 0),
|
|
50
66
|
queuedAt: String(row.queuedAt),
|
|
51
67
|
claimedAt: typeof row.claimedAt === "string" ? row.claimedAt : null,
|
|
@@ -53,13 +69,15 @@ function mapQueueRow(row) {
|
|
|
53
69
|
processedAt: typeof row.processedAt === "string" ? row.processedAt : null,
|
|
54
70
|
resultPageId: typeof row.resultPageId === "string" ? row.resultPageId : null,
|
|
55
71
|
errorMessage: typeof row.errorMessage === "string" ? row.errorMessage : null,
|
|
56
|
-
attempts
|
|
72
|
+
attempts,
|
|
57
73
|
threadId: typeof row.threadId === "string" ? row.threadId : null,
|
|
58
74
|
workflowVersion: typeof row.workflowVersion === "string" ? row.workflowVersion : null,
|
|
59
75
|
decision: typeof row.decision === "string" ? row.decision : null,
|
|
60
76
|
resultManifestPath: typeof row.resultManifestPath === "string" ? row.resultManifestPath : null,
|
|
61
77
|
lastErrorAt: typeof row.lastErrorAt === "string" ? row.lastErrorAt : null,
|
|
78
|
+
lastErrorCode: typeof row.lastErrorCode === "string" ? row.lastErrorCode : null,
|
|
62
79
|
retryAfter: typeof row.retryAfter === "string" ? row.retryAfter : null,
|
|
80
|
+
autoRetryExhausted: status === "error" && attempts > MAX_QUEUE_ERROR_RETRIES,
|
|
63
81
|
createdPageIds: parseOptionalStringArray(row.createdPageIds),
|
|
64
82
|
updatedPageIds: parseOptionalStringArray(row.updatedPageIds),
|
|
65
83
|
appliedTypeNames: parseOptionalStringArray(row.appliedTypeNames),
|
|
@@ -72,8 +90,20 @@ function mapQueueRow(row) {
|
|
|
72
90
|
filePath: typeof row.filePath === "string" ? row.filePath : undefined,
|
|
73
91
|
};
|
|
74
92
|
}
|
|
75
|
-
function claimQueueItems(db, limit,
|
|
76
|
-
const filter = buildFileIdFilterClause(filterFileIds);
|
|
93
|
+
function claimQueueItems(db, limit, options = {}) {
|
|
94
|
+
const filter = buildFileIdFilterClause(options.filterFileIds);
|
|
95
|
+
const exclude = buildExcludedFileIdClause(options.excludeFileIds);
|
|
96
|
+
const manualClaim = Boolean(options.filterFileIds && options.filterFileIds.length > 0);
|
|
97
|
+
const errorEligibility = manualClaim
|
|
98
|
+
? "vault_processing_queue.status = 'error'"
|
|
99
|
+
: [
|
|
100
|
+
"vault_processing_queue.status = 'error'",
|
|
101
|
+
`vault_processing_queue.attempts <= ${MAX_QUEUE_ERROR_RETRIES}`,
|
|
102
|
+
`COALESCE(vault_processing_queue.last_error_code, '') NOT IN (${Array.from(NON_RETRYABLE_QUEUE_ERROR_CODES)
|
|
103
|
+
.map((code) => `'${code}'`)
|
|
104
|
+
.join(", ")})`,
|
|
105
|
+
"(vault_processing_queue.retry_after IS NULL OR julianday(vault_processing_queue.retry_after) <= julianday(?))",
|
|
106
|
+
].join("\n AND ");
|
|
77
107
|
const select = db.prepare(`
|
|
78
108
|
SELECT
|
|
79
109
|
file_id AS fileId,
|
|
@@ -91,6 +121,7 @@ function claimQueueItems(db, limit, filterFileIds) {
|
|
|
91
121
|
decision,
|
|
92
122
|
result_manifest_path AS resultManifestPath,
|
|
93
123
|
last_error_at AS lastErrorAt,
|
|
124
|
+
last_error_code AS lastErrorCode,
|
|
94
125
|
retry_after AS retryAfter,
|
|
95
126
|
created_page_ids AS createdPageIds,
|
|
96
127
|
updated_page_ids AS updatedPageIds,
|
|
@@ -104,7 +135,12 @@ function claimQueueItems(db, limit, filterFileIds) {
|
|
|
104
135
|
vault_files.file_path AS filePath
|
|
105
136
|
FROM vault_processing_queue
|
|
106
137
|
LEFT JOIN vault_files ON vault_files.id = vault_processing_queue.file_id
|
|
107
|
-
WHERE
|
|
138
|
+
WHERE (
|
|
139
|
+
vault_processing_queue.status = 'pending'
|
|
140
|
+
OR (
|
|
141
|
+
${errorEligibility}
|
|
142
|
+
)
|
|
143
|
+
)${filter.clause}${exclude.clause}
|
|
108
144
|
ORDER BY priority DESC, queued_at ASC
|
|
109
145
|
LIMIT ?
|
|
110
146
|
`);
|
|
@@ -114,12 +150,16 @@ function claimQueueItems(db, limit, filterFileIds) {
|
|
|
114
150
|
status = 'processing',
|
|
115
151
|
claimed_at = @claimed_at,
|
|
116
152
|
started_at = @started_at,
|
|
117
|
-
error_message = NULL
|
|
153
|
+
error_message = NULL,
|
|
154
|
+
retry_after = NULL
|
|
118
155
|
WHERE file_id = @file_id AND status IN ('pending', 'error')
|
|
119
156
|
`);
|
|
120
157
|
return db.transaction((claimLimit, claimFilterParams) => {
|
|
121
158
|
const startedAt = toOffsetIso();
|
|
122
|
-
const
|
|
159
|
+
const selectParams = manualClaim
|
|
160
|
+
? [...claimFilterParams, claimLimit]
|
|
161
|
+
: [startedAt, ...claimFilterParams, claimLimit];
|
|
162
|
+
const items = select.all(...selectParams).map(mapQueueRow);
|
|
123
163
|
for (const item of items) {
|
|
124
164
|
markProcessing.run({
|
|
125
165
|
file_id: item.fileId,
|
|
@@ -132,7 +172,7 @@ function claimQueueItems(db, limit, filterFileIds) {
|
|
|
132
172
|
claimedAt: startedAt,
|
|
133
173
|
startedAt,
|
|
134
174
|
}));
|
|
135
|
-
})(limit, filter.params);
|
|
175
|
+
})(limit, [...filter.params, ...exclude.params]);
|
|
136
176
|
}
|
|
137
177
|
function fetchQueueItemsByStatus(db, status) {
|
|
138
178
|
const rows = db.prepare(`
|
|
@@ -152,6 +192,7 @@ function fetchQueueItemsByStatus(db, status) {
|
|
|
152
192
|
decision,
|
|
153
193
|
result_manifest_path AS resultManifestPath,
|
|
154
194
|
last_error_at AS lastErrorAt,
|
|
195
|
+
last_error_code AS lastErrorCode,
|
|
155
196
|
retry_after AS retryAfter,
|
|
156
197
|
created_page_ids AS createdPageIds,
|
|
157
198
|
updated_page_ids AS updatedPageIds,
|
|
@@ -188,6 +229,7 @@ function fetchQueueItemByFileId(db, fileId) {
|
|
|
188
229
|
decision,
|
|
189
230
|
result_manifest_path AS resultManifestPath,
|
|
190
231
|
last_error_at AS lastErrorAt,
|
|
232
|
+
last_error_code AS lastErrorCode,
|
|
191
233
|
retry_after AS retryAfter,
|
|
192
234
|
created_page_ids AS createdPageIds,
|
|
193
235
|
updated_page_ids AS updatedPageIds,
|
|
@@ -262,10 +304,112 @@ function serializeArray(value) {
|
|
|
262
304
|
function formatManifestLogFields(manifest) {
|
|
263
305
|
return `decision=${manifest.decision} skills=${manifest.skillsUsed.join(",") || "-"} created=${manifest.createdPageIds.join(",") || "-"} updated=${manifest.updatedPageIds.join(",") || "-"} proposed=${manifest.proposedTypes.map((item) => item.name).join(",") || "-"}`;
|
|
264
306
|
}
|
|
265
|
-
function
|
|
307
|
+
function extractErrorDetailsCode(error) {
|
|
308
|
+
if (!(error instanceof AppError)) {
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
311
|
+
if (typeof error.details !== "object" || error.details === null || Array.isArray(error.details)) {
|
|
312
|
+
return null;
|
|
313
|
+
}
|
|
314
|
+
const code = error.details.code;
|
|
315
|
+
return typeof code === "string" && code.trim() ? code.trim() : null;
|
|
316
|
+
}
|
|
317
|
+
function inferWorkflowErrorCode(message) {
|
|
318
|
+
const normalized = message.toLowerCase();
|
|
319
|
+
if (normalized.includes("queue_full") || normalized.includes("write queue is full")) {
|
|
320
|
+
return "queue_full";
|
|
321
|
+
}
|
|
322
|
+
if (normalized.includes("timed out")) {
|
|
323
|
+
return "workflow_timeout";
|
|
324
|
+
}
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
327
|
+
function buildRetryAfter(seconds) {
|
|
328
|
+
return toOffsetIso(addSeconds(new Date(), seconds));
|
|
329
|
+
}
|
|
330
|
+
function buildQueueFailureState(message, options = {}) {
|
|
331
|
+
const inferredCode = inferWorkflowErrorCode(message);
|
|
332
|
+
const errorCode = inferredCode ?? options.explicitCode ?? (options.errorType === "config" ? "config_error" : null);
|
|
333
|
+
if (errorCode && NON_RETRYABLE_QUEUE_ERROR_CODES.has(errorCode)) {
|
|
334
|
+
return {
|
|
335
|
+
errorCode,
|
|
336
|
+
retryAfter: null,
|
|
337
|
+
autoRetryEligible: false,
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
if (errorCode === "queue_full") {
|
|
341
|
+
return {
|
|
342
|
+
errorCode,
|
|
343
|
+
retryAfter: buildRetryAfter(QUEUE_FULL_RETRY_DELAY_SECONDS),
|
|
344
|
+
autoRetryEligible: true,
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
if (errorCode === "workflow_timeout") {
|
|
348
|
+
return {
|
|
349
|
+
errorCode,
|
|
350
|
+
retryAfter: buildRetryAfter(WORKFLOW_TIMEOUT_RETRY_DELAY_SECONDS),
|
|
351
|
+
autoRetryEligible: true,
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
return {
|
|
355
|
+
errorCode,
|
|
356
|
+
retryAfter: null,
|
|
357
|
+
autoRetryEligible: true,
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
function formatQueueErrorMessage(message, autoRetryExhausted) {
|
|
361
|
+
const autoRetrySuffix = autoRetryExhausted
|
|
362
|
+
? ` Auto retry limit reached after ${MAX_QUEUE_ERROR_RETRIES} retries; use manual retry or requeue after the vault file changes.`
|
|
363
|
+
: "";
|
|
364
|
+
return `${message}${autoRetrySuffix}`.slice(0, 1_000);
|
|
365
|
+
}
|
|
366
|
+
function applyWorkflowManifest(db, fileId, manifest, resultManifestPath, currentAttempts) {
|
|
266
367
|
const resultPageId = manifest.createdPageIds[0] ?? manifest.updatedPageIds[0] ?? null;
|
|
267
368
|
const status = manifest.status;
|
|
268
369
|
const processedAt = toOffsetIso();
|
|
370
|
+
if (status === "error") {
|
|
371
|
+
const failureState = buildQueueFailureState(manifest.reason);
|
|
372
|
+
const nextAttempts = currentAttempts + 1;
|
|
373
|
+
const autoRetryExhausted = failureState.autoRetryEligible && nextAttempts > MAX_QUEUE_ERROR_RETRIES;
|
|
374
|
+
db.prepare(`
|
|
375
|
+
UPDATE vault_processing_queue
|
|
376
|
+
SET
|
|
377
|
+
status = 'error',
|
|
378
|
+
processed_at = @processed_at,
|
|
379
|
+
result_page_id = @result_page_id,
|
|
380
|
+
error_message = @error_message,
|
|
381
|
+
attempts = attempts + 1,
|
|
382
|
+
workflow_version = @workflow_version,
|
|
383
|
+
decision = @decision,
|
|
384
|
+
result_manifest_path = @result_manifest_path,
|
|
385
|
+
last_error_at = @last_error_at,
|
|
386
|
+
last_error_code = @last_error_code,
|
|
387
|
+
retry_after = @retry_after,
|
|
388
|
+
created_page_ids = @created_page_ids,
|
|
389
|
+
updated_page_ids = @updated_page_ids,
|
|
390
|
+
applied_type_names = @applied_type_names,
|
|
391
|
+
proposed_type_names = @proposed_type_names,
|
|
392
|
+
skills_used = @skills_used
|
|
393
|
+
WHERE file_id = @file_id
|
|
394
|
+
`).run({
|
|
395
|
+
file_id: fileId,
|
|
396
|
+
processed_at: processedAt,
|
|
397
|
+
result_page_id: resultPageId,
|
|
398
|
+
error_message: formatQueueErrorMessage(manifest.reason, autoRetryExhausted),
|
|
399
|
+
workflow_version: CODEX_WORKFLOW_VERSION,
|
|
400
|
+
decision: manifest.decision,
|
|
401
|
+
result_manifest_path: resultManifestPath,
|
|
402
|
+
last_error_at: processedAt,
|
|
403
|
+
last_error_code: failureState.errorCode,
|
|
404
|
+
retry_after: autoRetryExhausted ? null : failureState.retryAfter,
|
|
405
|
+
created_page_ids: serializeArray(manifest.createdPageIds),
|
|
406
|
+
updated_page_ids: serializeArray(manifest.updatedPageIds),
|
|
407
|
+
applied_type_names: serializeArray(manifest.appliedTypeNames),
|
|
408
|
+
proposed_type_names: serializeArray(manifest.proposedTypes.map((item) => item.name)),
|
|
409
|
+
skills_used: serializeArray(manifest.skillsUsed),
|
|
410
|
+
});
|
|
411
|
+
return { status, pageId: resultPageId };
|
|
412
|
+
}
|
|
269
413
|
db.prepare(`
|
|
270
414
|
UPDATE vault_processing_queue
|
|
271
415
|
SET
|
|
@@ -277,6 +421,7 @@ function applyWorkflowManifest(db, fileId, manifest, resultManifestPath) {
|
|
|
277
421
|
decision = @decision,
|
|
278
422
|
result_manifest_path = @result_manifest_path,
|
|
279
423
|
last_error_at = NULL,
|
|
424
|
+
last_error_code = NULL,
|
|
280
425
|
retry_after = NULL,
|
|
281
426
|
created_page_ids = @created_page_ids,
|
|
282
427
|
updated_page_ids = @updated_page_ids,
|
|
@@ -425,16 +570,20 @@ function updateQueueWorkflowError(db, fileId, payload) {
|
|
|
425
570
|
thread_id = COALESCE(@thread_id, thread_id),
|
|
426
571
|
workflow_version = @workflow_version,
|
|
427
572
|
result_manifest_path = COALESCE(@result_manifest_path, result_manifest_path),
|
|
428
|
-
last_error_at = @last_error_at
|
|
573
|
+
last_error_at = @last_error_at,
|
|
574
|
+
last_error_code = @last_error_code,
|
|
575
|
+
retry_after = @retry_after
|
|
429
576
|
WHERE file_id = @file_id
|
|
430
577
|
`).run({
|
|
431
578
|
file_id: fileId,
|
|
432
579
|
processed_at: processedAt,
|
|
433
|
-
error_message: payload.errorMessage
|
|
580
|
+
error_message: formatQueueErrorMessage(payload.errorMessage, payload.autoRetryExhausted === true),
|
|
434
581
|
thread_id: payload.threadId ?? null,
|
|
435
582
|
workflow_version: CODEX_WORKFLOW_VERSION,
|
|
436
583
|
result_manifest_path: payload.resultManifestPath ?? null,
|
|
437
584
|
last_error_at: processedAt,
|
|
585
|
+
last_error_code: payload.errorCode ?? null,
|
|
586
|
+
retry_after: payload.autoRetryExhausted ? null : payload.retryAfter ?? null,
|
|
438
587
|
});
|
|
439
588
|
}
|
|
440
589
|
function prepareCodexWorkflowInput(paths, item, file, localFilePath, env, allowTemplateEvolution) {
|
|
@@ -484,10 +633,189 @@ function prepareCodexWorkflowInput(paths, item, file, localFilePath, env, allowT
|
|
|
484
633
|
},
|
|
485
634
|
};
|
|
486
635
|
}
|
|
636
|
+
async function processClaimedQueueItem(input) {
|
|
637
|
+
const { db, env, paths, item, workflowRunner, templateEvolution, maxWorkflowAttempts, workflowTimeoutMs } = input;
|
|
638
|
+
input.log?.(`${item.fileId}: start processing attempt=${item.attempts + 1} queuedAt=${item.queuedAt} thread=${item.threadId ?? "-"}`);
|
|
639
|
+
const file = fetchVaultFile(db, item.fileId);
|
|
640
|
+
if (!file) {
|
|
641
|
+
updateQueueStatus(db, item.fileId, {
|
|
642
|
+
status: "error",
|
|
643
|
+
processedAt: toOffsetIso(),
|
|
644
|
+
errorMessage: `Vault file missing from index: ${item.fileId}`,
|
|
645
|
+
incrementAttempts: true,
|
|
646
|
+
});
|
|
647
|
+
input.log?.(`${item.fileId}: error thread=- result=- message=Vault file missing from index`);
|
|
648
|
+
return {
|
|
649
|
+
status: "error",
|
|
650
|
+
item: {
|
|
651
|
+
fileId: item.fileId,
|
|
652
|
+
status: "error",
|
|
653
|
+
reason: "Vault file missing from index",
|
|
654
|
+
},
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
let threadId = item.threadId ?? null;
|
|
658
|
+
let resultManifestPath = null;
|
|
659
|
+
try {
|
|
660
|
+
const localFilePath = await ensureLocalVaultFile(file, paths.vaultPath, env);
|
|
661
|
+
const { artifacts, input: workflowInput } = prepareCodexWorkflowInput(paths, item, file, localFilePath, env, templateEvolution.canApply);
|
|
662
|
+
resultManifestPath = artifacts.resultPath;
|
|
663
|
+
let finalOutcome = null;
|
|
664
|
+
let lastWorkflowError;
|
|
665
|
+
for (let attempt = 1; attempt <= maxWorkflowAttempts; attempt += 1) {
|
|
666
|
+
try {
|
|
667
|
+
const mode = threadId ? "resume" : "start";
|
|
668
|
+
const workflowController = new AbortController();
|
|
669
|
+
let loggedStartedThreadId = null;
|
|
670
|
+
const attemptInput = {
|
|
671
|
+
...workflowInput,
|
|
672
|
+
signal: workflowController.signal,
|
|
673
|
+
onThreadStarted: (startedThreadId) => {
|
|
674
|
+
if (loggedStartedThreadId === startedThreadId) {
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
loggedStartedThreadId = startedThreadId;
|
|
678
|
+
threadId = startedThreadId;
|
|
679
|
+
updateQueueWorkflowTracking(db, item.fileId, {
|
|
680
|
+
threadId: startedThreadId,
|
|
681
|
+
resultManifestPath: artifacts.resultPath,
|
|
682
|
+
});
|
|
683
|
+
input.log?.(`${item.fileId}: workflow started mode=${mode} attempt=${attempt}/${maxWorkflowAttempts} thread=${startedThreadId} result=${artifacts.resultPath}`);
|
|
684
|
+
},
|
|
685
|
+
};
|
|
686
|
+
input.log?.(`${item.fileId}: launching workflow mode=${mode} attempt=${attempt}/${maxWorkflowAttempts} timeout=${Math.ceil(workflowTimeoutMs / 1000)}s result=${artifacts.resultPath}`);
|
|
687
|
+
const handle = threadId
|
|
688
|
+
? await runWithWorkflowTimeout("resumeWorkflow", workflowTimeoutMs, workflowController, () => workflowRunner.resumeWorkflow(threadId, attemptInput))
|
|
689
|
+
: await runWithWorkflowTimeout("startWorkflow", workflowTimeoutMs, workflowController, () => workflowRunner.startWorkflow(attemptInput));
|
|
690
|
+
threadId = handle.threadId;
|
|
691
|
+
if (loggedStartedThreadId !== handle.threadId) {
|
|
692
|
+
loggedStartedThreadId = handle.threadId;
|
|
693
|
+
input.log?.(`${item.fileId}: workflow started mode=${mode} attempt=${attempt}/${maxWorkflowAttempts} thread=${handle.threadId} result=${artifacts.resultPath}`);
|
|
694
|
+
}
|
|
695
|
+
updateQueueWorkflowTracking(db, item.fileId, {
|
|
696
|
+
threadId: handle.threadId,
|
|
697
|
+
resultManifestPath: artifacts.resultPath,
|
|
698
|
+
});
|
|
699
|
+
input.log?.(`${item.fileId}: waiting for workflow result thread=${handle.threadId} attempt=${attempt}/${maxWorkflowAttempts} result=${artifacts.resultPath}`);
|
|
700
|
+
const collectController = new AbortController();
|
|
701
|
+
const manifest = await runWithWorkflowTimeout("collectResult", workflowTimeoutMs, collectController, () => workflowRunner.collectResult(handle, {
|
|
702
|
+
...workflowInput,
|
|
703
|
+
signal: collectController.signal,
|
|
704
|
+
}));
|
|
705
|
+
assertTemplateEvolutionAllowed(manifest, templateEvolution);
|
|
706
|
+
finalOutcome = {
|
|
707
|
+
outcome: applyWorkflowManifest(db, item.fileId, manifest, artifacts.resultPath, item.attempts),
|
|
708
|
+
manifest,
|
|
709
|
+
handleThreadId: handle.threadId,
|
|
710
|
+
};
|
|
711
|
+
break;
|
|
712
|
+
}
|
|
713
|
+
catch (error) {
|
|
714
|
+
lastWorkflowError = error;
|
|
715
|
+
threadId = readPersistedWorkflowThreadId(artifacts.queueItemPath) ?? threadId;
|
|
716
|
+
if (threadId) {
|
|
717
|
+
updateQueueWorkflowTracking(db, item.fileId, {
|
|
718
|
+
threadId,
|
|
719
|
+
resultManifestPath: artifacts.resultPath,
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
const recoveredManifest = shouldAttemptManifestRecovery(error)
|
|
723
|
+
? readRecoverableWorkflowResult(artifacts.resultPath, threadId)
|
|
724
|
+
: null;
|
|
725
|
+
if (recoveredManifest) {
|
|
726
|
+
assertTemplateEvolutionAllowed(recoveredManifest, templateEvolution);
|
|
727
|
+
finalOutcome = {
|
|
728
|
+
outcome: applyWorkflowManifest(db, item.fileId, recoveredManifest, artifacts.resultPath, item.attempts),
|
|
729
|
+
manifest: recoveredManifest,
|
|
730
|
+
handleThreadId: recoveredManifest.threadId,
|
|
731
|
+
};
|
|
732
|
+
input.log?.(`${item.fileId}: recovered persisted workflow result status=${recoveredManifest.status} thread=${recoveredManifest.threadId} ${formatManifestLogFields(recoveredManifest)} result=${artifacts.resultPath} message=${formatWorkflowError(error)}`);
|
|
733
|
+
break;
|
|
734
|
+
}
|
|
735
|
+
if (!shouldRetryWorkflowAttempt(error, attempt, maxWorkflowAttempts)) {
|
|
736
|
+
throw error;
|
|
737
|
+
}
|
|
738
|
+
input.log?.(`${item.fileId}: retrying workflow attempt ${attempt + 1}/${maxWorkflowAttempts} thread=${threadId ?? "-"} result=${artifacts.resultPath} message=${formatWorkflowError(error)}`);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
if (!finalOutcome) {
|
|
742
|
+
throw (lastWorkflowError ?? new AppError("Workflow completed without a result", "runtime"));
|
|
743
|
+
}
|
|
744
|
+
input.log?.(`${item.fileId}: ${finalOutcome.outcome.status} thread=${finalOutcome.handleThreadId} ${formatManifestLogFields(finalOutcome.manifest)} result=${artifacts.resultPath}`);
|
|
745
|
+
return {
|
|
746
|
+
status: finalOutcome.outcome.status,
|
|
747
|
+
item: {
|
|
748
|
+
fileId: item.fileId,
|
|
749
|
+
status: finalOutcome.outcome.status,
|
|
750
|
+
pageId: finalOutcome.outcome.pageId,
|
|
751
|
+
reason: finalOutcome.manifest.reason,
|
|
752
|
+
threadId: finalOutcome.handleThreadId,
|
|
753
|
+
decision: finalOutcome.manifest.decision,
|
|
754
|
+
skillsUsed: finalOutcome.manifest.skillsUsed,
|
|
755
|
+
createdPageIds: finalOutcome.manifest.createdPageIds,
|
|
756
|
+
updatedPageIds: finalOutcome.manifest.updatedPageIds,
|
|
757
|
+
proposedTypeNames: finalOutcome.manifest.proposedTypes.map((entry) => entry.name),
|
|
758
|
+
resultManifestPath: artifacts.resultPath,
|
|
759
|
+
},
|
|
760
|
+
};
|
|
761
|
+
}
|
|
762
|
+
catch (error) {
|
|
763
|
+
const recoveredManifest = shouldAttemptManifestRecovery(error)
|
|
764
|
+
? readRecoverableWorkflowResult(resultManifestPath, threadId)
|
|
765
|
+
: null;
|
|
766
|
+
if (recoveredManifest && resultManifestPath) {
|
|
767
|
+
assertTemplateEvolutionAllowed(recoveredManifest, templateEvolution);
|
|
768
|
+
const recoveredOutcome = applyWorkflowManifest(db, item.fileId, recoveredManifest, resultManifestPath, item.attempts);
|
|
769
|
+
input.log?.(`${item.fileId}: recovered persisted workflow result after terminal failure status=${recoveredOutcome.status} thread=${recoveredManifest.threadId} ${formatManifestLogFields(recoveredManifest)} result=${resultManifestPath} message=${formatWorkflowError(error)}`);
|
|
770
|
+
return {
|
|
771
|
+
status: recoveredOutcome.status,
|
|
772
|
+
item: {
|
|
773
|
+
fileId: item.fileId,
|
|
774
|
+
status: recoveredOutcome.status,
|
|
775
|
+
pageId: recoveredOutcome.pageId,
|
|
776
|
+
reason: recoveredManifest.reason,
|
|
777
|
+
threadId: recoveredManifest.threadId,
|
|
778
|
+
decision: recoveredManifest.decision,
|
|
779
|
+
skillsUsed: recoveredManifest.skillsUsed,
|
|
780
|
+
createdPageIds: recoveredManifest.createdPageIds,
|
|
781
|
+
updatedPageIds: recoveredManifest.updatedPageIds,
|
|
782
|
+
proposedTypeNames: recoveredManifest.proposedTypes.map((entry) => entry.name),
|
|
783
|
+
resultManifestPath,
|
|
784
|
+
},
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
const message = formatWorkflowError(error);
|
|
788
|
+
const failureState = buildQueueFailureState(message, {
|
|
789
|
+
explicitCode: extractErrorDetailsCode(error),
|
|
790
|
+
errorType: error instanceof AppError ? error.type : null,
|
|
791
|
+
});
|
|
792
|
+
const autoRetryExhausted = failureState.autoRetryEligible && item.attempts >= MAX_QUEUE_ERROR_RETRIES;
|
|
793
|
+
updateQueueWorkflowError(db, item.fileId, {
|
|
794
|
+
errorMessage: message,
|
|
795
|
+
errorCode: failureState.errorCode,
|
|
796
|
+
retryAfter: failureState.retryAfter,
|
|
797
|
+
threadId,
|
|
798
|
+
resultManifestPath,
|
|
799
|
+
autoRetryExhausted,
|
|
800
|
+
});
|
|
801
|
+
input.log?.(`${item.fileId}: error thread=${threadId ?? "-"} result=${resultManifestPath ?? "-"} message=${message}${autoRetryExhausted ? ` autoRetryLimit=${MAX_QUEUE_ERROR_RETRIES}` : ""}`);
|
|
802
|
+
return {
|
|
803
|
+
status: "error",
|
|
804
|
+
item: {
|
|
805
|
+
fileId: item.fileId,
|
|
806
|
+
status: "error",
|
|
807
|
+
pageId: item.resultPageId ?? null,
|
|
808
|
+
reason: message,
|
|
809
|
+
threadId,
|
|
810
|
+
resultManifestPath,
|
|
811
|
+
},
|
|
812
|
+
};
|
|
813
|
+
}
|
|
814
|
+
}
|
|
487
815
|
export function getVaultQueueSnapshot(env = process.env, status) {
|
|
488
816
|
const paths = resolveRuntimePaths(env);
|
|
489
817
|
const config = loadConfig(paths.configPath);
|
|
490
|
-
const { db } = openDb(paths.dbPath, config, Number.parseInt(env.EMBEDDING_DIMENSIONS ?? "384", 10) || 384);
|
|
818
|
+
const { db } = openDb(paths.dbPath, config, Number.parseInt(env.EMBEDDING_DIMENSIONS ?? "384", 10) || 384, paths.packageRoot);
|
|
491
819
|
try {
|
|
492
820
|
const items = fetchQueueItemsByStatus(db, status);
|
|
493
821
|
const counts = db.prepare(`
|
|
@@ -515,7 +843,7 @@ export function getVaultQueueSnapshot(env = process.env, status) {
|
|
|
515
843
|
export function getVaultQueueItem(env = process.env, fileId) {
|
|
516
844
|
const paths = resolveRuntimePaths(env);
|
|
517
845
|
const config = loadConfig(paths.configPath);
|
|
518
|
-
const { db } = openDb(paths.dbPath, config, Number.parseInt(env.EMBEDDING_DIMENSIONS ?? "384", 10) || 384);
|
|
846
|
+
const { db } = openDb(paths.dbPath, config, Number.parseInt(env.EMBEDDING_DIMENSIONS ?? "384", 10) || 384, paths.packageRoot);
|
|
519
847
|
try {
|
|
520
848
|
return fetchQueueItemByFileId(db, fileId);
|
|
521
849
|
}
|
|
@@ -537,9 +865,8 @@ export async function processVaultQueueBatch(env = process.env, options = {}) {
|
|
|
537
865
|
}
|
|
538
866
|
const paths = resolveRuntimePaths(env);
|
|
539
867
|
const config = loadConfig(paths.configPath);
|
|
540
|
-
const { db } = openDb(paths.dbPath, config, Number.parseInt(env.EMBEDDING_DIMENSIONS ?? "384", 10) || 384);
|
|
868
|
+
const { db } = openDb(paths.dbPath, config, Number.parseInt(env.EMBEDDING_DIMENSIONS ?? "384", 10) || 384, paths.packageRoot);
|
|
541
869
|
try {
|
|
542
|
-
const items = claimQueueItems(db, options.maxItems ?? agentSettings.batchSize, options.filterFileIds);
|
|
543
870
|
const result = {
|
|
544
871
|
enabled: true,
|
|
545
872
|
processed: 0,
|
|
@@ -552,9 +879,10 @@ export async function processVaultQueueBatch(env = process.env, options = {}) {
|
|
|
552
879
|
const templateEvolution = resolveTemplateEvolutionSettings(env);
|
|
553
880
|
const maxWorkflowAttempts = isInlineRetryCapable(workflowRunner) ? INLINE_WORKFLOW_ATTEMPTS : 1;
|
|
554
881
|
const workflowTimeoutMs = agentSettings.workflowTimeoutSeconds * 1000;
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
882
|
+
const workerSlots = Math.max(0, options.maxItems ?? agentSettings.batchSize);
|
|
883
|
+
const attemptedFileIds = new Set();
|
|
884
|
+
const orderedItems = [];
|
|
885
|
+
let nextSequence = 0;
|
|
558
886
|
const countOutcome = (status) => {
|
|
559
887
|
if (status === "done") {
|
|
560
888
|
result.done += 1;
|
|
@@ -567,169 +895,61 @@ export async function processVaultQueueBatch(env = process.env, options = {}) {
|
|
|
567
895
|
}
|
|
568
896
|
result.processed += 1;
|
|
569
897
|
};
|
|
570
|
-
|
|
571
|
-
options.
|
|
572
|
-
|
|
573
|
-
if (!file) {
|
|
574
|
-
updateQueueStatus(db, item.fileId, {
|
|
575
|
-
status: "error",
|
|
576
|
-
processedAt: toOffsetIso(),
|
|
577
|
-
errorMessage: `Vault file missing from index: ${item.fileId}`,
|
|
578
|
-
incrementAttempts: true,
|
|
579
|
-
});
|
|
580
|
-
countOutcome("error");
|
|
581
|
-
options.log?.(`${item.fileId}: error thread=- result=- message=Vault file missing from index`);
|
|
582
|
-
result.items.push({
|
|
583
|
-
fileId: item.fileId,
|
|
584
|
-
status: "error",
|
|
585
|
-
reason: "Vault file missing from index",
|
|
586
|
-
});
|
|
587
|
-
continue;
|
|
898
|
+
const claimNextQueueItem = () => {
|
|
899
|
+
if (options.shouldStop?.() === true) {
|
|
900
|
+
return null;
|
|
588
901
|
}
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
const localFilePath = await ensureLocalVaultFile(file, paths.vaultPath, env);
|
|
593
|
-
const { artifacts, input } = prepareCodexWorkflowInput(paths, item, file, localFilePath, env, templateEvolution.canApply);
|
|
594
|
-
resultManifestPath = artifacts.resultPath;
|
|
595
|
-
let finalOutcome = null;
|
|
596
|
-
let lastWorkflowError;
|
|
597
|
-
for (let attempt = 1; attempt <= maxWorkflowAttempts; attempt += 1) {
|
|
598
|
-
try {
|
|
599
|
-
const mode = threadId ? "resume" : "start";
|
|
600
|
-
const workflowController = new AbortController();
|
|
601
|
-
let loggedStartedThreadId = null;
|
|
602
|
-
const attemptInput = {
|
|
603
|
-
...input,
|
|
604
|
-
signal: workflowController.signal,
|
|
605
|
-
onThreadStarted: (startedThreadId) => {
|
|
606
|
-
if (loggedStartedThreadId === startedThreadId) {
|
|
607
|
-
return;
|
|
608
|
-
}
|
|
609
|
-
loggedStartedThreadId = startedThreadId;
|
|
610
|
-
threadId = startedThreadId;
|
|
611
|
-
updateQueueWorkflowTracking(db, item.fileId, {
|
|
612
|
-
threadId: startedThreadId,
|
|
613
|
-
resultManifestPath: artifacts.resultPath,
|
|
614
|
-
});
|
|
615
|
-
options.log?.(`${item.fileId}: workflow started mode=${mode} attempt=${attempt}/${maxWorkflowAttempts} thread=${startedThreadId} result=${artifacts.resultPath}`);
|
|
616
|
-
},
|
|
617
|
-
};
|
|
618
|
-
options.log?.(`${item.fileId}: launching workflow mode=${mode} attempt=${attempt}/${maxWorkflowAttempts} timeout=${agentSettings.workflowTimeoutSeconds}s result=${artifacts.resultPath}`);
|
|
619
|
-
const handle = threadId
|
|
620
|
-
? await runWithWorkflowTimeout("resumeWorkflow", workflowTimeoutMs, workflowController, () => workflowRunner.resumeWorkflow(threadId, attemptInput))
|
|
621
|
-
: await runWithWorkflowTimeout("startWorkflow", workflowTimeoutMs, workflowController, () => workflowRunner.startWorkflow(attemptInput));
|
|
622
|
-
threadId = handle.threadId;
|
|
623
|
-
if (loggedStartedThreadId !== handle.threadId) {
|
|
624
|
-
loggedStartedThreadId = handle.threadId;
|
|
625
|
-
options.log?.(`${item.fileId}: workflow started mode=${mode} attempt=${attempt}/${maxWorkflowAttempts} thread=${handle.threadId} result=${artifacts.resultPath}`);
|
|
626
|
-
}
|
|
627
|
-
updateQueueWorkflowTracking(db, item.fileId, {
|
|
628
|
-
threadId: handle.threadId,
|
|
629
|
-
resultManifestPath: artifacts.resultPath,
|
|
630
|
-
});
|
|
631
|
-
options.log?.(`${item.fileId}: waiting for workflow result thread=${handle.threadId} attempt=${attempt}/${maxWorkflowAttempts} result=${artifacts.resultPath}`);
|
|
632
|
-
const collectController = new AbortController();
|
|
633
|
-
const manifest = await runWithWorkflowTimeout("collectResult", workflowTimeoutMs, collectController, () => workflowRunner.collectResult(handle, {
|
|
634
|
-
...input,
|
|
635
|
-
signal: collectController.signal,
|
|
636
|
-
}));
|
|
637
|
-
assertTemplateEvolutionAllowed(manifest, templateEvolution);
|
|
638
|
-
finalOutcome = {
|
|
639
|
-
outcome: applyWorkflowManifest(db, item.fileId, manifest, artifacts.resultPath),
|
|
640
|
-
manifest,
|
|
641
|
-
handleThreadId: handle.threadId,
|
|
642
|
-
};
|
|
643
|
-
break;
|
|
644
|
-
}
|
|
645
|
-
catch (error) {
|
|
646
|
-
lastWorkflowError = error;
|
|
647
|
-
threadId = readPersistedWorkflowThreadId(artifacts.queueItemPath) ?? threadId;
|
|
648
|
-
if (threadId) {
|
|
649
|
-
updateQueueWorkflowTracking(db, item.fileId, {
|
|
650
|
-
threadId,
|
|
651
|
-
resultManifestPath: artifacts.resultPath,
|
|
652
|
-
});
|
|
653
|
-
}
|
|
654
|
-
const recoveredManifest = shouldAttemptManifestRecovery(error)
|
|
655
|
-
? readRecoverableWorkflowResult(artifacts.resultPath, threadId)
|
|
656
|
-
: null;
|
|
657
|
-
if (recoveredManifest) {
|
|
658
|
-
assertTemplateEvolutionAllowed(recoveredManifest, templateEvolution);
|
|
659
|
-
finalOutcome = {
|
|
660
|
-
outcome: applyWorkflowManifest(db, item.fileId, recoveredManifest, artifacts.resultPath),
|
|
661
|
-
manifest: recoveredManifest,
|
|
662
|
-
handleThreadId: recoveredManifest.threadId,
|
|
663
|
-
};
|
|
664
|
-
options.log?.(`${item.fileId}: recovered persisted workflow result status=${recoveredManifest.status} thread=${recoveredManifest.threadId} ${formatManifestLogFields(recoveredManifest)} result=${artifacts.resultPath} message=${formatWorkflowError(error)}`);
|
|
665
|
-
break;
|
|
666
|
-
}
|
|
667
|
-
if (!shouldRetryWorkflowAttempt(error, attempt, maxWorkflowAttempts)) {
|
|
668
|
-
throw error;
|
|
669
|
-
}
|
|
670
|
-
options.log?.(`${item.fileId}: retrying workflow attempt ${attempt + 1}/${maxWorkflowAttempts} thread=${threadId ?? "-"} result=${artifacts.resultPath} message=${formatWorkflowError(error)}`);
|
|
671
|
-
}
|
|
672
|
-
}
|
|
673
|
-
if (!finalOutcome) {
|
|
674
|
-
throw (lastWorkflowError ?? new AppError("Workflow completed without a result", "runtime"));
|
|
675
|
-
}
|
|
676
|
-
options.log?.(`${item.fileId}: ${finalOutcome.outcome.status} thread=${finalOutcome.handleThreadId} ${formatManifestLogFields(finalOutcome.manifest)} result=${artifacts.resultPath}`);
|
|
677
|
-
countOutcome(finalOutcome.outcome.status);
|
|
678
|
-
result.items.push({
|
|
679
|
-
fileId: item.fileId,
|
|
680
|
-
status: finalOutcome.outcome.status,
|
|
681
|
-
pageId: finalOutcome.outcome.pageId,
|
|
682
|
-
reason: finalOutcome.manifest.reason,
|
|
683
|
-
threadId: finalOutcome.handleThreadId,
|
|
684
|
-
decision: finalOutcome.manifest.decision,
|
|
685
|
-
skillsUsed: finalOutcome.manifest.skillsUsed,
|
|
686
|
-
createdPageIds: finalOutcome.manifest.createdPageIds,
|
|
687
|
-
updatedPageIds: finalOutcome.manifest.updatedPageIds,
|
|
688
|
-
proposedTypeNames: finalOutcome.manifest.proposedTypes.map((entry) => entry.name),
|
|
689
|
-
resultManifestPath: artifacts.resultPath,
|
|
690
|
-
});
|
|
902
|
+
const remainingFilterFileIds = options.filterFileIds?.filter((fileId) => !attemptedFileIds.has(fileId));
|
|
903
|
+
if (options.filterFileIds && remainingFilterFileIds?.length === 0) {
|
|
904
|
+
return null;
|
|
691
905
|
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
continue;
|
|
906
|
+
const item = claimQueueItems(db, 1, {
|
|
907
|
+
filterFileIds: remainingFilterFileIds,
|
|
908
|
+
excludeFileIds: attemptedFileIds,
|
|
909
|
+
})[0];
|
|
910
|
+
if (!item) {
|
|
911
|
+
return null;
|
|
912
|
+
}
|
|
913
|
+
attemptedFileIds.add(item.fileId);
|
|
914
|
+
options.log?.(`claimed 1 items: ${item.fileId}`);
|
|
915
|
+
return {
|
|
916
|
+
sequence: nextSequence++,
|
|
917
|
+
item,
|
|
918
|
+
};
|
|
919
|
+
};
|
|
920
|
+
const workerCount = options.filterFileIds
|
|
921
|
+
? Math.min(workerSlots, options.filterFileIds.length)
|
|
922
|
+
: workerSlots;
|
|
923
|
+
const workers = Array.from({ length: workerCount }, async () => {
|
|
924
|
+
while (true) {
|
|
925
|
+
const claimed = claimNextQueueItem();
|
|
926
|
+
if (!claimed) {
|
|
927
|
+
return;
|
|
715
928
|
}
|
|
716
|
-
const
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
929
|
+
const processed = await processClaimedQueueItem({
|
|
930
|
+
db,
|
|
931
|
+
env,
|
|
932
|
+
paths,
|
|
933
|
+
item: claimed.item,
|
|
934
|
+
log: options.log,
|
|
935
|
+
workflowRunner,
|
|
936
|
+
templateEvolution,
|
|
937
|
+
maxWorkflowAttempts,
|
|
938
|
+
workflowTimeoutMs,
|
|
721
939
|
});
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
status: "error",
|
|
727
|
-
pageId: item.resultPageId ?? null,
|
|
728
|
-
reason: message,
|
|
729
|
-
threadId,
|
|
730
|
-
resultManifestPath,
|
|
940
|
+
countOutcome(processed.status);
|
|
941
|
+
orderedItems.push({
|
|
942
|
+
sequence: claimed.sequence,
|
|
943
|
+
item: processed.item,
|
|
731
944
|
});
|
|
732
945
|
}
|
|
946
|
+
});
|
|
947
|
+
await Promise.all(workers);
|
|
948
|
+
result.items = orderedItems
|
|
949
|
+
.sort((left, right) => left.sequence - right.sequence)
|
|
950
|
+
.map((entry) => entry.item);
|
|
951
|
+
if (result.items.length > 0) {
|
|
952
|
+
options.log?.(`processed ${result.items.length} queue items with workerPool=${workerCount}`);
|
|
733
953
|
}
|
|
734
954
|
return result;
|
|
735
955
|
}
|