OXYGENHUB

Milestone 0.1: source control view, diffs over the oxy: scheme, quick diff, decorations, status bar, commit, quick-pick log; process wrapper with a Node test; packaging bed495a96408

Author
Andy McCall @andymccall <[email protected]>
When
· 2026-09-27 15:10 UTC
Signature
✔ signed by Andy McCall
Hash
bed495a96408da78fc1c66f82c024c3d70e163f6a088fd3af3d1d44f5b294256
Depends on
3006b700c514
Depended on by
390a91e2e39c dac6876e4d08 9f29d3164a2a c530a43af73b ca2304a7851a 3701d06abc9b 26410312ce16 6e06e5a13d17 691d366b4c69 813df1a92fbb b55e0d00545e 013fefef11e3 0c26846a9a81 196392053c9f f532c4266f21 0072c2b27293 cdcffe39dd9f 1f7fc1e3ae4a 44bfd5adfc9a b4626290e660 9627171e23e2 f6bda77ecb9f 9020cf612749 d2c9a5843004 25103dddd993 ab81294a9ba9 c8f955891224 5779e5c731a6 35d56b5803d9 810a1c538e78 71d8bf9717d2
In bookmarks
main
In tags
v0.16.0 v0.16.1 v0.17.0 v0.17.1 v0.18.0
Edits
16 atoms, 19 tree operations, 16 files

Browse the tree at this change

added .vscode/launch.json +13 −0
1 +
{
2 +
  "version": "0.2.0",
3 +
  "configurations": [
4 +
    {
5 +
      "name": "Run Extension",
6 +
      "type": "extensionHost",
7 +
      "request": "launch",
8 +
      "args": ["--extensionDevelopmentPath=${workspaceFolder}", "${workspaceFolder}/../hellooxy"],
9 +
      "outFiles": ["${workspaceFolder}/dist/**/*.js"],
10 +
      "preLaunchTask": "npm: build"
11 +
    }
12 +
  ]
13 +
}
added .vscode/tasks.json +19 −0
1 +
{
2 +
  "version": "2.0.0",
3 +
  "tasks": [
4 +
    {
5 +
      "type": "npm",
6 +
      "script": "build",
7 +
      "group": "build",
8 +
      "problemMatcher": ["$esbuild"],
9 +
      "label": "npm: build"
10 +
    },
11 +
    {
12 +
      "type": "npm",
13 +
      "script": "watch",
14 +
      "isBackground": true,
15 +
      "problemMatcher": ["$esbuild-watch"],
16 +
      "label": "npm: watch"
17 +
    }
18 +
  ]
19 +
}
added .vscodeignore +8 −0
1 +
.oxy/**
2 +
.oxyignore
3 +
src/**
4 +
test/**
5 +
out/**
6 +
node_modules/**
7 +
tsconfig.json
8 +
**/*.map
added CHANGELOG.md +9 −0
1 +
# Changelog
2 +
3 +
## 0.1.0 (2026-09-27)
4 +
5 +
Milestone 0.1 of the spec: Source Control view with Changes and Conflicts,
6 +
diff of a file against the working copy's base over the `oxy:` scheme,
7 +
quick-diff gutters, explorer and tab decorations, a status bar item fed by
8 +
`oxy prompt`, commit from the input box, a quick-pick log, and an output
9 +
channel logging every `oxy` invocation.
added LICENSE +30 −0
1 +
Oxygen for Visual Studio Code is licensed under either of
2 +
3 +
  * the MIT License (below), or
4 +
  * the Apache License, Version 2.0 (https://www.apache.org/licenses/LICENSE-2.0)
5 +
6 +
at your option, as is Oxygen itself.
7 +
8 +
---
9 +
10 +
MIT License
11 +
12 +
Copyright (c) 2026 Andy McCall
13 +
14 +
Permission is hereby granted, free of charge, to any person obtaining a copy
15 +
of this software and associated documentation files (the "Software"), to deal
16 +
in the Software without restriction, including without limitation the rights
17 +
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
18 +
copies of the Software, and to permit persons to whom the Software is
19 +
furnished to do so, subject to the following conditions:
20 +
21 +
The above copyright notice and this permission notice shall be included in all
22 +
copies or substantial portions of the Software.
23 +
24 +
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25 +
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26 +
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27 +
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28 +
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
29 +
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30 +
SOFTWARE.
added README.md +35 −0
1 +
# Oxygen for Visual Studio Code
2 +
3 +
Source control integration for Oxygen (`oxy`) repositories: a
4 +
Source Control view with changes and conflicts, diffs against the working
5 +
copy's base, quick-diff gutters, explorer decorations, a status bar item, and
6 +
commit from the input box. The full design, milestone by milestone, is in
7 +
`docs/spec.md` in this repository.
8 +
9 +
## Requirements
10 +
11 +
- Visual Studio Code 1.137 or later.
12 +
- `oxy` 0.1 or later on your PATH, at `~/.local/bin/oxy`, or named in the
13 +
  `oxygen.path` setting. The extension drives `oxy --json` and never reads
14 +
  `.oxy/` itself.
15 +
16 +
## Building
17 +
18 +
```
19 +
npm install
20 +
npm run check          # typecheck, bundle, and test against the oxy binary
21 +
npm run package        # oxygen-vscode-<version>.vsix
22 +
code --install-extension oxygen-vscode-0.1.0.vsix
23 +
```
24 +
25 +
`npm run watch` rebuilds on change; press F5 in VS Code to launch an
26 +
Extension Development Host with the bundle.
27 +
28 +
## Status
29 +
30 +
Milestone 0.1 (read-only plus commit) of the spec. Bookmarks, sync, history,
31 +
blame, conflicts, subrepos, bisect and trust views follow in 0.2 to 0.6.
32 +
33 +
## Licence
34 +
35 +
MIT or Apache-2.0, at your option, as for Oxygen itself.
added assets/images/oxygen.png +0 −0

No line changes.

added package-lock.json +2372 −0
1 +
{
2 +
  "name": "oxygen-vscode",
3 +
  "version": "0.1.0",
4 +
  "lockfileVersion": 3,
5 +
  "requires": true,
6 +
  "packages": {
7 +
    "": {
8 +
      "name": "oxygen-vscode",
9 +
      "version": "0.1.0",
10 +
      "license": "MIT OR Apache-2.0",
11 +
      "devDependencies": {
12 +
        "@types/node": "^24.0.0",
13 +
        "@types/vscode": "^1.137.0",
14 +
        "@vscode/vsce": "^4.0.0",
15 +
        "esbuild": "^0.28.0",
16 +
        "typescript": "^5.9.0"
17 +
      },
18 +
      "engines": {
19 +
        "vscode": "^1.137.0"
20 +
      }
21 +
    },
22 +
    "node_modules/@azure/abort-controller": {
23 +
      "version": "2.2.0",
24 +
      "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.2.0.tgz",
25 +
      "integrity": "sha512-fNAjWnA/nZ2jz31kxR/AqRaUT8ewHBw/WuBIosK0moMy1C9e5ValbDfFdIxJzVOOYaYkV/b2F1S4H/aHiqfVQg==",
26 +
      "dev": true,
27 +
      "license": "MIT",
28 +
      "dependencies": {
29 +
        "tslib": "^2.6.2"
30 +
      },
31 +
      "engines": {
32 +
        "node": ">=22.0.0"
33 +
      }
34 +
    },
35 +
    "node_modules/@azure/core-auth": {
36 +
      "version": "1.11.0",
37 +
      "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.11.0.tgz",
38 +
      "integrity": "sha512-IUZydyTUkDnYdstOW9pFOOUQlBjAepK5teihDE3x6yxsPJs/hsAaaYpeGxdxrgtOiJbBKSjKW7MDk7AEhb4LRg==",
39 +
      "dev": true,
40 +
      "license": "MIT",
41 +
      "dependencies": {
42 +
        "@azure/abort-controller": "^2.1.2",
43 +
        "@azure/core-util": "^1.13.0",
44 +
        "tslib": "^2.6.2"
45 +
      },
46 +
      "engines": {
47 +
        "node": ">=22.0.0"
48 +
      }
49 +
    },
50 +
    "node_modules/@azure/core-client": {
51 +
      "version": "1.11.1",
52 +
      "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.11.1.tgz",
53 +
      "integrity": "sha512-2QygG2F76ZpMP2eMztiJvAiFMu71M9rDeU7vO/QKg5Css7MgM4frUOslFjhVjRhbGaCNPtz/S8M6y46/fFKVuQ==",
54 +
      "dev": true,
55 +
      "license": "MIT",
56 +
      "dependencies": {
57 +
        "@azure/abort-controller": "^2.1.2",
58 +
        "@azure/core-auth": "^1.10.0",
59 +
        "@azure/core-rest-pipeline": "^1.22.0",
60 +
        "@azure/core-tracing": "^1.3.0",
61 +
        "@azure/core-util": "^1.13.0",
62 +
        "@azure/logger": "^1.3.0",
63 +
        "tslib": "^2.6.2"
64 +
      },
65 +
      "engines": {
66 +
        "node": ">=22.0.0"
67 +
      }
68 +
    },
69 +
    "node_modules/@azure/core-process": {
70 +
      "version": "1.0.0",
71 +
      "resolved": "https://registry.npmjs.org/@azure/core-process/-/core-process-1.0.0.tgz",
72 +
      "integrity": "sha512-/shnJ+ooO8WPxDhPEeI/2oRQuubn16gZ6CvlbpWbEswZfzwI9tI/sMAHmF3x1LuQ9yZYXfLW3TjzGMLEC5blKg==",
73 +
      "dev": true,
74 +
      "license": "MIT",
75 +
      "engines": {
76 +
        "node": ">=22.0.0"
77 +
      }
78 +
    },
79 +
    "node_modules/@azure/core-rest-pipeline": {
80 +
      "version": "1.25.0",
81 +
      "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.25.0.tgz",
82 +
      "integrity": "sha512-bMs8ekJLjX8wPV+9IPBges1SLPyuDtE9g5gLDWOpxzKcoOFQnpLGkbcT1tdw3FaAmDS1gnPmMmJ6y/T5B96kIA==",
83 +
      "dev": true,
84 +
      "license": "MIT",
85 +
      "dependencies": {
86 +
        "@azure/abort-controller": "^2.1.2",
87 +
        "@azure/core-auth": "^1.10.0",
88 +
        "@azure/core-tracing": "^1.3.0",
89 +
        "@azure/core-util": "^1.13.0",
90 +
        "@azure/logger": "^1.3.0",
91 +
        "@typespec/ts-http-runtime": "^0.3.4",
92 +
        "tslib": "^2.6.2"
93 +
      },
94 +
      "engines": {
95 +
        "node": ">=22.0.0"
96 +
      }
97 +
    },
98 +
    "node_modules/@azure/core-tracing": {
99 +
      "version": "1.4.0",
100 +
      "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.4.0.tgz",
101 +
      "integrity": "sha512-eGwxD0AtncrxeBM4tG8R55Pc3rdX1hNW2WibJAgYpCVA6E93mvvVH+LcssoVjOBrSKWS55yEIHsk0X8ctHmfOQ==",
102 +
      "dev": true,
103 +
      "license": "MIT",
104 +
      "dependencies": {
105 +
        "tslib": "^2.6.2"
106 +
      },
107 +
      "engines": {
108 +
        "node": ">=22.0.0"
109 +
      }
110 +
    },
111 +
    "node_modules/@azure/core-util": {
112 +
      "version": "1.14.0",
113 +
      "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.14.0.tgz",
114 +
      "integrity": "sha512-9n2pWK61veAuN0V20t9lOuoV4CFMdyAZ1ygZzvBGk/pBBJRib/PjL9PLXa/aI2CcPpyHfqVsxxqLCYl6uZlfDw==",
115 +
      "dev": true,
116 +
      "license": "MIT",
117 +
      "dependencies": {
118 +
        "@azure/abort-controller": "^2.1.2",
119 +
        "@typespec/ts-http-runtime": "^0.3.0",
120 +
        "tslib": "^2.6.2"
121 +
      },
122 +
      "engines": {
123 +
        "node": ">=22.0.0"
124 +
      }
125 +
    },
126 +
    "node_modules/@azure/identity": {
127 +
      "version": "4.13.3",
128 +
      "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.13.3.tgz",
129 +
      "integrity": "sha512-zGQPtqvXPgSA8yfV2CkIQ1qirqk0p9AIVpC5uEkdXQYcKl07QHvyaGYRnZOk0AsQUmxNb4wfkcwY5di8Z5xa9A==",
130 +
      "dev": true,
131 +
      "license": "MIT",
132 +
      "dependencies": {
133 +
        "@azure/abort-controller": "^2.0.0",
134 +
        "@azure/core-auth": "^1.9.0",
135 +
        "@azure/core-client": "^1.9.2",
136 +
        "@azure/core-process": "^1.0.0",
137 +
        "@azure/core-rest-pipeline": "^1.17.0",
138 +
        "@azure/core-tracing": "^1.0.0",
139 +
        "@azure/core-util": "^1.11.0",
140 +
        "@azure/logger": "^1.0.0",
141 +
        "@azure/msal-browser": "^5.5.0",
142 +
        "@azure/msal-node": "^6.0.0",
143 +
        "open": "^10.1.0",
144 +
        "tslib": "^2.2.0"
145 +
      },
146 +
      "engines": {
147 +
        "node": ">=22.0.0"
148 +
      }
149 +
    },
150 +
    "node_modules/@azure/logger": {
151 +
      "version": "1.4.0",
152 +
      "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.4.0.tgz",
153 +
      "integrity": "sha512-rbAE25KUfjU/s3XHUdJgceoCP5dEOpMx85J04kF+QMdta73XkuG9JGHHinch+XIoKpBdqljin+KqURpJriSzLA==",
154 +
      "dev": true,
155 +
      "license": "MIT",
156 +
      "dependencies": {
157 +
        "@typespec/ts-http-runtime": "^0.3.0",
158 +
        "tslib": "^2.6.2"
159 +
      },
160 +
      "engines": {
161 +
        "node": ">=22.0.0"
162 +
      }
163 +
    },
164 +
    "node_modules/@azure/msal-browser": {
165 +
      "version": "5.23.0",
166 +
      "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-5.23.0.tgz",
167 +
      "integrity": "sha512-IRCwkRCK47hBXK+7bu67UZWY7HS0Jx1dZgi4C1HVbjlBum8Jy8fb2l87xFr6HxdQJWy951zRUHtwwDITHFAtaA==",
168 +
      "dev": true,
169 +
      "license": "MIT",
170 +
      "dependencies": {
171 +
        "@azure/msal-common": "16.14.1"
172 +
      },
173 +
      "engines": {
174 +
        "node": ">=0.8.0"
175 +
      }
176 +
    },
177 +
    "node_modules/@azure/msal-common": {
178 +
      "version": "16.14.1",
179 +
      "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-16.14.1.tgz",
180 +
      "integrity": "sha512-Or6xhPNyi4zHW25158yxBoyxuCqNSPa5YBVqfF1J5Ks4MJWBo/USXdp05DQIPu1Zli00YZu6t0+h6KvHJealxQ==",
181 +
      "dev": true,
182 +
      "license": "MIT",
183 +
      "engines": {
184 +
        "node": ">=0.8.0"
185 +
      }
186 +
    },
187 +
    "node_modules/@azure/msal-node": {
188 +
      "version": "6.0.1",
189 +
      "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-6.0.1.tgz",
190 +
      "integrity": "sha512-ixSO1Y/kCVRthRs+hSx/5qkwaunX1/RAePhlMN0wIpIQ4WEZ6AREGGnGd1AP0qspHVsAwzWQKGubpjh50JyJIQ==",
191 +
      "dev": true,
192 +
      "license": "MIT",
193 +
      "dependencies": {
194 +
        "@azure/msal-common": "16.14.1",
195 +
        "jsonwebtoken": "^9.0.0"
196 +
      },
197 +
      "engines": {
198 +
        "node": ">=20"
199 +
      }
200 +
    },
201 +
    "node_modules/@esbuild/aix-ppc64": {
202 +
      "version": "0.28.2",
203 +
      "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz",
204 +
      "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==",
205 +
      "cpu": [
206 +
        "ppc64"
207 +
      ],
208 +
      "dev": true,
209 +
      "license": "MIT",
210 +
      "optional": true,
211 +
      "os": [
212 +
        "aix"
213 +
      ],
214 +
      "engines": {
215 +
        "node": ">=18"
216 +
      }
217 +
    },
218 +
    "node_modules/@esbuild/android-arm": {
219 +
      "version": "0.28.2",
220 +
      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz",
221 +
      "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==",
222 +
      "cpu": [
223 +
        "arm"
224 +
      ],
225 +
      "dev": true,
226 +
      "license": "MIT",
227 +
      "optional": true,
228 +
      "os": [
229 +
        "android"
230 +
      ],
231 +
      "engines": {
232 +
        "node": ">=18"
233 +
      }
234 +
    },
235 +
    "node_modules/@esbuild/android-arm64": {
236 +
      "version": "0.28.2",
237 +
      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz",
238 +
      "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==",
239 +
      "cpu": [
240 +
        "arm64"
241 +
      ],
242 +
      "dev": true,
243 +
      "license": "MIT",
244 +
      "optional": true,
245 +
      "os": [
246 +
        "android"
247 +
      ],
248 +
      "engines": {
249 +
        "node": ">=18"
250 +
      }
251 +
    },
252 +
    "node_modules/@esbuild/android-x64": {
253 +
      "version": "0.28.2",
254 +
      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz",
255 +
      "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==",
256 +
      "cpu": [
257 +
        "x64"
258 +
      ],
259 +
      "dev": true,
260 +
      "license": "MIT",
261 +
      "optional": true,
262 +
      "os": [
263 +
        "android"
264 +
      ],
265 +
      "engines": {
266 +
        "node": ">=18"
267 +
      }
268 +
    },
269 +
    "node_modules/@esbuild/darwin-arm64": {
270 +
      "version": "0.28.2",
271 +
      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz",
272 +
      "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==",
273 +
      "cpu": [
274 +
        "arm64"
275 +
      ],
276 +
      "dev": true,
277 +
      "license": "MIT",
278 +
      "optional": true,
279 +
      "os": [
280 +
        "darwin"
281 +
      ],
282 +
      "engines": {
283 +
        "node": ">=18"
284 +
      }
285 +
    },
286 +
    "node_modules/@esbuild/darwin-x64": {
287 +
      "version": "0.28.2",
288 +
      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz",
289 +
      "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==",
290 +
      "cpu": [
291 +
        "x64"
292 +
      ],
293 +
      "dev": true,
294 +
      "license": "MIT",
295 +
      "optional": true,
296 +
      "os": [
297 +
        "darwin"
298 +
      ],
299 +
      "engines": {
300 +
        "node": ">=18"
301 +
      }
302 +
    },
303 +
    "node_modules/@esbuild/freebsd-arm64": {
304 +
      "version": "0.28.2",
305 +
      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz",
306 +
      "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==",
307 +
      "cpu": [
308 +
        "arm64"
309 +
      ],
310 +
      "dev": true,
311 +
      "license": "MIT",
312 +
      "optional": true,
313 +
      "os": [
314 +
        "freebsd"
315 +
      ],
316 +
      "engines": {
317 +
        "node": ">=18"
318 +
      }
319 +
    },
320 +
    "node_modules/@esbuild/freebsd-x64": {
321 +
      "version": "0.28.2",
322 +
      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz",
323 +
      "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==",
324 +
      "cpu": [
325 +
        "x64"
326 +
      ],
327 +
      "dev": true,
328 +
      "license": "MIT",
329 +
      "optional": true,
330 +
      "os": [
331 +
        "freebsd"
332 +
      ],
333 +
      "engines": {
334 +
        "node": ">=18"
335 +
      }
336 +
    },
337 +
    "node_modules/@esbuild/linux-arm": {
338 +
      "version": "0.28.2",
339 +
      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz",
340 +
      "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==",
341 +
      "cpu": [
342 +
        "arm"
343 +
      ],
344 +
      "dev": true,
345 +
      "license": "MIT",
346 +
      "optional": true,
347 +
      "os": [
348 +
        "linux"
349 +
      ],
350 +
      "engines": {
351 +
        "node": ">=18"
352 +
      }
353 +
    },
354 +
    "node_modules/@esbuild/linux-arm64": {
355 +
      "version": "0.28.2",
356 +
      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz",
357 +
      "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==",
358 +
      "cpu": [
359 +
        "arm64"
360 +
      ],
361 +
      "dev": true,
362 +
      "license": "MIT",
363 +
      "optional": true,
364 +
      "os": [
365 +
        "linux"
366 +
      ],
367 +
      "engines": {
368 +
        "node": ">=18"
369 +
      }
370 +
    },
371 +
    "node_modules/@esbuild/linux-ia32": {
372 +
      "version": "0.28.2",
373 +
      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz",
374 +
      "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==",
375 +
      "cpu": [
376 +
        "ia32"
377 +
      ],
378 +
      "dev": true,
379 +
      "license": "MIT",
380 +
      "optional": true,
381 +
      "os": [
382 +
        "linux"
383 +
      ],
384 +
      "engines": {
385 +
        "node": ">=18"
386 +
      }
387 +
    },
388 +
    "node_modules/@esbuild/linux-loong64": {
389 +
      "version": "0.28.2",
390 +
      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz",
391 +
      "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==",
392 +
      "cpu": [
393 +
        "loong64"
394 +
      ],
395 +
      "dev": true,
396 +
      "license": "MIT",
397 +
      "optional": true,
398 +
      "os": [
399 +
        "linux"
400 +
      ],
401 +
      "engines": {
402 +
        "node": ">=18"
403 +
      }
404 +
    },
405 +
    "node_modules/@esbuild/linux-mips64el": {
406 +
      "version": "0.28.2",
407 +
      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz",
408 +
      "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==",
409 +
      "cpu": [
410 +
        "mips64el"
411 +
      ],
412 +
      "dev": true,
413 +
      "license": "MIT",
414 +
      "optional": true,
415 +
      "os": [
416 +
        "linux"
417 +
      ],
418 +
      "engines": {
419 +
        "node": ">=18"
420 +
      }
421 +
    },
422 +
    "node_modules/@esbuild/linux-ppc64": {
423 +
      "version": "0.28.2",
424 +
      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz",
425 +
      "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==",
426 +
      "cpu": [
427 +
        "ppc64"
428 +
      ],
429 +
      "dev": true,
430 +
      "license": "MIT",
431 +
      "optional": true,
432 +
      "os": [
433 +
        "linux"
434 +
      ],
435 +
      "engines": {
436 +
        "node": ">=18"
437 +
      }
438 +
    },
439 +
    "node_modules/@esbuild/linux-riscv64": {
440 +
      "version": "0.28.2",
441 +
      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz",
442 +
      "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==",
443 +
      "cpu": [
444 +
        "riscv64"
445 +
      ],
446 +
      "dev": true,
447 +
      "license": "MIT",
448 +
      "optional": true,
449 +
      "os": [
450 +
        "linux"
451 +
      ],
452 +
      "engines": {
453 +
        "node": ">=18"
454 +
      }
455 +
    },
456 +
    "node_modules/@esbuild/linux-s390x": {
457 +
      "version": "0.28.2",
458 +
      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz",
459 +
      "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==",
460 +
      "cpu": [
461 +
        "s390x"
462 +
      ],
463 +
      "dev": true,
464 +
      "license": "MIT",
465 +
      "optional": true,
466 +
      "os": [
467 +
        "linux"
468 +
      ],
469 +
      "engines": {
470 +
        "node": ">=18"
471 +
      }
472 +
    },
473 +
    "node_modules/@esbuild/linux-x64": {
474 +
      "version": "0.28.2",
475 +
      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz",
476 +
      "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==",
477 +
      "cpu": [
478 +
        "x64"
479 +
      ],
480 +
      "dev": true,
481 +
      "license": "MIT",
482 +
      "optional": true,
483 +
      "os": [
484 +
        "linux"
485 +
      ],
486 +
      "engines": {
487 +
        "node": ">=18"
488 +
      }
489 +
    },
490 +
    "node_modules/@esbuild/netbsd-arm64": {
491 +
      "version": "0.28.2",
492 +
      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz",
493 +
      "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==",
494 +
      "cpu": [
495 +
        "arm64"
496 +
      ],
497 +
      "dev": true,
498 +
      "license": "MIT",
499 +
      "optional": true,
500 +
      "os": [
501 +
        "netbsd"
502 +
      ],
503 +
      "engines": {
504 +
        "node": ">=18"
505 +
      }
506 +
    },
507 +
    "node_modules/@esbuild/netbsd-x64": {
508 +
      "version": "0.28.2",
509 +
      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz",
510 +
      "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==",
511 +
      "cpu": [
512 +
        "x64"
513 +
      ],
514 +
      "dev": true,
515 +
      "license": "MIT",
516 +
      "optional": true,
517 +
      "os": [
518 +
        "netbsd"
519 +
      ],
520 +
      "engines": {
521 +
        "node": ">=18"
522 +
      }
523 +
    },
524 +
    "node_modules/@esbuild/openbsd-arm64": {
525 +
      "version": "0.28.2",
526 +
      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz",
527 +
      "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==",
528 +
      "cpu": [
529 +
        "arm64"
530 +
      ],
531 +
      "dev": true,
532 +
      "license": "MIT",
533 +
      "optional": true,
534 +
      "os": [
535 +
        "openbsd"
536 +
      ],
537 +
      "engines": {
538 +
        "node": ">=18"
539 +
      }
540 +
    },
541 +
    "node_modules/@esbuild/openbsd-x64": {
542 +
      "version": "0.28.2",
543 +
      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz",
544 +
      "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==",
545 +
      "cpu": [
546 +
        "x64"
547 +
      ],
548 +
      "dev": true,
549 +
      "license": "MIT",
550 +
      "optional": true,
551 +
      "os": [
552 +
        "openbsd"
553 +
      ],
554 +
      "engines": {
555 +
        "node": ">=18"
556 +
      }
557 +
    },
558 +
    "node_modules/@esbuild/openharmony-arm64": {
559 +
      "version": "0.28.2",
560 +
      "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz",
561 +
      "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==",
562 +
      "cpu": [
563 +
        "arm64"
564 +
      ],
565 +
      "dev": true,
566 +
      "license": "MIT",
567 +
      "optional": true,
568 +
      "os": [
569 +
        "openharmony"
570 +
      ],
571 +
      "engines": {
572 +
        "node": ">=18"
573 +
      }
574 +
    },
575 +
    "node_modules/@esbuild/sunos-x64": {
576 +
      "version": "0.28.2",
577 +
      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz",
578 +
      "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==",
579 +
      "cpu": [
580 +
        "x64"
581 +
      ],
582 +
      "dev": true,
583 +
      "license": "MIT",
584 +
      "optional": true,
585 +
      "os": [
586 +
        "sunos"
587 +
      ],
588 +
      "engines": {
589 +
        "node": ">=18"
590 +
      }
591 +
    },
592 +
    "node_modules/@esbuild/win32-arm64": {
593 +
      "version": "0.28.2",
594 +
      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz",
595 +
      "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==",
596 +
      "cpu": [
597 +
        "arm64"
598 +
      ],
599 +
      "dev": true,
600 +
      "license": "MIT",
601 +
      "optional": true,
602 +
      "os": [
603 +
        "win32"
604 +
      ],
605 +
      "engines": {
606 +
        "node": ">=18"
607 +
      }
608 +
    },
609 +
    "node_modules/@esbuild/win32-ia32": {
610 +
      "version": "0.28.2",
611 +
      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz",
612 +
      "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==",
613 +
      "cpu": [
614 +
        "ia32"
615 +
      ],
616 +
      "dev": true,
617 +
      "license": "MIT",
618 +
      "optional": true,
619 +
      "os": [
620 +
        "win32"
621 +
      ],
622 +
      "engines": {
623 +
        "node": ">=18"
624 +
      }
625 +
    },
626 +
    "node_modules/@esbuild/win32-x64": {
627 +
      "version": "0.28.2",
628 +
      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz",
629 +
      "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==",
630 +
      "cpu": [
631 +
        "x64"
632 +
      ],
633 +
      "dev": true,
634 +
      "license": "MIT",
635 +
      "optional": true,
636 +
      "os": [
637 +
        "win32"
638 +
      ],
639 +
      "engines": {
640 +
        "node": ">=18"
641 +
      }
642 +
    },
643 +
    "node_modules/@napi-rs/keyring": {
644 +
      "version": "1.3.0",
645 +
      "resolved": "https://registry.npmjs.org/@napi-rs/keyring/-/keyring-1.3.0.tgz",
646 +
      "integrity": "sha512-WrOw/bcXm0f9qHkumlT1QlArXSTWqaY9sunsDpOk+yCCorCKMxvWT/a3xko4EYHVdeZoh00yI2TydXn6eyICDA==",
647 +
      "dev": true,
648 +
      "license": "MIT",
649 +
      "engines": {
650 +
        "node": ">= 10"
651 +
      },
652 +
      "funding": {
653 +
        "type": "github",
654 +
        "url": "https://github.com/sponsors/Brooooooklyn"
655 +
      },
656 +
      "optionalDependencies": {
657 +
        "@napi-rs/keyring-darwin-arm64": "1.3.0",
658 +
        "@napi-rs/keyring-darwin-x64": "1.3.0",
659 +
        "@napi-rs/keyring-freebsd-x64": "1.3.0",
660 +
        "@napi-rs/keyring-linux-arm-gnueabihf": "1.3.0",
661 +
        "@napi-rs/keyring-linux-arm64-gnu": "1.3.0",
662 +
        "@napi-rs/keyring-linux-arm64-musl": "1.3.0",
663 +
        "@napi-rs/keyring-linux-riscv64-gnu": "1.3.0",
664 +
        "@napi-rs/keyring-linux-x64-gnu": "1.3.0",
665 +
        "@napi-rs/keyring-linux-x64-musl": "1.3.0",
666 +
        "@napi-rs/keyring-win32-arm64-msvc": "1.3.0",
667 +
        "@napi-rs/keyring-win32-ia32-msvc": "1.3.0",
668 +
        "@napi-rs/keyring-win32-x64-msvc": "1.3.0"
669 +
      }
670 +
    },
671 +
    "node_modules/@napi-rs/keyring-darwin-arm64": {
672 +
      "version": "1.3.0",
673 +
      "resolved": "https://registry.npmjs.org/@napi-rs/keyring-darwin-arm64/-/keyring-darwin-arm64-1.3.0.tgz",
674 +
      "integrity": "sha512-pl76hJvdYUBn6I24bXiOBMA9nbDapo3I5B+f3OorjDU4dUMSypXeKbOVehJe8fhgTiH24flMyTS3aAIy43xegQ==",
675 +
      "cpu": [
676 +
        "arm64"
677 +
      ],
678 +
      "dev": true,
679 +
      "license": "MIT",
680 +
      "optional": true,
681 +
      "os": [
682 +
        "darwin"
683 +
      ],
684 +
      "engines": {
685 +
        "node": ">= 10"
686 +
      }
687 +
    },
688 +
    "node_modules/@napi-rs/keyring-darwin-x64": {
689 +
      "version": "1.3.0",
690 +
      "resolved": "https://registry.npmjs.org/@napi-rs/keyring-darwin-x64/-/keyring-darwin-x64-1.3.0.tgz",
691 +
      "integrity": "sha512-YcJtEV5LA3cvA4z3BurgxH5IhTsW1JfIvcAAcqcecwk06Si9F9NqkxbZVIfDwQ8oRHgaBmT3zZJnLAotCrVahw==",
692 +
      "cpu": [
693 +
        "x64"
694 +
      ],
695 +
      "dev": true,
696 +
      "license": "MIT",
697 +
      "optional": true,
698 +
      "os": [
699 +
        "darwin"
700 +
      ],
701 +
      "engines": {
702 +
        "node": ">= 10"
703 +
      }
704 +
    },
705 +
    "node_modules/@napi-rs/keyring-freebsd-x64": {
706 +
      "version": "1.3.0",
707 +
      "resolved": "https://registry.npmjs.org/@napi-rs/keyring-freebsd-x64/-/keyring-freebsd-x64-1.3.0.tgz",
708 +
      "integrity": "sha512-vlLf31TGhfRAaxLDBhg8b89ss0HHD/lyNmL5F3UjSaz5CUXElsJmKYq9fqA/B+cZKUEUcLHHGhF0I/CqcFdaVw==",
709 +
      "cpu": [
710 +
        "x64"
711 +
      ],
712 +
      "dev": true,
713 +
      "license": "MIT",
714 +
      "optional": true,
715 +
      "os": [
716 +
        "freebsd"
717 +
      ],
718 +
      "engines": {
719 +
        "node": ">= 10"
720 +
      }
721 +
    },
722 +
    "node_modules/@napi-rs/keyring-linux-arm-gnueabihf": {
723 +
      "version": "1.3.0",
724 +
      "resolved": "https://registry.npmjs.org/@napi-rs/keyring-linux-arm-gnueabihf/-/keyring-linux-arm-gnueabihf-1.3.0.tgz",
725 +
      "integrity": "sha512-KiWdMMu/Inz/bHHIAGrnF7r54FZDYXuHO6UFF/rhIrshUsxbMG1Rl9lEymNtqqsVo927G0VYcb02FzWQ3iBQRQ==",
726 +
      "cpu": [
727 +
        "arm"
728 +
      ],
729 +
      "dev": true,
730 +
      "license": "MIT",
731 +
      "optional": true,
732 +
      "os": [
733 +
        "linux"
734 +
      ],
735 +
      "engines": {
736 +
        "node": ">= 10"
737 +
      }
738 +
    },
739 +
    "node_modules/@napi-rs/keyring-linux-arm64-gnu": {
740 +
      "version": "1.3.0",
741 +
      "resolved": "https://registry.npmjs.org/@napi-rs/keyring-linux-arm64-gnu/-/keyring-linux-arm64-gnu-1.3.0.tgz",
742 +
      "integrity": "sha512-eyKGpY40lm9Jvs1aD294XRH4y7+TlJM0YVAryZeXA6TX0mb4gMkxVXwSQv7MCwgah7raeUd0dKUb4BPAYIgcMg==",
743 +
      "cpu": [
744 +
        "arm64"
745 +
      ],
746 +
      "dev": true,
747 +
      "libc": [
748 +
        "glibc"
749 +
      ],
750 +
      "license": "MIT",
751 +
      "optional": true,
752 +
      "os": [
753 +
        "linux"
754 +
      ],
755 +
      "engines": {
756 +
        "node": ">= 10"
757 +
      }
758 +
    },
759 +
    "node_modules/@napi-rs/keyring-linux-arm64-musl": {
760 +
      "version": "1.3.0",
761 +
      "resolved": "https://registry.npmjs.org/@napi-rs/keyring-linux-arm64-musl/-/keyring-linux-arm64-musl-1.3.0.tgz",
762 +
      "integrity": "sha512-iIK6JWHXAJqDrEyLY3TmswwloVyt2vj+04TZnew+uSJ9gnDO8EwRbp3/iw3LpWaXiDO7VomGO6y8I0Id8uBZSw==",
763 +
      "cpu": [
764 +
        "arm64"
765 +
      ],
766 +
      "dev": true,
767 +
      "libc": [
768 +
        "musl"
769 +
      ],
770 +
      "license": "MIT",
771 +
      "optional": true,
772 +
      "os": [
773 +
        "linux"
774 +
      ],
775 +
      "engines": {
776 +
        "node": ">= 10"
777 +
      }
778 +
    },
779 +
    "node_modules/@napi-rs/keyring-linux-riscv64-gnu": {
780 +
      "version": "1.3.0",
781 +
      "resolved": "https://registry.npmjs.org/@napi-rs/keyring-linux-riscv64-gnu/-/keyring-linux-riscv64-gnu-1.3.0.tgz",
782 +
      "integrity": "sha512-/PGqrwn6EwgtK6vccASSXJRfOSP4vN1F4ASsIQ+7MdrK6hNvAJ1FZPrIuD5gGGdxezo3F++To2Wq7DbuGIeuNQ==",
783 +
      "cpu": [
784 +
        "riscv64"
785 +
      ],
786 +
      "dev": true,
787 +
      "libc": [
788 +
        "glibc"
789 +
      ],
790 +
      "license": "MIT",
791 +
      "optional": true,
792 +
      "os": [
793 +
        "linux"
794 +
      ],
795 +
      "engines": {
796 +
        "node": ">= 10"
797 +
      }
798 +
    },
799 +
    "node_modules/@napi-rs/keyring-linux-x64-gnu": {
800 +
      "version": "1.3.0",
801 +
      "resolved": "https://registry.npmjs.org/@napi-rs/keyring-linux-x64-gnu/-/keyring-linux-x64-gnu-1.3.0.tgz",
802 +
      "integrity": "sha512-2PDK1WKWTu9lBGq9VvNEkSlQD3O7YwVpmnyN2M3cy4v7NJ/8gDMd9GXv3G+FVXN13uhp4gnnPBS+ScefmEeD2A==",
803 +
      "cpu": [
804 +
        "x64"
805 +
      ],
806 +
      "dev": true,
807 +
      "libc": [
808 +
        "glibc"
809 +
      ],
810 +
      "license": "MIT",
811 +
      "optional": true,
812 +
      "os": [
813 +
        "linux"
814 +
      ],
815 +
      "engines": {
816 +
        "node": ">= 10"
817 +
      }
818 +
    },
819 +
    "node_modules/@napi-rs/keyring-linux-x64-musl": {
820 +
      "version": "1.3.0",
821 +
      "resolved": "https://registry.npmjs.org/@napi-rs/keyring-linux-x64-musl/-/keyring-linux-x64-musl-1.3.0.tgz",
822 +
      "integrity": "sha512-oJ2HkX8YUo46QBkn0pG+HuIKQNqr523q6vBobCn+P95s4C4K6/kLBqHY/1bg5J4ap31DzsznhnFKcfBNBsjCnw==",
823 +
      "cpu": [
824 +
        "x64"
825 +
      ],
826 +
      "dev": true,
827 +
      "libc": [
828 +
        "musl"
829 +
      ],
830 +
      "license": "MIT",
831 +
      "optional": true,
832 +
      "os": [
833 +
        "linux"
834 +
      ],
835 +
      "engines": {
836 +
        "node": ">= 10"
837 +
      }
838 +
    },
839 +
    "node_modules/@napi-rs/keyring-win32-arm64-msvc": {
840 +
      "version": "1.3.0",
841 +
      "resolved": "https://registry.npmjs.org/@napi-rs/keyring-win32-arm64-msvc/-/keyring-win32-arm64-msvc-1.3.0.tgz",
842 +
      "integrity": "sha512-tOd3c/uAaeoE4ycVlmAdSvygz0Zt3zdca6Y7gokBeIbaRDWpjDIUOpU3MvML59XAaqyuKGsVVu0F/DZb1lHPmw==",
843 +
      "cpu": [
844 +
        "arm64"
845 +
      ],
846 +
      "dev": true,
847 +
      "license": "MIT",
848 +
      "optional": true,
849 +
      "os": [
850 +
        "win32"
851 +
      ],
852 +
      "engines": {
853 +
        "node": ">= 10"
854 +
      }
855 +
    },
856 +
    "node_modules/@napi-rs/keyring-win32-ia32-msvc": {
857 +
      "version": "1.3.0",
858 +
      "resolved": "https://registry.npmjs.org/@napi-rs/keyring-win32-ia32-msvc/-/keyring-win32-ia32-msvc-1.3.0.tgz",
859 +
      "integrity": "sha512-sPSqeAFZMGqP1R++M2JTza7GQJJ/TpCo6JU6Vcd4jnebvOaEDs9b7eipakU1PJdSvhpC2yXMCNRk9gXfrhuwHQ==",
860 +
      "cpu": [
861 +
        "ia32"
862 +
      ],
863 +
      "dev": true,
864 +
      "license": "MIT",
865 +
      "optional": true,
866 +
      "os": [
867 +
        "win32"
868 +
      ],
869 +
      "engines": {
870 +
        "node": ">= 10"
871 +
      }
872 +
    },
873 +
    "node_modules/@napi-rs/keyring-win32-x64-msvc": {
874 +
      "version": "1.3.0",
875 +
      "resolved": "https://registry.npmjs.org/@napi-rs/keyring-win32-x64-msvc/-/keyring-win32-x64-msvc-1.3.0.tgz",
876 +
      "integrity": "sha512-4DnCWXwDc0HRKwyRlG5y0VhKZW2tNRQfKKfyj6IX/KWfDNyq9hn4n+GL1auyDcOO/v8PwnhmYo2+rOOqCkvvOg==",
877 +
      "cpu": [
878 +
        "x64"
879 +
      ],
880 +
      "dev": true,
881 +
      "license": "MIT",
882 +
      "optional": true,
883 +
      "os": [
884 +
        "win32"
885 +
      ],
886 +
      "engines": {
887 +
        "node": ">= 10"
888 +
      }
889 +
    },
890 +
    "node_modules/@secretlint/core": {
891 +
      "version": "10.2.2",
892 +
      "resolved": "https://registry.npmjs.org/@secretlint/core/-/core-10.2.2.tgz",
893 +
      "integrity": "sha512-6rdwBwLP9+TO3rRjMVW1tX+lQeo5gBbxl1I5F8nh8bgGtKwdlCMhMKsBWzWg1ostxx/tIG7OjZI0/BxsP8bUgw==",
894 +
      "dev": true,
895 +
      "license": "MIT",
896 +
      "dependencies": {
897 +
        "@secretlint/profiler": "^10.2.2",
898 +
        "@secretlint/types": "^10.2.2",
899 +
        "debug": "^4.4.1",
900 +
        "structured-source": "^4.0.0"
901 +
      },
902 +
      "engines": {
903 +
        "node": ">=20.0.0"
904 +
      }
905 +
    },
906 +
    "node_modules/@secretlint/profiler": {
907 +
      "version": "10.2.2",
908 +
      "resolved": "https://registry.npmjs.org/@secretlint/profiler/-/profiler-10.2.2.tgz",
909 +
      "integrity": "sha512-qm9rWfkh/o8OvzMIfY8a5bCmgIniSpltbVlUVl983zDG1bUuQNd1/5lUEeWx5o/WJ99bXxS7yNI4/KIXfHexig==",
910 +
      "dev": true,
911 +
      "license": "MIT"
912 +
    },
913 +
    "node_modules/@secretlint/secretlint-rule-no-dotenv": {
914 +
      "version": "10.2.2",
915 +
      "resolved": "https://registry.npmjs.org/@secretlint/secretlint-rule-no-dotenv/-/secretlint-rule-no-dotenv-10.2.2.tgz",
916 +
      "integrity": "sha512-KJRbIShA9DVc5Va3yArtJ6QDzGjg3PRa1uYp9As4RsyKtKSSZjI64jVca57FZ8gbuk4em0/0Jq+uy6485wxIdg==",
917 +
      "dev": true,
918 +
      "license": "MIT",
919 +
      "dependencies": {
920 +
        "@secretlint/types": "^10.2.2"
921 +
      },
922 +
      "engines": {
923 +
        "node": ">=20.0.0"
924 +
      }
925 +
    },
926 +
    "node_modules/@secretlint/secretlint-rule-preset-recommend": {
927 +
      "version": "10.2.2",
928 +
      "resolved": "https://registry.npmjs.org/@secretlint/secretlint-rule-preset-recommend/-/secretlint-rule-preset-recommend-10.2.2.tgz",
929 +
      "integrity": "sha512-K3jPqjva8bQndDKJqctnGfwuAxU2n9XNCPtbXVI5JvC7FnQiNg/yWlQPbMUlBXtBoBGFYp08A94m6fvtc9v+zA==",
930 +
      "dev": true,
931 +
      "license": "MIT",
932 +
      "engines": {
933 +
        "node": ">=20.0.0"
934 +
      }
935 +
    },
936 +
    "node_modules/@secretlint/source-creator": {
937 +
      "version": "10.2.2",
938 +
      "resolved": "https://registry.npmjs.org/@secretlint/source-creator/-/source-creator-10.2.2.tgz",
939 +
      "integrity": "sha512-h6I87xJfwfUTgQ7irWq7UTdq/Bm1RuQ/fYhA3dtTIAop5BwSFmZyrchph4WcoEvbN460BWKmk4RYSvPElIIvxw==",
940 +
      "dev": true,
941 +
      "license": "MIT",
942 +
      "dependencies": {
943 +
        "@secretlint/types": "^10.2.2",
944 +
        "istextorbinary": "^9.5.0"
945 +
      },
946 +
      "engines": {
947 +
        "node": ">=20.0.0"
948 +
      }
949 +
    },
950 +
    "node_modules/@secretlint/types": {
951 +
      "version": "10.2.2",
952 +
      "resolved": "https://registry.npmjs.org/@secretlint/types/-/types-10.2.2.tgz",
953 +
      "integrity": "sha512-Nqc90v4lWCXyakD6xNyNACBJNJ0tNCwj2WNk/7ivyacYHxiITVgmLUFXTBOeCdy79iz6HtN9Y31uw/jbLrdOAg==",
954 +
      "dev": true,
955 +
      "license": "MIT",
956 +
      "engines": {
957 +
        "node": ">=20.0.0"
958 +
      }
959 +
    },
960 +
    "node_modules/@types/node": {
961 +
      "version": "24.19.0",
962 +
      "resolved": "https://registry.npmjs.org/@types/node/-/node-24.19.0.tgz",
963 +
      "integrity": "sha512-zY+5tKxXdhGh1PYI0ac+7juvEu4OI6vWtVVoj5i2m42jxAY1U+zHGt6QCyOFwykdP62sM3MJ9stoYYUw5aCWew==",
964 +
      "dev": true,
965 +
      "license": "MIT",
966 +
      "dependencies": {
967 +
        "undici-types": ">=7.24.0 <7.24.7"
968 +
      }
969 +
    },
970 +
    "node_modules/@types/vscode": {
971 +
      "version": "1.138.0",
972 +
      "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.138.0.tgz",
973 +
      "integrity": "sha512-DhlvrucJa8n6EHst7qQcXOcjLKs/VRTQy55plLO+RN74jonYsr/t9dd66zBnAyz1tpDig0okQAxuoPHpVNje2g==",
974 +
      "dev": true,
975 +
      "license": "MIT"
976 +
    },
977 +
    "node_modules/@typespec/ts-http-runtime": {
978 +
      "version": "0.3.9",
979 +
      "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.9.tgz",
980 +
      "integrity": "sha512-edSdeAqkdxBVzA1yL1LrLCml1YjyCVvPMtMqJpbF+6K609tHe8V6sQUzFQSGcYNhcuhOceZtjvN32+mpIth30A==",
981 +
      "dev": true,
982 +
      "license": "MIT",
983 +
      "dependencies": {
984 +
        "http-proxy-agent": "^7.0.0",
985 +
        "https-proxy-agent": "^7.0.0",
986 +
        "tslib": "^2.6.2"
987 +
      },
988 +
      "engines": {
989 +
        "node": ">=22.0.0"
990 +
      }
991 +
    },
992 +
    "node_modules/@vscode/vsce": {
993 +
      "version": "4.0.0",
994 +
      "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-4.0.0.tgz",
995 +
      "integrity": "sha512-NImwuLaenMmb5D5Jer9/lzi/F9ZQUBOp8Azhj/BVYcTFgixv8KehFXqEUDjQlD2tAiw2E6dDGyjTuAB//di60A==",
996 +
      "dev": true,
997 +
      "license": "MIT",
998 +
      "dependencies": {
999 +
        "@azure/identity": "^4.13.2",
1000 +
        "@napi-rs/keyring": "^1.3.0",
1001 +
        "@secretlint/core": "^10.2.2",
1002 +
        "@secretlint/secretlint-rule-no-dotenv": "^10.2.2",
1003 +
        "@secretlint/secretlint-rule-preset-recommend": "^10.2.2",
1004 +
        "@secretlint/source-creator": "^10.2.2",
1005 +
        "@secretlint/types": "^10.2.2",
1006 +
        "@vscode/vsce-sign": "^2.1.0",
1007 +
        "azure-devops-node-api": "^12.5.0",
1008 +
        "cockatiel": "^3.2.1",
1009 +
        "commander": "^12.1.0",
1010 +
        "hosted-git-info": "^4.1.0",
1011 +
        "jsonc-parser": "^3.3.1",
1012 +
        "marked": "^18.0.11",
1013 +
        "mime": "^1.6.0",
1014 +
        "minimatch": "^10.2.6",
1015 +
        "parse5": "^8.0.1",
1016 +
        "proper-lockfile": "^4.1.2",
1017 +
        "read": "^1.0.7",
1018 +
        "semver": "^7.8.5",
1019 +
        "tinyglobby": "^0.2.17",
1020 +
        "typed-rest-client": "^1.8.11",
1021 +
        "url-join": "^4.0.1",
1022 +
        "xml2js": "^0.5.0",
1023 +
        "yauzl": "^3.4.0",
1024 +
        "yazl": "^2.5.1"
1025 +
      },
1026 +
      "bin": {
1027 +
        "vsce": "vsce"
1028 +
      },
1029 +
      "engines": {
1030 +
        "node": ">= 22"
1031 +
      }
1032 +
    },
1033 +
    "node_modules/@vscode/vsce-sign": {
1034 +
      "version": "2.1.0",
1035 +
      "resolved": "https://registry.npmjs.org/@vscode/vsce-sign/-/vsce-sign-2.1.0.tgz",
1036 +
      "integrity": "sha512-9AQrqazrBgTgRSuwleLVXUrIUphY02/SFCh2TKYoLV/xifJAdblhdmEmw5gUrYSPQ3sRwNs9iyCMD14sATEE6g==",
1037 +
      "dev": true,
1038 +
      "hasInstallScript": true,
1039 +
      "license": "SEE LICENSE IN LICENSE.txt",
1040 +
      "optionalDependencies": {
1041 +
        "@vscode/vsce-sign-alpine-arm64": "2.0.6",
1042 +
        "@vscode/vsce-sign-alpine-x64": "2.0.6",
1043 +
        "@vscode/vsce-sign-darwin-arm64": "2.0.6",
1044 +
        "@vscode/vsce-sign-darwin-x64": "2.0.6",
1045 +
        "@vscode/vsce-sign-linux-arm": "2.0.6",
1046 +
        "@vscode/vsce-sign-linux-arm64": "2.0.6",
1047 +
        "@vscode/vsce-sign-linux-x64": "2.0.6",
1048 +
        "@vscode/vsce-sign-win32-arm64": "2.0.6",
1049 +
        "@vscode/vsce-sign-win32-x64": "2.0.6"
1050 +
      }
1051 +
    },
1052 +
    "node_modules/@vscode/vsce-sign-alpine-arm64": {
1053 +
      "version": "2.0.6",
1054 +
      "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-alpine-arm64/-/vsce-sign-alpine-arm64-2.0.6.tgz",
1055 +
      "integrity": "sha512-wKkJBsvKF+f0GfsUuGT0tSW0kZL87QggEiqNqK6/8hvqsXvpx8OsTEc3mnE1kejkh5r+qUyQ7PtF8jZYN0mo8Q==",
1056 +
      "cpu": [
1057 +
        "arm64"
1058 +
      ],
1059 +
      "dev": true,
1060 +
      "license": "SEE LICENSE IN LICENSE.txt",
1061 +
      "optional": true,
1062 +
      "os": [
1063 +
        "alpine"
1064 +
      ]
1065 +
    },
1066 +
    "node_modules/@vscode/vsce-sign-alpine-x64": {
1067 +
      "version": "2.0.6",
1068 +
      "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-alpine-x64/-/vsce-sign-alpine-x64-2.0.6.tgz",
1069 +
      "integrity": "sha512-YoAGlmdK39vKi9jA18i4ufBbd95OqGJxRvF3n6ZbCyziwy3O+JgOpIUPxv5tjeO6gQfx29qBivQ8ZZTUF2Ba0w==",
1070 +
      "cpu": [
1071 +
        "x64"
1072 +
      ],
1073 +
      "dev": true,
1074 +
      "license": "SEE LICENSE IN LICENSE.txt",
1075 +
      "optional": true,
1076 +
      "os": [
1077 +
        "alpine"
1078 +
      ]
1079 +
    },
1080 +
    "node_modules/@vscode/vsce-sign-darwin-arm64": {
1081 +
      "version": "2.0.6",
1082 +
      "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-darwin-arm64/-/vsce-sign-darwin-arm64-2.0.6.tgz",
1083 +
      "integrity": "sha512-5HMHaJRIQuozm/XQIiJiA0W9uhdblwwl2ZNDSSAeXGO9YhB9MH5C4KIHOmvyjUnKy4UCuiP43VKpIxW1VWP4tQ==",
1084 +
      "cpu": [
1085 +
        "arm64"
1086 +
      ],
1087 +
      "dev": true,
1088 +
      "license": "SEE LICENSE IN LICENSE.txt",
1089 +
      "optional": true,
1090 +
      "os": [
1091 +
        "darwin"
1092 +
      ]
1093 +
    },
1094 +
    "node_modules/@vscode/vsce-sign-darwin-x64": {
1095 +
      "version": "2.0.6",
1096 +
      "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-darwin-x64/-/vsce-sign-darwin-x64-2.0.6.tgz",
1097 +
      "integrity": "sha512-25GsUbTAiNfHSuRItoQafXOIpxlYj+IXb4/qarrXu7kmbH94jlm5sdWSCKrrREs8+GsXF1b+l3OB7VJy5jsykw==",
1098 +
      "cpu": [
1099 +
        "x64"
1100 +
      ],
1101 +
      "dev": true,
1102 +
      "license": "SEE LICENSE IN LICENSE.txt",
1103 +
      "optional": true,
1104 +
      "os": [
1105 +
        "darwin"
1106 +
      ]
1107 +
    },
1108 +
    "node_modules/@vscode/vsce-sign-linux-arm": {
1109 +
      "version": "2.0.6",
1110 +
      "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-arm/-/vsce-sign-linux-arm-2.0.6.tgz",
1111 +
      "integrity": "sha512-UndEc2Xlq4HsuMPnwu7420uqceXjs4yb5W8E2/UkaHBB9OWCwMd3/bRe/1eLe3D8kPpxzcaeTyXiK3RdzS/1CA==",
1112 +
      "cpu": [
1113 +
        "arm"
1114 +
      ],
1115 +
      "dev": true,
1116 +
      "license": "SEE LICENSE IN LICENSE.txt",
1117 +
      "optional": true,
1118 +
      "os": [
1119 +
        "linux"
1120 +
      ]
1121 +
    },
1122 +
    "node_modules/@vscode/vsce-sign-linux-arm64": {
1123 +
      "version": "2.0.6",
1124 +
      "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-arm64/-/vsce-sign-linux-arm64-2.0.6.tgz",
1125 +
      "integrity": "sha512-cfb1qK7lygtMa4NUl2582nP7aliLYuDEVpAbXJMkDq1qE+olIw/es+C8j1LJwvcRq1I2yWGtSn3EkDp9Dq5FdA==",
1126 +
      "cpu": [
1127 +
        "arm64"
1128 +
      ],
1129 +
      "dev": true,
1130 +
      "license": "SEE LICENSE IN LICENSE.txt",
1131 +
      "optional": true,
1132 +
      "os": [
1133 +
        "linux"
1134 +
      ]
1135 +
    },
1136 +
    "node_modules/@vscode/vsce-sign-linux-x64": {
1137 +
      "version": "2.0.6",
1138 +
      "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-x64/-/vsce-sign-linux-x64-2.0.6.tgz",
1139 +
      "integrity": "sha512-/olerl1A4sOqdP+hjvJ1sbQjKN07Y3DVnxO4gnbn/ahtQvFrdhUi0G1VsZXDNjfqmXw57DmPi5ASnj/8PGZhAA==",
1140 +
      "cpu": [
1141 +
        "x64"
1142 +
      ],
1143 +
      "dev": true,
1144 +
      "license": "SEE LICENSE IN LICENSE.txt",
1145 +
      "optional": true,
1146 +
      "os": [
1147 +
        "linux"
1148 +
      ]
1149 +
    },
1150 +
    "node_modules/@vscode/vsce-sign-win32-arm64": {
1151 +
      "version": "2.0.6",
1152 +
      "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-win32-arm64/-/vsce-sign-win32-arm64-2.0.6.tgz",
1153 +
      "integrity": "sha512-ivM/MiGIY0PJNZBoGtlRBM/xDpwbdlCWomUWuLmIxbi1Cxe/1nooYrEQoaHD8ojVRgzdQEUzMsRbyF5cJJgYOg==",
1154 +
      "cpu": [
1155 +
        "arm64"
1156 +
      ],
1157 +
      "dev": true,
1158 +
      "license": "SEE LICENSE IN LICENSE.txt",
1159 +
      "optional": true,
1160 +
      "os": [
1161 +
        "win32"
1162 +
      ]
1163 +
    },
1164 +
    "node_modules/@vscode/vsce-sign-win32-x64": {
1165 +
      "version": "2.0.6",
1166 +
      "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-win32-x64/-/vsce-sign-win32-x64-2.0.6.tgz",
1167 +
      "integrity": "sha512-mgth9Kvze+u8CruYMmhHw6Zgy3GRX2S+Ed5oSokDEK5vPEwGGKnmuXua9tmFhomeAnhgJnL4DCna3TiNuGrBTQ==",
1168 +
      "cpu": [
1169 +
        "x64"
1170 +
      ],
1171 +
      "dev": true,
1172 +
      "license": "SEE LICENSE IN LICENSE.txt",
1173 +
      "optional": true,
1174 +
      "os": [
1175 +
        "win32"
1176 +
      ]
1177 +
    },
1178 +
    "node_modules/agent-base": {
1179 +
      "version": "7.1.4",
1180 +
      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
1181 +
      "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
1182 +
      "dev": true,
1183 +
      "license": "MIT",
1184 +
      "engines": {
1185 +
        "node": ">= 14"
1186 +
      }
1187 +
    },
1188 +
    "node_modules/azure-devops-node-api": {
1189 +
      "version": "12.5.0",
1190 +
      "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-12.5.0.tgz",
1191 +
      "integrity": "sha512-R5eFskGvOm3U/GzeAuxRkUsAl0hrAwGgWn6zAd2KrZmrEhWZVqLew4OOupbQlXUuojUzpGtq62SmdhJ06N88og==",
1192 +
      "dev": true,
1193 +
      "license": "MIT",
1194 +
      "dependencies": {
1195 +
        "tunnel": "0.0.6",
1196 +
        "typed-rest-client": "^1.8.4"
1197 +
      }
1198 +
    },
1199 +
    "node_modules/balanced-match": {
1200 +
      "version": "4.0.4",
1201 +
      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
1202 +
      "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
1203 +
      "dev": true,
1204 +
      "license": "MIT",
1205 +
      "engines": {
1206 +
        "node": "18 || 20 || >=22"
1207 +
      }
1208 +
    },
1209 +
    "node_modules/binaryextensions": {
1210 +
      "version": "6.11.0",
1211 +
      "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-6.11.0.tgz",
1212 +
      "integrity": "sha512-sXnYK/Ij80TO3lcqZVV2YgfKN5QjUWIRk/XSm2J/4bd/lPko3lvk0O4ZppH6m+6hB2/GTu+ptNwVFe1xh+QLQw==",
1213 +
      "dev": true,
1214 +
      "license": "Artistic-2.0",
1215 +
      "dependencies": {
1216 +
        "editions": "^6.21.0"
1217 +
      },
1218 +
      "engines": {
1219 +
        "node": ">=4"
1220 +
      },
1221 +
      "funding": {
1222 +
        "url": "https://bevry.me/fund"
1223 +
      }
1224 +
    },
1225 +
    "node_modules/boundary": {
1226 +
      "version": "2.0.0",
1227 +
      "resolved": "https://registry.npmjs.org/boundary/-/boundary-2.0.0.tgz",
1228 +
      "integrity": "sha512-rJKn5ooC9u8q13IMCrW0RSp31pxBCHE3y9V/tp3TdWSLf8Em3p6Di4NBpfzbJge9YjjFEsD0RtFEjtvHL5VyEA==",
1229 +
      "dev": true,
1230 +
      "license": "BSD-2-Clause"
1231 +
    },
1232 +
    "node_modules/brace-expansion": {
1233 +
      "version": "5.0.12",
1234 +
      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz",
1235 +
      "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==",
1236 +
      "dev": true,
1237 +
      "license": "MIT",
1238 +
      "dependencies": {
1239 +
        "balanced-match": "^4.0.2"
1240 +
      },
1241 +
      "engines": {
1242 +
        "node": "20 || >=22"
1243 +
      }
1244 +
    },
1245 +
    "node_modules/buffer-crc32": {
1246 +
      "version": "0.2.13",
1247 +
      "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
1248 +
      "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
1249 +
      "dev": true,
1250 +
      "license": "MIT",
1251 +
      "engines": {
1252 +
        "node": "*"
1253 +
      }
1254 +
    },
1255 +
    "node_modules/buffer-equal-constant-time": {
1256 +
      "version": "1.0.1",
1257 +
      "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
1258 +
      "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
1259 +
      "dev": true,
1260 +
      "license": "BSD-3-Clause"
1261 +
    },
1262 +
    "node_modules/bundle-name": {
1263 +
      "version": "4.1.1",
1264 +
      "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.1.tgz",
1265 +
      "integrity": "sha512-DdH81/zPLVS11EUgWq3tEu/xn+EzljlMYooDNdzWEnFha3R3NBMpMV1UqYIpjYHV/SgpFKMIX1Oh7o06SjM/oA==",
1266 +
      "dev": true,
1267 +
      "license": "MIT",
1268 +
      "dependencies": {
1269 +
        "run-applescript": "^7.0.0"
1270 +
      },
1271 +
      "engines": {
1272 +
        "node": ">=18"
1273 +
      },
1274 +
      "funding": {
1275 +
        "url": "https://github.com/sponsors/sindresorhus"
1276 +
      }
1277 +
    },
1278 +
    "node_modules/call-bind-apply-helpers": {
1279 +
      "version": "1.0.2",
1280 +
      "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
1281 +
      "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
1282 +
      "dev": true,
1283 +
      "license": "MIT",
1284 +
      "dependencies": {
1285 +
        "es-errors": "^1.3.0",
1286 +
        "function-bind": "^1.1.2"
1287 +
      },
1288 +
      "engines": {
1289 +
        "node": ">= 0.4"
1290 +
      }
1291 +
    },
1292 +
    "node_modules/call-bound": {
1293 +
      "version": "1.0.4",
1294 +
      "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
1295 +
      "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
1296 +
      "dev": true,
1297 +
      "license": "MIT",
1298 +
      "dependencies": {
1299 +
        "call-bind-apply-helpers": "^1.0.2",
1300 +
        "get-intrinsic": "^1.3.0"
1301 +
      },
1302 +
      "engines": {
1303 +
        "node": ">= 0.4"
1304 +
      },
1305 +
      "funding": {
1306 +
        "url": "https://github.com/sponsors/ljharb"
1307 +
      }
1308 +
    },
1309 +
    "node_modules/cockatiel": {
1310 +
      "version": "3.2.1",
1311 +
      "resolved": "https://registry.npmjs.org/cockatiel/-/cockatiel-3.2.1.tgz",
1312 +
      "integrity": "sha512-gfrHV6ZPkquExvMh9IOkKsBzNDk6sDuZ6DdBGUBkvFnTCqCxzpuq48RySgP0AnaqQkw2zynOFj9yly6T1Q2G5Q==",
1313 +
      "dev": true,
1314 +
      "license": "MIT",
1315 +
      "engines": {
1316 +
        "node": ">=16"
1317 +
      }
1318 +
    },
1319 +
    "node_modules/commander": {
1320 +
      "version": "12.1.0",
1321 +
      "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
1322 +
      "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
1323 +
      "dev": true,
1324 +
      "license": "MIT",
1325 +
      "engines": {
1326 +
        "node": ">=18"
1327 +
      }
1328 +
    },
1329 +
    "node_modules/debug": {
1330 +
      "version": "4.4.3",
1331 +
      "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
1332 +
      "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
1333 +
      "dev": true,
1334 +
      "license": "MIT",
1335 +
      "dependencies": {
1336 +
        "ms": "^2.1.3"
1337 +
      },
1338 +
      "engines": {
1339 +
        "node": ">=6.0"
1340 +
      },
1341 +
      "peerDependenciesMeta": {
1342 +
        "supports-color": {
1343 +
          "optional": true
1344 +
        }
1345 +
      }
1346 +
    },
1347 +
    "node_modules/default-browser": {
1348 +
      "version": "5.5.1",
1349 +
      "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.1.tgz",
1350 +
      "integrity": "sha512-m1pAzaJgZ/gssEqlOhJkPJp8Xly7QyW6xcrkUa2KKcDeDSEMP7X8xipU3snUcfisTQx0w1AGae+9UtJSfVnXGw==",
1351 +
      "dev": true,
1352 +
      "license": "MIT",
1353 +
      "dependencies": {
1354 +
        "bundle-name": "^4.1.0",
1355 +
        "default-browser-id": "^5.0.0"
1356 +
      },
1357 +
      "engines": {
1358 +
        "node": ">=18"
1359 +
      },
1360 +
      "funding": {
1361 +
        "url": "https://github.com/sponsors/sindresorhus"
1362 +
      }
1363 +
    },
1364 +
    "node_modules/default-browser-id": {
1365 +
      "version": "5.0.1",
1366 +
      "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz",
1367 +
      "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==",
1368 +
      "dev": true,
1369 +
      "license": "MIT",
1370 +
      "engines": {
1371 +
        "node": ">=18"
1372 +
      },
1373 +
      "funding": {
1374 +
        "url": "https://github.com/sponsors/sindresorhus"
1375 +
      }
1376 +
    },
1377 +
    "node_modules/define-lazy-prop": {
1378 +
      "version": "3.0.0",
1379 +
      "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz",
1380 +
      "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==",
1381 +
      "dev": true,
1382 +
      "license": "MIT",
1383 +
      "engines": {
1384 +
        "node": ">=12"
1385 +
      },
1386 +
      "funding": {
1387 +
        "url": "https://github.com/sponsors/sindresorhus"
1388 +
      }
1389 +
    },
1390 +
    "node_modules/dunder-proto": {
1391 +
      "version": "1.0.1",
1392 +
      "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
1393 +
      "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
1394 +
      "dev": true,
1395 +
      "license": "MIT",
1396 +
      "dependencies": {
1397 +
        "call-bind-apply-helpers": "^1.0.1",
1398 +
        "es-errors": "^1.3.0",
1399 +
        "gopd": "^1.2.0"
1400 +
      },
1401 +
      "engines": {
1402 +
        "node": ">= 0.4"
1403 +
      }
1404 +
    },
1405 +
    "node_modules/ecdsa-sig-formatter": {
1406 +
      "version": "1.0.11",
1407 +
      "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
1408 +
      "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
1409 +
      "dev": true,
1410 +
      "license": "Apache-2.0",
1411 +
      "dependencies": {
1412 +
        "safe-buffer": "^5.0.1"
1413 +
      }
1414 +
    },
1415 +
    "node_modules/editions": {
1416 +
      "version": "6.22.0",
1417 +
      "resolved": "https://registry.npmjs.org/editions/-/editions-6.22.0.tgz",
1418 +
      "integrity": "sha512-UgGlf8IW75je7HZjNDpJdCv4cGJWIi6yumFdZ0R7A8/CIhQiWUjyGLCxdHpd8bmyD1gnkfUNK0oeOXqUS2cpfQ==",
1419 +
      "dev": true,
1420 +
      "license": "Artistic-2.0",
1421 +
      "dependencies": {
1422 +
        "version-range": "^4.15.0"
1423 +
      },
1424 +
      "engines": {
1425 +
        "ecmascript": ">= es5",
1426 +
        "node": ">=4"
1427 +
      },
1428 +
      "funding": {
1429 +
        "url": "https://bevry.me/fund"
1430 +
      }
1431 +
    },
1432 +
    "node_modules/entities": {
1433 +
      "version": "8.1.0",
1434 +
      "resolved": "https://registry.npmjs.org/entities/-/entities-8.1.0.tgz",
1435 +
      "integrity": "sha512-kxL7msIffSuh9aaFAMD7rxAIuTRMAHMeBtgHW2yUdWw732ZNh4MehkF2gdjvtdmikkaIP9bFDDJOPlsvm7avrA==",
1436 +
      "dev": true,
1437 +
      "license": "BSD-2-Clause",
1438 +
      "engines": {
1439 +
        "node": ">=20.19.0"
1440 +
      },
1441 +
      "funding": {
1442 +
        "url": "https://github.com/fb55/entities?sponsor=1"
1443 +
      }
1444 +
    },
1445 +
    "node_modules/es-define-property": {
1446 +
      "version": "1.0.1",
1447 +
      "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
1448 +
      "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
1449 +
      "dev": true,
1450 +
      "license": "MIT",
1451 +
      "engines": {
1452 +
        "node": ">= 0.4"
1453 +
      }
1454 +
    },
1455 +
    "node_modules/es-errors": {
1456 +
      "version": "1.3.0",
1457 +
      "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
1458 +
      "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
1459 +
      "dev": true,
1460 +
      "license": "MIT",
1461 +
      "engines": {
1462 +
        "node": ">= 0.4"
1463 +
      }
1464 +
    },
1465 +
    "node_modules/es-object-atoms": {
1466 +
      "version": "1.1.2",
1467 +
      "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
1468 +
      "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
1469 +
      "dev": true,
1470 +
      "license": "MIT",
1471 +
      "dependencies": {
1472 +
        "es-errors": "^1.3.0"
1473 +
      },
1474 +
      "engines": {
1475 +
        "node": ">= 0.4"
1476 +
      }
1477 +
    },
1478 +
    "node_modules/esbuild": {
1479 +
      "version": "0.28.2",
1480 +
      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz",
1481 +
      "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==",
1482 +
      "dev": true,
1483 +
      "hasInstallScript": true,
1484 +
      "license": "MIT",
1485 +
      "bin": {
1486 +
        "esbuild": "bin/esbuild"
1487 +
      },
1488 +
      "engines": {
1489 +
        "node": ">=18"
1490 +
      },
1491 +
      "optionalDependencies": {
1492 +
        "@esbuild/aix-ppc64": "0.28.2",
1493 +
        "@esbuild/android-arm": "0.28.2",
1494 +
        "@esbuild/android-arm64": "0.28.2",
1495 +
        "@esbuild/android-x64": "0.28.2",
1496 +
        "@esbuild/darwin-arm64": "0.28.2",
1497 +
        "@esbuild/darwin-x64": "0.28.2",
1498 +
        "@esbuild/freebsd-arm64": "0.28.2",
1499 +
        "@esbuild/freebsd-x64": "0.28.2",
1500 +
        "@esbuild/linux-arm": "0.28.2",
1501 +
        "@esbuild/linux-arm64": "0.28.2",
1502 +
        "@esbuild/linux-ia32": "0.28.2",
1503 +
        "@esbuild/linux-loong64": "0.28.2",
1504 +
        "@esbuild/linux-mips64el": "0.28.2",
1505 +
        "@esbuild/linux-ppc64": "0.28.2",
1506 +
        "@esbuild/linux-riscv64": "0.28.2",
1507 +
        "@esbuild/linux-s390x": "0.28.2",
1508 +
        "@esbuild/linux-x64": "0.28.2",
1509 +
        "@esbuild/netbsd-arm64": "0.28.2",
1510 +
        "@esbuild/netbsd-x64": "0.28.2",
1511 +
        "@esbuild/openbsd-arm64": "0.28.2",
1512 +
        "@esbuild/openbsd-x64": "0.28.2",
1513 +
        "@esbuild/openharmony-arm64": "0.28.2",
1514 +
        "@esbuild/sunos-x64": "0.28.2",
1515 +
        "@esbuild/win32-arm64": "0.28.2",
1516 +
        "@esbuild/win32-ia32": "0.28.2",
1517 +
        "@esbuild/win32-x64": "0.28.2"
1518 +
      }
1519 +
    },
1520 +
    "node_modules/fdir": {
1521 +
      "version": "6.5.0",
1522 +
      "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
1523 +
      "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
1524 +
      "dev": true,
1525 +
      "license": "MIT",
1526 +
      "engines": {
1527 +
        "node": ">=12.0.0"
1528 +
      },
1529 +
      "peerDependencies": {
1530 +
        "picomatch": "^3 || ^4"
1531 +
      },
1532 +
      "peerDependenciesMeta": {
1533 +
        "picomatch": {
1534 +
          "optional": true
1535 +
        }
1536 +
      }
1537 +
    },
1538 +
    "node_modules/function-bind": {
1539 +
      "version": "1.1.2",
1540 +
      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
1541 +
      "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
1542 +
      "dev": true,
1543 +
      "license": "MIT",
1544 +
      "funding": {
1545 +
        "url": "https://github.com/sponsors/ljharb"
1546 +
      }
1547 +
    },
1548 +
    "node_modules/get-intrinsic": {
1549 +
      "version": "1.3.0",
1550 +
      "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
1551 +
      "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
1552 +
      "dev": true,
1553 +
      "license": "MIT",
1554 +
      "dependencies": {
1555 +
        "call-bind-apply-helpers": "^1.0.2",
1556 +
        "es-define-property": "^1.0.1",
1557 +
        "es-errors": "^1.3.0",
1558 +
        "es-object-atoms": "^1.1.1",
1559 +
        "function-bind": "^1.1.2",
1560 +
        "get-proto": "^1.0.1",
1561 +
        "gopd": "^1.2.0",
1562 +
        "has-symbols": "^1.1.0",
1563 +
        "hasown": "^2.0.2",
1564 +
        "math-intrinsics": "^1.1.0"
1565 +
      },
1566 +
      "engines": {
1567 +
        "node": ">= 0.4"
1568 +
      },
1569 +
      "funding": {
1570 +
        "url": "https://github.com/sponsors/ljharb"
1571 +
      }
1572 +
    },
1573 +
    "node_modules/get-proto": {
1574 +
      "version": "1.0.1",
1575 +
      "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
1576 +
      "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
1577 +
      "dev": true,
1578 +
      "license": "MIT",
1579 +
      "dependencies": {
1580 +
        "dunder-proto": "^1.0.1",
1581 +
        "es-object-atoms": "^1.0.0"
1582 +
      },
1583 +
      "engines": {
1584 +
        "node": ">= 0.4"
1585 +
      }
1586 +
    },
1587 +
    "node_modules/gopd": {
1588 +
      "version": "1.2.0",
1589 +
      "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
1590 +
      "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
1591 +
      "dev": true,
1592 +
      "license": "MIT",
1593 +
      "engines": {
1594 +
        "node": ">= 0.4"
1595 +
      },
1596 +
      "funding": {
1597 +
        "url": "https://github.com/sponsors/ljharb"
1598 +
      }
1599 +
    },
1600 +
    "node_modules/graceful-fs": {
1601 +
      "version": "4.2.11",
1602 +
      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
1603 +
      "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
1604 +
      "dev": true,
1605 +
      "license": "ISC"
1606 +
    },
1607 +
    "node_modules/has-symbols": {
1608 +
      "version": "1.1.0",
1609 +
      "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
1610 +
      "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
1611 +
      "dev": true,
1612 +
      "license": "MIT",
1613 +
      "engines": {
1614 +
        "node": ">= 0.4"
1615 +
      },
1616 +
      "funding": {
1617 +
        "url": "https://github.com/sponsors/ljharb"
1618 +
      }
1619 +
    },
1620 +
    "node_modules/hasown": {
1621 +
      "version": "2.0.4",
1622 +
      "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
1623 +
      "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
1624 +
      "dev": true,
1625 +
      "license": "MIT",
1626 +
      "dependencies": {
1627 +
        "function-bind": "^1.1.2"
1628 +
      },
1629 +
      "engines": {
1630 +
        "node": ">= 0.4"
1631 +
      }
1632 +
    },
1633 +
    "node_modules/hosted-git-info": {
1634 +
      "version": "4.1.0",
1635 +
      "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz",
1636 +
      "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==",
1637 +
      "dev": true,
1638 +
      "license": "ISC",
1639 +
      "dependencies": {
1640 +
        "lru-cache": "^6.0.0"
1641 +
      },
1642 +
      "engines": {
1643 +
        "node": ">=10"
1644 +
      }
1645 +
    },
1646 +
    "node_modules/http-proxy-agent": {
1647 +
      "version": "7.0.2",
1648 +
      "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
1649 +
      "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
1650 +
      "dev": true,
1651 +
      "license": "MIT",
1652 +
      "dependencies": {
1653 +
        "agent-base": "^7.1.0",
1654 +
        "debug": "^4.3.4"
1655 +
      },
1656 +
      "engines": {
1657 +
        "node": ">= 14"
1658 +
      }
1659 +
    },
1660 +
    "node_modules/https-proxy-agent": {
1661 +
      "version": "7.0.6",
1662 +
      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
1663 +
      "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
1664 +
      "dev": true,
1665 +
      "license": "MIT",
1666 +
      "dependencies": {
1667 +
        "agent-base": "^7.1.2",
1668 +
        "debug": "4"
1669 +
      },
1670 +
      "engines": {
1671 +
        "node": ">= 14"
1672 +
      }
1673 +
    },
1674 +
    "node_modules/is-docker": {
1675 +
      "version": "3.0.0",
1676 +
      "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz",
1677 +
      "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==",
1678 +
      "dev": true,
1679 +
      "license": "MIT",
1680 +
      "bin": {
1681 +
        "is-docker": "cli.js"
1682 +
      },
1683 +
      "engines": {
1684 +
        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
1685 +
      },
1686 +
      "funding": {
1687 +
        "url": "https://github.com/sponsors/sindresorhus"
1688 +
      }
1689 +
    },
1690 +
    "node_modules/is-inside-container": {
1691 +
      "version": "1.0.0",
1692 +
      "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz",
1693 +
      "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==",
1694 +
      "dev": true,
1695 +
      "license": "MIT",
1696 +
      "dependencies": {
1697 +
        "is-docker": "^3.0.0"
1698 +
      },
1699 +
      "bin": {
1700 +
        "is-inside-container": "cli.js"
1701 +
      },
1702 +
      "engines": {
1703 +
        "node": ">=14.16"
1704 +
      },
1705 +
      "funding": {
1706 +
        "url": "https://github.com/sponsors/sindresorhus"
1707 +
      }
1708 +
    },
1709 +
    "node_modules/is-wsl": {
1710 +
      "version": "3.1.1",
1711 +
      "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz",
1712 +
      "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==",
1713 +
      "dev": true,
1714 +
      "license": "MIT",
1715 +
      "dependencies": {
1716 +
        "is-inside-container": "^1.0.0"
1717 +
      },
1718 +
      "engines": {
1719 +
        "node": ">=16"
1720 +
      },
1721 +
      "funding": {
1722 +
        "url": "https://github.com/sponsors/sindresorhus"
1723 +
      }
1724 +
    },
1725 +
    "node_modules/istextorbinary": {
1726 +
      "version": "9.5.0",
1727 +
      "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-9.5.0.tgz",
1728 +
      "integrity": "sha512-5mbUj3SiZXCuRf9fT3ibzbSSEWiy63gFfksmGfdOzujPjW3k+z8WvIBxcJHBoQNlaZaiyB25deviif2+osLmLw==",
1729 +
      "dev": true,
1730 +
      "license": "Artistic-2.0",
1731 +
      "dependencies": {
1732 +
        "binaryextensions": "^6.11.0",
1733 +
        "editions": "^6.21.0",
1734 +
        "textextensions": "^6.11.0"
1735 +
      },
1736 +
      "engines": {
1737 +
        "node": ">=4"
1738 +
      },
1739 +
      "funding": {
1740 +
        "url": "https://bevry.me/fund"
1741 +
      }
1742 +
    },
1743 +
    "node_modules/jsonc-parser": {
1744 +
      "version": "3.3.1",
1745 +
      "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz",
1746 +
      "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==",
1747 +
      "dev": true,
1748 +
      "license": "MIT"
1749 +
    },
1750 +
    "node_modules/jsonwebtoken": {
1751 +
      "version": "9.0.3",
1752 +
      "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz",
1753 +
      "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==",
1754 +
      "dev": true,
1755 +
      "license": "MIT",
1756 +
      "dependencies": {
1757 +
        "jws": "^4.0.1",
1758 +
        "lodash.includes": "^4.3.0",
1759 +
        "lodash.isboolean": "^3.0.3",
1760 +
        "lodash.isinteger": "^4.0.4",
1761 +
        "lodash.isnumber": "^3.0.3",
1762 +
        "lodash.isplainobject": "^4.0.6",
1763 +
        "lodash.isstring": "^4.0.1",
1764 +
        "lodash.once": "^4.0.0",
1765 +
        "ms": "^2.1.1",
1766 +
        "semver": "^7.5.4"
1767 +
      },
1768 +
      "engines": {
1769 +
        "node": ">=12",
1770 +
        "npm": ">=6"
1771 +
      }
1772 +
    },
1773 +
    "node_modules/jwa": {
1774 +
      "version": "2.0.1",
1775 +
      "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
1776 +
      "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
1777 +
      "dev": true,
1778 +
      "license": "MIT",
1779 +
      "dependencies": {
1780 +
        "buffer-equal-constant-time": "^1.0.1",
1781 +
        "ecdsa-sig-formatter": "1.0.11",
1782 +
        "safe-buffer": "^5.0.1"
1783 +
      }
1784 +
    },
1785 +
    "node_modules/jws": {
1786 +
      "version": "4.0.1",
1787 +
      "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz",
1788 +
      "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==",
1789 +
      "dev": true,
1790 +
      "license": "MIT",
1791 +
      "dependencies": {
1792 +
        "jwa": "^2.0.1",
1793 +
        "safe-buffer": "^5.0.1"
1794 +
      }
1795 +
    },
1796 +
    "node_modules/lodash.includes": {
1797 +
      "version": "4.3.0",
1798 +
      "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
1799 +
      "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",
1800 +
      "dev": true,
1801 +
      "license": "MIT"
1802 +
    },
1803 +
    "node_modules/lodash.isboolean": {
1804 +
      "version": "3.0.3",
1805 +
      "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
1806 +
      "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
1807 +
      "dev": true,
1808 +
      "license": "MIT"
1809 +
    },
1810 +
    "node_modules/lodash.isinteger": {
1811 +
      "version": "4.0.4",
1812 +
      "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
1813 +
      "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
1814 +
      "dev": true,
1815 +
      "license": "MIT"
1816 +
    },
1817 +
    "node_modules/lodash.isnumber": {
1818 +
      "version": "3.0.3",
1819 +
      "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
1820 +
      "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==",
1821 +
      "dev": true,
1822 +
      "license": "MIT"
1823 +
    },
1824 +
    "node_modules/lodash.isplainobject": {
1825 +
      "version": "4.0.6",
1826 +
      "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
1827 +
      "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
1828 +
      "dev": true,
1829 +
      "license": "MIT"
1830 +
    },
1831 +
    "node_modules/lodash.isstring": {
1832 +
      "version": "4.0.1",
1833 +
      "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
1834 +
      "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
1835 +
      "dev": true,
1836 +
      "license": "MIT"
1837 +
    },
1838 +
    "node_modules/lodash.once": {
1839 +
      "version": "4.1.1",
1840 +
      "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
1841 +
      "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
1842 +
      "dev": true,
1843 +
      "license": "MIT"
1844 +
    },
1845 +
    "node_modules/lru-cache": {
1846 +
      "version": "6.0.0",
1847 +
      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
1848 +
      "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
1849 +
      "dev": true,
1850 +
      "license": "ISC",
1851 +
      "dependencies": {
1852 +
        "yallist": "^4.0.0"
1853 +
      },
1854 +
      "engines": {
1855 +
        "node": ">=10"
1856 +
      }
1857 +
    },
1858 +
    "node_modules/marked": {
1859 +
      "version": "18.0.14",
1860 +
      "resolved": "https://registry.npmjs.org/marked/-/marked-18.0.14.tgz",
1861 +
      "integrity": "sha512-mBHK6FBHuBAlhgRe88w9F0O1AbwwXJUcQibUbC/QcdTbVGAD7aWza+xt3N6oT/jCZx3/OMeS+8rnuiHZcQ9s7A==",
1862 +
      "dev": true,
1863 +
      "license": "MIT",
1864 +
      "bin": {
1865 +
        "marked": "bin/marked.js"
1866 +
      },
1867 +
      "engines": {
1868 +
        "node": ">= 20"
1869 +
      }
1870 +
    },
1871 +
    "node_modules/math-intrinsics": {
1872 +
      "version": "1.1.0",
1873 +
      "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
1874 +
      "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
1875 +
      "dev": true,
1876 +
      "license": "MIT",
1877 +
      "engines": {
1878 +
        "node": ">= 0.4"
1879 +
      }
1880 +
    },
1881 +
    "node_modules/mime": {
1882 +
      "version": "1.6.0",
1883 +
      "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
1884 +
      "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
1885 +
      "dev": true,
1886 +
      "license": "MIT",
1887 +
      "bin": {
1888 +
        "mime": "cli.js"
1889 +
      },
1890 +
      "engines": {
1891 +
        "node": ">=4"
1892 +
      }
1893 +
    },
1894 +
    "node_modules/minimatch": {
1895 +
      "version": "10.2.6",
1896 +
      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz",
1897 +
      "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==",
1898 +
      "dev": true,
1899 +
      "license": "BlueOak-1.0.0",
1900 +
      "dependencies": {
1901 +
        "brace-expansion": "^5.0.8"
1902 +
      },
1903 +
      "engines": {
1904 +
        "node": "18 || 20 || >=22"
1905 +
      },
1906 +
      "funding": {
1907 +
        "url": "https://github.com/sponsors/isaacs"
1908 +
      }
1909 +
    },
1910 +
    "node_modules/ms": {
1911 +
      "version": "2.1.3",
1912 +
      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
1913 +
      "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
1914 +
      "dev": true,
1915 +
      "license": "MIT"
1916 +
    },
1917 +
    "node_modules/mute-stream": {
1918 +
      "version": "0.0.8",
1919 +
      "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz",
1920 +
      "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==",
1921 +
      "dev": true,
1922 +
      "license": "ISC"
1923 +
    },
1924 +
    "node_modules/object-inspect": {
1925 +
      "version": "1.13.4",
1926 +
      "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
1927 +
      "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
1928 +
      "dev": true,
1929 +
      "license": "MIT",
1930 +
      "engines": {
1931 +
        "node": ">= 0.4"
1932 +
      },
1933 +
      "funding": {
1934 +
        "url": "https://github.com/sponsors/ljharb"
1935 +
      }
1936 +
    },
1937 +
    "node_modules/open": {
1938 +
      "version": "10.2.0",
1939 +
      "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz",
1940 +
      "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==",
1941 +
      "dev": true,
1942 +
      "license": "MIT",
1943 +
      "dependencies": {
1944 +
        "default-browser": "^5.2.1",
1945 +
        "define-lazy-prop": "^3.0.0",
1946 +
        "is-inside-container": "^1.0.0",
1947 +
        "wsl-utils": "^0.1.0"
1948 +
      },
1949 +
      "engines": {
1950 +
        "node": ">=18"
1951 +
      },
1952 +
      "funding": {
1953 +
        "url": "https://github.com/sponsors/sindresorhus"
1954 +
      }
1955 +
    },
1956 +
    "node_modules/parse5": {
1957 +
      "version": "8.0.1",
1958 +
      "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz",
1959 +
      "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==",
1960 +
      "dev": true,
1961 +
      "license": "MIT",
1962 +
      "dependencies": {
1963 +
        "entities": "^8.0.0"
1964 +
      },
1965 +
      "funding": {
1966 +
        "url": "https://github.com/inikulin/parse5?sponsor=1"
1967 +
      }
1968 +
    },
1969 +
    "node_modules/pend": {
1970 +
      "version": "1.2.0",
1971 +
      "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
1972 +
      "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
1973 +
      "dev": true,
1974 +
      "license": "MIT"
1975 +
    },
1976 +
    "node_modules/picomatch": {
1977 +
      "version": "4.0.7",
1978 +
      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz",
1979 +
      "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==",
1980 +
      "dev": true,
1981 +
      "license": "MIT",
1982 +
      "engines": {
1983 +
        "node": ">=12"
1984 +
      },
1985 +
      "funding": {
1986 +
        "url": "https://github.com/sponsors/jonschlinkert"
1987 +
      }
1988 +
    },
1989 +
    "node_modules/proper-lockfile": {
1990 +
      "version": "4.1.2",
1991 +
      "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz",
1992 +
      "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==",
1993 +
      "dev": true,
1994 +
      "license": "MIT",
1995 +
      "dependencies": {
1996 +
        "graceful-fs": "^4.2.4",
1997 +
        "retry": "^0.12.0",
1998 +
        "signal-exit": "^3.0.2"
1999 +
      }
2000 +
    },
2001 +
    "node_modules/qs": {
2002 +
      "version": "6.16.0",
2003 +
      "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz",
2004 +
      "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==",
2005 +
      "dev": true,
2006 +
      "license": "BSD-3-Clause",
2007 +
      "dependencies": {
2008 +
        "es-define-property": "^1.0.1",
2009 +
        "side-channel": "^1.1.1"
2010 +
      },
2011 +
      "engines": {
2012 +
        "node": ">=0.6"
2013 +
      },
2014 +
      "funding": {
2015 +
        "url": "https://github.com/sponsors/ljharb"
2016 +
      }
2017 +
    },
2018 +
    "node_modules/read": {
2019 +
      "version": "1.0.7",
2020 +
      "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz",
2021 +
      "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==",
2022 +
      "dev": true,
2023 +
      "license": "ISC",
2024 +
      "dependencies": {
2025 +
        "mute-stream": "~0.0.4"
2026 +
      },
2027 +
      "engines": {
2028 +
        "node": ">=0.8"
2029 +
      }
2030 +
    },
2031 +
    "node_modules/retry": {
2032 +
      "version": "0.12.0",
2033 +
      "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
2034 +
      "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
2035 +
      "dev": true,
2036 +
      "license": "MIT",
2037 +
      "engines": {
2038 +
        "node": ">= 4"
2039 +
      }
2040 +
    },
2041 +
    "node_modules/run-applescript": {
2042 +
      "version": "7.1.0",
2043 +
      "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz",
2044 +
      "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==",
2045 +
      "dev": true,
2046 +
      "license": "MIT",
2047 +
      "engines": {
2048 +
        "node": ">=18"
2049 +
      },
2050 +
      "funding": {
2051 +
        "url": "https://github.com/sponsors/sindresorhus"
2052 +
      }
2053 +
    },
2054 +
    "node_modules/safe-buffer": {
2055 +
      "version": "5.2.1",
2056 +
      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
2057 +
      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
2058 +
      "dev": true,
2059 +
      "funding": [
2060 +
        {
2061 +
          "type": "github",
2062 +
          "url": "https://github.com/sponsors/feross"
2063 +
        },
2064 +
        {
2065 +
          "type": "patreon",
2066 +
          "url": "https://www.patreon.com/feross"
2067 +
        },
2068 +
        {
2069 +
          "type": "consulting",
2070 +
          "url": "https://feross.org/support"
2071 +
        }
2072 +
      ],
2073 +
      "license": "MIT"
2074 +
    },
2075 +
    "node_modules/sax": {
2076 +
      "version": "1.6.1",
2077 +
      "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz",
2078 +
      "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==",
2079 +
      "dev": true,
2080 +
      "license": "BlueOak-1.0.0",
2081 +
      "engines": {
2082 +
        "node": ">=11.0.0"
2083 +
      }
2084 +
    },
2085 +
    "node_modules/semver": {
2086 +
      "version": "7.8.5",
2087 +
      "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
2088 +
      "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
2089 +
      "dev": true,
2090 +
      "license": "ISC",
2091 +
      "bin": {
2092 +
        "semver": "bin/semver.js"
2093 +
      },
2094 +
      "engines": {
2095 +
        "node": ">=10"
2096 +
      }
2097 +
    },
2098 +
    "node_modules/side-channel": {
2099 +
      "version": "1.1.1",
2100 +
      "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz",
2101 +
      "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==",
2102 +
      "dev": true,
2103 +
      "license": "MIT",
2104 +
      "dependencies": {
2105 +
        "es-errors": "^1.3.0",
2106 +
        "object-inspect": "^1.13.4",
2107 +
        "side-channel-list": "^1.0.1",
2108 +
        "side-channel-map": "^1.0.1",
2109 +
        "side-channel-weakmap": "^1.0.2"
2110 +
      },
2111 +
      "engines": {
2112 +
        "node": ">= 0.4"
2113 +
      },
2114 +
      "funding": {
2115 +
        "url": "https://github.com/sponsors/ljharb"
2116 +
      }
2117 +
    },
2118 +
    "node_modules/side-channel-list": {
2119 +
      "version": "1.0.1",
2120 +
      "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz",
2121 +
      "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
2122 +
      "dev": true,
2123 +
      "license": "MIT",
2124 +
      "dependencies": {
2125 +
        "es-errors": "^1.3.0",
2126 +
        "object-inspect": "^1.13.4"
2127 +
      },
2128 +
      "engines": {
2129 +
        "node": ">= 0.4"
2130 +
      },
2131 +
      "funding": {
2132 +
        "url": "https://github.com/sponsors/ljharb"
2133 +
      }
2134 +
    },
2135 +
    "node_modules/side-channel-map": {
2136 +
      "version": "1.0.1",
2137 +
      "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
2138 +
      "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
2139 +
      "dev": true,
2140 +
      "license": "MIT",
2141 +
      "dependencies": {
2142 +
        "call-bound": "^1.0.2",
2143 +
        "es-errors": "^1.3.0",
2144 +
        "get-intrinsic": "^1.2.5",
2145 +
        "object-inspect": "^1.13.3"
2146 +
      },
2147 +
      "engines": {
2148 +
        "node": ">= 0.4"
2149 +
      },
2150 +
      "funding": {
2151 +
        "url": "https://github.com/sponsors/ljharb"
2152 +
      }
2153 +
    },
2154 +
    "node_modules/side-channel-weakmap": {
2155 +
      "version": "1.0.2",
2156 +
      "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
2157 +
      "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
2158 +
      "dev": true,
2159 +
      "license": "MIT",
2160 +
      "dependencies": {
2161 +
        "call-bound": "^1.0.2",
2162 +
        "es-errors": "^1.3.0",
2163 +
        "get-intrinsic": "^1.2.5",
2164 +
        "object-inspect": "^1.13.3",
2165 +
        "side-channel-map": "^1.0.1"
2166 +
      },
2167 +
      "engines": {
2168 +
        "node": ">= 0.4"
2169 +
      },
2170 +
      "funding": {
2171 +
        "url": "https://github.com/sponsors/ljharb"
2172 +
      }
2173 +
    },
2174 +
    "node_modules/signal-exit": {
2175 +
      "version": "3.0.7",
2176 +
      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
2177 +
      "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
2178 +
      "dev": true,
2179 +
      "license": "ISC"
2180 +
    },
2181 +
    "node_modules/structured-source": {
2182 +
      "version": "4.0.0",
2183 +
      "resolved": "https://registry.npmjs.org/structured-source/-/structured-source-4.0.0.tgz",
2184 +
      "integrity": "sha512-qGzRFNJDjFieQkl/sVOI2dUjHKRyL9dAJi2gCPGJLbJHBIkyOHxjuocpIEfbLioX+qSJpvbYdT49/YCdMznKxA==",
2185 +
      "dev": true,
2186 +
      "license": "BSD-2-Clause",
2187 +
      "dependencies": {
2188 +
        "boundary": "^2.0.0"
2189 +
      }
2190 +
    },
2191 +
    "node_modules/textextensions": {
2192 +
      "version": "6.11.0",
2193 +
      "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-6.11.0.tgz",
2194 +
      "integrity": "sha512-tXJwSr9355kFJI3lbCkPpUH5cP8/M0GGy2xLO34aZCjMXBaK3SoPnZwr/oWmo1FdCnELcs4npdCIOFtq9W3ruQ==",
2195 +
      "dev": true,
2196 +
      "license": "Artistic-2.0",
2197 +
      "dependencies": {
2198 +
        "editions": "^6.21.0"
2199 +
      },
2200 +
      "engines": {
2201 +
        "node": ">=4"
2202 +
      },
2203 +
      "funding": {
2204 +
        "url": "https://bevry.me/fund"
2205 +
      }
2206 +
    },
2207 +
    "node_modules/tinyglobby": {
2208 +
      "version": "0.2.17",
2209 +
      "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
2210 +
      "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
2211 +
      "dev": true,
2212 +
      "license": "MIT",
2213 +
      "dependencies": {
2214 +
        "fdir": "^6.5.0",
2215 +
        "picomatch": "^4.0.4"
2216 +
      },
2217 +
      "engines": {
2218 +
        "node": ">=12.0.0"
2219 +
      },
2220 +
      "funding": {
2221 +
        "url": "https://github.com/sponsors/SuperchupuDev"
2222 +
      }
2223 +
    },
2224 +
    "node_modules/tslib": {
2225 +
      "version": "2.8.1",
2226 +
      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
2227 +
      "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
2228 +
      "dev": true,
2229 +
      "license": "0BSD"
2230 +
    },
2231 +
    "node_modules/tunnel": {
2232 +
      "version": "0.0.6",
2233 +
      "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz",
2234 +
      "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==",
2235 +
      "dev": true,
2236 +
      "license": "MIT",
2237 +
      "engines": {
2238 +
        "node": ">=0.6.11 <=0.7.0 || >=0.7.3"
2239 +
      }
2240 +
    },
2241 +
    "node_modules/typed-rest-client": {
2242 +
      "version": "1.8.11",
2243 +
      "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.11.tgz",
2244 +
      "integrity": "sha512-5UvfMpd1oelmUPRbbaVnq+rHP7ng2cE4qoQkQeAqxRL6PklkxsM0g32/HL0yfvruK6ojQ5x8EE+HF4YV6DtuCA==",
2245 +
      "dev": true,
2246 +
      "license": "MIT",
2247 +
      "dependencies": {
2248 +
        "qs": "^6.9.1",
2249 +
        "tunnel": "0.0.6",
2250 +
        "underscore": "^1.12.1"
2251 +
      }
2252 +
    },
2253 +
    "node_modules/typescript": {
2254 +
      "version": "5.9.3",
2255 +
      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
2256 +
      "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
2257 +
      "dev": true,
2258 +
      "license": "Apache-2.0",
2259 +
      "bin": {
2260 +
        "tsc": "bin/tsc",
2261 +
        "tsserver": "bin/tsserver"
2262 +
      },
2263 +
      "engines": {
2264 +
        "node": ">=14.17"
2265 +
      }
2266 +
    },
2267 +
    "node_modules/underscore": {
2268 +
      "version": "1.13.8",
2269 +
      "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.8.tgz",
2270 +
      "integrity": "sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==",
2271 +
      "dev": true,
2272 +
      "license": "MIT"
2273 +
    },
2274 +
    "node_modules/undici-types": {
2275 +
      "version": "7.24.6",
2276 +
      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz",
2277 +
      "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==",
2278 +
      "dev": true,
2279 +
      "license": "MIT"
2280 +
    },
2281 +
    "node_modules/url-join": {
2282 +
      "version": "4.0.1",
2283 +
      "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz",
2284 +
      "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==",
2285 +
      "dev": true,
2286 +
      "license": "MIT"
2287 +
    },
2288 +
    "node_modules/version-range": {
2289 +
      "version": "4.15.0",
2290 +
      "resolved": "https://registry.npmjs.org/version-range/-/version-range-4.15.0.tgz",
2291 +
      "integrity": "sha512-Ck0EJbAGxHwprkzFO966t4/5QkRuzh+/I1RxhLgUKKwEn+Cd8NwM60mE3AqBZg5gYODoXW0EFsQvbZjRlvdqbg==",
2292 +
      "dev": true,
2293 +
      "license": "Artistic-2.0",
2294 +
      "engines": {
2295 +
        "node": ">=4"
2296 +
      },
2297 +
      "funding": {
2298 +
        "url": "https://bevry.me/fund"
2299 +
      }
2300 +
    },
2301 +
    "node_modules/wsl-utils": {
2302 +
      "version": "0.1.0",
2303 +
      "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz",
2304 +
      "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==",
2305 +
      "dev": true,
2306 +
      "license": "MIT",
2307 +
      "dependencies": {
2308 +
        "is-wsl": "^3.1.0"
2309 +
      },
2310 +
      "engines": {
2311 +
        "node": ">=18"
2312 +
      },
2313 +
      "funding": {
2314 +
        "url": "https://github.com/sponsors/sindresorhus"
2315 +
      }
2316 +
    },
2317 +
    "node_modules/xml2js": {
2318 +
      "version": "0.5.0",
2319 +
      "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz",
2320 +
      "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==",
2321 +
      "dev": true,
2322 +
      "license": "MIT",
2323 +
      "dependencies": {
2324 +
        "sax": ">=0.6.0",
2325 +
        "xmlbuilder": "~11.0.0"
2326 +
      },
2327 +
      "engines": {
2328 +
        "node": ">=4.0.0"
2329 +
      }
2330 +
    },
2331 +
    "node_modules/xmlbuilder": {
2332 +
      "version": "11.0.1",
2333 +
      "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz",
2334 +
      "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==",
2335 +
      "dev": true,
2336 +
      "license": "MIT",
2337 +
      "engines": {
2338 +
        "node": ">=4.0"
2339 +
      }
2340 +
    },
2341 +
    "node_modules/yallist": {
2342 +
      "version": "4.0.0",
2343 +
      "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
2344 +
      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
2345 +
      "dev": true,
2346 +
      "license": "ISC"
2347 +
    },
2348 +
    "node_modules/yauzl": {
2349 +
      "version": "3.4.0",
2350 +
      "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.4.0.tgz",
2351 +
      "integrity": "sha512-jIH9yLR9wqr0wOS0TpBvo/g/2UgZH5qePVbjgRliiF0BYvOZyaBknKsF+x9Iht0O6sqgnB93rCICdOZFecJuDw==",
2352 +
      "dev": true,
2353 +
      "license": "MIT",
2354 +
      "dependencies": {
2355 +
        "pend": "~1.2.0"
2356 +
      },
2357 +
      "engines": {
2358 +
        "node": ">=12"
2359 +
      }
2360 +
    },
2361 +
    "node_modules/yazl": {
2362 +
      "version": "2.5.1",
2363 +
      "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz",
2364 +
      "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==",
2365 +
      "dev": true,
2366 +
      "license": "MIT",
2367 +
      "dependencies": {
2368 +
        "buffer-crc32": "~0.2.3"
2369 +
      }
2370 +
    }
2371 +
  }
2372 +
}
added package.json +95 −0
1 +
{
2 +
  "name": "oxygen-vscode",
3 +
  "displayName": "Oxygen",
4 +
  "description": "Oxygen version control: source control view, diffs, decorations, bookmarks and history for oxy repositories",
5 +
  "version": "0.1.0",
6 +
  "publisher": "oxygen",
7 +
  "license": "MIT OR Apache-2.0",
8 +
  "icon": "assets/images/oxygen.png",
9 +
  "engines": {
10 +
    "vscode": "^1.137.0"
11 +
  },
12 +
  "categories": [
13 +
    "SCM Providers"
14 +
  ],
15 +
  "keywords": [
16 +
    "oxygen",
17 +
    "oxy",
18 +
    "scm",
19 +
    "version control"
20 +
  ],
21 +
  "activationEvents": [
22 +
    "workspaceContains:.oxy"
23 +
  ],
24 +
  "main": "./dist/extension.js",
25 +
  "contributes": {
26 +
    "commands": [
27 +
      { "command": "oxygen.refresh", "title": "Refresh", "category": "Oxygen", "icon": "$(refresh)" },
28 +
      { "command": "oxygen.commit", "title": "Commit", "category": "Oxygen", "icon": "$(check)" },
29 +
      { "command": "oxygen.openFile", "title": "Open File", "category": "Oxygen", "icon": "$(go-to-file)" },
30 +
      { "command": "oxygen.openChanges", "title": "Open Changes", "category": "Oxygen" },
31 +
      { "command": "oxygen.openBaseVersion", "title": "Open Base Version", "category": "Oxygen" },
32 +
      { "command": "oxygen.showOutput", "title": "Show Output", "category": "Oxygen" },
33 +
      { "command": "oxygen.showLog", "title": "Show Log", "category": "Oxygen" }
34 +
    ],
35 +
    "menus": {
36 +
      "scm/title": [
37 +
        { "command": "oxygen.commit", "group": "navigation@1", "when": "scmProvider == oxygen" },
38 +
        { "command": "oxygen.refresh", "group": "navigation@2", "when": "scmProvider == oxygen" },
39 +
        { "command": "oxygen.showLog", "group": "1_view", "when": "scmProvider == oxygen" },
40 +
        { "command": "oxygen.showOutput", "group": "1_view", "when": "scmProvider == oxygen" }
41 +
      ],
42 +
      "scm/resourceState/context": [
43 +
        { "command": "oxygen.openFile", "group": "inline", "when": "scmProvider == oxygen" },
44 +
        { "command": "oxygen.openChanges", "group": "navigation", "when": "scmProvider == oxygen" },
45 +
        { "command": "oxygen.openBaseVersion", "group": "navigation", "when": "scmProvider == oxygen" }
46 +
      ],
47 +
      "commandPalette": [
48 +
        { "command": "oxygen.openFile", "when": "false" },
49 +
        { "command": "oxygen.openChanges", "when": "false" },
50 +
        { "command": "oxygen.openBaseVersion", "when": "false" }
51 +
      ]
52 +
    },
53 +
    "configuration": {
54 +
      "title": "Oxygen",
55 +
      "properties": {
56 +
        "oxygen.path": {
57 +
          "type": "string",
58 +
          "default": "",
59 +
          "description": "Path to the oxy binary. Empty means oxy on PATH, then ~/.local/bin/oxy."
60 +
        },
61 +
        "oxygen.autoRefresh": {
62 +
          "type": "boolean",
63 +
          "default": true,
64 +
          "description": "Refresh the source control view when files in the workspace change."
65 +
        },
66 +
        "oxygen.decorations.enabled": {
67 +
          "type": "boolean",
68 +
          "default": true,
69 +
          "description": "Show status decorations in the explorer and on editor tabs."
70 +
        },
71 +
        "oxygen.statusBar.enabled": {
72 +
          "type": "boolean",
73 +
          "default": true,
74 +
          "description": "Show the bookmark, working copy and state in the status bar."
75 +
        }
76 +
      }
77 +
    }
78 +
  },
79 +
  "scripts": {
80 +
    "build": "esbuild src/extension.ts --bundle --platform=node --format=cjs --external:vscode --outfile=dist/extension.js --sourcemap",
81 +
    "watch": "npm run build -- --watch",
82 +
    "typecheck": "tsc --noEmit -p .",
83 +
    "pretest": "esbuild src/oxy.ts --bundle --platform=node --format=esm --outfile=out/oxy.mjs",
84 +
    "test": "node --test \"test/**/*.test.mjs\"",
85 +
    "check": "npm run typecheck && npm run build && npm test",
86 +
    "package": "vsce package --no-dependencies"
87 +
  },
88 +
  "devDependencies": {
89 +
    "@types/node": "^24.0.0",
90 +
    "@types/vscode": "^1.137.0",
91 +
    "@vscode/vsce": "^4.0.0",
92 +
    "esbuild": "^0.28.0",
93 +
    "typescript": "^5.9.0"
94 +
  }
95 +
}
added src/contentProvider.ts +62 −0
1 +
// The `oxy:` URI scheme serves file content at a state through `oxy cat`
2 +
// (spec §3): oxy:/<repository-relative path>?root=<repository root>&change=<change>
3 +
// where `change` is `@base` (the working copy's base) or anything
4 +
// `oxy cat -r` accepts.
5 +
6 +
import * as vscode from "vscode";
7 +
import { Oxy } from "./oxy";
8 +
9 +
export const SCHEME = "oxy";
10 +
11 +
export function oxyUri(root: string, path: string, change: string): vscode.Uri {
12 +
  const query = new URLSearchParams({ root, change }).toString();
13 +
  return vscode.Uri.from({ scheme: SCHEME, path: "/" + path, query });
14 +
}
15 +
16 +
export function baseUri(root: string, path: string): vscode.Uri {
17 +
  return oxyUri(root, path, "@base");
18 +
}
19 +
20 +
export function parseOxyUri(uri: vscode.Uri): { root: string; path: string; change: string } | undefined {
21 +
  if (uri.scheme !== SCHEME) {
22 +
    return undefined;
23 +
  }
24 +
  const q = new URLSearchParams(uri.query);
25 +
  const root = q.get("root");
26 +
  const change = q.get("change") ?? "@base";
27 +
  if (!root) {
28 +
    return undefined;
29 +
  }
30 +
  return { root, path: uri.path.replace(/^\//, ""), change };
31 +
}
32 +
33 +
export class OxyContentProvider implements vscode.TextDocumentContentProvider {
34 +
  private readonly emitter = new vscode.EventEmitter<vscode.Uri>();
35 +
  readonly onDidChange = this.emitter.event;
36 +
37 +
  constructor(private readonly oxy: Oxy) {}
38 +
39 +
  /** Tell open `oxy:` documents under `root` to reload (after an operation). */
40 +
  invalidate(root: string): void {
41 +
    for (const doc of vscode.workspace.textDocuments) {
42 +
      const parsed = parseOxyUri(doc.uri);
43 +
      if (parsed && parsed.root === root && parsed.change === "@base") {
44 +
        this.emitter.fire(doc.uri);
45 +
      }
46 +
    }
47 +
  }
48 +
49 +
  async provideTextDocumentContent(uri: vscode.Uri): Promise<string> {
50 +
    const parsed = parseOxyUri(uri);
51 +
    if (!parsed) {
52 +
      return "";
53 +
    }
54 +
    try {
55 +
      const bytes = await this.oxy.cat(parsed.root, parsed.path, parsed.change === "@base" ? undefined : parsed.change);
56 +
      return bytes.toString("utf8");
57 +
    } catch {
58 +
      // A path absent from that state (an added file's base) is empty.
59 +
      return "";
60 +
    }
61 +
  }
62 +
}
added src/decorations.ts +63 −0
1 +
// Explorer and tab decorations from each repository's latest status (spec §5.1).
2 +
3 +
import * as vscode from "vscode";
4 +
import { Repository } from "./repository";
5 +
6 +
export class OxyDecorationProvider implements vscode.FileDecorationProvider, vscode.Disposable {
7 +
  private readonly emitter = new vscode.EventEmitter<vscode.Uri | vscode.Uri[] | undefined>();
8 +
  readonly onDidChangeFileDecorations = this.emitter.event;
9 +
  private readonly disposables: vscode.Disposable[] = [];
10 +
11 +
  constructor(private readonly repositories: () => Repository[]) {
12 +
    this.disposables.push(vscode.window.registerFileDecorationProvider(this), this.emitter);
13 +
  }
14 +
15 +
  /** Call when a repository's status changed; every decoration is recomputed. */
16 +
  refresh(): void {
17 +
    this.emitter.fire(undefined);
18 +
  }
19 +
20 +
  provideFileDecoration(uri: vscode.Uri): vscode.FileDecoration | undefined {
21 +
    if (!vscode.workspace.getConfiguration("oxygen").get<boolean>("decorations.enabled", true)) {
22 +
      return undefined;
23 +
    }
24 +
    for (const repo of this.repositories()) {
25 +
      const rel = repo.relativePath(uri);
26 +
      if (rel === undefined || !repo.status) {
27 +
        continue;
28 +
      }
29 +
      const entry = repo.status.paths.find((p) => p.path === rel);
30 +
      const conflict = repo.status.conflicts.find((c) => c.path === rel);
31 +
      if (conflict && !entry) {
32 +
        return new vscode.FileDecoration("‼", "conflict", new vscode.ThemeColor("gitDecoration.conflictingResourceForeground"));
33 +
      }
34 +
      if (!entry) {
35 +
        // A directory containing changes gets a dot, like Git.
36 +
        if (repo.status.paths.some((p) => p.path.startsWith(rel + "/"))) {
37 +
          return new vscode.FileDecoration("•", "contains changes", new vscode.ThemeColor("gitDecoration.modifiedResourceForeground"));
38 +
        }
39 +
        continue;
40 +
      }
41 +
      const letters: Record<string, [string, string, string]> = {
42 +
        Added: ["A", "added", "gitDecoration.addedResourceForeground"],
43 +
        Modified: ["M", "modified", "gitDecoration.modifiedResourceForeground"],
44 +
        Removed: ["D", "removed", "gitDecoration.deletedResourceForeground"],
45 +
        Renamed: ["R", "renamed", "gitDecoration.renamedResourceForeground"],
46 +
        ModeChanged: ["X", "mode changed", "gitDecoration.modifiedResourceForeground"],
47 +
        ConflictEdited: ["‼", "conflict markers remain", "gitDecoration.conflictingResourceForeground"],
48 +
        Resolved: ["✔", "conflict resolved", "gitDecoration.stageModifiedResourceForeground"],
49 +
      };
50 +
      const [badge, tooltip, color] = letters[entry.status] ?? ["?", entry.status, "gitDecoration.modifiedResourceForeground"];
51 +
      const d = new vscode.FileDecoration(badge, tooltip, new vscode.ThemeColor(color));
52 +
      d.propagate = false;
53 +
      return d;
54 +
    }
55 +
    return undefined;
56 +
  }
57 +
58 +
  dispose(): void {
59 +
    for (const d of this.disposables) {
60 +
      d.dispose();
61 +
    }
62 +
  }
63 +
}
added src/extension.ts +188 −0
1 +
// Activation: find oxy, find repositories, wire the source control view,
2 +
// decorations, the content provider, and the commands (spec §3).
3 +
4 +
import * as vscode from "vscode";
5 +
import { basename } from "node:path";
6 +
import { Oxy, OxyError, findBinary } from "./oxy";
7 +
import { Repository, OxyResourceState } from "./repository";
8 +
import { OxyContentProvider, SCHEME, baseUri } from "./contentProvider";
9 +
import { OxyDecorationProvider } from "./decorations";
10 +
11 +
const MIN_VERSION = { format: 2, protocol: 1 };
12 +
13 +
let output: vscode.OutputChannel;
14 +
let oxy: Oxy;
15 +
let contentProvider: OxyContentProvider;
16 +
let decorations: OxyDecorationProvider;
17 +
const repositories = new Map<string, Repository>();
18 +
19 +
export async function activate(context: vscode.ExtensionContext): Promise<void> {
20 +
  output = vscode.window.createOutputChannel("Oxygen");
21 +
  context.subscriptions.push(output);
22 +
  oxy = new Oxy(findBinary(vscode.workspace.getConfiguration("oxygen").get<string>("path")), (line) => output.appendLine(line));
23 +
24 +
  try {
25 +
    const v = await oxy.version();
26 +
    output.appendLine(`oxy ${v.version} (format ${v.format}, protocol ${v.protocol}) at ${oxy.binary}`);
27 +
    if (v.format < MIN_VERSION.format || v.protocol < MIN_VERSION.protocol) {
28 +
      void vscode.window.showWarningMessage(`Oxygen: oxy ${v.version} is older than this extension supports.`);
29 +
    }
30 +
  } catch (e) {
31 +
    const pick = await vscode.window.showErrorMessage(`Oxygen: could not run oxy (${(e as Error).message}). Set "oxygen.path" or put oxy on your PATH.`, "Open Settings");
32 +
    if (pick === "Open Settings") {
33 +
      void vscode.commands.executeCommand("workbench.action.openSettings", "oxygen.path");
34 +
    }
35 +
    return;
36 +
  }
37 +
38 +
  contentProvider = new OxyContentProvider(oxy);
39 +
  context.subscriptions.push(vscode.workspace.registerTextDocumentContentProvider(SCHEME, contentProvider));
40 +
  decorations = new OxyDecorationProvider(() => [...repositories.values()]);
41 +
  context.subscriptions.push(decorations);
42 +
43 +
  context.subscriptions.push(
44 +
    vscode.commands.registerCommand("oxygen.refresh", async () => {
45 +
      await Promise.all([...repositories.values()].map((r) => r.refresh()));
46 +
    }),
47 +
    vscode.commands.registerCommand("oxygen.showOutput", () => output.show()),
48 +
    vscode.commands.registerCommand("oxygen.openFile", (state?: OxyResourceState) => state && vscode.commands.executeCommand("vscode.open", state.resourceUri)),
49 +
    vscode.commands.registerCommand("oxygen.openChanges", (state?: OxyResourceState) => state && openChanges(state)),
50 +
    vscode.commands.registerCommand("oxygen.openBaseVersion", (state?: OxyResourceState) => state && vscode.commands.executeCommand("vscode.open", baseUri(state.repository.root, state.path))),
51 +
    vscode.commands.registerCommand("oxygen.commit", (arg?: Repository | vscode.SourceControl) => commit(arg)),
52 +
    vscode.commands.registerCommand("oxygen.showLog", () => showLog()),
53 +
    vscode.workspace.onDidChangeWorkspaceFolders(() => void discover()),
54 +
    vscode.workspace.onDidChangeConfiguration((e) => {
55 +
      if (e.affectsConfiguration("oxygen.path")) {
56 +
        void vscode.window.showInformationMessage("Oxygen: reload the window to use the new oxy path.");
57 +
      }
58 +
      if (e.affectsConfiguration("oxygen")) {
59 +
        decorations.refresh();
60 +
        for (const r of repositories.values()) {
61 +
          r.scheduleRefresh();
62 +
        }
63 +
      }
64 +
    }),
65 +
  );
66 +
  context.subscriptions.push({ dispose: () => repositories.forEach((r) => r.dispose()) });
67 +
68 +
  await discover();
69 +
}
70 +
71 +
export function deactivate(): void {
72 +
  for (const r of repositories.values()) {
73 +
    r.dispose();
74 +
  }
75 +
  repositories.clear();
76 +
}
77 +
78 +
/** One Repository per distinct oxy root among the workspace folders. */
79 +
async function discover(): Promise<void> {
80 +
  const wanted = new Map<string, vscode.WorkspaceFolder>();
81 +
  for (const folder of vscode.workspace.workspaceFolders ?? []) {
82 +
    const info = await oxy.root(folder.uri.fsPath);
83 +
    if (info) {
84 +
      wanted.set(info.root, folder);
85 +
    }
86 +
  }
87 +
  for (const [root, repo] of repositories) {
88 +
    if (!wanted.has(root)) {
89 +
      repo.dispose();
90 +
      repositories.delete(root);
91 +
    }
92 +
  }
93 +
  for (const [root, folder] of wanted) {
94 +
    if (!repositories.has(root)) {
95 +
      const repo = new Repository(oxy, root, folder);
96 +
      repo.onDidChangeStatus(() => {
97 +
        decorations.refresh();
98 +
        contentProvider.invalidate(root);
99 +
      });
100 +
      repositories.set(root, repo);
101 +
      output.appendLine(`repository ${root}`);
102 +
      await repo.refresh();
103 +
    }
104 +
  }
105 +
  void vscode.commands.executeCommand("setContext", "oxygen.hasRepository", repositories.size > 0);
106 +
}
107 +
108 +
function repositoryFor(arg: Repository | vscode.SourceControl | undefined): Repository | undefined {
109 +
  if (arg instanceof Repository) {
110 +
    return arg;
111 +
  }
112 +
  if (arg && "rootUri" in arg && arg.rootUri) {
113 +
    return repositories.get(arg.rootUri.fsPath);
114 +
  }
115 +
  const active = vscode.window.activeTextEditor?.document.uri;
116 +
  for (const repo of repositories.values()) {
117 +
    if (active && repo.relativePath(active) !== undefined) {
118 +
      return repo;
119 +
    }
120 +
  }
121 +
  return repositories.size === 1 ? [...repositories.values()][0] : undefined;
122 +
}
123 +
124 +
async function openChanges(state: OxyResourceState): Promise<void> {
125 +
  const repo = state.repository;
126 +
  const title = `${basename(state.path)} (base ↔ working copy)`;
127 +
  if (state.status === "Added") {
128 +
    await vscode.commands.executeCommand("vscode.open", state.resourceUri);
129 +
    return;
130 +
  }
131 +
  if (state.status === "Removed") {
132 +
    await vscode.commands.executeCommand("vscode.open", baseUri(repo.root, state.path));
133 +
    return;
134 +
  }
135 +
  await vscode.commands.executeCommand("vscode.diff", baseUri(repo.root, state.path), state.resourceUri, title);
136 +
}
137 +
138 +
async function commit(arg: Repository | vscode.SourceControl | undefined): Promise<void> {
139 +
  const repo = repositoryFor(arg);
140 +
  if (!repo) {
141 +
    void vscode.window.showErrorMessage("Oxygen: no repository for the active file.");
142 +
    return;
143 +
  }
144 +
  let message = repo.sourceControl.inputBox.value.trim();
145 +
  if (!message) {
146 +
    message = (await vscode.window.showInputBox({ prompt: "Commit message", placeHolder: "What did you change?" }))?.trim() ?? "";
147 +
  }
148 +
  if (!message) {
149 +
    return;
150 +
  }
151 +
  try {
152 +
    await vscode.window.withProgress({ location: vscode.ProgressLocation.SourceControl, title: "Committing" }, () => repo.oxy.commit(repo.root, message));
153 +
    repo.sourceControl.inputBox.value = "";
154 +
    await repo.refresh();
155 +
  } catch (e) {
156 +
    showError(e);
157 +
  }
158 +
}
159 +
160 +
async function showLog(): Promise<void> {
161 +
  const repo = repositoryFor(undefined);
162 +
  if (!repo) {
163 +
    return;
164 +
  }
165 +
  try {
166 +
    const log = await repo.oxy.log(repo.root, 50);
167 +
    const items = log.changes
168 +
      .filter((c) => !c.working_copy)
169 +
      .map((c) => ({
170 +
        label: `${c.head ? "$(circle-filled)" : "$(circle-outline)"} ${c.message.split("\n")[0]}`,
171 +
        description: `${c.change_id.slice(0, 8)} · ${c.author}`,
172 +
        detail: `${new Date(c.when_ms).toLocaleString()} · ${c.signature?.status ?? "unsigned"}${c.signature?.revoked ? " (key revoked)" : ""}`,
173 +
        change: c,
174 +
      }));
175 +
    const pick = await vscode.window.showQuickPick(items, { placeHolder: `${log.bookmark}: ${log.total - 1} changes`, matchOnDescription: true, matchOnDetail: true });
176 +
    if (pick) {
177 +
      const doc = await vscode.workspace.openTextDocument({ content: pick.change.message, language: "markdown" });
178 +
      await vscode.window.showTextDocument(doc, { preview: true });
179 +
    }
180 +
  } catch (e) {
181 +
    showError(e);
182 +
  }
183 +
}
184 +
185 +
function showError(e: unknown): void {
186 +
  const message = e instanceof OxyError ? e.message : String(e);
187 +
  void vscode.window.showErrorMessage(`Oxygen: ${message}`, "Show Output").then((pick) => pick && output.show());
188 +
}
added src/oxy.ts +205 −0
1 +
// The oxy process wrapper: one function per command the extension uses,
2 +
// all through `oxy --plain --json` (spec §4). This module never imports
3 +
// the vscode API so it can be tested with plain Node.
4 +
5 +
import { execFile } from "node:child_process";
6 +
import { existsSync } from "node:fs";
7 +
import { homedir } from "node:os";
8 +
import { join } from "node:path";
9 +
10 +
export interface RunResult {
11 +
  code: number;
12 +
  stdout: Buffer;
13 +
  stderr: string;
14 +
}
15 +
16 +
export class OxyError extends Error {
17 +
  constructor(
18 +
    public readonly args: string[],
19 +
    public readonly code: number,
20 +
    public readonly stderr: string,
21 +
  ) {
22 +
    super(stderr.trim() || `oxy ${args.join(" ")} exited with status ${code}`);
23 +
    this.name = "OxyError";
24 +
  }
25 +
}
26 +
27 +
/** Where to find `oxy`: the configured path, else `oxy` on PATH, else ~/.local/bin/oxy. */
28 +
export function findBinary(configured: string | undefined): string {
29 +
  if (configured && configured.trim()) {
30 +
    return configured.trim();
31 +
  }
32 +
  const local = join(homedir(), ".local", "bin", "oxy");
33 +
  return existsSync(local) ? local : "oxy";
34 +
}
35 +
36 +
export type Logger = (line: string) => void;
37 +
38 +
export class Oxy {
39 +
  constructor(
40 +
    public readonly binary: string,
41 +
    private readonly logger: Logger = () => {},
42 +
  ) {}
43 +
44 +
  /** Run oxy in `cwd`; resolves with the exit status, never rejects on a non-zero status. */
45 +
  run(cwd: string, args: string[]): Promise<RunResult> {
46 +
    const started = Date.now();
47 +
    const full = ["--plain", ...args];
48 +
    return new Promise((resolve, reject) => {
49 +
      execFile(
50 +
        this.binary,
51 +
        full,
52 +
        { cwd, maxBuffer: 256 * 1024 * 1024, encoding: "buffer", env: { ...process.env, NO_COLOR: "1" } },
53 +
        (error, stdout, stderr) => {
54 +
          const err = stderr.toString("utf8");
55 +
          const code = error && typeof (error as NodeJS.ErrnoException).code === "number" ? ((error as NodeJS.ErrnoException).code as unknown as number) : error ? -1 : 0;
56 +
          this.logger(`oxy ${args.join(" ")} (${cwd}) → ${code} in ${Date.now() - started} ms${err.trim() ? `: ${err.trim()}` : ""}`);
57 +
          if (error && (error as NodeJS.ErrnoException).code === "ENOENT") {
58 +
            reject(new OxyError(full, -1, `oxy not found at ${this.binary}`));
59 +
            return;
60 +
          }
61 +
          resolve({ code, stdout: stdout as Buffer, stderr: err });
62 +
        },
63 +
      );
64 +
    });
65 +
  }
66 +
67 +
  /** Run and require success (exit 0, or one of `okCodes`). */
68 +
  async check(cwd: string, args: string[], okCodes: number[] = [0]): Promise<RunResult> {
69 +
    const r = await this.run(cwd, args);
70 +
    if (!okCodes.includes(r.code)) {
71 +
      throw new OxyError(args, r.code, r.stderr);
72 +
    }
73 +
    return r;
74 +
  }
75 +
76 +
  async json<T>(cwd: string, args: string[], okCodes: number[] = [0]): Promise<T> {
77 +
    const r = await this.check(cwd, [...args, "--json"], okCodes);
78 +
    const text = r.stdout.toString("utf8").trim();
79 +
    try {
80 +
      return JSON.parse(text) as T;
81 +
    } catch (e) {
82 +
      throw new OxyError(args, r.code, `oxy ${args.join(" ")} did not print JSON: ${text.slice(0, 200)}`);
83 +
    }
84 +
  }
85 +
86 +
  // ---------------------------------------------------------------- commands
87 +
88 +
  async version(): Promise<VersionInfo> {
89 +
    return this.json<VersionInfo>(process.cwd(), ["version"]);
90 +
  }
91 +
92 +
  /** The repository containing `cwd`, or null when there is none. */
93 +
  async root(cwd: string): Promise<RootInfo | null> {
94 +
    const r = await this.run(cwd, ["root", "--json"]);
95 +
    if (r.code !== 0) {
96 +
      return null;
97 +
    }
98 +
    const v = JSON.parse(r.stdout.toString("utf8"));
99 +
    return v === null ? null : (v as RootInfo);
100 +
  }
101 +
102 +
  status(root: string): Promise<StatusInfo> {
103 +
    return this.json<StatusInfo>(root, ["status"], [0, 2]);
104 +
  }
105 +
106 +
  async prompt(root: string, format?: string): Promise<string> {
107 +
    const args = ["prompt", "--glyphs"];
108 +
    if (format) {
109 +
      args.push("--format", format);
110 +
    }
111 +
    const r = await this.run(root, args);
112 +
    return r.code === 0 ? r.stdout.toString("utf8").trim() : "";
113 +
  }
114 +
115 +
  /** File content at a state; `change` may be a hash prefix, id prefix, bookmark, or tag. */
116 +
  async cat(root: string, path: string, change?: string): Promise<Buffer> {
117 +
    const args = ["cat", path];
118 +
    if (change) {
119 +
      args.push("-r", change);
120 +
    }
121 +
    return (await this.check(root, args)).stdout;
122 +
  }
123 +
124 +
  log(root: string, limit?: number, path?: string): Promise<LogInfo> {
125 +
    const args = ["log"];
126 +
    if (limit !== undefined) {
127 +
      args.push("-n", String(limit));
128 +
    }
129 +
    if (path) {
130 +
      args.push("--path", path);
131 +
    }
132 +
    return this.json<LogInfo>(root, args);
133 +
  }
134 +
135 +
  commit(root: string, message: string, paths: string[] = []): Promise<void> {
136 +
    return this.check(root, ["commit", "-m", message, ...paths]).then(() => undefined);
137 +
  }
138 +
139 +
  bookmarks(root: string): Promise<BookmarkInfo[]> {
140 +
    return this.json<BookmarkInfo[]>(root, ["bookmark", "list"]);
141 +
  }
142 +
}
143 +
144 +
// ---------------------------------------------------------------- types
145 +
146 +
export interface VersionInfo {
147 +
  version: string;
148 +
  protocol: number;
149 +
  format: number;
150 +
}
151 +
152 +
export interface RootInfo {
153 +
  root: string;
154 +
  workspace: string;
155 +
  bookmark: string;
156 +
}
157 +
158 +
export type PathStatus = "Added" | "Modified" | "Removed" | "Renamed" | "ModeChanged" | "ConflictEdited" | "Resolved";
159 +
160 +
export interface StatusInfo {
161 +
  bookmark: string;
162 +
  working_copy: string;
163 +
  heads: number;
164 +
  clean: boolean;
165 +
  conflicted: boolean;
166 +
  paths: { path: string; status: PathStatus }[];
167 +
  conflicts: ConflictInfo[];
168 +
}
169 +
170 +
export interface ConflictInfo {
171 +
  kind: string;
172 +
  path?: string;
173 +
  node?: string;
174 +
  nodes?: string[];
175 +
  changes?: string[];
176 +
  resolved?: boolean | "markers";
177 +
}
178 +
179 +
export interface LogEntry {
180 +
  hash: string;
181 +
  change_id: string;
182 +
  author: string;
183 +
  author_key: string;
184 +
  when_ms: number;
185 +
  message: string;
186 +
  kind: string;
187 +
  deps: string[];
188 +
  head: boolean;
189 +
  working_copy: boolean;
190 +
  signature: { status: string; key?: string; name?: string; revoked?: boolean; reason?: string } | null;
191 +
}
192 +
193 +
export interface LogInfo {
194 +
  bookmark: string;
195 +
  total: number;
196 +
  changes: LogEntry[];
197 +
}
198 +
199 +
export interface BookmarkInfo {
200 +
  name: string;
201 +
  heads: string[];
202 +
  current: boolean;
203 +
  ahead: number | null;
204 +
  behind: number | null;
205 +
}
added src/repository.ts +231 −0
1 +
// One Oxygen repository as VS Code sees it: the source control provider,
2 +
// its resource groups, the status bar item, and the watchers that keep
3 +
// them fresh (spec §3, §5.1, §5.4).
4 +
5 +
import * as vscode from "vscode";
6 +
import { basename, join, relative } from "node:path";
7 +
import { Oxy, PathStatus, StatusInfo } from "./oxy";
8 +
import { baseUri } from "./contentProvider";
9 +
10 +
const STATUS_LETTER: Record<PathStatus, string> = {
11 +
  Added: "A",
12 +
  Modified: "M",
13 +
  Removed: "D",
14 +
  Renamed: "R",
15 +
  ModeChanged: "X",
16 +
  ConflictEdited: "‼",
17 +
  Resolved: "✔",
18 +
};
19 +
20 +
const STATUS_WORD: Record<PathStatus, string> = {
21 +
  Added: "added",
22 +
  Modified: "modified",
23 +
  Removed: "removed",
24 +
  Renamed: "renamed",
25 +
  ModeChanged: "mode changed",
26 +
  ConflictEdited: "conflict markers remain",
27 +
  Resolved: "conflict resolved",
28 +
};
29 +
30 +
const STATUS_COLOR: Record<PathStatus, string> = {
31 +
  Added: "gitDecoration.addedResourceForeground",
32 +
  Modified: "gitDecoration.modifiedResourceForeground",
33 +
  Removed: "gitDecoration.deletedResourceForeground",
34 +
  Renamed: "gitDecoration.renamedResourceForeground",
35 +
  ModeChanged: "gitDecoration.modifiedResourceForeground",
36 +
  ConflictEdited: "gitDecoration.conflictingResourceForeground",
37 +
  Resolved: "gitDecoration.stageModifiedResourceForeground",
38 +
};
39 +
40 +
export class OxyResourceState implements vscode.SourceControlResourceState {
41 +
  readonly resourceUri: vscode.Uri;
42 +
  readonly decorations: vscode.SourceControlResourceDecorations;
43 +
  readonly command: vscode.Command;
44 +
45 +
  constructor(
46 +
    readonly repository: Repository,
47 +
    readonly path: string,
48 +
    readonly status: PathStatus,
49 +
  ) {
50 +
    this.resourceUri = vscode.Uri.file(join(repository.root, path));
51 +
    this.decorations = {
52 +
      strikeThrough: status === "Removed",
53 +
      tooltip: STATUS_WORD[status],
54 +
      faded: false,
55 +
    };
56 +
    this.command = { command: "oxygen.openChanges", title: "Open Changes", arguments: [this] };
57 +
  }
58 +
59 +
  get letter(): string {
60 +
    return STATUS_LETTER[this.status];
61 +
  }
62 +
63 +
  get color(): vscode.ThemeColor {
64 +
    return new vscode.ThemeColor(STATUS_COLOR[this.status]);
65 +
  }
66 +
}
67 +
68 +
export class Repository implements vscode.Disposable, vscode.QuickDiffProvider {
69 +
  readonly sourceControl: vscode.SourceControl;
70 +
  readonly conflictsGroup: vscode.SourceControlResourceGroup;
71 +
  readonly changesGroup: vscode.SourceControlResourceGroup;
72 +
  private readonly statusBar: vscode.StatusBarItem;
73 +
  private readonly disposables: vscode.Disposable[] = [];
74 +
  private readonly onDidChangeStatusEmitter = new vscode.EventEmitter<StatusInfo>();
75 +
  readonly onDidChangeStatus = this.onDidChangeStatusEmitter.event;
76 +
  private latest: StatusInfo | undefined;
77 +
  private refreshTimer: NodeJS.Timeout | undefined;
78 +
  private refreshing: Promise<void> | undefined;
79 +
  private refreshAgain = false;
80 +
81 +
  constructor(
82 +
    readonly oxy: Oxy,
83 +
    readonly root: string,
84 +
    readonly workspaceFolder: vscode.WorkspaceFolder | undefined,
85 +
  ) {
86 +
    const rootUri = vscode.Uri.file(root);
87 +
    this.sourceControl = vscode.scm.createSourceControl("oxygen", `Oxygen (${basename(root)})`, rootUri);
88 +
    this.sourceControl.quickDiffProvider = this;
89 +
    this.sourceControl.acceptInputCommand = { command: "oxygen.commit", title: "Commit", arguments: [this] };
90 +
    this.sourceControl.inputBox.placeholder = "Message (Ctrl+Enter to commit)";
91 +
    this.conflictsGroup = this.sourceControl.createResourceGroup("conflicts", "Conflicts");
92 +
    this.conflictsGroup.hideWhenEmpty = true;
93 +
    this.changesGroup = this.sourceControl.createResourceGroup("changes", "Changes");
94 +
    this.statusBar = vscode.window.createStatusBarItem("oxygen.status", vscode.StatusBarAlignment.Left, 100);
95 +
    this.statusBar.name = "Oxygen";
96 +
    this.statusBar.command = "oxygen.showLog";
97 +
    this.disposables.push(this.sourceControl, this.conflictsGroup, this.changesGroup, this.statusBar, this.onDidChangeStatusEmitter);
98 +
99 +
    if (vscode.workspace.getConfiguration("oxygen").get<boolean>("autoRefresh", true)) {
100 +
      // Files in the workspace, except the repository's own store.
101 +
      const files = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(rootUri, "**/*"));
102 +
      const onFile = (uri: vscode.Uri) => {
103 +
        if (!uri.fsPath.startsWith(join(root, ".oxy"))) {
104 +
          this.scheduleRefresh();
105 +
        }
106 +
      };
107 +
      files.onDidChange(onFile, this, this.disposables);
108 +
      files.onDidCreate(onFile, this, this.disposables);
109 +
      files.onDidDelete(onFile, this, this.disposables);
110 +
      this.disposables.push(files);
111 +
      // Any operation, including ones run from a terminal, moves the op head.
112 +
      const ops = vscode.workspace.createFileSystemWatcher(new vscode.RelativePattern(rootUri, ".oxy/ops/head"));
113 +
      ops.onDidChange(() => this.scheduleRefresh(), this, this.disposables);
114 +
      ops.onDidCreate(() => this.scheduleRefresh(), this, this.disposables);
115 +
      this.disposables.push(ops);
116 +
    }
117 +
    vscode.window.onDidChangeWindowState((s) => s.focused && this.scheduleRefresh(), this, this.disposables);
118 +
  }
119 +
120 +
  get status(): StatusInfo | undefined {
121 +
    return this.latest;
122 +
  }
123 +
124 +
  /** The repository-relative path of a file under this root, or undefined. */
125 +
  relativePath(uri: vscode.Uri): string | undefined {
126 +
    if (uri.scheme !== "file") {
127 +
      return undefined;
128 +
    }
129 +
    const rel = relative(this.root, uri.fsPath);
130 +
    if (!rel || rel.startsWith("..") || rel.startsWith(".oxy")) {
131 +
      return undefined;
132 +
    }
133 +
    return rel.split("\\").join("/");
134 +
  }
135 +
136 +
  scheduleRefresh(): void {
137 +
    if (this.refreshTimer) {
138 +
      clearTimeout(this.refreshTimer);
139 +
    }
140 +
    this.refreshTimer = setTimeout(() => void this.refresh(), 250);
141 +
  }
142 +
143 +
  /** Refresh now; a refresh requested during one runs once more afterwards. */
144 +
  refresh(): Promise<void> {
145 +
    if (this.refreshing) {
146 +
      this.refreshAgain = true;
147 +
      return this.refreshing;
148 +
    }
149 +
    this.refreshing = this.doRefresh().finally(() => {
150 +
      this.refreshing = undefined;
151 +
      if (this.refreshAgain) {
152 +
        this.refreshAgain = false;
153 +
        void this.refresh();
154 +
      }
155 +
    });
156 +
    return this.refreshing;
157 +
  }
158 +
159 +
  private async doRefresh(): Promise<void> {
160 +
    try {
161 +
      const [status, prompt] = await Promise.all([this.oxy.status(this.root), this.oxy.prompt(this.root)]);
162 +
      this.latest = status;
163 +
      const conflicted = new Set(status.conflicts.map((c) => c.path).filter((p): p is string => !!p));
164 +
      const conflicts: OxyResourceState[] = [];
165 +
      const changes: OxyResourceState[] = [];
166 +
      for (const p of status.paths) {
167 +
        const state = new OxyResourceState(this, p.path, p.status);
168 +
        if (p.status === "ConflictEdited" || p.status === "Resolved" || conflicted.has(p.path)) {
169 +
          conflicts.push(state);
170 +
        } else {
171 +
          changes.push(state);
172 +
        }
173 +
      }
174 +
      // Conflicted files the working copy has not touched yet still need resolving.
175 +
      for (const c of status.conflicts) {
176 +
        if (c.path && !status.paths.some((p) => p.path === c.path)) {
177 +
          conflicts.push(new OxyResourceState(this, c.path, "ConflictEdited"));
178 +
        }
179 +
      }
180 +
      this.conflictsGroup.resourceStates = conflicts;
181 +
      this.changesGroup.resourceStates = changes;
182 +
      this.sourceControl.count = status.paths.length + (status.conflicts.length ? status.conflicts.length : 0);
183 +
      this.updateStatusBar(status, prompt);
184 +
      this.onDidChangeStatusEmitter.fire(status);
185 +
    } catch (e) {
186 +
      this.statusBar.text = "$(warning) oxy";
187 +
      this.statusBar.tooltip = String(e);
188 +
      this.statusBar.show();
189 +
    }
190 +
  }
191 +
192 +
  private updateStatusBar(status: StatusInfo, prompt: string): void {
193 +
    if (!vscode.workspace.getConfiguration("oxygen").get<boolean>("statusBar.enabled", true)) {
194 +
      this.statusBar.hide();
195 +
      return;
196 +
    }
197 +
    const text = prompt || `${status.bookmark} ${status.working_copy.slice(0, 8)}`;
198 +
    this.statusBar.text = text;
199 +
    this.statusBar.tooltip = new vscode.MarkdownString(
200 +
      `**Oxygen** ${basename(this.root)}\n\n` +
201 +
        `Bookmark \`${status.bookmark}\` with ${status.heads} head${status.heads === 1 ? "" : "s"}\n\n` +
202 +
        (status.conflicted ? `$(warning) The state has conflicts\n\n` : "") +
203 +
        `${status.paths.length} changed path${status.paths.length === 1 ? "" : "s"}`,
204 +
      true,
205 +
    );
206 +
    this.statusBar.backgroundColor = status.conflicted ? new vscode.ThemeColor("statusBarItem.errorBackground") : undefined;
207 +
    this.statusBar.show();
208 +
  }
209 +
210 +
  // QuickDiffProvider: the base version of a file, served from oxy.
211 +
  provideOriginalResource(uri: vscode.Uri): vscode.Uri | undefined {
212 +
    const rel = this.relativePath(uri);
213 +
    if (!rel) {
214 +
      return undefined;
215 +
    }
216 +
    const status = this.latest?.paths.find((p) => p.path === rel)?.status;
217 +
    if (status === "Added") {
218 +
      return undefined;
219 +
    }
220 +
    return baseUri(this.root, rel);
221 +
  }
222 +
223 +
  dispose(): void {
224 +
    if (this.refreshTimer) {
225 +
      clearTimeout(this.refreshTimer);
226 +
    }
227 +
    for (const d of this.disposables) {
228 +
      d.dispose();
229 +
    }
230 +
  }
231 +
}
added test/oxy.test.mjs +47 −0
1 +
// The process wrapper against the real oxy binary on a scratch repository.
2 +
// Runs with `npm test`; needs oxy on PATH, at ~/.local/bin/oxy, or in OXY_BIN.
3 +
4 +
import { test } from "node:test";
5 +
import assert from "node:assert/strict";
6 +
import { mkdtempSync, writeFileSync, mkdirSync } from "node:fs";
7 +
import { tmpdir } from "node:os";
8 +
import { join } from "node:path";
9 +
import { Oxy, findBinary } from "../out/oxy.mjs";
10 +
11 +
const binary = process.env.OXY_BIN ?? findBinary(undefined);
12 +
const oxy = new Oxy(binary);
13 +
14 +
test("root, status, cat, log and commit round trip", async () => {
15 +
  const dir = mkdtempSync(join(tmpdir(), "oxy-vscode-"));
16 +
  const home = join(dir, "home");
17 +
  mkdirSync(home);
18 +
  process.env.OXY_HOME = home;
19 +
  await oxy.check(dir, ["init", "repo"]);
20 +
  const root = join(dir, "repo");
21 +
  await oxy.check(root, ["identity", "create", "--name", "Test", "--email", "[email protected]"]);
22 +
23 +
  const version = await oxy.version();
24 +
  assert.equal(version.protocol, 1);
25 +
  assert.equal(await oxy.root(dir), null, "outside a repository");
26 +
  const info = await oxy.root(root);
27 +
  assert.equal(info?.bookmark, "main");
28 +
  assert.ok(info?.root.endsWith("repo"));
29 +
30 +
  writeFileSync(join(root, "a.txt"), "one\n");
31 +
  let status = await oxy.status(root);
32 +
  assert.deepEqual(status.paths, [{ path: "a.txt", status: "Added" }]);
33 +
  assert.equal(status.clean, false);
34 +
  await oxy.commit(root, "first");
35 +
  status = await oxy.status(root);
36 +
  assert.equal(status.clean, true);
37 +
38 +
  writeFileSync(join(root, "a.txt"), "one\ntwo\n");
39 +
  assert.equal((await oxy.cat(root, "a.txt")).toString(), "one\n", "the base version");
40 +
  const log = await oxy.log(root, 10);
41 +
  assert.equal(log.changes.filter((c) => !c.working_copy).length, 1);
42 +
  assert.equal(log.changes.find((c) => !c.working_copy)?.signature?.status, "unsigned");
43 +
  assert.ok((await oxy.prompt(root)).includes("main"));
44 +
  const bookmarks = await oxy.bookmarks(root);
45 +
  assert.deepEqual(bookmarks.map((b) => [b.name, b.current]), [["main", true]]);
46 +
  await assert.rejects(oxy.cat(root, "missing.txt"), /not in that state/);
47 +
});
added tsconfig.json +18 −0
1 +
{
2 +
  "compilerOptions": {
3 +
    "target": "ES2022",
4 +
    "module": "Node16",
5 +
    "moduleResolution": "Node16",
6 +
    "lib": ["ES2022"],
7 +
    "strict": true,
8 +
    "noImplicitReturns": true,
9 +
    "noUnusedLocals": true,
10 +
    "noFallthroughCasesInSwitch": true,
11 +
    "esModuleInterop": true,
12 +
    "skipLibCheck": true,
13 +
    "sourceMap": true,
14 +
    "outDir": "out",
15 +
    "rootDir": "src"
16 +
  },
17 +
  "include": ["src"]
18 +
}