Files
Omar Mihilmy eb0c1ea962
plugin-validate / validate (push) Has been cancelled
secret-scan / gitleaks (push) Has been cancelled
Add read-only add-in data export; harden cache-clear scripts (#302)
* Add read-only add-in data export; harden cache-clear scripts

Chat history, skills, MCP registrations and memory live only in browser
storage on the user's machine. Add export-addin-data.{sh,ps1} to copy them
before a rebuild, and fix the cache-clear scripts that sit next to them.

clear-addin-cache.sh matched the add-in ID case-sensitively, but Office names
the wef file with whatever casing the manifest <Id> carried. A lowercase GUID
reported "already clear" and exited 0, sending admins to the folder-wide wipe
that destroys chat history on Windows. Match case-insensitively, and make a
miss print NOT cleared and exit non-zero on both platforms.

clear-addin-cache.ps1 never parsed on Windows PowerShell 5.1: em dashes in a
BOM-less .ps1 are decoded as ANSI into mojibake containing a quote, which
terminates a string mid-file. All .ps1 files are now pure ASCII, and check.py
rejects non-ASCII bytes in a .ps1 without a UTF-8 BOM.

Also:
- export: parse the port out of WebKit's binary origin file. Dropping it made
  two origins on the same host collide in one export folder, silently
  overwriting the first.
- export: include localStorage, which holds settings, the inference config,
  and the onboarding/terms flags.
- export: exporting nothing while --out was given now exits non-zero, so
  "export then wipe" cannot treat an empty backup as success.
- export: a failed database copy no longer aborts the run; paths containing
  an apostrophe work.
- export.ps1: clear the destination before Copy-Item -Recurse, which nested
  instead of refreshing on a re-run; add -IncludeAll for stores whose names
  compaction has hidden; scan uncompressed LevelDB files first.
- clear.ps1: require a GUID for -Id, since Remove-Item -Path expands wildcards;
  escape value names for Remove-ItemProperty.
- clear.sh: scan Outlook too, and guard flags that take a value.
- Document why replacing or reinstalling a manifest never moves the data.

Verified on macOS against live add-in data, and on a Windows host against
Windows PowerShell 5.1.

* Export Windows localStorage as the whole per-profile store

localStorage holds the settings, the inference/customer config, and the
onboarding + terms-accepted flags -- none of it in IndexedDB -- so an export
without it left a rebuilt machine re-doing onboarding.

Chromium keeps one LevelDB per profile shared by every origin, so it cannot be
filtered down to one add-in. Copy it whole rather than drop it, once per Office
account, beside that account's origin folders. It therefore also contains other
origins' settings: the size is printed on every run including the argument-less
preview, the help text says so plainly, and the folder can simply be deleted
from the export if a data policy forbids it.

- Copy to a scratch name and swap only on success. Deleting the destination
  first meant a locked LOCK file (Office still running) left the export with
  neither the new copy nor the previous good one. A failure is now counted and
  reported as INCOMPLETE with a non-zero exit, matching the macOS script.
- Drive the copy off the unique profile list instead of the per-store loop, so
  a profile whose only store fails the origin-name parse keeps its settings.
- Report in list mode too, matching macOS -- the argument-less run is
  advertised as printing what it found.
- .SYNOPSIS/.DESCRIPTION no longer claim only Claude's data is copied.
- Correct the export-data doc: the export tree is relabelled for readability,
  not a path mirror, so it cannot be copied back verbatim.

Verified on a Windows host against Windows PowerShell 5.1: copied once per
profile, flat on re-run, real LevelDB contents present, disclosed in list mode
without writing anything.
2026-07-22 12:12:21 -04:00
..