@staix/vault-mem 0.2.1 → 0.2.3
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/CHANGELOG.md +13 -0
- package/README.md +26 -4
- package/bin/vmem.js +1 -1
- package/package.json +1 -1
- package/src/amem/seam.ts +56 -0
- package/src/analysis-commands.ts +4 -4
- package/src/analysis-input.ts +2 -1
- package/src/analysis-reports.ts +15 -5
- package/src/cli.ts +24 -2
- package/src/commands-table.ts +4 -3
- package/src/mcp-vault.ts +11 -3
- package/src/registry.ts +15 -3
- package/src/skills-mine.ts +485 -0
- package/src/sync.ts +20 -10
- package/src/vault-graph.ts +234 -29
- package/src/vault-log.ts +1 -1
- package/src/workspace-graph.ts +139 -0
- package/src/workspace-input.ts +150 -0
- package/src/write-validation.ts +2 -1
- package/src/writer.ts +8 -8
package/src/writer.ts
CHANGED
|
@@ -10,7 +10,7 @@ import { VaultIndex, MAX_NOTE_BYTES } from "./vault-index.ts";
|
|
|
10
10
|
import { parseFrontmatter } from "./frontmatter.ts";
|
|
11
11
|
import { canonicalPath, isDenied } from "./platform.ts";
|
|
12
12
|
import { moveSourcePath, notePath, planOperation, refuse, WriteRefusal } from "./write-operations.ts";
|
|
13
|
-
import { validateWrite } from "./write-validation.ts";
|
|
13
|
+
import { MAX_REPORT_BYTES, validateWrite } from "./write-validation.ts";
|
|
14
14
|
import type { PiiSurface } from "./config.ts";
|
|
15
15
|
import type { LogBinding } from "./log-types.ts";
|
|
16
16
|
import type { ChangeReceipt, PlannedChange, WriteIssue, WriteReceipt, WriteRequest } from "./write-types.ts";
|
|
@@ -41,22 +41,22 @@ function safePath(root: string, path: string): string {
|
|
|
41
41
|
function stamp(stat: { dev: bigint; ino: bigint; size: bigint; mtimeNs: bigint; ctimeNs: bigint }): string {
|
|
42
42
|
return `${stat.dev}:${stat.ino}:${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}`;
|
|
43
43
|
}
|
|
44
|
-
function readPlain(root: string, path: string): string | null {
|
|
44
|
+
function readPlain(root: string, path: string, maximum = MAX_NOTE_BYTES): string | null {
|
|
45
45
|
const file = safePath(root, path);
|
|
46
46
|
let fd: number;
|
|
47
47
|
try { fd = openSync(file, constants.O_RDONLY | constants.O_NOFOLLOW | constants.O_NONBLOCK); }
|
|
48
48
|
catch (error) { if ((error as NodeJS.ErrnoException).code === "ENOENT") return null; throw error; }
|
|
49
49
|
try {
|
|
50
50
|
const before = fstatSync(fd, { bigint: true });
|
|
51
|
-
if (!before.isFile() || before.size > BigInt(
|
|
52
|
-
const bytes = Buffer.alloc(Math.min(Number(before.size) + 1,
|
|
51
|
+
if (!before.isFile() || before.size > BigInt(maximum)) refuse("V09", "file exceeds read bound or is not regular", path);
|
|
52
|
+
const bytes = Buffer.alloc(Math.min(Number(before.size) + 1, maximum + 1));
|
|
53
53
|
let size = 0;
|
|
54
54
|
while (size < bytes.length) {
|
|
55
55
|
const count = readSync(fd, bytes, size, bytes.length - size, null); size += count;
|
|
56
56
|
if (!count) break;
|
|
57
57
|
}
|
|
58
58
|
const after = fstatSync(fd, { bigint: true });
|
|
59
|
-
if (size >
|
|
59
|
+
if (size > maximum || stamp(before) !== stamp(after)) refuse("V09", "file changed during read", path);
|
|
60
60
|
const buffer = bytes.subarray(0, size), text = buffer.toString("utf8");
|
|
61
61
|
if (!Buffer.from(text).equals(buffer)) refuse("V01", "note must be valid UTF-8", path);
|
|
62
62
|
return text;
|
|
@@ -150,7 +150,7 @@ export function applyWrite(home: string, request: WriteRequest, options: { env?:
|
|
|
150
150
|
// Planning validates the report's exact relative path before it can be read.
|
|
151
151
|
planOperation(request.operation, notes, null);
|
|
152
152
|
if (isDenied(join(root, request.operation.path), config.denyPaths)) refuse("V06", "report output path is denied");
|
|
153
|
-
reportBefore = readPlain(root, request.operation.path);
|
|
153
|
+
reportBefore = readPlain(root, request.operation.path, MAX_REPORT_BYTES);
|
|
154
154
|
}
|
|
155
155
|
changes = planOperation(request.operation, notes, reportBefore);
|
|
156
156
|
for (const change of changes) if (isDenied(join(root, change.path), config.denyPaths)) refuse("V06", "planned mutation path is denied", change.path);
|
|
@@ -196,7 +196,7 @@ export function applyWrite(home: string, request: WriteRequest, options: { env?:
|
|
|
196
196
|
if (current.size !== notes.size || [...current].some(([path, text]) => notes.get(path) !== text)) refuse("V09", "vault changed while staging");
|
|
197
197
|
for (const change of changes) {
|
|
198
198
|
const file = safePath(root, change.path);
|
|
199
|
-
if (readPlain(root, change.path) !== change.before) refuse("V09", "file changed while staging", change.path);
|
|
199
|
+
if (readPlain(root, change.path, request.operation.op === "report" ? MAX_REPORT_BYTES : MAX_NOTE_BYTES) !== change.before) refuse("V09", "file changed while staging", change.path);
|
|
200
200
|
if (change.after === null) unlinkSync(file);
|
|
201
201
|
else { const temp = staged.get(change.path)!; renameSync(temp, file); temps.delete(temp); }
|
|
202
202
|
committed.push(change);
|
|
@@ -209,7 +209,7 @@ export function applyWrite(home: string, request: WriteRequest, options: { env?:
|
|
|
209
209
|
for (const change of [...committed].reverse()) {
|
|
210
210
|
try {
|
|
211
211
|
const file = safePath(root, change.path);
|
|
212
|
-
if (readPlain(root, change.path) !== change.after) throw new Error("external write detected before rollback");
|
|
212
|
+
if (readPlain(root, change.path, request.operation.op === "report" ? MAX_REPORT_BYTES : MAX_NOTE_BYTES) !== change.after) throw new Error("external write detected before rollback");
|
|
213
213
|
if (change.before === null) unlinkSync(file);
|
|
214
214
|
else { const backup = backups.get(change.path)!; renameSync(backup, file); temps.delete(backup); }
|
|
215
215
|
} catch {
|