@lmzhen/dsh-evolution-merge 0.30.11 → 0.30.13
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/lib/index.js +54 -27
- package/lib/types/index.js +2 -2
- package/lib/types/runtime.d.ts +1 -1
- package/lib/types/runtime.js +9 -3
- package/lib/types/scan-command.js +1 -0
- package/lib/types/scan-run.d.ts +1 -0
- package/lib/types/scan-run.js +8 -7
- package/lib/types/state.d.ts +1 -1
- package/lib/types/state.js +4 -1
- package/lib/types/target-claim.js +35 -22
- package/package.json +3 -3
package/lib/index.js
CHANGED
|
@@ -345,10 +345,17 @@ async function mutateMergeState(io, home, now, fn) {
|
|
|
345
345
|
});
|
|
346
346
|
return result;
|
|
347
347
|
}
|
|
348
|
-
async function upsertRow(io, home, now, row, expect = "any") {
|
|
348
|
+
async function upsertRow(io, home, now, row, expect = "any", cas) {
|
|
349
349
|
return await mutateMergeState(io, home, now, (rows) => {
|
|
350
350
|
const index = rows.findIndex((item) => item.pairKey === row.pairKey);
|
|
351
351
|
const current = index < 0 ? void 0 : rows[index];
|
|
352
|
+
if (cas !== void 0 && (cas === null && current !== void 0 || cas !== null && current?.updatedAt !== cas)) return {
|
|
353
|
+
rows,
|
|
354
|
+
result: {
|
|
355
|
+
ok: false,
|
|
356
|
+
current: current ?? row
|
|
357
|
+
}
|
|
358
|
+
};
|
|
352
359
|
if (current !== void 0 && expect !== "any" && (expect === "absent" || queueOf(current.state) !== expect)) return {
|
|
353
360
|
rows,
|
|
354
361
|
result: {
|
|
@@ -1789,17 +1796,20 @@ async function runScan(options) {
|
|
|
1789
1796
|
at: at(options.now)
|
|
1790
1797
|
});
|
|
1791
1798
|
if (!created.ok || created.row === null) throw new Error(created.ok ? "candidate row missing" : created.message);
|
|
1792
|
-
row = created.row
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1799
|
+
row = options.runId === void 0 ? created.row : {
|
|
1800
|
+
...created.row,
|
|
1801
|
+
runId: options.runId
|
|
1802
|
+
};
|
|
1803
|
+
const before = (await loadMergeState(options.io, options.home)).state.rows.find((item) => item.pairKey === row.pairKey);
|
|
1804
|
+
const placed = await upsertRow(options.io, options.home, options.now, row, "verify", before?.updatedAt ?? null);
|
|
1805
|
+
if (!placed.ok && before?.state === "skipped") {
|
|
1806
|
+
const restored = transition(before, {
|
|
1796
1807
|
type: "restore",
|
|
1797
1808
|
at: at(options.now)
|
|
1798
1809
|
});
|
|
1799
1810
|
if (!restored.ok || restored.row === null) throw new Error("cannot restore skipped pair");
|
|
1811
|
+
if (!(await upsertRow(options.io, options.home, options.now, restored.row, "any", before.updatedAt)).ok) throw new Error("pair changed while restoring skipped row");
|
|
1800
1812
|
row = restored.row;
|
|
1801
|
-
const retried = await upsertRow(options.io, options.home, options.now, row, "verify");
|
|
1802
|
-
if (!retried.ok) throw new Error("pair already in " + retried.current.state);
|
|
1803
1813
|
} else if (!placed.ok) throw new Error("pair already in " + placed.current.state);
|
|
1804
1814
|
} catch (error) {
|
|
1805
1815
|
totals.failed += 1;
|
|
@@ -2166,6 +2176,7 @@ async function runScanCommand(options) {
|
|
|
2166
2176
|
ports,
|
|
2167
2177
|
stage: ctx.stage,
|
|
2168
2178
|
startedAt: ctx.startedAt,
|
|
2179
|
+
runId: ctx.runId,
|
|
2169
2180
|
...options.timeoutMinutes === void 0 ? {} : { timeoutMinutes: options.timeoutMinutes },
|
|
2170
2181
|
...options.notes === void 0 ? {} : { notes: options.notes }
|
|
2171
2182
|
});
|
|
@@ -2242,37 +2253,51 @@ function releaseClaim(current, token) {
|
|
|
2242
2253
|
//#endregion
|
|
2243
2254
|
//#region lib/types/target-claim.js
|
|
2244
2255
|
/** Durable target claim: one approve writer per merge target at a time. */
|
|
2245
|
-
const LEASE_MS =
|
|
2256
|
+
const LEASE_MS = 30 * 6e4;
|
|
2246
2257
|
function claimPath(home, target) {
|
|
2247
2258
|
return join(home, "merge", "claims", contentHash(target) + ".json");
|
|
2248
2259
|
}
|
|
2249
|
-
|
|
2250
|
-
const path = claimPath(home, target);
|
|
2251
|
-
if (!await io.exists(path)) return void 0;
|
|
2252
|
-
const raw = await io.readText(path);
|
|
2260
|
+
function parseClaim(raw) {
|
|
2253
2261
|
if (raw === null) return void 0;
|
|
2254
2262
|
const parsed = JSON.parse(raw);
|
|
2255
|
-
if (typeof parsed.token !== "string" || typeof parsed.expiresAt !== "number") throw new Error("corrupt target claim
|
|
2263
|
+
if (typeof parsed.token !== "string" || typeof parsed.expiresAt !== "number") throw new Error("corrupt target claim");
|
|
2256
2264
|
return {
|
|
2257
2265
|
id: "claim",
|
|
2258
2266
|
token: parsed.token,
|
|
2259
2267
|
expiresAt: parsed.expiresAt
|
|
2260
2268
|
};
|
|
2261
2269
|
}
|
|
2270
|
+
async function compareAndSwap(io, path, task) {
|
|
2271
|
+
if (io.transact !== void 0) {
|
|
2272
|
+
await io.transact(path, (current) => task(current));
|
|
2273
|
+
return;
|
|
2274
|
+
}
|
|
2275
|
+
const current = await io.exists(path) ? await io.readText(path) : null;
|
|
2276
|
+
const next = task(current);
|
|
2277
|
+
if (next === null) {
|
|
2278
|
+
if (current !== null) try {
|
|
2279
|
+
await io.remove(path);
|
|
2280
|
+
} catch {}
|
|
2281
|
+
} else if (next !== current) await io.writeText(path, next);
|
|
2282
|
+
}
|
|
2262
2283
|
async function acquireTargetClaim(io, home, target, now) {
|
|
2263
|
-
const
|
|
2284
|
+
const path = claimPath(home, target);
|
|
2264
2285
|
const token = randomUUID();
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2286
|
+
let granted;
|
|
2287
|
+
await compareAndSwap(io, path, (raw) => {
|
|
2288
|
+
const claim = acquireClaim(parseClaim(raw), now, LEASE_MS, token);
|
|
2289
|
+
if (claim === void 0) return raw;
|
|
2290
|
+
granted = claim;
|
|
2291
|
+
return JSON.stringify(claim) + "\n";
|
|
2292
|
+
});
|
|
2293
|
+
if (granted === void 0) return null;
|
|
2268
2294
|
return {
|
|
2269
2295
|
token,
|
|
2270
2296
|
async release() {
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
}
|
|
2275
|
-
else await io.writeText(claimPath(home, target), JSON.stringify(next) + "\n");
|
|
2297
|
+
await compareAndSwap(io, path, (raw) => {
|
|
2298
|
+
const next = releaseClaim(parseClaim(raw), token);
|
|
2299
|
+
return next === void 0 ? null : JSON.stringify(next) + "\n";
|
|
2300
|
+
});
|
|
2276
2301
|
}
|
|
2277
2302
|
};
|
|
2278
2303
|
}
|
|
@@ -3780,7 +3805,7 @@ function createMergeRuntime(deps) {
|
|
|
3780
3805
|
ok: false,
|
|
3781
3806
|
text: "merge pair 不接受受保护/保护状态未知技能: " + command.a + " / " + command.b
|
|
3782
3807
|
};
|
|
3783
|
-
if (deps.isBlocked?.(command.a) === true || deps.isBlocked?.(command.b) === true) return {
|
|
3808
|
+
if (await deps.isBlocked?.(command.a) === true || await deps.isBlocked?.(command.b) === true) return {
|
|
3784
3809
|
ok: false,
|
|
3785
3810
|
text: "merge pair 不接受被范围门拦下的技能: " + command.a + " / " + command.b
|
|
3786
3811
|
};
|
|
@@ -3850,10 +3875,12 @@ function createMergeRuntime(deps) {
|
|
|
3850
3875
|
if (command.verb === "scan") {
|
|
3851
3876
|
const signal = new AbortController().signal;
|
|
3852
3877
|
const cards = await deps.cards(signal);
|
|
3853
|
-
const
|
|
3878
|
+
const blocked = /* @__PURE__ */ new Set();
|
|
3879
|
+
for (const card of cards) if (await deps.isBlocked?.(card.name) === true) blocked.add(card.name);
|
|
3880
|
+
const managed = new Set(cards.filter((card) => card.managed === true && card.protectedBy == null && !blocked.has(card.name)).map((card) => card.name));
|
|
3854
3881
|
const loadedState = await loadMergeState(deps.io, deps.home);
|
|
3855
3882
|
const skipped = skippedPairKeys(loadedState.state.rows, deps.params.mergeSkippedTtlDays, deps.now());
|
|
3856
|
-
const held = new Set(loadedState.state.rows.filter((row) => row.queue === "pending" || row.queue === "fuse" || row.queue === "verify" && !canRetry(row)).map((row) => row.pairKey));
|
|
3883
|
+
const held = new Set(loadedState.state.rows.filter((row) => row.queue === "pending" || row.queue === "fuse" || row.queue === "verify" && (row.runId !== void 0 && deps.now() - Date.parse(row.updatedAt) < 10 * 6e4 || !canRetry(row))).map((row) => row.pairKey));
|
|
3857
3884
|
const conflictNotes = loadedState.state.rows.filter((row) => row.state === "pending" || row.state === "approved" || row.state === "fuse" || row.state === "skipped").map((row) => "conflict:" + row.state + ":" + row.pairKey + ":" + row.id);
|
|
3858
3885
|
const result = await runScanCommand({
|
|
3859
3886
|
...common,
|
|
@@ -4788,8 +4815,8 @@ function apply(ctx, rawConfig = {}) {
|
|
|
4788
4815
|
parent: void 0,
|
|
4789
4816
|
allow: ["merge_stage"],
|
|
4790
4817
|
timeoutMinutes: params.mergeRunTimeoutMinutes,
|
|
4791
|
-
isBlocked: (name) => {
|
|
4792
|
-
return ctx.get("evolutionCurator")?.
|
|
4818
|
+
isBlocked: async (name) => {
|
|
4819
|
+
return await ctx.get("evolutionCurator")?.isBlocked?.(name) === true;
|
|
4793
4820
|
},
|
|
4794
4821
|
async cards(signal) {
|
|
4795
4822
|
const listed = await skills.list({ withContent: true });
|
package/lib/types/index.js
CHANGED
|
@@ -218,9 +218,9 @@ export function apply(ctx, rawConfig = {}) {
|
|
|
218
218
|
const runtime = createMergeRuntime({
|
|
219
219
|
io, home, registry: runs, skills, now: () => Date.now(), params, platformWrite: true,
|
|
220
220
|
channel, parent: undefined, allow: ['merge_stage'], timeoutMinutes: params.mergeRunTimeoutMinutes,
|
|
221
|
-
isBlocked: (name) => {
|
|
221
|
+
isBlocked: async (name) => {
|
|
222
222
|
const curator = ctx.get('evolutionCurator');
|
|
223
|
-
return curator?.
|
|
223
|
+
return (await curator?.isBlocked?.(name)) === true;
|
|
224
224
|
},
|
|
225
225
|
async cards(signal) {
|
|
226
226
|
const listed = await skills.list({ withContent: true });
|
package/lib/types/runtime.d.ts
CHANGED
|
@@ -32,7 +32,7 @@ export interface MergeRuntimeDeps {
|
|
|
32
32
|
readonly detached?: boolean;
|
|
33
33
|
readonly cards: (signal: AbortSignal) => Promise<readonly SkillCard[]>;
|
|
34
34
|
/** Curator gate set: excluded/referenced/suppressed/protected builtins. */
|
|
35
|
-
readonly isBlocked?: (name: string) => boolean
|
|
35
|
+
readonly isBlocked?: (name: string) => boolean | Promise<boolean>;
|
|
36
36
|
/** Run-scoped engine screening. */
|
|
37
37
|
readonly screeningForRun: (cards: readonly SkillCard[], runId: string, signal: AbortSignal) => Promise<readonly MergeCandidate[]>;
|
|
38
38
|
readonly stage?: MergeStageBindingPort;
|
package/lib/types/runtime.js
CHANGED
|
@@ -59,7 +59,7 @@ export function createMergeRuntime(deps) {
|
|
|
59
59
|
|| cardA.protectionUnknown === true || cardB.protectionUnknown === true) {
|
|
60
60
|
return { ok: false, text: 'merge pair 不接受受保护/保护状态未知技能: ' + command.a + ' / ' + command.b };
|
|
61
61
|
}
|
|
62
|
-
if (deps.isBlocked?.(command.a) === true || deps.isBlocked?.(command.b) === true) {
|
|
62
|
+
if ((await deps.isBlocked?.(command.a)) === true || (await deps.isBlocked?.(command.b)) === true) {
|
|
63
63
|
return { ok: false, text: 'merge pair 不接受被范围门拦下的技能: ' + command.a + ' / ' + command.b };
|
|
64
64
|
}
|
|
65
65
|
const pair = [{ a: command.a, b: command.b }];
|
|
@@ -104,12 +104,18 @@ export function createMergeRuntime(deps) {
|
|
|
104
104
|
if (command.verb === 'scan') {
|
|
105
105
|
const signal = new AbortController().signal;
|
|
106
106
|
const cards = await deps.cards(signal);
|
|
107
|
+
const blocked = new Set();
|
|
108
|
+
for (const card of cards)
|
|
109
|
+
if ((await deps.isBlocked?.(card.name)) === true)
|
|
110
|
+
blocked.add(card.name);
|
|
107
111
|
const managed = new Set(cards
|
|
108
|
-
.filter(card => card.managed === true && card.protectedBy == null &&
|
|
112
|
+
.filter(card => card.managed === true && card.protectedBy == null && !blocked.has(card.name))
|
|
109
113
|
.map(card => card.name));
|
|
110
114
|
const loadedState = await loadMergeState(deps.io, deps.home);
|
|
111
115
|
const skipped = skippedPairKeys(loadedState.state.rows, deps.params.mergeSkippedTtlDays, deps.now());
|
|
112
|
-
const held = new Set(loadedState.state.rows.filter(row => row.queue === 'pending' || row.queue === 'fuse'
|
|
116
|
+
const held = new Set(loadedState.state.rows.filter(row => row.queue === 'pending' || row.queue === 'fuse'
|
|
117
|
+
|| (row.queue === 'verify' && (row.runId !== undefined && deps.now() - Date.parse(row.updatedAt) < 10 * 60_000 || !canRetry(row))))
|
|
118
|
+
.map(row => row.pairKey));
|
|
113
119
|
const conflictNotes = loadedState.state.rows
|
|
114
120
|
.filter(row => row.state === 'pending' || row.state === 'approved' || row.state === 'fuse' || row.state === 'skipped')
|
|
115
121
|
.map(row => 'conflict:' + row.state + ':' + row.pairKey + ':' + row.id);
|
|
@@ -22,6 +22,7 @@ export async function runScanCommand(options) {
|
|
|
22
22
|
ports,
|
|
23
23
|
stage: ctx.stage,
|
|
24
24
|
startedAt: ctx.startedAt,
|
|
25
|
+
runId: ctx.runId,
|
|
25
26
|
...(options.timeoutMinutes === undefined ? {} : { timeoutMinutes: options.timeoutMinutes }),
|
|
26
27
|
...(options.notes === undefined ? {} : { notes: options.notes }),
|
|
27
28
|
});
|
package/lib/types/scan-run.d.ts
CHANGED
|
@@ -44,6 +44,7 @@ export interface RunScanOptions {
|
|
|
44
44
|
total: number;
|
|
45
45
|
}) => Promise<void>;
|
|
46
46
|
readonly notes?: readonly string[];
|
|
47
|
+
readonly runId?: string;
|
|
47
48
|
}
|
|
48
49
|
export declare function runScan(options: RunScanOptions): Promise<MergeRunReportDraft>;
|
|
49
50
|
//# sourceMappingURL=scan-run.d.ts.map
|
package/lib/types/scan-run.js
CHANGED
|
@@ -64,16 +64,17 @@ export async function runScan(options) {
|
|
|
64
64
|
const created = transition(undefined, { type: 'candidate', a: candidate.a, b: candidate.b, at: at(options.now) });
|
|
65
65
|
if (!created.ok || created.row === null)
|
|
66
66
|
throw new Error(created.ok ? 'candidate row missing' : created.message);
|
|
67
|
-
row = created.row;
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
67
|
+
row = options.runId === undefined ? created.row : { ...created.row, runId: options.runId };
|
|
68
|
+
const before = (await loadMergeState(options.io, options.home)).state.rows.find(item => item.pairKey === row.pairKey);
|
|
69
|
+
const placed = await upsertRow(options.io, options.home, options.now, row, 'verify', before?.updatedAt ?? null);
|
|
70
|
+
if (!placed.ok && before?.state === 'skipped') {
|
|
71
|
+
const restored = transition(before, { type: 'restore', at: at(options.now) });
|
|
71
72
|
if (!restored.ok || restored.row === null)
|
|
72
73
|
throw new Error('cannot restore skipped pair');
|
|
74
|
+
const persisted = await upsertRow(options.io, options.home, options.now, restored.row, 'any', before.updatedAt);
|
|
75
|
+
if (!persisted.ok)
|
|
76
|
+
throw new Error('pair changed while restoring skipped row');
|
|
73
77
|
row = restored.row;
|
|
74
|
-
const retried = await upsertRow(options.io, options.home, options.now, row, 'verify');
|
|
75
|
-
if (!retried.ok)
|
|
76
|
-
throw new Error('pair already in ' + retried.current.state);
|
|
77
78
|
}
|
|
78
79
|
else if (!placed.ok) {
|
|
79
80
|
throw new Error('pair already in ' + placed.current.state);
|
package/lib/types/state.d.ts
CHANGED
|
@@ -29,7 +29,7 @@ export type UpsertResult = {
|
|
|
29
29
|
readonly ok: false;
|
|
30
30
|
readonly current: MergeRow;
|
|
31
31
|
};
|
|
32
|
-
export declare function upsertRow(io: EvolutionIoLike, home: string, now: () => number, row: MergeRow, expect?: UpsertExpect): Promise<UpsertResult>;
|
|
32
|
+
export declare function upsertRow(io: EvolutionIoLike, home: string, now: () => number, row: MergeRow, expect?: UpsertExpect, cas?: string | null): Promise<UpsertResult>;
|
|
33
33
|
export declare function moveRow(io: EvolutionIoLike, home: string, now: () => number, id: string, patch: MoveRowPatch): Promise<MergeRow | undefined>;
|
|
34
34
|
export declare function dropRow(io: EvolutionIoLike, home: string, now: () => number, id: string): Promise<boolean>;
|
|
35
35
|
export declare function rowsOf(io: EvolutionIoLike, home: string, queue?: MergeQueue): Promise<readonly MergeRow[]>;
|
package/lib/types/state.js
CHANGED
|
@@ -55,10 +55,13 @@ export async function mutateMergeState(io, home, now, fn) {
|
|
|
55
55
|
void now;
|
|
56
56
|
return result;
|
|
57
57
|
}
|
|
58
|
-
export async function upsertRow(io, home, now, row, expect = 'any') {
|
|
58
|
+
export async function upsertRow(io, home, now, row, expect = 'any', cas) {
|
|
59
59
|
return await mutateMergeState(io, home, now, (rows) => {
|
|
60
60
|
const index = rows.findIndex(item => item.pairKey === row.pairKey);
|
|
61
61
|
const current = index < 0 ? undefined : rows[index];
|
|
62
|
+
if (cas !== undefined && ((cas === null && current !== undefined) || (cas !== null && current?.updatedAt !== cas))) {
|
|
63
|
+
return { rows, result: { ok: false, current: current ?? row } };
|
|
64
|
+
}
|
|
62
65
|
if (current !== undefined && expect !== 'any' && (expect === 'absent' || queueOf(current.state) !== expect)) {
|
|
63
66
|
return { rows, result: { ok: false, current } };
|
|
64
67
|
}
|
|
@@ -3,43 +3,56 @@ import { randomUUID } from 'node:crypto';
|
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
import { contentHash } from '@lmzhen/dsh-evolution-core';
|
|
5
5
|
import { acquireClaim, releaseClaim } from "./domain/claim.js";
|
|
6
|
-
const LEASE_MS =
|
|
6
|
+
const LEASE_MS = 30 * 60_000;
|
|
7
7
|
function claimPath(home, target) {
|
|
8
8
|
return join(home, 'merge', 'claims', contentHash(target) + '.json');
|
|
9
9
|
}
|
|
10
|
-
|
|
11
|
-
const path = claimPath(home, target);
|
|
12
|
-
if (!(await io.exists(path)))
|
|
13
|
-
return undefined;
|
|
14
|
-
const raw = await io.readText(path);
|
|
10
|
+
function parseClaim(raw) {
|
|
15
11
|
if (raw === null)
|
|
16
12
|
return undefined;
|
|
17
13
|
const parsed = JSON.parse(raw);
|
|
18
14
|
if (typeof parsed.token !== 'string' || typeof parsed.expiresAt !== 'number')
|
|
19
|
-
throw new Error('corrupt target claim
|
|
15
|
+
throw new Error('corrupt target claim');
|
|
20
16
|
return { id: 'claim', token: parsed.token, expiresAt: parsed.expiresAt };
|
|
21
17
|
}
|
|
18
|
+
async function compareAndSwap(io, path, task) {
|
|
19
|
+
if (io.transact !== undefined) {
|
|
20
|
+
await io.transact(path, current => task(current));
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const current = (await io.exists(path)) ? await io.readText(path) : null;
|
|
24
|
+
const next = task(current);
|
|
25
|
+
if (next === null) {
|
|
26
|
+
if (current !== null)
|
|
27
|
+
try {
|
|
28
|
+
await io.remove(path);
|
|
29
|
+
}
|
|
30
|
+
catch { /* already gone */ }
|
|
31
|
+
}
|
|
32
|
+
else if (next !== current) {
|
|
33
|
+
await io.writeText(path, next);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
22
36
|
export async function acquireTargetClaim(io, home, target, now) {
|
|
23
|
-
const
|
|
37
|
+
const path = claimPath(home, target);
|
|
24
38
|
const token = randomUUID();
|
|
25
|
-
|
|
26
|
-
|
|
39
|
+
let granted;
|
|
40
|
+
await compareAndSwap(io, path, (raw) => {
|
|
41
|
+
const claim = acquireClaim(parseClaim(raw), now, LEASE_MS, token);
|
|
42
|
+
if (claim === undefined)
|
|
43
|
+
return raw;
|
|
44
|
+
granted = claim;
|
|
45
|
+
return JSON.stringify(claim) + '\n';
|
|
46
|
+
});
|
|
47
|
+
if (granted === undefined)
|
|
27
48
|
return null;
|
|
28
|
-
await io.writeText(claimPath(home, target), JSON.stringify(claim) + '\n');
|
|
29
49
|
return {
|
|
30
50
|
token,
|
|
31
51
|
async release() {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
await io.remove(claimPath(home, target));
|
|
37
|
-
}
|
|
38
|
-
catch { /* already gone */ }
|
|
39
|
-
}
|
|
40
|
-
else {
|
|
41
|
-
await io.writeText(claimPath(home, target), JSON.stringify(next) + '\n');
|
|
42
|
-
}
|
|
52
|
+
await compareAndSwap(io, path, (raw) => {
|
|
53
|
+
const next = releaseClaim(parseClaim(raw), token);
|
|
54
|
+
return next === undefined ? null : JSON.stringify(next) + '\n';
|
|
55
|
+
});
|
|
43
56
|
},
|
|
44
57
|
};
|
|
45
58
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lmzhen/dsh-evolution-merge",
|
|
3
3
|
"description": "Skill auto-merge domain: screening, two-round authoring, queues and commit orchestration (community build)",
|
|
4
|
-
"version": "0.30.
|
|
4
|
+
"version": "0.30.13",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"license": "MIT",
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@deepseek-ai/schemastery": "^3.18.5",
|
|
30
|
-
"@lmzhen/dsh-evolution-core": "^0.30.
|
|
31
|
-
"@lmzhen/dsh-evolution-merge-engine": "^0.30.
|
|
30
|
+
"@lmzhen/dsh-evolution-core": "^0.30.13",
|
|
31
|
+
"@lmzhen/dsh-evolution-merge-engine": "^0.30.13"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
34
|
"@deepseek-ai/cordis": "^4.0.5"
|