@remnic/cli 9.49.0 → 9.50.0
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/index.js +796 -166
- package/package.json +29 -29
package/dist/index.js
CHANGED
|
@@ -19,8 +19,8 @@ async function persistEnrichmentCandidate(storage, entityName, candidate) {
|
|
|
19
19
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
import fs13 from "fs";
|
|
22
|
-
import
|
|
23
|
-
import
|
|
22
|
+
import os2 from "os";
|
|
23
|
+
import path16 from "path";
|
|
24
24
|
import { createHash as createHash4 } from "crypto";
|
|
25
25
|
import * as childProcess2 from "child_process";
|
|
26
26
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
@@ -1213,13 +1213,13 @@ async function computeConvergePlan(options = {}) {
|
|
|
1213
1213
|
files,
|
|
1214
1214
|
parseMemory: parseFrontmatter,
|
|
1215
1215
|
readFile: async (file) => {
|
|
1216
|
-
const
|
|
1217
|
-
if (!
|
|
1216
|
+
const readFile3 = io.readFile;
|
|
1217
|
+
if (!readFile3) {
|
|
1218
1218
|
manifestReadFailed = true;
|
|
1219
1219
|
throw new Error("offline storage cannot read reconciliation manifest files");
|
|
1220
1220
|
}
|
|
1221
1221
|
try {
|
|
1222
|
-
return await
|
|
1222
|
+
return await readFile3({
|
|
1223
1223
|
root: rootInfo.rootDir,
|
|
1224
1224
|
path: file.path,
|
|
1225
1225
|
filePath: path2.join(rootInfo.rootDir, file.path)
|
|
@@ -1589,7 +1589,7 @@ async function executeConvergeApply(options = {}) {
|
|
|
1589
1589
|
if (current.sha256 !== entry.localSha256) {
|
|
1590
1590
|
throw new Error(`local file changed during push: ${localPath}`);
|
|
1591
1591
|
}
|
|
1592
|
-
const
|
|
1592
|
+
const stat2 = await fs4.promises.stat(filePath);
|
|
1593
1593
|
let chunks;
|
|
1594
1594
|
let chunkOffset = 0;
|
|
1595
1595
|
const resetChunks = async () => {
|
|
@@ -1608,7 +1608,7 @@ async function executeConvergeApply(options = {}) {
|
|
|
1608
1608
|
source = {
|
|
1609
1609
|
sha256: entry.localSha256,
|
|
1610
1610
|
bytes: current.bytes,
|
|
1611
|
-
mtimeMs:
|
|
1611
|
+
mtimeMs: stat2.mtimeMs,
|
|
1612
1612
|
...expectedPeerSha256 ? { baseSha256: expectedPeerSha256 } : {},
|
|
1613
1613
|
readChunk: async (offset, length) => {
|
|
1614
1614
|
if (!chunks || offset < chunkOffset) await resetChunks();
|
|
@@ -2309,21 +2309,21 @@ function newestMtime(roots) {
|
|
|
2309
2309
|
if (!existsSync2(entryPath)) {
|
|
2310
2310
|
return;
|
|
2311
2311
|
}
|
|
2312
|
-
const
|
|
2313
|
-
if (
|
|
2312
|
+
const stat2 = lstatSync(entryPath);
|
|
2313
|
+
if (stat2.isSymbolicLink()) {
|
|
2314
2314
|
return;
|
|
2315
2315
|
}
|
|
2316
|
-
if (
|
|
2316
|
+
if (stat2.isDirectory()) {
|
|
2317
2317
|
for (const child of readdirSync(entryPath)) {
|
|
2318
2318
|
visit(path3.join(entryPath, child));
|
|
2319
2319
|
}
|
|
2320
2320
|
return;
|
|
2321
2321
|
}
|
|
2322
|
-
if (!
|
|
2322
|
+
if (!stat2.isFile()) {
|
|
2323
2323
|
return;
|
|
2324
2324
|
}
|
|
2325
|
-
if (!newest ||
|
|
2326
|
-
newest = { path: entryPath, mtimeMs:
|
|
2325
|
+
if (!newest || stat2.mtimeMs > newest.mtimeMs) {
|
|
2326
|
+
newest = { path: entryPath, mtimeMs: stat2.mtimeMs };
|
|
2327
2327
|
}
|
|
2328
2328
|
};
|
|
2329
2329
|
for (const root of roots) {
|
|
@@ -2524,8 +2524,8 @@ function findCommandOnPath(command, pathEnv = process.env.PATH ?? "") {
|
|
|
2524
2524
|
for (const name of commandNames(command)) {
|
|
2525
2525
|
const candidate = path5.join(dir, name);
|
|
2526
2526
|
try {
|
|
2527
|
-
const
|
|
2528
|
-
if (!
|
|
2527
|
+
const stat2 = fs7.statSync(candidate);
|
|
2528
|
+
if (!stat2.isFile()) continue;
|
|
2529
2529
|
if (process.platform !== "win32") fs7.accessSync(candidate, fs7.constants.X_OK);
|
|
2530
2530
|
const runnable = resolveRunnableNodeScript(candidate);
|
|
2531
2531
|
if (runnable) return runnable;
|
|
@@ -5660,8 +5660,630 @@ function printBenchComparisonSummary(comparison, baseline, candidate) {
|
|
|
5660
5660
|
}
|
|
5661
5661
|
}
|
|
5662
5662
|
|
|
5663
|
-
// src/bench-
|
|
5663
|
+
// src/bench-coding-commands.ts
|
|
5664
|
+
import { lstat as lstat2, readFile as readFile2, realpath, stat } from "fs/promises";
|
|
5665
|
+
import os from "os";
|
|
5664
5666
|
import path14 from "path";
|
|
5667
|
+
var UINT32_MAX = 4294967295;
|
|
5668
|
+
var FROZEN_GENERATOR_SEED = 81;
|
|
5669
|
+
var FROZEN_TASK_COUNT = 30;
|
|
5670
|
+
var FROZEN_SEED_COUNT = 5;
|
|
5671
|
+
var FROZEN_STATISTICS_DRAWS = 1e4;
|
|
5672
|
+
var FROZEN_MAX_STEPS = 12;
|
|
5673
|
+
var FROZEN_MAX_TOOL_CALLS = 8;
|
|
5674
|
+
var FROZEN_MAX_OUTPUT_CHARS = 16384;
|
|
5675
|
+
var MAX_OUTPUT_BYTES = 16384;
|
|
5676
|
+
var DEFAULT_REPEATED_FAILURE_OUTPUT_DIR = path14.join(
|
|
5677
|
+
resolveHomeDir(),
|
|
5678
|
+
".remnic",
|
|
5679
|
+
"bench",
|
|
5680
|
+
"results",
|
|
5681
|
+
"h6-repeated-failure"
|
|
5682
|
+
);
|
|
5683
|
+
var BENCH_CODING_USAGE = `Usage: remnic bench coding repo-gen [--count 30] [--seed N] [--out DIR]
|
|
5684
|
+
remnic bench coding repo-gen verify-all [DIR]
|
|
5685
|
+
remnic bench coding repeated-failure --seeds N --profile FILE [--profile FILE ...] [options]
|
|
5686
|
+
remnic bench coding repeated-failure stats --run DIR
|
|
5687
|
+
remnic bench coding repeated-failure report --run DIR
|
|
5688
|
+
remnic bench coding repeated-failure trap-audit --profile FILE [--profile FILE ...] [options]
|
|
5689
|
+
|
|
5690
|
+
Commands:
|
|
5691
|
+
repo-gen Generate the frozen H6 synthetic repo fixture dataset
|
|
5692
|
+
repo-gen verify-all [DIR] Verify every H6 fixture in DIR, or the committed fixtures
|
|
5693
|
+
repeated-failure Run or resume the controlled repeated-failure suite
|
|
5694
|
+
repeated-failure stats Replay statistics offline from a completed run
|
|
5695
|
+
repeated-failure report Generate paper tables and figures from a completed run
|
|
5696
|
+
repeated-failure trap-audit Run seeded trap effectiveness audit for model profiles
|
|
5697
|
+
|
|
5698
|
+
Repo generation options:
|
|
5699
|
+
--count 30 Contract assertion; the published H6 v1 suite has exactly 30 tasks
|
|
5700
|
+
--seed 81 Contract assertion; the published H6 v1 inventory uses seed 81
|
|
5701
|
+
--out DIR Output directory (default: ./h6-failure-gate)
|
|
5702
|
+
|
|
5703
|
+
Repeated-failure options:
|
|
5704
|
+
--phase <pilot|main> Run phase (default: pilot); main also requires --pilot-run
|
|
5705
|
+
--pilot-run DIR Directory of completed pilot run (required when --phase is main)
|
|
5706
|
+
--seeds 5 Five deterministic seeds (required)
|
|
5707
|
+
--profile FILE One or two immutable model profile files are required
|
|
5708
|
+
--out DIR New run output directory (default: ~/.remnic/bench/results/h6-repeated-failure)
|
|
5709
|
+
--run DIR Existing run directory to resume
|
|
5710
|
+
--fixture DIR Generated H6 fixture directory
|
|
5711
|
+
--max-steps 12 Frozen episode step cap
|
|
5712
|
+
--max-output-chars 16384 Frozen serialized model output cap
|
|
5713
|
+
--max-duration-ms 120000 Episode wall-clock cap (trap-audit only; raise for large local models)
|
|
5714
|
+
--request-timeout-ms 60000 Per-request timeout (trap-audit only; raise for large local models)
|
|
5715
|
+
--draws 10000 Contract assertion for confirmatory and pilot statistics
|
|
5716
|
+
--statistics-seed N Unsigned 32-bit statistics seed
|
|
5717
|
+
--help, -h Show this help
|
|
5718
|
+
|
|
5719
|
+
Profile JSON v2:
|
|
5720
|
+
{"schemaVersion":2,"id":"...","provider":"openai-responses","model":"...",
|
|
5721
|
+
"instructions":{"system":"...","developer":"..."},
|
|
5722
|
+
"tokenizer":{"identity":"...","implementation":"nfkc-whitespace-v1"},
|
|
5723
|
+
"temperature":0,"maxOutputTokens":N}
|
|
5724
|
+
Optional endpoint, reasoningEffort, think, and strict nonstandard endpoint seedCapability
|
|
5725
|
+
fields participate in the canonical profile hash. Credentials never enter profile files or hashes.
|
|
5726
|
+
|
|
5727
|
+
A live run never chooses a model implicitly. The bench package derives the immutable
|
|
5728
|
+
profile ID and lowercase SHA-256 profile hash. Stats replay and report generation accept
|
|
5729
|
+
only --run and do not load a model or host.`;
|
|
5730
|
+
var DEFAULT_DEPENDENCIES = {
|
|
5731
|
+
loadBenchModule: async () => await loadBenchModule()
|
|
5732
|
+
};
|
|
5733
|
+
function parseBoundedInteger(value, flag, minimum, maximum) {
|
|
5734
|
+
if (value === void 0 || value.length === 0 || value.startsWith("-")) {
|
|
5735
|
+
throw new Error(`missing value for ${flag}`);
|
|
5736
|
+
}
|
|
5737
|
+
if (!/^(0|[1-9]\d*)$/.test(value)) {
|
|
5738
|
+
throw new Error(`${flag} must be an integer between ${minimum} and ${maximum}`);
|
|
5739
|
+
}
|
|
5740
|
+
const parsed = Number(value);
|
|
5741
|
+
if (!Number.isSafeInteger(parsed) || parsed < minimum || parsed > maximum) {
|
|
5742
|
+
throw new Error(`${flag} must be an integer between ${minimum} and ${maximum}`);
|
|
5743
|
+
}
|
|
5744
|
+
return parsed;
|
|
5745
|
+
}
|
|
5746
|
+
function readSingleValue(args, index, flag, seen) {
|
|
5747
|
+
if (seen.has(flag)) throw new Error(`${flag} may be provided only once`);
|
|
5748
|
+
seen.add(flag);
|
|
5749
|
+
const value = args[index + 1];
|
|
5750
|
+
if (value === void 0 || value.trim().length === 0 || value.startsWith("-")) {
|
|
5751
|
+
throw new Error(`missing value for ${flag}`);
|
|
5752
|
+
}
|
|
5753
|
+
return { value, nextIndex: index + 1 };
|
|
5754
|
+
}
|
|
5755
|
+
function parseRepoGenerate(args) {
|
|
5756
|
+
let seed = 81;
|
|
5757
|
+
let outputDir = "./h6-failure-gate";
|
|
5758
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5759
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
5760
|
+
const flag = args[index];
|
|
5761
|
+
if (flag === void 0) break;
|
|
5762
|
+
if (flag === "--help" || flag === "-h") return { kind: "help" };
|
|
5763
|
+
if (flag !== "--count" && flag !== "--seed" && flag !== "--out") {
|
|
5764
|
+
throw new Error(flag.startsWith("-") ? `unknown option ${flag}` : `ambiguous repo-gen subcommand ${flag}`);
|
|
5765
|
+
}
|
|
5766
|
+
const read = readSingleValue(args, index, flag, seen);
|
|
5767
|
+
index = read.nextIndex;
|
|
5768
|
+
if (flag === "--count") {
|
|
5769
|
+
const count = parseBoundedInteger(read.value, flag, 0, UINT32_MAX);
|
|
5770
|
+
if (count !== FROZEN_TASK_COUNT) {
|
|
5771
|
+
throw new Error("--count must be exactly 30 for the published H6 v1 suite");
|
|
5772
|
+
}
|
|
5773
|
+
} else if (flag === "--seed") {
|
|
5774
|
+
const parsedSeed = parseBoundedInteger(read.value, flag, 0, UINT32_MAX);
|
|
5775
|
+
if (parsedSeed !== FROZEN_GENERATOR_SEED) {
|
|
5776
|
+
throw new Error("--seed must be exactly 81 for the published H6 v1 inventory");
|
|
5777
|
+
}
|
|
5778
|
+
seed = parsedSeed;
|
|
5779
|
+
} else {
|
|
5780
|
+
outputDir = read.value;
|
|
5781
|
+
}
|
|
5782
|
+
}
|
|
5783
|
+
return { kind: "repo-generate", count: 30, seed, outputDir };
|
|
5784
|
+
}
|
|
5785
|
+
function parseRepoVerify(args) {
|
|
5786
|
+
if (args.length === 1 && (args[0] === "--help" || args[0] === "-h")) return { kind: "help" };
|
|
5787
|
+
const unknownFlag = args.find((arg) => arg.startsWith("-"));
|
|
5788
|
+
if (unknownFlag) throw new Error(`unknown option ${unknownFlag}`);
|
|
5789
|
+
if (args.length > 1) throw new Error("repo-gen verify-all accepts at most one directory");
|
|
5790
|
+
const directory = args[0];
|
|
5791
|
+
return directory === void 0 ? { kind: "repo-verify" } : { kind: "repo-verify", directory };
|
|
5792
|
+
}
|
|
5793
|
+
function parseRepeatedRunArtifact(args, kind, commandName) {
|
|
5794
|
+
if (args.length === 1 && (args[0] === "--help" || args[0] === "-h")) return { kind: "help" };
|
|
5795
|
+
if (args.length !== 2 || args[0] !== "--run") {
|
|
5796
|
+
const unknown = args.find((arg) => arg.startsWith("-") && arg !== "--run");
|
|
5797
|
+
if (unknown) throw new Error(`unknown option ${unknown}`);
|
|
5798
|
+
throw new Error(`repeated-failure ${commandName} requires exactly --run DIR`);
|
|
5799
|
+
}
|
|
5800
|
+
const runDir = args[1];
|
|
5801
|
+
if (runDir === void 0 || runDir.trim().length === 0 || runDir.startsWith("-")) {
|
|
5802
|
+
throw new Error("missing value for --run");
|
|
5803
|
+
}
|
|
5804
|
+
return { kind, runDir };
|
|
5805
|
+
}
|
|
5806
|
+
function parseRepeatedRun(args) {
|
|
5807
|
+
let phase = "pilot";
|
|
5808
|
+
let seedCount;
|
|
5809
|
+
const profilePaths = [];
|
|
5810
|
+
let outputDir = DEFAULT_REPEATED_FAILURE_OUTPUT_DIR;
|
|
5811
|
+
let fixtureDir;
|
|
5812
|
+
let resumeRunDir;
|
|
5813
|
+
let pilotRunDir;
|
|
5814
|
+
let maxSteps;
|
|
5815
|
+
let maxToolCalls;
|
|
5816
|
+
let maxOutputChars;
|
|
5817
|
+
let maxDurationMs;
|
|
5818
|
+
let requestTimeoutMs;
|
|
5819
|
+
let statisticsDraws;
|
|
5820
|
+
let statisticsSeed;
|
|
5821
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5822
|
+
const allowed = /* @__PURE__ */ new Set([
|
|
5823
|
+
"--phase",
|
|
5824
|
+
"--pilot-run",
|
|
5825
|
+
"--pilot-run-dir",
|
|
5826
|
+
"--seeds",
|
|
5827
|
+
"--profile",
|
|
5828
|
+
"--out",
|
|
5829
|
+
"--run",
|
|
5830
|
+
"--fixture",
|
|
5831
|
+
"--max-steps",
|
|
5832
|
+
"--max-tool-calls",
|
|
5833
|
+
"--max-output-chars",
|
|
5834
|
+
"--max-duration-ms",
|
|
5835
|
+
"--request-timeout-ms",
|
|
5836
|
+
"--draws",
|
|
5837
|
+
"--statistics-seed"
|
|
5838
|
+
]);
|
|
5839
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
5840
|
+
const flag = args[index];
|
|
5841
|
+
if (flag === void 0) break;
|
|
5842
|
+
if (flag === "--help" || flag === "-h") return { kind: "help" };
|
|
5843
|
+
if (!allowed.has(flag)) {
|
|
5844
|
+
throw new Error(
|
|
5845
|
+
flag.startsWith("-") ? `unknown option ${flag}` : `ambiguous repeated-failure subcommand ${flag}`
|
|
5846
|
+
);
|
|
5847
|
+
}
|
|
5848
|
+
if (flag === "--profile") {
|
|
5849
|
+
const value = args[index + 1];
|
|
5850
|
+
if (value === void 0 || value.trim().length === 0 || value.startsWith("-")) {
|
|
5851
|
+
throw new Error("missing value for --profile");
|
|
5852
|
+
}
|
|
5853
|
+
profilePaths.push(value);
|
|
5854
|
+
index += 1;
|
|
5855
|
+
continue;
|
|
5856
|
+
}
|
|
5857
|
+
const read = readSingleValue(args, index, flag, seen);
|
|
5858
|
+
index = read.nextIndex;
|
|
5859
|
+
if (flag === "--phase") {
|
|
5860
|
+
if (read.value !== "pilot" && read.value !== "main") {
|
|
5861
|
+
throw new Error("--phase must be pilot or main");
|
|
5862
|
+
}
|
|
5863
|
+
phase = read.value;
|
|
5864
|
+
} else if (flag === "--seeds") {
|
|
5865
|
+
seedCount = parseBoundedInteger(read.value, flag, FROZEN_SEED_COUNT, FROZEN_SEED_COUNT);
|
|
5866
|
+
} else if (flag === "--out") outputDir = read.value;
|
|
5867
|
+
else if (flag === "--run") resumeRunDir = read.value;
|
|
5868
|
+
else if (flag === "--pilot-run" || flag === "--pilot-run-dir") pilotRunDir = read.value;
|
|
5869
|
+
else if (flag === "--fixture") fixtureDir = read.value;
|
|
5870
|
+
else if (flag === "--max-steps") {
|
|
5871
|
+
maxSteps = parseBoundedInteger(read.value, flag, FROZEN_MAX_STEPS, FROZEN_MAX_STEPS);
|
|
5872
|
+
} else if (flag === "--max-tool-calls") {
|
|
5873
|
+
maxToolCalls = parseBoundedInteger(
|
|
5874
|
+
read.value,
|
|
5875
|
+
flag,
|
|
5876
|
+
FROZEN_MAX_TOOL_CALLS,
|
|
5877
|
+
FROZEN_MAX_TOOL_CALLS
|
|
5878
|
+
);
|
|
5879
|
+
} else if (flag === "--max-output-chars") {
|
|
5880
|
+
maxOutputChars = parseBoundedInteger(
|
|
5881
|
+
read.value,
|
|
5882
|
+
flag,
|
|
5883
|
+
FROZEN_MAX_OUTPUT_CHARS,
|
|
5884
|
+
FROZEN_MAX_OUTPUT_CHARS
|
|
5885
|
+
);
|
|
5886
|
+
} else if (flag === "--max-duration-ms") {
|
|
5887
|
+
maxDurationMs = parseBoundedInteger(read.value, flag, 1e3, 36e5);
|
|
5888
|
+
} else if (flag === "--request-timeout-ms") {
|
|
5889
|
+
requestTimeoutMs = parseBoundedInteger(read.value, flag, 1e3, 36e5);
|
|
5890
|
+
} else if (flag === "--draws") {
|
|
5891
|
+
statisticsDraws = parseBoundedInteger(
|
|
5892
|
+
read.value,
|
|
5893
|
+
flag,
|
|
5894
|
+
FROZEN_STATISTICS_DRAWS,
|
|
5895
|
+
FROZEN_STATISTICS_DRAWS
|
|
5896
|
+
);
|
|
5897
|
+
} else statisticsSeed = parseBoundedInteger(read.value, flag, 0, UINT32_MAX);
|
|
5898
|
+
}
|
|
5899
|
+
if (seedCount === void 0) throw new Error("repeated-failure requires --seeds N");
|
|
5900
|
+
if (profilePaths.length < 1 || profilePaths.length > 2) {
|
|
5901
|
+
throw new Error("repeated-failure runs require one or two --profile files");
|
|
5902
|
+
}
|
|
5903
|
+
if (phase === "main" && pilotRunDir === void 0 && resumeRunDir === void 0) {
|
|
5904
|
+
throw new Error("--phase main requires --pilot-run DIR");
|
|
5905
|
+
}
|
|
5906
|
+
if (phase === "pilot" && pilotRunDir !== void 0) {
|
|
5907
|
+
throw new Error("--pilot-run is only valid when --phase is main");
|
|
5908
|
+
}
|
|
5909
|
+
if (resumeRunDir !== void 0 && seen.has("--out")) throw new Error("--run and --out are mutually exclusive");
|
|
5910
|
+
return {
|
|
5911
|
+
kind: "repeated-run",
|
|
5912
|
+
phase,
|
|
5913
|
+
seedCount,
|
|
5914
|
+
profilePaths,
|
|
5915
|
+
outputDir,
|
|
5916
|
+
...fixtureDir === void 0 ? {} : { fixtureDir },
|
|
5917
|
+
...resumeRunDir === void 0 ? {} : { resumeRunDir },
|
|
5918
|
+
...pilotRunDir === void 0 ? {} : { pilotRunDir },
|
|
5919
|
+
...maxSteps === void 0 ? {} : { maxSteps },
|
|
5920
|
+
...maxToolCalls === void 0 ? {} : { maxToolCalls },
|
|
5921
|
+
...maxOutputChars === void 0 ? {} : { maxOutputChars },
|
|
5922
|
+
...maxDurationMs === void 0 ? {} : { maxDurationMs },
|
|
5923
|
+
...requestTimeoutMs === void 0 ? {} : { requestTimeoutMs },
|
|
5924
|
+
...statisticsDraws === void 0 ? {} : { statisticsDraws },
|
|
5925
|
+
...statisticsSeed === void 0 ? {} : { statisticsSeed }
|
|
5926
|
+
};
|
|
5927
|
+
}
|
|
5928
|
+
function parseTrapAudit(args) {
|
|
5929
|
+
const profilePaths = [];
|
|
5930
|
+
let outputDir = "./h6-trap-audit";
|
|
5931
|
+
let fixtureDir;
|
|
5932
|
+
let maxSteps;
|
|
5933
|
+
let maxToolCalls;
|
|
5934
|
+
let maxOutputChars;
|
|
5935
|
+
let maxDurationMs;
|
|
5936
|
+
let requestTimeoutMs;
|
|
5937
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5938
|
+
const allowed = /* @__PURE__ */ new Set([
|
|
5939
|
+
"--profile",
|
|
5940
|
+
"--out",
|
|
5941
|
+
"--fixture",
|
|
5942
|
+
"--max-steps",
|
|
5943
|
+
"--max-tool-calls",
|
|
5944
|
+
"--max-output-chars",
|
|
5945
|
+
"--max-duration-ms",
|
|
5946
|
+
"--request-timeout-ms"
|
|
5947
|
+
]);
|
|
5948
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
5949
|
+
const flag = args[index];
|
|
5950
|
+
if (flag === void 0) break;
|
|
5951
|
+
if (flag === "--help" || flag === "-h") return { kind: "help" };
|
|
5952
|
+
if (!allowed.has(flag)) {
|
|
5953
|
+
throw new Error(
|
|
5954
|
+
flag.startsWith("-") ? `unknown option ${flag}` : `ambiguous trap-audit subcommand ${flag}`
|
|
5955
|
+
);
|
|
5956
|
+
}
|
|
5957
|
+
if (flag === "--profile") {
|
|
5958
|
+
const value = args[index + 1];
|
|
5959
|
+
if (value === void 0 || value.trim().length === 0 || value.startsWith("-")) {
|
|
5960
|
+
throw new Error("missing value for --profile");
|
|
5961
|
+
}
|
|
5962
|
+
profilePaths.push(value);
|
|
5963
|
+
index += 1;
|
|
5964
|
+
continue;
|
|
5965
|
+
}
|
|
5966
|
+
const read = readSingleValue(args, index, flag, seen);
|
|
5967
|
+
index = read.nextIndex;
|
|
5968
|
+
if (flag === "--out") outputDir = read.value;
|
|
5969
|
+
else if (flag === "--fixture") fixtureDir = read.value;
|
|
5970
|
+
else if (flag === "--max-steps") maxSteps = parseBoundedInteger(read.value, flag, 1, 100);
|
|
5971
|
+
else if (flag === "--max-tool-calls") maxToolCalls = parseBoundedInteger(read.value, flag, 1, 100);
|
|
5972
|
+
else if (flag === "--max-output-chars") maxOutputChars = parseBoundedInteger(read.value, flag, 256, 65536);
|
|
5973
|
+
else if (flag === "--max-duration-ms") maxDurationMs = parseBoundedInteger(read.value, flag, 1e3, 36e5);
|
|
5974
|
+
else if (flag === "--request-timeout-ms") requestTimeoutMs = parseBoundedInteger(read.value, flag, 1e3, 36e5);
|
|
5975
|
+
}
|
|
5976
|
+
if (profilePaths.length === 0) throw new Error("trap-audit requires at least one --profile FILE");
|
|
5977
|
+
return {
|
|
5978
|
+
kind: "trap-audit",
|
|
5979
|
+
profilePaths,
|
|
5980
|
+
outputDir,
|
|
5981
|
+
...fixtureDir === void 0 ? {} : { fixtureDir },
|
|
5982
|
+
...maxSteps === void 0 ? {} : { maxSteps },
|
|
5983
|
+
...maxToolCalls === void 0 ? {} : { maxToolCalls },
|
|
5984
|
+
...maxOutputChars === void 0 ? {} : { maxOutputChars },
|
|
5985
|
+
...maxDurationMs === void 0 ? {} : { maxDurationMs },
|
|
5986
|
+
...requestTimeoutMs === void 0 ? {} : { requestTimeoutMs }
|
|
5987
|
+
};
|
|
5988
|
+
}
|
|
5989
|
+
function parseBenchCodingArgs(args) {
|
|
5990
|
+
if (args.length === 0 || args[0] === "--help" || args[0] === "-h") return { kind: "help" };
|
|
5991
|
+
if (args[0] === "repo-gen") {
|
|
5992
|
+
return args[1] === "verify-all" ? parseRepoVerify(args.slice(2)) : parseRepoGenerate(args.slice(1));
|
|
5993
|
+
}
|
|
5994
|
+
if (args[0] === "repeated-failure") {
|
|
5995
|
+
if (args[1] === "stats") {
|
|
5996
|
+
return parseRepeatedRunArtifact(args.slice(2), "repeated-stats", "stats");
|
|
5997
|
+
}
|
|
5998
|
+
if (args[1] === "report") {
|
|
5999
|
+
return parseRepeatedRunArtifact(args.slice(2), "repeated-report", "report");
|
|
6000
|
+
}
|
|
6001
|
+
if (args[1] === "trap-audit" || args[1] === "audit") return parseTrapAudit(args.slice(2));
|
|
6002
|
+
return parseRepeatedRun(args.slice(1));
|
|
6003
|
+
}
|
|
6004
|
+
if (args[0] === "trap-audit") {
|
|
6005
|
+
return parseTrapAudit(args.slice(1));
|
|
6006
|
+
}
|
|
6007
|
+
throw new Error(`unknown bench coding subcommand ${args[0]}`);
|
|
6008
|
+
}
|
|
6009
|
+
function normalizeCommandPaths(command) {
|
|
6010
|
+
const resolve2 = (value) => path14.resolve(expandTilde(value));
|
|
6011
|
+
if (command.kind === "repo-generate") {
|
|
6012
|
+
return { ...command, outputDir: resolve2(command.outputDir) };
|
|
6013
|
+
}
|
|
6014
|
+
if (command.kind === "repo-verify") {
|
|
6015
|
+
return command.directory === void 0 ? command : { ...command, directory: resolve2(command.directory) };
|
|
6016
|
+
}
|
|
6017
|
+
if (command.kind === "repeated-stats" || command.kind === "repeated-report") {
|
|
6018
|
+
return { ...command, runDir: resolve2(command.runDir) };
|
|
6019
|
+
}
|
|
6020
|
+
if (command.kind === "repeated-run") {
|
|
6021
|
+
return {
|
|
6022
|
+
...command,
|
|
6023
|
+
profilePaths: command.profilePaths.map(resolve2),
|
|
6024
|
+
outputDir: resolve2(command.resumeRunDir ?? command.outputDir),
|
|
6025
|
+
...command.fixtureDir === void 0 ? {} : { fixtureDir: resolve2(command.fixtureDir) },
|
|
6026
|
+
...command.resumeRunDir === void 0 ? {} : { resumeRunDir: resolve2(command.resumeRunDir) },
|
|
6027
|
+
...command.pilotRunDir === void 0 ? {} : { pilotRunDir: resolve2(command.pilotRunDir) }
|
|
6028
|
+
};
|
|
6029
|
+
}
|
|
6030
|
+
if (command.kind === "trap-audit") {
|
|
6031
|
+
return {
|
|
6032
|
+
...command,
|
|
6033
|
+
profilePaths: command.profilePaths.map(resolve2),
|
|
6034
|
+
outputDir: resolve2(command.outputDir),
|
|
6035
|
+
...command.fixtureDir === void 0 ? {} : { fixtureDir: resolve2(command.fixtureDir) }
|
|
6036
|
+
};
|
|
6037
|
+
}
|
|
6038
|
+
return command;
|
|
6039
|
+
}
|
|
6040
|
+
async function canonicalProspectivePath(value) {
|
|
6041
|
+
let candidate = path14.resolve(value);
|
|
6042
|
+
const missingSegments = [];
|
|
6043
|
+
while (true) {
|
|
6044
|
+
try {
|
|
6045
|
+
return path14.join(await realpath(candidate), ...missingSegments.reverse());
|
|
6046
|
+
} catch (error) {
|
|
6047
|
+
if (error.code !== "ENOENT") throw error;
|
|
6048
|
+
const parent = path14.dirname(candidate);
|
|
6049
|
+
if (parent === candidate) throw error;
|
|
6050
|
+
missingSegments.push(path14.basename(candidate));
|
|
6051
|
+
candidate = parent;
|
|
6052
|
+
}
|
|
6053
|
+
}
|
|
6054
|
+
}
|
|
6055
|
+
function isSameOrDescendant(candidate, root) {
|
|
6056
|
+
const relative = path14.relative(root, candidate);
|
|
6057
|
+
return relative === "" || relative !== ".." && !relative.startsWith(`..${path14.sep}`) && !path14.isAbsolute(relative);
|
|
6058
|
+
}
|
|
6059
|
+
async function pathExists(value) {
|
|
6060
|
+
try {
|
|
6061
|
+
await lstat2(value);
|
|
6062
|
+
return true;
|
|
6063
|
+
} catch (error) {
|
|
6064
|
+
if (error.code === "ENOENT") return false;
|
|
6065
|
+
throw error;
|
|
6066
|
+
}
|
|
6067
|
+
}
|
|
6068
|
+
async function assertSafeBenchmarkOutput(outputDir) {
|
|
6069
|
+
const refusal = "refusing benchmark output inside a Remnic memory store";
|
|
6070
|
+
try {
|
|
6071
|
+
const canonicalOutput = await canonicalProspectivePath(outputDir);
|
|
6072
|
+
for (const variable of ["REMNIC_MEMORY_DIR", "ENGRAM_MEMORY_DIR"]) {
|
|
6073
|
+
const configured = process.env[variable]?.trim();
|
|
6074
|
+
if (!configured) continue;
|
|
6075
|
+
const memoryRoot = await canonicalProspectivePath(
|
|
6076
|
+
path14.resolve(expandTilde(configured))
|
|
6077
|
+
);
|
|
6078
|
+
if (isSameOrDescendant(canonicalOutput, memoryRoot)) {
|
|
6079
|
+
throw new Error(refusal);
|
|
6080
|
+
}
|
|
6081
|
+
}
|
|
6082
|
+
let candidate = canonicalOutput;
|
|
6083
|
+
while (true) {
|
|
6084
|
+
const hasProfile = await pathExists(path14.join(candidate, "profile.md"));
|
|
6085
|
+
const hasMemoryData = await pathExists(path14.join(candidate, "facts")) || await pathExists(path14.join(candidate, "entities")) || await pathExists(path14.join(candidate, "state"));
|
|
6086
|
+
if (hasProfile && hasMemoryData) throw new Error(refusal);
|
|
6087
|
+
const parent = path14.dirname(candidate);
|
|
6088
|
+
if (parent === candidate) break;
|
|
6089
|
+
candidate = parent;
|
|
6090
|
+
}
|
|
6091
|
+
} catch (error) {
|
|
6092
|
+
if (error instanceof Error && error.message === refusal) throw error;
|
|
6093
|
+
throw new Error(refusal);
|
|
6094
|
+
}
|
|
6095
|
+
}
|
|
6096
|
+
async function assertH6StatsRunDirectory(runDir, commandName = "stats") {
|
|
6097
|
+
try {
|
|
6098
|
+
const parsed = JSON.parse(
|
|
6099
|
+
await readFile2(path14.join(runDir, "run.json"), "utf8")
|
|
6100
|
+
);
|
|
6101
|
+
if (parsed.schemaVersion !== 1 || typeof parsed.runId !== "string" || parsed.runId.length === 0 || typeof parsed.suiteVersion !== "string" || !parsed.suiteVersion.startsWith("h6-failure-gate-v1-")) {
|
|
6102
|
+
throw new Error("invalid H6 metadata");
|
|
6103
|
+
}
|
|
6104
|
+
} catch {
|
|
6105
|
+
throw new Error(`${commandName} requires existing H6 run metadata`);
|
|
6106
|
+
}
|
|
6107
|
+
}
|
|
6108
|
+
function sanitizeOutput(output) {
|
|
6109
|
+
const home = os.homedir();
|
|
6110
|
+
const safe = home.length > 1 ? output.replaceAll(home, "~") : output;
|
|
6111
|
+
if (Buffer.byteLength(safe, "utf8") <= MAX_OUTPUT_BYTES) return safe.trimEnd();
|
|
6112
|
+
const bounded = Buffer.from(safe, "utf8").subarray(0, MAX_OUTPUT_BYTES - 32).toString("utf8");
|
|
6113
|
+
return `${bounded}
|
|
6114
|
+
[output truncated]`;
|
|
6115
|
+
}
|
|
6116
|
+
async function requireDirectory(directory, label) {
|
|
6117
|
+
const details = await stat(directory).catch(() => void 0);
|
|
6118
|
+
if (!details?.isDirectory()) throw new Error(`${label} must be a directory that exists`);
|
|
6119
|
+
}
|
|
6120
|
+
async function rejectExistingNonDirectory(directory, label) {
|
|
6121
|
+
const details = await stat(directory).catch(() => void 0);
|
|
6122
|
+
if (details !== void 0 && !details.isDirectory()) throw new Error(`${label} must be a directory`);
|
|
6123
|
+
}
|
|
6124
|
+
function requireFunction(bench, name) {
|
|
6125
|
+
const value = bench[name];
|
|
6126
|
+
if (typeof value !== "function") {
|
|
6127
|
+
throw new Error(`Installed @remnic/bench does not export ${name}; install a compatible version`);
|
|
6128
|
+
}
|
|
6129
|
+
return value;
|
|
6130
|
+
}
|
|
6131
|
+
function formatValidationSummary(report) {
|
|
6132
|
+
const { metrics } = report;
|
|
6133
|
+
return `H6 repo fixtures valid: ${metrics.totalTasks} tasks, ${metrics.totalVariants} variants (dev=${metrics.devTaskCount}, pilot=${metrics.pilotTaskCount}, main=${metrics.mainTaskCount}).`;
|
|
6134
|
+
}
|
|
6135
|
+
async function runRepoGeneration(command, bench) {
|
|
6136
|
+
const generate = requireFunction(bench, "generateH6BenchmarkDataset");
|
|
6137
|
+
const validate = requireFunction(bench, "validateH6Dataset");
|
|
6138
|
+
const writeBundle = requireFunction(bench, "writeH6FixtureBundle");
|
|
6139
|
+
const dataset = await generate(command.seed);
|
|
6140
|
+
const report = await validate(dataset);
|
|
6141
|
+
if (!report.valid) {
|
|
6142
|
+
return {
|
|
6143
|
+
exitCode: 1,
|
|
6144
|
+
output: `Generated H6 repo fixtures are invalid: ${report.issues.length} issue(s).`
|
|
6145
|
+
};
|
|
6146
|
+
}
|
|
6147
|
+
await writeBundle(command.outputDir, dataset);
|
|
6148
|
+
return {
|
|
6149
|
+
exitCode: 0,
|
|
6150
|
+
output: `Generated H6 repo fixtures: ${report.metrics.totalTasks} tasks, ${report.metrics.totalVariants} variants, seed ${command.seed}.`
|
|
6151
|
+
};
|
|
6152
|
+
}
|
|
6153
|
+
async function runRepoVerification(command, bench) {
|
|
6154
|
+
let dataset;
|
|
6155
|
+
if (command.directory === void 0) {
|
|
6156
|
+
dataset = await requireFunction(bench, "loadCommittedH6BenchmarkDataset")();
|
|
6157
|
+
} else {
|
|
6158
|
+
const serialized = await readFile2(path14.join(command.directory, "dataset.json"), "utf8").catch(
|
|
6159
|
+
() => void 0
|
|
6160
|
+
);
|
|
6161
|
+
if (serialized === void 0) {
|
|
6162
|
+
return {
|
|
6163
|
+
exitCode: 1,
|
|
6164
|
+
output: "H6 repo fixtures invalid: dataset.json is missing or unreadable."
|
|
6165
|
+
};
|
|
6166
|
+
}
|
|
6167
|
+
try {
|
|
6168
|
+
dataset = JSON.parse(serialized);
|
|
6169
|
+
} catch {
|
|
6170
|
+
return { exitCode: 1, output: "H6 repo fixtures invalid: dataset.json is not valid JSON." };
|
|
6171
|
+
}
|
|
6172
|
+
}
|
|
6173
|
+
const report = command.directory === void 0 ? await requireFunction(bench, "validateH6Dataset")(dataset) : await requireFunction(bench, "validateH6FixtureBundle")(command.directory);
|
|
6174
|
+
if (!report.valid) {
|
|
6175
|
+
const codes = [...new Set(report.issues.map((issue) => issue.code))].sort().slice(0, 20);
|
|
6176
|
+
return {
|
|
6177
|
+
exitCode: 1,
|
|
6178
|
+
output: `H6 repo fixtures invalid: ${report.issues.length} issue(s) [${codes.join(", ")}].`
|
|
6179
|
+
};
|
|
6180
|
+
}
|
|
6181
|
+
return { exitCode: 0, output: formatValidationSummary(report) };
|
|
6182
|
+
}
|
|
6183
|
+
async function executeCommand(command, dependencies) {
|
|
6184
|
+
if (command.kind === "help") return { exitCode: 0, output: BENCH_CODING_USAGE };
|
|
6185
|
+
if (command.kind === "repo-generate") {
|
|
6186
|
+
await assertSafeBenchmarkOutput(command.outputDir);
|
|
6187
|
+
await rejectExistingNonDirectory(command.outputDir, "--out");
|
|
6188
|
+
} else if (command.kind === "repo-verify" && command.directory !== void 0) {
|
|
6189
|
+
await requireDirectory(command.directory, "verify-all input");
|
|
6190
|
+
} else if (command.kind === "repeated-stats" || command.kind === "repeated-report") {
|
|
6191
|
+
await assertSafeBenchmarkOutput(command.runDir);
|
|
6192
|
+
await requireDirectory(command.runDir, "--run");
|
|
6193
|
+
await assertH6StatsRunDirectory(
|
|
6194
|
+
command.runDir,
|
|
6195
|
+
command.kind === "repeated-stats" ? "stats" : "report"
|
|
6196
|
+
);
|
|
6197
|
+
} else if (command.kind === "repeated-run" || command.kind === "trap-audit") {
|
|
6198
|
+
await assertSafeBenchmarkOutput(command.outputDir);
|
|
6199
|
+
if (command.kind === "repeated-run" && command.resumeRunDir !== void 0) {
|
|
6200
|
+
await requireDirectory(command.resumeRunDir, "--run");
|
|
6201
|
+
} else {
|
|
6202
|
+
await rejectExistingNonDirectory(command.outputDir, "--out");
|
|
6203
|
+
}
|
|
6204
|
+
if (command.fixtureDir !== void 0) {
|
|
6205
|
+
await requireDirectory(command.fixtureDir, "--fixture");
|
|
6206
|
+
}
|
|
6207
|
+
if (command.kind === "repeated-run" && command.pilotRunDir !== void 0) {
|
|
6208
|
+
await requireDirectory(command.pilotRunDir, "--pilot-run");
|
|
6209
|
+
}
|
|
6210
|
+
for (const profilePath of command.profilePaths) {
|
|
6211
|
+
const details = await stat(profilePath).catch(() => void 0);
|
|
6212
|
+
if (!details?.isFile()) {
|
|
6213
|
+
throw new Error("each --profile value must be an existing file");
|
|
6214
|
+
}
|
|
6215
|
+
}
|
|
6216
|
+
}
|
|
6217
|
+
const bench = await dependencies.loadBenchModule();
|
|
6218
|
+
if (command.kind === "repo-generate") return runRepoGeneration(command, bench);
|
|
6219
|
+
if (command.kind === "repo-verify") return runRepoVerification(command, bench);
|
|
6220
|
+
if (command.kind === "repeated-stats") {
|
|
6221
|
+
return requireFunction(bench, "replayRepeatedFailureStatistics")({ runDir: command.runDir });
|
|
6222
|
+
}
|
|
6223
|
+
if (command.kind === "repeated-report") {
|
|
6224
|
+
return requireFunction(bench, "runRepeatedFailurePaperReportCliCommand")({
|
|
6225
|
+
runDir: command.runDir
|
|
6226
|
+
});
|
|
6227
|
+
}
|
|
6228
|
+
if (command.kind === "trap-audit") {
|
|
6229
|
+
return requireFunction(bench, "runTrapAuditCliCommand")({
|
|
6230
|
+
profilePaths: command.profilePaths,
|
|
6231
|
+
outputDir: command.outputDir,
|
|
6232
|
+
...command.fixtureDir === void 0 ? {} : { fixtureDir: command.fixtureDir },
|
|
6233
|
+
...command.maxSteps === void 0 ? {} : { maxSteps: command.maxSteps },
|
|
6234
|
+
...command.maxToolCalls === void 0 ? {} : { maxToolCalls: command.maxToolCalls },
|
|
6235
|
+
...command.maxOutputChars === void 0 ? {} : { maxOutputChars: command.maxOutputChars },
|
|
6236
|
+
...command.maxDurationMs === void 0 ? {} : { maxDurationMs: command.maxDurationMs },
|
|
6237
|
+
...command.requestTimeoutMs === void 0 ? {} : { requestTimeoutMs: command.requestTimeoutMs }
|
|
6238
|
+
});
|
|
6239
|
+
}
|
|
6240
|
+
if (command.kind === "repeated-run") {
|
|
6241
|
+
return requireFunction(bench, "runRepeatedFailureCliCommand")({
|
|
6242
|
+
phase: command.phase,
|
|
6243
|
+
seedCount: command.seedCount,
|
|
6244
|
+
profilePaths: command.profilePaths,
|
|
6245
|
+
outputDir: command.outputDir,
|
|
6246
|
+
...command.fixtureDir === void 0 ? {} : { fixtureDir: command.fixtureDir },
|
|
6247
|
+
...command.resumeRunDir === void 0 ? {} : { resumeRunDir: command.resumeRunDir },
|
|
6248
|
+
...command.pilotRunDir === void 0 ? {} : { pilotRunDir: command.pilotRunDir },
|
|
6249
|
+
...command.maxSteps === void 0 ? {} : { maxSteps: command.maxSteps },
|
|
6250
|
+
...command.maxToolCalls === void 0 ? {} : { maxToolCalls: command.maxToolCalls },
|
|
6251
|
+
...command.maxOutputChars === void 0 ? {} : { maxOutputChars: command.maxOutputChars },
|
|
6252
|
+
...command.maxDurationMs === void 0 ? {} : { maxDurationMs: command.maxDurationMs },
|
|
6253
|
+
...command.requestTimeoutMs === void 0 ? {} : { requestTimeoutMs: command.requestTimeoutMs },
|
|
6254
|
+
...command.statisticsDraws === void 0 ? {} : { statisticsDraws: command.statisticsDraws },
|
|
6255
|
+
...command.statisticsSeed === void 0 ? {} : { statisticsSeed: command.statisticsSeed }
|
|
6256
|
+
});
|
|
6257
|
+
}
|
|
6258
|
+
throw new Error("unhandled command kind");
|
|
6259
|
+
}
|
|
6260
|
+
function validateCommandResult(result) {
|
|
6261
|
+
if (!Number.isInteger(result.exitCode) || result.exitCode < 0 || result.exitCode > 255 || typeof result.output !== "string") {
|
|
6262
|
+
throw new Error("Installed @remnic/bench returned an invalid coding command result");
|
|
6263
|
+
}
|
|
6264
|
+
return result;
|
|
6265
|
+
}
|
|
6266
|
+
async function runBenchCodingCommand(args, dependencies = DEFAULT_DEPENDENCIES) {
|
|
6267
|
+
try {
|
|
6268
|
+
const command = normalizeCommandPaths(parseBenchCodingArgs(args));
|
|
6269
|
+
const result = validateCommandResult(await executeCommand(command, dependencies));
|
|
6270
|
+
return { ...result, output: sanitizeOutput(result.output) };
|
|
6271
|
+
} catch (error) {
|
|
6272
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
6273
|
+
return { exitCode: 1, output: sanitizeOutput(`${message}
|
|
6274
|
+
|
|
6275
|
+
${BENCH_CODING_USAGE}`) };
|
|
6276
|
+
}
|
|
6277
|
+
}
|
|
6278
|
+
async function cmdBenchCoding(args) {
|
|
6279
|
+
const result = await runBenchCodingCommand(args);
|
|
6280
|
+
if (result.exitCode === 0) console.log(result.output);
|
|
6281
|
+
else console.error(result.output);
|
|
6282
|
+
if (result.exitCode !== 0) process.exitCode = result.exitCode;
|
|
6283
|
+
}
|
|
6284
|
+
|
|
6285
|
+
// src/bench-research-commands.ts
|
|
6286
|
+
import path15 from "path";
|
|
5665
6287
|
function emit(result) {
|
|
5666
6288
|
if (result.output) {
|
|
5667
6289
|
console.log(result.output);
|
|
@@ -5679,7 +6301,7 @@ async function runBenchResearchCommand(parsed) {
|
|
|
5679
6301
|
emit(
|
|
5680
6302
|
await runAttributeCliCommand({
|
|
5681
6303
|
runRef: parsed.runRef,
|
|
5682
|
-
resultsDir: parsed.resultsDir ??
|
|
6304
|
+
resultsDir: parsed.resultsDir ?? path15.join(resolveHomeDir(), ".remnic", "bench", "results"),
|
|
5683
6305
|
memoryDir: parsed.memoryDir,
|
|
5684
6306
|
qmdPath: parsed.qmdPath,
|
|
5685
6307
|
collection: parsed.collection,
|
|
@@ -5708,8 +6330,8 @@ async function runBenchResearchCommand(parsed) {
|
|
|
5708
6330
|
|
|
5709
6331
|
// src/bench-usage.ts
|
|
5710
6332
|
function getBenchUsageText() {
|
|
5711
|
-
return `Usage: remnic bench <list|run|published|datasets|runs|compare|results|baseline|export|publish|ui|providers|judge-calibrate|attribute|drift-gen> [options] [benchmark...]
|
|
5712
|
-
remnic benchmark <list|run|published|datasets|runs|compare|results|baseline|export|publish|ui|providers|judge-calibrate|check|report|attribute|drift-gen> [options] [benchmark...]
|
|
6333
|
+
return `Usage: remnic bench <list|run|published|datasets|runs|compare|results|baseline|export|publish|ui|providers|judge-calibrate|attribute|drift-gen|coding> [options] [benchmark...]
|
|
6334
|
+
remnic benchmark <list|run|published|datasets|runs|compare|results|baseline|export|publish|ui|providers|judge-calibrate|check|report|attribute|drift-gen|coding> [options] [benchmark...]
|
|
5713
6335
|
|
|
5714
6336
|
Commands:
|
|
5715
6337
|
list List published benchmark packs
|
|
@@ -5744,6 +6366,9 @@ Commands:
|
|
|
5744
6366
|
local + frontier judges over a benchmark's cached
|
|
5745
6367
|
answers, reports Cohen's kappa, and persists it so
|
|
5746
6368
|
subsequent local artifacts carry the kappa + warning.
|
|
6369
|
+
coding H6 synthetic coding benchmark commands
|
|
6370
|
+
Run \`remnic bench coding --help\` for repo generation,
|
|
6371
|
+
repeated-failure runs, resume, and offline stats replay
|
|
5747
6372
|
check Legacy latency regression gate (compatibility)
|
|
5748
6373
|
attribute --run <id> [--results-dir <path>] [--memory-dir <path>] [--threshold <value>]
|
|
5749
6374
|
[--qmd <path> --collection <name>]
|
|
@@ -5895,6 +6520,10 @@ Examples:
|
|
|
5895
6520
|
remnic bench attribute --run legacy-run --memory-dir ./memories --qmd /opt/qmd --collection memories
|
|
5896
6521
|
remnic bench drift-gen generate --users 20 --epochs 10 --out ./corpus
|
|
5897
6522
|
remnic bench drift-gen validate ./corpus
|
|
6523
|
+
remnic bench coding repo-gen --count 30 --seed 81 --out ./h6-fixtures
|
|
6524
|
+
remnic bench coding repo-gen verify-all ./h6-fixtures
|
|
6525
|
+
remnic bench coding repeated-failure --seeds 5 --profile ./profiles/model-a.json
|
|
6526
|
+
remnic bench coding repeated-failure stats --run ./h6-repeated-failure
|
|
5898
6527
|
remnic benchmark run --quick longmemeval`;
|
|
5899
6528
|
}
|
|
5900
6529
|
|
|
@@ -5941,15 +6570,15 @@ registerPublisher("omp", () => new LazyPluginPiPublisher("omp", (mod) => mod.Omp
|
|
|
5941
6570
|
function readCompatEnv(primary, legacy) {
|
|
5942
6571
|
return process.env[primary] ?? process.env[legacy];
|
|
5943
6572
|
}
|
|
5944
|
-
var PID_DIR =
|
|
5945
|
-
var LEGACY_PID_DIR =
|
|
5946
|
-
var PID_FILE =
|
|
5947
|
-
var LEGACY_PID_FILE =
|
|
5948
|
-
var LOG_FILE =
|
|
5949
|
-
var LEGACY_LOG_FILE =
|
|
5950
|
-
var CLI_MODULE_DIR =
|
|
5951
|
-
var CLI_REPO_ROOT =
|
|
5952
|
-
var EVAL_RUNNER_PATH =
|
|
6573
|
+
var PID_DIR = path16.join(resolveHomeDir(), ".remnic");
|
|
6574
|
+
var LEGACY_PID_DIR = path16.join(resolveHomeDir(), ".engram");
|
|
6575
|
+
var PID_FILE = path16.join(PID_DIR, "server.pid");
|
|
6576
|
+
var LEGACY_PID_FILE = path16.join(LEGACY_PID_DIR, "server.pid");
|
|
6577
|
+
var LOG_FILE = path16.join(PID_DIR, "server.log");
|
|
6578
|
+
var LEGACY_LOG_FILE = path16.join(LEGACY_PID_DIR, "server.log");
|
|
6579
|
+
var CLI_MODULE_DIR = path16.dirname(fileURLToPath4(import.meta.url));
|
|
6580
|
+
var CLI_REPO_ROOT = path16.resolve(CLI_MODULE_DIR, "../../..");
|
|
6581
|
+
var EVAL_RUNNER_PATH = path16.join(CLI_REPO_ROOT, "evals", "run.ts");
|
|
5953
6582
|
var OPENCLAW_GATEWAY_LABEL = "ai.openclaw.gateway";
|
|
5954
6583
|
var CLI_SUCCESS_EXIT_GRACE_MS = 5e3;
|
|
5955
6584
|
var CLI_OUTPUT_FLUSH_GRACE_MS = 250;
|
|
@@ -6169,8 +6798,8 @@ async function runBenchViaFallback(parsed, benchmarkId, runtimeProfile) {
|
|
|
6169
6798
|
process.exit(1);
|
|
6170
6799
|
}
|
|
6171
6800
|
const tsxCandidates = [
|
|
6172
|
-
|
|
6173
|
-
|
|
6801
|
+
path16.join(CLI_REPO_ROOT, "node_modules", ".bin", "tsx"),
|
|
6802
|
+
path16.join(CLI_REPO_ROOT, "packages", "remnic-cli", "node_modules", ".bin", "tsx")
|
|
6174
6803
|
];
|
|
6175
6804
|
const tsxCmd = tsxCandidates.find((candidate) => fs13.existsSync(candidate)) ?? "tsx";
|
|
6176
6805
|
const fallbackOutputDir = createFallbackBenchOutputDir(
|
|
@@ -6189,7 +6818,7 @@ async function runBenchViaFallback(parsed, benchmarkId, runtimeProfile) {
|
|
|
6189
6818
|
return resolveFallbackBenchResultPath(fallbackOutputDir);
|
|
6190
6819
|
}
|
|
6191
6820
|
function resolveBenchOutputDir() {
|
|
6192
|
-
return
|
|
6821
|
+
return path16.join(resolveHomeDir(), ".remnic", "bench", "results");
|
|
6193
6822
|
}
|
|
6194
6823
|
var DOWNLOADABLE_BENCHMARK_DATASETS = [
|
|
6195
6824
|
"ama-bench",
|
|
@@ -6234,8 +6863,8 @@ var MEMORY_AGENT_BENCH_SPLIT_FILENAMES = [
|
|
|
6234
6863
|
];
|
|
6235
6864
|
var MEMORY_AGENT_BENCH_ENTITY_MAPPING_CANDIDATES = [
|
|
6236
6865
|
"entity2id.json",
|
|
6237
|
-
|
|
6238
|
-
|
|
6866
|
+
path16.join("processed_data", "Recsys_Redial", "entity2id.json"),
|
|
6867
|
+
path16.join("Recsys_Redial", "entity2id.json")
|
|
6239
6868
|
];
|
|
6240
6869
|
var DOWNLOADED_DATASET_MARKERS = {
|
|
6241
6870
|
"ama-bench": { anyOf: ["open_end_qa_set.jsonl"] },
|
|
@@ -6310,7 +6939,7 @@ var PERSONAMEM_DATASET_FILE_CANDIDATES = [
|
|
|
6310
6939
|
"benchmark/benchmark.csv",
|
|
6311
6940
|
"benchmark.csv"
|
|
6312
6941
|
];
|
|
6313
|
-
var PERSONAMEM_COMPLETION_MARKER =
|
|
6942
|
+
var PERSONAMEM_COMPLETION_MARKER = path16.join(
|
|
6314
6943
|
"data",
|
|
6315
6944
|
"chat_history_32k",
|
|
6316
6945
|
".download-complete"
|
|
@@ -6318,10 +6947,10 @@ var PERSONAMEM_COMPLETION_MARKER = path15.join(
|
|
|
6318
6947
|
function resolveRealpathWithinDataset(datasetPath, relativePath) {
|
|
6319
6948
|
try {
|
|
6320
6949
|
const datasetRoot = fs13.realpathSync(datasetPath);
|
|
6321
|
-
const candidatePath =
|
|
6950
|
+
const candidatePath = path16.resolve(datasetRoot, relativePath);
|
|
6322
6951
|
const candidateRealPath = fs13.realpathSync(candidatePath);
|
|
6323
|
-
const relativeToRoot =
|
|
6324
|
-
if (relativeToRoot.startsWith("..") ||
|
|
6952
|
+
const relativeToRoot = path16.relative(datasetRoot, candidateRealPath);
|
|
6953
|
+
if (relativeToRoot.startsWith("..") || path16.isAbsolute(relativeToRoot)) {
|
|
6325
6954
|
return null;
|
|
6326
6955
|
}
|
|
6327
6956
|
return candidateRealPath;
|
|
@@ -6377,7 +7006,7 @@ function parseCsvRows(raw) {
|
|
|
6377
7006
|
}
|
|
6378
7007
|
function isPersonaMemDatasetComplete(datasetPath) {
|
|
6379
7008
|
try {
|
|
6380
|
-
const completionMarkerPath =
|
|
7009
|
+
const completionMarkerPath = path16.join(datasetPath, PERSONAMEM_COMPLETION_MARKER);
|
|
6381
7010
|
if (fs13.statSync(completionMarkerPath).isFile()) {
|
|
6382
7011
|
return true;
|
|
6383
7012
|
}
|
|
@@ -6385,7 +7014,7 @@ function isPersonaMemDatasetComplete(datasetPath) {
|
|
|
6385
7014
|
}
|
|
6386
7015
|
const datasetFile = PERSONAMEM_DATASET_FILE_CANDIDATES.find((candidate) => {
|
|
6387
7016
|
try {
|
|
6388
|
-
return fs13.statSync(
|
|
7017
|
+
return fs13.statSync(path16.join(datasetPath, candidate)).isFile();
|
|
6389
7018
|
} catch {
|
|
6390
7019
|
return false;
|
|
6391
7020
|
}
|
|
@@ -6394,7 +7023,7 @@ function isPersonaMemDatasetComplete(datasetPath) {
|
|
|
6394
7023
|
return false;
|
|
6395
7024
|
}
|
|
6396
7025
|
try {
|
|
6397
|
-
const rows = parseCsvRows(fs13.readFileSync(
|
|
7026
|
+
const rows = parseCsvRows(fs13.readFileSync(path16.join(datasetPath, datasetFile), "utf8"));
|
|
6398
7027
|
if (rows.length < 2) {
|
|
6399
7028
|
return false;
|
|
6400
7029
|
}
|
|
@@ -6417,14 +7046,14 @@ function isPersonaMemDatasetComplete(datasetPath) {
|
|
|
6417
7046
|
}
|
|
6418
7047
|
function hasDatasetFile(datasetPath, relativePath) {
|
|
6419
7048
|
try {
|
|
6420
|
-
return fs13.statSync(
|
|
7049
|
+
return fs13.statSync(path16.join(datasetPath, relativePath)).isFile();
|
|
6421
7050
|
} catch {
|
|
6422
7051
|
return false;
|
|
6423
7052
|
}
|
|
6424
7053
|
}
|
|
6425
7054
|
function hasMemoryAgentBenchEntityMapping(datasetPath) {
|
|
6426
|
-
const absoluteDatasetPath =
|
|
6427
|
-
const roots = [absoluteDatasetPath,
|
|
7055
|
+
const absoluteDatasetPath = path16.resolve(datasetPath);
|
|
7056
|
+
const roots = [absoluteDatasetPath, path16.dirname(absoluteDatasetPath)];
|
|
6428
7057
|
return hasDatasetFile(absoluteDatasetPath, "entity2id.json") || roots.some(
|
|
6429
7058
|
(root) => MEMORY_AGENT_BENCH_ENTITY_MAPPING_CANDIDATES.filter((relativePath) => relativePath !== "entity2id.json").some((relativePath) => hasDatasetFile(root, relativePath))
|
|
6430
7059
|
);
|
|
@@ -6435,7 +7064,7 @@ function memoryAgentBenchDatasetHasRecSysSamples(datasetPath) {
|
|
|
6435
7064
|
...MEMORY_AGENT_BENCH_SPLIT_FILENAMES
|
|
6436
7065
|
];
|
|
6437
7066
|
return candidateFilenames.some((filename) => {
|
|
6438
|
-
const filePath =
|
|
7067
|
+
const filePath = path16.join(datasetPath, filename);
|
|
6439
7068
|
try {
|
|
6440
7069
|
if (!fs13.statSync(filePath).isFile()) {
|
|
6441
7070
|
return false;
|
|
@@ -6474,7 +7103,7 @@ function isDatasetDownloaded(datasetPath, benchmarkId) {
|
|
|
6474
7103
|
if (marker.allOf) {
|
|
6475
7104
|
const hasAllRequiredFiles = marker.allOf.every((name) => {
|
|
6476
7105
|
try {
|
|
6477
|
-
return fs13.statSync(
|
|
7106
|
+
return fs13.statSync(path16.join(datasetPath, name)).isFile();
|
|
6478
7107
|
} catch {
|
|
6479
7108
|
return false;
|
|
6480
7109
|
}
|
|
@@ -6486,7 +7115,7 @@ function isDatasetDownloaded(datasetPath, benchmarkId) {
|
|
|
6486
7115
|
if (marker.anyOf) {
|
|
6487
7116
|
const hasMarkerFile = marker.anyOf.some((name) => {
|
|
6488
7117
|
try {
|
|
6489
|
-
return fs13.statSync(
|
|
7118
|
+
return fs13.statSync(path16.join(datasetPath, name)).isFile();
|
|
6490
7119
|
} catch {
|
|
6491
7120
|
return false;
|
|
6492
7121
|
}
|
|
@@ -6514,9 +7143,9 @@ function isDatasetDownloaded(datasetPath, benchmarkId) {
|
|
|
6514
7143
|
return false;
|
|
6515
7144
|
}
|
|
6516
7145
|
async function launchBenchUi(resultsDir) {
|
|
6517
|
-
const benchUiDir =
|
|
7146
|
+
const benchUiDir = path16.join(CLI_REPO_ROOT, "packages", "bench-ui");
|
|
6518
7147
|
const pnpmCmd = process.platform === "win32" ? "pnpm.cmd" : "pnpm";
|
|
6519
|
-
if (!fs13.existsSync(
|
|
7148
|
+
if (!fs13.existsSync(path16.join(benchUiDir, "package.json"))) {
|
|
6520
7149
|
console.error("ERROR: @remnic/bench-ui is not available in this checkout.");
|
|
6521
7150
|
process.exit(1);
|
|
6522
7151
|
}
|
|
@@ -6543,24 +7172,24 @@ async function launchBenchUi(resultsDir) {
|
|
|
6543
7172
|
});
|
|
6544
7173
|
}
|
|
6545
7174
|
function resolveRepoDatasetRoot() {
|
|
6546
|
-
const repoCandidate =
|
|
7175
|
+
const repoCandidate = path16.join(CLI_REPO_ROOT, "evals", "datasets");
|
|
6547
7176
|
if (isRepoCheckout()) {
|
|
6548
7177
|
return repoCandidate;
|
|
6549
7178
|
}
|
|
6550
|
-
return
|
|
7179
|
+
return path16.join(resolveHomeDir(), ".remnic", "bench", "datasets");
|
|
6551
7180
|
}
|
|
6552
7181
|
function listDownloadableBenchmarks() {
|
|
6553
7182
|
return [...DOWNLOADABLE_BENCHMARK_DATASETS];
|
|
6554
7183
|
}
|
|
6555
7184
|
function resolveDatasetDownloadScriptPath() {
|
|
6556
|
-
const bundled =
|
|
7185
|
+
const bundled = path16.join(CLI_MODULE_DIR, "assets", "download-datasets.sh");
|
|
6557
7186
|
if (fs13.existsSync(bundled)) {
|
|
6558
7187
|
return bundled;
|
|
6559
7188
|
}
|
|
6560
|
-
return
|
|
7189
|
+
return path16.join(CLI_REPO_ROOT, "evals", "scripts", "download-datasets.sh");
|
|
6561
7190
|
}
|
|
6562
7191
|
function isRepoCheckout() {
|
|
6563
|
-
return fs13.existsSync(
|
|
7192
|
+
return fs13.existsSync(path16.join(CLI_REPO_ROOT, "pnpm-workspace.yaml")) && fs13.existsSync(path16.join(CLI_REPO_ROOT, "evals", "scripts", "download-datasets.sh"));
|
|
6564
7193
|
}
|
|
6565
7194
|
function runDatasetDownloadScript(scriptPath, benchmarkId, datasetRoot, jsonMode) {
|
|
6566
7195
|
const stdio = jsonMode ? ["inherit", process.stderr, "inherit"] : "inherit";
|
|
@@ -6611,7 +7240,7 @@ function resolveBenchDatasetDir(benchmarkId, quick, datasetDirOverride) {
|
|
|
6611
7240
|
if (quick) {
|
|
6612
7241
|
return void 0;
|
|
6613
7242
|
}
|
|
6614
|
-
const datasetDir =
|
|
7243
|
+
const datasetDir = path16.join(resolveRepoDatasetRoot(), benchmarkId);
|
|
6615
7244
|
if (isDatasetDownloaded(datasetDir, benchmarkId)) {
|
|
6616
7245
|
return datasetDir;
|
|
6617
7246
|
}
|
|
@@ -6868,12 +7497,12 @@ async function exportBenchPackageResult(parsed) {
|
|
|
6868
7497
|
process.exit(1);
|
|
6869
7498
|
}
|
|
6870
7499
|
const result = await loadBenchmarkResult(summary.path);
|
|
6871
|
-
const reportCardProvenance = parsed.format === "html" ? await loadBenchmarkReportCardProvenance(
|
|
7500
|
+
const reportCardProvenance = parsed.format === "html" ? await loadBenchmarkReportCardProvenance(path16.dirname(summary.path), result.meta.id) : void 0;
|
|
6872
7501
|
const rendered = renderBenchmarkResultExport(result, parsed.format, {
|
|
6873
7502
|
...reportCardProvenance ? { reportCardProvenance } : {}
|
|
6874
7503
|
});
|
|
6875
7504
|
if (parsed.output) {
|
|
6876
|
-
fs13.mkdirSync(
|
|
7505
|
+
fs13.mkdirSync(path16.dirname(parsed.output), { recursive: true });
|
|
6877
7506
|
fs13.writeFileSync(parsed.output, rendered);
|
|
6878
7507
|
console.log(`Exported ${summary.id} as ${parsed.format} to ${parsed.output}`);
|
|
6879
7508
|
return;
|
|
@@ -6891,7 +7520,7 @@ async function manageBenchDatasets(parsed) {
|
|
|
6891
7520
|
process.exit(1);
|
|
6892
7521
|
}
|
|
6893
7522
|
const status = supported.map((benchmarkId) => {
|
|
6894
|
-
const datasetPath =
|
|
7523
|
+
const datasetPath = path16.join(datasetRoot, benchmarkId);
|
|
6895
7524
|
return {
|
|
6896
7525
|
benchmark: benchmarkId,
|
|
6897
7526
|
downloaded: isDatasetDownloaded(datasetPath, benchmarkId),
|
|
@@ -6929,7 +7558,7 @@ async function manageBenchDatasets(parsed) {
|
|
|
6929
7558
|
runDatasetDownloadScript(scriptPath, benchmarkId, datasetRoot, parsed.json === true);
|
|
6930
7559
|
downloaded.push({
|
|
6931
7560
|
benchmark: benchmarkId,
|
|
6932
|
-
path:
|
|
7561
|
+
path: path16.join(datasetRoot, benchmarkId)
|
|
6933
7562
|
});
|
|
6934
7563
|
}
|
|
6935
7564
|
if (parsed.json) {
|
|
@@ -7068,10 +7697,10 @@ async function calibrateBenchJudges(parsed, rawArgs) {
|
|
|
7068
7697
|
}
|
|
7069
7698
|
const bench = await loadBenchModule();
|
|
7070
7699
|
const resultsDir = expandTilde(
|
|
7071
|
-
parsed.resultsDir ??
|
|
7700
|
+
parsed.resultsDir ?? path16.join(resolveHomeDir(), ".remnic", "bench", "results")
|
|
7072
7701
|
);
|
|
7073
7702
|
const calibrationDir = expandTilde(
|
|
7074
|
-
parsed.calibrationDir ??
|
|
7703
|
+
parsed.calibrationDir ?? path16.join(resolveHomeDir(), ".remnic", "bench", "calibration")
|
|
7075
7704
|
);
|
|
7076
7705
|
const stored = await bench.listBenchmarkResults(resultsDir);
|
|
7077
7706
|
const allForBenchmark = stored.filter((entry) => entry.benchmark === benchmarkId);
|
|
@@ -7631,7 +8260,7 @@ async function loadPublishedPromotionHelpers() {
|
|
|
7631
8260
|
return {
|
|
7632
8261
|
async promoteArtifactsToPublished(args) {
|
|
7633
8262
|
const { mkdirSync, readFileSync: readFileSync4, writeFileSync } = await import("fs");
|
|
7634
|
-
const
|
|
8263
|
+
const path17 = await import("path");
|
|
7635
8264
|
mkdirSync(args.publishedOutDir, { recursive: true });
|
|
7636
8265
|
if (args.artifactPaths.length === 0) {
|
|
7637
8266
|
console.warn(
|
|
@@ -7648,13 +8277,13 @@ async function loadPublishedPromotionHelpers() {
|
|
|
7648
8277
|
const modelSlug = args.model.replace(/[^a-zA-Z0-9_.-]/g, "-");
|
|
7649
8278
|
const rawProfile = parsedObj.config?.runtimeProfile;
|
|
7650
8279
|
const profileSlug = typeof rawProfile === "string" && rawProfile.length > 0 ? `-${rawProfile.replace(/[^a-zA-Z0-9_.-]/g, "-")}` : "";
|
|
7651
|
-
const target =
|
|
8280
|
+
const target = path17.join(
|
|
7652
8281
|
args.publishedOutDir,
|
|
7653
8282
|
`${today}-${args.benchmarkId}-${modelSlug}${profileSlug}-${gitShaShort}.json`
|
|
7654
8283
|
);
|
|
7655
8284
|
writeFileSync(target, raw, "utf8");
|
|
7656
8285
|
console.log(
|
|
7657
|
-
`[bench published] Promoted ${
|
|
8286
|
+
`[bench published] Promoted ${path17.basename(artifactPath)} \u2192 ${target}`
|
|
7658
8287
|
);
|
|
7659
8288
|
}
|
|
7660
8289
|
void benchModule;
|
|
@@ -7761,7 +8390,7 @@ async function runBenchViaPackage(parsed, benchmarkId, runtimeProfile, benchStat
|
|
|
7761
8390
|
const previousCodexDiagnosticsDir = process.env[CODEX_CLI_BENCH_DIAGNOSTICS_DIR_ENV];
|
|
7762
8391
|
const previousCodexDiagnosticsMode = process.env[CODEX_CLI_BENCH_DIAGNOSTICS_MODE_ENV];
|
|
7763
8392
|
if (!previousCodexDiagnosticsDir) {
|
|
7764
|
-
process.env[CODEX_CLI_BENCH_DIAGNOSTICS_DIR_ENV] =
|
|
8393
|
+
process.env[CODEX_CLI_BENCH_DIAGNOSTICS_DIR_ENV] = path16.join(
|
|
7765
8394
|
outputDir,
|
|
7766
8395
|
"codex-cli-diagnostics"
|
|
7767
8396
|
);
|
|
@@ -7909,7 +8538,7 @@ async function preparePersistedJudgeCalibrationAttachment(benchModule, benchmark
|
|
|
7909
8538
|
);
|
|
7910
8539
|
}
|
|
7911
8540
|
const calibrationDir = expandTilde(
|
|
7912
|
-
calibrationBinding.calibrationDir ??
|
|
8541
|
+
calibrationBinding.calibrationDir ?? path16.join(resolveHomeDir(), ".remnic", "bench", "calibration")
|
|
7913
8542
|
);
|
|
7914
8543
|
const state = await benchModule.loadJudgeCalibrationState?.(benchmarkId, calibrationDir);
|
|
7915
8544
|
if (!state) {
|
|
@@ -8279,19 +8908,19 @@ function loadConvergeCommandConfig() {
|
|
|
8279
8908
|
return loadStandaloneConvergeCommandConfig();
|
|
8280
8909
|
}
|
|
8281
8910
|
function resolveConfigPath(cliPath) {
|
|
8282
|
-
if (cliPath) return
|
|
8911
|
+
if (cliPath) return path16.resolve(expandTilde(cliPath));
|
|
8283
8912
|
const envPath = readCompatEnv("REMNIC_CONFIG_PATH", "ENGRAM_CONFIG_PATH");
|
|
8284
|
-
if (envPath) return
|
|
8913
|
+
if (envPath) return path16.resolve(expandTilde(envPath));
|
|
8285
8914
|
const candidates = [
|
|
8286
|
-
|
|
8287
|
-
|
|
8288
|
-
|
|
8289
|
-
|
|
8915
|
+
path16.join(process.cwd(), "remnic.config.json"),
|
|
8916
|
+
path16.join(process.cwd(), "engram.config.json"),
|
|
8917
|
+
path16.join(resolveHomeDir(), ".config", "remnic", "config.json"),
|
|
8918
|
+
path16.join(resolveHomeDir(), ".config", "engram", "config.json")
|
|
8290
8919
|
];
|
|
8291
8920
|
for (const candidate of candidates) {
|
|
8292
8921
|
if (fs13.existsSync(candidate)) return candidate;
|
|
8293
8922
|
}
|
|
8294
|
-
return
|
|
8923
|
+
return path16.join(resolveHomeDir(), ".config", "remnic", "config.json");
|
|
8295
8924
|
}
|
|
8296
8925
|
function resolveExistingBenchRemnicConfigPath(cliPath) {
|
|
8297
8926
|
const configPath = resolveConfigPath(cliPath);
|
|
@@ -8405,7 +9034,7 @@ async function resolvePackageBenchRuntime(benchModule, parsed, runtimeProfile) {
|
|
|
8405
9034
|
);
|
|
8406
9035
|
}
|
|
8407
9036
|
function normalizeMemoryDirPath(memoryDir) {
|
|
8408
|
-
return
|
|
9037
|
+
return path16.resolve(expandTilde(memoryDir));
|
|
8409
9038
|
}
|
|
8410
9039
|
function resolveMemoryDir() {
|
|
8411
9040
|
const configMemoryDir = (() => {
|
|
@@ -8418,9 +9047,9 @@ function resolveMemoryDir() {
|
|
|
8418
9047
|
return normalizeMemoryDirPath(remnicCfg.memoryDir);
|
|
8419
9048
|
}
|
|
8420
9049
|
const home = resolveHomeDir();
|
|
8421
|
-
const standalonePath =
|
|
8422
|
-
const legacyStandalonePath =
|
|
8423
|
-
const openclawPath =
|
|
9050
|
+
const standalonePath = path16.join(home, ".remnic", "memory");
|
|
9051
|
+
const legacyStandalonePath = path16.join(home, ".engram", "memory");
|
|
9052
|
+
const openclawPath = path16.join(home, ".openclaw", "workspace", "memory", "local");
|
|
8424
9053
|
if (fs13.existsSync(standalonePath)) return standalonePath;
|
|
8425
9054
|
if (fs13.existsSync(legacyStandalonePath)) return legacyStandalonePath;
|
|
8426
9055
|
return openclawPath;
|
|
@@ -8471,16 +9100,16 @@ var REMNIC_OPENCLAW_LEGACY_PLUGIN_ID = "openclaw-engram";
|
|
|
8471
9100
|
var DEFAULT_OPENCLAW_CONFIG_PATHS_FOR_DOCTOR = [
|
|
8472
9101
|
process.env.OPENCLAW_CONFIG_PATH,
|
|
8473
9102
|
process.env.OPENCLAW_ENGRAM_CONFIG_PATH,
|
|
8474
|
-
|
|
9103
|
+
path16.join(resolveHomeDir(), ".openclaw", "openclaw.json")
|
|
8475
9104
|
].filter(Boolean);
|
|
8476
9105
|
function resolveOpenclawConfigPath(cliPath) {
|
|
8477
|
-
if (cliPath) return
|
|
9106
|
+
if (cliPath) return path16.resolve(expandTilde(cliPath));
|
|
8478
9107
|
const envPath = process.env.OPENCLAW_CONFIG_PATH || process.env.OPENCLAW_ENGRAM_CONFIG_PATH;
|
|
8479
|
-
if (envPath) return
|
|
9108
|
+
if (envPath) return path16.resolve(expandTilde(envPath));
|
|
8480
9109
|
for (const candidate of DEFAULT_OPENCLAW_CONFIG_PATHS_FOR_DOCTOR) {
|
|
8481
9110
|
if (fs13.existsSync(candidate)) return candidate;
|
|
8482
9111
|
}
|
|
8483
|
-
return
|
|
9112
|
+
return path16.join(resolveHomeDir(), ".openclaw", "openclaw.json");
|
|
8484
9113
|
}
|
|
8485
9114
|
function readOpenclawConfig(configPath) {
|
|
8486
9115
|
if (!fs13.existsSync(configPath)) return {};
|
|
@@ -8539,10 +9168,10 @@ function buildRemnicOpenclawHooksPolicy(legacyHooks, existingHooks) {
|
|
|
8539
9168
|
function resolveOpenclawInstallMemoryDir(args) {
|
|
8540
9169
|
const existingMemoryDir = (typeof args.existingNewEntryConfig.memoryDir === "string" ? args.existingNewEntryConfig.memoryDir : void 0) || (args.migrateLegacy && typeof args.legacyConfigToMerge.memoryDir === "string" ? args.legacyConfigToMerge.memoryDir : void 0);
|
|
8541
9170
|
if (args.requestedMemoryDir) {
|
|
8542
|
-
return
|
|
9171
|
+
return path16.resolve(expandTilde(args.requestedMemoryDir));
|
|
8543
9172
|
}
|
|
8544
9173
|
if (existingMemoryDir) {
|
|
8545
|
-
return
|
|
9174
|
+
return path16.resolve(expandTilde(existingMemoryDir));
|
|
8546
9175
|
}
|
|
8547
9176
|
return args.fallbackMemoryDir;
|
|
8548
9177
|
}
|
|
@@ -8560,18 +9189,18 @@ function resolveCurrentOpenclawMemoryDir(entries, slots, fallbackMemoryDir) {
|
|
|
8560
9189
|
if (!config || typeof config !== "object" || Array.isArray(config)) continue;
|
|
8561
9190
|
const memoryDir = config.memoryDir;
|
|
8562
9191
|
if (typeof memoryDir === "string" && memoryDir.trim().length > 0) {
|
|
8563
|
-
return
|
|
9192
|
+
return path16.resolve(expandTilde(memoryDir));
|
|
8564
9193
|
}
|
|
8565
9194
|
}
|
|
8566
9195
|
return fallbackMemoryDir;
|
|
8567
9196
|
}
|
|
8568
9197
|
function resolveOpenclawPluginDir(cliPath) {
|
|
8569
|
-
if (cliPath) return
|
|
8570
|
-
return
|
|
9198
|
+
if (cliPath) return path16.resolve(expandTilde(cliPath));
|
|
9199
|
+
return path16.join(resolveHomeDir(), ".openclaw", "extensions", REMNIC_OPENCLAW_PLUGIN_ID);
|
|
8571
9200
|
}
|
|
8572
9201
|
function resolveOpenclawLegacyPluginDir(cliPath) {
|
|
8573
|
-
if (cliPath) return
|
|
8574
|
-
return
|
|
9202
|
+
if (cliPath) return path16.resolve(expandTilde(cliPath));
|
|
9203
|
+
return path16.join(resolveHomeDir(), ".openclaw", "extensions", REMNIC_OPENCLAW_LEGACY_PLUGIN_ID);
|
|
8575
9204
|
}
|
|
8576
9205
|
function formatOpenclawUpgradeStamp(now = /* @__PURE__ */ new Date()) {
|
|
8577
9206
|
const yyyy = now.getFullYear().toString();
|
|
@@ -8584,14 +9213,14 @@ function formatOpenclawUpgradeStamp(now = /* @__PURE__ */ new Date()) {
|
|
|
8584
9213
|
}
|
|
8585
9214
|
function backupPathIfPresent(sourcePath, backupPath) {
|
|
8586
9215
|
if (!fs13.existsSync(sourcePath)) return false;
|
|
8587
|
-
fs13.mkdirSync(
|
|
9216
|
+
fs13.mkdirSync(path16.dirname(backupPath), { recursive: true });
|
|
8588
9217
|
fs13.cpSync(sourcePath, backupPath, { recursive: true });
|
|
8589
9218
|
return true;
|
|
8590
9219
|
}
|
|
8591
9220
|
function assertDirectoryPathOrMissing(targetPath, label) {
|
|
8592
9221
|
if (!fs13.existsSync(targetPath)) return;
|
|
8593
|
-
const
|
|
8594
|
-
if (!
|
|
9222
|
+
const stat2 = fs13.statSync(targetPath);
|
|
9223
|
+
if (!stat2.isDirectory()) {
|
|
8595
9224
|
throw new Error(`${label} must be a directory when it already exists: ${targetPath}`);
|
|
8596
9225
|
}
|
|
8597
9226
|
}
|
|
@@ -8615,7 +9244,7 @@ var PublishedOpenclawPluginInstallError = class extends Error {
|
|
|
8615
9244
|
}
|
|
8616
9245
|
};
|
|
8617
9246
|
function installPublishedOpenclawPlugin(spec, pluginDir) {
|
|
8618
|
-
const tempRoot = fs13.mkdtempSync(
|
|
9247
|
+
const tempRoot = fs13.mkdtempSync(path16.join(os2.tmpdir(), "remnic-openclaw-upgrade-"));
|
|
8619
9248
|
const stagedDir = `${pluginDir}.next-${process.pid}-${Date.now()}`;
|
|
8620
9249
|
const rollbackDir = `${pluginDir}.rollback-${process.pid}-${Date.now()}`;
|
|
8621
9250
|
let swapRollbackDir;
|
|
@@ -8630,12 +9259,12 @@ function installPublishedOpenclawPlugin(spec, pluginDir) {
|
|
|
8630
9259
|
if (!tarballName) {
|
|
8631
9260
|
throw new Error(`npm pack ${spec} did not return a tarball name`);
|
|
8632
9261
|
}
|
|
8633
|
-
const unpackDir =
|
|
9262
|
+
const unpackDir = path16.join(tempRoot, "unpacked");
|
|
8634
9263
|
fs13.mkdirSync(unpackDir, { recursive: true });
|
|
8635
|
-
childProcess2.execFileSync("tar", ["-xzf",
|
|
9264
|
+
childProcess2.execFileSync("tar", ["-xzf", path16.join(tempRoot, tarballName), "-C", unpackDir], {
|
|
8636
9265
|
stdio: ["ignore", "pipe", "pipe"]
|
|
8637
9266
|
});
|
|
8638
|
-
const packagedDir =
|
|
9267
|
+
const packagedDir = path16.join(unpackDir, "package");
|
|
8639
9268
|
if (!fs13.existsSync(packagedDir)) {
|
|
8640
9269
|
throw new Error(`npm pack ${spec} did not contain a package/ directory`);
|
|
8641
9270
|
}
|
|
@@ -8655,7 +9284,7 @@ function installPublishedOpenclawPlugin(spec, pluginDir) {
|
|
|
8655
9284
|
}
|
|
8656
9285
|
})();
|
|
8657
9286
|
swapRollbackDir = swapResult.rollbackDir;
|
|
8658
|
-
const installedPackageJsonPath =
|
|
9287
|
+
const installedPackageJsonPath = path16.join(pluginDir, "package.json");
|
|
8659
9288
|
const installedPackage = fs13.existsSync(installedPackageJsonPath) ? JSON.parse(fs13.readFileSync(installedPackageJsonPath, "utf8")) : {};
|
|
8660
9289
|
return {
|
|
8661
9290
|
rollbackDir: swapRollbackDir,
|
|
@@ -8690,7 +9319,7 @@ function restartOpenclawGateway() {
|
|
|
8690
9319
|
});
|
|
8691
9320
|
}
|
|
8692
9321
|
function cmdInit() {
|
|
8693
|
-
const configPath =
|
|
9322
|
+
const configPath = path16.join(process.cwd(), "remnic.config.json");
|
|
8694
9323
|
if (fs13.existsSync(configPath)) {
|
|
8695
9324
|
console.log(`Config already exists: ${configPath}`);
|
|
8696
9325
|
return;
|
|
@@ -8698,7 +9327,7 @@ function cmdInit() {
|
|
|
8698
9327
|
const template = {
|
|
8699
9328
|
remnic: {
|
|
8700
9329
|
openaiApiKey: "${OPENAI_API_KEY}",
|
|
8701
|
-
memoryDir:
|
|
9330
|
+
memoryDir: path16.join(process.cwd(), ".remnic", "memory"),
|
|
8702
9331
|
memoryOsPreset: "balanced"
|
|
8703
9332
|
},
|
|
8704
9333
|
server: {
|
|
@@ -8835,7 +9464,7 @@ function oauthResolveOperatorToken() {
|
|
|
8835
9464
|
}
|
|
8836
9465
|
return void 0;
|
|
8837
9466
|
}
|
|
8838
|
-
async function oauthFetch(method,
|
|
9467
|
+
async function oauthFetch(method, path17, token, body) {
|
|
8839
9468
|
const controller = new AbortController();
|
|
8840
9469
|
const timeoutId = setTimeout(() => controller.abort(), 5e3);
|
|
8841
9470
|
try {
|
|
@@ -8854,7 +9483,7 @@ async function oauthFetch(method, path16, token, body) {
|
|
|
8854
9483
|
if (body !== void 0) {
|
|
8855
9484
|
init.body = JSON.stringify(body);
|
|
8856
9485
|
}
|
|
8857
|
-
const response = await fetch(`${oauthResolveBaseUrl()}${
|
|
9486
|
+
const response = await fetch(`${oauthResolveBaseUrl()}${path17}`, init);
|
|
8858
9487
|
if (response.status === 401) {
|
|
8859
9488
|
throw new Error(
|
|
8860
9489
|
"operator token rejected by remnic-server (HTTP 401). Update `server.authToken` or `REMNIC_AUTH_TOKEN` to match the running daemon."
|
|
@@ -9409,7 +10038,7 @@ async function cmdVersions(rest) {
|
|
|
9409
10038
|
console.error("Usage: remnic versions list <page-path>");
|
|
9410
10039
|
process.exit(1);
|
|
9411
10040
|
}
|
|
9412
|
-
const absPath =
|
|
10041
|
+
const absPath = path16.resolve(pagePath);
|
|
9413
10042
|
const history = await listVersions(absPath, versioningConfig, memDir);
|
|
9414
10043
|
if (json) {
|
|
9415
10044
|
console.log(JSON.stringify(history, null, 2));
|
|
@@ -9434,7 +10063,7 @@ async function cmdVersions(rest) {
|
|
|
9434
10063
|
console.error("Usage: remnic versions show <page-path> <version-id>");
|
|
9435
10064
|
process.exit(1);
|
|
9436
10065
|
}
|
|
9437
|
-
const absPath =
|
|
10066
|
+
const absPath = path16.resolve(pagePath);
|
|
9438
10067
|
try {
|
|
9439
10068
|
const content = await getVersion(absPath, versionId, versioningConfig, memDir);
|
|
9440
10069
|
console.log(content);
|
|
@@ -9452,7 +10081,7 @@ async function cmdVersions(rest) {
|
|
|
9452
10081
|
console.error("Usage: remnic versions diff <page-path> <v1> <v2>");
|
|
9453
10082
|
process.exit(1);
|
|
9454
10083
|
}
|
|
9455
|
-
const absPath =
|
|
10084
|
+
const absPath = path16.resolve(pagePath);
|
|
9456
10085
|
try {
|
|
9457
10086
|
const diffOutput = await diffVersions(absPath, v1, v2, versioningConfig, memDir);
|
|
9458
10087
|
console.log(diffOutput);
|
|
@@ -9469,7 +10098,7 @@ async function cmdVersions(rest) {
|
|
|
9469
10098
|
console.error("Usage: remnic versions revert <page-path> <version-id>");
|
|
9470
10099
|
process.exit(1);
|
|
9471
10100
|
}
|
|
9472
|
-
const absPath =
|
|
10101
|
+
const absPath = path16.resolve(pagePath);
|
|
9473
10102
|
try {
|
|
9474
10103
|
const version = await revertToVersion(absPath, versionId, versioningConfig, void 0, memDir);
|
|
9475
10104
|
if (json) {
|
|
@@ -9509,7 +10138,7 @@ async function cmdEnrich(rest) {
|
|
|
9509
10138
|
const subcommand = rest[0];
|
|
9510
10139
|
if (subcommand === "audit") {
|
|
9511
10140
|
const memoryDir2 = expandTilde(config.memoryDir);
|
|
9512
|
-
const auditDir2 =
|
|
10141
|
+
const auditDir2 = path16.join(memoryDir2, "enrichment");
|
|
9513
10142
|
const sinceFlag = resolveFlag(rest.slice(1), "--since");
|
|
9514
10143
|
const entries = await readAuditLog(auditDir2, sinceFlag ?? void 0);
|
|
9515
10144
|
if (entries.length === 0) {
|
|
@@ -9634,7 +10263,7 @@ Registered providers:`);
|
|
|
9634
10263
|
return;
|
|
9635
10264
|
}
|
|
9636
10265
|
const memoryDir = expandTilde(config.memoryDir);
|
|
9637
|
-
const auditDir =
|
|
10266
|
+
const auditDir = path16.join(memoryDir, "enrichment");
|
|
9638
10267
|
let totalPersisted = 0;
|
|
9639
10268
|
for (const result of results) {
|
|
9640
10269
|
for (const candidate of result.acceptedCandidates) {
|
|
@@ -9825,7 +10454,7 @@ Root: ${root}`);
|
|
|
9825
10454
|
const validNames = new Set(extensions.map((e) => e.name));
|
|
9826
10455
|
let errors = 0;
|
|
9827
10456
|
for (const entry of entries) {
|
|
9828
|
-
const entryPath =
|
|
10457
|
+
const entryPath = path16.join(root, entry);
|
|
9829
10458
|
try {
|
|
9830
10459
|
if (!fs13.statSync(entryPath).isDirectory()) continue;
|
|
9831
10460
|
} catch {
|
|
@@ -9941,7 +10570,7 @@ async function cmdBriefing(rest) {
|
|
|
9941
10570
|
const saveDir = resolveBriefingSaveDir(config.briefing.saveDir);
|
|
9942
10571
|
fs13.mkdirSync(saveDir, { recursive: true });
|
|
9943
10572
|
const filename = briefingFilename(new Date(result.window.to), format);
|
|
9944
|
-
const filePath =
|
|
10573
|
+
const filePath = path16.join(saveDir, filename);
|
|
9945
10574
|
fs13.writeFileSync(filePath, payload + (payload.endsWith("\n") ? "" : "\n"));
|
|
9946
10575
|
console.error(`Saved briefing: ${filePath}`);
|
|
9947
10576
|
} catch (err) {
|
|
@@ -10097,14 +10726,14 @@ async function cmdDoctor() {
|
|
|
10097
10726
|
const rawMemoryDir = entryConfig?.memoryDir;
|
|
10098
10727
|
const configuredMemoryDir = typeof rawMemoryDir === "string" ? rawMemoryDir : void 0;
|
|
10099
10728
|
if (configuredMemoryDir) {
|
|
10100
|
-
const resolvedMemDir =
|
|
10729
|
+
const resolvedMemDir = path16.resolve(expandTilde(configuredMemoryDir));
|
|
10101
10730
|
let memDirOk = false;
|
|
10102
10731
|
let memDirDetail = `${resolvedMemDir} (not found)`;
|
|
10103
10732
|
let memDirRemediation = `Run \`remnic openclaw install --memory-dir "${resolvedMemDir}"\` to create the directory.`;
|
|
10104
10733
|
if (fs13.existsSync(resolvedMemDir)) {
|
|
10105
10734
|
try {
|
|
10106
|
-
const
|
|
10107
|
-
if (
|
|
10735
|
+
const stat2 = fs13.statSync(resolvedMemDir);
|
|
10736
|
+
if (stat2.isDirectory()) {
|
|
10108
10737
|
memDirOk = true;
|
|
10109
10738
|
memDirDetail = resolvedMemDir;
|
|
10110
10739
|
memDirRemediation = void 0;
|
|
@@ -10297,7 +10926,7 @@ async function cmdMigrate(json, rollback) {
|
|
|
10297
10926
|
console.log(` Rollback: ${result.rollbackCommand}`);
|
|
10298
10927
|
}
|
|
10299
10928
|
function cmdOnboard(dirPath, json) {
|
|
10300
|
-
const directory =
|
|
10929
|
+
const directory = path16.resolve(dirPath || process.cwd());
|
|
10301
10930
|
const result = onboard({ directory });
|
|
10302
10931
|
if (json) {
|
|
10303
10932
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -10316,7 +10945,7 @@ Suggested namespace: ${result.plan.suggestedNamespace}`);
|
|
|
10316
10945
|
async function cmdCurate(targetPath, json) {
|
|
10317
10946
|
const memoryDir = resolveMemoryDir();
|
|
10318
10947
|
const result = await curate({
|
|
10319
|
-
targetPath:
|
|
10948
|
+
targetPath: path16.resolve(targetPath),
|
|
10320
10949
|
memoryDir,
|
|
10321
10950
|
source: "curation",
|
|
10322
10951
|
checkDuplicates: true,
|
|
@@ -10445,8 +11074,8 @@ async function cmdSync(action, rest, json) {
|
|
|
10445
11074
|
}
|
|
10446
11075
|
}
|
|
10447
11076
|
function localOfflineSourceId(memoryDir) {
|
|
10448
|
-
const host =
|
|
10449
|
-
const dirHash = createHash4("sha256").update(
|
|
11077
|
+
const host = os2.hostname() || "unknown-host";
|
|
11078
|
+
const dirHash = createHash4("sha256").update(path16.resolve(memoryDir)).digest("hex").slice(0, 16);
|
|
10450
11079
|
return `remnic-local:${host}:${dirHash}`;
|
|
10451
11080
|
}
|
|
10452
11081
|
function normalizeOfflineRemoteUrl(raw) {
|
|
@@ -10844,10 +11473,10 @@ var OFFLINE_SYNC_CONTENT_MISSING_RETRY_MAX = 3;
|
|
|
10844
11473
|
var OFFLINE_SYNC_CONTENT_MISSING_RETRY_DELAY_MS = 250;
|
|
10845
11474
|
var OfflineRemoteFileChangedError = class extends Error {
|
|
10846
11475
|
path;
|
|
10847
|
-
constructor(
|
|
10848
|
-
super(`remote file changed while fetching offline content: ${
|
|
11476
|
+
constructor(path17) {
|
|
11477
|
+
super(`remote file changed while fetching offline content: ${path17}`);
|
|
10849
11478
|
this.name = "OfflineRemoteFileChangedError";
|
|
10850
|
-
this.path =
|
|
11479
|
+
this.path = path17;
|
|
10851
11480
|
}
|
|
10852
11481
|
};
|
|
10853
11482
|
function isOfflineRemoteFileChangedError(error) {
|
|
@@ -11038,10 +11667,10 @@ function offlineDirectPushFiles(options) {
|
|
|
11038
11667
|
}).sort((left, right) => right.bytes - left.bytes || left.path.localeCompare(right.path));
|
|
11039
11668
|
}
|
|
11040
11669
|
function resolveOfflineDirectHydrationPath(memoryDir, relPath) {
|
|
11041
|
-
const base =
|
|
11042
|
-
const target =
|
|
11043
|
-
const relative =
|
|
11044
|
-
if (relative === "" || relative === ".." || relative.startsWith(`..${
|
|
11670
|
+
const base = path16.resolve(memoryDir);
|
|
11671
|
+
const target = path16.resolve(base, relPath);
|
|
11672
|
+
const relative = path16.relative(base, target);
|
|
11673
|
+
if (relative === "" || relative === ".." || relative.startsWith(`..${path16.sep}`) || path16.isAbsolute(relative)) {
|
|
11045
11674
|
throw new Error(`offline sync direct hydration path escapes memory dir: ${relPath}`);
|
|
11046
11675
|
}
|
|
11047
11676
|
return target;
|
|
@@ -11111,13 +11740,13 @@ async function pushOfflineFileContent(args) {
|
|
|
11111
11740
|
}
|
|
11112
11741
|
async function pushOfflineFileContentFromChunkReader(args) {
|
|
11113
11742
|
const filePath = resolveOfflineDirectHydrationPath(args.memoryDir, args.file.path);
|
|
11114
|
-
const
|
|
11115
|
-
if (
|
|
11743
|
+
const stat2 = fs13.statSync(filePath);
|
|
11744
|
+
if (stat2.mtimeMs !== args.file.mtimeMs) {
|
|
11116
11745
|
throw new Error(`local file changed while pushing offline content: ${args.file.path}`);
|
|
11117
11746
|
}
|
|
11118
11747
|
const hash = createHash4("sha256");
|
|
11119
11748
|
const chunks = args.readFileChunks({
|
|
11120
|
-
root:
|
|
11749
|
+
root: path16.resolve(args.memoryDir),
|
|
11121
11750
|
path: args.file.path,
|
|
11122
11751
|
filePath,
|
|
11123
11752
|
chunkSize: OFFLINE_SYNC_FILE_CONTENT_UPLOAD_CHUNK_BYTES
|
|
@@ -12222,7 +12851,7 @@ Environment fallbacks:
|
|
|
12222
12851
|
REMNIC_OFFLINE_REMOTE_URL, REMNIC_OFFLINE_TOKEN, REMNIC_AUTH_TOKEN`);
|
|
12223
12852
|
return;
|
|
12224
12853
|
}
|
|
12225
|
-
const memoryDir =
|
|
12854
|
+
const memoryDir = path16.resolve(expandTilde(resolveRequiredValueFlag(rest, "--memory-dir") ?? resolveMemoryDir()));
|
|
12226
12855
|
const namespace = resolveRequiredValueFlag(rest, "--namespace");
|
|
12227
12856
|
const includeTranscripts = !hasFlag(rest, "--no-transcripts");
|
|
12228
12857
|
const stateOverride = resolveRequiredValueFlag(rest, "--state");
|
|
@@ -12242,7 +12871,7 @@ Environment fallbacks:
|
|
|
12242
12871
|
const needsRemote = action === "prepare" || action === "sync" || action === "watch";
|
|
12243
12872
|
const remoteUrl = needsRemote ? resolveOfflineRemoteUrl(rest) : resolveOptionalOfflineRemoteUrl(rest);
|
|
12244
12873
|
const token = needsRemote ? resolveOfflineToken(rest) : void 0;
|
|
12245
|
-
const statePath = statePathExplicit ?
|
|
12874
|
+
const statePath = statePathExplicit ? path16.resolve(expandTilde(stateOverride)) : remoteUrl !== void 0 ? defaultOfflineSyncStatePath(memoryDir, remoteUrl, namespace) : void 0;
|
|
12246
12875
|
if (action === "prepare") {
|
|
12247
12876
|
if (!remoteUrl || !token || !statePath) throw new Error("offline prepare requires remote URL and token");
|
|
12248
12877
|
fs13.mkdirSync(memoryDir, { recursive: true });
|
|
@@ -12424,11 +13053,11 @@ Environment fallbacks:
|
|
|
12424
13053
|
failures: result.largeFilePushFailures
|
|
12425
13054
|
});
|
|
12426
13055
|
largeFileFailureCounts = advanced.counts;
|
|
12427
|
-
for (const
|
|
12428
|
-
if (skippedLargeFiles.has(
|
|
12429
|
-
skippedLargeFiles.add(
|
|
13056
|
+
for (const path17 of advanced.newlySkipped) {
|
|
13057
|
+
if (skippedLargeFiles.has(path17)) continue;
|
|
13058
|
+
skippedLargeFiles.add(path17);
|
|
12430
13059
|
console.warn(
|
|
12431
|
-
`offline sync: permanently skipping ${
|
|
13060
|
+
`offline sync: permanently skipping ${path17} after ${OFFLINE_LARGE_FILE_SKIP_AFTER_FAILURES} failed large-file pushes for this watcher process (see issue #1786; use --exclude or offlineSyncExcludes to silence permanently)`
|
|
12432
13061
|
);
|
|
12433
13062
|
}
|
|
12434
13063
|
const pulled = result.pull ? result.pull.upserted + result.pull.deleted : 0;
|
|
@@ -12443,11 +13072,11 @@ Environment fallbacks:
|
|
|
12443
13072
|
failures: error.failures
|
|
12444
13073
|
});
|
|
12445
13074
|
largeFileFailureCounts = advanced.counts;
|
|
12446
|
-
for (const
|
|
12447
|
-
if (skippedLargeFiles.has(
|
|
12448
|
-
skippedLargeFiles.add(
|
|
13075
|
+
for (const path17 of advanced.newlySkipped) {
|
|
13076
|
+
if (skippedLargeFiles.has(path17)) continue;
|
|
13077
|
+
skippedLargeFiles.add(path17);
|
|
12449
13078
|
console.warn(
|
|
12450
|
-
`offline sync: permanently skipping ${
|
|
13079
|
+
`offline sync: permanently skipping ${path17} after ${OFFLINE_LARGE_FILE_SKIP_AFTER_FAILURES} failed large-file pushes for this watcher process (see issue #1786; use --exclude or offlineSyncExcludes to silence permanently)`
|
|
12451
13080
|
);
|
|
12452
13081
|
}
|
|
12453
13082
|
}
|
|
@@ -12588,7 +13217,7 @@ async function cmdConnectors(action, rest, json) {
|
|
|
12588
13217
|
const connectorDaemonUrl = typeof effectiveConnectorConfig.remnicDaemonUrl === "string" && effectiveConnectorConfig.remnicDaemonUrl.trim().length > 0 ? effectiveConnectorConfig.remnicDaemonUrl.trim() : void 0;
|
|
12589
13218
|
const pubResult = await pub.publish({
|
|
12590
13219
|
config: { memoryDir, namespace: connectorNamespace, daemonUrl: connectorDaemonUrl },
|
|
12591
|
-
skillsRoot:
|
|
13220
|
+
skillsRoot: path16.join(memoryDir, "skills"),
|
|
12592
13221
|
rollbackTokenEntry: preInstallTokenEntry,
|
|
12593
13222
|
log: { info: console.log, warn: console.warn, error: console.error }
|
|
12594
13223
|
});
|
|
@@ -12948,15 +13577,15 @@ async function cmdConnectorsMarketplace(subAction, rest, json) {
|
|
|
12948
13577
|
}
|
|
12949
13578
|
const manifest = generateMarketplaceManifest();
|
|
12950
13579
|
await writeMarketplaceManifest(outputDir, manifest);
|
|
12951
|
-
const outPath =
|
|
13580
|
+
const outPath = path16.join(outputDir, "marketplace.json");
|
|
12952
13581
|
if (json) {
|
|
12953
13582
|
console.log(JSON.stringify({ status: "generated", path: outPath }, null, 2));
|
|
12954
13583
|
} else {
|
|
12955
13584
|
console.log(`Generated marketplace.json at ${outPath}`);
|
|
12956
13585
|
}
|
|
12957
13586
|
} else if (subAction === "validate") {
|
|
12958
|
-
const targetPath = rest.filter((a) => !a.startsWith("--"))[0] ??
|
|
12959
|
-
const resolved =
|
|
13587
|
+
const targetPath = rest.filter((a) => !a.startsWith("--"))[0] ?? path16.join(process.cwd(), "marketplace.json");
|
|
13588
|
+
const resolved = path16.resolve(targetPath);
|
|
12960
13589
|
if (!fs13.existsSync(resolved)) {
|
|
12961
13590
|
console.error(`File not found: ${resolved}`);
|
|
12962
13591
|
process.exit(1);
|
|
@@ -13238,6 +13867,7 @@ async function cmdLegacyBenchmark(action, rest, json) {
|
|
|
13238
13867
|
}
|
|
13239
13868
|
}
|
|
13240
13869
|
async function cmdBench(rest) {
|
|
13870
|
+
if (rest[0] === "coding") return cmdBenchCoding(rest.slice(1));
|
|
13241
13871
|
if (rest[0] === "procedural-ablation") {
|
|
13242
13872
|
await cmdBenchProceduralAblation(rest.slice(1));
|
|
13243
13873
|
return;
|
|
@@ -13364,7 +13994,7 @@ async function cmdBench(rest) {
|
|
|
13364
13994
|
}
|
|
13365
13995
|
const completeCount = prevStatus.benchmarks.filter((b) => b.status === "complete").length;
|
|
13366
13996
|
const failedCount = prevStatus.benchmarks.filter((b) => b.status === "failed").length;
|
|
13367
|
-
printBenchStatusLine(parsed.json, `Resuming from: ${
|
|
13997
|
+
printBenchStatusLine(parsed.json, `Resuming from: ${path16.basename(latestStatusPath)}`);
|
|
13368
13998
|
printBenchStatusLine(parsed.json, ` Previous run: ${prevStatus.startedAt}`);
|
|
13369
13999
|
printBenchStatusLine(parsed.json, ` Benchmarks: ${prevStatus.benchmarks.length} total, ${completeCount} complete, ${failedCount} failed`);
|
|
13370
14000
|
const before = selectedBenchmarks.length;
|
|
@@ -13532,9 +14162,9 @@ Options:
|
|
|
13532
14162
|
);
|
|
13533
14163
|
process.exit(1);
|
|
13534
14164
|
} else {
|
|
13535
|
-
fixturePath =
|
|
14165
|
+
fixturePath = path16.resolve(expandTilde(fixturePathRaw));
|
|
13536
14166
|
}
|
|
13537
|
-
const outPath =
|
|
14167
|
+
const outPath = path16.resolve(expandTilde(outPathRaw));
|
|
13538
14168
|
const benchModule = await loadBenchModule();
|
|
13539
14169
|
const runner = benchModule.runProceduralAblationCli;
|
|
13540
14170
|
if (typeof runner !== "function") {
|
|
@@ -13553,7 +14183,7 @@ Options:
|
|
|
13553
14183
|
);
|
|
13554
14184
|
console.log(`wrote ${outPath}`);
|
|
13555
14185
|
}
|
|
13556
|
-
var LOGS_DIR =
|
|
14186
|
+
var LOGS_DIR = path16.join(PID_DIR, "logs");
|
|
13557
14187
|
var LAUNCHD_PLIST_PATHS = launchdPlistPaths(resolveHomeDir());
|
|
13558
14188
|
var [LAUNCHD_PLIST_PATH] = LAUNCHD_PLIST_PATHS;
|
|
13559
14189
|
var SYSTEMD_UNIT_PATHS = systemdUnitPaths(resolveHomeDir());
|
|
@@ -13630,7 +14260,7 @@ function selectLaunchdInspection(openclawPluginModeConfigured) {
|
|
|
13630
14260
|
for (const plistPath of LAUNCHD_PLIST_PATHS.slice(1)) {
|
|
13631
14261
|
const legacy = inspectLaunchdPlist(plistPath);
|
|
13632
14262
|
if (!legacy.installed) continue;
|
|
13633
|
-
const label =
|
|
14263
|
+
const label = path16.basename(plistPath, ".plist");
|
|
13634
14264
|
return legacy.ok ? {
|
|
13635
14265
|
...legacy,
|
|
13636
14266
|
warn: true,
|
|
@@ -13664,10 +14294,10 @@ function daemonInstall() {
|
|
|
13664
14294
|
const vars = { HOME: home, NODE_PATH: nodePath, REMNIC_SERVER_BIN: serverBin };
|
|
13665
14295
|
fs13.mkdirSync(LOGS_DIR, { recursive: true });
|
|
13666
14296
|
if (isMacOS()) {
|
|
13667
|
-
const templatePath =
|
|
14297
|
+
const templatePath = path16.resolve(import.meta.dirname, "../templates/launchd/ai.remnic.daemon.plist");
|
|
13668
14298
|
const template = fs13.readFileSync(templatePath, "utf8");
|
|
13669
14299
|
const plist = renderTemplate(template, vars);
|
|
13670
|
-
fs13.mkdirSync(
|
|
14300
|
+
fs13.mkdirSync(path16.dirname(LAUNCHD_PLIST_PATH), { recursive: true });
|
|
13671
14301
|
fs13.writeFileSync(LAUNCHD_PLIST_PATH, plist);
|
|
13672
14302
|
try {
|
|
13673
14303
|
launchdLoadPlist(LAUNCHD_PLIST_PATH);
|
|
@@ -13684,10 +14314,10 @@ function daemonInstall() {
|
|
|
13684
14314
|
console.log(` RunAtLoad: true, KeepAlive: true`);
|
|
13685
14315
|
console.log(` Logs: ${LOGS_DIR}/daemon.log`);
|
|
13686
14316
|
} else if (isLinux()) {
|
|
13687
|
-
const templatePath =
|
|
14317
|
+
const templatePath = path16.resolve(import.meta.dirname, "../templates/systemd/remnic.service");
|
|
13688
14318
|
const template = fs13.readFileSync(templatePath, "utf8");
|
|
13689
14319
|
const unit = renderTemplate(template, vars);
|
|
13690
|
-
fs13.mkdirSync(
|
|
14320
|
+
fs13.mkdirSync(path16.dirname(SYSTEMD_UNIT_PATH), { recursive: true });
|
|
13691
14321
|
fs13.writeFileSync(SYSTEMD_UNIT_PATH, unit);
|
|
13692
14322
|
try {
|
|
13693
14323
|
childProcess2.execSync("systemctl --user daemon-reload", { stdio: "pipe" });
|
|
@@ -14166,7 +14796,7 @@ Clean complete: cleaned=${result.cleaned}`
|
|
|
14166
14796
|
}
|
|
14167
14797
|
async function cmdOpenclawInstall(opts) {
|
|
14168
14798
|
const configPath = resolveOpenclawConfigPath(opts.configPath);
|
|
14169
|
-
const fallbackMemoryDir =
|
|
14799
|
+
const fallbackMemoryDir = path16.join(resolveHomeDir(), ".openclaw", "workspace", "memory", "local");
|
|
14170
14800
|
console.log(`OpenClaw config: ${configPath}`);
|
|
14171
14801
|
const existingConfig = readOpenclawConfig(configPath);
|
|
14172
14802
|
const { plugins, entries, slots } = parseOpenclawPluginState(existingConfig, configPath);
|
|
@@ -14269,7 +14899,7 @@ Remove it first and re-run, or choose a different path with --memory-dir.`
|
|
|
14269
14899
|
fs13.mkdirSync(memoryDir, { recursive: true });
|
|
14270
14900
|
console.log(`Created memory directory: ${memoryDir}`);
|
|
14271
14901
|
}
|
|
14272
|
-
const configDir =
|
|
14902
|
+
const configDir = path16.dirname(configPath);
|
|
14273
14903
|
if (!fs13.existsSync(configDir)) {
|
|
14274
14904
|
fs13.mkdirSync(configDir, { recursive: true });
|
|
14275
14905
|
}
|
|
@@ -14297,11 +14927,11 @@ async function cmdOpenclawUpgrade(opts) {
|
|
|
14297
14927
|
const configPath = resolveOpenclawConfigPath(opts.configPath);
|
|
14298
14928
|
const pluginDir = resolveOpenclawPluginDir(opts.pluginDir);
|
|
14299
14929
|
const legacyPluginDirForBackup = opts.legacyPluginDirForBackup ? resolveOpenclawLegacyPluginDir(opts.legacyPluginDirForBackup) : void 0;
|
|
14300
|
-
const fallbackMemoryDir =
|
|
14930
|
+
const fallbackMemoryDir = path16.join(resolveHomeDir(), ".openclaw", "workspace", "memory", "local");
|
|
14301
14931
|
const packageSpec = `@remnic/plugin-openclaw@${opts.version ?? "latest"}`;
|
|
14302
14932
|
const existingConfig = readOpenclawConfig(configPath);
|
|
14303
14933
|
const { entries, slots } = parseOpenclawPluginState(existingConfig, configPath);
|
|
14304
|
-
const preservedMemoryDir = opts.memoryDir ?
|
|
14934
|
+
const preservedMemoryDir = opts.memoryDir ? path16.resolve(expandTilde(opts.memoryDir)) : resolveCurrentOpenclawMemoryDir(entries, slots, fallbackMemoryDir);
|
|
14305
14935
|
assertDirectoryPathOrMissing(pluginDir, "OpenClaw plugin dir");
|
|
14306
14936
|
if (legacyPluginDirForBackup) {
|
|
14307
14937
|
assertDirectoryPathOrMissing(legacyPluginDirForBackup, "Legacy OpenClaw plugin dir");
|
|
@@ -14313,7 +14943,7 @@ async function cmdOpenclawUpgrade(opts) {
|
|
|
14313
14943
|
}
|
|
14314
14944
|
console.log(`Memory dir: ${preservedMemoryDir}`);
|
|
14315
14945
|
console.log(`Package spec: ${packageSpec}`);
|
|
14316
|
-
console.log(`Backup root: ${
|
|
14946
|
+
console.log(`Backup root: ${path16.join(resolveHomeDir(), ".openclaw", "backups")}`);
|
|
14317
14947
|
const plannedActions = [
|
|
14318
14948
|
`backup openclaw.json and the existing ${REMNIC_OPENCLAW_PLUGIN_ID} extension`,
|
|
14319
14949
|
...legacyPluginDirForBackup ? [`backup the existing ${REMNIC_OPENCLAW_LEGACY_PLUGIN_ID} extension without modifying it`] : [],
|
|
@@ -14339,9 +14969,9 @@ async function cmdOpenclawUpgrade(opts) {
|
|
|
14339
14969
|
}
|
|
14340
14970
|
}
|
|
14341
14971
|
const backupDir = createOpenclawUpgradeBackupDir();
|
|
14342
|
-
const configBackupPath =
|
|
14343
|
-
const pluginBackupDir =
|
|
14344
|
-
const legacyPluginBackupDir = legacyPluginDirForBackup ?
|
|
14972
|
+
const configBackupPath = path16.join(backupDir, "openclaw.json");
|
|
14973
|
+
const pluginBackupDir = path16.join(backupDir, "extensions", REMNIC_OPENCLAW_PLUGIN_ID);
|
|
14974
|
+
const legacyPluginBackupDir = legacyPluginDirForBackup ? path16.join(backupDir, "extensions", REMNIC_OPENCLAW_LEGACY_PLUGIN_ID) : void 0;
|
|
14345
14975
|
const backupNotes = [];
|
|
14346
14976
|
if (backupPathIfPresent(configPath, configBackupPath)) {
|
|
14347
14977
|
backupNotes.push(`+ Backed up config to ${configBackupPath}`);
|
|
@@ -14442,9 +15072,9 @@ async function cmdOpenclawMigrateEngram(opts) {
|
|
|
14442
15072
|
console.log(" - Re-apply any local source patches to the new package only after verifying the published build.");
|
|
14443
15073
|
}
|
|
14444
15074
|
function createOpenclawUpgradeBackupDir() {
|
|
14445
|
-
const backupsRoot =
|
|
15075
|
+
const backupsRoot = path16.join(resolveHomeDir(), ".openclaw", "backups");
|
|
14446
15076
|
fs13.mkdirSync(backupsRoot, { recursive: true });
|
|
14447
|
-
return fs13.mkdtempSync(
|
|
15077
|
+
return fs13.mkdtempSync(path16.join(backupsRoot, `remnic-openclaw-upgrade-${formatOpenclawUpgradeStamp()}-`));
|
|
14448
15078
|
}
|
|
14449
15079
|
async function cmdTaxonomy(rest) {
|
|
14450
15080
|
initLogger2();
|
|
@@ -14486,8 +15116,8 @@ async function cmdTaxonomy(rest) {
|
|
|
14486
15116
|
const doc = generateResolverDocument(taxonomy);
|
|
14487
15117
|
console.log(doc);
|
|
14488
15118
|
if (config.taxonomyAutoGenResolver) {
|
|
14489
|
-
const resolverPath =
|
|
14490
|
-
fs13.mkdirSync(
|
|
15119
|
+
const resolverPath = path16.join(config.memoryDir, ".taxonomy", "RESOLVER.md");
|
|
15120
|
+
fs13.mkdirSync(path16.dirname(resolverPath), { recursive: true });
|
|
14491
15121
|
fs13.writeFileSync(resolverPath, doc);
|
|
14492
15122
|
console.error(`Written: ${resolverPath}`);
|
|
14493
15123
|
}
|
|
@@ -14533,7 +15163,7 @@ async function cmdTaxonomy(rest) {
|
|
|
14533
15163
|
console.log(`Added category "${id}" (${name}).`);
|
|
14534
15164
|
if (config.taxonomyAutoGenResolver) {
|
|
14535
15165
|
const doc = generateResolverDocument(taxonomy);
|
|
14536
|
-
const resolverPath =
|
|
15166
|
+
const resolverPath = path16.join(config.memoryDir, ".taxonomy", "RESOLVER.md");
|
|
14537
15167
|
fs13.writeFileSync(resolverPath, doc);
|
|
14538
15168
|
console.error(`Regenerated: ${resolverPath}`);
|
|
14539
15169
|
}
|
|
@@ -14564,7 +15194,7 @@ async function cmdTaxonomy(rest) {
|
|
|
14564
15194
|
console.log(`Removed category "${id}".`);
|
|
14565
15195
|
if (config.taxonomyAutoGenResolver) {
|
|
14566
15196
|
const doc = generateResolverDocument(taxonomy);
|
|
14567
|
-
const resolverPath =
|
|
15197
|
+
const resolverPath = path16.join(config.memoryDir, ".taxonomy", "RESOLVER.md");
|
|
14568
15198
|
fs13.writeFileSync(resolverPath, doc);
|
|
14569
15199
|
console.error(`Regenerated: ${resolverPath}`);
|
|
14570
15200
|
}
|
|
@@ -14846,7 +15476,7 @@ async function runTrainingExport(args, stdout = process.stdout) {
|
|
|
14846
15476
|
);
|
|
14847
15477
|
}
|
|
14848
15478
|
const formatted = adapter.formatRecords(records);
|
|
14849
|
-
const outDir =
|
|
15479
|
+
const outDir = path16.dirname(args.output);
|
|
14850
15480
|
fs13.mkdirSync(outDir, { recursive: true });
|
|
14851
15481
|
const tmpPath = `${args.output}.tmp-${process.pid}-${Date.now()}`;
|
|
14852
15482
|
fs13.writeFileSync(tmpPath, formatted, "utf-8");
|
|
@@ -14954,7 +15584,7 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
14954
15584
|
case "tree": {
|
|
14955
15585
|
const subAction = rest[0];
|
|
14956
15586
|
const json = rest.includes("--json");
|
|
14957
|
-
const outputDir = resolveFlag(rest, "--output") ??
|
|
15587
|
+
const outputDir = resolveFlag(rest, "--output") ?? path16.join(process.cwd(), ".remnic", "context-tree");
|
|
14958
15588
|
const categoriesFlag = resolveFlag(rest, "--categories");
|
|
14959
15589
|
const categories = categoriesFlag ? categoriesFlag.split(",") : void 0;
|
|
14960
15590
|
const maxPerCategoryRaw = resolveFlag(rest, "--max-per-category");
|
|
@@ -15031,7 +15661,7 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
15031
15661
|
console.error(`Context tree not found at ${treeDir}. Run 'remnic tree generate' first.`);
|
|
15032
15662
|
process.exit(1);
|
|
15033
15663
|
}
|
|
15034
|
-
const indexPath =
|
|
15664
|
+
const indexPath = path16.join(treeDir, "INDEX.md");
|
|
15035
15665
|
if (!fs13.existsSync(indexPath)) {
|
|
15036
15666
|
console.error(`INDEX.md missing in ${treeDir}. Tree may be corrupt \u2014 regenerate.`);
|
|
15037
15667
|
process.exit(1);
|
|
@@ -15502,9 +16132,9 @@ Usage:
|
|
|
15502
16132
|
remnic extensions <list|show|validate|reload> Manage memory extensions
|
|
15503
16133
|
remnic space <list|switch|create|delete|push|pull|share|promote|audit> Manage spaces
|
|
15504
16134
|
create accepts --parent <id> to set parent-child relationship
|
|
15505
|
-
remnic bench <list|run|published|datasets|runs|compare|results|baseline|export|publish|ui|providers|judge-calibrate|attribute|drift-gen> [benchmark...] [--quick] [--all] [--dataset-dir <path>] [--results-dir <path>] [--baselines-dir <path>] [--threshold <value>] [--detail] [--format <json|csv|html>] [--output <path>] [--target remnic-ai] [--json]
|
|
16135
|
+
remnic bench <list|run|published|datasets|runs|compare|results|baseline|export|publish|ui|providers|judge-calibrate|attribute|drift-gen|coding> [benchmark...] [--quick] [--all] [--dataset-dir <path>] [--results-dir <path>] [--baselines-dir <path>] [--threshold <value>] [--detail] [--format <json|csv|html>] [--output <path>] [--target remnic-ai] [--json]
|
|
15506
16136
|
benchmark is kept as a compatibility alias. check/report remain under that alias.
|
|
15507
|
-
remnic benchmark <list|run|datasets|runs|compare|results|baseline|export|publish|ui|providers|check|report|attribute|drift-gen> [queries...] [--explain] [--baseline=<path>] [--report=<path>]
|
|
16137
|
+
remnic benchmark <list|run|datasets|runs|compare|results|baseline|export|publish|ui|providers|check|report|attribute|drift-gen|coding> [queries...] [--explain] [--baseline=<path>] [--report=<path>]
|
|
15508
16138
|
remnic briefing [--since <window>] [--focus <filter>] [--save] [--format markdown|json]
|
|
15509
16139
|
Daily context briefing. Windows: yesterday, today, NNh, NNd, NNw.
|
|
15510
16140
|
Focus: person:<name>, project:<name>, topic:<name>.
|