@remnic/plugin-openclaw 1.0.10 → 1.0.12
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/{calibration-674TDQNV.js → calibration-WCHOK6DX.js} +12 -4
- package/dist/capsule-cli-GBM3WPAM.js +33 -0
- package/dist/capsule-crypto-K3IRTKRH.js +17 -0
- package/dist/capsule-export-IXVERCQG.js +17 -0
- package/dist/capsule-import-IA6VIOPQ.js +16 -0
- package/dist/capsule-merge-IWOQ34KL.js +189 -0
- package/dist/{causal-chain-OKDZSDEB.js → causal-chain-WYN5QOPS.js} +3 -2
- package/dist/{causal-consolidation-5BEXLQV5.js → causal-consolidation-YI53C2AO.js} +16 -12
- package/dist/{causal-retrieval-3BKBXVXD.js → causal-retrieval-NZHQOZOE.js} +6 -5
- package/dist/{causal-trajectory-graph-RQIT37DN.js → causal-trajectory-graph-VBPE2WPM.js} +1 -1
- package/dist/chunk-37NKFWSO.js +233 -0
- package/dist/chunk-3G7FAF6S.js +60 -0
- package/dist/{chunk-Z7GRLVK3.js → chunk-3GUF7RQI.js} +235 -19
- package/dist/chunk-4G2XCSD2.js +186 -0
- package/dist/chunk-4LYQ4ONL.js +185 -0
- package/dist/chunk-6F6EKSVP.js +453 -0
- package/dist/chunk-6IWEAUN6.js +148 -0
- package/dist/{chunk-LN5UZQVG.js → chunk-6UFI73TJ.js} +5 -3
- package/dist/chunk-7OQEPGQF.js +529 -0
- package/dist/{chunk-JJSNPSCD.js → chunk-7UZNLMW5.js} +652 -174
- package/dist/chunk-B52XADV3.js +244 -0
- package/dist/chunk-BU5KJVWF.js +78 -0
- package/dist/chunk-CDAZGIGT.js +720 -0
- package/dist/chunk-CXM7EBAO.js +289 -0
- package/dist/{chunk-HCFFXBLV.js → chunk-EXDYWXMB.js} +6 -1861
- package/dist/chunk-FGTYFLL5.js +274 -0
- package/dist/chunk-FQRSVYY4.js +110 -0
- package/dist/chunk-HRGFO6AW.js +349 -0
- package/dist/chunk-I6B2W2IY.js +47 -0
- package/dist/chunk-JZBOXOUC.js +259 -0
- package/dist/chunk-L6I4MQKO.js +227 -0
- package/dist/chunk-LLUROTZJ.js +328 -0
- package/dist/chunk-MBIFE6SA.js +250 -0
- package/dist/chunk-NKVIN6RD.js +118 -0
- package/dist/chunk-OAE7AQ6R.js +1832 -0
- package/dist/chunk-OEI7GLV2.js +17 -0
- package/dist/chunk-RKR6PTPA.js +308 -0
- package/dist/{chunk-7TENHBV2.js → chunk-RQCTMECT.js} +10 -48
- package/dist/chunk-SSFTU6LP.js +182 -0
- package/dist/{chunk-BXTMZDRT.js → chunk-SVSQAG6M.js} +7 -5
- package/dist/{chunk-S2ISS4AH.js → chunk-TILAJIJR.js} +10 -10
- package/dist/chunk-TLVIQLB4.js +874 -0
- package/dist/{chunk-YHH3SXKD.js → chunk-WPINX4MF.js} +1 -59
- package/dist/chunk-YGGGUTG3.js +125 -0
- package/dist/cipher-VHAFCG7Z.js +27 -0
- package/dist/dreams-ledger-3I52ISYR.js +285 -0
- package/dist/{engine-65C2J63X.js → engine-BIYI3P4J.js} +7 -2
- package/dist/{fallback-llm-LVK5PDIM.js → fallback-llm-WCWNGIQ3.js} +2 -1
- package/dist/first-start-migration-I24M2JEE.js +258 -0
- package/dist/forget-NI4RBDPB.js +68 -0
- package/dist/fs-utils-PZRI2HDZ.js +29 -0
- package/dist/graph-edge-decay-5CVKWBYH.js +203 -0
- package/dist/index.js +10654 -3067
- package/dist/kdf-H5B23ZM2.js +25 -0
- package/dist/memory-governance-SJ5DGRB3.js +25 -0
- package/dist/metadata-JAGIWHEA.js +20 -0
- package/dist/migrate-from-identity-anchor-N3354WMP.js +7 -0
- package/dist/path-5LCUBAAZ.js +8 -0
- package/dist/peers-JF2I6RCR.js +43 -0
- package/dist/purge-XN2VSPZ2.js +204 -0
- package/dist/secure-store-A4NGCNXV.js +155 -0
- package/dist/state-PVISYXRH.js +7 -0
- package/dist/state-store-LP5BO6SF.js +15 -0
- package/dist/{storage-DM4ZGOCN.js → storage-PTQ2H2YJ.js} +3 -1
- package/dist/tier-stats-IZNW66NC.js +147 -0
- package/dist/trace-NJESSGH7.js +289 -0
- package/dist/tui-MGK2LYJY.js +12 -0
- package/dist/types-R4DO7AKM.js +30 -0
- package/openclaw.plugin.json +519 -4
- package/package.json +2 -2
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import {
|
|
2
|
+
parseExportBundle
|
|
3
|
+
} from "./chunk-4LYQ4ONL.js";
|
|
4
|
+
import {
|
|
5
|
+
assertIsDirectoryNotSymlink,
|
|
6
|
+
assertRealpathInsideRoot,
|
|
7
|
+
fromPosixRelPath,
|
|
8
|
+
isPathInsideRoot,
|
|
9
|
+
sha256String
|
|
10
|
+
} from "./chunk-NKVIN6RD.js";
|
|
11
|
+
import {
|
|
12
|
+
decryptCapsuleFileInMemory,
|
|
13
|
+
isEncryptedCapsuleFile
|
|
14
|
+
} from "./chunk-SSFTU6LP.js";
|
|
15
|
+
import {
|
|
16
|
+
createVersion
|
|
17
|
+
} from "./chunk-6OJAU466.js";
|
|
18
|
+
|
|
19
|
+
// ../remnic-core/src/transfer/capsule-import.ts
|
|
20
|
+
import { lstat, mkdir, readFile, realpath, stat, writeFile } from "fs/promises";
|
|
21
|
+
import path from "path";
|
|
22
|
+
import { createHash, randomUUID } from "crypto";
|
|
23
|
+
import { gunzipSync } from "zlib";
|
|
24
|
+
async function importCapsule(opts) {
|
|
25
|
+
const archiveAbs = path.resolve(opts.archivePath);
|
|
26
|
+
const rootAbs = path.resolve(opts.root);
|
|
27
|
+
await assertIsDirectoryNotSymlink(rootAbs, "importCapsule", "root");
|
|
28
|
+
const mode = opts.mode ?? "skip";
|
|
29
|
+
if (mode !== "skip" && mode !== "overwrite" && mode !== "fork") {
|
|
30
|
+
throw new Error(
|
|
31
|
+
`importCapsule: unknown mode ${JSON.stringify(mode)}; expected "skip", "overwrite", or "fork"`
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
const encrypted = await isEncryptedCapsuleFile(archiveAbs);
|
|
35
|
+
let raw;
|
|
36
|
+
if (encrypted) {
|
|
37
|
+
if (!opts.memoryDir) {
|
|
38
|
+
throw new Error(
|
|
39
|
+
`importCapsule: archive is encrypted but 'memoryDir' was not provided. Pass the memory directory so the secure-store key can be retrieved, or run \`remnic secure-store unlock\` before importing.`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
raw = await decryptCapsuleFileInMemory(archiveAbs, opts.memoryDir);
|
|
43
|
+
} else {
|
|
44
|
+
raw = await readFile(archiveAbs);
|
|
45
|
+
}
|
|
46
|
+
const json = gunzipSync(raw).toString("utf-8");
|
|
47
|
+
let parsedJson;
|
|
48
|
+
try {
|
|
49
|
+
parsedJson = JSON.parse(json);
|
|
50
|
+
} catch (cause) {
|
|
51
|
+
throw new Error(
|
|
52
|
+
`importCapsule: archive is not valid JSON after gunzip: ${archiveAbs}`,
|
|
53
|
+
{ cause }
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
const parsed = parseExportBundle(parsedJson);
|
|
57
|
+
if (parsed.capsuleVersion !== 2) {
|
|
58
|
+
throw new Error(
|
|
59
|
+
"importCapsule: archive is V1; only V2 capsule archives are supported"
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
const bundle = parsed.bundle;
|
|
63
|
+
const manifest = bundle.manifest;
|
|
64
|
+
const capsule = manifest.capsule;
|
|
65
|
+
const manifestIndex = /* @__PURE__ */ new Map();
|
|
66
|
+
for (const f of manifest.files) {
|
|
67
|
+
manifestIndex.set(f.path, f);
|
|
68
|
+
}
|
|
69
|
+
if (manifestIndex.size !== manifest.files.length) {
|
|
70
|
+
throw new Error(
|
|
71
|
+
"importCapsule: manifest contains duplicate file paths"
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
const recordPaths = /* @__PURE__ */ new Set();
|
|
75
|
+
for (const rec of bundle.records) {
|
|
76
|
+
if (recordPaths.has(rec.path)) {
|
|
77
|
+
throw new Error(
|
|
78
|
+
`importCapsule: bundle contains duplicate record path: ${rec.path}`
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
recordPaths.add(rec.path);
|
|
82
|
+
}
|
|
83
|
+
const rootReal = await realpath(rootAbs).catch(() => rootAbs);
|
|
84
|
+
const seenTargetPaths = /* @__PURE__ */ new Map();
|
|
85
|
+
for (const rec of bundle.records) {
|
|
86
|
+
const entry = manifestIndex.get(rec.path);
|
|
87
|
+
if (!entry) {
|
|
88
|
+
throw new Error(
|
|
89
|
+
`importCapsule: archive checksum mismatch (record without manifest entry: ${rec.path})`
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
const { sha256, bytes } = sha256String(rec.content);
|
|
93
|
+
if (sha256 !== entry.sha256 || bytes !== entry.bytes) {
|
|
94
|
+
throw new Error(
|
|
95
|
+
`importCapsule: archive checksum mismatch for ${rec.path}: expected sha256=${entry.sha256} bytes=${entry.bytes}, got sha256=${sha256} bytes=${bytes}`
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
if (rec.path.includes("\\")) {
|
|
99
|
+
throw new Error(
|
|
100
|
+
`importCapsule: record path contains backslash separators (Windows-style paths are not allowed): ${rec.path}`
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
const posixNormalized = path.posix.normalize(rec.path);
|
|
104
|
+
if (rec.path.startsWith("/") || rec.path.split("/").some((seg) => seg === "..") || posixNormalized.startsWith("..") || posixNormalized.startsWith("/")) {
|
|
105
|
+
throw new Error(
|
|
106
|
+
`importCapsule: record path escapes target root: ${rec.path}`
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
const targetRel = computeTargetPath(rec.path, mode, capsule.id);
|
|
110
|
+
const targetAbs = path.join(rootReal, fromPosixRelPath(targetRel));
|
|
111
|
+
if (!isPathInsideRoot(rootReal, targetAbs)) {
|
|
112
|
+
throw new Error(
|
|
113
|
+
`importCapsule: record path escapes target root: ${rec.path}`
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
await assertRealpathInsideRoot(rootReal, targetAbs, rec.path, "importCapsule");
|
|
117
|
+
const targetLstat = await lstat(targetAbs).catch(() => null);
|
|
118
|
+
if (targetLstat !== null && targetLstat.isSymbolicLink()) {
|
|
119
|
+
throw new Error(
|
|
120
|
+
`importCapsule: record target is a symlink and cannot be written to safely: ${rec.path}`
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
const dedupKey = targetAbs.toLowerCase();
|
|
124
|
+
const firstSourcePath = seenTargetPaths.get(dedupKey);
|
|
125
|
+
if (firstSourcePath !== void 0) {
|
|
126
|
+
throw new Error(
|
|
127
|
+
`importCapsule: manifest contains two entries that resolve to the same target path: "${firstSourcePath}" and "${rec.path}" both map to "${targetRel}"`
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
seenTargetPaths.set(dedupKey, rec.path);
|
|
131
|
+
}
|
|
132
|
+
for (const f of manifest.files) {
|
|
133
|
+
if (!recordPaths.has(f.path)) {
|
|
134
|
+
throw new Error(
|
|
135
|
+
`importCapsule: archive checksum mismatch (manifest entry without record: ${f.path})`
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const imported = [];
|
|
140
|
+
const skipped = [];
|
|
141
|
+
const sortedRecords = [...bundle.records].sort(
|
|
142
|
+
(a, b) => a.path.localeCompare(b.path)
|
|
143
|
+
);
|
|
144
|
+
for (const rec of sortedRecords) {
|
|
145
|
+
const targetRel = computeTargetPath(rec.path, mode, capsule.id);
|
|
146
|
+
const targetAbs = path.join(rootReal, fromPosixRelPath(targetRel));
|
|
147
|
+
const exists = await fileExistsAt(targetAbs);
|
|
148
|
+
if ((mode === "skip" || mode === "fork") && exists) {
|
|
149
|
+
skipped.push({ path: rec.path, reason: "exists" });
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
let snapshotted = false;
|
|
153
|
+
if (mode === "overwrite" && exists) {
|
|
154
|
+
if (opts.versioning && opts.versioning.enabled) {
|
|
155
|
+
const prior = await readFile(targetAbs, "utf-8").catch(() => "");
|
|
156
|
+
await createVersion(
|
|
157
|
+
targetAbs,
|
|
158
|
+
prior,
|
|
159
|
+
"manual",
|
|
160
|
+
opts.versioning,
|
|
161
|
+
opts.log,
|
|
162
|
+
`capsule-import: ${capsule.id}`,
|
|
163
|
+
rootReal
|
|
164
|
+
);
|
|
165
|
+
snapshotted = true;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
let contentToWrite = rec.content;
|
|
169
|
+
let rewroteId = false;
|
|
170
|
+
if (mode === "fork") {
|
|
171
|
+
const forked = rewriteFrontmatterIdForFork(rec.content, capsule.id, opts.now);
|
|
172
|
+
contentToWrite = forked.content;
|
|
173
|
+
rewroteId = forked.rewrote;
|
|
174
|
+
}
|
|
175
|
+
await mkdir(path.dirname(targetAbs), { recursive: true });
|
|
176
|
+
await writeFile(targetAbs, contentToWrite, "utf-8");
|
|
177
|
+
imported.push({
|
|
178
|
+
sourcePath: rec.path,
|
|
179
|
+
targetPath: targetRel,
|
|
180
|
+
snapshotted,
|
|
181
|
+
rewroteId
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
skipped.sort((a, b) => a.path.localeCompare(b.path));
|
|
185
|
+
return { imported, skipped, manifest };
|
|
186
|
+
}
|
|
187
|
+
function computeTargetPath(sourcePosix, mode, capsuleId) {
|
|
188
|
+
if (mode === "fork") return `forks/${capsuleId}/${sourcePosix}`;
|
|
189
|
+
return sourcePosix;
|
|
190
|
+
}
|
|
191
|
+
async function fileExistsAt(absPath) {
|
|
192
|
+
const st = await stat(absPath).catch(() => null);
|
|
193
|
+
return st !== null && st.isFile();
|
|
194
|
+
}
|
|
195
|
+
function rewriteFrontmatterIdForFork(content, capsuleId, now) {
|
|
196
|
+
const crlfMatch = /^---(\r\n|\r|\n)/.exec(content);
|
|
197
|
+
const eol = crlfMatch ? crlfMatch[1] : "\n";
|
|
198
|
+
const fmMatch = /^---\r?\n([\s\S]*?)\r?\n---(\r?\n|$)/.exec(content);
|
|
199
|
+
if (!fmMatch) return { content, rewrote: false };
|
|
200
|
+
const fmBody = fmMatch[1];
|
|
201
|
+
const fmTrailer = fmMatch[2];
|
|
202
|
+
const fmStart = fmMatch.index;
|
|
203
|
+
const fmEnd = fmStart + fmMatch[0].length;
|
|
204
|
+
const idLineRe = /^id:[ \t]*([^\r\n]*)$/m;
|
|
205
|
+
const idMatch = idLineRe.exec(fmBody);
|
|
206
|
+
if (!idMatch) return { content, rewrote: false };
|
|
207
|
+
const newId = mintForkId(capsuleId, idMatch[1]?.trim() ?? "", now);
|
|
208
|
+
const replacedBody = fmBody.replace(idLineRe, `id: ${newId}`);
|
|
209
|
+
const prefix = content.slice(0, fmStart);
|
|
210
|
+
const tail = content.slice(fmEnd);
|
|
211
|
+
const rebuilt = `${prefix}---${eol}${replacedBody}${eol}---${fmTrailer}${tail}`;
|
|
212
|
+
return { content: rebuilt, rewrote: true };
|
|
213
|
+
}
|
|
214
|
+
function mintForkId(capsuleId, originalId, now) {
|
|
215
|
+
const base = originalId.length > 0 ? originalId : "fork";
|
|
216
|
+
if (now === void 0) {
|
|
217
|
+
const suffix2 = randomUUID().slice(0, 8);
|
|
218
|
+
return `${base}-fork-${capsuleId}-${suffix2}`;
|
|
219
|
+
}
|
|
220
|
+
const payload = JSON.stringify({ c: capsuleId, i: originalId, n: now });
|
|
221
|
+
const suffix = createHash("sha256").update(payload, "utf-8").digest("hex").slice(0, 8);
|
|
222
|
+
return `${base}-fork-${capsuleId}-${suffix}`;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export {
|
|
226
|
+
importCapsule
|
|
227
|
+
};
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
// ../remnic-core/src/capsule-cli.ts
|
|
2
|
+
import path from "path";
|
|
3
|
+
function parseCapsuleOutputFormat(raw) {
|
|
4
|
+
if (raw === void 0 || raw === null) return "text";
|
|
5
|
+
if (typeof raw !== "string" || raw.trim() === "") {
|
|
6
|
+
throw new Error(
|
|
7
|
+
`--format expects one of text, markdown, json; got ${JSON.stringify(raw)}`
|
|
8
|
+
);
|
|
9
|
+
}
|
|
10
|
+
const v = raw.trim();
|
|
11
|
+
if (v !== "text" && v !== "markdown" && v !== "json") {
|
|
12
|
+
throw new Error(
|
|
13
|
+
`--format expects one of text, markdown, json; got ${JSON.stringify(raw)}`
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
return v;
|
|
17
|
+
}
|
|
18
|
+
function parseCapsuleImportMode(raw) {
|
|
19
|
+
if (raw === void 0 || raw === null) return "skip";
|
|
20
|
+
if (typeof raw !== "string" || raw.trim() === "") {
|
|
21
|
+
throw new Error(
|
|
22
|
+
`--mode expects one of skip, overwrite, fork; got ${JSON.stringify(raw)}`
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
const v = raw.trim();
|
|
26
|
+
if (v !== "skip" && v !== "overwrite" && v !== "fork") {
|
|
27
|
+
throw new Error(
|
|
28
|
+
`--mode expects one of skip, overwrite, fork; got ${JSON.stringify(raw)}`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
return v;
|
|
32
|
+
}
|
|
33
|
+
function parseCapsuleConflictMode(raw) {
|
|
34
|
+
if (raw === void 0 || raw === null) return "skip-conflicts";
|
|
35
|
+
if (typeof raw !== "string" || raw.trim() === "") {
|
|
36
|
+
throw new Error(
|
|
37
|
+
`--conflict-mode expects one of skip-conflicts, prefer-source, prefer-local; got ${JSON.stringify(raw)}`
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
const v = raw.trim();
|
|
41
|
+
if (v !== "skip-conflicts" && v !== "prefer-source" && v !== "prefer-local") {
|
|
42
|
+
throw new Error(
|
|
43
|
+
`--conflict-mode expects one of skip-conflicts, prefer-source, prefer-local; got ${JSON.stringify(raw)}`
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
return v;
|
|
47
|
+
}
|
|
48
|
+
var ISO_8601_RE = /^\d{4}-\d{2}-\d{2}(?:[Tt]\d{2}:\d{2}(?::\d{2}(?:\.\d{1,9})?)?(?:[Zz]|[+-]\d{2}:?\d{2}))?$/;
|
|
49
|
+
function parseCapsuleSince(raw) {
|
|
50
|
+
if (raw === void 0 || raw === null) return void 0;
|
|
51
|
+
if (typeof raw !== "string") {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`--since expects an ISO 8601 timestamp; got ${JSON.stringify(raw)}`
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
if (raw.trim() === "") {
|
|
57
|
+
throw new Error(`--since expects an ISO 8601 timestamp; received empty string`);
|
|
58
|
+
}
|
|
59
|
+
if (!ISO_8601_RE.test(raw)) {
|
|
60
|
+
throw new Error(
|
|
61
|
+
`--since is not a valid ISO 8601 timestamp: ${raw}. Accepted forms: YYYY-MM-DD, YYYY-MM-DDTHH:MM:SSZ, YYYY-MM-DDTHH:MM:SS\xB1HH:MM`
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
const ms = Date.parse(raw);
|
|
65
|
+
if (!Number.isFinite(ms)) {
|
|
66
|
+
throw new Error(`--since is not a valid ISO 8601 timestamp: ${raw}`);
|
|
67
|
+
}
|
|
68
|
+
const m = /^(\d{4})-(\d{2})-(\d{2})/.exec(raw);
|
|
69
|
+
if (m) {
|
|
70
|
+
const offsetMatch = /([+-])(\d{2}):?(\d{2})$/.exec(raw);
|
|
71
|
+
let displayMs = ms;
|
|
72
|
+
if (offsetMatch) {
|
|
73
|
+
const sign = offsetMatch[1] === "-" ? -1 : 1;
|
|
74
|
+
const offsetMin = sign * (Number(offsetMatch[2]) * 60 + Number(offsetMatch[3]));
|
|
75
|
+
displayMs = ms + offsetMin * 6e4;
|
|
76
|
+
}
|
|
77
|
+
const dd = new Date(displayMs);
|
|
78
|
+
if (dd.getUTCFullYear() !== Number(m[1]) || dd.getUTCMonth() + 1 !== Number(m[2]) || dd.getUTCDate() !== Number(m[3])) {
|
|
79
|
+
throw new Error(
|
|
80
|
+
`--since: calendar overflow \u2014 ${raw} normalises to a different calendar date`
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return raw;
|
|
85
|
+
}
|
|
86
|
+
function parseCapsuleIncludeKinds(raw) {
|
|
87
|
+
if (raw === void 0 || raw === null) return void 0;
|
|
88
|
+
if (typeof raw !== "string") {
|
|
89
|
+
throw new Error(
|
|
90
|
+
`--include-kinds expects a comma-separated list of directory names; got ${JSON.stringify(raw)}`
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
if (raw.trim() === "") {
|
|
94
|
+
throw new Error(
|
|
95
|
+
`--include-kinds expects at least one non-empty kind name`
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
const parts = raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
99
|
+
if (parts.length === 0) {
|
|
100
|
+
throw new Error(`--include-kinds expects at least one non-empty kind name`);
|
|
101
|
+
}
|
|
102
|
+
for (const p of parts) {
|
|
103
|
+
if (p.includes("/") || p.includes("\\")) {
|
|
104
|
+
throw new Error(
|
|
105
|
+
`--include-kinds entries must be top-level directory names (no path separators): ${p}`
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return [...new Set(parts)];
|
|
110
|
+
}
|
|
111
|
+
function parseCapsulePeers(raw) {
|
|
112
|
+
if (raw === void 0 || raw === null) return void 0;
|
|
113
|
+
if (typeof raw !== "string") {
|
|
114
|
+
throw new Error(
|
|
115
|
+
`--peers expects a comma-separated list of peer ids; got ${JSON.stringify(raw)}`
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
if (raw.trim() === "") {
|
|
119
|
+
throw new Error(`--peers expects at least one non-empty peer id`);
|
|
120
|
+
}
|
|
121
|
+
const parts = raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
|
|
122
|
+
if (parts.length === 0) {
|
|
123
|
+
throw new Error(`--peers expects at least one non-empty peer id`);
|
|
124
|
+
}
|
|
125
|
+
for (const p of parts) {
|
|
126
|
+
if (p.includes("/") || p.includes("\\") || p === "." || p === "..") {
|
|
127
|
+
throw new Error(
|
|
128
|
+
`--peers entries must be plain id tokens (no path separators): ${p}`
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return [...new Set(parts)];
|
|
133
|
+
}
|
|
134
|
+
function parseCapsuleExportOptions(nameArg, opts) {
|
|
135
|
+
if (typeof nameArg !== "string" || nameArg.trim() === "") {
|
|
136
|
+
throw new Error(
|
|
137
|
+
`capsule export: <name> is required (e.g. "remnic capsule export my-capsule")`
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
const name = nameArg.trim();
|
|
141
|
+
const out = typeof opts.out === "string" && opts.out.trim() !== "" ? opts.out.trim() : void 0;
|
|
142
|
+
const since = parseCapsuleSince(opts.since);
|
|
143
|
+
const includeKinds = parseCapsuleIncludeKinds(opts.includeKinds);
|
|
144
|
+
const peers = parseCapsulePeers(opts.peers);
|
|
145
|
+
return { name, out, since, includeKinds, peers };
|
|
146
|
+
}
|
|
147
|
+
function parseCapsuleImportOptions(archiveArg, opts) {
|
|
148
|
+
if (typeof archiveArg !== "string" || archiveArg.trim() === "") {
|
|
149
|
+
throw new Error(
|
|
150
|
+
`capsule import: <archive> path is required (e.g. "remnic capsule import /path/to/my-capsule.capsule.json.gz")`
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
const archive = archiveArg.trim();
|
|
154
|
+
const mode = parseCapsuleImportMode(opts.mode);
|
|
155
|
+
return { archive, mode };
|
|
156
|
+
}
|
|
157
|
+
function parseCapsuleMergeOptions(archiveArg, opts) {
|
|
158
|
+
if (typeof archiveArg !== "string" || archiveArg.trim() === "") {
|
|
159
|
+
throw new Error(
|
|
160
|
+
`capsule merge: <archive> path is required (e.g. "remnic capsule merge /path/to/my-capsule.capsule.json.gz")`
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
const archive = archiveArg.trim();
|
|
164
|
+
const conflictMode = parseCapsuleConflictMode(opts.conflictMode);
|
|
165
|
+
return { archive, conflictMode };
|
|
166
|
+
}
|
|
167
|
+
function parseCapsuleListOptions(opts, defaultCapsulesDir2) {
|
|
168
|
+
const format = parseCapsuleOutputFormat(opts.format);
|
|
169
|
+
const rawDir = typeof opts.dir === "string" && opts.dir.trim() !== "" ? opts.dir.trim() : void 0;
|
|
170
|
+
const capsulesDir = rawDir ?? defaultCapsulesDir2;
|
|
171
|
+
return { format, capsulesDir };
|
|
172
|
+
}
|
|
173
|
+
function parseCapsuleInspectOptions(archiveArg, opts) {
|
|
174
|
+
if (typeof archiveArg !== "string" || archiveArg.trim() === "") {
|
|
175
|
+
throw new Error(
|
|
176
|
+
`capsule inspect: <archive> path is required (e.g. "remnic capsule inspect my-capsule.capsule.json.gz")`
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
const archive = archiveArg.trim();
|
|
180
|
+
const format = parseCapsuleOutputFormat(opts.format);
|
|
181
|
+
return { archive, format };
|
|
182
|
+
}
|
|
183
|
+
function defaultCapsulesDir(memoryDir) {
|
|
184
|
+
return path.join(memoryDir, ".capsules");
|
|
185
|
+
}
|
|
186
|
+
function escapeMarkdownCell(v) {
|
|
187
|
+
return v.replace(/\\/g, "\\\\").replace(/\|/g, "\\|");
|
|
188
|
+
}
|
|
189
|
+
function renderCapsuleList(entries, format) {
|
|
190
|
+
if (format === "json") {
|
|
191
|
+
return JSON.stringify({ capsules: entries }, null, 2);
|
|
192
|
+
}
|
|
193
|
+
if (entries.length === 0) {
|
|
194
|
+
if (format === "markdown") return "_No capsule archives found._\n";
|
|
195
|
+
return "No capsule archives found.\n";
|
|
196
|
+
}
|
|
197
|
+
if (format === "markdown") {
|
|
198
|
+
const rows = entries.map((e) => {
|
|
199
|
+
const id = escapeMarkdownCell(e.id);
|
|
200
|
+
const date = escapeMarkdownCell(e.createdAt?.slice(0, 10) ?? "\u2014");
|
|
201
|
+
const ver = escapeMarkdownCell(e.pluginVersion ?? "\u2014");
|
|
202
|
+
const files = e.fileCount !== null ? String(e.fileCount) : "\u2014";
|
|
203
|
+
const desc = escapeMarkdownCell(
|
|
204
|
+
e.description != null && e.description.trim() !== "" ? e.description.trim() : "\u2014"
|
|
205
|
+
);
|
|
206
|
+
return `| \`${id}\` | ${date} | ${ver} | ${files} | ${desc} |`;
|
|
207
|
+
});
|
|
208
|
+
return [
|
|
209
|
+
"# Capsule archives",
|
|
210
|
+
"",
|
|
211
|
+
"| ID | Created | Version | Files | Description |",
|
|
212
|
+
"| -- | ------- | ------- | ----- | ----------- |",
|
|
213
|
+
...rows,
|
|
214
|
+
""
|
|
215
|
+
].join("\n");
|
|
216
|
+
}
|
|
217
|
+
const lines = entries.map((e) => {
|
|
218
|
+
const date = e.createdAt?.slice(0, 10) ?? "\u2014";
|
|
219
|
+
const files = e.fileCount !== null ? `${e.fileCount} file${e.fileCount !== 1 ? "s" : ""}` : "? files";
|
|
220
|
+
const desc = e.description != null && e.description.trim() !== "" ? ` ${e.description.trim()}` : "";
|
|
221
|
+
return `${e.id} [${date}] [${files}]${desc}`;
|
|
222
|
+
});
|
|
223
|
+
return lines.join("\n") + "\n";
|
|
224
|
+
}
|
|
225
|
+
function renderCapsuleInspect(manifest, format) {
|
|
226
|
+
if (format === "json") {
|
|
227
|
+
return JSON.stringify(manifest, null, 2);
|
|
228
|
+
}
|
|
229
|
+
const {
|
|
230
|
+
capsuleId,
|
|
231
|
+
version,
|
|
232
|
+
schemaVersion,
|
|
233
|
+
createdAt,
|
|
234
|
+
pluginVersion,
|
|
235
|
+
fileCount,
|
|
236
|
+
includesTranscripts,
|
|
237
|
+
description,
|
|
238
|
+
parentCapsule,
|
|
239
|
+
retrievalPolicy,
|
|
240
|
+
includes,
|
|
241
|
+
topFiles
|
|
242
|
+
} = manifest;
|
|
243
|
+
if (format === "markdown") {
|
|
244
|
+
const policyLines = Object.entries(retrievalPolicy.tierWeights ?? {}).map(
|
|
245
|
+
([k, v]) => ` - ${k}: ${v}`
|
|
246
|
+
);
|
|
247
|
+
const policyBlock = policyLines.length > 0 ? policyLines.join("\n") : " _(no tier weight overrides)_";
|
|
248
|
+
return [
|
|
249
|
+
`# Capsule: \`${capsuleId}\``,
|
|
250
|
+
"",
|
|
251
|
+
`**Version:** ${version} `,
|
|
252
|
+
`**Schema version:** ${schemaVersion} `,
|
|
253
|
+
`**Created:** ${createdAt ?? "\u2014"} `,
|
|
254
|
+
`**Plugin version:** ${pluginVersion ?? "\u2014"} `,
|
|
255
|
+
`**Files:** ${fileCount} `,
|
|
256
|
+
`**Includes transcripts:** ${includesTranscripts ? "yes" : "no"} `,
|
|
257
|
+
`**Parent capsule:** ${parentCapsule ?? "_none_"} `,
|
|
258
|
+
`**Description:** ${description && description.trim() !== "" ? description.trim() : "_none_"} `,
|
|
259
|
+
"",
|
|
260
|
+
"## Includes",
|
|
261
|
+
`- taxonomy: ${includes.taxonomy ? "yes" : "no"}`,
|
|
262
|
+
`- identityAnchors: ${includes.identityAnchors ? "yes" : "no"}`,
|
|
263
|
+
`- peerProfiles: ${includes.peerProfiles ? "yes" : "no"}`,
|
|
264
|
+
`- procedural: ${includes.procedural ? "yes" : "no"}`,
|
|
265
|
+
"",
|
|
266
|
+
"## Retrieval policy",
|
|
267
|
+
`- directAnswerEnabled: ${retrievalPolicy.directAnswerEnabled ? "yes" : "no"}`,
|
|
268
|
+
`- tierWeights:`,
|
|
269
|
+
policyBlock,
|
|
270
|
+
"",
|
|
271
|
+
...topFiles.length > 0 ? [
|
|
272
|
+
`## Files (${fileCount} total, showing first ${topFiles.length})`,
|
|
273
|
+
...topFiles.map((f) => `- \`${f}\``),
|
|
274
|
+
""
|
|
275
|
+
] : [`## Files (${fileCount} total)`, "_Empty capsule._", ""]
|
|
276
|
+
].join("\n");
|
|
277
|
+
}
|
|
278
|
+
const lines = [
|
|
279
|
+
`Capsule: ${capsuleId}`,
|
|
280
|
+
` version: ${version}`,
|
|
281
|
+
` schema: ${schemaVersion}`,
|
|
282
|
+
` created: ${createdAt ?? "\u2014"}`,
|
|
283
|
+
` plugin: ${pluginVersion ?? "\u2014"}`,
|
|
284
|
+
` files: ${fileCount}`,
|
|
285
|
+
` transcripts: ${includesTranscripts ? "yes" : "no"}`,
|
|
286
|
+
` parent: ${parentCapsule ?? "(none)"}`,
|
|
287
|
+
` description: ${description && description.trim() !== "" ? description.trim() : "(none)"}`,
|
|
288
|
+
"",
|
|
289
|
+
` includes.taxonomy: ${includes.taxonomy ? "yes" : "no"}`,
|
|
290
|
+
` includes.identityAnchors: ${includes.identityAnchors ? "yes" : "no"}`,
|
|
291
|
+
` includes.peerProfiles: ${includes.peerProfiles ? "yes" : "no"}`,
|
|
292
|
+
` includes.procedural: ${includes.procedural ? "yes" : "no"}`,
|
|
293
|
+
"",
|
|
294
|
+
` policy.directAnswer: ${retrievalPolicy.directAnswerEnabled ? "yes" : "no"}`
|
|
295
|
+
];
|
|
296
|
+
const weights = Object.entries(retrievalPolicy.tierWeights ?? {});
|
|
297
|
+
if (weights.length > 0) {
|
|
298
|
+
for (const [k, v] of weights) {
|
|
299
|
+
lines.push(` policy.tierWeight[${k}]: ${v}`);
|
|
300
|
+
}
|
|
301
|
+
} else {
|
|
302
|
+
lines.push(` policy.tierWeights: (none)`);
|
|
303
|
+
}
|
|
304
|
+
if (topFiles.length > 0) {
|
|
305
|
+
lines.push("", ` files (first ${topFiles.length} of ${fileCount}):`);
|
|
306
|
+
for (const f of topFiles) {
|
|
307
|
+
lines.push(` ${f}`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
return lines.join("\n") + "\n";
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
export {
|
|
314
|
+
parseCapsuleOutputFormat,
|
|
315
|
+
parseCapsuleImportMode,
|
|
316
|
+
parseCapsuleConflictMode,
|
|
317
|
+
parseCapsuleSince,
|
|
318
|
+
parseCapsuleIncludeKinds,
|
|
319
|
+
parseCapsulePeers,
|
|
320
|
+
parseCapsuleExportOptions,
|
|
321
|
+
parseCapsuleImportOptions,
|
|
322
|
+
parseCapsuleMergeOptions,
|
|
323
|
+
parseCapsuleListOptions,
|
|
324
|
+
parseCapsuleInspectOptions,
|
|
325
|
+
defaultCapsulesDir,
|
|
326
|
+
renderCapsuleList,
|
|
327
|
+
renderCapsuleInspect
|
|
328
|
+
};
|