forked from farhoodlabs/paperclip
refactor(skills): replace per-host REST shims with git wire protocol
The skill import/update/file-read pipeline talked to host-specific REST
APIs (GitHub /commits/{ref}, /git/trees/{sha}, raw.githubusercontent.com)
and the recent Gitea support was a parallel shim on top of the same
pattern. The result was multiple ref-resolution shapes that needed
per-host branching, and on Gitea the /commits/{ref} endpoint returns
404 outright -- so even public Gitea/Forgejo repos failed to import.
Replace with a single git-source module backed by isomorphic-git +
memfs. It speaks the smart-HTTP protocol any sane git server already
serves:
- resolveGitRef: one listServerRefs call, no host API. Handles default
branch (symref on HEAD), named branches, annotated/lightweight tags,
and SHA passthrough.
- openRepoSnapshot: shallow singleBranch clone into an in-memory fs;
listFiles via git.walk, readFile via git.readBlob. No tempdirs, no
execFile, no per-host endpoints.
- Universal auth via onAuth (token-as-username) covering GitHub PATs,
GitLab PATs, Gitea/Forgejo tokens.
- parseGitSourceUrl recognises github tree/blob, gitea src/branch|
commit|tag, gitlab /-/tree, bitbucket /src/{ref} URL shapes plus
bare clone URLs.
Stored skill metadata is unchanged (hostname/owner/repo/ref/trackingRef/
repoSkillDir), so existing rows keep working -- the clone URL is
derived at fetch time.
company-portability.ts still imports github-fetch.ts (same broken
pattern, separate feature). Left as a follow-up.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Generated
+541
@@ -646,9 +646,15 @@ importers:
|
||||
hermes-paperclip-adapter:
|
||||
specifier: ^0.2.0
|
||||
version: 0.2.0
|
||||
isomorphic-git:
|
||||
specifier: ^1.38.0
|
||||
version: 1.38.0
|
||||
jsdom:
|
||||
specifier: ^28.1.0
|
||||
version: 28.1.0(@noble/hashes@2.0.1)
|
||||
memfs:
|
||||
specifier: ^4.57.2
|
||||
version: 4.57.2(tslib@2.8.1)
|
||||
multer:
|
||||
specifier: ^2.1.1
|
||||
version: 2.1.1
|
||||
@@ -2246,6 +2252,126 @@ packages:
|
||||
'@jridgewell/trace-mapping@0.3.31':
|
||||
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
|
||||
|
||||
'@jsonjoy.com/base64@1.1.2':
|
||||
resolution: {integrity: sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==}
|
||||
engines: {node: '>=10.0'}
|
||||
peerDependencies:
|
||||
tslib: '2'
|
||||
|
||||
'@jsonjoy.com/base64@17.67.0':
|
||||
resolution: {integrity: sha512-5SEsJGsm15aP8TQGkDfJvz9axgPwAEm98S5DxOuYe8e1EbfajcDmgeXXzccEjh+mLnjqEKrkBdjHWS5vFNwDdw==}
|
||||
engines: {node: '>=10.0'}
|
||||
peerDependencies:
|
||||
tslib: '2'
|
||||
|
||||
'@jsonjoy.com/buffers@1.2.1':
|
||||
resolution: {integrity: sha512-12cdlDwX4RUM3QxmUbVJWqZ/mrK6dFQH4Zxq6+r1YXKXYBNgZXndx2qbCJwh3+WWkCSn67IjnlG3XYTvmvYtgA==}
|
||||
engines: {node: '>=10.0'}
|
||||
peerDependencies:
|
||||
tslib: '2'
|
||||
|
||||
'@jsonjoy.com/buffers@17.67.0':
|
||||
resolution: {integrity: sha512-tfExRpYxBvi32vPs9ZHaTjSP4fHAfzSmcahOfNxtvGHcyJel+aibkPlGeBB+7AoC6hL7lXIE++8okecBxx7lcw==}
|
||||
engines: {node: '>=10.0'}
|
||||
peerDependencies:
|
||||
tslib: '2'
|
||||
|
||||
'@jsonjoy.com/codegen@1.0.0':
|
||||
resolution: {integrity: sha512-E8Oy+08cmCf0EK/NMxpaJZmOxPqM+6iSe2S4nlSBrPZOORoDJILxtbSUEDKQyTamm/BVAhIGllOBNU79/dwf0g==}
|
||||
engines: {node: '>=10.0'}
|
||||
peerDependencies:
|
||||
tslib: '2'
|
||||
|
||||
'@jsonjoy.com/codegen@17.67.0':
|
||||
resolution: {integrity: sha512-idnkUplROpdBOV0HMcwhsCUS5TRUi9poagdGs70A6S4ux9+/aPuKbh8+UYRTLYQHtXvAdNfQWXDqZEx5k4Dj2Q==}
|
||||
engines: {node: '>=10.0'}
|
||||
peerDependencies:
|
||||
tslib: '2'
|
||||
|
||||
'@jsonjoy.com/fs-core@4.57.2':
|
||||
resolution: {integrity: sha512-SVjwklkpIV5wrynpYtuYnfYH1QF4/nDuLBX7VXdb+3miglcAgBVZb/5y0cOsehRV/9Vb+3UqhkMq3/NR3ztdkQ==}
|
||||
engines: {node: '>=10.0'}
|
||||
peerDependencies:
|
||||
tslib: '2'
|
||||
|
||||
'@jsonjoy.com/fs-fsa@4.57.2':
|
||||
resolution: {integrity: sha512-fhO8+iR2I+OCw668ISDJdn1aArc9zx033sWejIyzQ8RBeXa9bDSaUeA3ix0poYOfrj1KdOzytmYNv2/uLDfV6g==}
|
||||
engines: {node: '>=10.0'}
|
||||
peerDependencies:
|
||||
tslib: '2'
|
||||
|
||||
'@jsonjoy.com/fs-node-builtins@4.57.2':
|
||||
resolution: {integrity: sha512-xhiegylRmhw43Ki2HO1ZBL7DQ5ja/qpRsL29VtQ2xuUHiuDGbgf2uD4p9Qd8hJI5P6RCtGYD50IXHXVq/Ocjcg==}
|
||||
engines: {node: '>=10.0'}
|
||||
peerDependencies:
|
||||
tslib: '2'
|
||||
|
||||
'@jsonjoy.com/fs-node-to-fsa@4.57.2':
|
||||
resolution: {integrity: sha512-18LmWTSONhoAPW+IWRuf8w/+zRolPFGPeGwMxlAhhfY11EKzX+5XHDBPAw67dBF5dxDErHJbl40U+3IXSDRXSQ==}
|
||||
engines: {node: '>=10.0'}
|
||||
peerDependencies:
|
||||
tslib: '2'
|
||||
|
||||
'@jsonjoy.com/fs-node-utils@4.57.2':
|
||||
resolution: {integrity: sha512-rsPSJgekz43IlNbLyAM/Ab+ouYLWGp5DDBfYBNNEqDaSpsbXfthBn29Q4muFA9L0F+Z3mKo+CWlgSCXrf+mOyQ==}
|
||||
engines: {node: '>=10.0'}
|
||||
peerDependencies:
|
||||
tslib: '2'
|
||||
|
||||
'@jsonjoy.com/fs-node@4.57.2':
|
||||
resolution: {integrity: sha512-nX2AdL6cOFwLdju9G4/nbRnYevmCJbh7N7hvR3gGm97Cs60uEjyd0rpR+YBS7cTg175zzl22pGKXR5USaQMvKg==}
|
||||
engines: {node: '>=10.0'}
|
||||
peerDependencies:
|
||||
tslib: '2'
|
||||
|
||||
'@jsonjoy.com/fs-print@4.57.2':
|
||||
resolution: {integrity: sha512-wK9NSow48i4DbDl9F1CQE5TqnyZOJ04elU3WFG5aJ76p+YxO/ulyBBQvKsessPxdo381Bc2pcEoyPujMOhcRqQ==}
|
||||
engines: {node: '>=10.0'}
|
||||
peerDependencies:
|
||||
tslib: '2'
|
||||
|
||||
'@jsonjoy.com/fs-snapshot@4.57.2':
|
||||
resolution: {integrity: sha512-GdduDZuoP5V/QCgJkx9+BZ6SC0EZ/smXAdTS7PfMqgMTGXLlt/bH/FqMYaqB9JmLf05sJPtO0XRbAwwkEEPbVw==}
|
||||
engines: {node: '>=10.0'}
|
||||
peerDependencies:
|
||||
tslib: '2'
|
||||
|
||||
'@jsonjoy.com/json-pack@1.21.0':
|
||||
resolution: {integrity: sha512-+AKG+R2cfZMShzrF2uQw34v3zbeDYUqnQ+jg7ORic3BGtfw9p/+N6RJbq/kkV8JmYZaINknaEQ2m0/f693ZPpg==}
|
||||
engines: {node: '>=10.0'}
|
||||
peerDependencies:
|
||||
tslib: '2'
|
||||
|
||||
'@jsonjoy.com/json-pack@17.67.0':
|
||||
resolution: {integrity: sha512-t0ejURcGaZsn1ClbJ/3kFqSOjlryd92eQY465IYrezsXmPcfHPE/av4twRSxf6WE+TkZgLY+71vCZbiIiFKA/w==}
|
||||
engines: {node: '>=10.0'}
|
||||
peerDependencies:
|
||||
tslib: '2'
|
||||
|
||||
'@jsonjoy.com/json-pointer@1.0.2':
|
||||
resolution: {integrity: sha512-Fsn6wM2zlDzY1U+v4Nc8bo3bVqgfNTGcn6dMgs6FjrEnt4ZCe60o6ByKRjOGlI2gow0aE/Q41QOigdTqkyK5fg==}
|
||||
engines: {node: '>=10.0'}
|
||||
peerDependencies:
|
||||
tslib: '2'
|
||||
|
||||
'@jsonjoy.com/json-pointer@17.67.0':
|
||||
resolution: {integrity: sha512-+iqOFInH+QZGmSuaybBUNdh7yvNrXvqR+h3wjXm0N/3JK1EyyFAeGJvqnmQL61d1ARLlk/wJdFKSL+LHJ1eaUA==}
|
||||
engines: {node: '>=10.0'}
|
||||
peerDependencies:
|
||||
tslib: '2'
|
||||
|
||||
'@jsonjoy.com/util@1.9.0':
|
||||
resolution: {integrity: sha512-pLuQo+VPRnN8hfPqUTLTHk126wuYdXVxE6aDmjSeV4NCAgyxWbiOIeNJVtID3h1Vzpoi9m4jXezf73I6LgabgQ==}
|
||||
engines: {node: '>=10.0'}
|
||||
peerDependencies:
|
||||
tslib: '2'
|
||||
|
||||
'@jsonjoy.com/util@17.67.0':
|
||||
resolution: {integrity: sha512-6+8xBaz1rLSohlGh68D1pdw3AwDi9xydm8QNlAFkvnavCJYSze+pxoW2VKP8p308jtlMRLs5NTHfPlZLd4w7ew==}
|
||||
engines: {node: '>=10.0'}
|
||||
peerDependencies:
|
||||
tslib: '2'
|
||||
|
||||
'@lexical/clipboard@0.35.0':
|
||||
resolution: {integrity: sha512-ko7xSIIiayvDiqjNDX6fgH9RlcM6r9vrrvJYTcfGVBor5httx16lhIi0QJZ4+RNPvGtTjyFv4bwRmsixRRwImg==}
|
||||
|
||||
@@ -4067,6 +4193,10 @@ packages:
|
||||
abbrev@1.1.1:
|
||||
resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==}
|
||||
|
||||
abort-controller@3.0.0:
|
||||
resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
|
||||
engines: {node: '>=6.5'}
|
||||
|
||||
accepts@2.0.0:
|
||||
resolution: {integrity: sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==}
|
||||
engines: {node: '>= 0.6'}
|
||||
@@ -4174,6 +4304,9 @@ packages:
|
||||
resolution: {integrity: sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==}
|
||||
engines: {node: '>=0.12.0'}
|
||||
|
||||
async-lock@1.4.1:
|
||||
resolution: {integrity: sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==}
|
||||
|
||||
asynckit@0.4.0:
|
||||
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
|
||||
|
||||
@@ -4181,6 +4314,10 @@ packages:
|
||||
resolution: {integrity: sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==}
|
||||
engines: {node: '>=8.0.0'}
|
||||
|
||||
available-typed-arrays@1.0.7:
|
||||
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
axe-core@4.11.3:
|
||||
resolution: {integrity: sha512-zBQouZixDTbo3jMGqHKyePxYxr1e5W8UdTmBQ7sNtaA9M2bE32daxxPLS/jojhKOHxQ7LWwPjfiwf/fhaJWzlg==}
|
||||
engines: {node: '>=4'}
|
||||
@@ -4382,6 +4519,10 @@ packages:
|
||||
resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
call-bind@1.0.9:
|
||||
resolution: {integrity: sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
call-bound@1.0.4:
|
||||
resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -4437,6 +4578,9 @@ packages:
|
||||
classnames@2.5.1:
|
||||
resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==}
|
||||
|
||||
clean-git-ref@2.0.1:
|
||||
resolution: {integrity: sha512-bLSptAy2P0s6hU4PzuIMKmMJJSE6gLXGH1cntDu7bWJUksvuM+7ReOK61mozULErYvP6a15rnYl0zFDef+pyPw==}
|
||||
|
||||
clean-set@1.1.2:
|
||||
resolution: {integrity: sha512-cA8uCj0qSoG9e0kevyOWXwPaELRPVg5Pxp6WskLMwerx257Zfnh8Nl0JBH59d7wQzij2CK7qEfJQK3RjuKKIug==}
|
||||
|
||||
@@ -4550,6 +4694,11 @@ packages:
|
||||
cose-base@2.2.0:
|
||||
resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==}
|
||||
|
||||
crc-32@1.2.2:
|
||||
resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==}
|
||||
engines: {node: '>=0.8'}
|
||||
hasBin: true
|
||||
|
||||
crelt@1.0.6:
|
||||
resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==}
|
||||
|
||||
@@ -4790,6 +4939,10 @@ packages:
|
||||
resolution: {integrity: sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
define-data-property@1.1.4:
|
||||
resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
define-lazy-prop@3.0.0:
|
||||
resolution: {integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -4833,6 +4986,9 @@ packages:
|
||||
dezalgo@1.0.4:
|
||||
resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==}
|
||||
|
||||
diff3@0.0.3:
|
||||
resolution: {integrity: sha512-iSq8ngPOt0K53A6eVr4d5Kn6GNrM2nQZtC740pzIriHtn4pOQ2lyzEXQMBeVcWERN0ye7fhBsk9PbLLQOnUx/g==}
|
||||
|
||||
diff@5.2.2:
|
||||
resolution: {integrity: sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==}
|
||||
engines: {node: '>=0.3.1'}
|
||||
@@ -5102,9 +5258,17 @@ packages:
|
||||
event-emitter@0.3.5:
|
||||
resolution: {integrity: sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==}
|
||||
|
||||
event-target-shim@5.0.1:
|
||||
resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
events-universal@1.0.1:
|
||||
resolution: {integrity: sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==}
|
||||
|
||||
events@3.3.0:
|
||||
resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
|
||||
engines: {node: '>=0.8.x'}
|
||||
|
||||
eventsource-parser@3.0.6:
|
||||
resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
@@ -5184,6 +5348,10 @@ packages:
|
||||
resolution: {integrity: sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==}
|
||||
engines: {node: '>= 18.0.0'}
|
||||
|
||||
for-each@0.3.5:
|
||||
resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
form-data@4.0.5:
|
||||
resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==}
|
||||
engines: {node: '>= 6'}
|
||||
@@ -5258,6 +5426,12 @@ packages:
|
||||
github-from-package@0.0.0:
|
||||
resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==}
|
||||
|
||||
glob-to-regex.js@1.2.0:
|
||||
resolution: {integrity: sha512-QMwlOQKU/IzqMUOAZWubUOT8Qft+Y0KQWnX9nK3ch0CJg0tTp4TvGZsTfudYKv2NzoQSyPcnA6TYeIQ3jGichQ==}
|
||||
engines: {node: '>=10.0'}
|
||||
peerDependencies:
|
||||
tslib: '2'
|
||||
|
||||
glob@13.0.6:
|
||||
resolution: {integrity: sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==}
|
||||
engines: {node: 18 || 20 || >=22}
|
||||
@@ -5276,6 +5450,9 @@ packages:
|
||||
hachure-fill@0.5.2:
|
||||
resolution: {integrity: sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==}
|
||||
|
||||
has-property-descriptors@1.0.2:
|
||||
resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
|
||||
|
||||
has-symbols@1.1.0:
|
||||
resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -5341,6 +5518,10 @@ packages:
|
||||
humanize-ms@1.2.1:
|
||||
resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==}
|
||||
|
||||
hyperdyperid@1.2.0:
|
||||
resolution: {integrity: sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==}
|
||||
engines: {node: '>=10.18'}
|
||||
|
||||
iconv-lite@0.6.3:
|
||||
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@@ -5352,6 +5533,10 @@ packages:
|
||||
ieee754@1.2.1:
|
||||
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
|
||||
|
||||
ignore@5.3.2:
|
||||
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
|
||||
engines: {node: '>= 4'}
|
||||
|
||||
imurmurhash@0.1.4:
|
||||
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
|
||||
engines: {node: '>=0.8.19'}
|
||||
@@ -5405,6 +5590,10 @@ packages:
|
||||
is-alphanumerical@2.0.1:
|
||||
resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==}
|
||||
|
||||
is-callable@1.2.7:
|
||||
resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
is-core-module@2.16.1:
|
||||
resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==}
|
||||
engines: {node: '>= 0.4'}
|
||||
@@ -5449,13 +5638,25 @@ packages:
|
||||
is-promise@4.0.0:
|
||||
resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==}
|
||||
|
||||
is-typed-array@1.1.15:
|
||||
resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
is-wsl@3.1.1:
|
||||
resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
isarray@2.0.5:
|
||||
resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
|
||||
|
||||
isexe@2.0.0:
|
||||
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
||||
|
||||
isomorphic-git@1.38.0:
|
||||
resolution: {integrity: sha512-gsBFnAT8Fxrpx+53ymG5kEOHSrUDVcSMFl7fCEGVnPpQbPS0aKti3UzZXR+3DKA0yyf+4z6CXJxULlQ5QPxDJw==}
|
||||
engines: {node: '>=14.17'}
|
||||
hasBin: true
|
||||
|
||||
isomorphic.js@0.2.5:
|
||||
resolution: {integrity: sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==}
|
||||
|
||||
@@ -5732,6 +5933,11 @@ packages:
|
||||
resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==}
|
||||
engines: {node: '>= 0.8'}
|
||||
|
||||
memfs@4.57.2:
|
||||
resolution: {integrity: sha512-2nWzSsJzrukurSDna4Z0WywuScK4Id3tSKejgu74u8KCdW4uNrseKRSIDg75C6Yw5ZRqBe0F0EtMNlTbUq8bAQ==}
|
||||
peerDependencies:
|
||||
tslib: '2'
|
||||
|
||||
merge-descriptors@2.0.0:
|
||||
resolution: {integrity: sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -5896,6 +6102,9 @@ packages:
|
||||
minimist@1.2.8:
|
||||
resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
|
||||
|
||||
minimisted@2.0.1:
|
||||
resolution: {integrity: sha512-1oPjfuLQa2caorJUM8HV8lGgWCc0qqAO1MNv/k05G4qslmsndV/5WdNZrqCiyqiz3wohia2Ij2B7w2Dr7/IyrA==}
|
||||
|
||||
minipass-collect@1.0.2:
|
||||
resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==}
|
||||
engines: {node: '>= 8'}
|
||||
@@ -6044,6 +6253,9 @@ packages:
|
||||
package-manager-detector@1.6.0:
|
||||
resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==}
|
||||
|
||||
pako@1.0.11:
|
||||
resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
|
||||
|
||||
parse-entities@4.0.2:
|
||||
resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==}
|
||||
|
||||
@@ -6126,6 +6338,10 @@ packages:
|
||||
resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
pify@4.0.1:
|
||||
resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
pino-abstract-transport@2.0.0:
|
||||
resolution: {integrity: sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==}
|
||||
|
||||
@@ -6169,6 +6385,10 @@ packages:
|
||||
points-on-path@0.2.1:
|
||||
resolution: {integrity: sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==}
|
||||
|
||||
possible-typed-array-names@1.1.0:
|
||||
resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
postcss-selector-parser@6.0.10:
|
||||
resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==}
|
||||
engines: {node: '>=4'}
|
||||
@@ -6218,6 +6438,10 @@ packages:
|
||||
process-warning@5.0.0:
|
||||
resolution: {integrity: sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==}
|
||||
|
||||
process@0.11.10:
|
||||
resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
|
||||
engines: {node: '>= 0.6.0'}
|
||||
|
||||
promise-inflight@1.0.1:
|
||||
resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==}
|
||||
peerDependencies:
|
||||
@@ -6385,6 +6609,10 @@ packages:
|
||||
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
readable-stream@4.7.0:
|
||||
resolution: {integrity: sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
|
||||
readdirp@4.1.2:
|
||||
resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
|
||||
engines: {node: '>= 14.18.0'}
|
||||
@@ -6506,9 +6734,18 @@ packages:
|
||||
set-cookie-parser@2.7.2:
|
||||
resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==}
|
||||
|
||||
set-function-length@1.2.2:
|
||||
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
setprototypeof@1.2.0:
|
||||
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
|
||||
|
||||
sha.js@2.4.12:
|
||||
resolution: {integrity: sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==}
|
||||
engines: {node: '>= 0.10'}
|
||||
hasBin: true
|
||||
|
||||
sharp@0.34.5:
|
||||
resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==}
|
||||
engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
|
||||
@@ -6730,6 +6967,12 @@ packages:
|
||||
text-decoder@1.2.7:
|
||||
resolution: {integrity: sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==}
|
||||
|
||||
thingies@2.6.0:
|
||||
resolution: {integrity: sha512-rMHRjmlFLM1R96UYPvpmnc3LYtdFrT33JIB7L9hetGue1qAPfn1N2LJeEjxUSidu1Iku+haLZXDuEXUHNGO/lg==}
|
||||
engines: {node: '>=10.18'}
|
||||
peerDependencies:
|
||||
tslib: ^2
|
||||
|
||||
thread-stream@3.1.0:
|
||||
resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==}
|
||||
|
||||
@@ -6769,6 +7012,10 @@ packages:
|
||||
resolution: {integrity: sha512-WiGwQjr0qYdNNG8KpMKlSvpxz652lqa3Rd+/hSaDcY4Uo6SKWZq2LAF+hsAhUewTtYhXlorBKgNF3Kk8hnjGoQ==}
|
||||
hasBin: true
|
||||
|
||||
to-buffer@1.2.2:
|
||||
resolution: {integrity: sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
toidentifier@1.0.1:
|
||||
resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
|
||||
engines: {node: '>=0.6'}
|
||||
@@ -6781,6 +7028,12 @@ packages:
|
||||
resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==}
|
||||
engines: {node: '>=20'}
|
||||
|
||||
tree-dump@1.1.0:
|
||||
resolution: {integrity: sha512-rMuvhU4MCDbcbnleZTFezWsaZXRFemSqAM+7jPnzUl1fo9w3YEKOxAeui0fz3OI4EU4hf23iyA7uQRVko+UaBA==}
|
||||
engines: {node: '>=10.0'}
|
||||
peerDependencies:
|
||||
tslib: '2'
|
||||
|
||||
trim-lines@3.0.1:
|
||||
resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==}
|
||||
|
||||
@@ -6820,6 +7073,10 @@ packages:
|
||||
type@2.7.3:
|
||||
resolution: {integrity: sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==}
|
||||
|
||||
typed-array-buffer@1.0.3:
|
||||
resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
typedarray@0.0.6:
|
||||
resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==}
|
||||
|
||||
@@ -7126,6 +7383,10 @@ packages:
|
||||
resolution: {integrity: sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==}
|
||||
engines: {node: ^20.19.0 || ^22.12.0 || >=24.0.0}
|
||||
|
||||
which-typed-array@1.1.20:
|
||||
resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
which@2.0.2:
|
||||
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
|
||||
engines: {node: '>= 8'}
|
||||
@@ -8860,6 +9121,133 @@ snapshots:
|
||||
'@jridgewell/resolve-uri': 3.1.2
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
||||
'@jsonjoy.com/base64@1.1.2(tslib@2.8.1)':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
'@jsonjoy.com/base64@17.67.0(tslib@2.8.1)':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
'@jsonjoy.com/buffers@1.2.1(tslib@2.8.1)':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
'@jsonjoy.com/buffers@17.67.0(tslib@2.8.1)':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
'@jsonjoy.com/codegen@1.0.0(tslib@2.8.1)':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
'@jsonjoy.com/codegen@17.67.0(tslib@2.8.1)':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
'@jsonjoy.com/fs-core@4.57.2(tslib@2.8.1)':
|
||||
dependencies:
|
||||
'@jsonjoy.com/fs-node-builtins': 4.57.2(tslib@2.8.1)
|
||||
'@jsonjoy.com/fs-node-utils': 4.57.2(tslib@2.8.1)
|
||||
thingies: 2.6.0(tslib@2.8.1)
|
||||
tslib: 2.8.1
|
||||
|
||||
'@jsonjoy.com/fs-fsa@4.57.2(tslib@2.8.1)':
|
||||
dependencies:
|
||||
'@jsonjoy.com/fs-core': 4.57.2(tslib@2.8.1)
|
||||
'@jsonjoy.com/fs-node-builtins': 4.57.2(tslib@2.8.1)
|
||||
'@jsonjoy.com/fs-node-utils': 4.57.2(tslib@2.8.1)
|
||||
thingies: 2.6.0(tslib@2.8.1)
|
||||
tslib: 2.8.1
|
||||
|
||||
'@jsonjoy.com/fs-node-builtins@4.57.2(tslib@2.8.1)':
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
'@jsonjoy.com/fs-node-to-fsa@4.57.2(tslib@2.8.1)':
|
||||
dependencies:
|
||||
'@jsonjoy.com/fs-fsa': 4.57.2(tslib@2.8.1)
|
||||
'@jsonjoy.com/fs-node-builtins': 4.57.2(tslib@2.8.1)
|
||||
'@jsonjoy.com/fs-node-utils': 4.57.2(tslib@2.8.1)
|
||||
tslib: 2.8.1
|
||||
|
||||
'@jsonjoy.com/fs-node-utils@4.57.2(tslib@2.8.1)':
|
||||
dependencies:
|
||||
'@jsonjoy.com/fs-node-builtins': 4.57.2(tslib@2.8.1)
|
||||
tslib: 2.8.1
|
||||
|
||||
'@jsonjoy.com/fs-node@4.57.2(tslib@2.8.1)':
|
||||
dependencies:
|
||||
'@jsonjoy.com/fs-core': 4.57.2(tslib@2.8.1)
|
||||
'@jsonjoy.com/fs-node-builtins': 4.57.2(tslib@2.8.1)
|
||||
'@jsonjoy.com/fs-node-utils': 4.57.2(tslib@2.8.1)
|
||||
'@jsonjoy.com/fs-print': 4.57.2(tslib@2.8.1)
|
||||
'@jsonjoy.com/fs-snapshot': 4.57.2(tslib@2.8.1)
|
||||
glob-to-regex.js: 1.2.0(tslib@2.8.1)
|
||||
thingies: 2.6.0(tslib@2.8.1)
|
||||
tslib: 2.8.1
|
||||
|
||||
'@jsonjoy.com/fs-print@4.57.2(tslib@2.8.1)':
|
||||
dependencies:
|
||||
'@jsonjoy.com/fs-node-utils': 4.57.2(tslib@2.8.1)
|
||||
tree-dump: 1.1.0(tslib@2.8.1)
|
||||
tslib: 2.8.1
|
||||
|
||||
'@jsonjoy.com/fs-snapshot@4.57.2(tslib@2.8.1)':
|
||||
dependencies:
|
||||
'@jsonjoy.com/buffers': 17.67.0(tslib@2.8.1)
|
||||
'@jsonjoy.com/fs-node-utils': 4.57.2(tslib@2.8.1)
|
||||
'@jsonjoy.com/json-pack': 17.67.0(tslib@2.8.1)
|
||||
'@jsonjoy.com/util': 17.67.0(tslib@2.8.1)
|
||||
tslib: 2.8.1
|
||||
|
||||
'@jsonjoy.com/json-pack@1.21.0(tslib@2.8.1)':
|
||||
dependencies:
|
||||
'@jsonjoy.com/base64': 1.1.2(tslib@2.8.1)
|
||||
'@jsonjoy.com/buffers': 1.2.1(tslib@2.8.1)
|
||||
'@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1)
|
||||
'@jsonjoy.com/json-pointer': 1.0.2(tslib@2.8.1)
|
||||
'@jsonjoy.com/util': 1.9.0(tslib@2.8.1)
|
||||
hyperdyperid: 1.2.0
|
||||
thingies: 2.6.0(tslib@2.8.1)
|
||||
tree-dump: 1.1.0(tslib@2.8.1)
|
||||
tslib: 2.8.1
|
||||
|
||||
'@jsonjoy.com/json-pack@17.67.0(tslib@2.8.1)':
|
||||
dependencies:
|
||||
'@jsonjoy.com/base64': 17.67.0(tslib@2.8.1)
|
||||
'@jsonjoy.com/buffers': 17.67.0(tslib@2.8.1)
|
||||
'@jsonjoy.com/codegen': 17.67.0(tslib@2.8.1)
|
||||
'@jsonjoy.com/json-pointer': 17.67.0(tslib@2.8.1)
|
||||
'@jsonjoy.com/util': 17.67.0(tslib@2.8.1)
|
||||
hyperdyperid: 1.2.0
|
||||
thingies: 2.6.0(tslib@2.8.1)
|
||||
tree-dump: 1.1.0(tslib@2.8.1)
|
||||
tslib: 2.8.1
|
||||
|
||||
'@jsonjoy.com/json-pointer@1.0.2(tslib@2.8.1)':
|
||||
dependencies:
|
||||
'@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1)
|
||||
'@jsonjoy.com/util': 1.9.0(tslib@2.8.1)
|
||||
tslib: 2.8.1
|
||||
|
||||
'@jsonjoy.com/json-pointer@17.67.0(tslib@2.8.1)':
|
||||
dependencies:
|
||||
'@jsonjoy.com/util': 17.67.0(tslib@2.8.1)
|
||||
tslib: 2.8.1
|
||||
|
||||
'@jsonjoy.com/util@1.9.0(tslib@2.8.1)':
|
||||
dependencies:
|
||||
'@jsonjoy.com/buffers': 1.2.1(tslib@2.8.1)
|
||||
'@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1)
|
||||
tslib: 2.8.1
|
||||
|
||||
'@jsonjoy.com/util@17.67.0(tslib@2.8.1)':
|
||||
dependencies:
|
||||
'@jsonjoy.com/buffers': 17.67.0(tslib@2.8.1)
|
||||
'@jsonjoy.com/codegen': 17.67.0(tslib@2.8.1)
|
||||
tslib: 2.8.1
|
||||
|
||||
'@lexical/clipboard@0.35.0':
|
||||
dependencies:
|
||||
'@lexical/html': 0.35.0
|
||||
@@ -11046,6 +11434,10 @@ snapshots:
|
||||
abbrev@1.1.1:
|
||||
optional: true
|
||||
|
||||
abort-controller@3.0.0:
|
||||
dependencies:
|
||||
event-target-shim: 5.0.1
|
||||
|
||||
accepts@2.0.0:
|
||||
dependencies:
|
||||
mime-types: 3.0.2
|
||||
@@ -11151,10 +11543,16 @@ snapshots:
|
||||
|
||||
async-exit-hook@2.0.1: {}
|
||||
|
||||
async-lock@1.4.1: {}
|
||||
|
||||
asynckit@0.4.0: {}
|
||||
|
||||
atomic-sleep@1.0.0: {}
|
||||
|
||||
available-typed-arrays@1.0.7:
|
||||
dependencies:
|
||||
possible-typed-array-names: 1.1.0
|
||||
|
||||
axe-core@4.11.3: {}
|
||||
|
||||
b4a@1.8.1: {}
|
||||
@@ -11334,6 +11732,13 @@ snapshots:
|
||||
es-errors: 1.3.0
|
||||
function-bind: 1.1.2
|
||||
|
||||
call-bind@1.0.9:
|
||||
dependencies:
|
||||
call-bind-apply-helpers: 1.0.2
|
||||
es-define-property: 1.0.1
|
||||
get-intrinsic: 1.3.0
|
||||
set-function-length: 1.2.2
|
||||
|
||||
call-bound@1.0.4:
|
||||
dependencies:
|
||||
call-bind-apply-helpers: 1.0.2
|
||||
@@ -11389,6 +11794,8 @@ snapshots:
|
||||
|
||||
classnames@2.5.1: {}
|
||||
|
||||
clean-git-ref@2.0.1: {}
|
||||
|
||||
clean-set@1.1.2: {}
|
||||
|
||||
clean-stack@2.2.0:
|
||||
@@ -11490,6 +11897,8 @@ snapshots:
|
||||
dependencies:
|
||||
layout-base: 2.0.1
|
||||
|
||||
crc-32@1.2.2: {}
|
||||
|
||||
crelt@1.0.6: {}
|
||||
|
||||
cross-env@10.1.0:
|
||||
@@ -11748,6 +12157,12 @@ snapshots:
|
||||
bundle-name: 4.1.0
|
||||
default-browser-id: 5.0.1
|
||||
|
||||
define-data-property@1.1.4:
|
||||
dependencies:
|
||||
es-define-property: 1.0.1
|
||||
es-errors: 1.3.0
|
||||
gopd: 1.2.0
|
||||
|
||||
define-lazy-prop@3.0.0: {}
|
||||
|
||||
defu@6.1.4: {}
|
||||
@@ -11782,6 +12197,8 @@ snapshots:
|
||||
asap: 2.0.6
|
||||
wrappy: 1.0.2
|
||||
|
||||
diff3@0.0.3: {}
|
||||
|
||||
diff@5.2.2: {}
|
||||
|
||||
doctrine@3.0.0:
|
||||
@@ -12045,12 +12462,16 @@ snapshots:
|
||||
d: 1.0.2
|
||||
es5-ext: 0.10.64
|
||||
|
||||
event-target-shim@5.0.1: {}
|
||||
|
||||
events-universal@1.0.1:
|
||||
dependencies:
|
||||
bare-events: 2.8.2
|
||||
transitivePeerDependencies:
|
||||
- bare-abort-controller
|
||||
|
||||
events@3.3.0: {}
|
||||
|
||||
eventsource-parser@3.0.6: {}
|
||||
|
||||
eventsource@3.0.7:
|
||||
@@ -12150,6 +12571,10 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
for-each@0.3.5:
|
||||
dependencies:
|
||||
is-callable: 1.2.7
|
||||
|
||||
form-data@4.0.5:
|
||||
dependencies:
|
||||
asynckit: 0.4.0
|
||||
@@ -12229,6 +12654,10 @@ snapshots:
|
||||
|
||||
github-from-package@0.0.0: {}
|
||||
|
||||
glob-to-regex.js@1.2.0(tslib@2.8.1):
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
glob@13.0.6:
|
||||
dependencies:
|
||||
minimatch: 10.2.5
|
||||
@@ -12251,6 +12680,10 @@ snapshots:
|
||||
|
||||
hachure-fill@0.5.2: {}
|
||||
|
||||
has-property-descriptors@1.0.2:
|
||||
dependencies:
|
||||
es-define-property: 1.0.1
|
||||
|
||||
has-symbols@1.1.0: {}
|
||||
|
||||
has-tostringtag@1.0.2:
|
||||
@@ -12352,6 +12785,8 @@ snapshots:
|
||||
ms: 2.1.3
|
||||
optional: true
|
||||
|
||||
hyperdyperid@1.2.0: {}
|
||||
|
||||
iconv-lite@0.6.3:
|
||||
dependencies:
|
||||
safer-buffer: 2.1.2
|
||||
@@ -12362,6 +12797,8 @@ snapshots:
|
||||
|
||||
ieee754@1.2.1: {}
|
||||
|
||||
ignore@5.3.2: {}
|
||||
|
||||
imurmurhash@0.1.4:
|
||||
optional: true
|
||||
|
||||
@@ -12402,6 +12839,8 @@ snapshots:
|
||||
is-alphabetical: 2.0.1
|
||||
is-decimal: 2.0.1
|
||||
|
||||
is-callable@1.2.7: {}
|
||||
|
||||
is-core-module@2.16.1:
|
||||
dependencies:
|
||||
hasown: 2.0.2
|
||||
@@ -12432,12 +12871,32 @@ snapshots:
|
||||
|
||||
is-promise@4.0.0: {}
|
||||
|
||||
is-typed-array@1.1.15:
|
||||
dependencies:
|
||||
which-typed-array: 1.1.20
|
||||
|
||||
is-wsl@3.1.1:
|
||||
dependencies:
|
||||
is-inside-container: 1.0.0
|
||||
|
||||
isarray@2.0.5: {}
|
||||
|
||||
isexe@2.0.0: {}
|
||||
|
||||
isomorphic-git@1.38.0:
|
||||
dependencies:
|
||||
async-lock: 1.4.1
|
||||
clean-git-ref: 2.0.1
|
||||
crc-32: 1.2.2
|
||||
diff3: 0.0.3
|
||||
ignore: 5.3.2
|
||||
minimisted: 2.0.1
|
||||
pako: 1.0.11
|
||||
pify: 4.0.1
|
||||
readable-stream: 4.7.0
|
||||
sha.js: 2.4.12
|
||||
simple-get: 4.0.1
|
||||
|
||||
isomorphic.js@0.2.5: {}
|
||||
|
||||
jiti@2.6.1: {}
|
||||
@@ -12829,6 +13288,23 @@ snapshots:
|
||||
|
||||
media-typer@1.1.0: {}
|
||||
|
||||
memfs@4.57.2(tslib@2.8.1):
|
||||
dependencies:
|
||||
'@jsonjoy.com/fs-core': 4.57.2(tslib@2.8.1)
|
||||
'@jsonjoy.com/fs-fsa': 4.57.2(tslib@2.8.1)
|
||||
'@jsonjoy.com/fs-node': 4.57.2(tslib@2.8.1)
|
||||
'@jsonjoy.com/fs-node-builtins': 4.57.2(tslib@2.8.1)
|
||||
'@jsonjoy.com/fs-node-to-fsa': 4.57.2(tslib@2.8.1)
|
||||
'@jsonjoy.com/fs-node-utils': 4.57.2(tslib@2.8.1)
|
||||
'@jsonjoy.com/fs-print': 4.57.2(tslib@2.8.1)
|
||||
'@jsonjoy.com/fs-snapshot': 4.57.2(tslib@2.8.1)
|
||||
'@jsonjoy.com/json-pack': 1.21.0(tslib@2.8.1)
|
||||
'@jsonjoy.com/util': 1.9.0(tslib@2.8.1)
|
||||
glob-to-regex.js: 1.2.0(tslib@2.8.1)
|
||||
thingies: 2.6.0(tslib@2.8.1)
|
||||
tree-dump: 1.1.0(tslib@2.8.1)
|
||||
tslib: 2.8.1
|
||||
|
||||
merge-descriptors@2.0.0: {}
|
||||
|
||||
mermaid@11.12.3:
|
||||
@@ -13175,6 +13651,10 @@ snapshots:
|
||||
|
||||
minimist@1.2.8: {}
|
||||
|
||||
minimisted@2.0.1:
|
||||
dependencies:
|
||||
minimist: 1.2.8
|
||||
|
||||
minipass-collect@1.0.2:
|
||||
dependencies:
|
||||
minipass: 3.3.6
|
||||
@@ -13331,6 +13811,8 @@ snapshots:
|
||||
|
||||
package-manager-detector@1.6.0: {}
|
||||
|
||||
pako@1.0.11: {}
|
||||
|
||||
parse-entities@4.0.2:
|
||||
dependencies:
|
||||
'@types/unist': 2.0.11
|
||||
@@ -13410,6 +13892,8 @@ snapshots:
|
||||
|
||||
picomatch@4.0.3: {}
|
||||
|
||||
pify@4.0.1: {}
|
||||
|
||||
pino-abstract-transport@2.0.0:
|
||||
dependencies:
|
||||
split2: 4.2.0
|
||||
@@ -13480,6 +13964,8 @@ snapshots:
|
||||
path-data-parser: 0.1.0
|
||||
points-on-curve: 0.2.0
|
||||
|
||||
possible-typed-array-names@1.1.0: {}
|
||||
|
||||
postcss-selector-parser@6.0.10:
|
||||
dependencies:
|
||||
cssesc: 3.0.0
|
||||
@@ -13530,6 +14016,8 @@ snapshots:
|
||||
|
||||
process-warning@5.0.0: {}
|
||||
|
||||
process@0.11.10: {}
|
||||
|
||||
promise-inflight@1.0.1:
|
||||
optional: true
|
||||
|
||||
@@ -13763,6 +14251,14 @@ snapshots:
|
||||
string_decoder: 1.3.0
|
||||
util-deprecate: 1.0.2
|
||||
|
||||
readable-stream@4.7.0:
|
||||
dependencies:
|
||||
abort-controller: 3.0.0
|
||||
buffer: 6.0.3
|
||||
events: 3.3.0
|
||||
process: 0.11.10
|
||||
string_decoder: 1.3.0
|
||||
|
||||
readdirp@4.1.2: {}
|
||||
|
||||
real-require@0.2.0: {}
|
||||
@@ -13940,8 +14436,23 @@ snapshots:
|
||||
|
||||
set-cookie-parser@2.7.2: {}
|
||||
|
||||
set-function-length@1.2.2:
|
||||
dependencies:
|
||||
define-data-property: 1.1.4
|
||||
es-errors: 1.3.0
|
||||
function-bind: 1.1.2
|
||||
get-intrinsic: 1.3.0
|
||||
gopd: 1.2.0
|
||||
has-property-descriptors: 1.0.2
|
||||
|
||||
setprototypeof@1.2.0: {}
|
||||
|
||||
sha.js@2.4.12:
|
||||
dependencies:
|
||||
inherits: 2.0.4
|
||||
safe-buffer: 5.2.1
|
||||
to-buffer: 1.2.2
|
||||
|
||||
sharp@0.34.5:
|
||||
dependencies:
|
||||
'@img/colour': 1.1.0
|
||||
@@ -14264,6 +14775,10 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- react-native-b4a
|
||||
|
||||
thingies@2.6.0(tslib@2.8.1):
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
thread-stream@3.1.0:
|
||||
dependencies:
|
||||
real-require: 0.2.0
|
||||
@@ -14293,6 +14808,12 @@ snapshots:
|
||||
dependencies:
|
||||
tldts-core: 7.0.26
|
||||
|
||||
to-buffer@1.2.2:
|
||||
dependencies:
|
||||
isarray: 2.0.5
|
||||
safe-buffer: 5.2.1
|
||||
typed-array-buffer: 1.0.3
|
||||
|
||||
toidentifier@1.0.1: {}
|
||||
|
||||
tough-cookie@6.0.1:
|
||||
@@ -14303,6 +14824,10 @@ snapshots:
|
||||
dependencies:
|
||||
punycode: 2.3.1
|
||||
|
||||
tree-dump@1.1.0(tslib@2.8.1):
|
||||
dependencies:
|
||||
tslib: 2.8.1
|
||||
|
||||
trim-lines@3.0.1: {}
|
||||
|
||||
trough@2.2.0: {}
|
||||
@@ -14343,6 +14868,12 @@ snapshots:
|
||||
|
||||
type@2.7.3: {}
|
||||
|
||||
typed-array-buffer@1.0.3:
|
||||
dependencies:
|
||||
call-bound: 1.0.4
|
||||
es-errors: 1.3.0
|
||||
is-typed-array: 1.1.15
|
||||
|
||||
typedarray@0.0.6: {}
|
||||
|
||||
typescript@5.9.3: {}
|
||||
@@ -14717,6 +15248,16 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- '@noble/hashes'
|
||||
|
||||
which-typed-array@1.1.20:
|
||||
dependencies:
|
||||
available-typed-arrays: 1.0.7
|
||||
call-bind: 1.0.9
|
||||
call-bound: 1.0.4
|
||||
for-each: 0.3.5
|
||||
get-proto: 1.0.1
|
||||
gopd: 1.2.0
|
||||
has-tostringtag: 1.0.2
|
||||
|
||||
which@2.0.2:
|
||||
dependencies:
|
||||
isexe: 2.0.0
|
||||
|
||||
@@ -68,7 +68,9 @@
|
||||
"embedded-postgres": "^18.1.0-beta.16",
|
||||
"express": "^5.1.0",
|
||||
"hermes-paperclip-adapter": "^0.2.0",
|
||||
"isomorphic-git": "^1.38.0",
|
||||
"jsdom": "^28.1.0",
|
||||
"memfs": "^4.57.2",
|
||||
"multer": "^2.1.1",
|
||||
"open": "^11.0.0",
|
||||
"pino": "^9.6.0",
|
||||
|
||||
@@ -29,7 +29,7 @@ import type {
|
||||
import { normalizeAgentUrlKey } from "@paperclipai/shared";
|
||||
import { resolvePaperclipInstanceRoot } from "../home-paths.js";
|
||||
import { notFound, unprocessable } from "../errors.js";
|
||||
import { ghFetch, gitHubApiBase, inferGitHostFamily, resolveRawGitHubUrl } from "./github-fetch.js";
|
||||
import { openRepoSnapshot, parseGitSourceUrl, resolveGitRef, type ParsedGitSource, type RepoSnapshot } from "./git-source.js";
|
||||
import { agentService } from "./agents.js";
|
||||
import { projectService } from "./projects.js";
|
||||
import { secretService } from "./secrets.js";
|
||||
@@ -541,106 +541,20 @@ function parseFrontmatterMarkdown(raw: string): { frontmatter: Record<string, un
|
||||
};
|
||||
}
|
||||
|
||||
async function fetchText(url: string, authToken?: string) {
|
||||
const response = await ghFetch(url, undefined, authToken);
|
||||
async function fetchPlainText(url: string) {
|
||||
let response: Response;
|
||||
try {
|
||||
response = await fetch(url);
|
||||
} catch {
|
||||
const hostname = (() => { try { return new URL(url).hostname; } catch { return url; } })();
|
||||
throw unprocessable(`Could not connect to ${hostname}`);
|
||||
}
|
||||
if (!response.ok) {
|
||||
throw unprocessable(`Failed to fetch ${url}: ${response.status}`);
|
||||
}
|
||||
return response.text();
|
||||
}
|
||||
|
||||
async function fetchJson<T>(url: string, authToken?: string): Promise<T> {
|
||||
const response = await ghFetch(url, {
|
||||
headers: {
|
||||
accept: "application/vnd.github+json",
|
||||
},
|
||||
}, authToken);
|
||||
if (!response.ok) {
|
||||
throw unprocessable(`Failed to fetch ${url}: ${response.status}`);
|
||||
}
|
||||
return response.json() as Promise<T>;
|
||||
}
|
||||
|
||||
|
||||
async function resolveGitHubDefaultBranch(owner: string, repo: string, apiBase: string, authToken?: string) {
|
||||
const response = await fetchJson<{ default_branch?: string }>(
|
||||
`${apiBase}/repos/${owner}/${repo}`,
|
||||
authToken,
|
||||
);
|
||||
return asString(response.default_branch) ?? "main";
|
||||
}
|
||||
|
||||
async function resolveGitHubCommitSha(owner: string, repo: string, ref: string, apiBase: string, authToken?: string) {
|
||||
const response = await fetchJson<{ sha?: string }>(
|
||||
`${apiBase}/repos/${owner}/${repo}/commits/${encodeURIComponent(ref)}`,
|
||||
authToken,
|
||||
);
|
||||
const sha = asString(response.sha);
|
||||
if (!sha) {
|
||||
throw unprocessable(`Failed to resolve ref ${ref}`);
|
||||
}
|
||||
return sha;
|
||||
}
|
||||
|
||||
function parseGitHubSourceUrl(rawUrl: string) {
|
||||
const url = new URL(rawUrl);
|
||||
if (url.protocol !== "https:") {
|
||||
throw unprocessable("Source URL must use HTTPS");
|
||||
}
|
||||
const parts = url.pathname.split("/").filter(Boolean);
|
||||
if (parts.length < 2) {
|
||||
throw unprocessable("Invalid git source URL");
|
||||
}
|
||||
const owner = parts[0]!;
|
||||
const repo = parts[1]!.replace(/\.git$/i, "");
|
||||
const family = inferGitHostFamily(url.hostname);
|
||||
let ref = "main";
|
||||
let basePath = "";
|
||||
let filePath: string | null = null;
|
||||
let explicitRef = false;
|
||||
if (family === "github") {
|
||||
if (parts[2] === "tree") {
|
||||
ref = parts[3] ?? "main";
|
||||
basePath = parts.slice(4).join("/");
|
||||
explicitRef = true;
|
||||
} else if (parts[2] === "blob") {
|
||||
ref = parts[3] ?? "main";
|
||||
filePath = parts.slice(4).join("/");
|
||||
basePath = filePath ? path.posix.dirname(filePath) : "";
|
||||
explicitRef = true;
|
||||
}
|
||||
} else if (parts[2] === "src" && (parts[3] === "branch" || parts[3] === "commit" || parts[3] === "tag")) {
|
||||
// Gitea/Forgejo web URLs: /{owner}/{repo}/src/{branch|commit|tag}/{ref}/{path}
|
||||
ref = parts[4] ?? "main";
|
||||
const tail = parts.slice(5);
|
||||
const tailJoined = tail.join("/");
|
||||
if (tail.length > 0 && /\.[A-Za-z0-9]+$/.test(tail[tail.length - 1]!)) {
|
||||
filePath = tailJoined;
|
||||
basePath = path.posix.dirname(tailJoined);
|
||||
} else {
|
||||
basePath = tailJoined;
|
||||
}
|
||||
explicitRef = true;
|
||||
}
|
||||
return { hostname: url.hostname, owner, repo, ref, basePath, filePath, explicitRef };
|
||||
}
|
||||
|
||||
async function resolveGitHubPinnedRef(parsed: ReturnType<typeof parseGitHubSourceUrl>, authToken?: string) {
|
||||
const apiBase = gitHubApiBase(parsed.hostname);
|
||||
if (/^[0-9a-f]{40}$/i.test(parsed.ref.trim())) {
|
||||
return {
|
||||
pinnedRef: parsed.ref,
|
||||
trackingRef: parsed.explicitRef ? parsed.ref : null,
|
||||
};
|
||||
}
|
||||
|
||||
const trackingRef = parsed.explicitRef
|
||||
? parsed.ref
|
||||
: await resolveGitHubDefaultBranch(parsed.owner, parsed.repo, apiBase, authToken);
|
||||
const pinnedRef = await resolveGitHubCommitSha(parsed.owner, parsed.repo, trackingRef, apiBase, authToken);
|
||||
return { pinnedRef, trackingRef };
|
||||
}
|
||||
|
||||
|
||||
function extractCommandTokens(raw: string) {
|
||||
const matches = raw.match(/"[^"]*"|'[^']*'|\S+/g) ?? [];
|
||||
@@ -1081,20 +995,12 @@ async function readUrlSkillImports(
|
||||
return segments.length >= 2 && !parsed.pathname.endsWith(".md");
|
||||
} catch { return false; } })();
|
||||
if (looksLikeRepoUrl) {
|
||||
const parsed = parseGitHubSourceUrl(url);
|
||||
const apiBase = gitHubApiBase(parsed.hostname);
|
||||
const { pinnedRef, trackingRef } = await resolveGitHubPinnedRef(parsed, authToken);
|
||||
let ref = pinnedRef;
|
||||
const tree = await fetchJson<{ tree?: Array<{ path: string; type: string }> }>(
|
||||
`${apiBase}/repos/${parsed.owner}/${parsed.repo}/git/trees/${ref}?recursive=1`,
|
||||
authToken,
|
||||
).catch(() => {
|
||||
throw unprocessable(`Failed to read GitHub tree for ${url}`);
|
||||
});
|
||||
const allPaths = (tree.tree ?? [])
|
||||
.filter((entry) => entry.type === "blob")
|
||||
.map((entry) => entry.path)
|
||||
.filter((entry): entry is string => typeof entry === "string");
|
||||
const parsed = parseGitSourceUrl(url);
|
||||
const resolved = await resolveGitRef(parsed, authToken);
|
||||
const snapshot = await openRepoSnapshot(parsed, resolved.trackingRef, resolved.pinnedSha, authToken);
|
||||
const ref = snapshot.sha;
|
||||
const trackingRef = resolved.trackingRef;
|
||||
const allPaths = await snapshot.listFiles();
|
||||
const basePrefix = parsed.basePath ? `${parsed.basePath.replace(/^\/+|\/+$/g, "")}/` : "";
|
||||
const scopedPaths = basePrefix
|
||||
? allPaths.filter((entry) => entry.startsWith(basePrefix))
|
||||
@@ -1108,13 +1014,13 @@ async function readUrlSkillImports(
|
||||
);
|
||||
if (skillPaths.length === 0) {
|
||||
throw unprocessable(
|
||||
"No SKILL.md files were found in the provided GitHub source.",
|
||||
"No SKILL.md files were found in the provided source.",
|
||||
);
|
||||
}
|
||||
const skills: ImportedSkill[] = [];
|
||||
for (const relativeSkillPath of skillPaths) {
|
||||
const repoSkillPath = basePrefix ? `${basePrefix}${relativeSkillPath}` : relativeSkillPath;
|
||||
const markdown = await fetchText(resolveRawGitHubUrl(parsed.hostname, parsed.owner, parsed.repo, ref, repoSkillPath), authToken);
|
||||
const markdown = await snapshot.readFile(repoSkillPath);
|
||||
const parsedMarkdown = parseFrontmatterMarkdown(markdown);
|
||||
const skillDir = path.posix.dirname(relativeSkillPath);
|
||||
const slug = deriveImportedSkillSlug(parsedMarkdown.frontmatter, path.posix.basename(skillDir));
|
||||
@@ -1168,15 +1074,15 @@ async function readUrlSkillImports(
|
||||
if (skills.length === 0) {
|
||||
throw unprocessable(
|
||||
requestedSkillSlug
|
||||
? `Skill ${requestedSkillSlug} was not found in the provided GitHub source.`
|
||||
: "No SKILL.md files were found in the provided GitHub source.",
|
||||
? `Skill ${requestedSkillSlug} was not found in the provided source.`
|
||||
: "No SKILL.md files were found in the provided source.",
|
||||
);
|
||||
}
|
||||
return { skills, warnings };
|
||||
}
|
||||
|
||||
if (url.startsWith("http://") || url.startsWith("https://")) {
|
||||
const markdown = await fetchText(url, authToken);
|
||||
const markdown = await fetchPlainText(url);
|
||||
const parsedMarkdown = parseFrontmatterMarkdown(markdown);
|
||||
const urlObj = new URL(url);
|
||||
const fileName = path.posix.basename(urlObj.pathname);
|
||||
@@ -1801,9 +1707,18 @@ export function companySkillService(db: Db) {
|
||||
}
|
||||
|
||||
const hostname = asString(metadata.hostname) || "github.com";
|
||||
const apiBase = gitHubApiBase(hostname);
|
||||
const authToken = await resolveSkillAuthToken(companyId, skill);
|
||||
const latestRef = await resolveGitHubCommitSha(owner, repo, trackingRef, apiBase, authToken);
|
||||
const parsed: ParsedGitSource = {
|
||||
cloneUrl: `https://${hostname}/${owner}/${repo}.git`,
|
||||
hostname,
|
||||
owner,
|
||||
repo,
|
||||
ref: trackingRef,
|
||||
basePath: "",
|
||||
filePath: null,
|
||||
explicitRef: true,
|
||||
};
|
||||
const { pinnedSha: latestRef } = await resolveGitRef(parsed, authToken);
|
||||
return {
|
||||
supported: true,
|
||||
reason: null,
|
||||
@@ -1843,13 +1758,25 @@ export function companySkillService(db: Db) {
|
||||
const repo = asString(metadata.repo);
|
||||
const hostname = asString(metadata.hostname) || "github.com";
|
||||
const ref = skill.sourceRef ?? asString(metadata.ref) ?? "main";
|
||||
const trackingRef = asString(metadata.trackingRef);
|
||||
const repoSkillDir = normalizeGitHubSkillDirectory(asString(metadata.repoSkillDir), skill.slug);
|
||||
if (!owner || !repo) {
|
||||
throw unprocessable("Skill source metadata is incomplete.");
|
||||
}
|
||||
const authToken = await resolveSkillAuthToken(companyId, skill);
|
||||
const repoPath = normalizePortablePath(path.posix.join(repoSkillDir, normalizedPath));
|
||||
content = await fetchText(resolveRawGitHubUrl(hostname, owner, repo, ref, repoPath), authToken);
|
||||
const parsedSource: ParsedGitSource = {
|
||||
cloneUrl: `https://${hostname}/${owner}/${repo}.git`,
|
||||
hostname,
|
||||
owner,
|
||||
repo,
|
||||
ref,
|
||||
basePath: repoSkillDir,
|
||||
filePath: null,
|
||||
explicitRef: true,
|
||||
};
|
||||
const snapshot: RepoSnapshot = await openRepoSnapshot(parsedSource, trackingRef ?? null, ref, authToken);
|
||||
content = await snapshot.readFile(repoPath);
|
||||
} else if (skill.sourceType === "url") {
|
||||
if (normalizedPath !== "SKILL.md") {
|
||||
throw notFound("This skill source only exposes SKILL.md");
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
import path from "path";
|
||||
import git from "isomorphic-git";
|
||||
import http from "isomorphic-git/http/node";
|
||||
import { Volume, createFsFromVolume } from "memfs";
|
||||
|
||||
import { unprocessable } from "../errors.js";
|
||||
|
||||
export type ParsedGitSource = {
|
||||
cloneUrl: string;
|
||||
hostname: string;
|
||||
owner: string;
|
||||
repo: string;
|
||||
ref: string | null;
|
||||
basePath: string;
|
||||
filePath: string | null;
|
||||
explicitRef: boolean;
|
||||
};
|
||||
|
||||
export type RefResolution = {
|
||||
pinnedSha: string;
|
||||
trackingRef: string | null;
|
||||
};
|
||||
|
||||
export type RepoSnapshot = {
|
||||
sha: string;
|
||||
listFiles(): Promise<string[]>;
|
||||
readFile(repoPath: string): Promise<string>;
|
||||
};
|
||||
|
||||
const SHA_REGEX = /^[0-9a-f]{40}$/i;
|
||||
|
||||
export function buildCloneUrl(hostname: string, owner: string, repo: string): string {
|
||||
return `https://${hostname}/${owner}/${repo}.git`;
|
||||
}
|
||||
|
||||
export function parseGitSourceUrl(rawUrl: string): ParsedGitSource {
|
||||
let url: URL;
|
||||
try {
|
||||
url = new URL(rawUrl);
|
||||
} catch {
|
||||
throw unprocessable("Invalid git source URL");
|
||||
}
|
||||
if (url.protocol !== "https:") {
|
||||
throw unprocessable("Source URL must use HTTPS");
|
||||
}
|
||||
const segments = url.pathname.split("/").filter(Boolean);
|
||||
if (segments.length < 2) {
|
||||
throw unprocessable("Source URL must include an owner and repository");
|
||||
}
|
||||
const owner = segments[0]!;
|
||||
const repo = segments[1]!.replace(/\.git$/i, "");
|
||||
|
||||
let ref: string | null = null;
|
||||
let basePath = "";
|
||||
let filePath: string | null = null;
|
||||
let explicitRef = false;
|
||||
let tail: string[] = [];
|
||||
|
||||
// Recognise common host-specific URL shapes so users can paste a tree/blob link.
|
||||
if (segments[2] === "tree" || segments[2] === "blob") {
|
||||
// github.com style
|
||||
ref = segments[3] ?? null;
|
||||
tail = segments.slice(4);
|
||||
explicitRef = ref !== null;
|
||||
} else if (segments[2] === "src" && (segments[3] === "branch" || segments[3] === "commit" || segments[3] === "tag")) {
|
||||
// gitea / forgejo style
|
||||
ref = segments[4] ?? null;
|
||||
tail = segments.slice(5);
|
||||
explicitRef = ref !== null;
|
||||
} else if (segments[2] === "-" && (segments[3] === "tree" || segments[3] === "blob")) {
|
||||
// gitlab style: /{owner}/{repo}/-/tree/{ref}/{path}
|
||||
ref = segments[4] ?? null;
|
||||
tail = segments.slice(5);
|
||||
explicitRef = ref !== null;
|
||||
} else if (segments[2] === "src" && segments.length >= 4) {
|
||||
// bitbucket style: /{owner}/{repo}/src/{ref}/{path}
|
||||
ref = segments[3] ?? null;
|
||||
tail = segments.slice(4);
|
||||
explicitRef = ref !== null;
|
||||
}
|
||||
|
||||
if (segments[2] === "blob" || (segments[2] === "-" && segments[3] === "blob")) {
|
||||
const joined = tail.join("/");
|
||||
filePath = joined || null;
|
||||
basePath = filePath ? path.posix.dirname(filePath) : "";
|
||||
if (basePath === ".") basePath = "";
|
||||
} else if (tail.length > 0) {
|
||||
const joined = tail.join("/");
|
||||
// Heuristic: if the last segment looks like a file (has an extension), treat as file
|
||||
const last = tail[tail.length - 1]!;
|
||||
if (/\.[A-Za-z0-9]+$/.test(last)) {
|
||||
filePath = joined;
|
||||
basePath = path.posix.dirname(joined);
|
||||
if (basePath === ".") basePath = "";
|
||||
} else {
|
||||
basePath = joined;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
cloneUrl: buildCloneUrl(url.hostname, owner, repo),
|
||||
hostname: url.hostname,
|
||||
owner,
|
||||
repo,
|
||||
ref,
|
||||
basePath,
|
||||
filePath,
|
||||
explicitRef,
|
||||
};
|
||||
}
|
||||
|
||||
function buildAuthCallback(authToken: string | undefined) {
|
||||
if (!authToken) return undefined;
|
||||
// Universal pattern: token-as-username works for GitHub PATs (classic and fine-grained),
|
||||
// GitLab project/personal access tokens, Gitea/Forgejo tokens, and Bitbucket app passwords
|
||||
// when used over the git smart-HTTP protocol.
|
||||
return () => ({ username: authToken, password: "x-oauth-basic" });
|
||||
}
|
||||
|
||||
async function withGitErrors<T>(label: string, fn: () => Promise<T>): Promise<T> {
|
||||
try {
|
||||
return await fn();
|
||||
} catch (err) {
|
||||
const message = err instanceof Error ? err.message : String(err);
|
||||
if (/HTTP Error: 401/i.test(message)) {
|
||||
throw unprocessable(`${label}: authentication required or token rejected`);
|
||||
}
|
||||
if (/HTTP Error: 403/i.test(message)) {
|
||||
throw unprocessable(`${label}: access forbidden`);
|
||||
}
|
||||
if (/HTTP Error: 404/i.test(message) || /repository not found/i.test(message)) {
|
||||
throw unprocessable(`${label}: repository not found`);
|
||||
}
|
||||
if (/ENOTFOUND|EAI_AGAIN|ECONNREFUSED|ETIMEDOUT/i.test(message)) {
|
||||
throw unprocessable(`${label}: could not connect to host`);
|
||||
}
|
||||
throw unprocessable(`${label}: ${message}`);
|
||||
}
|
||||
}
|
||||
|
||||
export async function resolveGitRef(
|
||||
parsed: ParsedGitSource,
|
||||
authToken?: string,
|
||||
): Promise<RefResolution> {
|
||||
const onAuth = buildAuthCallback(authToken);
|
||||
|
||||
if (parsed.ref && SHA_REGEX.test(parsed.ref.trim())) {
|
||||
return {
|
||||
pinnedSha: parsed.ref.trim().toLowerCase(),
|
||||
trackingRef: parsed.explicitRef ? parsed.ref.trim() : null,
|
||||
};
|
||||
}
|
||||
|
||||
const refs = await withGitErrors(`Resolve refs for ${parsed.cloneUrl}`, () =>
|
||||
git.listServerRefs({
|
||||
http,
|
||||
url: parsed.cloneUrl,
|
||||
onAuth,
|
||||
symrefs: true,
|
||||
protocolVersion: 2,
|
||||
}),
|
||||
);
|
||||
|
||||
const findExact = (fullRef: string) => refs.find((r) => r.ref === fullRef);
|
||||
|
||||
if (!parsed.ref) {
|
||||
const head = refs.find((r) => r.ref === "HEAD");
|
||||
if (!head?.oid) {
|
||||
throw unprocessable(`Could not determine default branch for ${parsed.cloneUrl}`);
|
||||
}
|
||||
const target = head.target?.replace(/^refs\/heads\//, "") ?? null;
|
||||
return { pinnedSha: head.oid, trackingRef: target };
|
||||
}
|
||||
|
||||
const wanted = parsed.ref.replace(/^refs\/(heads|tags)\//, "");
|
||||
const branch = findExact(`refs/heads/${wanted}`);
|
||||
if (branch?.oid) return { pinnedSha: branch.oid, trackingRef: wanted };
|
||||
|
||||
// Prefer the peeled (annotated) tag oid when present, else the tag object oid.
|
||||
const peeled = findExact(`refs/tags/${wanted}^{}`);
|
||||
if (peeled?.oid) return { pinnedSha: peeled.oid, trackingRef: wanted };
|
||||
const tag = findExact(`refs/tags/${wanted}`);
|
||||
if (tag?.oid) return { pinnedSha: tag.oid, trackingRef: wanted };
|
||||
|
||||
throw unprocessable(`Ref '${parsed.ref}' not found in ${parsed.cloneUrl}`);
|
||||
}
|
||||
|
||||
export async function openRepoSnapshot(
|
||||
parsed: ParsedGitSource,
|
||||
trackingRef: string | null,
|
||||
expectedSha: string,
|
||||
authToken?: string,
|
||||
): Promise<RepoSnapshot> {
|
||||
const volume = new Volume();
|
||||
const fs = createFsFromVolume(volume) as unknown as Parameters<typeof git.clone>[0]["fs"];
|
||||
const dir = "/repo";
|
||||
const onAuth = buildAuthCallback(authToken);
|
||||
|
||||
await withGitErrors(`Clone ${parsed.cloneUrl}`, async () => {
|
||||
await git.clone({
|
||||
fs,
|
||||
http,
|
||||
dir,
|
||||
url: parsed.cloneUrl,
|
||||
ref: trackingRef ?? expectedSha,
|
||||
singleBranch: true,
|
||||
depth: 1,
|
||||
noCheckout: true,
|
||||
onAuth,
|
||||
});
|
||||
});
|
||||
|
||||
// Re-resolve to the actual commit cloned. If upstream moved between resolveGitRef and
|
||||
// clone, we trust what we cloned (snapshot is self-consistent).
|
||||
const sha = await git.resolveRef({ fs, dir, ref: "HEAD" });
|
||||
|
||||
async function listFiles(): Promise<string[]> {
|
||||
const out: string[] = [];
|
||||
await git.walk({
|
||||
fs,
|
||||
dir,
|
||||
trees: [git.TREE({ ref: sha })],
|
||||
map: async (filepath, entries) => {
|
||||
if (filepath === ".") return;
|
||||
const entry = entries?.[0];
|
||||
if (!entry) return;
|
||||
const type = await entry.type();
|
||||
if (type === "blob") {
|
||||
out.push(filepath);
|
||||
}
|
||||
},
|
||||
});
|
||||
return out;
|
||||
}
|
||||
|
||||
async function readFile(repoPath: string): Promise<string> {
|
||||
const normalized = repoPath.replace(/^\/+/, "");
|
||||
const { blob } = await git.readBlob({ fs, dir, oid: sha, filepath: normalized });
|
||||
return new TextDecoder("utf-8").decode(blob);
|
||||
}
|
||||
|
||||
return { sha, listFiles, readFile };
|
||||
}
|
||||
Reference in New Issue
Block a user