@lvnt/release-radar 1.7.16 → 1.8.2

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.
File without changes
File without changes
@@ -12,7 +12,9 @@
12
12
  "downloadUrl": "{{MIRROR_URL}}",
13
13
  "filename": "claude-code-{{VERSION}}-win32-x64.vsix",
14
14
  "mirror": {
15
- "sourceUrl": "marketplace-api"
15
+ "sourceUrl": "marketplace-api",
16
+ "extensionId": "anthropic.claude-code",
17
+ "targetPlatform": "win32-x64"
16
18
  }
17
19
  },
18
20
  "Claude Code CLI": {
@@ -89,5 +91,104 @@
89
91
  "displayName": "PowerShell",
90
92
  "downloadUrl": "github.com/PowerShell/PowerShell/releases/download/v{{VERSION}}/PowerShell-{{VERSION}}-win-x64.msi",
91
93
  "filename": "PowerShell-{{VERSION}}-win-x64.msi"
94
+ },
95
+ "C/C++ Extension Pack": {
96
+ "displayName": "C/C++ Extension Pack",
97
+ "downloadUrl": "{{MIRROR_URL}}",
98
+ "filename": "cpptools-extension-pack-{{VERSION}}.vsix",
99
+ "mirror": {
100
+ "sourceUrl": "marketplace-api",
101
+ "extensionId": "ms-vscode.cpptools-extension-pack"
102
+ }
103
+ },
104
+ "C/C++ Themes": {
105
+ "displayName": "C/C++ Themes",
106
+ "downloadUrl": "{{MIRROR_URL}}",
107
+ "filename": "cpptools-themes-{{VERSION}}.vsix",
108
+ "mirror": {
109
+ "sourceUrl": "marketplace-api",
110
+ "extensionId": "ms-vscode.cpptools-themes"
111
+ }
112
+ },
113
+ "YAML": {
114
+ "displayName": "YAML Extension",
115
+ "downloadUrl": "{{MIRROR_URL}}",
116
+ "filename": "vscode-yaml-{{VERSION}}.vsix",
117
+ "mirror": {
118
+ "sourceUrl": "marketplace-api",
119
+ "extensionId": "redhat.vscode-yaml"
120
+ }
121
+ },
122
+ "Python": {
123
+ "displayName": "Python Extension",
124
+ "downloadUrl": "{{MIRROR_URL}}",
125
+ "filename": "python-{{VERSION}}.vsix",
126
+ "mirror": {
127
+ "sourceUrl": "marketplace-api",
128
+ "extensionId": "ms-python.python"
129
+ }
130
+ },
131
+ "Python Debugger": {
132
+ "displayName": "Python Debugger",
133
+ "downloadUrl": "{{MIRROR_URL}}",
134
+ "filename": "debugpy-{{VERSION}}.vsix",
135
+ "mirror": {
136
+ "sourceUrl": "marketplace-api",
137
+ "extensionId": "ms-python.debugpy"
138
+ }
139
+ },
140
+ "Python Environments": {
141
+ "displayName": "Python Environments",
142
+ "downloadUrl": "{{MIRROR_URL}}",
143
+ "filename": "vscode-python-envs-{{VERSION}}.vsix",
144
+ "mirror": {
145
+ "sourceUrl": "marketplace-api",
146
+ "extensionId": "ms-python.vscode-python-envs"
147
+ }
148
+ },
149
+ "Pylance": {
150
+ "displayName": "Pylance",
151
+ "downloadUrl": "{{MIRROR_URL}}",
152
+ "filename": "vscode-pylance-{{VERSION}}.vsix",
153
+ "mirror": {
154
+ "sourceUrl": "marketplace-api",
155
+ "extensionId": "ms-python.vscode-pylance"
156
+ }
157
+ },
158
+ "isort": {
159
+ "displayName": "isort Extension",
160
+ "downloadUrl": "{{MIRROR_URL}}",
161
+ "filename": "isort-{{VERSION}}.vsix",
162
+ "mirror": {
163
+ "sourceUrl": "marketplace-api",
164
+ "extensionId": "ms-python.isort"
165
+ }
166
+ },
167
+ "Go": {
168
+ "displayName": "Go Extension",
169
+ "downloadUrl": "{{MIRROR_URL}}",
170
+ "filename": "go-{{VERSION}}.vsix",
171
+ "mirror": {
172
+ "sourceUrl": "marketplace-api",
173
+ "extensionId": "golang.go"
174
+ }
175
+ },
176
+ "GitHub Theme": {
177
+ "displayName": "GitHub Theme",
178
+ "downloadUrl": "{{MIRROR_URL}}",
179
+ "filename": "github-vscode-theme-{{VERSION}}.vsix",
180
+ "mirror": {
181
+ "sourceUrl": "marketplace-api",
182
+ "extensionId": "github.github-vscode-theme"
183
+ }
184
+ },
185
+ "Material Icon Theme": {
186
+ "displayName": "Material Icon Theme",
187
+ "downloadUrl": "{{MIRROR_URL}}",
188
+ "filename": "material-icon-theme-{{VERSION}}.vsix",
189
+ "mirror": {
190
+ "sourceUrl": "marketplace-api",
191
+ "extensionId": "pkief.material-icon-theme"
192
+ }
92
193
  }
93
194
  }
package/config/tools.json CHANGED
@@ -85,6 +85,61 @@
85
85
  "name": "PowerShell",
86
86
  "type": "github",
87
87
  "repo": "PowerShell/PowerShell"
88
+ },
89
+ {
90
+ "name": "C/C++ Extension Pack",
91
+ "type": "vscode-marketplace",
92
+ "extensionId": "ms-vscode.cpptools-extension-pack"
93
+ },
94
+ {
95
+ "name": "C/C++ Themes",
96
+ "type": "vscode-marketplace",
97
+ "extensionId": "ms-vscode.cpptools-themes"
98
+ },
99
+ {
100
+ "name": "YAML",
101
+ "type": "vscode-marketplace",
102
+ "extensionId": "redhat.vscode-yaml"
103
+ },
104
+ {
105
+ "name": "Python",
106
+ "type": "vscode-marketplace",
107
+ "extensionId": "ms-python.python"
108
+ },
109
+ {
110
+ "name": "Python Debugger",
111
+ "type": "vscode-marketplace",
112
+ "extensionId": "ms-python.debugpy"
113
+ },
114
+ {
115
+ "name": "Python Environments",
116
+ "type": "vscode-marketplace",
117
+ "extensionId": "ms-python.vscode-python-envs"
118
+ },
119
+ {
120
+ "name": "Pylance",
121
+ "type": "vscode-marketplace",
122
+ "extensionId": "ms-python.vscode-pylance"
123
+ },
124
+ {
125
+ "name": "isort",
126
+ "type": "vscode-marketplace",
127
+ "extensionId": "ms-python.isort"
128
+ },
129
+ {
130
+ "name": "Go",
131
+ "type": "vscode-marketplace",
132
+ "extensionId": "golang.go"
133
+ },
134
+ {
135
+ "name": "GitHub Theme",
136
+ "type": "vscode-marketplace",
137
+ "extensionId": "github.github-vscode-theme"
138
+ },
139
+ {
140
+ "name": "Material Icon Theme",
141
+ "type": "vscode-marketplace",
142
+ "extensionId": "pkief.material-icon-theme"
88
143
  }
89
144
  ]
90
145
  }
@@ -59,14 +59,15 @@ export class AssetMirror {
59
59
  }
60
60
  async getSourceUrl(config, version) {
61
61
  if (config.sourceUrl === 'marketplace-api') {
62
- return this.getMarketplaceVsixUrl(version);
62
+ if (!config.extensionId) {
63
+ throw new Error('extensionId is required when sourceUrl is "marketplace-api"');
64
+ }
65
+ return this.getMarketplaceVsixUrl(config.extensionId, version, config.targetPlatform);
63
66
  }
64
67
  // For direct URLs, just return as-is (curl -L will follow redirects)
65
68
  return config.sourceUrl;
66
69
  }
67
- async getMarketplaceVsixUrl(version) {
68
- const extensionId = 'anthropic.claude-code';
69
- const targetPlatform = 'win32-x64';
70
+ async getMarketplaceVsixUrl(extensionId, version, targetPlatform) {
70
71
  const query = JSON.stringify({
71
72
  filters: [{
72
73
  criteria: [{ filterType: 7, value: extensionId }],
@@ -82,13 +83,23 @@ export class AssetMirror {
82
83
  const response = execSync(cmd, { encoding: 'utf-8', timeout: 30000 });
83
84
  const data = JSON.parse(response);
84
85
  const versions = data.results?.[0]?.extensions?.[0]?.versions || [];
85
- const targetVersion = versions.find((v) => v.version === version && v.targetPlatform === targetPlatform);
86
+ // Find matching version - with or without platform filter
87
+ const targetVersion = versions.find((v) => {
88
+ if (v.version !== version)
89
+ return false;
90
+ if (targetPlatform) {
91
+ return v.targetPlatform === targetPlatform;
92
+ }
93
+ // For universal extensions, targetPlatform is undefined/null
94
+ return !v.targetPlatform;
95
+ });
86
96
  if (!targetVersion) {
87
- throw new Error(`Version ${version} for ${targetPlatform} not found in marketplace`);
97
+ const platformInfo = targetPlatform ? ` for ${targetPlatform}` : ' (universal)';
98
+ throw new Error(`Version ${version}${platformInfo} not found in marketplace for ${extensionId}`);
88
99
  }
89
100
  const vsixFile = targetVersion.files?.find((f) => f.assetType === 'Microsoft.VisualStudio.Services.VSIXPackage');
90
101
  if (!vsixFile?.source) {
91
- throw new Error('VSIX download URL not found in marketplace response');
102
+ throw new Error(`VSIX download URL not found in marketplace response for ${extensionId}`);
92
103
  }
93
104
  return vsixFile.source;
94
105
  }
@@ -12,7 +12,7 @@ vi.mock('fs', () => ({
12
12
  describe('AssetMirror', () => {
13
13
  let mirror;
14
14
  beforeEach(() => {
15
- vi.clearAllMocks();
15
+ vi.resetAllMocks();
16
16
  mirror = new AssetMirror();
17
17
  });
18
18
  describe('buildTag', () => {
@@ -74,11 +74,56 @@ describe('AssetMirror', () => {
74
74
  vi.mocked(execSync).mockReturnValueOnce('');
75
75
  vi.mocked(existsSync).mockReturnValue(true);
76
76
  const result = await mirror.mirror('Claude Code VSCode', '2.1.9', {
77
- sourceUrl: 'marketplace-api'
77
+ sourceUrl: 'marketplace-api',
78
+ extensionId: 'anthropic.claude-code',
79
+ targetPlatform: 'win32-x64'
78
80
  }, 'claude-code-{{VERSION}}-win32-x64.vsix');
79
81
  expect(result.success).toBe(true);
80
82
  expect(result.downloadUrl).toBe('github.com/lvntbkdmr/apps/releases/download/claude-code-vscode-v2.1.9/claude-code-2.1.9-win32-x64.vsix');
81
83
  });
84
+ it('handles universal marketplace extensions without targetPlatform', async () => {
85
+ const marketplaceResponse = JSON.stringify({
86
+ results: [{
87
+ extensions: [{
88
+ versions: [{
89
+ version: '1.2.3',
90
+ files: [{
91
+ assetType: 'Microsoft.VisualStudio.Services.VSIXPackage',
92
+ source: 'https://marketplace.visualstudio.com/vsix/download'
93
+ }]
94
+ }]
95
+ }]
96
+ }]
97
+ });
98
+ // 1. gh release view fails
99
+ vi.mocked(execSync).mockImplementationOnce(() => {
100
+ throw new Error('release not found');
101
+ });
102
+ // 2. curl marketplace query succeeds
103
+ vi.mocked(execSync).mockReturnValueOnce(marketplaceResponse);
104
+ // 3. curl download succeeds
105
+ vi.mocked(execSync).mockReturnValueOnce('');
106
+ // 4. gh release create succeeds
107
+ vi.mocked(execSync).mockReturnValueOnce('');
108
+ vi.mocked(existsSync).mockReturnValue(true);
109
+ const result = await mirror.mirror('GitHub Theme', '1.2.3', {
110
+ sourceUrl: 'marketplace-api',
111
+ extensionId: 'github.github-vscode-theme'
112
+ }, 'github-vscode-theme-{{VERSION}}.vsix');
113
+ expect(result.success).toBe(true);
114
+ expect(result.downloadUrl).toBe('github.com/lvntbkdmr/apps/releases/download/github-theme-v1.2.3/github-vscode-theme-1.2.3.vsix');
115
+ });
116
+ it('fails when extensionId is missing for marketplace-api', async () => {
117
+ // gh release view fails
118
+ vi.mocked(execSync).mockImplementationOnce(() => {
119
+ throw new Error('release not found');
120
+ });
121
+ const result = await mirror.mirror('Test Extension', '1.0.0', {
122
+ sourceUrl: 'marketplace-api'
123
+ }, 'test-{{VERSION}}.vsix');
124
+ expect(result.success).toBe(false);
125
+ expect(result.error).toContain('extensionId is required');
126
+ });
82
127
  it('successfully mirrors direct URL through full flow', async () => {
83
128
  // 1. gh release view fails
84
129
  vi.mocked(execSync).mockImplementationOnce(() => {
package/dist/types.d.ts CHANGED
@@ -14,6 +14,8 @@ export interface Config {
14
14
  }
15
15
  export interface MirrorConfig {
16
16
  sourceUrl: string;
17
+ extensionId?: string;
18
+ targetPlatform?: string;
17
19
  }
18
20
  export interface DownloadConfigUrl {
19
21
  type?: 'download';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvnt/release-radar",
3
- "version": "1.7.16",
3
+ "version": "1.8.2",
4
4
  "description": "Monitor tool versions and notify via Telegram when updates are detected",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -1,15 +0,0 @@
1
- export interface MirrorResult {
2
- success: boolean;
3
- downloadUrl?: string;
4
- error?: string;
5
- }
6
- export declare class VsixMirror {
7
- private repo;
8
- private extensionId;
9
- private targetPlatform;
10
- mirror(version: string): Promise<MirrorResult>;
11
- releaseExists(tag: string): Promise<boolean>;
12
- private getMarketplaceVsixUrl;
13
- private downloadVsix;
14
- private createRelease;
15
- }
@@ -1,96 +0,0 @@
1
- // src/vsix-mirror.ts
2
- import { execSync } from 'child_process';
3
- import { unlinkSync, existsSync } from 'fs';
4
- import { tmpdir } from 'os';
5
- import { join } from 'path';
6
- export class VsixMirror {
7
- repo = 'lvntbkdmr/apps';
8
- extensionId = 'anthropic.claude-code';
9
- targetPlatform = 'win32-x64';
10
- async mirror(version) {
11
- const tag = `claude-code-vsix-v${version}`;
12
- const filename = `claude-code-${version}-win32-x64.vsix`;
13
- const downloadUrl = `github.com/${this.repo}/releases/download/${tag}/${filename}`;
14
- try {
15
- // Check if release already exists
16
- if (await this.releaseExists(tag)) {
17
- console.log(`[VsixMirror] Release ${tag} already exists, skipping`);
18
- return { success: true, downloadUrl };
19
- }
20
- // Get VSIX URL from marketplace
21
- console.log(`[VsixMirror] Querying marketplace for ${this.extensionId} v${version}...`);
22
- const vsixUrl = await this.getMarketplaceVsixUrl(version);
23
- // Download VSIX to temp file
24
- const tempPath = join(tmpdir(), filename);
25
- console.log(`[VsixMirror] Downloading VSIX to ${tempPath}...`);
26
- await this.downloadVsix(vsixUrl, tempPath);
27
- // Create GitHub release with VSIX attached
28
- console.log(`[VsixMirror] Creating release ${tag}...`);
29
- await this.createRelease(tag, tempPath, filename, version);
30
- // Cleanup temp file
31
- if (existsSync(tempPath)) {
32
- unlinkSync(tempPath);
33
- }
34
- console.log(`[VsixMirror] Successfully mirrored to ${downloadUrl}`);
35
- return { success: true, downloadUrl };
36
- }
37
- catch (error) {
38
- const message = error instanceof Error ? error.message : String(error);
39
- console.error(`[VsixMirror] Failed to mirror: ${message}`);
40
- return { success: false, error: message };
41
- }
42
- }
43
- async releaseExists(tag) {
44
- try {
45
- execSync(`gh release view ${tag} --repo ${this.repo}`, {
46
- encoding: 'utf-8',
47
- stdio: ['pipe', 'pipe', 'pipe'],
48
- });
49
- return true;
50
- }
51
- catch {
52
- return false;
53
- }
54
- }
55
- async getMarketplaceVsixUrl(version) {
56
- const query = JSON.stringify({
57
- filters: [{
58
- criteria: [{ filterType: 7, value: this.extensionId }],
59
- pageNumber: 1,
60
- pageSize: 1,
61
- }],
62
- flags: 3,
63
- });
64
- const cmd = `curl -sS 'https://marketplace.visualstudio.com/_apis/public/gallery/extensionquery' ` +
65
- `-H 'Accept: application/json; api-version=7.2-preview.1' ` +
66
- `-H 'Content-Type: application/json' ` +
67
- `--data '${query}'`;
68
- const response = execSync(cmd, { encoding: 'utf-8', timeout: 30000 });
69
- const data = JSON.parse(response);
70
- const versions = data.results?.[0]?.extensions?.[0]?.versions || [];
71
- const targetVersion = versions.find((v) => v.version === version && v.targetPlatform === this.targetPlatform);
72
- if (!targetVersion) {
73
- throw new Error(`Version ${version} for ${this.targetPlatform} not found in marketplace`);
74
- }
75
- const vsixFile = targetVersion.files?.find((f) => f.assetType === 'Microsoft.VisualStudio.Services.VSIXPackage');
76
- if (!vsixFile?.source) {
77
- throw new Error('VSIX download URL not found in marketplace response');
78
- }
79
- return vsixFile.source;
80
- }
81
- async downloadVsix(url, destPath) {
82
- execSync(`curl -sS -L -o "${destPath}" "${url}"`, {
83
- encoding: 'utf-8',
84
- timeout: 300000, // 5 minutes for large files
85
- });
86
- if (!existsSync(destPath)) {
87
- throw new Error('Download failed - file not created');
88
- }
89
- }
90
- async createRelease(tag, vsixPath, filename, version) {
91
- const title = `Claude Code VSCode ${version}`;
92
- const notes = `Mirrored from VS Code Marketplace for Nexus proxy access.\n\nPlatform: win32-x64`;
93
- execSync(`gh release create "${tag}" "${vsixPath}#${filename}" ` +
94
- `--repo ${this.repo} --title "${title}" --notes "${notes}"`, { encoding: 'utf-8', timeout: 300000 });
95
- }
96
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,96 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach } from 'vitest';
2
- import { VsixMirror } from './vsix-mirror.js';
3
- import { execSync } from 'child_process';
4
- import { existsSync, unlinkSync } from 'fs';
5
- vi.mock('child_process', () => ({
6
- execSync: vi.fn()
7
- }));
8
- vi.mock('fs', () => ({
9
- existsSync: vi.fn(),
10
- unlinkSync: vi.fn()
11
- }));
12
- describe('VsixMirror', () => {
13
- let mirror;
14
- beforeEach(() => {
15
- vi.clearAllMocks();
16
- mirror = new VsixMirror();
17
- });
18
- describe('mirror', () => {
19
- it('returns existing URL if release already exists', async () => {
20
- // gh release view succeeds = release exists
21
- vi.mocked(execSync).mockReturnValueOnce(Buffer.from(''));
22
- const result = await mirror.mirror('2.1.9');
23
- expect(result.success).toBe(true);
24
- expect(result.downloadUrl).toBe('github.com/lvntbkdmr/apps/releases/download/claude-code-vsix-v2.1.9/claude-code-2.1.9-win32-x64.vsix');
25
- // Should not attempt to create release
26
- expect(execSync).toHaveBeenCalledTimes(1);
27
- });
28
- it('returns error when marketplace query fails', async () => {
29
- // gh release view fails = release does not exist
30
- vi.mocked(execSync).mockImplementationOnce(() => {
31
- throw new Error('release not found');
32
- });
33
- // curl for marketplace query fails
34
- vi.mocked(execSync).mockImplementationOnce(() => {
35
- throw new Error('network error');
36
- });
37
- const result = await mirror.mirror('2.1.9');
38
- expect(result.success).toBe(false);
39
- expect(result.error).toContain('network error');
40
- });
41
- it('successfully mirrors new version through full flow', async () => {
42
- const marketplaceResponse = JSON.stringify({
43
- results: [{
44
- extensions: [{
45
- versions: [{
46
- version: '2.1.9',
47
- targetPlatform: 'win32-x64',
48
- files: [{
49
- assetType: 'Microsoft.VisualStudio.Services.VSIXPackage',
50
- source: 'https://marketplace.visualstudio.com/vsix/download'
51
- }]
52
- }]
53
- }]
54
- }]
55
- });
56
- // 1. gh release view fails = release does not exist
57
- vi.mocked(execSync).mockImplementationOnce(() => {
58
- throw new Error('release not found');
59
- });
60
- // 2. curl marketplace query succeeds
61
- vi.mocked(execSync).mockReturnValueOnce(marketplaceResponse);
62
- // 3. curl download succeeds (returns empty)
63
- vi.mocked(execSync).mockReturnValueOnce('');
64
- // 4. gh release create succeeds
65
- vi.mocked(execSync).mockReturnValueOnce('');
66
- // Mock existsSync to return true (file exists after download)
67
- vi.mocked(existsSync).mockReturnValue(true);
68
- const result = await mirror.mirror('2.1.9');
69
- expect(result.success).toBe(true);
70
- expect(result.downloadUrl).toBe('github.com/lvntbkdmr/apps/releases/download/claude-code-vsix-v2.1.9/claude-code-2.1.9-win32-x64.vsix');
71
- expect(execSync).toHaveBeenCalledTimes(4);
72
- // Verify the gh release create was called with correct arguments
73
- const lastCall = vi.mocked(execSync).mock.calls[3][0];
74
- expect(lastCall).toContain('gh release create');
75
- expect(lastCall).toContain('claude-code-vsix-v2.1.9');
76
- expect(lastCall).toContain('--repo lvntbkdmr/apps');
77
- // Verify cleanup was called
78
- expect(unlinkSync).toHaveBeenCalled();
79
- });
80
- });
81
- describe('releaseExists', () => {
82
- it('returns true when release exists', async () => {
83
- vi.mocked(execSync).mockReturnValueOnce(Buffer.from(''));
84
- const exists = await mirror.releaseExists('claude-code-vsix-v2.1.9');
85
- expect(exists).toBe(true);
86
- expect(execSync).toHaveBeenCalledWith('gh release view claude-code-vsix-v2.1.9 --repo lvntbkdmr/apps', expect.any(Object));
87
- });
88
- it('returns false when release does not exist', async () => {
89
- vi.mocked(execSync).mockImplementationOnce(() => {
90
- throw new Error('release not found');
91
- });
92
- const exists = await mirror.releaseExists('claude-code-vsix-v2.1.9');
93
- expect(exists).toBe(false);
94
- });
95
- });
96
- });