Sync third-party and MCP marketplace plugins
Constraint: Public skills are published only by explicit administrator action unless they are tracked third-party market sources. Confidence: high Scope-risk: narrow Directive: Keep private/internal skills out of the public marketplace and preserve normal incremental market Git history. Tested: Marketplace validation passed.
This commit is contained in:
1 parent
be7ce9bda2
commit
36c089fee9
298 files changed
+32935
-11681
No files matched your search
@@ -0,0 +1,75 @@
|
||||
"""Accept tracked changes into a new DOCX using an isolated LibreOffice profile."""
|
||||
|
||||
import argparse
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
from defusedxml import ElementTree
|
||||
from office.soffice import run_soffice
|
||||
|
||||
MACRO = '''<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE script:module PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "module.dtd">
|
||||
<script:module xmlns:script="http://openoffice.org/2000/script" script:name="Module1" script:language="StarBasic">
|
||||
Sub AcceptAllTrackedChanges()
|
||||
Dim dispatcher As Object
|
||||
dispatcher = createUnoService("com.sun.star.frame.DispatchHelper")
|
||||
dispatcher.executeDispatch(ThisComponent.CurrentController.Frame, ".uno:AcceptAllTrackedChanges", "", 0, Array())
|
||||
ThisComponent.store()
|
||||
ThisComponent.close(True)
|
||||
End Sub
|
||||
</script:module>'''
|
||||
WORD_NS = "{http://schemas.openxmlformats.org/wordprocessingml/2006/main}"
|
||||
|
||||
|
||||
def accept_changes(input_file, output_file, timeout=120):
|
||||
source = Path(input_file).resolve()
|
||||
output = Path(output_file).resolve()
|
||||
if source == output or not source.is_file() or source.suffix.lower() != ".docx":
|
||||
return None, "Input must be an existing DOCX and output must be a different file"
|
||||
output.parent.mkdir(parents=True, exist_ok=True)
|
||||
try:
|
||||
with tempfile.TemporaryDirectory(prefix="docx-accept-", ignore_cleanup_errors=True) as temporary:
|
||||
root = Path(temporary).resolve()
|
||||
profile = root / "profile"
|
||||
profile_arg = f"-env:UserInstallation={profile.as_uri()}"
|
||||
run_soffice([profile_arg, "--headless", "--terminate_after_init"], check=True, capture_output=True, timeout=timeout)
|
||||
macro_dir = profile / "user/basic/Standard"
|
||||
macro_dir.mkdir(parents=True, exist_ok=True)
|
||||
(macro_dir / "Module1.xba").write_text(MACRO, encoding="utf-8")
|
||||
candidate = root / "accepted.docx"
|
||||
shutil.copy2(source, candidate)
|
||||
before = candidate.stat().st_mtime_ns
|
||||
run_soffice([
|
||||
profile_arg, "--headless", "--norestore",
|
||||
"vnd.sun.star.script:Standard.Module1.AcceptAllTrackedChanges?language=Basic&location=application",
|
||||
str(candidate),
|
||||
], check=True, capture_output=True, timeout=timeout)
|
||||
if candidate.stat().st_mtime_ns == before:
|
||||
return None, "LibreOffice did not save the document"
|
||||
with zipfile.ZipFile(candidate) as archive:
|
||||
for name in archive.namelist():
|
||||
if not name.startswith("word/") or not name.endswith(".xml"):
|
||||
continue
|
||||
document = ElementTree.fromstring(archive.read(name))
|
||||
if any(element.tag in {WORD_NS + "ins", WORD_NS + "del", WORD_NS + "moveFrom", WORD_NS + "moveTo"}
|
||||
or (element.tag.startswith(WORD_NS) and element.tag.endswith("Change"))
|
||||
for element in document.iter()):
|
||||
return None, f"Tracked changes remain in {name}"
|
||||
shutil.copy2(candidate, output)
|
||||
return None, f"Accepted tracked changes: {output}"
|
||||
except (OSError, subprocess.SubprocessError, zipfile.BadZipFile) as exc:
|
||||
return None, f"Failed to accept tracked changes: {exc}"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("input")
|
||||
parser.add_argument("output")
|
||||
parser.add_argument("--timeout", type=int, default=120)
|
||||
args = parser.parse_args()
|
||||
_, message = accept_changes(args.input, args.output, args.timeout)
|
||||
print(message)
|
||||
raise SystemExit(0 if message.startswith("Accepted tracked changes:") else 1)
|
||||
Reference in new issue
Block a user