@remnic/cli 10.0.60 → 10.0.62
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/buffer-drain-worker.js +117 -0
- package/dist/chunk-SXTPIL7I.js +20038 -0
- package/dist/index.js +22 -6
- package/package.json +32 -32
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import {
|
|
2
|
+
matchesFilters,
|
|
3
|
+
sessionNamespaceResolver
|
|
4
|
+
} from "./chunk-SXTPIL7I.js";
|
|
5
|
+
|
|
6
|
+
// src/buffer-drain-worker.ts
|
|
7
|
+
import { randomUUID } from "crypto";
|
|
8
|
+
import fs from "fs";
|
|
9
|
+
import { Orchestrator, parseConfig, resolveRemnicConfigRecord } from "@remnic/core";
|
|
10
|
+
import { BufferDrainRunner } from "@remnic/core/orchestration/buffer-drain-runner.js";
|
|
11
|
+
function parseWorkerArgs(argv) {
|
|
12
|
+
const options = { concurrency: 1, sessions: [], dryRun: false, json: false };
|
|
13
|
+
let configPath = "";
|
|
14
|
+
let memoryDir = "";
|
|
15
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
16
|
+
const arg = argv[index];
|
|
17
|
+
const value = argv[index + 1];
|
|
18
|
+
if (arg === "--config" && value) {
|
|
19
|
+
configPath = value;
|
|
20
|
+
index += 1;
|
|
21
|
+
} else if (arg === "--memory-dir" && value) {
|
|
22
|
+
memoryDir = value;
|
|
23
|
+
index += 1;
|
|
24
|
+
} else if (arg === "--session" && value) {
|
|
25
|
+
options.sessions.push(value);
|
|
26
|
+
index += 1;
|
|
27
|
+
} else if (arg === "--namespace" && value) {
|
|
28
|
+
options.namespace = value;
|
|
29
|
+
index += 1;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return { configPath, memoryDir, options };
|
|
33
|
+
}
|
|
34
|
+
function emit(payload) {
|
|
35
|
+
process.stdout.write(`${JSON.stringify(payload)}
|
|
36
|
+
`);
|
|
37
|
+
}
|
|
38
|
+
async function main() {
|
|
39
|
+
const args = parseWorkerArgs(process.argv.slice(2));
|
|
40
|
+
if (!args.configPath || !args.memoryDir) {
|
|
41
|
+
emit({ type: "error", error: "drain worker requires --config and --memory-dir" });
|
|
42
|
+
return 2;
|
|
43
|
+
}
|
|
44
|
+
const raw = fs.existsSync(args.configPath) ? JSON.parse(fs.readFileSync(args.configPath, "utf8")) : {};
|
|
45
|
+
const config = parseConfig(resolveRemnicConfigRecord(raw));
|
|
46
|
+
config.memoryDir = args.memoryDir;
|
|
47
|
+
const orchestrator = new Orchestrator(config);
|
|
48
|
+
await orchestrator.initialize();
|
|
49
|
+
await orchestrator.deferredReady;
|
|
50
|
+
const runner = new BufferDrainRunner(orchestrator);
|
|
51
|
+
const ownerId = `drain-${process.pid}-${randomUUID().slice(0, 8)}`;
|
|
52
|
+
const abort = new AbortController();
|
|
53
|
+
const stop = () => {
|
|
54
|
+
abort.abort(new Error("buffer drain interrupted"));
|
|
55
|
+
};
|
|
56
|
+
process.on("SIGTERM", stop);
|
|
57
|
+
process.on("SIGINT", stop);
|
|
58
|
+
const attempted = /* @__PURE__ */ new Set();
|
|
59
|
+
let failures = 0;
|
|
60
|
+
try {
|
|
61
|
+
for (; ; ) {
|
|
62
|
+
if (abort.signal.aborted) break;
|
|
63
|
+
const state = await orchestrator.storage.loadBuffer();
|
|
64
|
+
const entries = state.entries ?? {};
|
|
65
|
+
const claimed = await runner.claims.list();
|
|
66
|
+
const resolveNamespace = sessionNamespaceResolver(config);
|
|
67
|
+
const candidates = [];
|
|
68
|
+
for (const [key, entry] of Object.entries(entries)) {
|
|
69
|
+
const turns = entry.turns.length + (entry.retainedTurns?.length ?? 0);
|
|
70
|
+
if (turns === 0) continue;
|
|
71
|
+
if (!matchesFilters(key, entry, args.options, resolveNamespace)) continue;
|
|
72
|
+
candidates.push({ key, turns });
|
|
73
|
+
}
|
|
74
|
+
for (const listed of claimed) {
|
|
75
|
+
if (candidates.some((candidate) => candidate.key === listed.key)) continue;
|
|
76
|
+
if (args.options.sessions.length > 0 && !args.options.sessions.includes(listed.key)) continue;
|
|
77
|
+
const claim = await runner.claims.load(listed.key);
|
|
78
|
+
const entry = claim?.entry ?? { turns: [], lastExtractionAt: null, extractionCount: 0 };
|
|
79
|
+
if (!matchesFilters(listed.key, entry, args.options, resolveNamespace)) continue;
|
|
80
|
+
candidates.push({ key: listed.key, turns: listed.turnCount });
|
|
81
|
+
}
|
|
82
|
+
const next = candidates.find((candidate) => !attempted.has(candidate.key) && candidate.turns > 0);
|
|
83
|
+
if (!next) break;
|
|
84
|
+
attempted.add(next.key);
|
|
85
|
+
await runner.claim(next.key, ownerId);
|
|
86
|
+
const outcome = await runner.drainSession(next.key, {
|
|
87
|
+
ownerId,
|
|
88
|
+
signal: abort.signal,
|
|
89
|
+
onEvent: (event) => {
|
|
90
|
+
if (event.type === "session_started") {
|
|
91
|
+
emit({ type: "session_started", key: event.key, turns: event.turns });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
if (outcome.status === "failed") failures += 1;
|
|
96
|
+
emit({ type: "session_finished", outcome });
|
|
97
|
+
}
|
|
98
|
+
} catch (err) {
|
|
99
|
+
emit({ type: "error", error: err instanceof Error ? err.message : String(err) });
|
|
100
|
+
failures += 1;
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
await orchestrator.destroy();
|
|
104
|
+
} catch {
|
|
105
|
+
}
|
|
106
|
+
emit({ type: "done", failures });
|
|
107
|
+
return failures > 0 ? 1 : 0;
|
|
108
|
+
}
|
|
109
|
+
main().then(
|
|
110
|
+
(code) => {
|
|
111
|
+
process.exitCode = code;
|
|
112
|
+
},
|
|
113
|
+
(err) => {
|
|
114
|
+
emit({ type: "error", error: err instanceof Error ? err.message : String(err) });
|
|
115
|
+
process.exitCode = 1;
|
|
116
|
+
}
|
|
117
|
+
);
|