@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.
Files changed (70) hide show
  1. package/dist/{calibration-674TDQNV.js → calibration-WCHOK6DX.js} +12 -4
  2. package/dist/capsule-cli-GBM3WPAM.js +33 -0
  3. package/dist/capsule-crypto-K3IRTKRH.js +17 -0
  4. package/dist/capsule-export-IXVERCQG.js +17 -0
  5. package/dist/capsule-import-IA6VIOPQ.js +16 -0
  6. package/dist/capsule-merge-IWOQ34KL.js +189 -0
  7. package/dist/{causal-chain-OKDZSDEB.js → causal-chain-WYN5QOPS.js} +3 -2
  8. package/dist/{causal-consolidation-5BEXLQV5.js → causal-consolidation-YI53C2AO.js} +16 -12
  9. package/dist/{causal-retrieval-3BKBXVXD.js → causal-retrieval-NZHQOZOE.js} +6 -5
  10. package/dist/{causal-trajectory-graph-RQIT37DN.js → causal-trajectory-graph-VBPE2WPM.js} +1 -1
  11. package/dist/chunk-37NKFWSO.js +233 -0
  12. package/dist/chunk-3G7FAF6S.js +60 -0
  13. package/dist/{chunk-Z7GRLVK3.js → chunk-3GUF7RQI.js} +235 -19
  14. package/dist/chunk-4G2XCSD2.js +186 -0
  15. package/dist/chunk-4LYQ4ONL.js +185 -0
  16. package/dist/chunk-6F6EKSVP.js +453 -0
  17. package/dist/chunk-6IWEAUN6.js +148 -0
  18. package/dist/{chunk-LN5UZQVG.js → chunk-6UFI73TJ.js} +5 -3
  19. package/dist/chunk-7OQEPGQF.js +529 -0
  20. package/dist/{chunk-JJSNPSCD.js → chunk-7UZNLMW5.js} +652 -174
  21. package/dist/chunk-B52XADV3.js +244 -0
  22. package/dist/chunk-BU5KJVWF.js +78 -0
  23. package/dist/chunk-CDAZGIGT.js +720 -0
  24. package/dist/chunk-CXM7EBAO.js +289 -0
  25. package/dist/{chunk-HCFFXBLV.js → chunk-EXDYWXMB.js} +6 -1861
  26. package/dist/chunk-FGTYFLL5.js +274 -0
  27. package/dist/chunk-FQRSVYY4.js +110 -0
  28. package/dist/chunk-HRGFO6AW.js +349 -0
  29. package/dist/chunk-I6B2W2IY.js +47 -0
  30. package/dist/chunk-JZBOXOUC.js +259 -0
  31. package/dist/chunk-L6I4MQKO.js +227 -0
  32. package/dist/chunk-LLUROTZJ.js +328 -0
  33. package/dist/chunk-MBIFE6SA.js +250 -0
  34. package/dist/chunk-NKVIN6RD.js +118 -0
  35. package/dist/chunk-OAE7AQ6R.js +1832 -0
  36. package/dist/chunk-OEI7GLV2.js +17 -0
  37. package/dist/chunk-RKR6PTPA.js +308 -0
  38. package/dist/{chunk-7TENHBV2.js → chunk-RQCTMECT.js} +10 -48
  39. package/dist/chunk-SSFTU6LP.js +182 -0
  40. package/dist/{chunk-BXTMZDRT.js → chunk-SVSQAG6M.js} +7 -5
  41. package/dist/{chunk-S2ISS4AH.js → chunk-TILAJIJR.js} +10 -10
  42. package/dist/chunk-TLVIQLB4.js +874 -0
  43. package/dist/{chunk-YHH3SXKD.js → chunk-WPINX4MF.js} +1 -59
  44. package/dist/chunk-YGGGUTG3.js +125 -0
  45. package/dist/cipher-VHAFCG7Z.js +27 -0
  46. package/dist/dreams-ledger-3I52ISYR.js +285 -0
  47. package/dist/{engine-65C2J63X.js → engine-BIYI3P4J.js} +7 -2
  48. package/dist/{fallback-llm-LVK5PDIM.js → fallback-llm-WCWNGIQ3.js} +2 -1
  49. package/dist/first-start-migration-I24M2JEE.js +258 -0
  50. package/dist/forget-NI4RBDPB.js +68 -0
  51. package/dist/fs-utils-PZRI2HDZ.js +29 -0
  52. package/dist/graph-edge-decay-5CVKWBYH.js +203 -0
  53. package/dist/index.js +10654 -3067
  54. package/dist/kdf-H5B23ZM2.js +25 -0
  55. package/dist/memory-governance-SJ5DGRB3.js +25 -0
  56. package/dist/metadata-JAGIWHEA.js +20 -0
  57. package/dist/migrate-from-identity-anchor-N3354WMP.js +7 -0
  58. package/dist/path-5LCUBAAZ.js +8 -0
  59. package/dist/peers-JF2I6RCR.js +43 -0
  60. package/dist/purge-XN2VSPZ2.js +204 -0
  61. package/dist/secure-store-A4NGCNXV.js +155 -0
  62. package/dist/state-PVISYXRH.js +7 -0
  63. package/dist/state-store-LP5BO6SF.js +15 -0
  64. package/dist/{storage-DM4ZGOCN.js → storage-PTQ2H2YJ.js} +3 -1
  65. package/dist/tier-stats-IZNW66NC.js +147 -0
  66. package/dist/trace-NJESSGH7.js +289 -0
  67. package/dist/tui-MGK2LYJY.js +12 -0
  68. package/dist/types-R4DO7AKM.js +30 -0
  69. package/openclaw.plugin.json +519 -4
  70. package/package.json +2 -2
@@ -0,0 +1,185 @@
1
+ import {
2
+ external_exports
3
+ } from "./chunk-EXDYWXMB.js";
4
+
5
+ // ../remnic-core/src/transfer/types.ts
6
+ var ExportManifestV1Schema = external_exports.object({
7
+ format: external_exports.literal("openclaw-engram-export"),
8
+ schemaVersion: external_exports.literal(1),
9
+ createdAt: external_exports.string(),
10
+ pluginVersion: external_exports.string(),
11
+ includesTranscripts: external_exports.boolean(),
12
+ files: external_exports.array(
13
+ external_exports.object({
14
+ path: external_exports.string(),
15
+ sha256: external_exports.string(),
16
+ bytes: external_exports.number().int().nonnegative()
17
+ })
18
+ )
19
+ });
20
+ var ExportMemoryRecordV1Schema = external_exports.object({
21
+ path: external_exports.string(),
22
+ content: external_exports.string()
23
+ });
24
+ var ExportBundleV1Schema = external_exports.object({
25
+ manifest: ExportManifestV1Schema,
26
+ records: external_exports.array(ExportMemoryRecordV1Schema)
27
+ });
28
+ var CAPSULE_ID_PATTERN = /^[A-Za-z0-9](?:[A-Za-z0-9]|-(?!-))*[A-Za-z0-9]$|^[A-Za-z0-9]$/;
29
+ var CapsuleIdSchema = external_exports.string().min(1, "capsule.id must not be empty").max(64, "capsule.id must be 64 characters or fewer").regex(
30
+ CAPSULE_ID_PATTERN,
31
+ "capsule.id must be alphanumeric with single dashes (no spaces, no leading/trailing dashes)"
32
+ );
33
+ var SemverLikeSchema = external_exports.string().min(1, "capsule.version must not be empty").regex(
34
+ /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/,
35
+ "capsule.version must be a semver-like string (e.g. 1.0.0)"
36
+ );
37
+ var CapsuleRetrievalPolicySchema = external_exports.object({
38
+ /**
39
+ * Per-tier weight overrides applied during recall when the capsule is the
40
+ * active scope. Keys are tier names (e.g. `bm25`, `vector`, `graph`); values
41
+ * are non-negative finite multipliers. The set of valid keys is left open
42
+ * for now so future tiers do not break older manifests.
43
+ */
44
+ tierWeights: external_exports.record(
45
+ external_exports.string().min(1),
46
+ external_exports.number().finite().nonnegative()
47
+ ),
48
+ /**
49
+ * Whether the direct-answer fast path is allowed when this capsule is
50
+ * active. Operators may disable it for capsules whose contents should
51
+ * always flow through the full retrieval pipeline.
52
+ */
53
+ directAnswerEnabled: external_exports.boolean()
54
+ });
55
+ var CapsuleIncludesSchema = external_exports.object({
56
+ taxonomy: external_exports.boolean(),
57
+ identityAnchors: external_exports.boolean(),
58
+ peerProfiles: external_exports.boolean(),
59
+ procedural: external_exports.boolean()
60
+ });
61
+ var CapsuleParentSchema = external_exports.object({
62
+ capsuleId: CapsuleIdSchema,
63
+ version: SemverLikeSchema,
64
+ /**
65
+ * Posix-relative path prefix under the fork's memory root where the parent
66
+ * capsule's records were placed. Typically `forks/<parent-capsule-id>`.
67
+ * Must be a non-empty string with no leading slash.
68
+ */
69
+ forkRoot: external_exports.string().min(1, "capsule.parent.forkRoot must not be empty").refine((v) => !v.startsWith("/"), {
70
+ message: "capsule.parent.forkRoot must be a relative path (no leading slash)"
71
+ })
72
+ });
73
+ var CapsuleBlockSchema = external_exports.object({
74
+ id: CapsuleIdSchema,
75
+ version: SemverLikeSchema,
76
+ /**
77
+ * Taxonomy schema version the capsule was authored against. Free-form for
78
+ * now; later PRs may tighten this to a known taxonomy registry.
79
+ */
80
+ schemaVersion: external_exports.string().min(1, "capsule.schemaVersion must not be empty"),
81
+ /**
82
+ * Optional reference to the parent capsule this one was forked or derived
83
+ * from. `null` (not `undefined`) is the explicit "no parent" sentinel so
84
+ * that round-trips through JSON do not silently drop the field.
85
+ *
86
+ * @deprecated Prefer {@link parent} (structured). This field is preserved
87
+ * for backward compatibility with V2 manifests written before PR 4/6.
88
+ */
89
+ parentCapsule: external_exports.string().min(1).nullable(),
90
+ /**
91
+ * Structured fork-lineage pointer added in PR 4/6. When present, this
92
+ * capsule is a fork of the described parent. `null` is the explicit
93
+ * "not a fork" sentinel — round-trips through JSON cleanly without
94
+ * silently dropping the field.
95
+ *
96
+ * Defaults to `null` so that V2 manifests written before PR 4/6 (which
97
+ * omit this field) still parse without error. This preserves backward
98
+ * compatibility with the existing `makeV2Manifest()` test fixtures and
99
+ * any in-the-wild V2 archives produced by PR 2/6 and PR 3/6.
100
+ *
101
+ * Invariant: when `parent` is non-null, `parentCapsule` SHOULD equal
102
+ * `parent.capsuleId` for backward compatibility with readers that only
103
+ * understand the legacy field. `forkCapsule()` sets both.
104
+ */
105
+ parent: CapsuleParentSchema.nullable().default(null),
106
+ description: external_exports.string(),
107
+ retrievalPolicy: CapsuleRetrievalPolicySchema,
108
+ includes: CapsuleIncludesSchema
109
+ });
110
+ var ExportManifestV2Schema = external_exports.object({
111
+ format: external_exports.literal("openclaw-engram-export"),
112
+ schemaVersion: external_exports.literal(2),
113
+ createdAt: external_exports.string(),
114
+ pluginVersion: external_exports.string(),
115
+ includesTranscripts: external_exports.boolean(),
116
+ files: external_exports.array(
117
+ external_exports.object({
118
+ path: external_exports.string(),
119
+ sha256: external_exports.string(),
120
+ bytes: external_exports.number().int().nonnegative()
121
+ })
122
+ ),
123
+ capsule: CapsuleBlockSchema
124
+ });
125
+ var ExportBundleV2Schema = external_exports.object({
126
+ manifest: ExportManifestV2Schema,
127
+ records: external_exports.array(ExportMemoryRecordV1Schema)
128
+ });
129
+ function parseExportManifest(input) {
130
+ const version = typeof input === "object" && input !== null ? input.schemaVersion : void 0;
131
+ if (version === 2) {
132
+ const manifest = ExportManifestV2Schema.parse(input);
133
+ return {
134
+ capsuleVersion: 2,
135
+ manifest,
136
+ capsule: manifest.capsule
137
+ };
138
+ }
139
+ if (version === 1) {
140
+ const manifest = ExportManifestV1Schema.parse(input);
141
+ return { capsuleVersion: 1, manifest, capsule: null };
142
+ }
143
+ const v1 = ExportManifestV1Schema.safeParse(input);
144
+ if (v1.success) {
145
+ return { capsuleVersion: 1, manifest: v1.data, capsule: null };
146
+ }
147
+ const v2 = ExportManifestV2Schema.safeParse(input);
148
+ if (v2.success) {
149
+ return {
150
+ capsuleVersion: 2,
151
+ manifest: v2.data,
152
+ capsule: v2.data.capsule
153
+ };
154
+ }
155
+ throw v2.error;
156
+ }
157
+ function parseExportBundle(input) {
158
+ if (typeof input !== "object" || input === null) {
159
+ ExportBundleV1Schema.parse(input);
160
+ throw new Error("unreachable");
161
+ }
162
+ const manifestRaw = input.manifest;
163
+ const normalized = parseExportManifest(manifestRaw);
164
+ if (normalized.capsuleVersion === 2) {
165
+ const bundle2 = ExportBundleV2Schema.parse(input);
166
+ return { capsuleVersion: 2, bundle: bundle2, capsule: bundle2.manifest.capsule };
167
+ }
168
+ const bundle = ExportBundleV1Schema.parse(input);
169
+ return { capsuleVersion: 1, bundle, capsule: null };
170
+ }
171
+
172
+ export {
173
+ ExportManifestV1Schema,
174
+ ExportMemoryRecordV1Schema,
175
+ ExportBundleV1Schema,
176
+ CAPSULE_ID_PATTERN,
177
+ CapsuleRetrievalPolicySchema,
178
+ CapsuleIncludesSchema,
179
+ CapsuleParentSchema,
180
+ CapsuleBlockSchema,
181
+ ExportManifestV2Schema,
182
+ ExportBundleV2Schema,
183
+ parseExportManifest,
184
+ parseExportBundle
185
+ };
@@ -0,0 +1,453 @@
1
+ import {
2
+ buildHeaderFromPassphrase,
3
+ deriveKeyFromHeader,
4
+ getKey,
5
+ headerPath,
6
+ lock,
7
+ readHeader,
8
+ secureStoreDir,
9
+ status,
10
+ unlock,
11
+ verifyKey,
12
+ writeHeader
13
+ } from "./chunk-CXM7EBAO.js";
14
+ import {
15
+ DEFAULT_ARGON2ID_PARAMS,
16
+ DEFAULT_SCRYPT_PARAMS,
17
+ KDF_SALT_LENGTH
18
+ } from "./chunk-6IWEAUN6.js";
19
+ import {
20
+ decryptMemoryDirToPlaintext,
21
+ migrateMemoryDirToEncrypted
22
+ } from "./chunk-RKR6PTPA.js";
23
+ import {
24
+ generateSalt
25
+ } from "./chunk-YGGGUTG3.js";
26
+
27
+ // ../remnic-core/src/secure-store/cli-handlers.ts
28
+ async function runSecureStoreInit(options) {
29
+ const { memoryDir, readPassphrase } = options;
30
+ if (typeof memoryDir !== "string" || memoryDir.length === 0) {
31
+ throw new Error("secure-store init: memoryDir is required");
32
+ }
33
+ const existing = await readHeader(memoryDir);
34
+ if (existing !== null) {
35
+ throw new Error(
36
+ `secure-store header already exists at ${headerPath(memoryDir)}. Run 'remnic secure-store status' to inspect, or remove the .secure-store directory explicitly to reinitialize.`
37
+ );
38
+ }
39
+ const passphrase = await readPassphrase("Enter new passphrase: ", { confirm: true });
40
+ validatePassphrase(passphrase);
41
+ const algorithm = options.algorithm ?? "argon2id";
42
+ const params = resolveParams(algorithm, options.params);
43
+ const salt = options.salt ?? generateSalt();
44
+ if (salt.length !== KDF_SALT_LENGTH) {
45
+ throw new Error(`salt must be ${KDF_SALT_LENGTH} bytes, got ${salt.length}`);
46
+ }
47
+ const built = buildHeaderFromPassphrase({
48
+ passphrase,
49
+ salt,
50
+ algorithm,
51
+ params,
52
+ ...options.note !== void 0 ? { note: options.note } : {},
53
+ ...options.now ? { createdAt: options.now().toISOString() } : {}
54
+ });
55
+ built.derivedKey.fill(0);
56
+ const writtenPath = await writeHeader(memoryDir, built.header);
57
+ return {
58
+ ok: true,
59
+ headerPath: writtenPath,
60
+ kdf: built.header.metadata.kdf,
61
+ createdAt: built.header.createdAt
62
+ };
63
+ }
64
+ async function runSecureStoreUnlock(options) {
65
+ const { memoryDir, readPassphrase } = options;
66
+ const header = await readHeader(memoryDir);
67
+ if (!header) {
68
+ return { ok: false, reason: "not-initialized" };
69
+ }
70
+ const passphrase = await readPassphrase("Enter passphrase: ");
71
+ validatePassphrase(passphrase);
72
+ const candidateKey = deriveKeyFromHeader(header, passphrase);
73
+ if (!verifyKey(header, candidateKey)) {
74
+ candidateKey.fill(0);
75
+ return { ok: false, reason: "wrong-passphrase" };
76
+ }
77
+ const id = options.keyringId ?? secureStoreDir(memoryDir);
78
+ const now = options.now ?? (() => /* @__PURE__ */ new Date());
79
+ unlock(id, candidateKey, now);
80
+ const status2 = status(id);
81
+ return {
82
+ ok: true,
83
+ unlockedAt: status2.unlockedAt ?? now().toISOString(),
84
+ algorithm: header.metadata.kdf.algorithm
85
+ };
86
+ }
87
+ function runSecureStoreLock(options) {
88
+ const id = options.keyringId ?? secureStoreDir(options.memoryDir);
89
+ const cleared = lock(id);
90
+ return { ok: true, cleared };
91
+ }
92
+ async function runSecureStoreMigrate(options) {
93
+ const { memoryDir } = options;
94
+ const header = await readHeader(memoryDir);
95
+ if (!header) {
96
+ return {
97
+ ok: false,
98
+ reason: "not-initialized",
99
+ encrypted: 0,
100
+ skipped: 0,
101
+ errors: []
102
+ };
103
+ }
104
+ const id = options.keyringId ?? secureStoreDir(memoryDir);
105
+ const key = getKey(id);
106
+ if (key === null) {
107
+ return {
108
+ ok: false,
109
+ reason: "locked",
110
+ encrypted: 0,
111
+ skipped: 0,
112
+ errors: []
113
+ };
114
+ }
115
+ const result = await migrateMemoryDirToEncrypted(memoryDir, key);
116
+ if (result.errors.length > 0) {
117
+ return { ok: false, reason: "file-errors", ...result };
118
+ }
119
+ return { ok: true, ...result };
120
+ }
121
+ async function runSecureStoreDisable(options) {
122
+ const { memoryDir } = options;
123
+ const header = await readHeader(memoryDir);
124
+ if (!header) {
125
+ return {
126
+ ok: false,
127
+ reason: "not-initialized",
128
+ decrypted: 0,
129
+ skipped: 0,
130
+ errors: []
131
+ };
132
+ }
133
+ const id = options.keyringId ?? secureStoreDir(memoryDir);
134
+ const key = getKey(id);
135
+ if (key === null) {
136
+ return {
137
+ ok: false,
138
+ reason: "locked",
139
+ decrypted: 0,
140
+ skipped: 0,
141
+ errors: []
142
+ };
143
+ }
144
+ const result = await decryptMemoryDirToPlaintext(memoryDir, key);
145
+ if (result.errors.length > 0) {
146
+ return { ok: false, reason: "file-errors", ...result };
147
+ }
148
+ return { ok: true, ...result };
149
+ }
150
+ async function runSecureStoreStatus(options) {
151
+ const { memoryDir } = options;
152
+ const id = options.keyringId ?? secureStoreDir(memoryDir);
153
+ const header = await readHeader(memoryDir);
154
+ const ks = status(id);
155
+ const target = headerPath(memoryDir);
156
+ if (!header) {
157
+ return {
158
+ initialized: false,
159
+ headerPath: target,
160
+ locked: !ks.unlocked,
161
+ unlockedAt: ks.unlockedAt,
162
+ kdf: null,
163
+ createdAt: null
164
+ };
165
+ }
166
+ return {
167
+ initialized: true,
168
+ headerPath: target,
169
+ locked: !ks.unlocked,
170
+ unlockedAt: ks.unlockedAt,
171
+ kdf: header.metadata.kdf,
172
+ createdAt: header.createdAt
173
+ };
174
+ }
175
+ var MIN_PASSPHRASE_LENGTH = 8;
176
+ function validatePassphrase(passphrase) {
177
+ if (typeof passphrase !== "string") {
178
+ throw new Error("passphrase must be a string");
179
+ }
180
+ if (passphrase.length === 0) {
181
+ throw new Error("passphrase must not be empty");
182
+ }
183
+ if (passphrase.length < MIN_PASSPHRASE_LENGTH) {
184
+ throw new Error(`passphrase must be at least ${MIN_PASSPHRASE_LENGTH} characters`);
185
+ }
186
+ }
187
+ function resolveParams(algorithm, override) {
188
+ if (override !== void 0) return override;
189
+ if (algorithm === "scrypt") return { ...DEFAULT_SCRYPT_PARAMS };
190
+ return { ...DEFAULT_ARGON2ID_PARAMS };
191
+ }
192
+
193
+ // ../remnic-core/src/secure-store/cli-renderer.ts
194
+ function renderInitReport(report) {
195
+ const lines = [];
196
+ lines.push("=== Remnic secure-store initialized ===");
197
+ lines.push("");
198
+ lines.push(`header: ${report.headerPath}`);
199
+ lines.push(`createdAt: ${report.createdAt}`);
200
+ lines.push(...renderKdfLines(report.kdf));
201
+ lines.push("");
202
+ lines.push("Note: init does NOT auto-unlock the store. Run");
203
+ lines.push(" remnic engram secure-store unlock");
204
+ lines.push("to register the master key with the running daemon.");
205
+ return lines.join("\n");
206
+ }
207
+ function renderUnlockReport(report) {
208
+ if (report.ok) {
209
+ return `OK \u2014 secure-store unlocked at ${report.unlockedAt} (algorithm=${report.algorithm}).`;
210
+ }
211
+ if (report.reason === "not-initialized") {
212
+ return "ERR \u2014 secure-store is not initialized. Run 'remnic engram secure-store init' first.";
213
+ }
214
+ return "ERR \u2014 wrong passphrase.";
215
+ }
216
+ function renderLockReport(report) {
217
+ if (report.cleared) {
218
+ return "OK \u2014 secure-store key cleared from in-memory keyring.";
219
+ }
220
+ return "OK \u2014 secure-store was already locked (no in-memory key to clear).";
221
+ }
222
+ function renderMigrateReport(report) {
223
+ if (!report.ok && report.reason === "not-initialized") {
224
+ return "ERR \u2014 secure-store is not initialized. Run 'remnic engram secure-store init' first.";
225
+ }
226
+ if (!report.ok && report.reason === "locked") {
227
+ return "ERR \u2014 secure-store is locked. Run 'remnic engram secure-store unlock' before migrate.";
228
+ }
229
+ const lines = [];
230
+ lines.push(report.ok ? "OK \u2014 secure-store migration complete." : "ERR \u2014 secure-store migration completed with file errors.");
231
+ lines.push(`encrypted: ${report.encrypted}`);
232
+ lines.push(`skipped: ${report.skipped}`);
233
+ lines.push(`errors: ${report.errors.length}`);
234
+ for (const entry of report.errors.slice(0, 10)) {
235
+ lines.push(`- ${entry.filePath}: ${entry.error}`);
236
+ }
237
+ if (report.errors.length > 10) {
238
+ lines.push(`- ... ${report.errors.length - 10} more error(s)`);
239
+ }
240
+ return lines.join("\n");
241
+ }
242
+ function renderDisableReport(report) {
243
+ if (!report.ok && report.reason === "not-initialized") {
244
+ return "ERR \u2014 secure-store is not initialized. Run 'remnic engram secure-store init' first.";
245
+ }
246
+ if (!report.ok && report.reason === "locked") {
247
+ return "ERR \u2014 secure-store is locked. Run 'remnic engram secure-store unlock' before disable.";
248
+ }
249
+ const lines = [];
250
+ lines.push(report.ok ? "OK \u2014 secure-store disable complete." : "ERR \u2014 secure-store disable completed with file errors.");
251
+ lines.push(`decrypted: ${report.decrypted}`);
252
+ lines.push(`skipped: ${report.skipped}`);
253
+ lines.push(`errors: ${report.errors.length}`);
254
+ for (const entry of report.errors.slice(0, 10)) {
255
+ lines.push(`- ${entry.filePath}: ${entry.error}`);
256
+ }
257
+ if (report.errors.length > 10) {
258
+ lines.push(`- ... ${report.errors.length - 10} more error(s)`);
259
+ }
260
+ lines.push("header: kept");
261
+ return lines.join("\n");
262
+ }
263
+ function renderStatusReport(report) {
264
+ const lines = [];
265
+ lines.push("=== Remnic secure-store status ===");
266
+ lines.push("");
267
+ lines.push(`header: ${report.headerPath}`);
268
+ lines.push(`initialized: ${report.initialized ? "yes" : "no"}`);
269
+ if (!report.initialized) {
270
+ lines.push("");
271
+ lines.push("Run 'remnic engram secure-store init' to initialize a new store.");
272
+ return lines.join("\n");
273
+ }
274
+ lines.push(`createdAt: ${report.createdAt ?? "n/a"}`);
275
+ lines.push(`locked: ${report.locked ? "yes" : "no"}`);
276
+ if (!report.locked) {
277
+ lines.push(`lastUnlockAt: ${report.unlockedAt ?? "n/a"}`);
278
+ }
279
+ if (report.kdf) {
280
+ lines.push(...renderKdfLines(report.kdf));
281
+ }
282
+ return lines.join("\n");
283
+ }
284
+ function renderKdfLines(kdf) {
285
+ const lines = [];
286
+ lines.push(`kdf.algorithm: ${kdf.algorithm}`);
287
+ if (kdf.algorithm === "scrypt") {
288
+ const { N, r, p, keyLength, maxmem } = kdf.params;
289
+ lines.push(`kdf.params: N=${N} r=${r} p=${p} keyLength=${keyLength} maxmem=${maxmem}`);
290
+ } else {
291
+ const { memoryKiB, iterations, parallelism, keyLength } = kdf.params;
292
+ lines.push(
293
+ `kdf.params: memoryKiB=${memoryKiB} iterations=${iterations} parallelism=${parallelism} keyLength=${keyLength}`
294
+ );
295
+ }
296
+ return lines;
297
+ }
298
+
299
+ // ../remnic-core/src/secure-store/passphrase-reader.ts
300
+ import { createInterface } from "readline";
301
+ import { StringDecoder } from "string_decoder";
302
+ function createPassphraseReader(options = {}) {
303
+ const input = options.input ?? process.stdin;
304
+ const output = options.output ?? process.stdout;
305
+ const errorStream = options.errorStream ?? process.stderr;
306
+ let nonTtyReader = null;
307
+ let nonTtyWarned = false;
308
+ return async function readPassphrase(prompt, readerOptions) {
309
+ const first = await readSinglePassphrase(prompt);
310
+ if (readerOptions?.confirm) {
311
+ const second = await readSinglePassphrase("Confirm passphrase: ");
312
+ if (first !== second) {
313
+ throw new Error("passphrases did not match");
314
+ }
315
+ }
316
+ return first;
317
+ };
318
+ async function readSinglePassphrase(prompt) {
319
+ const inputAsAny = input;
320
+ if (inputAsAny.isTTY && typeof inputAsAny.setRawMode === "function") {
321
+ return readNoEcho(prompt, inputAsAny, output);
322
+ }
323
+ if (!nonTtyWarned) {
324
+ errorStream.write(
325
+ "[remnic secure-store] warning: stdin is not a TTY; reading passphrase as a plain line. Take care that the passphrase is not exposed in shell history.\n"
326
+ );
327
+ nonTtyWarned = true;
328
+ }
329
+ errorStream.write(prompt);
330
+ if (!nonTtyReader) nonTtyReader = createNonTtyLineReader(input);
331
+ return nonTtyReader.next();
332
+ }
333
+ }
334
+ function readNoEcho(prompt, input, output) {
335
+ return new Promise((resolve, reject) => {
336
+ output.write(prompt);
337
+ let buffer = "";
338
+ let settled = false;
339
+ const wasRaw = input.isRaw === true;
340
+ const decoder = new StringDecoder("utf8");
341
+ if (input.setRawMode) input.setRawMode(true);
342
+ input.resume();
343
+ const cleanup = () => {
344
+ input.pause();
345
+ input.removeListener("data", onData);
346
+ decoder.end();
347
+ if (input.setRawMode) input.setRawMode(wasRaw);
348
+ output.write("\n");
349
+ };
350
+ const onData = (chunk) => {
351
+ const str = decoder.write(chunk);
352
+ for (const ch of str) {
353
+ if (settled) return;
354
+ const code = ch.charCodeAt(0);
355
+ if (ch === "\n" || ch === "\r") {
356
+ settled = true;
357
+ cleanup();
358
+ resolve(buffer);
359
+ return;
360
+ }
361
+ if (code === 3) {
362
+ settled = true;
363
+ cleanup();
364
+ reject(new Error("passphrase entry aborted (Ctrl+C)"));
365
+ return;
366
+ }
367
+ if (code === 4) {
368
+ settled = true;
369
+ cleanup();
370
+ if (buffer.length === 0) {
371
+ reject(new Error("passphrase entry aborted (EOF)"));
372
+ } else {
373
+ resolve(buffer);
374
+ }
375
+ return;
376
+ }
377
+ if (code === 8 || code === 127) {
378
+ if (buffer.length > 0) {
379
+ const codePoints = Array.from(buffer);
380
+ buffer = codePoints.slice(0, -1).join("");
381
+ }
382
+ continue;
383
+ }
384
+ if (code < 32) {
385
+ continue;
386
+ }
387
+ buffer += ch;
388
+ }
389
+ };
390
+ input.on("data", onData);
391
+ });
392
+ }
393
+ function createNonTtyLineReader(input) {
394
+ const rl = createInterface({ input, terminal: false });
395
+ const lineQueue = [];
396
+ const waiterQueue = [];
397
+ const errorQueue = [];
398
+ let closed = false;
399
+ let error = null;
400
+ rl.on("line", (line) => {
401
+ const waiter = waiterQueue.shift();
402
+ if (waiter) {
403
+ waiter(line);
404
+ } else {
405
+ lineQueue.push(line);
406
+ }
407
+ });
408
+ rl.on("close", () => {
409
+ closed = true;
410
+ while (waiterQueue.length > 0) {
411
+ const w = waiterQueue.shift();
412
+ errorQueue.shift();
413
+ w("");
414
+ }
415
+ });
416
+ rl.on("error", (err) => {
417
+ error = err;
418
+ while (errorQueue.length > 0) {
419
+ const r = errorQueue.shift();
420
+ waiterQueue.shift();
421
+ r(err);
422
+ }
423
+ });
424
+ return {
425
+ next() {
426
+ if (error) return Promise.reject(error);
427
+ const queued = lineQueue.shift();
428
+ if (queued !== void 0) return Promise.resolve(queued);
429
+ if (closed) return Promise.resolve("");
430
+ return new Promise((resolve, reject) => {
431
+ waiterQueue.push(resolve);
432
+ errorQueue.push(reject);
433
+ });
434
+ }
435
+ };
436
+ }
437
+
438
+ export {
439
+ runSecureStoreInit,
440
+ runSecureStoreUnlock,
441
+ runSecureStoreLock,
442
+ runSecureStoreMigrate,
443
+ runSecureStoreDisable,
444
+ runSecureStoreStatus,
445
+ MIN_PASSPHRASE_LENGTH,
446
+ renderInitReport,
447
+ renderUnlockReport,
448
+ renderLockReport,
449
+ renderMigrateReport,
450
+ renderDisableReport,
451
+ renderStatusReport,
452
+ createPassphraseReader
453
+ };