@attentionform/astra-mcp-server 0.4.0 → 0.4.2
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 +176 -49
- package/dist/lib/mcp/handlers/history.d.ts +8 -0
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -6082,6 +6082,9 @@ var RSIDS_EMPTY_RE = /<w:rsids>\s*<\/w:rsids>/g;
|
|
|
6082
6082
|
var RSIDS_SELF_CLOSING_RE = /<w:rsids\s*\/>/g;
|
|
6083
6083
|
var AUTHOR_ATTR_RE = /\s+(w|w15):author="[^"]*"/g;
|
|
6084
6084
|
var INITIALS_ATTR_RE = /\s+w:initials="[^"]*"/g;
|
|
6085
|
+
var PRESENCE_INFO_TAG_RE = /<w15:presenceInfo\b(?:[^>"']|"[^"]*"|'[^']*')*>/g;
|
|
6086
|
+
var PRESENCE_PROVIDER_NONE_RE = /\sw15:providerId=(["'])None\1/;
|
|
6087
|
+
var PRESENCE_USERID_RE = /(\sw15:userId=)(?:"[^"]*"|'[^']*')/;
|
|
6085
6088
|
var EMAIL_RE = /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g;
|
|
6086
6089
|
var PHONE_RE = /(\+\d[\d\s\-().]{6,}\d|\(\d{3}\)\s*\d{3}[\s\-.]\d{4}|\b\d{3}[\s\-.]\d{3}[\s\-.]\d{4}\b)/g;
|
|
6087
6090
|
var CLAUSE_ACRONYM_RE = /\b(dfars|far|cfr|nist)\b/gi;
|
|
@@ -6853,6 +6856,15 @@ function sanitizeDocumentXml(xml, opts, partName = "word XML part", hooks) {
|
|
|
6853
6856
|
o.replacementAuthor.split(/[\s._-]+/).filter(Boolean).map((s) => s[0]).join("").slice(0, 4).toUpperCase() || "A"
|
|
6854
6857
|
);
|
|
6855
6858
|
out = out.replace(INITIALS_ATTR_RE, ` w:initials="${initials}"`);
|
|
6859
|
+
if (partName === "word/people.xml") {
|
|
6860
|
+
out = out.replace(PRESENCE_INFO_TAG_RE, (tag) => {
|
|
6861
|
+
if (!PRESENCE_PROVIDER_NONE_RE.test(tag)) return tag;
|
|
6862
|
+
return tag.replace(PRESENCE_USERID_RE, (_m, open) => {
|
|
6863
|
+
purgedAuthors += 1;
|
|
6864
|
+
return `${open}"${replacement === "" ? "Anonymous" : replacement}"`;
|
|
6865
|
+
});
|
|
6866
|
+
});
|
|
6867
|
+
}
|
|
6856
6868
|
}
|
|
6857
6869
|
let acceptedRevisions = 0;
|
|
6858
6870
|
let unresolvedRevisions = [];
|
|
@@ -10480,9 +10492,10 @@ var DocxParser = class _DocxParser {
|
|
|
10480
10492
|
/**
|
|
10481
10493
|
* Restores the package to a previously captured snapshot: every entry is
|
|
10482
10494
|
* restored (entries added after the snapshot are removed), then documentDom
|
|
10483
|
-
* and stylesDom are reloaded and stable IDs re-assigned.
|
|
10495
|
+
* and stylesDom are reloaded and stable IDs re-assigned. The restored package is exactly the snapshot;
|
|
10496
|
+
* only a restore that mixes a pre-0.4.2 commit with live parts passes `registerCoreProperties`.
|
|
10484
10497
|
*/
|
|
10485
|
-
restorePackageSnapshot(snapshot3) {
|
|
10498
|
+
restorePackageSnapshot(snapshot3, options) {
|
|
10486
10499
|
for (const entry of [...this.zip.getEntries()]) {
|
|
10487
10500
|
if (!snapshot3.has(entry.entryName)) {
|
|
10488
10501
|
this.zip.deleteFile(entry);
|
|
@@ -10501,8 +10514,15 @@ var DocxParser = class _DocxParser {
|
|
|
10501
10514
|
continue;
|
|
10502
10515
|
}
|
|
10503
10516
|
const buffer = Buffer.from(data);
|
|
10504
|
-
|
|
10505
|
-
|
|
10517
|
+
const liveEntry = this.zip.getEntry(entryName);
|
|
10518
|
+
if (liveEntry) {
|
|
10519
|
+
let unchanged = false;
|
|
10520
|
+
try {
|
|
10521
|
+
unchanged = !liveEntry.isDirectory && liveEntry.header.size === buffer.length && readPartData(liveEntry, entryName).equals(buffer);
|
|
10522
|
+
} catch {
|
|
10523
|
+
unchanged = false;
|
|
10524
|
+
}
|
|
10525
|
+
if (!unchanged) this.zip.updateFile(entryName, buffer);
|
|
10506
10526
|
} else {
|
|
10507
10527
|
this.zip.addFile(entryName, buffer);
|
|
10508
10528
|
}
|
|
@@ -10527,7 +10547,7 @@ var DocxParser = class _DocxParser {
|
|
|
10527
10547
|
} else {
|
|
10528
10548
|
this.stylesDom = null;
|
|
10529
10549
|
}
|
|
10530
|
-
registerExistingCorePropertiesPart(this.getZipAdapter());
|
|
10550
|
+
if (options?.registerCoreProperties) registerExistingCorePropertiesPart(this.getZipAdapter());
|
|
10531
10551
|
this.invalidateMediaCache();
|
|
10532
10552
|
this.assignStableIds();
|
|
10533
10553
|
this._bumpNodePagesRevision();
|
|
@@ -13818,7 +13838,55 @@ function getRepoDir(docPath) {
|
|
|
13818
13838
|
if (root !== null) return import_path3.default.join(root, historyKey(import_path3.default.dirname(docPath)), import_path3.default.basename(docPath));
|
|
13819
13839
|
return import_path3.default.join(import_path3.default.dirname(docPath), ".astra", import_path3.default.basename(docPath));
|
|
13820
13840
|
}
|
|
13841
|
+
var SNAPSHOT_FORMAT_MARKER = ".astra-snapshot-format";
|
|
13842
|
+
var SNAPSHOT_FORMAT_VERSION = "2";
|
|
13843
|
+
function isCompleteSnapshotMarker(content) {
|
|
13844
|
+
if (content === void 0) return false;
|
|
13845
|
+
return Buffer.from(content).toString("utf8").trim() === SNAPSHOT_FORMAT_VERSION;
|
|
13846
|
+
}
|
|
13847
|
+
var ESCAPED_DIR = "__astra_escaped__";
|
|
13848
|
+
var ESCAPED_NAME_MAX = 255;
|
|
13849
|
+
function needsHistoryEscape(entryName) {
|
|
13850
|
+
const name2 = entryName.replace(/\\/g, "/");
|
|
13851
|
+
if (name2 === SNAPSHOT_FORMAT_MARKER || name2 === "document.xml") return true;
|
|
13852
|
+
if (name2 === ESCAPED_DIR || name2.startsWith(`${ESCAPED_DIR}/`)) return true;
|
|
13853
|
+
return name2.split("/").some((segment) => segment.toLowerCase().startsWith(".git"));
|
|
13854
|
+
}
|
|
13855
|
+
function isUnrecordableName(entryName) {
|
|
13856
|
+
const name2 = entryName.replace(/\\/g, "/");
|
|
13857
|
+
if (name2 === "" || name2.endsWith("/") || name2.startsWith("/") || /^[a-zA-Z]:/.test(name2)) return true;
|
|
13858
|
+
return name2.split("/").some((segment) => segment === "" || segment === "." || segment === "..");
|
|
13859
|
+
}
|
|
13821
13860
|
function isTrackedPackageFile(entryName) {
|
|
13861
|
+
const name2 = entryName.replace(/\\/g, "/");
|
|
13862
|
+
if (name2 === "word/document.xml") return false;
|
|
13863
|
+
return !isUnrecordableName(name2);
|
|
13864
|
+
}
|
|
13865
|
+
function historyPathFor(entryName) {
|
|
13866
|
+
const name2 = entryName.replace(/\\/g, "/");
|
|
13867
|
+
if (!needsHistoryEscape(name2)) return name2;
|
|
13868
|
+
const encoded = Buffer.from(name2, "utf8").toString("base64url");
|
|
13869
|
+
if (encoded.length > ESCAPED_NAME_MAX) {
|
|
13870
|
+
throw Object.assign(
|
|
13871
|
+
new Error(`E_VALIDATION: the package entry name is too long to record in history (${name2.length} characters).`),
|
|
13872
|
+
{
|
|
13873
|
+
code: "E_VALIDATION",
|
|
13874
|
+
remedy: "Rename or remove that package part (for example with sanitize_document), then save again."
|
|
13875
|
+
}
|
|
13876
|
+
);
|
|
13877
|
+
}
|
|
13878
|
+
return `${ESCAPED_DIR}/${encoded}`;
|
|
13879
|
+
}
|
|
13880
|
+
function entryNameFor(repoPath) {
|
|
13881
|
+
const rel = repoPath.replace(/\\/g, "/");
|
|
13882
|
+
if (!rel.startsWith(`${ESCAPED_DIR}/`)) return rel;
|
|
13883
|
+
return Buffer.from(rel.slice(ESCAPED_DIR.length + 1), "base64url").toString("utf8");
|
|
13884
|
+
}
|
|
13885
|
+
function isRepoPartPath(repoPath) {
|
|
13886
|
+
const rel = repoPath.replace(/\\/g, "/");
|
|
13887
|
+
return rel !== "document.xml" && rel !== SNAPSHOT_FORMAT_MARKER && rel !== "" && !rel.startsWith(".git/");
|
|
13888
|
+
}
|
|
13889
|
+
function isLegacyTrackedPackageFile(entryName) {
|
|
13822
13890
|
const name2 = entryName.replace(/\\/g, "/");
|
|
13823
13891
|
if (name2 === "word/document.xml") return false;
|
|
13824
13892
|
if (name2 === "word/styles.xml") return true;
|
|
@@ -13842,7 +13910,7 @@ function toRepoRelative(entryName) {
|
|
|
13842
13910
|
}
|
|
13843
13911
|
async function writeAndAdd(repoDir, relPath, content) {
|
|
13844
13912
|
const safeRel = toRepoRelative(relPath);
|
|
13845
|
-
if (
|
|
13913
|
+
if (isTraversalName(safeRel)) {
|
|
13846
13914
|
throw new Error(`Security Violation: refusing to track package path '${relPath}'`);
|
|
13847
13915
|
}
|
|
13848
13916
|
const fullPath = import_path3.default.join(repoDir, safeRel);
|
|
@@ -13854,6 +13922,59 @@ async function writeAndAdd(repoDir, relPath, content) {
|
|
|
13854
13922
|
await writeFileNoFollow(fullPath, content);
|
|
13855
13923
|
await import_isomorphic_git.default.add({ fs: import_fs4.default, dir: repoDir, filepath: safeRel });
|
|
13856
13924
|
}
|
|
13925
|
+
function isTraversalName(entryName) {
|
|
13926
|
+
const name2 = toRepoRelative(entryName);
|
|
13927
|
+
return name2.split("/").includes("..") || import_path3.default.isAbsolute(name2) || /^[a-zA-Z]:/.test(name2);
|
|
13928
|
+
}
|
|
13929
|
+
async function writeSnapshotParts(repoDir, extraFiles) {
|
|
13930
|
+
const written = /* @__PURE__ */ new Set();
|
|
13931
|
+
if (!extraFiles) return written;
|
|
13932
|
+
for (const [name2, content] of Object.entries(extraFiles)) {
|
|
13933
|
+
if (isTraversalName(name2)) {
|
|
13934
|
+
await writeAndAdd(repoDir, name2, content);
|
|
13935
|
+
continue;
|
|
13936
|
+
}
|
|
13937
|
+
if (!isTrackedPackageFile(name2)) continue;
|
|
13938
|
+
const repoPath = historyPathFor(name2);
|
|
13939
|
+
await writeAndAdd(repoDir, repoPath, content);
|
|
13940
|
+
written.add(repoPath);
|
|
13941
|
+
}
|
|
13942
|
+
return written;
|
|
13943
|
+
}
|
|
13944
|
+
async function indexedPartPaths(repoDir) {
|
|
13945
|
+
const tracked = await import_isomorphic_git.default.listFiles({ fs: import_fs4.default, dir: repoDir });
|
|
13946
|
+
return new Set(tracked.map((t) => toRepoRelative(t)).filter(isRepoPartPath));
|
|
13947
|
+
}
|
|
13948
|
+
async function snapshotIsProvenComplete(repoDir, extraFiles, written) {
|
|
13949
|
+
if (!extraFiles) return false;
|
|
13950
|
+
try {
|
|
13951
|
+
const indexed = await indexedPartPaths(repoDir);
|
|
13952
|
+
if (indexed.size !== written.size) return false;
|
|
13953
|
+
for (const p of written) if (!indexed.has(p)) return false;
|
|
13954
|
+
return true;
|
|
13955
|
+
} catch {
|
|
13956
|
+
return false;
|
|
13957
|
+
}
|
|
13958
|
+
}
|
|
13959
|
+
async function applySnapshotMarker(repoDir, complete) {
|
|
13960
|
+
if (complete) {
|
|
13961
|
+
await writeAndAdd(repoDir, SNAPSHOT_FORMAT_MARKER, `${SNAPSHOT_FORMAT_VERSION}
|
|
13962
|
+
`);
|
|
13963
|
+
return;
|
|
13964
|
+
}
|
|
13965
|
+
try {
|
|
13966
|
+
await import_promises2.default.unlink(import_path3.default.join(repoDir, SNAPSHOT_FORMAT_MARKER));
|
|
13967
|
+
} catch {
|
|
13968
|
+
}
|
|
13969
|
+
try {
|
|
13970
|
+
await import_isomorphic_git.default.remove({ fs: import_fs4.default, dir: repoDir, filepath: SNAPSHOT_FORMAT_MARKER });
|
|
13971
|
+
} catch {
|
|
13972
|
+
}
|
|
13973
|
+
const stillIndexed = (await import_isomorphic_git.default.listFiles({ fs: import_fs4.default, dir: repoDir })).some((t) => toRepoRelative(t) === SNAPSHOT_FORMAT_MARKER);
|
|
13974
|
+
if (stillIndexed) {
|
|
13975
|
+
throw new Error("History commit refused: could not clear the snapshot marker of an earlier commit.");
|
|
13976
|
+
}
|
|
13977
|
+
}
|
|
13857
13978
|
async function chmod0700BestEffort(dir) {
|
|
13858
13979
|
if (process.platform === "win32") return;
|
|
13859
13980
|
try {
|
|
@@ -14229,11 +14350,8 @@ async function initAstraRepo(docPath, initialXml, extraFiles) {
|
|
|
14229
14350
|
const repoDir = await ensureAstraGitRepo(docPath);
|
|
14230
14351
|
await import_isomorphic_git.default.init({ fs: import_fs4.default, dir: repoDir });
|
|
14231
14352
|
await writeAndAdd(repoDir, "document.xml", initialXml);
|
|
14232
|
-
|
|
14233
|
-
|
|
14234
|
-
await writeAndAdd(repoDir, name2, content);
|
|
14235
|
-
}
|
|
14236
|
-
}
|
|
14353
|
+
const writtenParts = await writeSnapshotParts(repoDir, extraFiles);
|
|
14354
|
+
await applySnapshotMarker(repoDir, await snapshotIsProvenComplete(repoDir, extraFiles, writtenParts));
|
|
14237
14355
|
await import_isomorphic_git.default.commit({
|
|
14238
14356
|
fs: import_fs4.default,
|
|
14239
14357
|
dir: repoDir,
|
|
@@ -14289,41 +14407,35 @@ async function commitChange(docPath, xmlContent, message, extraFiles, deletedFil
|
|
|
14289
14407
|
} catch (err) {
|
|
14290
14408
|
if (err?.code === HISTORY_QUOTA_CODE) throw err;
|
|
14291
14409
|
}
|
|
14292
|
-
await writeAndAdd(repoDir, "document.xml", xmlContent);
|
|
14293
14410
|
if (extraFiles) {
|
|
14294
|
-
for (const
|
|
14295
|
-
await writeAndAdd(repoDir, name2, content);
|
|
14296
|
-
}
|
|
14411
|
+
for (const name2 of Object.keys(extraFiles)) if (!isTraversalName(name2) && isTrackedPackageFile(name2)) historyPathFor(name2);
|
|
14297
14412
|
}
|
|
14413
|
+
await writeAndAdd(repoDir, "document.xml", xmlContent);
|
|
14414
|
+
const writtenParts = await writeSnapshotParts(repoDir, extraFiles);
|
|
14298
14415
|
try {
|
|
14299
14416
|
const toDelete = /* @__PURE__ */ new Set();
|
|
14300
14417
|
if (deletedFiles) {
|
|
14301
14418
|
for (const name2 of deletedFiles) {
|
|
14302
|
-
|
|
14303
|
-
|
|
14304
|
-
toDelete.add(rel);
|
|
14419
|
+
if (!isTrackedPackageFile(name2) || isTraversalName(name2)) continue;
|
|
14420
|
+
toDelete.add(historyPathFor(name2));
|
|
14305
14421
|
}
|
|
14306
14422
|
}
|
|
14307
14423
|
if (extraFiles) {
|
|
14308
|
-
const newTracked =
|
|
14309
|
-
Object.keys(extraFiles).map((k) => toRepoRelative(k))
|
|
14310
|
-
);
|
|
14424
|
+
const newTracked = writtenParts;
|
|
14311
14425
|
try {
|
|
14312
14426
|
const tracked = await import_isomorphic_git.default.listFiles({ fs: import_fs4.default, dir: repoDir });
|
|
14313
14427
|
for (const t of tracked) {
|
|
14314
14428
|
const rel = toRepoRelative(t);
|
|
14315
|
-
if (rel
|
|
14316
|
-
if (isTrackedPackageFile(rel) && !newTracked.has(rel)) {
|
|
14429
|
+
if (isRepoPartPath(rel) && !newTracked.has(rel)) {
|
|
14317
14430
|
toDelete.add(rel);
|
|
14318
14431
|
}
|
|
14319
14432
|
}
|
|
14320
14433
|
} catch {
|
|
14321
14434
|
}
|
|
14322
14435
|
try {
|
|
14323
|
-
const onDisk = await
|
|
14324
|
-
for (const rel of onDisk
|
|
14325
|
-
if (rel
|
|
14326
|
-
if (isTrackedPackageFile(rel) && !newTracked.has(rel)) {
|
|
14436
|
+
const onDisk = await listRepoPaths(repoDir);
|
|
14437
|
+
for (const rel of onDisk) {
|
|
14438
|
+
if (isRepoPartPath(rel) && !newTracked.has(rel)) {
|
|
14327
14439
|
toDelete.add(rel);
|
|
14328
14440
|
}
|
|
14329
14441
|
}
|
|
@@ -14331,7 +14443,7 @@ async function commitChange(docPath, xmlContent, message, extraFiles, deletedFil
|
|
|
14331
14443
|
}
|
|
14332
14444
|
}
|
|
14333
14445
|
for (const rel of toDelete) {
|
|
14334
|
-
if (
|
|
14446
|
+
if (isTraversalName(rel)) continue;
|
|
14335
14447
|
const fullPath = import_path3.default.join(repoDir, rel);
|
|
14336
14448
|
try {
|
|
14337
14449
|
await import_promises2.default.unlink(fullPath);
|
|
@@ -14344,6 +14456,7 @@ async function commitChange(docPath, xmlContent, message, extraFiles, deletedFil
|
|
|
14344
14456
|
}
|
|
14345
14457
|
} catch {
|
|
14346
14458
|
}
|
|
14459
|
+
await applySnapshotMarker(repoDir, await snapshotIsProvenComplete(repoDir, extraFiles, writtenParts));
|
|
14347
14460
|
const sha = await import_isomorphic_git.default.commit({
|
|
14348
14461
|
fs: import_fs4.default,
|
|
14349
14462
|
dir: repoDir,
|
|
@@ -14395,8 +14508,8 @@ async function getHistory(docPath) {
|
|
|
14395
14508
|
});
|
|
14396
14509
|
return entries;
|
|
14397
14510
|
}
|
|
14398
|
-
async function
|
|
14399
|
-
const out =
|
|
14511
|
+
async function listRepoPaths(repoDir) {
|
|
14512
|
+
const out = [];
|
|
14400
14513
|
async function walk(dir, relBase) {
|
|
14401
14514
|
let entries = [];
|
|
14402
14515
|
try {
|
|
@@ -14406,21 +14519,18 @@ async function collectRepoFiles(repoDir) {
|
|
|
14406
14519
|
}
|
|
14407
14520
|
for (const entry of entries) {
|
|
14408
14521
|
if (entry.name === ".git") continue;
|
|
14409
|
-
const full = import_path3.default.join(dir, entry.name);
|
|
14410
14522
|
const rel = relBase ? `${relBase}/${entry.name}` : entry.name;
|
|
14411
|
-
if (entry.isDirectory())
|
|
14412
|
-
|
|
14413
|
-
} else if (entry.isFile()) {
|
|
14414
|
-
try {
|
|
14415
|
-
out.set(rel, await import_promises2.default.readFile(full));
|
|
14416
|
-
} catch {
|
|
14417
|
-
}
|
|
14418
|
-
}
|
|
14523
|
+
if (entry.isDirectory()) await walk(import_path3.default.join(dir, entry.name), rel);
|
|
14524
|
+
else if (entry.isFile()) out.push(rel);
|
|
14419
14525
|
}
|
|
14420
14526
|
}
|
|
14421
14527
|
await walk(repoDir, "");
|
|
14422
14528
|
return out;
|
|
14423
14529
|
}
|
|
14530
|
+
function trackedPredicateFor(target) {
|
|
14531
|
+
return target.complete ? isTrackedPackageFile : isLegacyTrackedPackageFile;
|
|
14532
|
+
}
|
|
14533
|
+
var INCOMPLETE_SNAPSHOT_WARNING = "This commit was recorded by an older ASTra version and predates complete snapshots: parts it never recorded (for example comment metadata, document properties, custom XML) keep their current content in the file, so the restore is not exact: those parts can be left without the content-type or relationship entries they had before. Restores to versions saved from now on are exact.";
|
|
14424
14534
|
async function checkoutCommit(docPath, commitOid) {
|
|
14425
14535
|
if (typeof commitOid !== "string" || commitOid.length === 0) {
|
|
14426
14536
|
throw new Error(`Cannot checkout: commit ${commitOid} not found in history.`);
|
|
@@ -14484,8 +14594,18 @@ async function checkoutCommit(docPath, commitOid) {
|
|
|
14484
14594
|
);
|
|
14485
14595
|
}
|
|
14486
14596
|
const xml = Buffer.from(docBlob.blob).toString("utf8");
|
|
14597
|
+
let complete = false;
|
|
14598
|
+
const markerOid = blobs.get(SNAPSHOT_FORMAT_MARKER);
|
|
14599
|
+
if (markerOid) {
|
|
14600
|
+
try {
|
|
14601
|
+
const marker = await import_isomorphic_git.default.readBlob({ fs: import_fs4.default, dir: repoDir, oid: markerOid });
|
|
14602
|
+
complete = isCompleteSnapshotMarker(marker.blob);
|
|
14603
|
+
} catch {
|
|
14604
|
+
complete = false;
|
|
14605
|
+
}
|
|
14606
|
+
}
|
|
14487
14607
|
const trackedEntries = [...blobs.entries()].filter(
|
|
14488
|
-
([rel]) => rel
|
|
14608
|
+
([rel]) => isRepoPartPath(rel) && (complete || isLegacyTrackedPackageFile(entryNameFor(rel)))
|
|
14489
14609
|
);
|
|
14490
14610
|
const extraBlobs = await Promise.all(
|
|
14491
14611
|
trackedEntries.map(async ([rel, blobOid]) => {
|
|
@@ -14498,11 +14618,11 @@ async function checkoutCommit(docPath, commitOid) {
|
|
|
14498
14618
|
`Checkout failed: cannot read blob '${rel}' (${blobOid}) at commit ${commitOid}: ${cause}`
|
|
14499
14619
|
);
|
|
14500
14620
|
}
|
|
14501
|
-
return [rel, Buffer.from(blobResult.blob)];
|
|
14621
|
+
return [entryNameFor(rel), Buffer.from(blobResult.blob)];
|
|
14502
14622
|
})
|
|
14503
14623
|
);
|
|
14504
14624
|
const extra = new Map(extraBlobs);
|
|
14505
|
-
return { xml, extra };
|
|
14625
|
+
return { xml, extra, complete };
|
|
14506
14626
|
}
|
|
14507
14627
|
async function commitStateIfDiffersFromHead(docPath, xmlContent, message, extraFiles = {}, author) {
|
|
14508
14628
|
try {
|
|
@@ -43908,15 +44028,16 @@ async function handleRevertDocument(store, input) {
|
|
|
43908
44028
|
});
|
|
43909
44029
|
}
|
|
43910
44030
|
const merged = new Map(preRevertSnap);
|
|
44031
|
+
const isTrackedForTarget = trackedPredicateFor(target);
|
|
43911
44032
|
for (const key of [...merged.keys()]) {
|
|
43912
|
-
if (key !== "word/document.xml" &&
|
|
44033
|
+
if (key !== "word/document.xml" && isTrackedForTarget(key)) merged.delete(key);
|
|
43913
44034
|
}
|
|
43914
44035
|
for (const [k, v] of target.extra) merged.set(k, v);
|
|
43915
44036
|
merged.set("word/document.xml", Buffer.from(target.xml, "utf8"));
|
|
43916
44037
|
let writeAttempted = false;
|
|
43917
44038
|
let commitPhase = false;
|
|
43918
44039
|
try {
|
|
43919
|
-
handle.parser.restorePackageSnapshot(merged);
|
|
44040
|
+
handle.parser.restorePackageSnapshot(merged, { registerCoreProperties: !target.complete });
|
|
43920
44041
|
await refreshHandleAstInPlace(handle);
|
|
43921
44042
|
const buf = handle.parser.rezip();
|
|
43922
44043
|
const candidate = await validateForBaseline(buf);
|
|
@@ -43964,7 +44085,9 @@ async function handleRevertDocument(store, input) {
|
|
|
43964
44085
|
dirty: false,
|
|
43965
44086
|
discardedUnsaved,
|
|
43966
44087
|
commitOid,
|
|
43967
|
-
commitMessage
|
|
44088
|
+
commitMessage,
|
|
44089
|
+
snapshotComplete: target.complete,
|
|
44090
|
+
...target.complete ? {} : { restoreWarning: INCOMPLETE_SNAPSHOT_WARNING }
|
|
43968
44091
|
};
|
|
43969
44092
|
} catch (stageErr) {
|
|
43970
44093
|
const preserved = getEngineErrorCode(stageErr);
|
|
@@ -44266,7 +44389,8 @@ async function handlePreviewRevert(store, input) {
|
|
|
44266
44389
|
const targetXmlBytes = Buffer.from(target.xml, "utf8");
|
|
44267
44390
|
if (!liveXml || Buffer.compare(liveXml, targetXmlBytes) !== 0) affectedFiles.push("word/document.xml");
|
|
44268
44391
|
const names = /* @__PURE__ */ new Set();
|
|
44269
|
-
|
|
44392
|
+
const isTrackedForTarget = trackedPredicateFor(target);
|
|
44393
|
+
for (const k of liveSnap.keys()) if (k !== "word/document.xml" && isTrackedForTarget(k)) names.add(k);
|
|
44270
44394
|
for (const k of target.extra.keys()) names.add(k);
|
|
44271
44395
|
for (const name2 of [...names].sort()) {
|
|
44272
44396
|
if (affectedFiles.length >= AUDIT_FILES_MAX) break;
|
|
@@ -44314,6 +44438,8 @@ async function handlePreviewRevert(store, input) {
|
|
|
44314
44438
|
changedValuesTruncated,
|
|
44315
44439
|
changedValuesPositional,
|
|
44316
44440
|
...changedValuesReason ? { changedValuesReason } : {},
|
|
44441
|
+
snapshotComplete: target.complete,
|
|
44442
|
+
...target.complete ? {} : { restoreWarning: INCOMPLETE_SNAPSHOT_WARNING },
|
|
44317
44443
|
dirty,
|
|
44318
44444
|
dirtyHandling: dirty ? `Session holds ${unsavedOps} revision(s) of unsaved edits: a bare real revert would fail closed with E_CONFLICT; a real revert with discardUnsaved:true WOULD discard them (echoed back bounded). Net-zero edits still count as dirty (revisions pending, not a content diff). This preview discarded nothing.` : "Session is clean: a real revert would discard nothing.",
|
|
44319
44445
|
currentHeadOid: full[0].oid,
|
|
@@ -45658,7 +45784,8 @@ var import_zod9 = require("zod");
|
|
|
45658
45784
|
// package.json
|
|
45659
45785
|
var package_default = {
|
|
45660
45786
|
name: "@attentionform/astra-mcp-server",
|
|
45661
|
-
|
|
45787
|
+
mcpName: "com.attentionform/astra-mcp-server",
|
|
45788
|
+
version: "0.4.2",
|
|
45662
45789
|
description: "Official Model Context Protocol (MCP) server for ASTra document IDE",
|
|
45663
45790
|
main: "dist/index.js",
|
|
45664
45791
|
types: "dist/index.d.ts",
|
|
@@ -45699,8 +45826,8 @@ var package_default = {
|
|
|
45699
45826
|
"adm-zip-gate": "node ../../scripts/check-adm-zip-imports.mjs"
|
|
45700
45827
|
},
|
|
45701
45828
|
dependencies: {
|
|
45702
|
-
"@attentionform/astra-engine": "^0.4.
|
|
45703
|
-
"@attentionform/ooxml-validator": "^0.4.
|
|
45829
|
+
"@attentionform/astra-engine": "^0.4.2",
|
|
45830
|
+
"@attentionform/ooxml-validator": "^0.4.2",
|
|
45704
45831
|
"@modelcontextprotocol/sdk": "^1.32.2",
|
|
45705
45832
|
"@xmldom/xmldom": "0.8.15",
|
|
45706
45833
|
"adm-zip": "0.6.1",
|
|
@@ -36,6 +36,10 @@ export declare function handleRevertDocument(store: McpSessionStore, input: unkn
|
|
|
36
36
|
discardedUnsaved: DiscardedUnsavedSummary;
|
|
37
37
|
commitOid: string;
|
|
38
38
|
commitMessage: string;
|
|
39
|
+
/** True when the target commit recorded every package part, so the restore is exact. */
|
|
40
|
+
snapshotComplete: boolean;
|
|
41
|
+
/** Present when the target commit predates complete snapshots: the restore is not exact. */
|
|
42
|
+
restoreWarning?: string;
|
|
39
43
|
} | (McpErrorShape & {
|
|
40
44
|
dirtyReason?: string;
|
|
41
45
|
})>;
|
|
@@ -79,6 +83,10 @@ export declare function handlePreviewRevert(store: McpSessionStore, input: unkno
|
|
|
79
83
|
changedValuesPositional: boolean;
|
|
80
84
|
/** Present when the target range is not computable (empty changedValues). */
|
|
81
85
|
changedValuesReason?: string;
|
|
86
|
+
/** True when the target commit recorded every package part, so a real revert is exact. */
|
|
87
|
+
snapshotComplete: boolean;
|
|
88
|
+
/** Present when the target commit predates complete snapshots: a real revert is not exact. */
|
|
89
|
+
restoreWarning?: string;
|
|
82
90
|
dirty: boolean;
|
|
83
91
|
dirtyHandling: string;
|
|
84
92
|
currentHeadOid: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@attentionform/astra-mcp-server",
|
|
3
|
-
"
|
|
3
|
+
"mcpName": "com.attentionform/astra-mcp-server",
|
|
4
|
+
"version": "0.4.2",
|
|
4
5
|
"description": "Official Model Context Protocol (MCP) server for ASTra document IDE",
|
|
5
6
|
"main": "dist/index.js",
|
|
6
7
|
"types": "dist/index.d.ts",
|
|
@@ -41,8 +42,8 @@
|
|
|
41
42
|
"adm-zip-gate": "node ../../scripts/check-adm-zip-imports.mjs"
|
|
42
43
|
},
|
|
43
44
|
"dependencies": {
|
|
44
|
-
"@attentionform/astra-engine": "^0.4.
|
|
45
|
-
"@attentionform/ooxml-validator": "^0.4.
|
|
45
|
+
"@attentionform/astra-engine": "^0.4.2",
|
|
46
|
+
"@attentionform/ooxml-validator": "^0.4.2",
|
|
46
47
|
"@modelcontextprotocol/sdk": "^1.32.2",
|
|
47
48
|
"@xmldom/xmldom": "0.8.15",
|
|
48
49
|
"adm-zip": "0.6.1",
|