chatccc 0.2.260 → 0.2.261
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/src/engines/engine-manager.js +54 -11
- package/dist/src/web-ui.js +1 -1
- package/package.json +1 -1
|
@@ -17,15 +17,21 @@ export const DEFAULT_ENGINE_ROOT = join(homedir(), ".chatccc", "engines");
|
|
|
17
17
|
export function createCoalescedAsyncTask(task) {
|
|
18
18
|
let requested = false;
|
|
19
19
|
let active = null;
|
|
20
|
+
let failed = false;
|
|
21
|
+
let failure;
|
|
20
22
|
const start = () => {
|
|
21
|
-
if (active)
|
|
23
|
+
if (active || failed)
|
|
22
24
|
return;
|
|
23
25
|
active = (async () => {
|
|
24
26
|
while (requested) {
|
|
25
27
|
requested = false;
|
|
26
28
|
await task();
|
|
27
29
|
}
|
|
28
|
-
})().
|
|
30
|
+
})().catch((error) => {
|
|
31
|
+
failed = true;
|
|
32
|
+
failure = error;
|
|
33
|
+
requested = false;
|
|
34
|
+
}).finally(() => {
|
|
29
35
|
active = null;
|
|
30
36
|
if (requested)
|
|
31
37
|
start();
|
|
@@ -37,13 +43,32 @@ export function createCoalescedAsyncTask(task) {
|
|
|
37
43
|
start();
|
|
38
44
|
},
|
|
39
45
|
async flush() {
|
|
40
|
-
|
|
41
|
-
|
|
46
|
+
if (!failed) {
|
|
47
|
+
requested = true;
|
|
48
|
+
start();
|
|
49
|
+
}
|
|
42
50
|
while (active)
|
|
43
51
|
await active;
|
|
52
|
+
if (failed)
|
|
53
|
+
throw failure;
|
|
44
54
|
},
|
|
45
55
|
};
|
|
46
56
|
}
|
|
57
|
+
async function renameWithTransientRetry(source, destination) {
|
|
58
|
+
const retriable = new Set(["EPERM", "EBUSY", "EACCES"]);
|
|
59
|
+
for (let attempt = 0;; attempt += 1) {
|
|
60
|
+
try {
|
|
61
|
+
await rename(source, destination);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
const code = error.code ?? "";
|
|
66
|
+
if (!retriable.has(code) || attempt >= 5)
|
|
67
|
+
throw error;
|
|
68
|
+
await new Promise((resolvePromise) => setTimeout(resolvePromise, 20 * (2 ** attempt)));
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
47
72
|
export function resolveNpmInvocation() {
|
|
48
73
|
const candidates = [
|
|
49
74
|
process.env.npm_execpath,
|
|
@@ -63,6 +88,7 @@ export class EngineManager {
|
|
|
63
88
|
verifyRuntime;
|
|
64
89
|
nodeVersion;
|
|
65
90
|
activeInstalls = new Map();
|
|
91
|
+
persistTails = new Map();
|
|
66
92
|
constructor(options) {
|
|
67
93
|
this.rootDir = options.rootDir ?? DEFAULT_ENGINE_ROOT;
|
|
68
94
|
for (const spec of options.specs)
|
|
@@ -208,7 +234,7 @@ export class EngineManager {
|
|
|
208
234
|
const pointerPath = join(this.engineDir(spec), "current.json");
|
|
209
235
|
const temporaryPointer = `${pointerPath}.${job.jobId}.tmp`;
|
|
210
236
|
await writeFile(temporaryPointer, JSON.stringify(pointer, null, 2) + "\n", "utf8");
|
|
211
|
-
await
|
|
237
|
+
await renameWithTransientRetry(temporaryPointer, pointerPath);
|
|
212
238
|
await update(100, `已切换到 v${spec.version}`);
|
|
213
239
|
});
|
|
214
240
|
await this.runStep(spec, job, "cleanup", async (update) => {
|
|
@@ -275,10 +301,27 @@ export class EngineManager {
|
|
|
275
301
|
}
|
|
276
302
|
async persistJob(spec, job) {
|
|
277
303
|
const path = this.jobPath(spec);
|
|
278
|
-
|
|
279
|
-
const
|
|
280
|
-
|
|
281
|
-
|
|
304
|
+
const contents = JSON.stringify(job, null, 2) + "\n";
|
|
305
|
+
const previous = this.persistTails.get(spec.id) ?? Promise.resolve();
|
|
306
|
+
const operation = previous.catch(() => { }).then(async () => {
|
|
307
|
+
await mkdir(dirname(path), { recursive: true });
|
|
308
|
+
const temporary = `${path}.${process.pid}.${randomUUID()}.tmp`;
|
|
309
|
+
try {
|
|
310
|
+
await writeFile(temporary, contents, "utf8");
|
|
311
|
+
await renameWithTransientRetry(temporary, path);
|
|
312
|
+
}
|
|
313
|
+
finally {
|
|
314
|
+
await rm(temporary, { force: true }).catch(() => { });
|
|
315
|
+
}
|
|
316
|
+
});
|
|
317
|
+
this.persistTails.set(spec.id, operation);
|
|
318
|
+
try {
|
|
319
|
+
await operation;
|
|
320
|
+
}
|
|
321
|
+
finally {
|
|
322
|
+
if (this.persistTails.get(spec.id) === operation)
|
|
323
|
+
this.persistTails.delete(spec.id);
|
|
324
|
+
}
|
|
282
325
|
}
|
|
283
326
|
async readJob(spec) {
|
|
284
327
|
try {
|
|
@@ -359,12 +402,12 @@ async function defaultInstallPackages(installationDir, spec, onProgress) {
|
|
|
359
402
|
});
|
|
360
403
|
child.once("close", (code) => {
|
|
361
404
|
clearInterval(timer);
|
|
362
|
-
void progressReporter.flush().
|
|
405
|
+
void progressReporter.flush().then(() => {
|
|
363
406
|
if (code === 0)
|
|
364
407
|
resolvePromise();
|
|
365
408
|
else
|
|
366
409
|
reject(new Error(`npm install 失败(退出码 ${String(code)}):${stderr.trim().split(/\r?\n/).slice(-6).join(" | ").slice(0, 1000)}`));
|
|
367
|
-
});
|
|
410
|
+
}).catch(reject);
|
|
368
411
|
});
|
|
369
412
|
});
|
|
370
413
|
}
|
package/dist/src/web-ui.js
CHANGED
|
@@ -2492,7 +2492,7 @@ function engineRefreshStatus(engineId) {
|
|
|
2492
2492
|
renderEngineStatus(engineId, status);
|
|
2493
2493
|
if (status.running || (status.job && status.job.state === 'running')) scheduleEnginePoll(engineId);
|
|
2494
2494
|
return status;
|
|
2495
|
-
});
|
|
2495
|
+
}).catch(function(){ scheduleEnginePoll(engineId); });
|
|
2496
2496
|
}
|
|
2497
2497
|
|
|
2498
2498
|
function installEngine(engineId) {
|