@blimu/backend 0.7.0 → 1.1.1

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 (122) hide show
  1. package/README.md +129 -134
  2. package/dist/client.d.mts +24 -0
  3. package/dist/client.d.ts +15 -57
  4. package/dist/client.js +67 -156
  5. package/dist/client.js.map +1 -1
  6. package/dist/client.mjs +48 -0
  7. package/dist/client.mjs.map +1 -0
  8. package/dist/index.d.mts +36 -0
  9. package/dist/index.d.ts +25 -26
  10. package/dist/index.js +1046 -82
  11. package/dist/index.js.map +1 -1
  12. package/dist/index.mjs +1014 -0
  13. package/dist/index.mjs.map +1 -0
  14. package/dist/main.d.mts +19 -0
  15. package/dist/main.d.ts +19 -0
  16. package/dist/main.js +1285 -0
  17. package/dist/main.js.map +1 -0
  18. package/dist/main.mjs +1275 -0
  19. package/dist/main.mjs.map +1 -0
  20. package/dist/schema-B1usIXCr.d.mts +424 -0
  21. package/dist/schema-B1usIXCr.d.ts +424 -0
  22. package/dist/schema.d.mts +2 -0
  23. package/dist/schema.d.ts +2 -381
  24. package/dist/schema.js +17 -1
  25. package/dist/schema.js.map +1 -1
  26. package/dist/schema.mjs +1 -0
  27. package/dist/schema.mjs.map +1 -0
  28. package/dist/schema.zod-CRNAHxbc.d.mts +444 -0
  29. package/dist/schema.zod-CRNAHxbc.d.ts +444 -0
  30. package/dist/schema.zod.d.mts +2 -0
  31. package/dist/schema.zod.d.ts +2 -0
  32. package/dist/schema.zod.js +563 -0
  33. package/dist/schema.zod.js.map +1 -0
  34. package/dist/schema.zod.mjs +496 -0
  35. package/dist/schema.zod.mjs.map +1 -0
  36. package/dist/services/bulk_resources.d.mts +12 -0
  37. package/dist/services/bulk_resources.d.ts +9 -4
  38. package/dist/services/bulk_resources.js +45 -17
  39. package/dist/services/bulk_resources.js.map +1 -1
  40. package/dist/services/bulk_resources.mjs +22 -0
  41. package/dist/services/bulk_resources.mjs.map +1 -0
  42. package/dist/services/bulk_roles.d.mts +12 -0
  43. package/dist/services/bulk_roles.d.ts +9 -4
  44. package/dist/services/bulk_roles.js +45 -17
  45. package/dist/services/bulk_roles.js.map +1 -1
  46. package/dist/services/bulk_roles.mjs +22 -0
  47. package/dist/services/bulk_roles.mjs.map +1 -0
  48. package/dist/services/entitlements.d.mts +14 -0
  49. package/dist/services/entitlements.d.ts +11 -6
  50. package/dist/services/entitlements.js +69 -33
  51. package/dist/services/entitlements.js.map +1 -1
  52. package/dist/services/entitlements.mjs +46 -0
  53. package/dist/services/entitlements.mjs.map +1 -0
  54. package/dist/services/plans.d.mts +14 -0
  55. package/dist/services/plans.d.ts +11 -6
  56. package/dist/services/plans.js +67 -31
  57. package/dist/services/plans.js.map +1 -1
  58. package/dist/services/plans.mjs +44 -0
  59. package/dist/services/plans.mjs.map +1 -0
  60. package/dist/services/resource_members.d.mts +12 -0
  61. package/dist/services/resource_members.d.ts +9 -4
  62. package/dist/services/resource_members.js +45 -16
  63. package/dist/services/resource_members.js.map +1 -1
  64. package/dist/services/resource_members.mjs +22 -0
  65. package/dist/services/resource_members.mjs.map +1 -0
  66. package/dist/services/resources.d.mts +16 -0
  67. package/dist/services/resources.d.ts +13 -8
  68. package/dist/services/resources.js +91 -48
  69. package/dist/services/resources.js.map +1 -1
  70. package/dist/services/resources.mjs +68 -0
  71. package/dist/services/resources.mjs.map +1 -0
  72. package/dist/services/roles.d.mts +14 -0
  73. package/dist/services/roles.d.ts +11 -6
  74. package/dist/services/roles.js +68 -32
  75. package/dist/services/roles.js.map +1 -1
  76. package/dist/services/roles.mjs +45 -0
  77. package/dist/services/roles.mjs.map +1 -0
  78. package/dist/services/usage.d.mts +16 -0
  79. package/dist/services/usage.d.ts +13 -8
  80. package/dist/services/usage.js +93 -51
  81. package/dist/services/usage.js.map +1 -1
  82. package/dist/services/usage.mjs +70 -0
  83. package/dist/services/usage.mjs.map +1 -0
  84. package/dist/services/users.d.mts +17 -0
  85. package/dist/services/users.d.ts +13 -8
  86. package/dist/services/users.js +102 -55
  87. package/dist/services/users.js.map +1 -1
  88. package/dist/services/users.mjs +79 -0
  89. package/dist/services/users.mjs.map +1 -0
  90. package/dist/token-verifier.d.mts +35 -0
  91. package/dist/token-verifier.d.ts +8 -6
  92. package/dist/token-verifier.js +227 -171
  93. package/dist/token-verifier.js.map +1 -1
  94. package/dist/token-verifier.mjs +203 -0
  95. package/dist/token-verifier.mjs.map +1 -0
  96. package/dist/tsconfig.tsbuildinfo +1 -1
  97. package/dist/utils.d.mts +20 -0
  98. package/dist/utils.d.ts +7 -3
  99. package/dist/utils.js +50 -19
  100. package/dist/utils.js.map +1 -1
  101. package/dist/utils.mjs +29 -0
  102. package/dist/utils.mjs.map +1 -0
  103. package/package.json +37 -62
  104. package/src/client.ts +74 -0
  105. package/src/index.ts +55 -0
  106. package/src/main.ts +3 -0
  107. package/src/schema.ts +430 -0
  108. package/src/schema.zod.ts +558 -0
  109. package/src/services/bulk_resources.ts +24 -0
  110. package/src/services/bulk_roles.ts +22 -0
  111. package/src/services/entitlements.ts +58 -0
  112. package/src/services/plans.ts +57 -0
  113. package/src/services/resource_members.ts +25 -0
  114. package/src/services/resources.ts +91 -0
  115. package/src/services/roles.ts +58 -0
  116. package/src/services/usage.ts +93 -0
  117. package/src/services/users.ts +100 -0
  118. package/src/token-verifier.ts +280 -0
  119. package/src/utils.ts +56 -0
  120. package/bin/blimu +0 -0
  121. package/scripts/download-binary.js +0 -243
  122. package/scripts/postinstall.js +0 -24
@@ -0,0 +1,280 @@
1
+ import { FetchError } from 'client';
2
+ import * as crypto from 'crypto';
3
+ import * as jwt from 'jsonwebtoken';
4
+
5
+ export interface JWK {
6
+ kty: string;
7
+ use: string;
8
+ kid: string;
9
+ alg: string;
10
+ n: string;
11
+ e: string;
12
+ }
13
+
14
+ export interface JWKSet {
15
+ keys: JWK[];
16
+ }
17
+
18
+ interface CachedJWK {
19
+ key: crypto.KeyObject;
20
+ kid: string;
21
+ expiresAt: number;
22
+ }
23
+
24
+ export interface VerifyTokenOptions {
25
+ url?: string; // Direct URL to JWK endpoint (for custom scenarios)
26
+ secretKey?: string; // API key/secret key - uses runtimeApiUrl + JWK endpoint
27
+ token: string;
28
+ runtimeApiUrl?: string; // Optional override for runtime API URL
29
+ }
30
+
31
+ export interface TokenVerifierOptions {
32
+ runtimeApiUrl?: string; // Default from BLIMU_AUTH_API_URL env var
33
+ cacheTTL?: number; // Default: 1 hour
34
+ }
35
+
36
+ export class TokenVerifier {
37
+ private readonly cache = new Map<string, CachedJWK>();
38
+ private readonly cacheTTL: number;
39
+ private readonly runtimeApiUrl: string;
40
+
41
+ constructor(options?: TokenVerifierOptions) {
42
+ this.cacheTTL = options?.cacheTTL ?? 60 * 60 * 1000; // 1 hour
43
+
44
+ const blimuAuthApiUrl =
45
+ typeof process !== 'undefined' && process.env.BLIMU_AUTH_API_URL
46
+ ? process.env.BLIMU_AUTH_API_URL
47
+ : undefined;
48
+
49
+ // if we have secretKey, we can call runtime-api directly, otherwise we need to use customer specific auth-api
50
+ this.runtimeApiUrl = blimuAuthApiUrl ?? 'https://api.blimu.dev';
51
+ }
52
+
53
+ /**
54
+ * Fetch JWK Set from runtime-api
55
+ */
56
+ private async fetchJWKSet(
57
+ endpoint: string,
58
+ headers?: Record<string, string>
59
+ ): Promise<JWKSet> {
60
+ console.log(`[TokenVerifier] 📡 Fetching JWK Set from: ${endpoint}`);
61
+ if (headers) {
62
+ console.log(
63
+ `[TokenVerifier] 📡 Request headers: ${JSON.stringify(Object.keys(headers).map((k) => `${k}: ${k === 'x-api-key' ? '***' : headers[k]}`))}`
64
+ );
65
+ }
66
+
67
+ const response = await fetch(endpoint, {
68
+ method: 'GET',
69
+ headers: {
70
+ 'Content-Type': 'application/json',
71
+ ...headers,
72
+ },
73
+ });
74
+
75
+ console.log(
76
+ `[TokenVerifier] 📡 Response status: ${response.status} ${response.statusText}`
77
+ );
78
+
79
+ if (!response.ok) {
80
+ const errorText = await response.text();
81
+ console.error(
82
+ `[TokenVerifier] ❌ Failed to fetch JWKs: ${response.status} ${errorText}`
83
+ );
84
+ throw new FetchError('Failed to fetch JWKs', response.status, errorText);
85
+ }
86
+
87
+ const jwkSet = (await response.json()) as JWKSet;
88
+ console.log(
89
+ `[TokenVerifier] ✅ Successfully fetched JWK Set with ${jwkSet.keys.length} keys`
90
+ );
91
+ return jwkSet;
92
+ }
93
+
94
+ /**
95
+ * Convert JWK to KeyObject
96
+ */
97
+ private jwkToKeyObject(jwk: JWK): crypto.KeyObject {
98
+ return crypto.createPublicKey({
99
+ key: {
100
+ kty: jwk.kty,
101
+ n: jwk.n,
102
+ e: jwk.e,
103
+ alg: jwk.alg,
104
+ },
105
+ format: 'jwk',
106
+ });
107
+ }
108
+
109
+ /**
110
+ * Get public key for a specific key ID
111
+ */
112
+ private async getPublicKey(
113
+ kid: string,
114
+ cacheKey: string,
115
+ endpoint: string,
116
+ headers?: Record<string, string>
117
+ ): Promise<crypto.KeyObject> {
118
+ // Check cache first
119
+ const cached = this.cache.get(cacheKey);
120
+ if (cached && cached.expiresAt > Date.now()) {
121
+ console.log(`[TokenVerifier] ✅ Using cached key for kid: ${kid}`);
122
+ return cached.key;
123
+ }
124
+
125
+ console.log(
126
+ `[TokenVerifier] 🔍 Cache miss or expired. Fetching new key for kid: ${kid}`
127
+ );
128
+
129
+ // Fetch JWK Set
130
+ const jwkSet = await this.fetchJWKSet(endpoint, headers);
131
+
132
+ // Find the key with matching kid
133
+ const jwk = jwkSet.keys.find((k) => k.kid === kid);
134
+ if (!jwk) {
135
+ const availableKids = jwkSet.keys.map((k) => k.kid).join(', ');
136
+ console.error(
137
+ `[TokenVerifier] ❌ Key with kid '${kid}' not found in JWK Set. Available kids: ${availableKids}`
138
+ );
139
+ throw new Error(
140
+ `Key with kid '${kid}' not found in JWK Set. Available kids: ${availableKids}`
141
+ );
142
+ }
143
+
144
+ console.log(`[TokenVerifier] ✅ Found key with kid: ${kid}`);
145
+
146
+ // Convert JWK to KeyObject
147
+ const keyObject = this.jwkToKeyObject(jwk);
148
+
149
+ // Cache the key
150
+ this.cache.set(cacheKey, {
151
+ key: keyObject,
152
+ kid,
153
+ expiresAt: Date.now() + this.cacheTTL,
154
+ });
155
+
156
+ return keyObject;
157
+ }
158
+
159
+ /**
160
+ * Verify JWT token using JWKs from runtime-api
161
+ */
162
+ async verifyToken<T = any>(options: VerifyTokenOptions): Promise<T> {
163
+ const { url, secretKey, token, runtimeApiUrl } = options;
164
+
165
+ if (!url && !secretKey) {
166
+ throw new Error('Either url or secretKey must be provided');
167
+ }
168
+
169
+ if (url && secretKey) {
170
+ throw new Error('Cannot provide both url and secretKey');
171
+ }
172
+
173
+ // Decode token header to get kid (without verification)
174
+ const decoded = jwt.decode(token, { complete: true });
175
+ if (!decoded || typeof decoded === 'string') {
176
+ throw new Error('Invalid token format');
177
+ }
178
+
179
+ const header = decoded.header;
180
+ if (!header.kid) {
181
+ throw new Error('Token missing kid in header');
182
+ }
183
+
184
+ let endpoint: string;
185
+ let cacheKey: string;
186
+ let headers: Record<string, string> | undefined;
187
+
188
+ if (secretKey) {
189
+ // Use secretKey with runtimeApiUrl
190
+ const apiUrl = runtimeApiUrl ?? this.runtimeApiUrl;
191
+ endpoint = `${apiUrl}/v1/auth/.well-known/jwks.json`;
192
+ cacheKey = secretKey;
193
+ headers = {
194
+ 'x-api-key': secretKey,
195
+ };
196
+ console.log(
197
+ `[TokenVerifier] 🔍 Verifying token with kid: ${header.kid}, endpoint: ${endpoint}`
198
+ );
199
+ } else {
200
+ // Use direct URL
201
+ endpoint = url!;
202
+ cacheKey = url!;
203
+ console.log(
204
+ `[TokenVerifier] 🔍 Verifying token with kid: ${header.kid}, endpoint: ${endpoint}`
205
+ );
206
+ }
207
+
208
+ // Get public key for this kid
209
+ let publicKey: crypto.KeyObject;
210
+ try {
211
+ publicKey = await this.getPublicKey(
212
+ header.kid,
213
+ cacheKey,
214
+ endpoint,
215
+ headers
216
+ );
217
+ console.log(
218
+ `[TokenVerifier] ✅ Successfully retrieved public key for kid: ${header.kid}`
219
+ );
220
+ } catch (error) {
221
+ console.error(
222
+ `[TokenVerifier] ❌ Failed to get public key (first attempt): ${error instanceof Error ? error.message : String(error)}`
223
+ );
224
+ // If verification fails, clear cache and retry once (handles key rotation)
225
+ this.clearCache(cacheKey);
226
+ console.log(`[TokenVerifier] 🔄 Retrying after cache clear...`);
227
+ try {
228
+ publicKey = await this.getPublicKey(
229
+ header.kid,
230
+ cacheKey,
231
+ endpoint,
232
+ headers
233
+ );
234
+ console.log(
235
+ `[TokenVerifier] ✅ Successfully retrieved public key for kid: ${header.kid} (retry)`
236
+ );
237
+ } catch (retryError) {
238
+ console.error(
239
+ `[TokenVerifier] ❌ Failed to get public key (retry): ${retryError instanceof Error ? retryError.message : String(retryError)}`
240
+ );
241
+ throw retryError;
242
+ }
243
+ }
244
+
245
+ // Verify token
246
+ try {
247
+ const payload = jwt.verify(token, publicKey, {
248
+ algorithms: ['RS256'],
249
+ }) as T;
250
+ console.log(`[TokenVerifier] ✅ Token verified successfully`);
251
+ return payload;
252
+ } catch (error) {
253
+ console.error(
254
+ `[TokenVerifier] ❌ JWT verification failed: ${error instanceof Error ? error.message : String(error)}`
255
+ );
256
+ throw error;
257
+ }
258
+ }
259
+
260
+ /**
261
+ * Clear cache (useful for testing or key rotation)
262
+ */
263
+ clearCache(secretKeyOrUrl?: string): void {
264
+ if (secretKeyOrUrl) {
265
+ this.cache.delete(secretKeyOrUrl);
266
+ } else {
267
+ this.cache.clear();
268
+ }
269
+ }
270
+ }
271
+
272
+ /**
273
+ * Convenience function to verify a token
274
+ */
275
+ export async function verifyToken<T = any>(
276
+ options: VerifyTokenOptions
277
+ ): Promise<T> {
278
+ const verifier = new TokenVerifier();
279
+ return verifier.verifyToken<T>(options);
280
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,56 @@
1
+ import { parseSSEStream, parseNDJSONStream } from '@blimu/fetch';
2
+
3
+ export type PaginableQuery = { limit?: number; offset?: number } & Record<
4
+ string,
5
+ unknown
6
+ >;
7
+
8
+ export async function* paginate<T>(
9
+ fetchPage: (
10
+ query?: any,
11
+ init?: Omit<RequestInit, 'method' | 'body'>
12
+ ) => Promise<{
13
+ data?: T[];
14
+ hasMore?: boolean;
15
+ limit?: number;
16
+ offset?: number;
17
+ }>,
18
+ initialQuery: PaginableQuery = {},
19
+ pageSize = 100
20
+ ): AsyncGenerator<T, void, unknown> {
21
+ let offset = Number(initialQuery.offset ?? 0);
22
+ const limit = Number(initialQuery.limit ?? pageSize);
23
+ // shallow copy to avoid mutating caller
24
+ const baseQuery: any = { ...initialQuery };
25
+ while (true) {
26
+ const page = await fetchPage({ ...baseQuery, limit, offset });
27
+ const items = page.data ?? [];
28
+ for (const item of items) {
29
+ yield item as T;
30
+ }
31
+ if (!page.hasMore || items.length < limit) break;
32
+ offset += limit;
33
+ }
34
+ }
35
+
36
+ export async function listAll<T>(
37
+ fetchPage: (
38
+ query?: any,
39
+ init?: Omit<RequestInit, 'method' | 'body'>
40
+ ) => Promise<{
41
+ data?: T[];
42
+ hasMore?: boolean;
43
+ limit?: number;
44
+ offset?: number;
45
+ }>,
46
+ query: PaginableQuery = {},
47
+ pageSize = 100
48
+ ): Promise<T[]> {
49
+ const out: T[] = [];
50
+ for await (const item of paginate<T>(fetchPage, query, pageSize))
51
+ out.push(item);
52
+ return out;
53
+ }
54
+
55
+ // Re-export streaming parsers from @blimu/fetch
56
+ export { parseSSEStream, parseNDJSONStream };
package/bin/blimu DELETED
Binary file
@@ -1,243 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- const https = require('https');
4
- const fs = require('fs');
5
- const path = require('path');
6
- const { execSync } = require('child_process');
7
-
8
- const BIN_DIR = path.join(__dirname, '..', 'bin');
9
- const GITHUB_REPO = 'blimu-dev/blimu-cli';
10
-
11
- // Platform mapping
12
- const platformMap = {
13
- darwin: 'darwin',
14
- linux: 'linux',
15
- win32: 'windows',
16
- };
17
-
18
- // Architecture mapping
19
- const archMap = {
20
- x64: 'amd64',
21
- arm64: 'arm64',
22
- };
23
-
24
- function getPlatformInfo() {
25
- const platform = process.platform;
26
- const arch = process.arch;
27
-
28
- const goPlatform = platformMap[platform];
29
- const goArch = archMap[arch];
30
-
31
- if (!goPlatform || !goArch) {
32
- throw new Error(
33
- `Unsupported platform: ${platform}/${arch}. Supported platforms: darwin, linux, win32. Supported architectures: x64, arm64`,
34
- );
35
- }
36
-
37
- const binaryName = platform === 'win32' ? 'blimu.exe' : 'blimu';
38
- const assetName = `blimu-${goPlatform}-${goArch}${platform === 'win32' ? '.exe' : ''}`;
39
-
40
- return {
41
- platform: goPlatform,
42
- arch: goArch,
43
- binaryName,
44
- assetName,
45
- };
46
- }
47
-
48
- /**
49
- * Verify that an existing binary matches the current platform/architecture
50
- * Returns true if the binary is correct, false otherwise
51
- */
52
- function verifyBinaryArchitecture(binaryPath) {
53
- try {
54
- // Use 'file' command to check binary type (available on macOS and Linux)
55
- const output = execSync(`file "${binaryPath}"`, { encoding: 'utf8' });
56
-
57
- const platform = process.platform;
58
- const arch = process.arch;
59
-
60
- if (platform === 'darwin') {
61
- // macOS: should be "Mach-O 64-bit executable arm64" or "Mach-O 64-bit executable x86_64"
62
- const expectedArch = arch === 'arm64' ? 'arm64' : 'x86_64';
63
- if (!output.includes('Mach-O') || !output.includes(expectedArch)) {
64
- return false;
65
- }
66
- } else if (platform === 'linux') {
67
- // Linux: should be "ELF 64-bit LSB executable, x86-64" or "ELF 64-bit LSB executable, ARM aarch64"
68
- const expectedArch = arch === 'arm64' ? 'ARM aarch64' : 'x86-64';
69
- if (!output.includes('ELF') || !output.includes(expectedArch)) {
70
- return false;
71
- }
72
- } else if (platform === 'win32') {
73
- // Windows: should be "PE32+ executable"
74
- if (!output.includes('PE32+')) {
75
- return false;
76
- }
77
- }
78
-
79
- return true;
80
- } catch (err) {
81
- // If 'file' command fails or is not available, assume binary is incorrect to be safe
82
- // This will trigger a re-download
83
- return false;
84
- }
85
- }
86
-
87
- function downloadFile(url, dest) {
88
- return new Promise((resolve, reject) => {
89
- const file = fs.createWriteStream(dest);
90
- https
91
- .get(url, (response) => {
92
- if (response.statusCode === 302 || response.statusCode === 301) {
93
- // Follow redirect
94
- return downloadFile(response.headers.location, dest).then(resolve).catch(reject);
95
- }
96
- if (response.statusCode !== 200) {
97
- file.close();
98
- fs.unlinkSync(dest);
99
- reject(new Error(`Failed to download: ${response.statusCode} ${response.statusMessage}`));
100
- return;
101
- }
102
- response.pipe(file);
103
- file.on('finish', () => {
104
- file.close();
105
- resolve();
106
- });
107
- })
108
- .on('error', (err) => {
109
- file.close();
110
- if (fs.existsSync(dest)) {
111
- fs.unlinkSync(dest);
112
- }
113
- reject(err);
114
- });
115
- });
116
- }
117
-
118
- function getReleaseUrl(version, assetName) {
119
- return new Promise((resolve, reject) => {
120
- // Always use latest release - version parameter is ignored
121
- const tag = 'latest';
122
- const url = `https://api.github.com/repos/${GITHUB_REPO}/releases/${tag}`;
123
-
124
- https
125
- .get(
126
- url,
127
- {
128
- headers: {
129
- 'User-Agent': 'blimu-ts-installer',
130
- Accept: 'application/vnd.github.v3+json',
131
- },
132
- },
133
- (res) => {
134
- let data = '';
135
- res.on('data', (chunk) => {
136
- data += chunk;
137
- });
138
- res.on('end', () => {
139
- if (res.statusCode === 404) {
140
- reject(
141
- new Error(
142
- `Latest CLI release not found. Please check the blimu-cli repository for available releases.`,
143
- ),
144
- );
145
- return;
146
- }
147
- if (res.statusCode !== 200) {
148
- reject(
149
- new Error(`Failed to fetch latest release: ${res.statusCode} ${res.statusMessage}`),
150
- );
151
- return;
152
- }
153
- try {
154
- const release = JSON.parse(data);
155
- const asset = release.assets.find((a) => a.name === assetName);
156
- if (!asset) {
157
- reject(new Error(`Asset ${assetName} not found in latest release`));
158
- return;
159
- }
160
- resolve(asset.browser_download_url);
161
- } catch (err) {
162
- reject(err);
163
- }
164
- });
165
- },
166
- )
167
- .on('error', reject);
168
- });
169
- }
170
-
171
- async function main(version) {
172
- try {
173
- // Always use latest version - version parameter is ignored for consistency
174
- // This ensures users always get the latest CLI features and fixes
175
- version = null;
176
-
177
- const { binaryName, assetName } = getPlatformInfo();
178
-
179
- // Create bin directory if it doesn't exist
180
- if (!fs.existsSync(BIN_DIR)) {
181
- fs.mkdirSync(BIN_DIR, { recursive: true });
182
- }
183
-
184
- const binaryPath = path.join(BIN_DIR, binaryName);
185
-
186
- // Check if binary already exists and is executable
187
- if (fs.existsSync(binaryPath)) {
188
- // Verify the binary matches the current platform/architecture
189
- const isCorrectArchitecture = verifyBinaryArchitecture(binaryPath);
190
-
191
- if (isCorrectArchitecture) {
192
- try {
193
- // Try to make it executable (Unix only)
194
- if (process.platform !== 'win32') {
195
- fs.chmodSync(binaryPath, 0o755);
196
- }
197
- console.log(`✅ Blimu CLI binary already exists at ${binaryPath}`);
198
- return;
199
- } catch (err) {
200
- // If we can't check/update permissions, continue to download
201
- }
202
- } else {
203
- // Binary exists but is for wrong platform/architecture - delete it
204
- console.log(`⚠️ Existing binary is for wrong platform/architecture, will re-download...`);
205
- try {
206
- fs.unlinkSync(binaryPath);
207
- } catch (err) {
208
- console.warn(`Warning: Could not delete incorrect binary: ${err.message}`);
209
- }
210
- }
211
- }
212
-
213
- console.log(
214
- `📥 Downloading Blimu CLI binary (latest) for ${process.platform}/${process.arch}...`,
215
- );
216
-
217
- // Get release download URL
218
- const downloadUrl = await getReleaseUrl(version, assetName);
219
- console.log(`🔗 Download URL: ${downloadUrl}`);
220
-
221
- // Download the binary
222
- await downloadFile(downloadUrl, binaryPath);
223
-
224
- // Make binary executable (Unix only)
225
- if (process.platform !== 'win32') {
226
- fs.chmodSync(binaryPath, 0o755);
227
- }
228
-
229
- console.log(`✅ Blimu CLI binary downloaded successfully to ${binaryPath}`);
230
- } catch (error) {
231
- console.error(`❌ Failed to download Blimu CLI binary: ${error.message}`);
232
- console.error(`\nYou can manually install it by running:`);
233
- console.error(` go install github.com/blimu-dev/blimu-cli/cmd/blimucli@latest`);
234
- console.error(`\nOr download from: https://github.com/${GITHUB_REPO}/releases/latest`);
235
- process.exit(1);
236
- }
237
- }
238
-
239
- if (require.main === module) {
240
- main();
241
- }
242
-
243
- module.exports = { main, getPlatformInfo };
@@ -1,24 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- const path = require('path');
4
- const fs = require('fs');
5
- const { main } = require('./download-binary');
6
-
7
- // Only run download if we're in a production install (not in development)
8
- // Check if we're in node_modules (production install) or in the repo (development)
9
- const isProductionInstall = __dirname.includes('node_modules');
10
-
11
- if (isProductionInstall || process.env.BLIMU_DOWNLOAD_CLI !== 'false') {
12
- // Always download the latest CLI version
13
- main(null).catch((error) => {
14
- // Fail the install if binary download fails
15
- console.error('Error: Failed to download Blimu CLI binary during install.');
16
- console.error(error.message);
17
- console.error('\nThe package will download the latest available CLI version.');
18
- console.error('\nYou can manually install it by running:');
19
- console.error(' go install github.com/blimu-dev/blimu-cli/cmd/blimucli@latest');
20
- process.exit(1);
21
- });
22
- } else {
23
- console.log('Skipping Blimu CLI binary download (development mode)');
24
- }