@keepgoingdev/cli 0.2.1 → 0.3.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.
- package/dist/index.js +255 -64
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -57,14 +57,12 @@ import { promisify } from "util";
|
|
|
57
57
|
var execFileAsync = promisify(execFile);
|
|
58
58
|
function findGitRoot(startPath) {
|
|
59
59
|
try {
|
|
60
|
-
const
|
|
60
|
+
const toplevel = execFileSync("git", ["rev-parse", "--show-toplevel"], {
|
|
61
61
|
cwd: startPath,
|
|
62
62
|
encoding: "utf-8",
|
|
63
63
|
timeout: 5e3
|
|
64
64
|
}).trim();
|
|
65
|
-
|
|
66
|
-
const absoluteGitDir = path.isAbsolute(gitCommonDir) ? gitCommonDir : path.resolve(startPath, gitCommonDir);
|
|
67
|
-
return path.dirname(absoluteGitDir);
|
|
65
|
+
return toplevel || startPath;
|
|
68
66
|
} catch {
|
|
69
67
|
return startPath;
|
|
70
68
|
}
|
|
@@ -132,22 +130,34 @@ function getRecentSessions(allSessions, count = RECENT_SESSION_COUNT) {
|
|
|
132
130
|
// ../../packages/shared/src/storage.ts
|
|
133
131
|
import fs from "fs";
|
|
134
132
|
import path2 from "path";
|
|
135
|
-
import { randomUUID as randomUUID2 } from "crypto";
|
|
133
|
+
import { randomUUID as randomUUID2, createHash } from "crypto";
|
|
136
134
|
var STORAGE_DIR = ".keepgoing";
|
|
137
135
|
var META_FILE = "meta.json";
|
|
138
136
|
var SESSIONS_FILE = "sessions.json";
|
|
139
137
|
var STATE_FILE = "state.json";
|
|
138
|
+
var CURRENT_TASKS_FILE = "current-tasks.json";
|
|
139
|
+
var STALE_SESSION_MS = 2 * 60 * 60 * 1e3;
|
|
140
|
+
function pruneStaleTasks(tasks) {
|
|
141
|
+
const now = Date.now();
|
|
142
|
+
return tasks.filter((t) => {
|
|
143
|
+
if (t.sessionActive) return true;
|
|
144
|
+
const updatedAt = new Date(t.updatedAt).getTime();
|
|
145
|
+
return !isNaN(updatedAt) && now - updatedAt < STALE_SESSION_MS;
|
|
146
|
+
});
|
|
147
|
+
}
|
|
140
148
|
var KeepGoingWriter = class {
|
|
141
149
|
storagePath;
|
|
142
150
|
sessionsFilePath;
|
|
143
151
|
stateFilePath;
|
|
144
152
|
metaFilePath;
|
|
153
|
+
currentTasksFilePath;
|
|
145
154
|
constructor(workspacePath) {
|
|
146
155
|
const mainRoot = resolveStorageRoot(workspacePath);
|
|
147
156
|
this.storagePath = path2.join(mainRoot, STORAGE_DIR);
|
|
148
157
|
this.sessionsFilePath = path2.join(this.storagePath, SESSIONS_FILE);
|
|
149
158
|
this.stateFilePath = path2.join(this.storagePath, STATE_FILE);
|
|
150
159
|
this.metaFilePath = path2.join(this.storagePath, META_FILE);
|
|
160
|
+
this.currentTasksFilePath = path2.join(this.storagePath, CURRENT_TASKS_FILE);
|
|
151
161
|
}
|
|
152
162
|
ensureDir() {
|
|
153
163
|
if (!fs.existsSync(this.storagePath)) {
|
|
@@ -208,82 +218,214 @@ var KeepGoingWriter = class {
|
|
|
208
218
|
}
|
|
209
219
|
fs.writeFileSync(this.metaFilePath, JSON.stringify(meta, null, 2), "utf-8");
|
|
210
220
|
}
|
|
221
|
+
// ---------------------------------------------------------------------------
|
|
222
|
+
// Multi-session API
|
|
223
|
+
// ---------------------------------------------------------------------------
|
|
224
|
+
/** Read all current tasks from current-tasks.json. Auto-prunes stale sessions. */
|
|
225
|
+
readCurrentTasks() {
|
|
226
|
+
try {
|
|
227
|
+
if (fs.existsSync(this.currentTasksFilePath)) {
|
|
228
|
+
const raw = JSON.parse(fs.readFileSync(this.currentTasksFilePath, "utf-8"));
|
|
229
|
+
const tasks = Array.isArray(raw) ? raw : raw.tasks ?? [];
|
|
230
|
+
return this.pruneStale(tasks);
|
|
231
|
+
}
|
|
232
|
+
} catch {
|
|
233
|
+
}
|
|
234
|
+
return [];
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Upsert a session task by sessionId into current-tasks.json.
|
|
238
|
+
* If no sessionId is present on the task, generates one.
|
|
239
|
+
*/
|
|
240
|
+
upsertSession(update) {
|
|
241
|
+
this.ensureDir();
|
|
242
|
+
this.upsertSessionCore(update);
|
|
243
|
+
}
|
|
244
|
+
/** Core upsert logic: merges the update into current-tasks.json and returns the pruned task list. */
|
|
245
|
+
upsertSessionCore(update) {
|
|
246
|
+
this.ensureDir();
|
|
247
|
+
const sessionId = update.sessionId || generateSessionId(update);
|
|
248
|
+
const tasks = this.readAllTasksRaw();
|
|
249
|
+
const existingIdx = tasks.findIndex((t) => t.sessionId === sessionId);
|
|
250
|
+
let merged;
|
|
251
|
+
if (existingIdx >= 0) {
|
|
252
|
+
const existing = tasks[existingIdx];
|
|
253
|
+
merged = { ...existing, ...update, sessionId };
|
|
254
|
+
tasks[existingIdx] = merged;
|
|
255
|
+
} else {
|
|
256
|
+
merged = { ...update, sessionId };
|
|
257
|
+
tasks.push(merged);
|
|
258
|
+
}
|
|
259
|
+
const pruned = this.pruneStale(tasks);
|
|
260
|
+
this.writeTasksFile(pruned);
|
|
261
|
+
return pruned;
|
|
262
|
+
}
|
|
263
|
+
/** Remove a specific session by ID. */
|
|
264
|
+
removeSession(sessionId) {
|
|
265
|
+
const tasks = this.readAllTasksRaw().filter((t) => t.sessionId !== sessionId);
|
|
266
|
+
this.writeTasksFile(tasks);
|
|
267
|
+
}
|
|
268
|
+
/** Get all active sessions (sessionActive=true and within stale threshold). */
|
|
269
|
+
getActiveSessions() {
|
|
270
|
+
return this.readCurrentTasks().filter((t) => t.sessionActive);
|
|
271
|
+
}
|
|
272
|
+
/** Get a specific session by ID. */
|
|
273
|
+
getSession(sessionId) {
|
|
274
|
+
return this.readCurrentTasks().find((t) => t.sessionId === sessionId);
|
|
275
|
+
}
|
|
276
|
+
// ---------------------------------------------------------------------------
|
|
277
|
+
// Private helpers
|
|
278
|
+
// ---------------------------------------------------------------------------
|
|
279
|
+
readAllTasksRaw() {
|
|
280
|
+
try {
|
|
281
|
+
if (fs.existsSync(this.currentTasksFilePath)) {
|
|
282
|
+
const raw = JSON.parse(fs.readFileSync(this.currentTasksFilePath, "utf-8"));
|
|
283
|
+
return Array.isArray(raw) ? [...raw] : [...raw.tasks ?? []];
|
|
284
|
+
}
|
|
285
|
+
} catch {
|
|
286
|
+
}
|
|
287
|
+
return [];
|
|
288
|
+
}
|
|
289
|
+
pruneStale(tasks) {
|
|
290
|
+
return pruneStaleTasks(tasks);
|
|
291
|
+
}
|
|
292
|
+
writeTasksFile(tasks) {
|
|
293
|
+
const data = { version: 1, tasks };
|
|
294
|
+
fs.writeFileSync(this.currentTasksFilePath, JSON.stringify(data, null, 2), "utf-8");
|
|
295
|
+
}
|
|
211
296
|
};
|
|
297
|
+
function generateSessionId(context) {
|
|
298
|
+
const parts = [
|
|
299
|
+
context.worktreePath || context.workspaceRoot || "",
|
|
300
|
+
context.agentLabel || "",
|
|
301
|
+
context.branch || ""
|
|
302
|
+
].filter(Boolean);
|
|
303
|
+
if (parts.length === 0) {
|
|
304
|
+
return randomUUID2();
|
|
305
|
+
}
|
|
306
|
+
const hash = createHash("sha256").update(parts.join("|")).digest("hex").slice(0, 12);
|
|
307
|
+
return `ses_${hash}`;
|
|
308
|
+
}
|
|
212
309
|
|
|
213
310
|
// ../../packages/shared/src/decisionStorage.ts
|
|
214
|
-
import
|
|
215
|
-
import
|
|
216
|
-
|
|
217
|
-
// ../../packages/shared/src/featureGate.ts
|
|
218
|
-
var DefaultFeatureGate = class {
|
|
219
|
-
isEnabled(_feature) {
|
|
220
|
-
return true;
|
|
221
|
-
}
|
|
222
|
-
};
|
|
223
|
-
var currentGate = new DefaultFeatureGate();
|
|
311
|
+
import fs3 from "fs";
|
|
312
|
+
import path4 from "path";
|
|
224
313
|
|
|
225
314
|
// ../../packages/shared/src/license.ts
|
|
226
315
|
import crypto from "crypto";
|
|
227
|
-
import
|
|
316
|
+
import fs2 from "fs";
|
|
228
317
|
import os from "os";
|
|
229
|
-
import
|
|
318
|
+
import path3 from "path";
|
|
230
319
|
var LICENSE_FILE = "license.json";
|
|
231
320
|
var DEVICE_ID_FILE = "device-id";
|
|
232
321
|
function getGlobalLicenseDir() {
|
|
233
|
-
return
|
|
322
|
+
return path3.join(os.homedir(), ".keepgoing");
|
|
234
323
|
}
|
|
235
324
|
function getGlobalLicensePath() {
|
|
236
|
-
return
|
|
325
|
+
return path3.join(getGlobalLicenseDir(), LICENSE_FILE);
|
|
237
326
|
}
|
|
238
327
|
function getDeviceId() {
|
|
239
328
|
const dir = getGlobalLicenseDir();
|
|
240
|
-
const filePath =
|
|
329
|
+
const filePath = path3.join(dir, DEVICE_ID_FILE);
|
|
241
330
|
try {
|
|
242
|
-
const existing =
|
|
331
|
+
const existing = fs2.readFileSync(filePath, "utf-8").trim();
|
|
243
332
|
if (existing) return existing;
|
|
244
333
|
} catch {
|
|
245
334
|
}
|
|
246
335
|
const id = crypto.randomUUID();
|
|
247
|
-
if (!
|
|
248
|
-
|
|
336
|
+
if (!fs2.existsSync(dir)) {
|
|
337
|
+
fs2.mkdirSync(dir, { recursive: true });
|
|
249
338
|
}
|
|
250
|
-
|
|
339
|
+
fs2.writeFileSync(filePath, id, "utf-8");
|
|
251
340
|
return id;
|
|
252
341
|
}
|
|
253
|
-
|
|
342
|
+
var DECISION_DETECTION_VARIANT_ID = 1361527;
|
|
343
|
+
var SESSION_AWARENESS_VARIANT_ID = 1366510;
|
|
344
|
+
var TEST_DECISION_DETECTION_VARIANT_ID = 1345647;
|
|
345
|
+
var TEST_SESSION_AWARENESS_VARIANT_ID = 1365992;
|
|
346
|
+
var VARIANT_FEATURE_MAP = {
|
|
347
|
+
[DECISION_DETECTION_VARIANT_ID]: ["decisions"],
|
|
348
|
+
[SESSION_AWARENESS_VARIANT_ID]: ["session-awareness"],
|
|
349
|
+
[TEST_DECISION_DETECTION_VARIANT_ID]: ["decisions"],
|
|
350
|
+
[TEST_SESSION_AWARENESS_VARIANT_ID]: ["session-awareness"]
|
|
351
|
+
// Future bundle: [BUNDLE_VARIANT_ID]: ['decisions', 'session-awareness'],
|
|
352
|
+
};
|
|
353
|
+
var KNOWN_VARIANT_IDS = new Set(Object.keys(VARIANT_FEATURE_MAP).map(Number));
|
|
354
|
+
function getVariantLabel(variantId) {
|
|
355
|
+
const features = VARIANT_FEATURE_MAP[variantId];
|
|
356
|
+
if (!features) return "Unknown Add-on";
|
|
357
|
+
if (features.includes("decisions") && features.includes("session-awareness")) return "Pro Bundle";
|
|
358
|
+
if (features.includes("decisions")) return "Decision Detection";
|
|
359
|
+
if (features.includes("session-awareness")) return "Session Awareness";
|
|
360
|
+
return "Pro Add-on";
|
|
361
|
+
}
|
|
362
|
+
var _cachedStore;
|
|
363
|
+
var _cacheTimestamp = 0;
|
|
364
|
+
var LICENSE_CACHE_TTL_MS = 2e3;
|
|
365
|
+
function readLicenseStore() {
|
|
366
|
+
const now = Date.now();
|
|
367
|
+
if (_cachedStore && now - _cacheTimestamp < LICENSE_CACHE_TTL_MS) {
|
|
368
|
+
return _cachedStore;
|
|
369
|
+
}
|
|
254
370
|
const licensePath = getGlobalLicensePath();
|
|
371
|
+
let store;
|
|
255
372
|
try {
|
|
256
|
-
if (!
|
|
257
|
-
|
|
373
|
+
if (!fs2.existsSync(licensePath)) {
|
|
374
|
+
store = { version: 2, licenses: [] };
|
|
375
|
+
} else {
|
|
376
|
+
const raw = fs2.readFileSync(licensePath, "utf-8");
|
|
377
|
+
const data = JSON.parse(raw);
|
|
378
|
+
if (data?.version === 2 && Array.isArray(data.licenses)) {
|
|
379
|
+
store = data;
|
|
380
|
+
} else {
|
|
381
|
+
store = { version: 2, licenses: [] };
|
|
382
|
+
}
|
|
258
383
|
}
|
|
259
|
-
const raw = fs3.readFileSync(licensePath, "utf-8");
|
|
260
|
-
return JSON.parse(raw);
|
|
261
384
|
} catch {
|
|
262
|
-
|
|
385
|
+
store = { version: 2, licenses: [] };
|
|
263
386
|
}
|
|
387
|
+
_cachedStore = store;
|
|
388
|
+
_cacheTimestamp = now;
|
|
389
|
+
return store;
|
|
264
390
|
}
|
|
265
|
-
function
|
|
391
|
+
function writeLicenseStore(store) {
|
|
266
392
|
const dirPath = getGlobalLicenseDir();
|
|
267
|
-
if (!
|
|
268
|
-
|
|
393
|
+
if (!fs2.existsSync(dirPath)) {
|
|
394
|
+
fs2.mkdirSync(dirPath, { recursive: true });
|
|
269
395
|
}
|
|
270
|
-
const licensePath =
|
|
271
|
-
|
|
396
|
+
const licensePath = path3.join(dirPath, LICENSE_FILE);
|
|
397
|
+
fs2.writeFileSync(licensePath, JSON.stringify(store, null, 2), "utf-8");
|
|
398
|
+
_cachedStore = store;
|
|
399
|
+
_cacheTimestamp = Date.now();
|
|
272
400
|
}
|
|
273
|
-
function
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
401
|
+
function addLicenseEntry(entry) {
|
|
402
|
+
const store = readLicenseStore();
|
|
403
|
+
const idx = store.licenses.findIndex((l) => l.licenseKey === entry.licenseKey);
|
|
404
|
+
if (idx >= 0) {
|
|
405
|
+
store.licenses[idx] = entry;
|
|
406
|
+
} else {
|
|
407
|
+
store.licenses.push(entry);
|
|
280
408
|
}
|
|
409
|
+
writeLicenseStore(store);
|
|
281
410
|
}
|
|
282
|
-
function
|
|
283
|
-
|
|
411
|
+
function removeLicenseEntry(licenseKey) {
|
|
412
|
+
const store = readLicenseStore();
|
|
413
|
+
store.licenses = store.licenses.filter((l) => l.licenseKey !== licenseKey);
|
|
414
|
+
writeLicenseStore(store);
|
|
415
|
+
}
|
|
416
|
+
function getActiveLicenses() {
|
|
417
|
+
return readLicenseStore().licenses.filter((l) => l.status === "active");
|
|
284
418
|
}
|
|
285
419
|
var REVALIDATION_THRESHOLD_MS = 24 * 60 * 60 * 1e3;
|
|
286
420
|
|
|
421
|
+
// ../../packages/shared/src/featureGate.ts
|
|
422
|
+
var DefaultFeatureGate = class {
|
|
423
|
+
isEnabled(_feature) {
|
|
424
|
+
return true;
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
var currentGate = new DefaultFeatureGate();
|
|
428
|
+
|
|
287
429
|
// ../../packages/shared/src/licenseClient.ts
|
|
288
430
|
var BASE_URL = "https://api.lemonsqueezy.com/v1/licenses";
|
|
289
431
|
var REQUEST_TIMEOUT_MS = 15e3;
|
|
@@ -334,13 +476,21 @@ async function activateLicense(licenseKey, instanceName, options) {
|
|
|
334
476
|
}
|
|
335
477
|
return { valid: false, error: productError };
|
|
336
478
|
}
|
|
479
|
+
if (data.meta?.variant_id && !KNOWN_VARIANT_IDS.has(data.meta.variant_id)) {
|
|
480
|
+
if (data.license_key?.key && data.instance?.id) {
|
|
481
|
+
await deactivateLicense(data.license_key.key, data.instance.id);
|
|
482
|
+
}
|
|
483
|
+
return { valid: false, error: "This license key is for an unrecognized add-on variant. Please update KeepGoing or contact support." };
|
|
484
|
+
}
|
|
337
485
|
}
|
|
338
486
|
return {
|
|
339
487
|
valid: true,
|
|
340
488
|
licenseKey: data.license_key?.key,
|
|
341
489
|
instanceId: data.instance?.id,
|
|
342
490
|
customerName: data.meta?.customer_name,
|
|
343
|
-
productName: data.meta?.product_name
|
|
491
|
+
productName: data.meta?.product_name,
|
|
492
|
+
variantId: data.meta?.variant_id,
|
|
493
|
+
variantName: data.meta?.variant_name
|
|
344
494
|
};
|
|
345
495
|
} catch (err) {
|
|
346
496
|
const message = err instanceof Error && err.name === "AbortError" ? "Request timed out. Please check your network connection and try again." : err instanceof Error ? err.message : "Network error";
|
|
@@ -483,7 +633,7 @@ import { spawn } from "child_process";
|
|
|
483
633
|
import { readFileSync, existsSync } from "fs";
|
|
484
634
|
import path6 from "path";
|
|
485
635
|
import os2 from "os";
|
|
486
|
-
var CLI_VERSION = "0.
|
|
636
|
+
var CLI_VERSION = "0.3.0";
|
|
487
637
|
var NPM_REGISTRY_URL = "https://registry.npmjs.org/@keepgoingdev/cli/latest";
|
|
488
638
|
var FETCH_TIMEOUT_MS = 5e3;
|
|
489
639
|
var CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
@@ -759,10 +909,14 @@ async function activateCommand({ licenseKey }) {
|
|
|
759
909
|
console.error("Usage: keepgoing activate <license-key>");
|
|
760
910
|
process.exit(1);
|
|
761
911
|
}
|
|
762
|
-
const
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
912
|
+
const store = readLicenseStore();
|
|
913
|
+
const existingForKey = store.licenses.find(
|
|
914
|
+
(l) => l.status === "active" && l.licenseKey === licenseKey
|
|
915
|
+
);
|
|
916
|
+
if (existingForKey) {
|
|
917
|
+
const label2 = getVariantLabel(existingForKey.variantId);
|
|
918
|
+
const who2 = existingForKey.customerName ? ` (${existingForKey.customerName})` : "";
|
|
919
|
+
console.log(`${label2} is already active${who2}.`);
|
|
766
920
|
return;
|
|
767
921
|
}
|
|
768
922
|
console.log("Activating license...");
|
|
@@ -771,35 +925,72 @@ async function activateCommand({ licenseKey }) {
|
|
|
771
925
|
console.error(`Activation failed: ${result.error ?? "unknown error"}`);
|
|
772
926
|
process.exit(1);
|
|
773
927
|
}
|
|
928
|
+
const variantId = result.variantId;
|
|
929
|
+
const existingForVariant = store.licenses.find(
|
|
930
|
+
(l) => l.status === "active" && l.variantId === variantId
|
|
931
|
+
);
|
|
932
|
+
if (existingForVariant) {
|
|
933
|
+
const label2 = getVariantLabel(variantId);
|
|
934
|
+
const who2 = existingForVariant.customerName ? ` (${existingForVariant.customerName})` : "";
|
|
935
|
+
console.log(`${label2} is already active${who2}.`);
|
|
936
|
+
return;
|
|
937
|
+
}
|
|
774
938
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
775
|
-
|
|
939
|
+
addLicenseEntry({
|
|
776
940
|
licenseKey: result.licenseKey || licenseKey,
|
|
777
941
|
instanceId: result.instanceId || getDeviceId(),
|
|
778
942
|
status: "active",
|
|
779
943
|
lastValidatedAt: now,
|
|
780
944
|
activatedAt: now,
|
|
945
|
+
variantId,
|
|
781
946
|
customerName: result.customerName,
|
|
782
|
-
productName: result.productName
|
|
947
|
+
productName: result.productName,
|
|
948
|
+
variantName: result.variantName
|
|
783
949
|
});
|
|
950
|
+
const label = getVariantLabel(variantId);
|
|
784
951
|
const who = result.customerName ? ` Welcome, ${result.customerName}!` : "";
|
|
785
|
-
console.log(
|
|
952
|
+
console.log(`${label} activated successfully.${who}`);
|
|
786
953
|
}
|
|
787
954
|
|
|
788
955
|
// src/commands/deactivate.ts
|
|
789
|
-
async function deactivateCommand() {
|
|
790
|
-
const
|
|
791
|
-
if (
|
|
956
|
+
async function deactivateCommand(opts) {
|
|
957
|
+
const active = getActiveLicenses();
|
|
958
|
+
if (active.length === 0) {
|
|
792
959
|
console.log("No active license found on this device.");
|
|
793
960
|
return;
|
|
794
961
|
}
|
|
962
|
+
let targets;
|
|
963
|
+
if (opts?.licenseKey) {
|
|
964
|
+
const match = active.find((l) => l.licenseKey === opts.licenseKey);
|
|
965
|
+
if (!match) {
|
|
966
|
+
console.error(`No active license found with key "${opts.licenseKey}".`);
|
|
967
|
+
process.exit(1);
|
|
968
|
+
}
|
|
969
|
+
targets = [match];
|
|
970
|
+
} else if (active.length === 1) {
|
|
971
|
+
targets = active;
|
|
972
|
+
} else {
|
|
973
|
+
console.log("Multiple active licenses found. Specify which to deactivate:");
|
|
974
|
+
console.log(" keepgoing deactivate <license-key>");
|
|
975
|
+
console.log("");
|
|
976
|
+
for (const l of active) {
|
|
977
|
+
const label = getVariantLabel(l.variantId);
|
|
978
|
+
const who = l.customerName ? ` (${l.customerName})` : "";
|
|
979
|
+
console.log(` ${label}${who}: ${l.licenseKey}`);
|
|
980
|
+
}
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
795
983
|
console.log("Deactivating license...");
|
|
796
|
-
const
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
984
|
+
for (const entry of targets) {
|
|
985
|
+
const result = await deactivateLicense(entry.licenseKey, entry.instanceId);
|
|
986
|
+
removeLicenseEntry(entry.licenseKey);
|
|
987
|
+
const label = getVariantLabel(entry.variantId);
|
|
988
|
+
if (!result.deactivated) {
|
|
989
|
+
console.error(`${label} license cleared locally, but remote deactivation failed: ${result.error ?? "unknown error"}`);
|
|
990
|
+
} else {
|
|
991
|
+
console.log(`${label} license deactivated successfully. The activation slot has been freed.`);
|
|
992
|
+
}
|
|
801
993
|
}
|
|
802
|
-
console.log("Pro license deactivated successfully. The activation slot has been freed.");
|
|
803
994
|
}
|
|
804
995
|
|
|
805
996
|
// src/index.ts
|
|
@@ -874,13 +1065,13 @@ async function main() {
|
|
|
874
1065
|
}
|
|
875
1066
|
break;
|
|
876
1067
|
case "version":
|
|
877
|
-
console.log(`keepgoing v${"0.
|
|
1068
|
+
console.log(`keepgoing v${"0.3.0"}`);
|
|
878
1069
|
break;
|
|
879
1070
|
case "activate":
|
|
880
1071
|
await activateCommand({ licenseKey: subcommand });
|
|
881
1072
|
break;
|
|
882
1073
|
case "deactivate":
|
|
883
|
-
await deactivateCommand();
|
|
1074
|
+
await deactivateCommand({ licenseKey: subcommand || void 0 });
|
|
884
1075
|
break;
|
|
885
1076
|
case "help":
|
|
886
1077
|
case "":
|