skillwiki 0.10.49 → 0.10.51
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/{chunk-F7LXKP4T.js → chunk-3IBRZTLF.js} +2309 -1398
- package/dist/{chunk-UHZBOOMK.js → chunk-74OSLCXE.js} +1 -1
- package/dist/{chunk-PLUHIOCQ.js → chunk-GAHMWLWU.js} +174 -175
- package/dist/chunk-HBQTTYXZ.js +1045 -0
- package/dist/{chunk-EVQASYII.js → chunk-IJ7DD7QZ.js} +59 -0
- package/dist/{chunk-GAU4D25O.js → chunk-O3HCB7R2.js} +2 -2
- package/dist/{chunk-FXASPB3C.js → chunk-UPQ6XJSV.js} +1964 -1285
- package/dist/cli.js +2391 -1789
- package/dist/{index-projection-BJNZUPH6.js → index-projection-EXBX32RR.js} +3 -3
- package/dist/{managed-write-preflight-H7KVK2KI.js → managed-write-preflight-7GDFRPUT.js} +4 -4
- package/dist/skillwiki-mcp.js +7 -6
- package/dist/sources-BFYEFTS4.js +9 -0
- package/package.json +1 -1
- package/skills/.claude-plugin/plugin.json +2 -2
- package/skills/.codex-plugin/plugin.json +3 -3
- package/skills/README.md +1 -1
- package/skills/agents/wiki-ingest.md +2 -0
- package/skills/package.json +1 -1
- package/skills/skills/using-skillwiki/SKILL.md +4 -1
- package/skills/skills/wiki-freshness-repair/SKILL.md +94 -0
- package/skills/skills/wiki-ingest/SKILL.md +8 -0
- package/skills/skills/wiki-query/SKILL.md +1 -1
- package/skills/using-skillwiki/SKILL.md +4 -1
- package/skills/wiki-freshness-repair/SKILL.md +94 -0
- package/skills/wiki-ingest/SKILL.md +8 -0
- package/skills/wiki-query/SKILL.md +1 -1
- package/dist/chunk-QNSD72FH.js +0 -466
- package/dist/sources-CMV5DKS5.js +0 -9
|
@@ -6,7 +6,102 @@ import {
|
|
|
6
6
|
readPage,
|
|
7
7
|
scanSensitiveContent,
|
|
8
8
|
splitFrontmatter
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-IJ7DD7QZ.js";
|
|
10
|
+
|
|
11
|
+
// src/utils/vault-path-safety.ts
|
|
12
|
+
import { lstatSync, realpathSync } from "fs";
|
|
13
|
+
import { lstat, realpath } from "fs/promises";
|
|
14
|
+
import { dirname, relative, resolve, sep } from "path";
|
|
15
|
+
function vaultRelative(vaultReal, candidate) {
|
|
16
|
+
return relative(vaultReal, candidate).split(sep).join("/");
|
|
17
|
+
}
|
|
18
|
+
function isInside(vaultReal, candidate) {
|
|
19
|
+
const rel = vaultRelative(vaultReal, candidate);
|
|
20
|
+
return rel === "" || rel !== ".." && !rel.startsWith("../");
|
|
21
|
+
}
|
|
22
|
+
function existingRegularFileInsideVaultSync(vault, target) {
|
|
23
|
+
try {
|
|
24
|
+
const vaultReal = realpathSync(vault);
|
|
25
|
+
const absolutePath = resolve(vaultReal, target);
|
|
26
|
+
const rel = vaultRelative(vaultReal, absolutePath);
|
|
27
|
+
if (!isInside(vaultReal, absolutePath) || rel === "") return false;
|
|
28
|
+
let current = vaultReal;
|
|
29
|
+
for (const part of rel.split("/").filter(Boolean)) {
|
|
30
|
+
current = resolve(current, part);
|
|
31
|
+
if (lstatSync(current).isSymbolicLink()) return false;
|
|
32
|
+
}
|
|
33
|
+
const info = lstatSync(absolutePath);
|
|
34
|
+
return info.isFile() && !info.isSymbolicLink() && realpathSync(absolutePath) === absolutePath;
|
|
35
|
+
} catch {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async function canonicalVault(vault, target) {
|
|
40
|
+
try {
|
|
41
|
+
return ok(await realpath(vault));
|
|
42
|
+
} catch (error) {
|
|
43
|
+
return err("VAULT_PATH_INVALID", { target, message: "vault realpath failed", cause: String(error) });
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async function rejectSymlinkedComponents(vaultReal, absolutePath, target) {
|
|
47
|
+
const rel = vaultRelative(vaultReal, absolutePath);
|
|
48
|
+
if (!isInside(vaultReal, absolutePath) || rel === "") {
|
|
49
|
+
return err("VAULT_PATH_INVALID", { target, message: "target escapes vault" });
|
|
50
|
+
}
|
|
51
|
+
const parts = rel.split("/").filter(Boolean);
|
|
52
|
+
let current = vaultReal;
|
|
53
|
+
for (const part of parts) {
|
|
54
|
+
current = resolve(current, part);
|
|
55
|
+
try {
|
|
56
|
+
const info = await lstat(current);
|
|
57
|
+
if (info.isSymbolicLink()) {
|
|
58
|
+
return err("VAULT_PATH_INVALID", { target, message: "target path may not contain symlink aliases" });
|
|
59
|
+
}
|
|
60
|
+
} catch (error) {
|
|
61
|
+
if (error.code === "ENOENT") break;
|
|
62
|
+
return err("VAULT_PATH_INVALID", { target, message: "target lstat failed", cause: String(error) });
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return ok(true);
|
|
66
|
+
}
|
|
67
|
+
async function resolveExistingRegularFileInsideVault(vault, target) {
|
|
68
|
+
const vaultResult = await canonicalVault(vault, target);
|
|
69
|
+
if (!vaultResult.ok) return vaultResult;
|
|
70
|
+
const vaultReal = vaultResult.data;
|
|
71
|
+
const absolutePath = resolve(vaultReal, target);
|
|
72
|
+
const components = await rejectSymlinkedComponents(vaultReal, absolutePath, target);
|
|
73
|
+
if (!components.ok) return components;
|
|
74
|
+
try {
|
|
75
|
+
const info = await lstat(absolutePath);
|
|
76
|
+
if (!info.isFile() || info.isSymbolicLink()) {
|
|
77
|
+
return err("VAULT_PATH_INVALID", { target, message: "target must be a regular non-symlink file" });
|
|
78
|
+
}
|
|
79
|
+
const targetReal = await realpath(absolutePath);
|
|
80
|
+
if (!isInside(vaultReal, targetReal) || targetReal !== absolutePath) {
|
|
81
|
+
return err("VAULT_PATH_INVALID", { target, message: "target realpath escapes vault or uses an alias" });
|
|
82
|
+
}
|
|
83
|
+
return ok(absolutePath);
|
|
84
|
+
} catch (error) {
|
|
85
|
+
return err("FILE_NOT_FOUND", { path: target, message: String(error) });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async function resolveAbsentTargetInsideVault(vault, target) {
|
|
89
|
+
const vaultResult = await canonicalVault(vault, target);
|
|
90
|
+
if (!vaultResult.ok) return vaultResult;
|
|
91
|
+
const vaultReal = vaultResult.data;
|
|
92
|
+
const absolutePath = resolve(vaultReal, target);
|
|
93
|
+
const components = await rejectSymlinkedComponents(vaultReal, dirname(absolutePath), target);
|
|
94
|
+
if (!components.ok) return components;
|
|
95
|
+
try {
|
|
96
|
+
await lstat(absolutePath);
|
|
97
|
+
return err("RAW_DESTINATION_EXISTS", { path: target });
|
|
98
|
+
} catch (error) {
|
|
99
|
+
if (error.code !== "ENOENT") {
|
|
100
|
+
return err("VAULT_PATH_INVALID", { target, message: "destination lstat failed", cause: String(error) });
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return ok(absolutePath);
|
|
104
|
+
}
|
|
10
105
|
|
|
11
106
|
// src/utils/log-events.ts
|
|
12
107
|
import { mkdir, open, readFile, readdir } from "fs/promises";
|
|
@@ -252,6 +347,74 @@ function hasWikilinkCitations(body) {
|
|
|
252
347
|
return /\[\[raw\/[^\]]+\]\]/.test(stripped);
|
|
253
348
|
}
|
|
254
349
|
|
|
350
|
+
// src/utils/raw-operation-policy.ts
|
|
351
|
+
import { posix } from "path";
|
|
352
|
+
var SOURCE_CATEGORIES = /* @__PURE__ */ new Set(["articles", "papers", "transcripts"]);
|
|
353
|
+
function classifyRawPath(value) {
|
|
354
|
+
const path = value.replaceAll("\\", "/");
|
|
355
|
+
if (path !== posix.normalize(path) || path.startsWith("/") || path.includes("\0") || path.split("/").includes("..")) {
|
|
356
|
+
return err("RAW_PATH_INVALID", { path: value });
|
|
357
|
+
}
|
|
358
|
+
const parts = path.split("/");
|
|
359
|
+
if (parts[0] !== "raw") return err("RAW_PATH_OUTSIDE_LAYER", { path });
|
|
360
|
+
if (parts[1] === "assets" && parts.length > 2) {
|
|
361
|
+
return ok({ path, category: "assets", storage: "asset", relativeWithinCategory: parts.slice(2).join("/") });
|
|
362
|
+
}
|
|
363
|
+
if ((parts[1] === "archived" || parts[1] === "duplicates") && SOURCE_CATEGORIES.has(parts[2] ?? "") && parts.length > 3) {
|
|
364
|
+
return ok({
|
|
365
|
+
path,
|
|
366
|
+
category: parts[2],
|
|
367
|
+
storage: parts[1] === "archived" ? "archived" : "duplicate",
|
|
368
|
+
relativeWithinCategory: parts.slice(3).join("/")
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
if (SOURCE_CATEGORIES.has(parts[1] ?? "") && parts.length > 2) {
|
|
372
|
+
return ok({
|
|
373
|
+
path,
|
|
374
|
+
category: parts[1],
|
|
375
|
+
storage: "active",
|
|
376
|
+
relativeWithinCategory: parts.slice(2).join("/")
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
return err("RAW_PATH_UNSUPPORTED", { path });
|
|
380
|
+
}
|
|
381
|
+
function lifecycleDestination(source, operation) {
|
|
382
|
+
const parsed = classifyRawPath(source);
|
|
383
|
+
if (!parsed.ok) return parsed;
|
|
384
|
+
if (parsed.data.category === "assets") {
|
|
385
|
+
return err("RAW_ASSET_PATH_FROZEN", { path: source, message: "asset lifecycle is logical by default" });
|
|
386
|
+
}
|
|
387
|
+
if (parsed.data.storage !== "active") return err("RAW_SOURCE_NOT_ACTIVE", { path: source, storage: parsed.data.storage });
|
|
388
|
+
if (operation === "archive") return ok(`raw/archived/${parsed.data.category}/${parsed.data.relativeWithinCategory}`);
|
|
389
|
+
if (operation === "deduplicate") return ok(`raw/duplicates/${parsed.data.category}/${parsed.data.relativeWithinCategory}`);
|
|
390
|
+
return ok(source);
|
|
391
|
+
}
|
|
392
|
+
function authorizeRawOperation(input) {
|
|
393
|
+
if (input.operationClass === "read") return ok(true);
|
|
394
|
+
if (input.operationClass === "rewrite") return err("RAW_REWRITE_FORBIDDEN", { source: input.source });
|
|
395
|
+
if (input.operationClass === "destructive-remove") {
|
|
396
|
+
return input.trigger === "attended-apply" && input.explicitExactTarget === true ? ok(true) : err("RAW_DISPOSAL_REQUIRES_EXACT_TARGET_APPROVAL", { source: input.source });
|
|
397
|
+
}
|
|
398
|
+
if (input.operationClass === "preserve-move") {
|
|
399
|
+
if (input.trigger === "report-only") return err("RAW_STRUCTURAL_REPORT_ONLY", { source: input.source, destination: input.destination });
|
|
400
|
+
if (!input.source || !input.destination) return err("RAW_PATH_INVALID", { source: input.source, destination: input.destination });
|
|
401
|
+
const source = classifyRawPath(input.source);
|
|
402
|
+
if (!source.ok) return source;
|
|
403
|
+
const destination = classifyRawPath(input.destination);
|
|
404
|
+
if (!destination.ok) return destination;
|
|
405
|
+
return ok(true);
|
|
406
|
+
}
|
|
407
|
+
return ok(true);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// src/utils/operation-id.ts
|
|
411
|
+
import { createHash } from "crypto";
|
|
412
|
+
function operationId(namespace, parts) {
|
|
413
|
+
const hash = createHash("sha256").update(namespace).update("\0");
|
|
414
|
+
for (const part of parts) hash.update(part).update("\0");
|
|
415
|
+
return hash.digest("hex");
|
|
416
|
+
}
|
|
417
|
+
|
|
255
418
|
// src/utils/source-relocations.ts
|
|
256
419
|
function parse(event) {
|
|
257
420
|
if (event.kind !== "source-relocation") return ok(null);
|
|
@@ -311,109 +474,12 @@ function resolveRelocatedSource(target, projection) {
|
|
|
311
474
|
}
|
|
312
475
|
|
|
313
476
|
// src/utils/raw-source.ts
|
|
314
|
-
import { join as join2, posix, relative as relative2 } from "path";
|
|
315
|
-
|
|
316
|
-
// src/utils/vault-path-safety.ts
|
|
317
|
-
import { lstatSync, realpathSync } from "fs";
|
|
318
|
-
import { lstat, realpath } from "fs/promises";
|
|
319
|
-
import { dirname, relative, resolve, sep } from "path";
|
|
320
|
-
function vaultRelative(vaultReal, candidate) {
|
|
321
|
-
return relative(vaultReal, candidate).split(sep).join("/");
|
|
322
|
-
}
|
|
323
|
-
function isInside(vaultReal, candidate) {
|
|
324
|
-
const rel = vaultRelative(vaultReal, candidate);
|
|
325
|
-
return rel === "" || rel !== ".." && !rel.startsWith("../");
|
|
326
|
-
}
|
|
327
|
-
function existingRegularFileInsideVaultSync(vault, target) {
|
|
328
|
-
try {
|
|
329
|
-
const vaultReal = realpathSync(vault);
|
|
330
|
-
const absolutePath = resolve(vaultReal, target);
|
|
331
|
-
const rel = vaultRelative(vaultReal, absolutePath);
|
|
332
|
-
if (!isInside(vaultReal, absolutePath) || rel === "") return false;
|
|
333
|
-
let current = vaultReal;
|
|
334
|
-
for (const part of rel.split("/").filter(Boolean)) {
|
|
335
|
-
current = resolve(current, part);
|
|
336
|
-
if (lstatSync(current).isSymbolicLink()) return false;
|
|
337
|
-
}
|
|
338
|
-
const info = lstatSync(absolutePath);
|
|
339
|
-
return info.isFile() && !info.isSymbolicLink() && realpathSync(absolutePath) === absolutePath;
|
|
340
|
-
} catch {
|
|
341
|
-
return false;
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
async function canonicalVault(vault, target) {
|
|
345
|
-
try {
|
|
346
|
-
return ok(await realpath(vault));
|
|
347
|
-
} catch (error) {
|
|
348
|
-
return err("VAULT_PATH_INVALID", { target, message: "vault realpath failed", cause: String(error) });
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
async function rejectSymlinkedComponents(vaultReal, absolutePath, target) {
|
|
352
|
-
const rel = vaultRelative(vaultReal, absolutePath);
|
|
353
|
-
if (!isInside(vaultReal, absolutePath) || rel === "") {
|
|
354
|
-
return err("VAULT_PATH_INVALID", { target, message: "target escapes vault" });
|
|
355
|
-
}
|
|
356
|
-
const parts = rel.split("/").filter(Boolean);
|
|
357
|
-
let current = vaultReal;
|
|
358
|
-
for (const part of parts) {
|
|
359
|
-
current = resolve(current, part);
|
|
360
|
-
try {
|
|
361
|
-
const info = await lstat(current);
|
|
362
|
-
if (info.isSymbolicLink()) {
|
|
363
|
-
return err("VAULT_PATH_INVALID", { target, message: "target path may not contain symlink aliases" });
|
|
364
|
-
}
|
|
365
|
-
} catch (error) {
|
|
366
|
-
if (error.code === "ENOENT") break;
|
|
367
|
-
return err("VAULT_PATH_INVALID", { target, message: "target lstat failed", cause: String(error) });
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
return ok(true);
|
|
371
|
-
}
|
|
372
|
-
async function resolveExistingRegularFileInsideVault(vault, target) {
|
|
373
|
-
const vaultResult = await canonicalVault(vault, target);
|
|
374
|
-
if (!vaultResult.ok) return vaultResult;
|
|
375
|
-
const vaultReal = vaultResult.data;
|
|
376
|
-
const absolutePath = resolve(vaultReal, target);
|
|
377
|
-
const components = await rejectSymlinkedComponents(vaultReal, absolutePath, target);
|
|
378
|
-
if (!components.ok) return components;
|
|
379
|
-
try {
|
|
380
|
-
const info = await lstat(absolutePath);
|
|
381
|
-
if (!info.isFile() || info.isSymbolicLink()) {
|
|
382
|
-
return err("VAULT_PATH_INVALID", { target, message: "target must be a regular non-symlink file" });
|
|
383
|
-
}
|
|
384
|
-
const targetReal = await realpath(absolutePath);
|
|
385
|
-
if (!isInside(vaultReal, targetReal) || targetReal !== absolutePath) {
|
|
386
|
-
return err("VAULT_PATH_INVALID", { target, message: "target realpath escapes vault or uses an alias" });
|
|
387
|
-
}
|
|
388
|
-
return ok(absolutePath);
|
|
389
|
-
} catch (error) {
|
|
390
|
-
return err("FILE_NOT_FOUND", { path: target, message: String(error) });
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
async function resolveAbsentTargetInsideVault(vault, target) {
|
|
394
|
-
const vaultResult = await canonicalVault(vault, target);
|
|
395
|
-
if (!vaultResult.ok) return vaultResult;
|
|
396
|
-
const vaultReal = vaultResult.data;
|
|
397
|
-
const absolutePath = resolve(vaultReal, target);
|
|
398
|
-
const components = await rejectSymlinkedComponents(vaultReal, dirname(absolutePath), target);
|
|
399
|
-
if (!components.ok) return components;
|
|
400
|
-
try {
|
|
401
|
-
await lstat(absolutePath);
|
|
402
|
-
return err("RAW_DESTINATION_EXISTS", { path: target });
|
|
403
|
-
} catch (error) {
|
|
404
|
-
if (error.code !== "ENOENT") {
|
|
405
|
-
return err("VAULT_PATH_INVALID", { target, message: "destination lstat failed", cause: String(error) });
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
return ok(absolutePath);
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
// src/utils/raw-source.ts
|
|
477
|
+
import { join as join2, posix as posix2, relative as relative2 } from "path";
|
|
412
478
|
function normalizeRawSourceTarget(entry) {
|
|
413
479
|
let target = entry.trim().replace(/^"/, "").replace(/"$/, "").replace(/^'/, "").replace(/'$/, "");
|
|
414
480
|
target = target.replace(/^\^\[/, "").replace(/\]$/, "");
|
|
415
|
-
if (!target || target.includes("\0") || target.includes("\\") ||
|
|
416
|
-
if (
|
|
481
|
+
if (!target || target.includes("\0") || target.includes("\\") || posix2.isAbsolute(target)) return null;
|
|
482
|
+
if (posix2.normalize(target) !== target || target.split("/").some((part) => part === "" || part === "." || part === "..")) return null;
|
|
417
483
|
if (!target.startsWith("raw/") && !target.startsWith("_archive/raw/")) return null;
|
|
418
484
|
return target;
|
|
419
485
|
}
|
|
@@ -457,74 +523,6 @@ async function rawSourceTargetExists(vault, target) {
|
|
|
457
523
|
return false;
|
|
458
524
|
}
|
|
459
525
|
|
|
460
|
-
// src/utils/raw-operation-policy.ts
|
|
461
|
-
import { posix as posix2 } from "path";
|
|
462
|
-
var SOURCE_CATEGORIES = /* @__PURE__ */ new Set(["articles", "papers", "transcripts"]);
|
|
463
|
-
function classifyRawPath(value) {
|
|
464
|
-
const path = value.replaceAll("\\", "/");
|
|
465
|
-
if (path !== posix2.normalize(path) || path.startsWith("/") || path.includes("\0") || path.split("/").includes("..")) {
|
|
466
|
-
return err("RAW_PATH_INVALID", { path: value });
|
|
467
|
-
}
|
|
468
|
-
const parts = path.split("/");
|
|
469
|
-
if (parts[0] !== "raw") return err("RAW_PATH_OUTSIDE_LAYER", { path });
|
|
470
|
-
if (parts[1] === "assets" && parts.length > 2) {
|
|
471
|
-
return ok({ path, category: "assets", storage: "asset", relativeWithinCategory: parts.slice(2).join("/") });
|
|
472
|
-
}
|
|
473
|
-
if ((parts[1] === "archived" || parts[1] === "duplicates") && SOURCE_CATEGORIES.has(parts[2] ?? "") && parts.length > 3) {
|
|
474
|
-
return ok({
|
|
475
|
-
path,
|
|
476
|
-
category: parts[2],
|
|
477
|
-
storage: parts[1] === "archived" ? "archived" : "duplicate",
|
|
478
|
-
relativeWithinCategory: parts.slice(3).join("/")
|
|
479
|
-
});
|
|
480
|
-
}
|
|
481
|
-
if (SOURCE_CATEGORIES.has(parts[1] ?? "") && parts.length > 2) {
|
|
482
|
-
return ok({
|
|
483
|
-
path,
|
|
484
|
-
category: parts[1],
|
|
485
|
-
storage: "active",
|
|
486
|
-
relativeWithinCategory: parts.slice(2).join("/")
|
|
487
|
-
});
|
|
488
|
-
}
|
|
489
|
-
return err("RAW_PATH_UNSUPPORTED", { path });
|
|
490
|
-
}
|
|
491
|
-
function lifecycleDestination(source, operation) {
|
|
492
|
-
const parsed = classifyRawPath(source);
|
|
493
|
-
if (!parsed.ok) return parsed;
|
|
494
|
-
if (parsed.data.category === "assets") {
|
|
495
|
-
return err("RAW_ASSET_PATH_FROZEN", { path: source, message: "asset lifecycle is logical by default" });
|
|
496
|
-
}
|
|
497
|
-
if (parsed.data.storage !== "active") return err("RAW_SOURCE_NOT_ACTIVE", { path: source, storage: parsed.data.storage });
|
|
498
|
-
if (operation === "archive") return ok(`raw/archived/${parsed.data.category}/${parsed.data.relativeWithinCategory}`);
|
|
499
|
-
if (operation === "deduplicate") return ok(`raw/duplicates/${parsed.data.category}/${parsed.data.relativeWithinCategory}`);
|
|
500
|
-
return ok(source);
|
|
501
|
-
}
|
|
502
|
-
function authorizeRawOperation(input) {
|
|
503
|
-
if (input.operationClass === "read") return ok(true);
|
|
504
|
-
if (input.operationClass === "rewrite") return err("RAW_REWRITE_FORBIDDEN", { source: input.source });
|
|
505
|
-
if (input.operationClass === "destructive-remove") {
|
|
506
|
-
return input.trigger === "attended-apply" && input.explicitExactTarget === true ? ok(true) : err("RAW_DISPOSAL_REQUIRES_EXACT_TARGET_APPROVAL", { source: input.source });
|
|
507
|
-
}
|
|
508
|
-
if (input.operationClass === "preserve-move") {
|
|
509
|
-
if (input.trigger === "report-only") return err("RAW_STRUCTURAL_REPORT_ONLY", { source: input.source, destination: input.destination });
|
|
510
|
-
if (!input.source || !input.destination) return err("RAW_PATH_INVALID", { source: input.source, destination: input.destination });
|
|
511
|
-
const source = classifyRawPath(input.source);
|
|
512
|
-
if (!source.ok) return source;
|
|
513
|
-
const destination = classifyRawPath(input.destination);
|
|
514
|
-
if (!destination.ok) return destination;
|
|
515
|
-
return ok(true);
|
|
516
|
-
}
|
|
517
|
-
return ok(true);
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
// src/utils/operation-id.ts
|
|
521
|
-
import { createHash } from "crypto";
|
|
522
|
-
function operationId(namespace, parts) {
|
|
523
|
-
const hash = createHash("sha256").update(namespace).update("\0");
|
|
524
|
-
for (const part of parts) hash.update(part).update("\0");
|
|
525
|
-
return hash.digest("hex");
|
|
526
|
-
}
|
|
527
|
-
|
|
528
526
|
// src/utils/source-reference-index.ts
|
|
529
527
|
function canonicalTarget(value) {
|
|
530
528
|
const normalized = normalizeRawSourceTarget(value);
|
|
@@ -598,17 +596,18 @@ export {
|
|
|
598
596
|
isLegacyCitationStyle,
|
|
599
597
|
hasOrphanedCitations,
|
|
600
598
|
hasWikilinkCitations,
|
|
599
|
+
classifyRawPath,
|
|
600
|
+
lifecycleDestination,
|
|
601
|
+
authorizeRawOperation,
|
|
602
|
+
operationId,
|
|
603
|
+
resolveExistingRegularFileInsideVault,
|
|
604
|
+
resolveAbsentTargetInsideVault,
|
|
601
605
|
projectSourceRelocations,
|
|
602
606
|
readSourceRelocations,
|
|
603
607
|
buildSourceRelocationProjection,
|
|
604
|
-
resolveExistingRegularFileInsideVault,
|
|
605
|
-
resolveAbsentTargetInsideVault,
|
|
606
608
|
normalizeRawSourceTarget,
|
|
607
609
|
rawSourceTargetExistsSync,
|
|
608
610
|
rawSourceTargetExists,
|
|
609
|
-
|
|
610
|
-
lifecycleDestination,
|
|
611
|
-
authorizeRawOperation,
|
|
612
|
-
operationId,
|
|
611
|
+
referencesFromText,
|
|
613
612
|
buildSourceReferenceIndex
|
|
614
613
|
};
|