From 9bf0fc13252f27f4b5cf6b11cd00b54bb10bf693 Mon Sep 17 00:00:00 2001 From: Volodymyr Vreshch Date: Sat, 18 Jul 2026 00:49:01 +0200 Subject: [PATCH] feat: restore git as the main sync channel (dual-channel with couch) Reverts #89 (couch-only). Git = default channel for every memory; a memory the resolution advertises on couch routes to the couch controller (server decides per memory, future /api/me capabilities gate the paid couch tier). - src/git/* + sync-controller restored; couch impl stays at master (resilient syncNow, missing-leaf abort, pendingCount) - previewSync routes per channel: git = both-way controller preview, couch = honest outgoing count (#92 semantics kept); SyncPreview.incoming optional - fake router advertises git_endpoint; lifecycle 10 rewritten for dual-channel --- CLAUDE.md | 21 +- README.md | 17 +- esbuild.config.mjs | 5 +- manifest.json | 2 +- package-lock.json | 379 +++++++++++++++++++++++-- package.json | 2 + scripts/check-docs.mjs | 4 +- scripts/check-hosts.mjs | 2 +- src/git/backup-ref.test.ts | 48 ++++ src/git/backup-ref.ts | 36 +++ src/git/git-client.test.ts | 124 ++++++++ src/git/git-client.ts | 212 ++++++++++++++ src/git/git-test-server.ts | 66 +++++ src/git/http-requesturl.test.ts | 96 +++++++ src/git/http-requesturl.ts | 49 ++++ src/git/merge-note.test.ts | 84 ++++++ src/git/merge-note.ts | 103 +++++++ src/git/stream-utils.test.ts | 27 ++ src/git/stream-utils.ts | 28 ++ src/git/vault-fs-git.test.ts | 152 ++++++++++ src/git/vault-fs.ts | 188 ++++++++++++ src/main.test.ts | 60 ++-- src/main.ts | 172 ++++++++--- src/resolve-host.test.ts | 91 ++++-- src/resolve-host.ts | 70 +++-- src/settings.ts | 4 +- src/sync-controller.test.ts | 221 ++++++++++++++ src/sync-controller.ts | 269 ++++++++++++++++++ src/sync-preview-modal.test.ts | 17 +- src/sync-preview-modal.ts | 16 +- src/types/diff3.d.ts | 20 ++ test/fakes/router.ts | 5 +- test/integration/lifecycle.test.ts | 13 +- test/integration/preview-count.test.ts | 6 +- vitest.config.ts | 16 +- 35 files changed, 2439 insertions(+), 186 deletions(-) create mode 100644 src/git/backup-ref.test.ts create mode 100644 src/git/backup-ref.ts create mode 100644 src/git/git-client.test.ts create mode 100644 src/git/git-client.ts create mode 100644 src/git/git-test-server.ts create mode 100644 src/git/http-requesturl.test.ts create mode 100644 src/git/http-requesturl.ts create mode 100644 src/git/merge-note.test.ts create mode 100644 src/git/merge-note.ts create mode 100644 src/git/stream-utils.test.ts create mode 100644 src/git/stream-utils.ts create mode 100644 src/git/vault-fs-git.test.ts create mode 100644 src/git/vault-fs.ts create mode 100644 src/sync-controller.test.ts create mode 100644 src/sync-controller.ts create mode 100644 src/types/diff3.d.ts diff --git a/CLAUDE.md b/CLAUDE.md index 264fa0a..3e242e3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,25 +4,26 @@ Guidance for Claude Code when working in this repository. ## Project Overview -**Agentage Sync — Obsidian plugin.** A configuration page + live **CouchDB** sync between an Obsidian vault and the user's agentage Memory, plus OAuth sign-in. AI clients reach the same memory over MCP at `memory.agentage.io`. +**Agentage Sync — Obsidian plugin.** A configuration page + bidirectional **git** sync between an Obsidian vault and the user's agentage Memory, plus OAuth sign-in. AI clients reach the same memory over MCP at `memory.agentage.io`. - **Plugin id:** `agentage-memory` (locked — the install/auto-update key) · **Display name:** `Agentage Sync` - **Repo:** `agentage/obsidian-sync` · **Default branch:** `master` -- **Couch is the only device channel.** The plugin replicates a content-addressed doc model (leaf docs + a file doc) to a per-memory CouchDB over Obsidian `requestUrl`; a server bridge commits every couch edit to git (the store stays one bare git repo per memory, server-authoritative, `git grep`-able). Plaintext markdown end to end. The git device channel was removed (see git history before the couch-only cut) - a memory the server has **not** advertised on the couch channel is an explicit error (server flip pending), never a silent fallback. +- The plugin is a **git client** (vendored `isomorphic-git` over Obsidian `requestUrl`) talking to the agentage git server at `sync.agentage.io`; the store is one bare git repo per vault, server-authoritative. Plaintext markdown end to end (no client E2E) so the cloud can `git grep` it. ## Architecture (`src/`) - **Config** — `settings.ts` (pure model mirroring memory-core `vaults.json`), `settings-tab.ts` (the page: Connect · Setup sync · Expose local/remote MCP · MCP address · config file), `vaults-config.ts` (merge-preserving `~/.agentage/vaults.json` writer; desktop, atomic). - **Auth** (`auth/`) — `pkce.ts` (S256), `oauth.ts` (DCR + token exchange/refresh/revoke; public PKCE client), `discovery.ts` (`/.well-known/oauth-authorization-server`), `token-store.ts` (`app.secretStorage` + the `auth.json` mirror), `auth-json.ts` (desktop `~/.agentage/auth.json`, atomic 0600, CLI shape), `auth-flow.ts` (DI orchestration: startSignIn/handleCallback/getValidToken-with-refresh/disconnect/isSignedIn). AS = Better Auth at `auth.agentage.io`; custom-scheme redirect `obsidian://agentage-memory-cb`. -- **Couch** (`couch/`) — `couch-sync.ts` (live replication: push-on-save + interval pull, echo-safe, resilient retry queues), `couch-doc.ts` (content-addressed leaf/file doc model + Web Crypto rev), `couch-state.ts` (persisted pull cursor + push-rev cache + pending push/delete queues, through `data.json`), `couch-channel.ts` (holds the single live controller per memory; a switch/sign-out tears it down), `couch-token.ts` (mints/caches the per-memory couch JWT from the resolved `couch_token_url`). -- **Sync** — `resolve-host.ts` (`/.well-known/agentage-sync` + 1h cache; parses the couch advert and routes a memory to `couch` or `error`). -- **Entry** — `main.ts` (wires Obsidian adapters: secretStorage, requestUrl, the couch channel + live vault-event handlers, node fs only for the desktop `~/.agentage` config, the ribbon/status-bar/command). Obsidian-coupled files are coverage-excluded; the rest is unit-tested. +- **Git** (`git/`) — `git-client.ts` (DI clone/fetch/pull/push/merge; full single-branch, token-in-header, **never force**), `merge-note.ts` (split-YAML field-LWW + diff3 body), `backup-ref.ts`, `http-requesturl.ts` (requestUrl `HttpClient`), `vault-fs.ts` (`vault.adapter` fs-shim — **wired as the git fs on every platform**; no `node:fs` on the sync path), `stream-utils.ts`, `git-test-server.ts` (test-only `git-http-backend`). +- **Sync** — `resolve-host.ts` (`/.well-known/agentage-sync` + 1h cache), `sync-controller.ts` (single-flight lifecycle: ensure repo → commit-before-pull → merge → conflict note → push). +- **Entry** — `main.ts` (wires Obsidian adapters: secretStorage, requestUrl, `VaultFs` for git, node fs only for the desktop `~/.agentage` config, the ribbon/status-bar/command). Obsidian-coupled files are coverage-excluded; the rest is unit/integration-tested. ## Key invariants -- **Server reconciles history.** The server bridge commits couch edits to git and reconciles concurrent edits; the plugin is echo-safe (a push/pull whose content already matches is skipped) so the vault↔couch↔git loop converges. Never silent-drop. -- **Couch is the only device channel.** A memory absent from the resolution's `couch_vaults` resolves to `channel: 'error'` - surfaced as a Notice + red status dot ("not on the new sync channel yet - server update pending"), never a git fallback or a no-op. -- **Tokens** live in `app.secretStorage` + `~/.agentage/auth.json` (0600) - **never** `vaults.json`/`data.json`. The per-memory couch JWT is minted on demand from `couch_token_url` (auth service is the sole minter), never persisted. -- **No node builtins on the sync path** — couch uses `requestUrl` + Web Crypto (mobile-safe). The only `node:fs/os/path` use is the desktop `~/.agentage` config mirror (lazy, `isDesktop`-guarded); `node:http` backs the desktop loopback OAuth listener. **Mobile is deferred** (`isDesktopOnly: true`): the sync layer is mobile-safe, but sign-in (the `obsidian://` deep-link round-trip) is not device-verified — re-enable only after a mobile smoke passes. +- **Client owns history.** The server is plain git (force-push allowed, no fast-forward-only), so the plugin **never force-pushes**; it commits-before-pull, 3-way merges, and surfaces conflicts (markers + a `conflict:true` note). Never silent-drop. +- **Tokens** live in `app.secretStorage` + `~/.agentage/auth.json` (0600) — **never** `vaults.json`/`data.json`. +- **isomorphic-git gotchas:** token via `onAuth` header only (never the URL, #1942); full single-branch clone (no `depth` — shallow breaks push, #682); no gc (re-clone on bloat — mobile, future). +- **Merge:** split YAML frontmatter before diff3 (markers inside `---` corrupt the note). +- **Git fs is `VaultFs` (vault adapter) on every platform** — the only `node:fs/os/path` use is the desktop `~/.agentage` config mirror (lazy, `isDesktop`-guarded). **Mobile is deferred** (`isDesktopOnly: true`): the git layer is mobile-safe, but sign-in (the `obsidian://` deep-link round-trip) and first-sync `.git` detection are not device-verified — re-enable only after a mobile smoke passes. ## Development @@ -30,7 +31,7 @@ Guidance for Claude Code when working in this repository. npm install npm run dev # esbuild watch -> main.js npm run build # production main.js -npm test # vitest +npm test # vitest (spawns git-http-backend; needs the git binary) npm run verify # type-check + lint + format + test + build + check:hosts/docs/bundle npm version # bump manifest.json + versions.json (release prep) ``` diff --git a/README.md b/README.md index 98bf084..ad1862d 100644 --- a/README.md +++ b/README.md @@ -6,17 +6,17 @@ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE) [![Obsidian](https://img.shields.io/badge/Obsidian-1.11.4%2B-7c3aed.svg)](https://obsidian.md) -Stop copying notes into every chatbot. **Agentage Sync** makes your [Obsidian](https://obsidian.md) vault one shared [Agentage Memory](https://agentage.io) that every AI reads and writes over MCP - Claude, ChatGPT, Cursor, or any MCP client - so the same context follows you across apps. It syncs live to a private hosted memory: notes stay plain Markdown you own, edits replicate on save, and you pick which memory to sync to. Desktop only, with up to 100 MB of storage included. +Stop copying notes into every chatbot. **Agentage Sync** makes your [Obsidian](https://obsidian.md) vault one shared [Agentage Memory](https://agentage.io) that every AI reads and writes over MCP - Claude, ChatGPT, Cursor, or any MCP client - so the same context follows you across apps. It works through two-way **Git** sync to a private hosted memory: notes stay plain Markdown you own, merges are safe with flagged conflicts, and you pick which memory to sync to. Desktop only, with up to 100 MB of storage included. > ⚠️ **Status:** **desktop only** for now - mobile is on the way. ## Features -- 🔄 **Two-way live sync** - edits replicate to your hosted memory as you save, and remote changes flow back in. On the server your notes are stored as a bare git repo per memory that you can clone or export anytime. +- 🔄 **Two-way Git sync** - clone / pull / commit / push your vault to `sync.agentage.io`. The server is a bare git repo per memory that you can clone or export anytime. - 🧠 **Pick or create a memory** - choose which memory this vault syncs into, or create a new one, from a single dialog (search your memories, see file/folder counts). - 🤝 **Shared with every AI over MCP** - the same memory is exposed at `memory.agentage.io`, so Claude / ChatGPT / Cursor read and write the same notes. [How to connect →](https://agentage.io/connect) - 🔐 **Sign in once** - a secure browser sign-in; your access is kept in Obsidian's encrypted storage, never in your notes or config. -- 🧩 **Plain Markdown** - notes stay `.md`; concurrent edits reconcile server-side so nothing is silently dropped. +- 🧩 **Plain Markdown, safe merges** - notes stay `.md`; edits from different places merge automatically, and anything that can't be is flagged with a note - never a silent drop. - 📊 **Status at a glance** - a status-bar dot (green / red / gray) with a click menu: Sync now, Open dashboard, Settings. The same actions are in the command palette and the ribbon. ## Installation @@ -33,7 +33,7 @@ From the [latest release](https://github.com/agentage/obsidian-sync/releases/lat 1. **Settings → Agentage Sync → Sign in to Agentage** - a browser window opens once; no password is stored by the plugin. 2. **Choose a memory** - click the status-bar dot (or run **Agentage Sync: Choose memory** from the command palette) → **Choose memory…** → pick an existing memory or **Create a new** one. (A fresh memory makes the cleanest first sync.) -3. **Sync now** - from the dot menu or the command palette (**Agentage Sync: Sync now**). Your notes replicate to the memory, and edits keep syncing live as you save. +3. **Sync now** - from the dot menu or the command palette (**Agentage Sync: Sync now**). Your notes are committed and pushed. 4. **Connect your AI apps** - point Claude / ChatGPT / Cursor at your memory over MCP. [See how to connect →](https://agentage.io/connect) ## Settings @@ -47,19 +47,20 @@ From the [latest release](https://github.com/agentage/obsidian-sync/releases/lat - **Account required:** you need an agentage account to sign in and sync. - **Storage:** each account includes up to 100 MB. - **No calls until you act:** a fresh or signed-out install makes **no network requests at all** - the plugin only contacts the network once you Connect or Sync. -- **Network use:** when active, the plugin contacts `auth.agentage.io` (sign-in, OAuth 2.1 / PKCE), **`sync.agentage.io`** (to discover your sync endpoint), and the per-memory sync host it hands back (for the live replication of your notes). "Open dashboard" opens `dashboard.agentage.io` in your browser. AI access to the same memory happens server-side over MCP at `memory.agentage.io`. +- **Network use:** when active, the plugin contacts `auth.agentage.io` (sign-in, OAuth 2.1 / PKCE) and **`sync.agentage.io`** (syncing your vault over Git). "Open dashboard" opens `dashboard.agentage.io` in your browser. AI access to the same memory happens server-side over MCP at `memory.agentage.io`. - **Privacy policy:** . **Terms of Service:** . -- **No client-side telemetry.** Your notes are stored server-side in your own per-tenant git repo (EU-hosted) and as plain Markdown on your machine; the OAuth token is kept in Obsidian's encrypted secret storage, never in `vaults.json` or `data.json`. +- **No client-side telemetry.** Your notes live in your own per-tenant git repo (EU-hosted) and as plain Markdown on your machine; the OAuth token is kept in Obsidian's encrypted secret storage, never in `vaults.json` or `data.json`. ## Third-party -- **js-yaml** (MIT) - frontmatter parsing. +- **[isomorphic-git](https://github.com/isomorphic-git/isomorphic-git)** (MIT) - pure-JS git client. +- **js-yaml** (MIT) + **diff3** - frontmatter parsing and 3-way merge. ## FAQ - **Why desktop only?** Mobile sign-in isn't fully tested on phones yet, so the plugin is desktop-only for now. Mobile is on the way. - **Where is my sign-in token stored?** In Obsidian's encrypted storage - never in your notes or plugin settings. -- **What happens on a conflict?** Nothing is silently dropped. Concurrent edits are reconciled server-side and flow back to your vault. +- **What happens on a conflict?** Nothing is silently dropped. If the same note changed in two places, anything that can't be merged automatically is flagged in an **Agentage Sync Conflicts** note listing the files. Fix them, then sync again. - **Can I leave or export?** Yes - you can export your memory anytime, and your notes stay as plain Markdown files on your computer. ## Support diff --git a/esbuild.config.mjs b/esbuild.config.mjs index c5e1e05..37b1371 100644 --- a/esbuild.config.mjs +++ b/esbuild.config.mjs @@ -18,9 +18,8 @@ const context = await esbuild.context({ // reaches them. Without the `node:`-prefixed entries esbuild leaves the raw // specifier and the Obsidian renderer can't resolve it. external: ['obsidian', 'electron', ...builtins, ...builtins.map((m) => `node:${m}`)], - // Browser platform so the bundle is mobile-safe; the couch sync channel uses Obsidian - // requestUrl + Web Crypto (no node builtins), js-yaml is pure-JS. A couple of Node - // globals are shimmed for browser resolution. + // Browser platform so the bundle is mobile-safe; isomorphic-git + diff3 + js-yaml + // are pure-JS. A couple of Node globals are shimmed for browser resolution. platform: 'browser', define: { global: 'window', diff --git a/manifest.json b/manifest.json index 8781131..1dc275f 100644 --- a/manifest.json +++ b/manifest.json @@ -3,7 +3,7 @@ "name": "Agentage Sync", "version": "0.2.3", "minAppVersion": "1.11.4", - "description": "Let Claude, ChatGPT, Cursor, and any MCP client read and write your vault. Two-way live sync to a private memory, concurrent edits reconciled server-side, plain Markdown you own. Up to 100 MB included.", + "description": "Let Claude, ChatGPT, Cursor, and any MCP client read and write your vault. Two-way Git sync to a private memory, safe merges with flagged conflicts, plain Markdown you own. Up to 100 MB included.", "author": "agentage", "authorUrl": "https://agentage.io", "helpUrl": "https://agentage.io/connect", diff --git a/package-lock.json b/package-lock.json index 1bddd8e..b168ee1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,8 @@ "version": "0.2.3", "license": "MIT", "dependencies": { + "diff3": "^0.0.4", + "isomorphic-git": "^1.38.6", "js-yaml": "^5.2.1" }, "devDependencies": { @@ -1932,6 +1934,18 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, "node_modules/acorn": { "version": "8.17.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", @@ -2186,11 +2200,16 @@ "node": ">= 0.4" } }, + "node_modules/async-lock": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", + "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==", + "license": "MIT" + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "dev": true, "license": "MIT", "dependencies": { "possible-typed-array-names": "^1.0.0" @@ -2212,6 +2231,26 @@ "node": "18 || 20 || >=22" } }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/brace-expansion": { "version": "5.0.7", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz", @@ -2225,6 +2264,30 @@ "node": "18 || 20 || >=22" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/builtin-modules": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-5.3.0.tgz", @@ -2242,7 +2305,6 @@ "version": "1.0.9", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -2261,7 +2323,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -2275,7 +2336,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -2325,6 +2385,12 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/clean-git-ref": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/clean-git-ref/-/clean-git-ref-2.0.1.tgz", + "integrity": "sha512-bLSptAy2P0s6hU4PzuIMKmMJJSE6gLXGH1cntDu7bWJUksvuM+7ReOK61mozULErYvP6a15rnYl0zFDef+pyPw==", + "license": "Apache-2.0" + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2359,6 +2425,18 @@ "dev": true, "license": "MIT" }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, "node_modules/crelt": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.7.tgz", @@ -2454,6 +2532,21 @@ } } }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -2465,7 +2558,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", @@ -2507,6 +2599,12 @@ "node": ">=8" } }, + "node_modules/diff3": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/diff3/-/diff3-0.0.4.tgz", + "integrity": "sha512-f1rQ7jXDn/3i37hdwRk9ohqcvLRH3+gEIgmA6qEM280WUOh7cOr3GXV8Jm5sPwUs46Nzl48SE8YNLGJoaLuodg==", + "license": "MIT" + }, "node_modules/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -2524,7 +2622,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -2651,7 +2748,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -2661,7 +2757,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -2706,7 +2801,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -3976,6 +4070,24 @@ "node": ">=0.10.0" } }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, "node_modules/expect-type": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", @@ -4104,7 +4216,6 @@ "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", - "dev": true, "license": "MIT", "dependencies": { "is-callable": "^1.2.7" @@ -4135,7 +4246,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -4189,7 +4299,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -4214,7 +4323,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -4302,7 +4410,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -4345,7 +4452,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, "license": "MIT", "dependencies": { "es-define-property": "^1.0.0" @@ -4374,7 +4480,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -4387,7 +4492,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -4403,7 +4507,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", - "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -4419,6 +4522,26 @@ "dev": true, "license": "MIT" }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/ignore": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", @@ -4456,6 +4579,12 @@ "node": ">=0.8.19" } }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", @@ -4546,7 +4675,6 @@ "version": "1.2.7", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -4811,7 +4939,6 @@ "version": "1.1.15", "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", - "dev": true, "license": "MIT", "dependencies": { "which-typed-array": "^1.1.16" @@ -4873,7 +5000,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", - "dev": true, "license": "MIT" }, "node_modules/isexe": { @@ -4883,6 +5009,46 @@ "dev": true, "license": "ISC" }, + "node_modules/isomorphic-git": { + "version": "1.38.7", + "resolved": "https://registry.npmjs.org/isomorphic-git/-/isomorphic-git-1.38.7.tgz", + "integrity": "sha512-2J2rCV7gTgcpscEDT7PjU5shbsKKI10E4RqBlPDI3LmMXDjSKsN2w1jTIKiay14v87sPDfXjJkgp/Rrxc0zlSg==", + "license": "MIT", + "dependencies": { + "async-lock": "^1.4.1", + "clean-git-ref": "^2.0.1", + "crc-32": "^1.2.0", + "diff3": "0.0.3", + "ignore": "^5.1.4", + "minimisted": "^2.0.0", + "pako": "^1.0.10", + "pify": "^4.0.1", + "readable-stream": "^4.0.0", + "sha.js": "^2.4.12", + "simple-get": "^4.0.1" + }, + "bin": { + "isogit": "cli.cjs" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/isomorphic-git/node_modules/diff3": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/diff3/-/diff3-0.0.3.tgz", + "integrity": "sha512-iSq8ngPOt0K53A6eVr4d5Kn6GNrM2nQZtC740pzIriHtn4pOQ2lyzEXQMBeVcWERN0ye7fhBsk9PbLLQOnUx/g==", + "license": "MIT" + }, + "node_modules/isomorphic-git/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", @@ -5460,12 +5626,23 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" } }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimatch": { "version": "10.2.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", @@ -5486,12 +5663,20 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minimisted": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/minimisted/-/minimisted-2.0.1.tgz", + "integrity": "sha512-1oPjfuLQa2caorJUM8HV8lGgWCc0qqAO1MNv/k05G4qslmsndV/5WdNZrqCiyqiz3wohia2Ij2B7w2Dr7/IyrA==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5" + } + }, "node_modules/module-replacements": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/module-replacements/-/module-replacements-2.11.0.tgz", @@ -5723,6 +5908,15 @@ "node": ">=12.20.0" } }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -5791,6 +5985,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "license": "(MIT AND Zlib)" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -5858,11 +6058,19 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -5936,6 +6144,15 @@ "node": ">=6.0.0" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -5965,6 +6182,22 @@ "dev": true, "license": "MIT" }, + "node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -6141,7 +6374,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -6220,7 +6452,6 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dev": true, "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", @@ -6265,6 +6496,26 @@ "node": ">= 0.4" } }, + "node_modules/sha.js": { + "version": "2.4.12", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", + "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", + "license": "(MIT AND BSD-3-Clause)", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.0" + }, + "bin": { + "sha.js": "bin.js" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -6371,6 +6622,51 @@ "dev": true, "license": "ISC" }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -6409,6 +6705,15 @@ "node": ">= 0.4" } }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, "node_modules/string.prototype.matchall": { "version": "4.0.12", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", @@ -6640,6 +6945,20 @@ "node": ">=14.0.0" } }, + "node_modules/to-buffer": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz", + "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==", + "license": "MIT", + "dependencies": { + "isarray": "^2.0.5", + "safe-buffer": "^5.2.1", + "typed-array-buffer": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/toml-eslint-parser": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/toml-eslint-parser/-/toml-eslint-parser-0.9.3.tgz", @@ -6719,7 +7038,6 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", - "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.3", @@ -7106,7 +7424,6 @@ "version": "1.1.22", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.22.tgz", "integrity": "sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==", - "dev": true, "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", @@ -7151,6 +7468,12 @@ "node": ">=0.10.0" } }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, "node_modules/yaml": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", diff --git a/package.json b/package.json index 55462bb..03c7497 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,8 @@ "vitest": "^4.1.7" }, "dependencies": { + "diff3": "^0.0.4", + "isomorphic-git": "^1.38.6", "js-yaml": "^5.2.1" } } diff --git a/scripts/check-docs.mjs b/scripts/check-docs.mjs index 4208198..b0ba723 100644 --- a/scripts/check-docs.mjs +++ b/scripts/check-docs.mjs @@ -7,10 +7,10 @@ import { readFileSync } from 'node:fs'; const readme = readFileSync('README.md', 'utf8'); const required = [ - 'sync.agentage.io', // the discovery host the plugin contacts + 'sync.agentage.io', // the single host the plugin contacts 'agentage.io/privacy', 'agentage.io/terms', - 'js-yaml', // third-party attribution (MIT) + 'isomorphic-git', // third-party attribution (MIT) 'No calls until you act', // honest disclosure of the no-network-on-load gate ]; // Hosts the shipped build never contacts must not be advertised (README⇄build diff --git a/scripts/check-hosts.mjs b/scripts/check-hosts.mjs index 13e1463..2a58b36 100644 --- a/scripts/check-hosts.mjs +++ b/scripts/check-hosts.mjs @@ -1,6 +1,6 @@ #!/usr/bin/env node // Regression guard: the wrong host `mcp.agentage.io` must never reappear. -// The API/MCP host is `memory.agentage.io`; the discovery host is `sync.agentage.io`. +// The API/MCP host is `memory.agentage.io`; the git sync host is `sync.agentage.io`. import { readFileSync } from 'node:fs'; import { execSync } from 'node:child_process'; diff --git a/src/git/backup-ref.test.ts b/src/git/backup-ref.test.ts new file mode 100644 index 0000000..3ee6a3d --- /dev/null +++ b/src/git/backup-ref.test.ts @@ -0,0 +1,48 @@ +import { describe, it, beforeEach, afterEach, expect } from 'vitest'; +import fs from 'node:fs'; +import os from 'node:os'; +import path from 'node:path'; +import git from 'isomorphic-git'; +import { snapshotBackupRef, restoreBackupRef } from './backup-ref'; + +const author = { name: 't', email: 't@e.io' }; +let dir: string; + +async function commit(file: string, body: string, message: string): Promise { + fs.writeFileSync(path.join(dir, file), body); + await git.add({ fs, dir, filepath: file }); + return git.commit({ fs, dir, message, author }); +} + +beforeEach(async () => { + dir = fs.mkdtempSync(path.join(os.tmpdir(), 'backup-')); + await git.init({ fs, dir, defaultBranch: 'main' }); +}); +afterEach(() => fs.rmSync(dir, { recursive: true, force: true })); + +describe('backup-ref (R16 data-loss guard)', () => { + it('snapshots HEAD at refs/agentage/local-