ovsx 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/README.md +71 -1
  2. package/lib/get.js +1 -1
  3. package/lib/get.js.map +1 -1
  4. package/lib/index.d.ts +4 -0
  5. package/lib/index.d.ts.map +1 -1
  6. package/lib/index.js +4 -0
  7. package/lib/index.js.map +1 -1
  8. package/lib/list-options.d.ts +24 -0
  9. package/lib/list-options.d.ts.map +1 -0
  10. package/lib/list-options.js +15 -0
  11. package/lib/list-options.js.map +1 -0
  12. package/lib/list.d.ts +18 -0
  13. package/lib/list.d.ts.map +1 -0
  14. package/lib/list.js +46 -0
  15. package/lib/list.js.map +1 -0
  16. package/lib/main.js +78 -2
  17. package/lib/main.js.map +1 -1
  18. package/lib/oidc.d.ts +22 -0
  19. package/lib/oidc.d.ts.map +1 -0
  20. package/lib/oidc.js +101 -0
  21. package/lib/oidc.js.map +1 -0
  22. package/lib/publish-options.d.ts +8 -1
  23. package/lib/publish-options.d.ts.map +1 -1
  24. package/lib/publish.d.ts.map +1 -1
  25. package/lib/publish.js +73 -4
  26. package/lib/publish.js.map +1 -1
  27. package/lib/registry-options.d.ts +8 -0
  28. package/lib/registry-options.d.ts.map +1 -1
  29. package/lib/registry.d.ts +119 -1
  30. package/lib/registry.d.ts.map +1 -1
  31. package/lib/registry.js +187 -19
  32. package/lib/registry.js.map +1 -1
  33. package/lib/search-options.d.ts +48 -0
  34. package/lib/search-options.d.ts.map +1 -0
  35. package/lib/search-options.js +15 -0
  36. package/lib/search-options.js.map +1 -0
  37. package/lib/search.d.ts +22 -0
  38. package/lib/search.d.ts.map +1 -0
  39. package/lib/search.js +86 -0
  40. package/lib/search.js.map +1 -0
  41. package/lib/show-options.d.ts +33 -0
  42. package/lib/show-options.d.ts.map +1 -0
  43. package/lib/show-options.js +15 -0
  44. package/lib/show-options.js.map +1 -0
  45. package/lib/show.d.ts +18 -0
  46. package/lib/show.d.ts.map +1 -0
  47. package/lib/show.js +289 -0
  48. package/lib/show.js.map +1 -0
  49. package/lib/table.d.ts +27 -0
  50. package/lib/table.d.ts.map +1 -0
  51. package/lib/table.js +56 -0
  52. package/lib/table.js.map +1 -0
  53. package/lib/trusted-publishing-options.d.ts +30 -0
  54. package/lib/trusted-publishing-options.d.ts.map +1 -0
  55. package/lib/trusted-publishing-options.js +15 -0
  56. package/lib/trusted-publishing-options.js.map +1 -0
  57. package/lib/trusted-publishing.d.ts +35 -0
  58. package/lib/trusted-publishing.d.ts.map +1 -0
  59. package/lib/trusted-publishing.js +112 -0
  60. package/lib/trusted-publishing.js.map +1 -0
  61. package/lib/unpublish-options.d.ts +34 -0
  62. package/lib/unpublish-options.d.ts.map +1 -0
  63. package/lib/unpublish-options.js +15 -0
  64. package/lib/unpublish-options.js.map +1 -0
  65. package/lib/unpublish.d.ts +22 -0
  66. package/lib/unpublish.d.ts.map +1 -0
  67. package/lib/unpublish.js +137 -0
  68. package/lib/unpublish.js.map +1 -0
  69. package/lib/util.d.ts +39 -1
  70. package/lib/util.d.ts.map +1 -1
  71. package/lib/util.js +91 -5
  72. package/lib/util.js.map +1 -1
  73. package/lib/verify-options.d.ts +24 -0
  74. package/lib/verify-options.d.ts.map +1 -0
  75. package/lib/verify-options.js +15 -0
  76. package/lib/verify-options.js.map +1 -0
  77. package/lib/verify-signature-options.d.ts +32 -0
  78. package/lib/verify-signature-options.d.ts.map +1 -0
  79. package/lib/verify-signature-options.js +15 -0
  80. package/lib/verify-signature-options.js.map +1 -0
  81. package/lib/verify-signature.d.ts +28 -0
  82. package/lib/verify-signature.d.ts.map +1 -0
  83. package/lib/verify-signature.js +105 -0
  84. package/lib/verify-signature.js.map +1 -0
  85. package/lib/verify.d.ts +21 -0
  86. package/lib/verify.d.ts.map +1 -0
  87. package/lib/verify.js +97 -0
  88. package/lib/verify.js.map +1 -0
  89. package/lib/version.d.ts +1 -1
  90. package/lib/version.js +1 -1
  91. package/lib/zip.d.ts +10 -0
  92. package/lib/zip.d.ts.map +1 -1
  93. package/lib/zip.js +31 -0
  94. package/lib/zip.js.map +1 -1
  95. package/package.json +9 -5
  96. package/src/get.ts +1 -1
  97. package/src/index.ts +4 -0
  98. package/src/list-options.ts +25 -0
  99. package/src/list.ts +48 -0
  100. package/src/main.ts +89 -3
  101. package/src/oidc.ts +107 -0
  102. package/src/publish-options.ts +8 -1
  103. package/src/publish.ts +97 -5
  104. package/src/registry-options.ts +8 -0
  105. package/src/registry.ts +292 -19
  106. package/src/search-options.ts +49 -0
  107. package/src/search.ts +95 -0
  108. package/src/show-options.ts +34 -0
  109. package/src/show.ts +328 -0
  110. package/src/table.ts +53 -0
  111. package/src/trusted-publishing-options.ts +30 -0
  112. package/src/trusted-publishing.ts +148 -0
  113. package/src/unpublish-options.ts +35 -0
  114. package/src/unpublish.ts +160 -0
  115. package/src/util.ts +102 -5
  116. package/src/verify-options.ts +25 -0
  117. package/src/verify-signature-options.ts +32 -0
  118. package/src/verify-signature.ts +124 -0
  119. package/src/verify.ts +107 -0
  120. package/src/version.ts +1 -1
  121. package/src/zip.ts +31 -1
@@ -0,0 +1,105 @@
1
+ "use strict";
2
+ /******************************************************************************
3
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation.
4
+ *
5
+ * See the NOTICE file(s) distributed with this work for additional
6
+ * information regarding copyright ownership.
7
+ *
8
+ * This program and the accompanying materials are made available under the
9
+ * terms of the Eclipse Public License 2.0 which is available at
10
+ * https://www.eclipse.org/legal/epl-2.0
11
+ *
12
+ * SPDX-License-Identifier: EPL-2.0
13
+ *****************************************************************************/
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.verifySignature = verifySignature;
16
+ const crypto = require("crypto");
17
+ const fs = require("fs");
18
+ const zip_1 = require("./zip");
19
+ /**
20
+ * Verifies a package/manifest/signature file trio entirely offline, the same way `vsce
21
+ * verify-signature` does - no registry involved, every input is a local file. Unlike `verify`,
22
+ * which fetches everything it needs from a registry for a package identified by name, this is for
23
+ * a signature archive you already extracted yourself (e.g. the `.sigzip` a registry serves has a
24
+ * `.signature.sig` and a `.signature.manifest` entry, matching `--signaturePath` and
25
+ * `--manifestPath` here).
26
+ *
27
+ * `vsce verify-signature` validates against Microsoft's own (closed-source) signing scheme and
28
+ * has no need for a public key argument - VS Code's Marketplace keys are baked into its verifier.
29
+ * Open VSX's registries each hold their own Ed25519 key pair instead, so `--publicKeyPath` is
30
+ * required here to say which registry's signature this is supposed to be.
31
+ */
32
+ async function verifySignature(options) {
33
+ const [packageBytes, manifestJson, signature, publicKeyPem] = await Promise.all([
34
+ fs.promises.readFile(options.packagePath),
35
+ fs.promises.readFile(options.manifestPath, 'utf-8'),
36
+ fs.promises.readFile(options.signaturePath),
37
+ fs.promises.readFile(options.publicKeyPath, 'utf-8')
38
+ ]);
39
+ let manifest;
40
+ try {
41
+ manifest = JSON.parse(manifestJson);
42
+ }
43
+ catch (err) {
44
+ throw new Error(`Failed to parse the manifest file as JSON: ${err instanceof Error ? err.message : err}`);
45
+ }
46
+ const publicKey = crypto.createPublicKey({ key: publicKeyPem, format: 'pem' });
47
+ const signatureValid = crypto.verify(null, packageBytes, publicKey, signature);
48
+ if (!signatureValid) {
49
+ throw new Error('Signature verification failed. The package may have been tampered with.');
50
+ }
51
+ const manifestIssues = await checkManifest(options.packagePath, packageBytes, manifest);
52
+ if (manifestIssues.length > 0) {
53
+ throw new Error('The signature is valid, but the package does not match the manifest:\n'
54
+ + manifestIssues.map(issue => ` - ${issue}`).join('\n'));
55
+ }
56
+ console.log('✅ Signature is valid.');
57
+ }
58
+ function sha256Base64(content) {
59
+ return crypto.createHash('sha256').update(content).digest('base64');
60
+ }
61
+ /**
62
+ * The manifest itself isn't part of what the signature covers - only the raw package bytes are
63
+ * signed - so this can't add cryptographic trust on its own. It does confirm the manifest wasn't
64
+ * corrupted or is stale relative to the actual package, and unlike the signature check alone, can
65
+ * point at exactly which entry doesn't match.
66
+ */
67
+ async function checkManifest(packagePath, packageBytes, manifest) {
68
+ const issues = [];
69
+ const packageDigest = manifest.package?.digests?.sha256;
70
+ if (packageDigest === undefined) {
71
+ issues.push('the manifest does not record a package digest');
72
+ }
73
+ else if (packageDigest !== sha256Base64(packageBytes)) {
74
+ issues.push('the package digest does not match the manifest');
75
+ }
76
+ const manifestEntries = manifest.entries ?? {};
77
+ const seenKeys = new Set();
78
+ // Streamed straight into a digest one entry at a time, rather than buffering every entry's
79
+ // full content into memory alongside the already-loaded package bytes.
80
+ const packageEntryDigests = await (0, zip_1.hashAllZipEntries)(packagePath);
81
+ for (const [name, actualDigest] of packageEntryDigests) {
82
+ const key = Buffer.from(name, 'utf-8').toString('base64');
83
+ seenKeys.add(key);
84
+ const entry = manifestEntries[key];
85
+ if (!entry) {
86
+ issues.push(`'${name}' is present in the package but missing from the manifest`);
87
+ continue;
88
+ }
89
+ const entryDigest = entry.digests?.sha256;
90
+ if (entryDigest === undefined) {
91
+ issues.push(`'${name}' has no recorded digest in the manifest`);
92
+ }
93
+ else if (entryDigest !== actualDigest) {
94
+ issues.push(`'${name}' does not match its digest in the manifest`);
95
+ }
96
+ }
97
+ for (const key of Object.keys(manifestEntries)) {
98
+ if (!seenKeys.has(key)) {
99
+ const name = Buffer.from(key, 'base64').toString('utf-8');
100
+ issues.push(`'${name}' is listed in the manifest but missing from the package`);
101
+ }
102
+ }
103
+ return issues;
104
+ }
105
+ //# sourceMappingURL=verify-signature.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify-signature.js","sourceRoot":"","sources":["../src/verify-signature.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /******************************************************************************
2
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation.
3
+ *
4
+ * See the NOTICE file(s) distributed with this work for additional
5
+ * information regarding copyright ownership.
6
+ *
7
+ * This program and the accompanying materials are made available under the
8
+ * terms of the Eclipse Public License 2.0 which is available at
9
+ * https://www.eclipse.org/legal/epl-2.0
10
+ *
11
+ * SPDX-License-Identifier: EPL-2.0
12
+ *****************************************************************************/
13
+ import { VerifyOptions } from './verify-options';
14
+ /**
15
+ * Verifies a downloaded .vsix package's signature against the registry's public key, the same way
16
+ * VS Code itself verifies signed packages on install. Mirrors `vsce verify-signature`, except the
17
+ * signature and public key are fetched from the registry instead of being passed in as separate
18
+ * files - the registry already exposes both for any published, signed version.
19
+ */
20
+ export declare function verify(options: VerifyOptions): Promise<void>;
21
+ //# sourceMappingURL=verify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;+EAW+E;AAO/E,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAIjD;;;;;GAKG;AACH,wBAAsB,MAAM,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAsClE"}
package/lib/verify.js ADDED
@@ -0,0 +1,97 @@
1
+ "use strict";
2
+ /******************************************************************************
3
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation.
4
+ *
5
+ * See the NOTICE file(s) distributed with this work for additional
6
+ * information regarding copyright ownership.
7
+ *
8
+ * This program and the accompanying materials are made available under the
9
+ * terms of the Eclipse Public License 2.0 which is available at
10
+ * https://www.eclipse.org/legal/epl-2.0
11
+ *
12
+ * SPDX-License-Identifier: EPL-2.0
13
+ *****************************************************************************/
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.verify = verify;
16
+ const crypto = require("crypto");
17
+ const fs = require("fs");
18
+ const registry_1 = require("./registry");
19
+ const util_1 = require("./util");
20
+ const zip_1 = require("./zip");
21
+ const SIGNATURE_ENTRY = '.signature.sig';
22
+ /**
23
+ * Verifies a downloaded .vsix package's signature against the registry's public key, the same way
24
+ * VS Code itself verifies signed packages on install. Mirrors `vsce verify-signature`, except the
25
+ * signature and public key are fetched from the registry instead of being passed in as separate
26
+ * files - the registry already exposes both for any published, signed version.
27
+ */
28
+ async function verify(options) {
29
+ (0, util_1.addEnvOptions)(options);
30
+ const registry = new registry_1.Registry(options);
31
+ const manifest = await (0, zip_1.readVSIXPackage)(options.packagePath);
32
+ const namespace = manifest.publisher;
33
+ const extensionName = manifest.name;
34
+ const latest = await registry.getMetadata(namespace, extensionName, options.target);
35
+ if (latest.error) {
36
+ throw new Error(latest.error);
37
+ }
38
+ const extension = await resolveVersion(registry, latest, manifest.version);
39
+ const description = `${namespace}.${extensionName} v${extension.version}`;
40
+ const signatureUrl = extension.files.signature;
41
+ const publicKeyUrl = extension.files.publicKey;
42
+ if (!signatureUrl || !publicKeyUrl) {
43
+ throw new Error(`${description} is not signed by the registry - there is nothing to verify.`);
44
+ }
45
+ const [signature, publicKeyPem] = await Promise.all([
46
+ downloadSignature(registry, signatureUrl),
47
+ downloadPublicKey(registry, publicKeyUrl)
48
+ ]);
49
+ const packageBytes = await fs.promises.readFile(options.packagePath);
50
+ const publicKey = crypto.createPublicKey({ key: publicKeyPem, format: 'pem' });
51
+ const verified = crypto.verify(null, packageBytes, publicKey, signature);
52
+ if (!verified) {
53
+ throw new Error(`${description} does not match the version published to ${registry.url}. `
54
+ + 'The package may have been tampered with, or did not come from that registry.');
55
+ }
56
+ console.log(`✅ This package is identical to ${description} as published to ${registry.url}.`);
57
+ }
58
+ /**
59
+ * `registry.getMetadata` always returns the latest version. Follow `allVersions` to the exact
60
+ * version recorded in the package's own manifest when it differs.
61
+ */
62
+ async function resolveVersion(registry, extension, version) {
63
+ if (extension.version === version) {
64
+ return extension;
65
+ }
66
+ const versionUrl = extension.allVersions[version];
67
+ if (!versionUrl) {
68
+ throw new Error(`${extension.namespace}.${extension.name} has no published version '${version}'.`);
69
+ }
70
+ return registry.getJson(new URL(versionUrl));
71
+ }
72
+ async function downloadSignature(registry, url) {
73
+ const sigzipPath = await (0, util_1.createTempFile)({ postfix: '.sigzip' });
74
+ try {
75
+ await registry.download(sigzipPath, new URL(url));
76
+ const entries = await (0, zip_1.readZip)(sigzipPath, name => name === SIGNATURE_ENTRY);
77
+ const signature = entries.get(SIGNATURE_ENTRY);
78
+ if (!signature) {
79
+ throw new Error(`The signature archive does not contain a '${SIGNATURE_ENTRY}' entry.`);
80
+ }
81
+ return signature;
82
+ }
83
+ finally {
84
+ await fs.promises.rm(sigzipPath, { force: true });
85
+ }
86
+ }
87
+ async function downloadPublicKey(registry, url) {
88
+ const publicKeyPath = await (0, util_1.createTempFile)({ postfix: '.pem' });
89
+ try {
90
+ await registry.download(publicKeyPath, new URL(url));
91
+ return await fs.promises.readFile(publicKeyPath, 'utf-8');
92
+ }
93
+ finally {
94
+ await fs.promises.rm(publicKeyPath, { force: true });
95
+ }
96
+ }
97
+ //# sourceMappingURL=verify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verify.js","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"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"}
package/lib/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const LIB_VERSION = "1.1.0";
1
+ export declare const LIB_VERSION = "1.2.0";
2
2
  //# sourceMappingURL=version.d.ts.map
package/lib/version.js CHANGED
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.LIB_VERSION = void 0;
4
- exports.LIB_VERSION = "1.1.0";
4
+ exports.LIB_VERSION = "1.2.0";
5
5
  //# sourceMappingURL=version.js.map
package/lib/zip.d.ts CHANGED
@@ -1,3 +1,13 @@
1
1
  import { Manifest } from './util';
2
+ export declare function readZip(packagePath: string, filter: (name: string) => boolean): Promise<Map<string, Buffer>>;
3
+ /**
4
+ * Computes the SHA256 digest of every non-directory entry of a zip file, keyed by its exact
5
+ * (case-preserving) name - unlike {@link readZip}, which lowercases names for its
6
+ * case-insensitive-filter callers. Used where entry names and digests are compared against another
7
+ * source of truth, such as a signature manifest that records the original names, without also
8
+ * having to hold every entry's full content in memory at once alongside it - each entry's content
9
+ * is streamed straight into a hash and discarded, one entry at a time.
10
+ */
11
+ export declare function hashAllZipEntries(packagePath: string): Promise<Map<string, string>>;
2
12
  export declare function readVSIXPackage(packagePath: string): Promise<Manifest>;
3
13
  //# sourceMappingURL=zip.d.ts.map
package/lib/zip.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"zip.d.ts","sourceRoot":"","sources":["../src/zip.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AA8BlC,wBAAsB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAQ5E"}
1
+ {"version":3,"file":"zip.d.ts","sourceRoot":"","sources":["../src/zip.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAWlC,wBAAsB,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAiBlH;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAkBzF;AAED,wBAAsB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAQ5E"}
package/lib/zip.js CHANGED
@@ -1,5 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.readZip = readZip;
4
+ exports.hashAllZipEntries = hashAllZipEntries;
3
5
  exports.readVSIXPackage = readVSIXPackage;
4
6
  /********************************************************************************
5
7
  * Copyright (c) 2024 Precies. Software and others
@@ -10,7 +12,9 @@ exports.readVSIXPackage = readVSIXPackage;
10
12
  *
11
13
  * SPDX-License-Identifier: EPL-2.0
12
14
  ********************************************************************************/
15
+ const crypto = require("crypto");
13
16
  const yauzl = require("yauzl-promise");
17
+ const promises_1 = require("stream/promises");
14
18
  async function bufferStream(stream) {
15
19
  return await new Promise((resolve, reject) => {
16
20
  const buffers = [];
@@ -37,6 +41,33 @@ async function readZip(packagePath, filter) {
37
41
  }
38
42
  return result;
39
43
  }
44
+ /**
45
+ * Computes the SHA256 digest of every non-directory entry of a zip file, keyed by its exact
46
+ * (case-preserving) name - unlike {@link readZip}, which lowercases names for its
47
+ * case-insensitive-filter callers. Used where entry names and digests are compared against another
48
+ * source of truth, such as a signature manifest that records the original names, without also
49
+ * having to hold every entry's full content in memory at once alongside it - each entry's content
50
+ * is streamed straight into a hash and discarded, one entry at a time.
51
+ */
52
+ async function hashAllZipEntries(packagePath) {
53
+ const result = new Map();
54
+ const zipfile = await yauzl.open(packagePath);
55
+ try {
56
+ for await (const entry of zipfile) {
57
+ if (entry.filename.endsWith('/')) {
58
+ continue;
59
+ }
60
+ const stream = await zipfile.openReadStream(entry);
61
+ const hash = crypto.createHash('sha256');
62
+ await (0, promises_1.pipeline)(stream, hash);
63
+ result.set(entry.filename, hash.digest('base64'));
64
+ }
65
+ }
66
+ finally {
67
+ await zipfile.close();
68
+ }
69
+ return result;
70
+ }
40
71
  async function readVSIXPackage(packagePath) {
41
72
  const map = await readZip(packagePath, name => /^extension\/package\.json$/i.test(name));
42
73
  const rawManifest = map.get('extension/package.json');
package/lib/zip.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"zip.js","sourceRoot":"","sources":["../src/zip.ts"],"names":[],"mappings":";;AAyCA,0CAQC;AAjDD;;;;;;;;kFAQkF;AAClF,uCAAuC;AAIvC,KAAK,UAAU,YAAY,CAAC,MAAgB;IAC3C,OAAO,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC5C,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;QAClD,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC7B,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;AACJ,CAAC;AAED,KAAK,UAAU,OAAO,CAAC,WAAmB,EAAE,MAAiC;IAC5E,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAC9C,IAAI,CAAC;QACJ,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YACnC,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;YAC1C,IAAI,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;gBAClB,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;gBACnD,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;gBAC1C,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAC1B,CAAC;QACF,CAAC;IACF,CAAC;YAAS,CAAC;QACV,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IAED,OAAO,MAAM,CAAC;AACf,CAAC;AAEM,KAAK,UAAU,eAAe,CAAC,WAAmB;IACxD,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC,EAAE,CAAC,6BAA6B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACzF,MAAM,WAAW,GAAG,GAAG,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;IACtD,IAAI,CAAC,WAAW,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACxC,CAAC;IAED,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAa,CAAC;AAC7D,CAAC"}
1
+ {"version":3,"file":"zip.js","sourceRoot":"","sources":["../src/zip.ts"],"names":[],"mappings":";;AAwBA,0BAiBC;AAUD,8CAkBC;AAED,0CAQC;AA/ED;;;;;;;;kFAQkF;AAClF,iCAAiC;AACjC,uCAAuC;AAEvC,8CAA2C;AAG3C,KAAK,UAAU,YAAY,CAAC,MAAgB;IAC3C,OAAO,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC5C,MAAM,OAAO,GAAa,EAAE,CAAC;QAC7B,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;QAClD,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QAC7B,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;AACJ,CAAC;AAEM,KAAK,UAAU,OAAO,CAAC,WAAmB,EAAE,MAAiC;IACnF,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAC9C,IAAI,CAAC;QACJ,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YACnC,MAAM,IAAI,GAAG,KAAK,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC;YAC1C,IAAI,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;gBAClB,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;gBACnD,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,MAAM,CAAC,CAAC;gBAC1C,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YAC1B,CAAC;QACF,CAAC;IACF,CAAC;YAAS,CAAC;QACV,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IAED,OAAO,MAAM,CAAC;AACf,CAAC;AAED;;;;;;;GAOG;AACI,KAAK,UAAU,iBAAiB,CAAC,WAAmB;IAC1D,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,MAAM,OAAO,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAC9C,IAAI,CAAC;QACJ,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YACnC,IAAI,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;gBAClC,SAAS;YACV,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;YACnD,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;YACzC,MAAM,IAAA,mBAAQ,EAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YAC7B,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;QACnD,CAAC;IACF,CAAC;YAAS,CAAC;QACV,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;IAED,OAAO,MAAM,CAAC;AACf,CAAC;AAEM,KAAK,UAAU,eAAe,CAAC,WAAmB;IACxD,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC,EAAE,CAAC,6BAA6B,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACzF,MAAM,WAAW,GAAG,GAAG,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;IACtD,IAAI,CAAC,WAAW,EAAE,CAAC;QAClB,MAAM,IAAI,KAAK,CAAC,qBAAqB,CAAC,CAAC;IACxC,CAAC;IAED,OAAO,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAa,CAAC;AAC7D,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ovsx",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "Command line interface for Eclipse Open VSX",
5
5
  "keywords": [
6
6
  "cli",
@@ -31,7 +31,7 @@
31
31
  "types": "lib/index",
32
32
  "bin": "bin/ovsx",
33
33
  "engines": {
34
- "node": ">= 20"
34
+ "node": ">=22.0.0"
35
35
  },
36
36
  "dependencies": {
37
37
  "@inquirer/prompts": "^7.10.1",
@@ -51,23 +51,27 @@
51
51
  "@stylistic/eslint-plugin": "^2.11.0",
52
52
  "@types/follow-redirects": "^1.13.1",
53
53
  "@types/is-ci": "^2.0.0",
54
- "@types/node": "^20.14.8",
54
+ "@types/node": "^22.0.0",
55
55
  "@types/semver": "^7.5.8",
56
56
  "@types/tmp": "^0.2.2",
57
57
  "@types/yauzl-promise": "^4",
58
+ "@types/yazl": "^3.3.1",
58
59
  "@typescript-eslint/eslint-plugin": "^8.15.0",
59
60
  "@typescript-eslint/parser": "^8.15.0",
60
61
  "eslint": "^9.15.0",
61
62
  "limiter": "^2.1.0",
62
63
  "rimraf": "^6.0.1",
63
- "typescript": "^5.6.3"
64
+ "typescript": "^5.6.3",
65
+ "vitest": "^4.1.11",
66
+ "yazl": "^3.3.1"
64
67
  },
65
68
  "scripts": {
66
69
  "clean": "rimraf lib",
67
70
  "prebuild": "node -p \"'export const LIB_VERSION = ' + JSON.stringify(require('./package.json').version) + ';'\" > src/version.ts",
68
71
  "build": "tsc -p ./tsconfig.json && yarn run lint",
69
72
  "watch": "tsc -w -p ./tsconfig.json",
70
- "lint": "eslint -c ./configs/eslintrc.mjs src",
73
+ "test": "vitest run",
74
+ "lint": "eslint -c ./configs/eslintrc.mjs src test",
71
75
  "prepare": "yarn run clean && yarn run prebuild && yarn run build",
72
76
  "publish:next": "yarn npm publish --tag next",
73
77
  "publish:latest": "yarn npm publish --tag latest",
package/src/get.ts CHANGED
@@ -23,7 +23,7 @@ export async function getExtension(options: GetOptions): Promise<void> {
23
23
  const registry = new Registry(options);
24
24
  const match = matchExtensionId(options.extensionId);
25
25
  if (!match) {
26
- throw new Error('The extension identifier must have the form `namespace.extension`.');
26
+ throw new Error('The extension identifier must have the form `namespace.extension` or `namespace/extension`.');
27
27
  }
28
28
 
29
29
  const extension = await registry.getMetadata(match[1], match[2], options.target);
package/src/index.ts CHANGED
@@ -19,6 +19,10 @@ export * from './publish';
19
19
  export * from './publish-options';
20
20
  export * from './registry';
21
21
  export * from './registry-options';
22
+ export * from './trusted-publishing';
23
+ export * from './trusted-publishing-options';
24
+ export * from './unpublish';
25
+ export * from './unpublish-options';
22
26
  export * from './verify-pat';
23
27
  export * from './verify-pat-options';
24
28
  export { isLicenseOk } from './check-license';
@@ -0,0 +1,25 @@
1
+ /******************************************************************************
2
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation.
3
+ *
4
+ * See the NOTICE file(s) distributed with this work for additional
5
+ * information regarding copyright ownership.
6
+ *
7
+ * This program and the accompanying materials are made available under the
8
+ * terms of the Eclipse Public License 2.0 which is available at
9
+ * https://www.eclipse.org/legal/epl-2.0.
10
+ *
11
+ * SPDX-License-Identifier: EPL-2.0
12
+ *****************************************************************************/
13
+
14
+ import { RegistryOptions } from './registry-options';
15
+
16
+ export interface ListOptions extends RegistryOptions {
17
+ /**
18
+ * Namespace whose extensions should be listed.
19
+ */
20
+ namespace: string;
21
+ /**
22
+ * Print the raw namespace metadata as JSON instead of a list.
23
+ */
24
+ json?: boolean;
25
+ }
package/src/list.ts ADDED
@@ -0,0 +1,48 @@
1
+ /******************************************************************************
2
+ * Copyright (c) 2026 Contributors to the Eclipse Foundation.
3
+ *
4
+ * See the NOTICE file(s) distributed with this work for additional
5
+ * information regarding copyright ownership.
6
+ *
7
+ * This program and the accompanying materials are made available under the
8
+ * terms of the Eclipse Public License 2.0 which is available at
9
+ * https://www.eclipse.org/legal/epl-2.0.
10
+ *
11
+ * SPDX-License-Identifier: EPL-2.0
12
+ *****************************************************************************/
13
+
14
+ import { ListOptions } from './list-options';
15
+ import { Registry } from './registry';
16
+ import { formatCount } from './table';
17
+ import { addEnvOptions } from './util';
18
+
19
+ /**
20
+ * Lists the extensions published in a namespace.
21
+ */
22
+ export async function list(options: ListOptions): Promise<void> {
23
+ addEnvOptions(options);
24
+ const registry = new Registry(options);
25
+ const namespace = await registry.getNamespace(options.namespace);
26
+ if (namespace.error) {
27
+ throw new Error(namespace.error);
28
+ }
29
+
30
+ if (options.json) {
31
+ console.log(JSON.stringify(namespace, null, 4));
32
+ return;
33
+ }
34
+
35
+ // Sorted here rather than relying on the response's key order, so the output is stable and
36
+ // scriptable regardless of how the registry happens to serialise the map.
37
+ const names = Object.keys(namespace.extensions ?? {}).sort((a, b) => a.localeCompare(b));
38
+ const verified = namespace.verified ? ' (verified)' : '';
39
+ console.log(`${namespace.name}${verified} - ${formatCount(names.length, 'extension')}`);
40
+ if (names.length === 0) {
41
+ return;
42
+ }
43
+
44
+ console.log();
45
+ for (const name of names) {
46
+ console.log(` ${namespace.name}.${name}`);
47
+ }
48
+ }
package/src/main.ts CHANGED
@@ -13,8 +13,14 @@ import * as leven from 'leven';
13
13
  import { createNamespace } from './create-namespace';
14
14
  import { verifyPat } from './verify-pat';
15
15
  import { publish } from './publish';
16
- import { handleError } from './util';
16
+ import { unpublish } from './unpublish';
17
+ import { handleError, parseNonNegativeInt } from './util';
17
18
  import { getExtension } from './get';
19
+ import { list } from './list';
20
+ import { DEFAULT_SEARCH_SIZE, SORT_KEYS, SORT_ORDERS, search } from './search';
21
+ import { show } from './show';
22
+ import { verify } from './verify';
23
+ import { verifySignature } from './verify-signature';
18
24
  import login from './login';
19
25
  import logout from './logout';
20
26
  import { LIB_VERSION } from './version';
@@ -50,12 +56,16 @@ module.exports = function (argv: string[]): void {
50
56
  .option('--baseContentUrl <url>', 'Prepend all relative links in README.md with this URL.')
51
57
  .option('--baseImagesUrl <url>', 'Prepend all relative image links in README.md with this URL.')
52
58
  .option('--yarn', 'Use yarn instead of npm while packing extension files.')
59
+ .option('--follow-symlinks', 'Recurse into symlinked directories instead of packing each symlink as a file.')
53
60
  .option('--pre-release', 'Mark this package as a pre-release')
54
61
  .option('--allow-missing-repository', 'Allow packaging an extension whose package.json has no repository field')
55
62
  .option('--no-dependencies', 'Disable dependency detection via npm or yarn')
56
63
  .option('--skip-duplicate', 'Fail silently if version already exists on the marketplace')
57
64
  .option('--packageVersion <version>', 'Version of the provided VSIX packages.')
58
- .action((extensionFile: string, { target, packagePath, baseContentUrl, baseImagesUrl, yarn, preRelease, allowMissingRepository, dependencies, skipDuplicate, packageVersion }) => {
65
+ .option('--trusted-publishing', 'Exchange an OIDC ID token for a short-lived publishing token. Enabled automatically when a CI system provides an ID token and no access token is given.')
66
+ .option('--idToken <token>', 'The OIDC ID token to exchange. Only needed on CI systems that provide the token directly, e.g. GitLab CI.')
67
+ .option('--oidcAudience <audience>', 'Audience to request for the OIDC ID token. Defaults to the registry URL.')
68
+ .action((extensionFile: string, { target, packagePath, baseContentUrl, baseImagesUrl, yarn, followSymlinks, preRelease, allowMissingRepository, dependencies, skipDuplicate, packageVersion, trustedPublishing, idToken, oidcAudience }) => {
59
69
  if (extensionFile !== undefined && packagePath !== undefined) {
60
70
  console.error('\u274c Please specify either a package file or a package path, but not both.\n');
61
71
  publishCmd.help();
@@ -70,12 +80,14 @@ module.exports = function (argv: string[]): void {
70
80
  console.warn("Ignoring option '--baseImagesUrl' for prepackaged extension.");
71
81
  if (extensionFile !== undefined && yarn !== undefined)
72
82
  console.warn("Ignoring option '--yarn' for prepackaged extension.");
83
+ if (extensionFile !== undefined && followSymlinks !== undefined)
84
+ console.warn("Ignoring option '--follow-symlinks' for prepackaged extension.");
73
85
  if (extensionFile !== undefined && packageVersion !== undefined)
74
86
  console.warn("Ignoring option '--packageVersion' for prepackaged extension.");
75
87
  if (extensionFile !== undefined && allowMissingRepository !== undefined)
76
88
  console.warn("Ignoring option '--allow-missing-repository' for prepackaged extension.");
77
89
  const { registryUrl, pat } = program.opts();
78
- publish({ extensionFile, registryUrl, pat, targets: typeof target === 'string' ? [target] : target, packagePath: typeof packagePath === 'string' ? [packagePath] : packagePath, baseContentUrl, baseImagesUrl, yarn, preRelease, allowMissingRepository, dependencies, skipDuplicate, packageVersion })
90
+ publish({ extensionFile, registryUrl, pat, targets: typeof target === 'string' ? [target] : target, packagePath: typeof packagePath === 'string' ? [packagePath] : packagePath, baseContentUrl, baseImagesUrl, yarn, followSymlinks, preRelease, allowMissingRepository, dependencies, skipDuplicate, packageVersion, trustedPublishing, idToken, oidcAudience })
79
91
  .then(results => {
80
92
  const reasons = results.filter(result => result.status === 'rejected')
81
93
  .map(rejectedResult => rejectedResult.reason);
@@ -93,6 +105,60 @@ module.exports = function (argv: string[]): void {
93
105
  });
94
106
  });
95
107
 
108
+ const unpublishCmd = program.command('unpublish [namespace.extension]');
109
+ unpublishCmd.description('Delete an extension or some of its versions from the registry.')
110
+ .option('-v, --versions <versions...>', 'Only delete the given versions.')
111
+ .option('-t, --target <targets...>', 'Only delete the given target architectures of the given versions.')
112
+ .option('-f, --force', 'Skip the confirmation prompt.')
113
+ .action((extensionId: string | undefined, { versions, target, force }) => {
114
+ const { registryUrl, pat } = program.opts();
115
+ unpublish({
116
+ extensionId,
117
+ versions: typeof versions === 'string' ? [versions] : versions,
118
+ targets: typeof target === 'string' ? [target] : target,
119
+ force,
120
+ registryUrl,
121
+ pat
122
+ }).catch(handleError(program.debug));
123
+ });
124
+
125
+ const searchCmd = program.command('search [text]');
126
+ searchCmd.description('Search the registry for extensions.')
127
+ .option('-c, --category <category>', 'Only return extensions in this category.')
128
+ .option('-t, --target <target>', 'Only return extensions built for this target architecture.')
129
+ .option(
130
+ '-s, --size <size>',
131
+ `Number of results to return (default ${DEFAULT_SEARCH_SIZE}).`,
132
+ parseNonNegativeInt
133
+ )
134
+ .option('-o, --offset <offset>', 'Index of the first result, for paging.', parseNonNegativeInt)
135
+ .option('--sort-by <key>', `Sort key: ${SORT_KEYS.join(', ')}.`)
136
+ .option('--sort-order <order>', `Sort order: ${SORT_ORDERS.join(', ')}.`)
137
+ .option('--json', 'Print the raw results as JSON.')
138
+ .action((text: string | undefined, { category, target, size, offset, sortBy, sortOrder, json }) => {
139
+ const { registryUrl } = program.opts();
140
+ search({ text, category, target, size, offset, sortBy, sortOrder, json, registryUrl })
141
+ .catch(handleError(program.debug));
142
+ });
143
+
144
+ const listCmd = program.command('list <namespace>');
145
+ listCmd.description('List the extensions published in a namespace.')
146
+ .option('--json', 'Print the raw namespace metadata as JSON.')
147
+ .action((namespace: string, { json }) => {
148
+ const { registryUrl } = program.opts();
149
+ list({ namespace, json, registryUrl }).catch(handleError(program.debug));
150
+ });
151
+
152
+ const showCmd = program.command('show <namespace.extension[@version]>');
153
+ showCmd.description('Show an extension\'s metadata.')
154
+ .option('-t, --target <target>', 'Only report on the given target architecture.')
155
+ .option('--all-versions', 'List every published version instead of the most recent few.')
156
+ .option('--json', 'Print the raw metadata as JSON.')
157
+ .action((extensionId: string, { target, allVersions, json }) => {
158
+ const { registryUrl } = program.opts();
159
+ show({ extensionId, target, allVersions, json, registryUrl }).catch(handleError(program.debug));
160
+ });
161
+
96
162
  const getCmd = program.command('get <namespace.extension>');
97
163
  getCmd.description('Download an extension or its metadata.')
98
164
  .option('-t, --target <target>', 'Target architecture')
@@ -105,6 +171,26 @@ module.exports = function (argv: string[]): void {
105
171
  .catch(handleError(program.debug));
106
172
  });
107
173
 
174
+ const verifyCmd = program.command('verify <extension.vsix>');
175
+ verifyCmd.description('Verify a downloaded package\'s signature against the registry\'s public key.')
176
+ .option('-t, --target <target>', 'Target architecture')
177
+ .action((packagePath: string, { target }) => {
178
+ const { registryUrl } = program.opts();
179
+ verify({ packagePath, target, registryUrl })
180
+ .catch(handleError(program.debug));
181
+ });
182
+
183
+ const verifySignatureCmd = program.command('verify-signature');
184
+ verifySignatureCmd.description('Verify a signature file against a package and manifest, entirely offline - like `vsce verify-signature`.')
185
+ .requiredOption('-i, --packagePath <path>', 'Path to the .vsix package.')
186
+ .requiredOption('-m, --manifestPath <path>', 'Path to the signature manifest file.')
187
+ .requiredOption('-s, --signaturePath <path>', 'Path to the signature file.')
188
+ .requiredOption('-k, --publicKeyPath <path>', 'Path to the registry\'s public key file.')
189
+ .action(({ packagePath, manifestPath, signaturePath, publicKeyPath }) => {
190
+ verifySignature({ packagePath, manifestPath, signaturePath, publicKeyPath })
191
+ .catch(handleError(program.debug));
192
+ });
193
+
108
194
  const loginCmd = program.command('login <namespace>');
109
195
  loginCmd.description('Adds a namespace to the list of known namespaces')
110
196
  .action((namespace: string) => {