@ran-sh/dsh-crew 2.2.9 → 2.2.10
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/package.json
CHANGED
|
@@ -1249,10 +1249,11 @@ export function installHudSegment({ home = homedir() } = {}) {
|
|
|
1249
1249
|
|
|
1250
1250
|
// The paths Crew has recorded as its own in this Claude host: the marketplace records
|
|
1251
1251
|
// it wrote (under the current and pre-rename names, in settings and in the CLI's own
|
|
1252
|
-
// cache)
|
|
1253
|
-
//
|
|
1254
|
-
//
|
|
1255
|
-
//
|
|
1252
|
+
// cache) and the constant an older installer used. Only these are grounds for removing
|
|
1253
|
+
// anything: a directory that merely happens to be named `dsh-crew` belongs to whoever
|
|
1254
|
+
// made it, the module's own checkout is not evidence (an uninstall may run from a
|
|
1255
|
+
// checkout this host never recorded), and a path that is not recorded here is left
|
|
1256
|
+
// alone.
|
|
1256
1257
|
function crewOwnedRoots({ home, settings, markets, installed }) {
|
|
1257
1258
|
const roots = [];
|
|
1258
1259
|
const push = (value) => { if (typeof value === 'string' && value.trim() && !roots.includes(value.trim())) roots.push(value.trim()); };
|
|
@@ -1266,7 +1267,6 @@ function crewOwnedRoots({ home, settings, markets, installed }) {
|
|
|
1266
1267
|
for (const entry of entries) push(entry?.installPath);
|
|
1267
1268
|
}
|
|
1268
1269
|
push(join(home, '.config', 'dsh-crew', 'marketplace'));
|
|
1269
|
-
push(ROOT);
|
|
1270
1270
|
return roots;
|
|
1271
1271
|
}
|
|
1272
1272
|
|
|
@@ -2785,6 +2785,9 @@ export async function npxLogs({ args = [], log = console.log } = {}) {
|
|
|
2785
2785
|
const keep = Number(args[1]);
|
|
2786
2786
|
const result = pruneCrewTempLogs(Number.isInteger(keep) && keep > 0 ? { keepRuns: keep } : {});
|
|
2787
2787
|
log(`✓ Crew diagnostics pruned: removed ${result.removed.length} file(s); kept ${result.kept.length} run(s)`);
|
|
2788
|
+
if (result.failed?.length) {
|
|
2789
|
+
log(`- ${result.failed.length} file(s) could not be deleted (a running process may hold them); they are counted in neither number and the next prune retries`);
|
|
2790
|
+
}
|
|
2788
2791
|
return { ok: true, ...result };
|
|
2789
2792
|
}
|
|
2790
2793
|
|
package/src/log-prune.mjs
CHANGED
|
@@ -26,7 +26,14 @@ const LOG_FAMILIES = [
|
|
|
26
26
|
{ prefix: 'dsh-crew-dsh-crew-3210-', suffix: '.out.log', keep: 10 },
|
|
27
27
|
{ prefix: 'dsh-crew-web-', suffix: '.out.log', keep: 20 },
|
|
28
28
|
{ prefix: 'dsh-official-web-', suffix: '.out.log', keep: 20 },
|
|
29
|
-
]
|
|
29
|
+
].map((family) => ({
|
|
30
|
+
...family,
|
|
31
|
+
// An `.err.log` whose `.out.log` sibling is gone (or was never written) is still a
|
|
32
|
+
// Crew diagnostic, so each family recognizes its error files as prunable runs too.
|
|
33
|
+
errStamp: (name) => name.startsWith(family.prefix) && name.endsWith('.err.log')
|
|
34
|
+
? name.slice(family.prefix.length, name.length - '.err.log'.length) || null
|
|
35
|
+
: null,
|
|
36
|
+
}));
|
|
30
37
|
|
|
31
38
|
// `dsh-crew-<profile>-<port>-<stamp>.out.log` -> the stamp identifies one run, and
|
|
32
39
|
// its .err.log sibling must live or die with it.
|
|
@@ -51,14 +58,28 @@ export function pruneCrewTempLogs({ tempDir = tmpdir(), keepRuns = null, maxAgeD
|
|
|
51
58
|
const runs = new Map();
|
|
52
59
|
for (const name of names) {
|
|
53
60
|
const stamp = runStamp(name, family);
|
|
54
|
-
if (
|
|
61
|
+
if (stamp) {
|
|
62
|
+
const file = join(tempDir, name);
|
|
63
|
+
let mtimeMs = 0;
|
|
64
|
+
try { mtimeMs = statSync(file).mtimeMs; } catch { continue; }
|
|
65
|
+
const run = runs.get(stamp) ?? { stamp, files: [], newest: 0 };
|
|
66
|
+
run.files.push(name);
|
|
67
|
+
run.newest = Math.max(run.newest, mtimeMs);
|
|
68
|
+
runs.set(stamp, run);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
// An orphan `.err.log` (its `.out.log` sibling already pruned or never written)
|
|
72
|
+
// is still a Crew diagnostic: prune it on its own stamp so a half-written run
|
|
73
|
+
// cannot outlive every bound.
|
|
74
|
+
const errStamp = family.errStamp(name);
|
|
75
|
+
if (!errStamp) continue;
|
|
55
76
|
const file = join(tempDir, name);
|
|
56
77
|
let mtimeMs = 0;
|
|
57
78
|
try { mtimeMs = statSync(file).mtimeMs; } catch { continue; }
|
|
58
|
-
const run = runs.get(
|
|
79
|
+
const run = runs.get(errStamp) ?? { stamp: errStamp, files: [], newest: 0 };
|
|
59
80
|
run.files.push(name);
|
|
60
81
|
run.newest = Math.max(run.newest, mtimeMs);
|
|
61
|
-
runs.set(
|
|
82
|
+
runs.set(errStamp, run);
|
|
62
83
|
}
|
|
63
84
|
const ordered = [...runs.values()].sort((left, right) => right.newest - left.newest);
|
|
64
85
|
const keep = keepOverride ?? family.keep;
|
|
@@ -73,15 +94,23 @@ export function pruneCrewTempLogs({ tempDir = tmpdir(), keepRuns = null, maxAgeD
|
|
|
73
94
|
// bound exists to stop. A burst of eleven young runs still keeps ten.
|
|
74
95
|
if ((tooOld || tooMany) && !tooRecentToTouch) {
|
|
75
96
|
for (const name of run.files) removed.push(name);
|
|
76
|
-
const errName = run.files[0].replace(/\.out\.log$/, '.err.log');
|
|
77
|
-
if (names.includes(errName)) removed.push(errName);
|
|
78
97
|
} else {
|
|
79
98
|
kept.push(run.stamp);
|
|
80
99
|
}
|
|
81
100
|
});
|
|
82
101
|
}
|
|
83
|
-
|
|
84
|
-
|
|
102
|
+
// `removed` lists what is actually gone. A locked file (Windows holds a log open
|
|
103
|
+
// while its process runs) is reported in `failed` instead of pretending a deletion
|
|
104
|
+
// happened: the next pass tries again, and the operator is not told a number that
|
|
105
|
+
// is not true.
|
|
106
|
+
const failed = [];
|
|
107
|
+
for (const name of [...new Set(removed)]) {
|
|
108
|
+
try { rmSync(join(tempDir, name), { force: true }); } catch { failed.push(name); }
|
|
85
109
|
}
|
|
86
|
-
return {
|
|
110
|
+
return {
|
|
111
|
+
ok: failed.length === 0 ? true : 'partial',
|
|
112
|
+
removed: removed.filter((name) => !failed.includes(name)).sort(),
|
|
113
|
+
kept: kept.sort(),
|
|
114
|
+
...(failed.length ? { failed: failed.sort() } : {}),
|
|
115
|
+
};
|
|
87
116
|
}
|
package/src/runtime-identity.mjs
CHANGED
|
@@ -36,7 +36,7 @@ export {
|
|
|
36
36
|
// included in the identity contract.
|
|
37
37
|
const RUNTIME_ID = randomUUID();
|
|
38
38
|
|
|
39
|
-
export const RUNTIME_VERSION = '2.2.
|
|
39
|
+
export const RUNTIME_VERSION = '2.2.10';
|
|
40
40
|
export const HUB_PROTOCOL_VERSION = 1;
|
|
41
41
|
|
|
42
42
|
export const HUB_CAPABILITIES = Object.freeze([
|
|
@@ -251,7 +251,12 @@ export function reprojectRuntimeModelCallability(snapshot, { enabled_roles = {},
|
|
|
251
251
|
const roles = {};
|
|
252
252
|
for (const role of ['worker', 'reviewer']) {
|
|
253
253
|
if (enabled_roles?.[role] === false) {
|
|
254
|
-
|
|
254
|
+
// The hub may still record a selected route for a role this session disables;
|
|
255
|
+
// keeping that route here (rather than nulling it) is what lets the canonical
|
|
256
|
+
// validation below accept the reprojection. Nulling it manufactured a
|
|
257
|
+
// selection mismatch against the unchanged outer snapshot and read the whole
|
|
258
|
+
// contract as DEGRADED for an intentionally disabled role.
|
|
259
|
+
roles[role] = { state: 'NOT_APPLICABLE', reason_code: 'ROLE_DISABLED', selected: snapshot[role]?.selected ?? null, last_success: null };
|
|
255
260
|
} else if (sourceProjection.enabled_roles?.[role] === true) {
|
|
256
261
|
roles[role] = structuredClone(sourceProjection.roles[role]);
|
|
257
262
|
} else {
|
|
@@ -275,9 +280,14 @@ export function reprojectRuntimeModelCallability(snapshot, { enabled_roles = {},
|
|
|
275
280
|
projection: projected,
|
|
276
281
|
runtime: snapshot.runtime,
|
|
277
282
|
expectedEnabledRoles: projected.enabled_roles,
|
|
283
|
+
// Expected selections are the routes the snapshot RECORDED, for disabled roles
|
|
284
|
+
// too: the canonical rule is "a disabled role keeps its route", and expecting
|
|
285
|
+
// null here would contradict the very projection this function just built — the
|
|
286
|
+
// mismatch then discarded a healthy worker's evidence and read the session as
|
|
287
|
+
// degraded for a role that was intentionally switched off.
|
|
278
288
|
expectedSelections: {
|
|
279
|
-
worker:
|
|
280
|
-
reviewer:
|
|
289
|
+
worker: snapshot.worker?.selected ?? null,
|
|
290
|
+
reviewer: snapshot.reviewer?.selected ?? null,
|
|
281
291
|
},
|
|
282
292
|
now,
|
|
283
293
|
});
|