vibefast-cli 0.1.1 → 0.1.3
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.
- package/README.md +3 -1
- package/cloudflare-worker/worker.js +197 -59
- package/dist/core/http.d.ts.map +1 -1
- package/dist/core/http.js +27 -3
- package/dist/core/http.js.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/scripts/sync-worker.sh +45 -0
- package/src/core/http.ts +24 -3
- package/src/index.ts +2 -1
package/README.md
CHANGED
|
@@ -17,8 +17,10 @@ npx vibefast-cli <command>
|
|
|
17
17
|
## Quick Start
|
|
18
18
|
|
|
19
19
|
1. **Login with your token:**
|
|
20
|
+
Use the same license key you received in your LemonSqueezy receipt:
|
|
21
|
+
|
|
20
22
|
```bash
|
|
21
|
-
vf login --token
|
|
23
|
+
vf login --token YOUR_LICENSE_KEY
|
|
22
24
|
```
|
|
23
25
|
|
|
24
26
|
2. **Verify your setup:**
|
|
@@ -9,6 +9,169 @@ async function sha256(str) {
|
|
|
9
9
|
.join("");
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
function parseEnvInt(value, fallback) {
|
|
13
|
+
const num = parseInt(value ?? "", 10);
|
|
14
|
+
return Number.isFinite(num) ? num : fallback;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function getDeviceLimitForVariant(variantId, env) {
|
|
18
|
+
const defaultLimit = parseEnvInt(env.LICENSE_MAX_DEVICES, 2);
|
|
19
|
+
const proId = parseEnvInt(env.VIBEFAST_PRO_ID, null);
|
|
20
|
+
const coreId = parseEnvInt(env.VIBEFAST_CORE_ID, null);
|
|
21
|
+
if (variantId === proId) {
|
|
22
|
+
return parseEnvInt(env.VIBEFAST_PRO_DEVICE_LIMIT, defaultLimit);
|
|
23
|
+
}
|
|
24
|
+
if (variantId === coreId) {
|
|
25
|
+
return parseEnvInt(env.VIBEFAST_CORE_DEVICE_LIMIT, defaultLimit);
|
|
26
|
+
}
|
|
27
|
+
return defaultLimit;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function validateLicenseWithLemon(licenseKey, env) {
|
|
31
|
+
if (!env.LEMON_API_KEY) {
|
|
32
|
+
throw new Error("LEMON_API_KEY is not configured");
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const storeId = parseEnvInt(env.LEMON_STORE_ID, null);
|
|
36
|
+
const productId = parseEnvInt(env.VIBEFAST_PRODUCT_ID, null);
|
|
37
|
+
const allowedVariants = [
|
|
38
|
+
parseEnvInt(env.VIBEFAST_PRO_ID, null),
|
|
39
|
+
parseEnvInt(env.VIBEFAST_CORE_ID, null),
|
|
40
|
+
].filter((v) => Number.isInteger(v));
|
|
41
|
+
|
|
42
|
+
const res = await fetch("https://api.lemonsqueezy.com/v1/licenses/validate", {
|
|
43
|
+
method: "POST",
|
|
44
|
+
headers: {
|
|
45
|
+
Authorization: `Bearer ${env.LEMON_API_KEY}`,
|
|
46
|
+
"Content-Type": "application/json",
|
|
47
|
+
Accept: "application/json",
|
|
48
|
+
},
|
|
49
|
+
body: JSON.stringify({ license_key: licenseKey }),
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
if (!res.ok) {
|
|
53
|
+
const detail = await res.text().catch(() => "");
|
|
54
|
+
console.error(
|
|
55
|
+
`[Worker] LemonSqueezy validation failed: ${res.status} ${res.statusText} ${detail}`
|
|
56
|
+
);
|
|
57
|
+
return {
|
|
58
|
+
ok: false,
|
|
59
|
+
status: res.status === 401 ? 401 : 502,
|
|
60
|
+
message:
|
|
61
|
+
res.status === 401
|
|
62
|
+
? "License validation is unauthorized. Check your LemonSqueezy API key."
|
|
63
|
+
: "Unable to validate license at this time.",
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const data = await res.json();
|
|
68
|
+
if (!data.valid) {
|
|
69
|
+
return {
|
|
70
|
+
ok: false,
|
|
71
|
+
status: 401,
|
|
72
|
+
message: "License key is invalid.",
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const keyData = data.license_key;
|
|
77
|
+
const meta = data.meta;
|
|
78
|
+
|
|
79
|
+
if (meta.store_id !== storeId) {
|
|
80
|
+
return {
|
|
81
|
+
ok: false,
|
|
82
|
+
status: 401,
|
|
83
|
+
message: "License does not belong to this store.",
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (meta.product_id !== productId) {
|
|
88
|
+
return {
|
|
89
|
+
ok: false,
|
|
90
|
+
status: 401,
|
|
91
|
+
message: "License is for a different product.",
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!allowedVariants.includes(meta.variant_id)) {
|
|
96
|
+
return {
|
|
97
|
+
ok: false,
|
|
98
|
+
status: 401,
|
|
99
|
+
message: "License variant is not supported.",
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (!["inactive", "active"].includes(keyData.status)) {
|
|
104
|
+
return {
|
|
105
|
+
ok: false,
|
|
106
|
+
status: 401,
|
|
107
|
+
message: `License status '${keyData.status}' is not eligible.`,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const deviceLimit = getDeviceLimitForVariant(meta.variant_id, env);
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
ok: true,
|
|
115
|
+
meta,
|
|
116
|
+
licenseStatus: keyData.status,
|
|
117
|
+
variantId: meta.variant_id,
|
|
118
|
+
deviceLimit,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function authorizeLicense(token, env) {
|
|
123
|
+
if (!token) {
|
|
124
|
+
return { ok: false, status: 401, message: "Missing license token." };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const validation = await validateLicenseWithLemon(token, env);
|
|
128
|
+
if (!validation.ok) {
|
|
129
|
+
return validation;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const tokenHash = await sha256(token);
|
|
133
|
+
const kvKey = `lic:${tokenHash}`;
|
|
134
|
+
const now = new Date().toISOString();
|
|
135
|
+
|
|
136
|
+
let licenseRecord = await env.LICENSES.get(kvKey, { type: "json" });
|
|
137
|
+
if (!licenseRecord) {
|
|
138
|
+
licenseRecord = {
|
|
139
|
+
status: "active",
|
|
140
|
+
devices: [],
|
|
141
|
+
max_devices: validation.deviceLimit,
|
|
142
|
+
variant_id: validation.variantId,
|
|
143
|
+
lastSeen: now,
|
|
144
|
+
};
|
|
145
|
+
} else {
|
|
146
|
+
licenseRecord.max_devices =
|
|
147
|
+
licenseRecord.max_devices || validation.deviceLimit;
|
|
148
|
+
licenseRecord.variant_id = validation.variantId;
|
|
149
|
+
licenseRecord.lastSeen = now;
|
|
150
|
+
if (!Array.isArray(licenseRecord.devices)) {
|
|
151
|
+
licenseRecord.devices = [];
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
await env.LICENSES.put(kvKey, JSON.stringify(licenseRecord));
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
ok: true,
|
|
159
|
+
tokenHash,
|
|
160
|
+
kvKey,
|
|
161
|
+
licenseRecord,
|
|
162
|
+
deviceLimit: validation.deviceLimit,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async function authorizeFromHeader(request, env) {
|
|
167
|
+
const authHeader = request.headers.get("Authorization");
|
|
168
|
+
if (!authHeader?.startsWith("Bearer ")) {
|
|
169
|
+
return { ok: false, status: 401, message: "Missing authorization header." };
|
|
170
|
+
}
|
|
171
|
+
const token = authHeader.slice(7).trim();
|
|
172
|
+
return authorizeLicense(token, env);
|
|
173
|
+
}
|
|
174
|
+
|
|
12
175
|
// --- Utility: JSON Response Helper ---
|
|
13
176
|
function json(data, status = 200) {
|
|
14
177
|
return new Response(JSON.stringify(data, null, 2), {
|
|
@@ -94,19 +257,20 @@ async function handleRecipeFetch(request, env) {
|
|
|
94
257
|
return json({ error: "Missing required fields" }, 400);
|
|
95
258
|
}
|
|
96
259
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}, 403);
|
|
260
|
+
const auth = await authorizeLicense(token, env);
|
|
261
|
+
if (!auth.ok) {
|
|
262
|
+
return json(
|
|
263
|
+
{
|
|
264
|
+
ok: false,
|
|
265
|
+
error: "License validation failed",
|
|
266
|
+
message: auth.message,
|
|
267
|
+
},
|
|
268
|
+
auth.status || 403
|
|
269
|
+
);
|
|
108
270
|
}
|
|
109
271
|
|
|
272
|
+
const { tokenHash, kvKey, licenseRecord } = auth;
|
|
273
|
+
|
|
110
274
|
// Check rate limit
|
|
111
275
|
if (!checkRateLimit(tokenHash)) {
|
|
112
276
|
return json({
|
|
@@ -117,19 +281,19 @@ async function handleRecipeFetch(request, env) {
|
|
|
117
281
|
}
|
|
118
282
|
|
|
119
283
|
// Enforce device slots
|
|
120
|
-
if (!
|
|
121
|
-
if (
|
|
284
|
+
if (!licenseRecord.devices.includes(device.id)) {
|
|
285
|
+
if (licenseRecord.devices.length >= (licenseRecord.max_devices || 2)) {
|
|
122
286
|
return json({
|
|
123
287
|
ok: false,
|
|
124
288
|
error: "Device limit reached",
|
|
125
|
-
message: `Maximum ${
|
|
289
|
+
message: `Maximum ${licenseRecord.max_devices || 2} devices allowed. Use 'vf devices --deactivate <id>' to free a slot.`
|
|
126
290
|
}, 403);
|
|
127
291
|
}
|
|
128
292
|
|
|
129
293
|
// Add new device
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
await env.LICENSES.put(kvKey, JSON.stringify(
|
|
294
|
+
licenseRecord.devices.push(device.id);
|
|
295
|
+
licenseRecord.lastSeen = new Date().toISOString();
|
|
296
|
+
await env.LICENSES.put(kvKey, JSON.stringify(licenseRecord));
|
|
133
297
|
console.log(`[Worker] Added device ${device.id} to license`);
|
|
134
298
|
}
|
|
135
299
|
|
|
@@ -183,18 +347,9 @@ async function handleRecipeFetch(request, env) {
|
|
|
183
347
|
// --- Handler: List Recipes ---
|
|
184
348
|
async function handleRecipesList(request, env) {
|
|
185
349
|
try {
|
|
186
|
-
const
|
|
187
|
-
if (!
|
|
188
|
-
return json({ error:
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
const token = authHeader.slice(7);
|
|
192
|
-
const tokenHash = await sha256(token);
|
|
193
|
-
const kvKey = `lic:${tokenHash}`;
|
|
194
|
-
const license = await env.LICENSES.get(kvKey, { type: "json" });
|
|
195
|
-
|
|
196
|
-
if (!license || license.status !== "active") {
|
|
197
|
-
return json({ error: "Invalid token" }, 403);
|
|
350
|
+
const auth = await authorizeFromHeader(request, env);
|
|
351
|
+
if (!auth.ok) {
|
|
352
|
+
return json({ error: auth.message }, auth.status || 403);
|
|
198
353
|
}
|
|
199
354
|
|
|
200
355
|
// List all recipes from R2
|
|
@@ -232,30 +387,21 @@ async function handleRecipesList(request, env) {
|
|
|
232
387
|
// --- Handler: List Devices ---
|
|
233
388
|
async function handleDevicesList(request, env) {
|
|
234
389
|
try {
|
|
235
|
-
const
|
|
236
|
-
if (!
|
|
237
|
-
return json({ error:
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
const token = authHeader.slice(7);
|
|
241
|
-
const tokenHash = await sha256(token);
|
|
242
|
-
const kvKey = `lic:${tokenHash}`;
|
|
243
|
-
const license = await env.LICENSES.get(kvKey, { type: "json" });
|
|
244
|
-
|
|
245
|
-
if (!license || license.status !== "active") {
|
|
246
|
-
return json({ error: "Invalid token" }, 403);
|
|
390
|
+
const auth = await authorizeFromHeader(request, env);
|
|
391
|
+
if (!auth.ok) {
|
|
392
|
+
return json({ error: auth.message }, auth.status || 403);
|
|
247
393
|
}
|
|
248
394
|
|
|
249
|
-
const devices =
|
|
395
|
+
const devices = (auth.licenseRecord.devices || []).map(id => ({
|
|
250
396
|
id,
|
|
251
397
|
os: 'unknown',
|
|
252
398
|
arch: 'unknown',
|
|
253
|
-
lastSeen:
|
|
399
|
+
lastSeen: auth.licenseRecord.lastSeen || 'unknown',
|
|
254
400
|
}));
|
|
255
401
|
|
|
256
402
|
return json({
|
|
257
403
|
devices,
|
|
258
|
-
maxDevices:
|
|
404
|
+
maxDevices: auth.licenseRecord.max_devices || 2,
|
|
259
405
|
});
|
|
260
406
|
|
|
261
407
|
} catch (err) {
|
|
@@ -267,12 +413,11 @@ async function handleDevicesList(request, env) {
|
|
|
267
413
|
// --- Handler: Deactivate Device ---
|
|
268
414
|
async function handleDeviceDeactivate(request, env) {
|
|
269
415
|
try {
|
|
270
|
-
const
|
|
271
|
-
if (!
|
|
272
|
-
return json({ error:
|
|
416
|
+
const auth = await authorizeFromHeader(request, env);
|
|
417
|
+
if (!auth.ok) {
|
|
418
|
+
return json({ error: auth.message }, auth.status || 403);
|
|
273
419
|
}
|
|
274
420
|
|
|
275
|
-
const token = authHeader.slice(7);
|
|
276
421
|
const body = await request.json();
|
|
277
422
|
const { deviceId } = body;
|
|
278
423
|
|
|
@@ -280,17 +425,10 @@ async function handleDeviceDeactivate(request, env) {
|
|
|
280
425
|
return json({ error: "Missing deviceId" }, 400);
|
|
281
426
|
}
|
|
282
427
|
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
if (!license || license.status !== "active") {
|
|
288
|
-
return json({ error: "Invalid token" }, 403);
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// Remove device
|
|
292
|
-
license.devices = license.devices.filter(id => id !== deviceId);
|
|
293
|
-
await env.LICENSES.put(kvKey, JSON.stringify(license));
|
|
428
|
+
auth.licenseRecord.devices = (auth.licenseRecord.devices || []).filter(
|
|
429
|
+
id => id !== deviceId
|
|
430
|
+
);
|
|
431
|
+
await env.LICENSES.put(auth.kvKey, JSON.stringify(auth.licenseRecord));
|
|
294
432
|
|
|
295
433
|
return json({ ok: true, message: "Device deactivated" });
|
|
296
434
|
|
package/dist/core/http.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/core/http.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE;QACN,EAAE,EAAE,MAAM,CAAC;QACX,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE;QACP,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,OAAO,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAID,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA0B3F;AAED,wBAAsB,WAAW,CAAC,iBAAiB,EAAE,MAAM,EAAE,QAAQ,UAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAyB9F;AAED,wBAAsB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/core/http.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE;QACN,EAAE,EAAE,MAAM,CAAC;QACX,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE;QACP,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,OAAO,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAID,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CA0B3F;AAED,wBAAsB,WAAW,CAAC,iBAAiB,EAAE,MAAM,EAAE,QAAQ,UAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAyB9F;AAED,wBAAsB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAwB7D;AAED,wBAAsB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAoB7D;AAED,wBAAsB,gBAAgB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBrF"}
|
package/dist/core/http.js
CHANGED
|
@@ -59,7 +59,15 @@ export async function listRecipes(token) {
|
|
|
59
59
|
},
|
|
60
60
|
});
|
|
61
61
|
if (!response.ok) {
|
|
62
|
-
|
|
62
|
+
let message = response.statusText;
|
|
63
|
+
try {
|
|
64
|
+
const body = (await response.json());
|
|
65
|
+
message = body?.error || body?.message || message;
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
// ignore JSON parse errors
|
|
69
|
+
}
|
|
70
|
+
throw new Error(`Failed to fetch recipes: ${message}`);
|
|
63
71
|
}
|
|
64
72
|
return response.json();
|
|
65
73
|
}
|
|
@@ -75,7 +83,15 @@ export async function listDevices(token) {
|
|
|
75
83
|
},
|
|
76
84
|
});
|
|
77
85
|
if (!response.ok) {
|
|
78
|
-
|
|
86
|
+
let message = response.statusText;
|
|
87
|
+
try {
|
|
88
|
+
const body = (await response.json());
|
|
89
|
+
message = body?.error || body?.message || message;
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// ignore
|
|
93
|
+
}
|
|
94
|
+
throw new Error(`Failed to fetch devices: ${message}`);
|
|
79
95
|
}
|
|
80
96
|
return response.json();
|
|
81
97
|
}
|
|
@@ -89,7 +105,15 @@ export async function deactivateDevice(token, deviceId) {
|
|
|
89
105
|
body: JSON.stringify({ deviceId }),
|
|
90
106
|
});
|
|
91
107
|
if (!response.ok) {
|
|
92
|
-
|
|
108
|
+
let message = response.statusText;
|
|
109
|
+
try {
|
|
110
|
+
const body = (await response.json());
|
|
111
|
+
message = body?.error || body?.message || message;
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
// ignore
|
|
115
|
+
}
|
|
116
|
+
throw new Error(`Failed to deactivate device: ${message}`);
|
|
93
117
|
}
|
|
94
118
|
}
|
|
95
119
|
//# sourceMappingURL=http.js.map
|
package/dist/core/http.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/core/http.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"http.js","sourceRoot":"","sources":["../../src/core/http.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from 'commander';
|
|
3
|
+
import pkg from '../package.json' assert { type: 'json' };
|
|
3
4
|
import { addCommand } from './commands/add.js';
|
|
4
5
|
import { removeCommand } from './commands/remove.js';
|
|
5
6
|
import { listCommand } from './commands/list.js';
|
|
@@ -11,7 +12,7 @@ const program = new Command();
|
|
|
11
12
|
program
|
|
12
13
|
.name('vf')
|
|
13
14
|
.description('VibeFast CLI - Install features into your monorepo')
|
|
14
|
-
.version('0.
|
|
15
|
+
.version(pkg.version || '0.0.0');
|
|
15
16
|
program.addCommand(loginCommand);
|
|
16
17
|
program.addCommand(logoutCommand);
|
|
17
18
|
program.addCommand(devicesCommand);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,IAAI,CAAC;KACV,WAAW,CAAC,oDAAoD,CAAC;KACjE,OAAO,CAAC,OAAO,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,GAAG,MAAM,iBAAiB,CAAC,SAAS,IAAI,EAAE,MAAM,EAAE,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAErD,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,IAAI,CAAC;KACV,WAAW,CAAC,oDAAoD,CAAC;KACjE,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,OAAO,CAAC,CAAC;AAEnC,OAAO,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AACjC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;AACnC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAClC,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AAChC,OAAO,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;AAC/B,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAElC,OAAO,CAAC,KAAK,EAAE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibefast-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "CLI for installing VibeFast features into your monorepo",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
|
-
"vf": "
|
|
7
|
+
"vf": "dist/index.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"build": "tsc",
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# Usage:
|
|
5
|
+
# ./scripts/sync-worker.sh
|
|
6
|
+
#
|
|
7
|
+
# This script guides you through updating worker secrets and deploying the
|
|
8
|
+
# Cloudflare worker. It does NOT read or store your secrets; you paste them
|
|
9
|
+
# interactively when prompted by wrangler.
|
|
10
|
+
|
|
11
|
+
WORKER_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/cloudflare-worker"
|
|
12
|
+
|
|
13
|
+
echo "==> Switching to worker directory: ${WORKER_DIR}"
|
|
14
|
+
cd "${WORKER_DIR}"
|
|
15
|
+
|
|
16
|
+
echo "======== Cloudflare Worker Sync ========"
|
|
17
|
+
echo "You will be prompted for the following secrets:"
|
|
18
|
+
echo " - LEMON_API_KEY (required)"
|
|
19
|
+
echo " - LEMON_STORE_ID (e.g., 190529)"
|
|
20
|
+
echo " - VIBEFAST_PRODUCT_ID (e.g., 560488)"
|
|
21
|
+
echo " - VIBEFAST_PRO_ID (e.g., 871411)"
|
|
22
|
+
echo " - VIBEFAST_CORE_ID (e.g., 871453)"
|
|
23
|
+
echo " - LICENSE_MAX_DEVICES (optional, press Enter to skip)"
|
|
24
|
+
echo
|
|
25
|
+
echo "None of these values are stored in this script."
|
|
26
|
+
echo
|
|
27
|
+
|
|
28
|
+
read -rp "Press Enter to begin entering secrets with wrangler..." _
|
|
29
|
+
|
|
30
|
+
wrangler secret put LEMON_API_KEY
|
|
31
|
+
# wrangler secret put LEMON_STORE_ID
|
|
32
|
+
# wrangler secret put VIBEFAST_PRODUCT_ID
|
|
33
|
+
# wrangler secret put VIBEFAST_PRO_ID
|
|
34
|
+
# wrangler secret put VIBEFAST_CORE_ID
|
|
35
|
+
|
|
36
|
+
# read -rp "Set LICENSE_MAX_DEVICES? (leave blank to skip) " DEVICE_LIMIT
|
|
37
|
+
# if [[ -n "${DEVICE_LIMIT}" ]]; then
|
|
38
|
+
# echo "${DEVICE_LIMIT}" | wrangler secret put LICENSE_MAX_DEVICES
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
echo
|
|
42
|
+
echo "==> Deploying worker via wrangler..."
|
|
43
|
+
wrangler deploy
|
|
44
|
+
|
|
45
|
+
echo "==> Done. Worker secrets updated and deployment triggered."
|
package/src/core/http.ts
CHANGED
|
@@ -96,7 +96,14 @@ export async function listRecipes(token: string): Promise<any> {
|
|
|
96
96
|
});
|
|
97
97
|
|
|
98
98
|
if (!response.ok) {
|
|
99
|
-
|
|
99
|
+
let message = response.statusText;
|
|
100
|
+
try {
|
|
101
|
+
const body = (await response.json()) as { error?: string; message?: string };
|
|
102
|
+
message = body?.error || body?.message || message;
|
|
103
|
+
} catch {
|
|
104
|
+
// ignore JSON parse errors
|
|
105
|
+
}
|
|
106
|
+
throw new Error(`Failed to fetch recipes: ${message}`);
|
|
100
107
|
}
|
|
101
108
|
|
|
102
109
|
return response.json();
|
|
@@ -114,7 +121,14 @@ export async function listDevices(token: string): Promise<any> {
|
|
|
114
121
|
});
|
|
115
122
|
|
|
116
123
|
if (!response.ok) {
|
|
117
|
-
|
|
124
|
+
let message = response.statusText;
|
|
125
|
+
try {
|
|
126
|
+
const body = (await response.json()) as { error?: string; message?: string };
|
|
127
|
+
message = body?.error || body?.message || message;
|
|
128
|
+
} catch {
|
|
129
|
+
// ignore
|
|
130
|
+
}
|
|
131
|
+
throw new Error(`Failed to fetch devices: ${message}`);
|
|
118
132
|
}
|
|
119
133
|
|
|
120
134
|
return response.json();
|
|
@@ -131,6 +145,13 @@ export async function deactivateDevice(token: string, deviceId: string): Promise
|
|
|
131
145
|
});
|
|
132
146
|
|
|
133
147
|
if (!response.ok) {
|
|
134
|
-
|
|
148
|
+
let message = response.statusText;
|
|
149
|
+
try {
|
|
150
|
+
const body = (await response.json()) as { error?: string; message?: string };
|
|
151
|
+
message = body?.error || body?.message || message;
|
|
152
|
+
} catch {
|
|
153
|
+
// ignore
|
|
154
|
+
}
|
|
155
|
+
throw new Error(`Failed to deactivate device: ${message}`);
|
|
135
156
|
}
|
|
136
157
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { Command } from 'commander';
|
|
4
|
+
import pkg from '../package.json' assert { type: 'json' };
|
|
4
5
|
import { addCommand } from './commands/add.js';
|
|
5
6
|
import { removeCommand } from './commands/remove.js';
|
|
6
7
|
import { listCommand } from './commands/list.js';
|
|
@@ -14,7 +15,7 @@ const program = new Command();
|
|
|
14
15
|
program
|
|
15
16
|
.name('vf')
|
|
16
17
|
.description('VibeFast CLI - Install features into your monorepo')
|
|
17
|
-
.version('0.
|
|
18
|
+
.version(pkg.version || '0.0.0');
|
|
18
19
|
|
|
19
20
|
program.addCommand(loginCommand);
|
|
20
21
|
program.addCommand(logoutCommand);
|