codexuse-cli 5.1.0 → 5.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.
- package/README.md +1 -1
- package/dist/index.js +1019 -133
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2022,7 +2022,7 @@ var require_toml = __commonJS({
|
|
|
2022
2022
|
});
|
|
2023
2023
|
|
|
2024
2024
|
// src/commands/accountPool.ts
|
|
2025
|
-
var
|
|
2025
|
+
var import_node_crypto7 = require("crypto");
|
|
2026
2026
|
var import_node_path7 = __toESM(require("path"));
|
|
2027
2027
|
|
|
2028
2028
|
// ../../packages/contracts/src/settings/types.ts
|
|
@@ -2366,7 +2366,7 @@ async function withDatabase(dbPath, task) {
|
|
|
2366
2366
|
function resolveAppStorageDbPath(userDataDir) {
|
|
2367
2367
|
return import_node_path.default.join(userDataDir, APP_STORAGE_DB_DIR, APP_STORAGE_DB_NAME);
|
|
2368
2368
|
}
|
|
2369
|
-
async function readDocument(dbPath, namespace,
|
|
2369
|
+
async function readDocument(dbPath, namespace, normalize2) {
|
|
2370
2370
|
return withDatabase(dbPath, (db) => {
|
|
2371
2371
|
const row = useStatement(
|
|
2372
2372
|
db,
|
|
@@ -2377,10 +2377,10 @@ async function readDocument(dbPath, namespace, normalize) {
|
|
|
2377
2377
|
if (parsed === null) {
|
|
2378
2378
|
return null;
|
|
2379
2379
|
}
|
|
2380
|
-
return
|
|
2380
|
+
return normalize2(parsed);
|
|
2381
2381
|
});
|
|
2382
2382
|
}
|
|
2383
|
-
async function readDocumentStrict(dbPath, namespace,
|
|
2383
|
+
async function readDocumentStrict(dbPath, namespace, normalize2) {
|
|
2384
2384
|
return withDatabase(dbPath, (db) => {
|
|
2385
2385
|
const row = useStatement(
|
|
2386
2386
|
db,
|
|
@@ -2401,7 +2401,7 @@ async function readDocumentStrict(dbPath, namespace, normalize) {
|
|
|
2401
2401
|
);
|
|
2402
2402
|
}
|
|
2403
2403
|
try {
|
|
2404
|
-
return
|
|
2404
|
+
return normalize2(parsed);
|
|
2405
2405
|
} catch (error) {
|
|
2406
2406
|
throw new AppStorageCorruptionError(
|
|
2407
2407
|
`App storage document ${namespace} failed normalization.`,
|
|
@@ -2480,6 +2480,25 @@ async function updateDocument(input) {
|
|
|
2480
2480
|
})
|
|
2481
2481
|
);
|
|
2482
2482
|
}
|
|
2483
|
+
async function deleteDocument(dbPath, namespace) {
|
|
2484
|
+
await withWriteQueue(
|
|
2485
|
+
dbPath,
|
|
2486
|
+
async () => withDatabase(dbPath, (db) => {
|
|
2487
|
+
beginImmediate(db);
|
|
2488
|
+
try {
|
|
2489
|
+
useStatement(
|
|
2490
|
+
db,
|
|
2491
|
+
`DELETE FROM ${APP_STORAGE_TABLE} WHERE namespace = ?`,
|
|
2492
|
+
(statement) => statement.run(namespace)
|
|
2493
|
+
);
|
|
2494
|
+
commit(db);
|
|
2495
|
+
} catch (error) {
|
|
2496
|
+
rollback(db);
|
|
2497
|
+
throw error;
|
|
2498
|
+
}
|
|
2499
|
+
})
|
|
2500
|
+
);
|
|
2501
|
+
}
|
|
2483
2502
|
|
|
2484
2503
|
// ../../packages/runtime-app-state/src/app/state.ts
|
|
2485
2504
|
var APP_STATE_DOCUMENT = "desktop.app-state";
|
|
@@ -2564,6 +2583,16 @@ function createDefaultAppState() {
|
|
|
2564
2583
|
status: "inactive",
|
|
2565
2584
|
signature: null
|
|
2566
2585
|
},
|
|
2586
|
+
trial: {
|
|
2587
|
+
deviceId: null,
|
|
2588
|
+
startedAt: null,
|
|
2589
|
+
expiresAt: null,
|
|
2590
|
+
lastServerCheckAt: null,
|
|
2591
|
+
lastLocalSeenAt: null,
|
|
2592
|
+
offlineGraceUntil: null,
|
|
2593
|
+
serverExpired: false,
|
|
2594
|
+
signature: null
|
|
2595
|
+
},
|
|
2567
2596
|
preferences: {
|
|
2568
2597
|
excludeFolders: [],
|
|
2569
2598
|
enableTaskCompleteBeep: true,
|
|
@@ -3011,6 +3040,25 @@ function normalizeAppState(raw) {
|
|
|
3011
3040
|
if (!["inactive", "active", "grace", "error"].includes(merged.license.status)) {
|
|
3012
3041
|
merged.license.status = "inactive";
|
|
3013
3042
|
}
|
|
3043
|
+
if (!isRecord2(merged.trial)) {
|
|
3044
|
+
merged.trial = clone2(defaults.trial);
|
|
3045
|
+
}
|
|
3046
|
+
merged.trial.deviceId = asString(merged.trial.deviceId);
|
|
3047
|
+
merged.trial.startedAt = asString(merged.trial.startedAt);
|
|
3048
|
+
merged.trial.expiresAt = asString(merged.trial.expiresAt);
|
|
3049
|
+
merged.trial.lastServerCheckAt = asString(merged.trial.lastServerCheckAt);
|
|
3050
|
+
merged.trial.lastLocalSeenAt = asString(merged.trial.lastLocalSeenAt);
|
|
3051
|
+
merged.trial.offlineGraceUntil = asString(merged.trial.offlineGraceUntil);
|
|
3052
|
+
merged.trial.serverExpired = merged.trial.serverExpired === true;
|
|
3053
|
+
merged.trial.signature = asString(merged.trial.signature);
|
|
3054
|
+
{
|
|
3055
|
+
const allowedTrialKeys = new Set(Object.keys(defaults.trial));
|
|
3056
|
+
for (const key of Object.keys(merged.trial)) {
|
|
3057
|
+
if (!allowedTrialKeys.has(key)) {
|
|
3058
|
+
delete merged.trial[key];
|
|
3059
|
+
}
|
|
3060
|
+
}
|
|
3061
|
+
}
|
|
3014
3062
|
if (!Array.isArray(merged.preferences.excludeFolders)) {
|
|
3015
3063
|
merged.preferences.excludeFolders = [];
|
|
3016
3064
|
}
|
|
@@ -3416,7 +3464,7 @@ async function writeAppSettings(settings, onUpdated) {
|
|
|
3416
3464
|
}
|
|
3417
3465
|
|
|
3418
3466
|
// ../../packages/runtime-profiles/src/license/service.ts
|
|
3419
|
-
var
|
|
3467
|
+
var import_node_crypto5 = __toESM(require("crypto"), 1);
|
|
3420
3468
|
|
|
3421
3469
|
// ../../packages/runtime-codex/src/codex/settings.ts
|
|
3422
3470
|
function asString2(value) {
|
|
@@ -3459,6 +3507,53 @@ function parseStoredLicense(raw) {
|
|
|
3459
3507
|
);
|
|
3460
3508
|
return hasValue ? license : null;
|
|
3461
3509
|
}
|
|
3510
|
+
function parseStoredTrial(raw) {
|
|
3511
|
+
if (!isRecord3(raw)) {
|
|
3512
|
+
return null;
|
|
3513
|
+
}
|
|
3514
|
+
const trial = {
|
|
3515
|
+
deviceId: asString2(raw.deviceId ?? raw.device_id),
|
|
3516
|
+
startedAt: asString2(raw.startedAt ?? raw.started_at),
|
|
3517
|
+
expiresAt: asString2(raw.expiresAt ?? raw.expires_at),
|
|
3518
|
+
lastServerCheckAt: asString2(raw.lastServerCheckAt ?? raw.last_server_check_at),
|
|
3519
|
+
lastLocalSeenAt: asString2(raw.lastLocalSeenAt ?? raw.last_local_seen_at),
|
|
3520
|
+
offlineGraceUntil: asString2(raw.offlineGraceUntil ?? raw.offline_grace_until),
|
|
3521
|
+
serverExpired: raw.serverExpired === true || raw.server_expired === true,
|
|
3522
|
+
signature: asString2(raw.signature)
|
|
3523
|
+
};
|
|
3524
|
+
const hasValue = Boolean(
|
|
3525
|
+
trial.deviceId || trial.startedAt || trial.expiresAt || trial.lastServerCheckAt || trial.lastLocalSeenAt || trial.serverExpired || trial.signature
|
|
3526
|
+
);
|
|
3527
|
+
return hasValue ? trial : null;
|
|
3528
|
+
}
|
|
3529
|
+
async function getStoredTrial() {
|
|
3530
|
+
const state = await getAppState();
|
|
3531
|
+
return parseStoredTrial(state.trial);
|
|
3532
|
+
}
|
|
3533
|
+
async function persistTrial(trial) {
|
|
3534
|
+
const parsed = parseStoredTrial(trial);
|
|
3535
|
+
await patchAppState({
|
|
3536
|
+
trial: parsed ? {
|
|
3537
|
+
deviceId: parsed.deviceId ?? null,
|
|
3538
|
+
startedAt: parsed.startedAt ?? null,
|
|
3539
|
+
expiresAt: parsed.expiresAt ?? null,
|
|
3540
|
+
lastServerCheckAt: parsed.lastServerCheckAt ?? null,
|
|
3541
|
+
lastLocalSeenAt: parsed.lastLocalSeenAt ?? null,
|
|
3542
|
+
offlineGraceUntil: parsed.offlineGraceUntil ?? null,
|
|
3543
|
+
serverExpired: parsed.serverExpired === true,
|
|
3544
|
+
signature: parsed.signature ?? null
|
|
3545
|
+
} : {
|
|
3546
|
+
deviceId: null,
|
|
3547
|
+
startedAt: null,
|
|
3548
|
+
expiresAt: null,
|
|
3549
|
+
lastServerCheckAt: null,
|
|
3550
|
+
lastLocalSeenAt: null,
|
|
3551
|
+
offlineGraceUntil: null,
|
|
3552
|
+
serverExpired: false,
|
|
3553
|
+
signature: null
|
|
3554
|
+
}
|
|
3555
|
+
});
|
|
3556
|
+
}
|
|
3462
3557
|
async function getStoredLicense() {
|
|
3463
3558
|
const state = await getAppState();
|
|
3464
3559
|
return parseStoredLicense(state.license);
|
|
@@ -3590,7 +3685,9 @@ function getActiveOffer() {
|
|
|
3590
3685
|
isActive: true,
|
|
3591
3686
|
campaign: "summer-2026",
|
|
3592
3687
|
productPermalink: "codex-use",
|
|
3593
|
-
checkoutBaseUrl: "https://hweihwang.gumroad.com/l/codex-use"
|
|
3688
|
+
checkoutBaseUrl: "https://hweihwang.gumroad.com/l/codex-use",
|
|
3689
|
+
trialEnabled: true,
|
|
3690
|
+
trialDays: 7
|
|
3594
3691
|
};
|
|
3595
3692
|
}
|
|
3596
3693
|
function buildCheckoutUrl(offer2, options) {
|
|
@@ -3613,6 +3710,504 @@ function buildCheckoutUrl(offer2, options) {
|
|
|
3613
3710
|
return url.toString();
|
|
3614
3711
|
}
|
|
3615
3712
|
|
|
3713
|
+
// ../../packages/runtime-profiles/src/license/trial.ts
|
|
3714
|
+
var import_node_crypto4 = __toESM(require("crypto"), 1);
|
|
3715
|
+
|
|
3716
|
+
// ../../packages/runtime-profiles/src/license/device-id.ts
|
|
3717
|
+
var import_node_crypto2 = __toESM(require("crypto"), 1);
|
|
3718
|
+
var import_promises = require("fs/promises");
|
|
3719
|
+
var import_node_child_process = require("child_process");
|
|
3720
|
+
var import_node_util = require("util");
|
|
3721
|
+
var DEVICE_ID_DOCUMENT = "desktop.device-id";
|
|
3722
|
+
var SHA256_HEX_PATTERN = /^[0-9a-f]{64}$/i;
|
|
3723
|
+
var execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
|
|
3724
|
+
var memoizedDeviceId = null;
|
|
3725
|
+
function normalize(value) {
|
|
3726
|
+
return typeof value === "string" ? value.trim() : "";
|
|
3727
|
+
}
|
|
3728
|
+
async function readHardwareUuid() {
|
|
3729
|
+
if (process.platform !== "darwin") {
|
|
3730
|
+
return null;
|
|
3731
|
+
}
|
|
3732
|
+
try {
|
|
3733
|
+
const { stdout } = await execFileAsync(
|
|
3734
|
+
"ioreg",
|
|
3735
|
+
["-rd1", "-c", "IOPlatformExpertDevice"],
|
|
3736
|
+
{ timeout: 4e3 }
|
|
3737
|
+
);
|
|
3738
|
+
const match = stdout.match(/"IOPlatformUUID"\s*=\s*"([^"]+)"/);
|
|
3739
|
+
return normalize(match?.[1]) || null;
|
|
3740
|
+
} catch {
|
|
3741
|
+
return null;
|
|
3742
|
+
}
|
|
3743
|
+
}
|
|
3744
|
+
async function readLinuxMachineId() {
|
|
3745
|
+
if (process.platform !== "linux") {
|
|
3746
|
+
return null;
|
|
3747
|
+
}
|
|
3748
|
+
for (const file of ["/etc/machine-id", "/var/lib/dbus/machine-id"]) {
|
|
3749
|
+
try {
|
|
3750
|
+
const value = normalize(await (0, import_promises.readFile)(file, "utf8"));
|
|
3751
|
+
if (value) {
|
|
3752
|
+
return value;
|
|
3753
|
+
}
|
|
3754
|
+
} catch {
|
|
3755
|
+
}
|
|
3756
|
+
}
|
|
3757
|
+
return null;
|
|
3758
|
+
}
|
|
3759
|
+
function hashDeviceComponent(value) {
|
|
3760
|
+
return import_node_crypto2.default.createHash("sha256").update(`codexuse-device:${value}`).digest("hex");
|
|
3761
|
+
}
|
|
3762
|
+
async function getDeviceId() {
|
|
3763
|
+
if (memoizedDeviceId) {
|
|
3764
|
+
return memoizedDeviceId;
|
|
3765
|
+
}
|
|
3766
|
+
const dbPath = resolveAppStorageDbPath(getUserDataDir());
|
|
3767
|
+
const cached = normalize(await readDocument(dbPath, DEVICE_ID_DOCUMENT, normalize));
|
|
3768
|
+
const stableDeviceSource = await readHardwareUuid() ?? await readLinuxMachineId();
|
|
3769
|
+
if (stableDeviceSource) {
|
|
3770
|
+
const deviceId2 = hashDeviceComponent(stableDeviceSource);
|
|
3771
|
+
if (cached !== deviceId2) {
|
|
3772
|
+
await writeDocument(dbPath, DEVICE_ID_DOCUMENT, deviceId2);
|
|
3773
|
+
}
|
|
3774
|
+
memoizedDeviceId = deviceId2;
|
|
3775
|
+
return deviceId2;
|
|
3776
|
+
}
|
|
3777
|
+
if (SHA256_HEX_PATTERN.test(cached)) {
|
|
3778
|
+
memoizedDeviceId = cached;
|
|
3779
|
+
return cached;
|
|
3780
|
+
}
|
|
3781
|
+
const seed = import_node_crypto2.default.randomBytes(32).toString("hex");
|
|
3782
|
+
const deviceId = hashDeviceComponent(seed);
|
|
3783
|
+
await writeDocument(dbPath, DEVICE_ID_DOCUMENT, deviceId);
|
|
3784
|
+
memoizedDeviceId = deviceId;
|
|
3785
|
+
return deviceId;
|
|
3786
|
+
}
|
|
3787
|
+
function hashEmail(email) {
|
|
3788
|
+
const normalized = typeof email === "string" ? email.trim().toLowerCase() : "";
|
|
3789
|
+
if (!normalized) {
|
|
3790
|
+
return null;
|
|
3791
|
+
}
|
|
3792
|
+
return import_node_crypto2.default.createHash("sha256").update(`codexuse-email:${normalized}`).digest("hex");
|
|
3793
|
+
}
|
|
3794
|
+
|
|
3795
|
+
// ../../packages/runtime-profiles/src/license/trial-client.ts
|
|
3796
|
+
var DEFAULT_API_BASE_URL = "https://api.codexuse.com";
|
|
3797
|
+
var TRIAL_REQUEST_TIMEOUT_MS = 1e4;
|
|
3798
|
+
function getApiBaseUrl() {
|
|
3799
|
+
return DEFAULT_API_BASE_URL;
|
|
3800
|
+
}
|
|
3801
|
+
function parseResponse(raw) {
|
|
3802
|
+
if (!raw || typeof raw !== "object") {
|
|
3803
|
+
return null;
|
|
3804
|
+
}
|
|
3805
|
+
const record = raw;
|
|
3806
|
+
const serverNow = typeof record.serverNow === "string" ? record.serverNow : null;
|
|
3807
|
+
if (!serverNow) {
|
|
3808
|
+
return null;
|
|
3809
|
+
}
|
|
3810
|
+
return {
|
|
3811
|
+
trialActive: record.trialActive === true,
|
|
3812
|
+
startedAt: typeof record.startedAt === "string" ? record.startedAt : null,
|
|
3813
|
+
expiresAt: typeof record.expiresAt === "string" ? record.expiresAt : null,
|
|
3814
|
+
trialToken: typeof record.trialToken === "string" ? record.trialToken : null,
|
|
3815
|
+
serverNow,
|
|
3816
|
+
offlineGraceMs: typeof record.offlineGraceMs === "number" && Number.isFinite(record.offlineGraceMs) ? record.offlineGraceMs : 0
|
|
3817
|
+
};
|
|
3818
|
+
}
|
|
3819
|
+
async function registerTrialWithServer(input) {
|
|
3820
|
+
const controller = new AbortController();
|
|
3821
|
+
const timeout = setTimeout(() => controller.abort(), TRIAL_REQUEST_TIMEOUT_MS);
|
|
3822
|
+
try {
|
|
3823
|
+
const response = await fetch(`${getApiBaseUrl()}/v1/trial/register`, {
|
|
3824
|
+
method: "POST",
|
|
3825
|
+
headers: { "Content-Type": "application/json" },
|
|
3826
|
+
body: JSON.stringify({
|
|
3827
|
+
deviceId: input.deviceId,
|
|
3828
|
+
emailHash: input.emailHash ?? null,
|
|
3829
|
+
trialToken: input.trialToken ?? null
|
|
3830
|
+
}),
|
|
3831
|
+
signal: controller.signal
|
|
3832
|
+
});
|
|
3833
|
+
if (!response.ok) {
|
|
3834
|
+
throw new Error(`Trial registration failed (${response.status}).`);
|
|
3835
|
+
}
|
|
3836
|
+
const parsed = parseResponse(await response.json());
|
|
3837
|
+
if (!parsed) {
|
|
3838
|
+
throw new Error("Trial registration response was incomplete.");
|
|
3839
|
+
}
|
|
3840
|
+
return parsed;
|
|
3841
|
+
} finally {
|
|
3842
|
+
clearTimeout(timeout);
|
|
3843
|
+
}
|
|
3844
|
+
}
|
|
3845
|
+
|
|
3846
|
+
// ../../packages/runtime-profiles/src/license/trial-token-store.ts
|
|
3847
|
+
var import_node_crypto3 = require("crypto");
|
|
3848
|
+
var import_node_child_process2 = require("child_process");
|
|
3849
|
+
var KEYCHAIN_SERVICE = "CODEXUSE_TRIAL_TOKEN";
|
|
3850
|
+
var KEYCHAIN_ACCOUNT_PREFIX = "device-sha256:";
|
|
3851
|
+
var FALLBACK_DOCUMENT_PREFIX = "desktop.trial-token.";
|
|
3852
|
+
function keychainAccountForDevice(deviceId) {
|
|
3853
|
+
const digest = (0, import_node_crypto3.createHash)("sha256").update(deviceId).digest("hex");
|
|
3854
|
+
return KEYCHAIN_ACCOUNT_PREFIX + digest;
|
|
3855
|
+
}
|
|
3856
|
+
function fallbackDocumentForDevice(deviceId) {
|
|
3857
|
+
const digest = (0, import_node_crypto3.createHash)("sha256").update(deviceId).digest("hex");
|
|
3858
|
+
return FALLBACK_DOCUMENT_PREFIX + digest;
|
|
3859
|
+
}
|
|
3860
|
+
function normalizeToken(value) {
|
|
3861
|
+
const trimmed = typeof value === "string" ? value.trim() : "";
|
|
3862
|
+
return trimmed.length > 0 ? trimmed : null;
|
|
3863
|
+
}
|
|
3864
|
+
function runSecurityCommand(args) {
|
|
3865
|
+
return new Promise((resolve, reject) => {
|
|
3866
|
+
const child = (0, import_node_child_process2.spawn)("security", args, { stdio: ["ignore", "pipe", "ignore"] });
|
|
3867
|
+
let stdout = "";
|
|
3868
|
+
child.stdout?.on("data", (chunk) => {
|
|
3869
|
+
stdout += String(chunk);
|
|
3870
|
+
});
|
|
3871
|
+
child.on("error", () => {
|
|
3872
|
+
reject(new Error("keychain-command-failed"));
|
|
3873
|
+
});
|
|
3874
|
+
child.on("close", (code) => {
|
|
3875
|
+
resolve({ code, stdout });
|
|
3876
|
+
});
|
|
3877
|
+
});
|
|
3878
|
+
}
|
|
3879
|
+
async function readStoredTrialToken(deviceId) {
|
|
3880
|
+
if (process.platform !== "darwin") {
|
|
3881
|
+
const dbPath = resolveAppStorageDbPath(getUserDataDir());
|
|
3882
|
+
return readDocument(
|
|
3883
|
+
dbPath,
|
|
3884
|
+
fallbackDocumentForDevice(deviceId),
|
|
3885
|
+
normalizeToken
|
|
3886
|
+
);
|
|
3887
|
+
}
|
|
3888
|
+
const result = await runSecurityCommand([
|
|
3889
|
+
"find-generic-password",
|
|
3890
|
+
"-a",
|
|
3891
|
+
keychainAccountForDevice(deviceId),
|
|
3892
|
+
"-s",
|
|
3893
|
+
KEYCHAIN_SERVICE,
|
|
3894
|
+
"-w"
|
|
3895
|
+
]).catch(() => null);
|
|
3896
|
+
await deleteDocument(
|
|
3897
|
+
resolveAppStorageDbPath(getUserDataDir()),
|
|
3898
|
+
fallbackDocumentForDevice(deviceId)
|
|
3899
|
+
).catch(() => void 0);
|
|
3900
|
+
if (!result || result.code !== 0) {
|
|
3901
|
+
return null;
|
|
3902
|
+
}
|
|
3903
|
+
return result.stdout.length > 0 ? result.stdout.replace(/\n$/, "") : null;
|
|
3904
|
+
}
|
|
3905
|
+
async function writeStoredTrialToken(deviceId, token) {
|
|
3906
|
+
const value = typeof token === "string" ? token.trim() : "";
|
|
3907
|
+
if (!value) {
|
|
3908
|
+
return;
|
|
3909
|
+
}
|
|
3910
|
+
if (process.platform !== "darwin") {
|
|
3911
|
+
const dbPath = resolveAppStorageDbPath(getUserDataDir());
|
|
3912
|
+
await writeDocument(
|
|
3913
|
+
dbPath,
|
|
3914
|
+
fallbackDocumentForDevice(deviceId),
|
|
3915
|
+
value
|
|
3916
|
+
);
|
|
3917
|
+
return;
|
|
3918
|
+
}
|
|
3919
|
+
const result = await runSecurityCommand([
|
|
3920
|
+
"add-generic-password",
|
|
3921
|
+
"-U",
|
|
3922
|
+
"-a",
|
|
3923
|
+
keychainAccountForDevice(deviceId),
|
|
3924
|
+
"-s",
|
|
3925
|
+
KEYCHAIN_SERVICE,
|
|
3926
|
+
"-w",
|
|
3927
|
+
value
|
|
3928
|
+
]).catch(() => null);
|
|
3929
|
+
await deleteDocument(
|
|
3930
|
+
resolveAppStorageDbPath(getUserDataDir()),
|
|
3931
|
+
fallbackDocumentForDevice(deviceId)
|
|
3932
|
+
).catch(() => void 0);
|
|
3933
|
+
if (!result || result.code !== 0) {
|
|
3934
|
+
throw new Error("trial-token-keychain-write-failed");
|
|
3935
|
+
}
|
|
3936
|
+
}
|
|
3937
|
+
|
|
3938
|
+
// ../../packages/runtime-profiles/src/license/trial.ts
|
|
3939
|
+
var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
3940
|
+
var TRIAL_SERVER_REFRESH_MS = 15 * 60 * 1e3;
|
|
3941
|
+
var TRIAL_BACKGROUND_RETRY_MS = 60 * 1e3;
|
|
3942
|
+
var CLOCK_ROLLBACK_TOLERANCE_MS = 5 * 60 * 1e3;
|
|
3943
|
+
var verifiedTrialCache = null;
|
|
3944
|
+
var backgroundTrialSyncPromise = null;
|
|
3945
|
+
var nextBackgroundTrialSyncAtMs = 0;
|
|
3946
|
+
function isTrialEnforcementEnabled() {
|
|
3947
|
+
return true;
|
|
3948
|
+
}
|
|
3949
|
+
function trialSignaturePayload(trial) {
|
|
3950
|
+
return JSON.stringify({
|
|
3951
|
+
deviceId: trial.deviceId ?? null,
|
|
3952
|
+
startedAt: trial.startedAt ?? null,
|
|
3953
|
+
expiresAt: trial.expiresAt ?? null,
|
|
3954
|
+
lastServerCheckAt: trial.lastServerCheckAt ?? null,
|
|
3955
|
+
lastLocalSeenAt: trial.lastLocalSeenAt ?? null,
|
|
3956
|
+
offlineGraceUntil: trial.offlineGraceUntil ?? null,
|
|
3957
|
+
serverExpired: trial.serverExpired === true
|
|
3958
|
+
});
|
|
3959
|
+
}
|
|
3960
|
+
function signTrial(trial, secret) {
|
|
3961
|
+
return import_node_crypto4.default.createHmac("sha256", secret).update(trialSignaturePayload(trial)).digest("hex");
|
|
3962
|
+
}
|
|
3963
|
+
function withTrialSignature(trial, secret) {
|
|
3964
|
+
return { ...trial, signature: signTrial(trial, secret) };
|
|
3965
|
+
}
|
|
3966
|
+
function isSignatureValid(trial, secret) {
|
|
3967
|
+
return Boolean(trial.signature) && signTrial(trial, secret) === trial.signature;
|
|
3968
|
+
}
|
|
3969
|
+
function parseTs(value) {
|
|
3970
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
3971
|
+
return null;
|
|
3972
|
+
}
|
|
3973
|
+
const parsed = Date.parse(value);
|
|
3974
|
+
return Number.isNaN(parsed) ? null : parsed;
|
|
3975
|
+
}
|
|
3976
|
+
function buildTrialInfo(stored, now) {
|
|
3977
|
+
const expiresAtMs = parseTs(stored?.expiresAt);
|
|
3978
|
+
const expired = stored?.serverExpired === true || expiresAtMs === null || expiresAtMs <= now;
|
|
3979
|
+
const daysLeft = expired || expiresAtMs === null ? 0 : Math.max(0, Math.ceil((expiresAtMs - now) / DAY_MS));
|
|
3980
|
+
return {
|
|
3981
|
+
active: !expired,
|
|
3982
|
+
expired,
|
|
3983
|
+
expiresAt: stored?.expiresAt ?? null,
|
|
3984
|
+
daysLeft
|
|
3985
|
+
};
|
|
3986
|
+
}
|
|
3987
|
+
function buildLockedTrialInfo(stored) {
|
|
3988
|
+
return {
|
|
3989
|
+
active: false,
|
|
3990
|
+
expired: true,
|
|
3991
|
+
expiresAt: stored?.expiresAt ?? null,
|
|
3992
|
+
daysLeft: 0
|
|
3993
|
+
};
|
|
3994
|
+
}
|
|
3995
|
+
function hasTrustedServerRecord(stored, secret, deviceId) {
|
|
3996
|
+
return Boolean(
|
|
3997
|
+
stored && stored.deviceId === deviceId && stored.startedAt && stored.lastServerCheckAt && isSignatureValid(stored, secret)
|
|
3998
|
+
);
|
|
3999
|
+
}
|
|
4000
|
+
function shouldRefreshInBackground(stored, now) {
|
|
4001
|
+
const lastServerCheckMs = parseTs(stored.lastServerCheckAt);
|
|
4002
|
+
return lastServerCheckMs !== null && now - lastServerCheckMs >= TRIAL_SERVER_REFRESH_MS;
|
|
4003
|
+
}
|
|
4004
|
+
function hasLocalClockRollback(stored, now) {
|
|
4005
|
+
const lastTrustedMs = Math.max(
|
|
4006
|
+
parseTs(stored.lastLocalSeenAt) ?? 0,
|
|
4007
|
+
parseTs(stored.lastServerCheckAt) ?? 0
|
|
4008
|
+
);
|
|
4009
|
+
return lastTrustedMs > 0 && now + CLOCK_ROLLBACK_TOLERANCE_MS < lastTrustedMs;
|
|
4010
|
+
}
|
|
4011
|
+
function canUseOfflineTrial(stored, now) {
|
|
4012
|
+
if (stored.serverExpired === true || hasLocalClockRollback(stored, now)) {
|
|
4013
|
+
return false;
|
|
4014
|
+
}
|
|
4015
|
+
const expiresAtMs = parseTs(stored.expiresAt);
|
|
4016
|
+
const offlineGraceUntilMs = parseTs(stored.offlineGraceUntil);
|
|
4017
|
+
const effectiveNow = trustedTrialNow(stored, now);
|
|
4018
|
+
if (expiresAtMs === null || offlineGraceUntilMs === null) {
|
|
4019
|
+
return false;
|
|
4020
|
+
}
|
|
4021
|
+
return effectiveNow < expiresAtMs && effectiveNow <= offlineGraceUntilMs;
|
|
4022
|
+
}
|
|
4023
|
+
function trustedTrialNow(stored, now) {
|
|
4024
|
+
return Math.max(now, parseTs(stored.lastServerCheckAt) ?? now);
|
|
4025
|
+
}
|
|
4026
|
+
async function persistLocalSeenIfNeeded(stored, secret, now) {
|
|
4027
|
+
const lastLocalSeenMs = parseTs(stored.lastLocalSeenAt);
|
|
4028
|
+
if (lastLocalSeenMs !== null && now - lastLocalSeenMs < TRIAL_SERVER_REFRESH_MS) {
|
|
4029
|
+
return stored;
|
|
4030
|
+
}
|
|
4031
|
+
const next = withTrialSignature(
|
|
4032
|
+
{
|
|
4033
|
+
...stored,
|
|
4034
|
+
lastLocalSeenAt: new Date(Math.max(now, lastLocalSeenMs ?? 0)).toISOString()
|
|
4035
|
+
},
|
|
4036
|
+
secret
|
|
4037
|
+
);
|
|
4038
|
+
await persistTrial(next);
|
|
4039
|
+
return next;
|
|
4040
|
+
}
|
|
4041
|
+
function startTrialSyncInBackground() {
|
|
4042
|
+
const now = Date.now();
|
|
4043
|
+
if (backgroundTrialSyncPromise || now < nextBackgroundTrialSyncAtMs) {
|
|
4044
|
+
return;
|
|
4045
|
+
}
|
|
4046
|
+
nextBackgroundTrialSyncAtMs = now + TRIAL_BACKGROUND_RETRY_MS;
|
|
4047
|
+
backgroundTrialSyncPromise = syncTrialWithServer({ force: true }).catch(() => false).finally(() => {
|
|
4048
|
+
backgroundTrialSyncPromise = null;
|
|
4049
|
+
});
|
|
4050
|
+
}
|
|
4051
|
+
async function resolveFirstProfileEmailHash() {
|
|
4052
|
+
try {
|
|
4053
|
+
const state = await getAppState();
|
|
4054
|
+
for (const record of Object.values(state.profilesByName ?? {})) {
|
|
4055
|
+
const hashed = hashEmail(record.email ?? null);
|
|
4056
|
+
if (hashed) {
|
|
4057
|
+
return hashed;
|
|
4058
|
+
}
|
|
4059
|
+
}
|
|
4060
|
+
} catch {
|
|
4061
|
+
}
|
|
4062
|
+
return null;
|
|
4063
|
+
}
|
|
4064
|
+
function signedServerTrial(input) {
|
|
4065
|
+
const serverNowMs = parseTs(input.response.serverNow) ?? input.now;
|
|
4066
|
+
return withTrialSignature(
|
|
4067
|
+
{
|
|
4068
|
+
deviceId: input.deviceId,
|
|
4069
|
+
startedAt: input.response.startedAt ?? input.stored?.startedAt ?? new Date(serverNowMs).toISOString(),
|
|
4070
|
+
expiresAt: input.response.expiresAt ?? input.stored?.expiresAt ?? null,
|
|
4071
|
+
lastServerCheckAt: input.response.serverNow,
|
|
4072
|
+
lastLocalSeenAt: new Date(input.now).toISOString(),
|
|
4073
|
+
offlineGraceUntil: new Date(serverNowMs + input.response.offlineGraceMs).toISOString(),
|
|
4074
|
+
serverExpired: input.response.trialActive === false
|
|
4075
|
+
},
|
|
4076
|
+
input.secret
|
|
4077
|
+
);
|
|
4078
|
+
}
|
|
4079
|
+
async function syncAndPersistTrial(input) {
|
|
4080
|
+
let deviceId;
|
|
4081
|
+
try {
|
|
4082
|
+
deviceId = await getDeviceId();
|
|
4083
|
+
} catch {
|
|
4084
|
+
return null;
|
|
4085
|
+
}
|
|
4086
|
+
const emailHash = input.emailHash ?? await resolveFirstProfileEmailHash();
|
|
4087
|
+
try {
|
|
4088
|
+
const trialToken = await readStoredTrialToken(deviceId).catch(() => null);
|
|
4089
|
+
const response = await registerTrialWithServer({ deviceId, emailHash, trialToken });
|
|
4090
|
+
if (response.trialToken) {
|
|
4091
|
+
try {
|
|
4092
|
+
await writeStoredTrialToken(deviceId, response.trialToken);
|
|
4093
|
+
} catch {
|
|
4094
|
+
if (!trialToken) {
|
|
4095
|
+
return null;
|
|
4096
|
+
}
|
|
4097
|
+
}
|
|
4098
|
+
}
|
|
4099
|
+
const next = signedServerTrial({
|
|
4100
|
+
response,
|
|
4101
|
+
secret: input.secret,
|
|
4102
|
+
stored: input.stored,
|
|
4103
|
+
now: input.now,
|
|
4104
|
+
deviceId
|
|
4105
|
+
});
|
|
4106
|
+
await persistTrial(next);
|
|
4107
|
+
rememberServerVerifiedTrial(next, input.now);
|
|
4108
|
+
return next;
|
|
4109
|
+
} catch {
|
|
4110
|
+
return null;
|
|
4111
|
+
}
|
|
4112
|
+
}
|
|
4113
|
+
function rememberServerVerifiedTrial(stored, now) {
|
|
4114
|
+
const refreshAtMs = now + TRIAL_SERVER_REFRESH_MS;
|
|
4115
|
+
const expiresAtMs = stored.serverExpired === true ? refreshAtMs : parseTs(stored.expiresAt) ?? now;
|
|
4116
|
+
verifiedTrialCache = {
|
|
4117
|
+
stored,
|
|
4118
|
+
usableUntilMs: Math.max(now, Math.min(expiresAtMs, refreshAtMs))
|
|
4119
|
+
};
|
|
4120
|
+
}
|
|
4121
|
+
function readServerVerifiedTrial(now) {
|
|
4122
|
+
if (!verifiedTrialCache || verifiedTrialCache.usableUntilMs <= now) {
|
|
4123
|
+
verifiedTrialCache = null;
|
|
4124
|
+
return null;
|
|
4125
|
+
}
|
|
4126
|
+
return verifiedTrialCache.stored;
|
|
4127
|
+
}
|
|
4128
|
+
async function resolveTrialState(now = Date.now(), options = {}) {
|
|
4129
|
+
if (!isTrialEnforcementEnabled()) {
|
|
4130
|
+
return { info: null, locked: false };
|
|
4131
|
+
}
|
|
4132
|
+
const allowNetwork = options.allowNetwork !== false;
|
|
4133
|
+
const secret = await getLicenseSecret();
|
|
4134
|
+
let stored = await getStoredTrial();
|
|
4135
|
+
const hadStoredTrial = stored !== null;
|
|
4136
|
+
const currentDeviceId = await getDeviceId().catch(() => null);
|
|
4137
|
+
const cached = readServerVerifiedTrial(now);
|
|
4138
|
+
if (cached) {
|
|
4139
|
+
if (!currentDeviceId || !hasTrustedServerRecord(cached, secret, currentDeviceId)) {
|
|
4140
|
+
verifiedTrialCache = null;
|
|
4141
|
+
} else {
|
|
4142
|
+
if (hasLocalClockRollback(cached, now)) {
|
|
4143
|
+
const refreshed2 = allowNetwork ? await syncAndPersistTrial({ stored: cached, secret, now }) : null;
|
|
4144
|
+
if (!refreshed2) {
|
|
4145
|
+
return { info: buildLockedTrialInfo(cached), locked: true };
|
|
4146
|
+
}
|
|
4147
|
+
const info3 = buildTrialInfo(refreshed2, trustedTrialNow(refreshed2, now));
|
|
4148
|
+
return { info: info3, locked: info3.expired };
|
|
4149
|
+
}
|
|
4150
|
+
if (shouldRefreshInBackground(cached, now)) {
|
|
4151
|
+
void syncTrialWithServer({ force: true }).catch(() => void 0);
|
|
4152
|
+
}
|
|
4153
|
+
const info2 = buildTrialInfo(cached, trustedTrialNow(cached, now));
|
|
4154
|
+
return { info: info2, locked: info2.expired };
|
|
4155
|
+
}
|
|
4156
|
+
}
|
|
4157
|
+
if (stored && (!currentDeviceId || !hasTrustedServerRecord(stored, secret, currentDeviceId))) {
|
|
4158
|
+
stored = null;
|
|
4159
|
+
}
|
|
4160
|
+
if (!allowNetwork) {
|
|
4161
|
+
if (stored && canUseOfflineTrial(stored, now)) {
|
|
4162
|
+
const next = await persistLocalSeenIfNeeded(stored, secret, now).catch(() => stored);
|
|
4163
|
+
rememberServerVerifiedTrial(next, now);
|
|
4164
|
+
if (options.refreshInBackground === true && shouldRefreshInBackground(next, now)) {
|
|
4165
|
+
startTrialSyncInBackground();
|
|
4166
|
+
}
|
|
4167
|
+
const info2 = buildTrialInfo(next, trustedTrialNow(next, now));
|
|
4168
|
+
return { info: info2, locked: info2.expired };
|
|
4169
|
+
}
|
|
4170
|
+
if (options.refreshInBackground === true) {
|
|
4171
|
+
startTrialSyncInBackground();
|
|
4172
|
+
}
|
|
4173
|
+
if (!hadStoredTrial && options.refreshInBackground === true) {
|
|
4174
|
+
return { info: null, locked: false };
|
|
4175
|
+
}
|
|
4176
|
+
return { info: buildLockedTrialInfo(stored), locked: true };
|
|
4177
|
+
}
|
|
4178
|
+
const refreshed = await syncAndPersistTrial({ stored, secret, now });
|
|
4179
|
+
if (!refreshed) {
|
|
4180
|
+
if (stored && canUseOfflineTrial(stored, now)) {
|
|
4181
|
+
const next = await persistLocalSeenIfNeeded(stored, secret, now).catch(() => stored);
|
|
4182
|
+
rememberServerVerifiedTrial(next, now);
|
|
4183
|
+
const info2 = buildTrialInfo(next, trustedTrialNow(next, now));
|
|
4184
|
+
return { info: info2, locked: info2.expired };
|
|
4185
|
+
}
|
|
4186
|
+
return { info: buildLockedTrialInfo(stored), locked: true };
|
|
4187
|
+
}
|
|
4188
|
+
const info = buildTrialInfo(refreshed, trustedTrialNow(refreshed, now));
|
|
4189
|
+
return { info, locked: info.expired };
|
|
4190
|
+
}
|
|
4191
|
+
async function syncTrialWithServer(opts = {}) {
|
|
4192
|
+
if (!isTrialEnforcementEnabled()) {
|
|
4193
|
+
return true;
|
|
4194
|
+
}
|
|
4195
|
+
const now = Date.now();
|
|
4196
|
+
const secret = await getLicenseSecret();
|
|
4197
|
+
const stored = await getStoredTrial();
|
|
4198
|
+
const explicitEmailHash = opts.emailHash ?? null;
|
|
4199
|
+
if (!opts.force && !explicitEmailHash && readServerVerifiedTrial(now)) {
|
|
4200
|
+
return true;
|
|
4201
|
+
}
|
|
4202
|
+
const next = await syncAndPersistTrial({
|
|
4203
|
+
stored,
|
|
4204
|
+
secret,
|
|
4205
|
+
now,
|
|
4206
|
+
emailHash: explicitEmailHash
|
|
4207
|
+
});
|
|
4208
|
+
return next !== null;
|
|
4209
|
+
}
|
|
4210
|
+
|
|
3616
4211
|
// ../../packages/runtime-profiles/src/license/service.ts
|
|
3617
4212
|
var offer = getActiveOffer();
|
|
3618
4213
|
var PRODUCT_PERMALINK = offer.productPermalink;
|
|
@@ -3677,7 +4272,7 @@ function licenseSignaturePayload(license) {
|
|
|
3677
4272
|
return JSON.stringify(payload);
|
|
3678
4273
|
}
|
|
3679
4274
|
function signLicense(license, secret) {
|
|
3680
|
-
return
|
|
4275
|
+
return import_node_crypto5.default.createHmac("sha256", secret).update(licenseSignaturePayload(license)).digest("hex");
|
|
3681
4276
|
}
|
|
3682
4277
|
function withSignature(license, secret) {
|
|
3683
4278
|
return {
|
|
@@ -3685,7 +4280,7 @@ function withSignature(license, secret) {
|
|
|
3685
4280
|
signature: signLicense(license, secret)
|
|
3686
4281
|
};
|
|
3687
4282
|
}
|
|
3688
|
-
function
|
|
4283
|
+
function isSignatureValid2(license, secret) {
|
|
3689
4284
|
if (!license.signature) {
|
|
3690
4285
|
return false;
|
|
3691
4286
|
}
|
|
@@ -3763,6 +4358,47 @@ function ensureWithinUseLimit(response) {
|
|
|
3763
4358
|
);
|
|
3764
4359
|
}
|
|
3765
4360
|
}
|
|
4361
|
+
function computeCapabilities(opts) {
|
|
4362
|
+
return {
|
|
4363
|
+
unlimitedProfiles: opts.entitled,
|
|
4364
|
+
accountPool: opts.entitled,
|
|
4365
|
+
telegram: opts.entitled,
|
|
4366
|
+
autoRoll: opts.entitled,
|
|
4367
|
+
cloudSync: opts.licensed || opts.entitled
|
|
4368
|
+
};
|
|
4369
|
+
}
|
|
4370
|
+
function applyTrial(base, trial) {
|
|
4371
|
+
if (base.isPro) {
|
|
4372
|
+
return { ...base, trial: null };
|
|
4373
|
+
}
|
|
4374
|
+
const { info, locked } = trial;
|
|
4375
|
+
if (!info) {
|
|
4376
|
+
return base;
|
|
4377
|
+
}
|
|
4378
|
+
if (info.active) {
|
|
4379
|
+
return {
|
|
4380
|
+
...base,
|
|
4381
|
+
tier: "trial",
|
|
4382
|
+
state: "trial",
|
|
4383
|
+
capabilities: computeCapabilities({ licensed: false, entitled: true }),
|
|
4384
|
+
profileLimit: null,
|
|
4385
|
+
profilesRemaining: null,
|
|
4386
|
+
trial: info
|
|
4387
|
+
};
|
|
4388
|
+
}
|
|
4389
|
+
if (locked) {
|
|
4390
|
+
return {
|
|
4391
|
+
...base,
|
|
4392
|
+
tier: "free",
|
|
4393
|
+
state: "locked",
|
|
4394
|
+
capabilities: computeCapabilities({ licensed: false, entitled: false }),
|
|
4395
|
+
profileLimit: 0,
|
|
4396
|
+
profilesRemaining: 0,
|
|
4397
|
+
trial: info
|
|
4398
|
+
};
|
|
4399
|
+
}
|
|
4400
|
+
return { ...base, trial: info };
|
|
4401
|
+
}
|
|
3766
4402
|
function toLicenseStatus(stored, overrides = {}) {
|
|
3767
4403
|
const tier = determineTierFromStored(stored);
|
|
3768
4404
|
const isPro = tier === "pro";
|
|
@@ -3771,6 +4407,8 @@ function toLicenseStatus(stored, overrides = {}) {
|
|
|
3771
4407
|
tier,
|
|
3772
4408
|
state,
|
|
3773
4409
|
isPro,
|
|
4410
|
+
capabilities: computeCapabilities({ licensed: isPro, entitled: isPro }),
|
|
4411
|
+
trial: null,
|
|
3774
4412
|
profileLimit: isPro ? null : FREE_PROFILE_LIMIT,
|
|
3775
4413
|
profilesRemaining: null,
|
|
3776
4414
|
purchaseEmail: stored?.purchaseEmail ?? null,
|
|
@@ -3812,15 +4450,27 @@ var LicenseService = class {
|
|
|
3812
4450
|
const refreshInBackground = Boolean(options.refreshInBackground);
|
|
3813
4451
|
const secret = await getLicenseSecret();
|
|
3814
4452
|
const stored = await getStoredLicense();
|
|
4453
|
+
const finalize = async (status) => {
|
|
4454
|
+
if (status.isPro) {
|
|
4455
|
+
return { ...status, trial: null };
|
|
4456
|
+
}
|
|
4457
|
+
return applyTrial(
|
|
4458
|
+
status,
|
|
4459
|
+
await resolveTrialState(Date.now(), {
|
|
4460
|
+
allowNetwork: !allowStale || forceRefresh,
|
|
4461
|
+
refreshInBackground
|
|
4462
|
+
})
|
|
4463
|
+
);
|
|
4464
|
+
};
|
|
3815
4465
|
if (!stored?.licenseKey) {
|
|
3816
|
-
return toLicenseStatus(null);
|
|
4466
|
+
return finalize(toLicenseStatus(null));
|
|
3817
4467
|
}
|
|
3818
4468
|
const now = Date.now();
|
|
3819
4469
|
const nextCheckTs = parseTimestamp(stored.nextCheckAt);
|
|
3820
4470
|
const lastVerifiedTs = parseTimestamp(stored.lastVerifiedAt);
|
|
3821
4471
|
const graceStale = (stored.status === "grace" || stored.status === "error") && (lastVerifiedTs === null || lastVerifiedTs + GRACE_MAX_AGE_MS < now);
|
|
3822
4472
|
let workingStored = { ...stored };
|
|
3823
|
-
const signatureValid =
|
|
4473
|
+
const signatureValid = isSignatureValid2(workingStored, secret);
|
|
3824
4474
|
let cappedNextCheckTs = nextCheckTs;
|
|
3825
4475
|
if (nextCheckTs !== null && nextCheckTs > now + MAX_NEXT_CHECK_MS) {
|
|
3826
4476
|
cappedNextCheckTs = now;
|
|
@@ -3843,7 +4493,7 @@ var LicenseService = class {
|
|
|
3843
4493
|
if (allowStale && shouldRefresh && refreshInBackground) {
|
|
3844
4494
|
this.refreshStatusInBackground({ force: forceRefresh });
|
|
3845
4495
|
}
|
|
3846
|
-
return cached;
|
|
4496
|
+
return finalize(cached);
|
|
3847
4497
|
}
|
|
3848
4498
|
if (this.verificationPromise && !forceRefresh) {
|
|
3849
4499
|
return this.verificationPromise;
|
|
@@ -3863,7 +4513,7 @@ var LicenseService = class {
|
|
|
3863
4513
|
};
|
|
3864
4514
|
const signed = withSignature(updated, secret);
|
|
3865
4515
|
await persistLicense(signed);
|
|
3866
|
-
return toLicenseStatus(signed);
|
|
4516
|
+
return finalize(toLicenseStatus(signed));
|
|
3867
4517
|
} catch (error) {
|
|
3868
4518
|
const message = error instanceof Error ? error.message : "License verification failed.";
|
|
3869
4519
|
if (error instanceof LicenseError && error.code === "network") {
|
|
@@ -3881,7 +4531,7 @@ var LicenseService = class {
|
|
|
3881
4531
|
state: fallbackStored2.status ?? "grace",
|
|
3882
4532
|
error: message
|
|
3883
4533
|
});
|
|
3884
|
-
return fallback;
|
|
4534
|
+
return finalize(fallback);
|
|
3885
4535
|
}
|
|
3886
4536
|
const fallbackStored = withSignature({
|
|
3887
4537
|
licenseKey: workingStored.licenseKey,
|
|
@@ -3892,7 +4542,7 @@ var LicenseService = class {
|
|
|
3892
4542
|
lastVerificationError: message
|
|
3893
4543
|
}, secret);
|
|
3894
4544
|
await persistLicense(fallbackStored);
|
|
3895
|
-
return toLicenseStatus(fallbackStored, { error: message });
|
|
4545
|
+
return finalize(toLicenseStatus(fallbackStored, { error: message }));
|
|
3896
4546
|
}
|
|
3897
4547
|
};
|
|
3898
4548
|
this.verificationPromise = verify();
|
|
@@ -3908,7 +4558,7 @@ var LicenseService = class {
|
|
|
3908
4558
|
throw new Error("License key is required.");
|
|
3909
4559
|
}
|
|
3910
4560
|
const secret = await getLicenseSecret();
|
|
3911
|
-
const digest =
|
|
4561
|
+
const digest = import_node_crypto5.default.createHash("sha256").update(trimmed).digest("hex");
|
|
3912
4562
|
const result = await requestGumroadVerify(trimmed, "activation");
|
|
3913
4563
|
ensureWithinUseLimit(result);
|
|
3914
4564
|
const normalized = normalizeVerificationResult(result);
|
|
@@ -3950,9 +4600,28 @@ var LicenseService = class {
|
|
|
3950
4600
|
}
|
|
3951
4601
|
};
|
|
3952
4602
|
var licenseService = new LicenseService();
|
|
4603
|
+
function isExplicitInvalidation(message) {
|
|
4604
|
+
if (!message) {
|
|
4605
|
+
return false;
|
|
4606
|
+
}
|
|
4607
|
+
const normalized = message.toLowerCase();
|
|
4608
|
+
return normalized.includes("revoked") || normalized.includes("refunded") || normalized.includes("invalid") || normalized.includes("no longer active");
|
|
4609
|
+
}
|
|
4610
|
+
function shouldEnforceProfileLimit(status) {
|
|
4611
|
+
if (status.capabilities.unlimitedProfiles) {
|
|
4612
|
+
return false;
|
|
4613
|
+
}
|
|
4614
|
+
if (status.state === "grace" || status.state === "verifying" || status.state === "error") {
|
|
4615
|
+
return false;
|
|
4616
|
+
}
|
|
4617
|
+
if (isExplicitInvalidation(status.error)) {
|
|
4618
|
+
return true;
|
|
4619
|
+
}
|
|
4620
|
+
return status.state === "inactive" || status.state === "locked";
|
|
4621
|
+
}
|
|
3953
4622
|
|
|
3954
4623
|
// ../../packages/runtime-profiles/src/profiles/profile-manager.ts
|
|
3955
|
-
var
|
|
4624
|
+
var import_node_crypto6 = require("crypto");
|
|
3956
4625
|
var import_fs = require("fs");
|
|
3957
4626
|
var import_path = __toESM(require("path"), 1);
|
|
3958
4627
|
var import_path2 = require("path");
|
|
@@ -4005,7 +4674,7 @@ function resolveProfileIdentityKeyFromProfile(profile) {
|
|
|
4005
4674
|
}
|
|
4006
4675
|
|
|
4007
4676
|
// ../../packages/runtime-codex/src/codex/rpc.ts
|
|
4008
|
-
var
|
|
4677
|
+
var import_node_child_process4 = require("child_process");
|
|
4009
4678
|
var import_node_readline = __toESM(require("readline"), 1);
|
|
4010
4679
|
var import_node_fs3 = require("fs");
|
|
4011
4680
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
@@ -4137,7 +4806,7 @@ async function requireCodexCli() {
|
|
|
4137
4806
|
}
|
|
4138
4807
|
|
|
4139
4808
|
// ../../packages/runtime-codex/src/codex/command.ts
|
|
4140
|
-
var
|
|
4809
|
+
var import_node_child_process3 = require("child_process");
|
|
4141
4810
|
var import_node_path4 = __toESM(require("path"), 1);
|
|
4142
4811
|
var cachedNodeRuntime = null;
|
|
4143
4812
|
function isJavaScriptEntrypoint(binaryPath) {
|
|
@@ -4180,7 +4849,7 @@ function resolveNodeRuntime(env) {
|
|
|
4180
4849
|
)
|
|
4181
4850
|
);
|
|
4182
4851
|
for (const candidate of candidates) {
|
|
4183
|
-
const probe = (0,
|
|
4852
|
+
const probe = (0, import_node_child_process3.spawnSync)(candidate, ["-v"], { env: runtimeEnv, stdio: "ignore" });
|
|
4184
4853
|
if (!probe.error && probe.status === 0) {
|
|
4185
4854
|
cachedNodeRuntime = candidate;
|
|
4186
4855
|
return candidate;
|
|
@@ -5059,7 +5728,7 @@ async function fetchRateLimitsViaRpc(envOverride, options = {}) {
|
|
|
5059
5728
|
["-s", "read-only", "-a", "untrusted", "app-server"],
|
|
5060
5729
|
childEnv
|
|
5061
5730
|
);
|
|
5062
|
-
const child = (0,
|
|
5731
|
+
const child = (0, import_node_child_process4.spawn)(command.command, command.args, {
|
|
5063
5732
|
stdio: ["pipe", "pipe", "pipe"],
|
|
5064
5733
|
env: childEnv,
|
|
5065
5734
|
shell: command.shell
|
|
@@ -5169,7 +5838,7 @@ async function activateResetWindowViaRpc(envOverride, options = {}) {
|
|
|
5169
5838
|
["-s", "read-only", "-a", "untrusted", "app-server"],
|
|
5170
5839
|
childEnv
|
|
5171
5840
|
);
|
|
5172
|
-
const child = (0,
|
|
5841
|
+
const child = (0, import_node_child_process4.spawn)(command.command, command.args, {
|
|
5173
5842
|
stdio: ["pipe", "pipe", "pipe"],
|
|
5174
5843
|
env: childEnv,
|
|
5175
5844
|
shell: command.shell
|
|
@@ -5633,7 +6302,7 @@ var ProfileManager = class {
|
|
|
5633
6302
|
if (!trimmed) {
|
|
5634
6303
|
return this.emptyAuthSnapshot();
|
|
5635
6304
|
}
|
|
5636
|
-
const fingerprint = (0,
|
|
6305
|
+
const fingerprint = (0, import_node_crypto6.createHash)("sha256").update(trimmed).digest("hex");
|
|
5637
6306
|
try {
|
|
5638
6307
|
const parsed = JSON.parse(trimmed);
|
|
5639
6308
|
const normalized = this.normalizeProfileData(parsed);
|
|
@@ -7285,7 +7954,7 @@ var ProfileManager = class {
|
|
|
7285
7954
|
}
|
|
7286
7955
|
if (options.canAutoImport === false) {
|
|
7287
7956
|
return this.buildActiveCodexAuthStatus("unmanaged", description, {
|
|
7288
|
-
importBlockedReason: options.importBlockedReason ?? "
|
|
7957
|
+
importBlockedReason: options.importBlockedReason ?? "Your CodexUse trial has ended. Upgrade to Pro before importing this Codex login."
|
|
7289
7958
|
});
|
|
7290
7959
|
}
|
|
7291
7960
|
const records = await this.listProfileRecords();
|
|
@@ -7473,7 +8142,7 @@ Flags:
|
|
|
7473
8142
|
--runtime=NAME Accounts Pool runtime store: desktop
|
|
7474
8143
|
--state-dir=PATH Override the runtime state dir for Accounts Pool inspection
|
|
7475
8144
|
--passphrase-stdin Read Cloud Sync passphrase from stdin
|
|
7476
|
-
Note: profile autoroll requires Pro.
|
|
8145
|
+
Note: profile autoroll requires Pro or an active trial.
|
|
7477
8146
|
Cloud Sync: set CODEXUSE_SYNC_PASSPHRASE or use --passphrase-stdin unless the passphrase is saved in Keychain.
|
|
7478
8147
|
`);
|
|
7479
8148
|
}
|
|
@@ -7525,7 +8194,7 @@ function parseStringFlag(flags, name) {
|
|
|
7525
8194
|
|
|
7526
8195
|
// src/commands/accountPool.ts
|
|
7527
8196
|
var ACCOUNT_POOL_DOCUMENT = "desktop.account-pool.v2";
|
|
7528
|
-
var ACCOUNT_POOL_PRO_REQUIRED_MESSAGE = "Accounts Pool requires a Pro license.";
|
|
8197
|
+
var ACCOUNT_POOL_PRO_REQUIRED_MESSAGE = "Accounts Pool requires a Pro license or an active trial.";
|
|
7529
8198
|
var DESKTOP_RPC_STATE_DIR = "desktop-rpc";
|
|
7530
8199
|
var ACCOUNT_POOL_STORE_VERSION = 3;
|
|
7531
8200
|
var MAX_RESPONSE_IDS_PER_SESSION = 64;
|
|
@@ -7913,13 +8582,13 @@ async function listApiKeys(stateDir) {
|
|
|
7913
8582
|
return Object.values(store.apiKeysById).filter((key) => key.revokedAt === null).sort((left, right) => right.createdAt.localeCompare(left.createdAt)).map(accountPoolApiKeyView);
|
|
7914
8583
|
}
|
|
7915
8584
|
function hashToken(token) {
|
|
7916
|
-
return (0,
|
|
8585
|
+
return (0, import_node_crypto7.createHash)("sha256").update(token).digest("hex");
|
|
7917
8586
|
}
|
|
7918
8587
|
async function createApiKey(stateDir) {
|
|
7919
|
-
const plaintextToken = `cux_pool_${(0,
|
|
8588
|
+
const plaintextToken = `cux_pool_${(0, import_node_crypto7.randomBytes)(24).toString("hex")}`;
|
|
7920
8589
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7921
8590
|
const keyRecord = {
|
|
7922
|
-
id: (0,
|
|
8591
|
+
id: (0, import_node_crypto7.randomUUID)(),
|
|
7923
8592
|
tokenHash: hashToken(plaintextToken),
|
|
7924
8593
|
tokenPreview: `${plaintextToken.slice(0, 8)}...${plaintextToken.slice(-6)}`,
|
|
7925
8594
|
createdAt: now,
|
|
@@ -8013,7 +8682,7 @@ async function readAccountPoolRuntimeSummary(input = {}) {
|
|
|
8013
8682
|
stateDir,
|
|
8014
8683
|
baseUrl: resolveBaseUrlFromPort(runtime, input.port ?? null),
|
|
8015
8684
|
enabled: normalized.accountPoolEnabled,
|
|
8016
|
-
|
|
8685
|
+
hasAccountPoolAccess: license?.capabilities.accountPool === true,
|
|
8017
8686
|
routingStrategy: normalized.accountPoolRoutingStrategy,
|
|
8018
8687
|
activeApiKeyCount: apiKeys.length,
|
|
8019
8688
|
activeSessionCount: sessions.length
|
|
@@ -8030,7 +8699,7 @@ async function updateSettings(transform) {
|
|
|
8030
8699
|
const { raw, normalized } = await readSettingsSnapshot();
|
|
8031
8700
|
const nextSettings = transform(normalized);
|
|
8032
8701
|
const license = await licenseService.getStatus().catch(() => null);
|
|
8033
|
-
const forcedDisabledByLicense =
|
|
8702
|
+
const forcedDisabledByLicense = license?.capabilities.accountPool !== true && nextSettings.accountPoolEnabled === true;
|
|
8034
8703
|
const sanitizedSettings = {
|
|
8035
8704
|
...nextSettings,
|
|
8036
8705
|
accountPoolEnabled: forcedDisabledByLicense ? false : nextSettings.accountPoolEnabled,
|
|
@@ -8057,7 +8726,7 @@ async function updateSettings(transform) {
|
|
|
8057
8726
|
}
|
|
8058
8727
|
async function requireProAccess() {
|
|
8059
8728
|
const license = await licenseService.getStatus();
|
|
8060
|
-
if (!license.
|
|
8729
|
+
if (!license.capabilities.accountPool) {
|
|
8061
8730
|
throw new Error(ACCOUNT_POOL_PRO_REQUIRED_MESSAGE);
|
|
8062
8731
|
}
|
|
8063
8732
|
}
|
|
@@ -8119,7 +8788,7 @@ async function handleStatus(flags) {
|
|
|
8119
8788
|
console.log(`Runtime: ${runtime}`);
|
|
8120
8789
|
console.log(`State dir: ${stateDir}`);
|
|
8121
8790
|
console.log(`Routing: ${formatRoutingStrategy(normalized.accountPoolRoutingStrategy)}`);
|
|
8122
|
-
console.log(`Public API ready: ${normalized.accountPoolEnabled && license?.
|
|
8791
|
+
console.log(`Public API ready: ${normalized.accountPoolEnabled && license?.capabilities.accountPool === true && apiKeys.length > 0 ? "yes" : "no"}`);
|
|
8123
8792
|
console.log(`API keys: ${apiKeys.length}`);
|
|
8124
8793
|
console.log(`Sessions: ${sessions.length}`);
|
|
8125
8794
|
console.log(`Selection mode: ${explicitSelection.length > 0 ? "explicit" : "all saved profiles"}`);
|
|
@@ -8135,7 +8804,7 @@ async function handleStatus(flags) {
|
|
|
8135
8804
|
printStringList("Published models", normalized.accountPoolExposedModels);
|
|
8136
8805
|
printStringList("Reasoning aliases", normalized.accountPoolExposedReasoningEfforts);
|
|
8137
8806
|
printStringList("Fast-mode aliases", normalized.accountPoolExposedFastModeReasoningEfforts);
|
|
8138
|
-
if (normalized.accountPoolEnabled && license?.
|
|
8807
|
+
if (normalized.accountPoolEnabled && license?.capabilities.accountPool !== true) {
|
|
8139
8808
|
console.log(ACCOUNT_POOL_PRO_REQUIRED_MESSAGE);
|
|
8140
8809
|
}
|
|
8141
8810
|
}
|
|
@@ -8340,7 +9009,7 @@ async function handleQuickstart(flags) {
|
|
|
8340
9009
|
console.log("Accounts Pool quickstart");
|
|
8341
9010
|
console.log(`Runtime: ${summary.runtime}`);
|
|
8342
9011
|
console.log(`Enabled: ${summary.enabled ? "yes" : "no"}`);
|
|
8343
|
-
console.log(`
|
|
9012
|
+
console.log(`Access: ${summary.hasAccountPoolAccess ? "yes" : "no"}`);
|
|
8344
9013
|
console.log(`API keys: ${summary.activeApiKeyCount}`);
|
|
8345
9014
|
console.log(`Base URL: ${baseUrl}`);
|
|
8346
9015
|
console.log("");
|
|
@@ -8424,7 +9093,7 @@ async function handleAccountPoolCommand(args, version) {
|
|
|
8424
9093
|
}
|
|
8425
9094
|
|
|
8426
9095
|
// src/commands/codex.ts
|
|
8427
|
-
var
|
|
9096
|
+
var import_node_child_process6 = require("child_process");
|
|
8428
9097
|
|
|
8429
9098
|
// ../../packages/runtime-profiles/src/profiles/rate-limit-notifier.ts
|
|
8430
9099
|
function maxUsedPercent(snapshot) {
|
|
@@ -8441,8 +9110,108 @@ function maxUsedPercent(snapshot) {
|
|
|
8441
9110
|
return Math.max(...candidates);
|
|
8442
9111
|
}
|
|
8443
9112
|
|
|
9113
|
+
// ../../packages/runtime-profiles/src/profiles/profile-visibility.ts
|
|
9114
|
+
function resolveLicenseVisibleProfiles(args) {
|
|
9115
|
+
const { profiles, license, currentProfile } = args;
|
|
9116
|
+
const licenseWithCounts = licenseService.applyProfileCount(license, profiles.length);
|
|
9117
|
+
const enforceProfileLimit = shouldEnforceProfileLimit(licenseWithCounts);
|
|
9118
|
+
const visibleProfiles = enforceProfileLimit ? resolveVisibleProfiles(profiles, licenseWithCounts, currentProfile) : profiles;
|
|
9119
|
+
return {
|
|
9120
|
+
allProfiles: profiles,
|
|
9121
|
+
profiles: licenseWithCounts.capabilities.unlimitedProfiles ? profiles : visibleProfiles,
|
|
9122
|
+
license,
|
|
9123
|
+
licenseWithCounts,
|
|
9124
|
+
enforceProfileLimit
|
|
9125
|
+
};
|
|
9126
|
+
}
|
|
9127
|
+
async function loadLicenseVisibleProfiles(profileManager, options = {}) {
|
|
9128
|
+
await profileManager.initialize();
|
|
9129
|
+
const license = options.freshLicense ? await licenseService.getStatus() : await licenseService.getCachedStatus();
|
|
9130
|
+
const profiles = await profileManager.listProfiles();
|
|
9131
|
+
const currentProfile = options.currentProfile !== void 0 ? options.currentProfile : (await profileManager.getCurrentProfile()).name;
|
|
9132
|
+
return resolveLicenseVisibleProfiles({
|
|
9133
|
+
profiles,
|
|
9134
|
+
license,
|
|
9135
|
+
currentProfile
|
|
9136
|
+
});
|
|
9137
|
+
}
|
|
9138
|
+
function resolveVisibleProfiles(profiles, license, currentProfile) {
|
|
9139
|
+
if (license.capabilities.unlimitedProfiles) {
|
|
9140
|
+
return profiles;
|
|
9141
|
+
}
|
|
9142
|
+
const limit = typeof license.profileLimit === "number" && license.profileLimit >= 0 ? license.profileLimit : FREE_PROFILE_LIMIT;
|
|
9143
|
+
if (profiles.length <= limit) {
|
|
9144
|
+
return profiles;
|
|
9145
|
+
}
|
|
9146
|
+
const normalizedCurrent = currentProfile ?? null;
|
|
9147
|
+
const sorted = profiles.slice().sort((a, b) => compareProfilesForLimit(a, b, normalizedCurrent));
|
|
9148
|
+
return sorted.slice(0, limit);
|
|
9149
|
+
}
|
|
9150
|
+
function compareProfilesForLimit(a, b, currentProfile) {
|
|
9151
|
+
if (currentProfile) {
|
|
9152
|
+
if (a.name === currentProfile && b.name !== currentProfile) {
|
|
9153
|
+
return -1;
|
|
9154
|
+
}
|
|
9155
|
+
if (b.name === currentProfile && a.name !== currentProfile) {
|
|
9156
|
+
return 1;
|
|
9157
|
+
}
|
|
9158
|
+
}
|
|
9159
|
+
if (Boolean(a.isValid) !== Boolean(b.isValid)) {
|
|
9160
|
+
return a.isValid ? -1 : 1;
|
|
9161
|
+
}
|
|
9162
|
+
const aTimestamp = parseTimestamp3(a.createdAt ?? null);
|
|
9163
|
+
const bTimestamp = parseTimestamp3(b.createdAt ?? null);
|
|
9164
|
+
if (aTimestamp !== null && bTimestamp !== null) {
|
|
9165
|
+
if (aTimestamp === bTimestamp) {
|
|
9166
|
+
return a.name.localeCompare(b.name);
|
|
9167
|
+
}
|
|
9168
|
+
return bTimestamp - aTimestamp;
|
|
9169
|
+
}
|
|
9170
|
+
if (aTimestamp !== null) {
|
|
9171
|
+
return -1;
|
|
9172
|
+
}
|
|
9173
|
+
if (bTimestamp !== null) {
|
|
9174
|
+
return 1;
|
|
9175
|
+
}
|
|
9176
|
+
return a.name.localeCompare(b.name);
|
|
9177
|
+
}
|
|
9178
|
+
function parseTimestamp3(value) {
|
|
9179
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
9180
|
+
return null;
|
|
9181
|
+
}
|
|
9182
|
+
const parsed = Date.parse(value);
|
|
9183
|
+
return Number.isNaN(parsed) ? null : parsed;
|
|
9184
|
+
}
|
|
9185
|
+
|
|
9186
|
+
// ../../packages/runtime-profiles/src/license/guard.ts
|
|
9187
|
+
var PROFILE_ACCESS_DENIED_MESSAGE = "Your CodexUse trial has ended. Upgrade to Pro to use saved profiles.";
|
|
9188
|
+
async function assertProfileCreationAllowed(profileManager) {
|
|
9189
|
+
const manager = profileManager ?? new ProfileManager();
|
|
9190
|
+
await manager.initialize();
|
|
9191
|
+
const license = await licenseService.getStatus();
|
|
9192
|
+
const profiles = await manager.listProfiles();
|
|
9193
|
+
const licenseWithCounts = licenseService.applyProfileCount(license, profiles.length);
|
|
9194
|
+
if (typeof licenseWithCounts.profileLimit === "number" && licenseWithCounts.profilesRemaining !== null && licenseWithCounts.profilesRemaining <= 0) {
|
|
9195
|
+
throw new Error("Your CodexUse trial has ended. Upgrade to Pro for unlimited profiles.");
|
|
9196
|
+
}
|
|
9197
|
+
}
|
|
9198
|
+
async function assertProfileAccessAllowed(profileManager, profileName) {
|
|
9199
|
+
const name = profileName.trim();
|
|
9200
|
+
if (!name) {
|
|
9201
|
+
throw new Error("Profile name is required.");
|
|
9202
|
+
}
|
|
9203
|
+
const visible = await loadLicenseVisibleProfiles(profileManager, {
|
|
9204
|
+
freshLicense: true
|
|
9205
|
+
});
|
|
9206
|
+
const exists = visible.allProfiles.some((profile) => profile.name === name);
|
|
9207
|
+
const allowed = visible.profiles.some((profile) => profile.name === name);
|
|
9208
|
+
if (exists && !allowed) {
|
|
9209
|
+
throw new Error(PROFILE_ACCESS_DENIED_MESSAGE);
|
|
9210
|
+
}
|
|
9211
|
+
}
|
|
9212
|
+
|
|
8444
9213
|
// src/platform/codexCli.ts
|
|
8445
|
-
var
|
|
9214
|
+
var import_node_child_process5 = require("child_process");
|
|
8446
9215
|
var import_node_fs5 = require("fs");
|
|
8447
9216
|
var import_node_path8 = __toESM(require("path"));
|
|
8448
9217
|
var ENV_HINTS2 = ["CODEX_BINARY", "CODEX_CLI_PATH", "CODEX_PATH"];
|
|
@@ -8514,7 +9283,7 @@ async function runCodexLogin(mode) {
|
|
|
8514
9283
|
const resolvedMode = resolveLoginMode(mode ?? null);
|
|
8515
9284
|
const loginArgs = resolvedMode === "device" ? ["login", "--device-auth"] : ["login"];
|
|
8516
9285
|
const { command, args, shell } = buildCodexCommand2(codexPath, loginArgs);
|
|
8517
|
-
const child = (0,
|
|
9286
|
+
const child = (0, import_node_child_process5.spawn)(command, args, {
|
|
8518
9287
|
stdio: "inherit",
|
|
8519
9288
|
env: process.env,
|
|
8520
9289
|
shell
|
|
@@ -8586,7 +9355,7 @@ function compareCandidates(left, right) {
|
|
|
8586
9355
|
async function runCommand(codexPath, args, timeoutMs) {
|
|
8587
9356
|
const command = buildCodexCommand2(codexPath, args);
|
|
8588
9357
|
return new Promise((resolve) => {
|
|
8589
|
-
const child = (0,
|
|
9358
|
+
const child = (0, import_node_child_process6.spawn)(command.command, command.args, {
|
|
8590
9359
|
env: process.env,
|
|
8591
9360
|
shell: command.shell,
|
|
8592
9361
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -8647,12 +9416,13 @@ async function runCodexWithProfile(manager, profileName, codexArgs) {
|
|
|
8647
9416
|
if (!codexPath) {
|
|
8648
9417
|
throw new Error("Codex CLI not found. Install Codex CLI or set CODEX_BINARY.");
|
|
8649
9418
|
}
|
|
9419
|
+
await assertProfileAccessAllowed(manager, profileName);
|
|
8650
9420
|
const runtime = await manager.prepareProfileRuntime(profileName, {
|
|
8651
9421
|
syncFromActiveAuthBeforeAction: false
|
|
8652
9422
|
});
|
|
8653
9423
|
const command = buildCodexCommand2(codexPath, codexArgs);
|
|
8654
9424
|
try {
|
|
8655
|
-
const child = (0,
|
|
9425
|
+
const child = (0, import_node_child_process6.spawn)(command.command, command.args, {
|
|
8656
9426
|
env: runtime.env,
|
|
8657
9427
|
shell: command.shell,
|
|
8658
9428
|
stdio: "inherit"
|
|
@@ -8753,7 +9523,7 @@ async function handleBestCommand(args) {
|
|
|
8753
9523
|
console.log(`Usage:
|
|
8754
9524
|
codexuse best -- <codex args>
|
|
8755
9525
|
|
|
8756
|
-
Picks the healthiest saved profile, then runs Codex. Auto-run requires Pro.`);
|
|
9526
|
+
Picks the healthiest saved profile, then runs Codex. Auto-run requires Pro or an active trial.`);
|
|
8757
9527
|
return;
|
|
8758
9528
|
}
|
|
8759
9529
|
const codexPath = resolveCodexBinary();
|
|
@@ -8761,20 +9531,17 @@ Picks the healthiest saved profile, then runs Codex. Auto-run requires Pro.`);
|
|
|
8761
9531
|
throw new Error("Codex CLI not found. Install Codex CLI or set CODEX_BINARY.");
|
|
8762
9532
|
}
|
|
8763
9533
|
const manager = new ProfileManager();
|
|
8764
|
-
const
|
|
9534
|
+
const visible = await loadLicenseVisibleProfiles(manager, { freshLicense: true });
|
|
9535
|
+
if (!visible.licenseWithCounts.capabilities.autoRoll) {
|
|
9536
|
+
throw new Error("Auto-run requires Pro or an active trial.");
|
|
9537
|
+
}
|
|
9538
|
+
const profiles = visible.profiles;
|
|
8765
9539
|
const best = await selectBestProfile(manager, profiles, codexPath);
|
|
8766
9540
|
if (!best) {
|
|
8767
9541
|
throw new Error("No valid saved profiles found. Add one with `codexuse profile add <name>`.");
|
|
8768
9542
|
}
|
|
8769
9543
|
const dryRun = hasFlag(flags, "--dry-run");
|
|
8770
|
-
const license = await licenseService.getStatus();
|
|
8771
9544
|
const summary = `${best.profile.name} (${formatUsagePercent(best.usedPercent)} used)`;
|
|
8772
|
-
if (!license.isPro) {
|
|
8773
|
-
console.log(`Best profile: ${summary}`);
|
|
8774
|
-
console.log("Auto-run is Pro. Free path:");
|
|
8775
|
-
console.log(` codexuse run --profile ${best.profile.name} -- ${codexArgs.join(" ")}`.trimEnd());
|
|
8776
|
-
return;
|
|
8777
|
-
}
|
|
8778
9545
|
if (dryRun) {
|
|
8779
9546
|
console.log(`Would run with profile: ${summary}`);
|
|
8780
9547
|
return;
|
|
@@ -8832,21 +9599,9 @@ async function handleLicense(args, version) {
|
|
|
8832
9599
|
}
|
|
8833
9600
|
}
|
|
8834
9601
|
|
|
8835
|
-
// ../../packages/runtime-profiles/src/license/guard.ts
|
|
8836
|
-
async function assertProfileCreationAllowed(profileManager) {
|
|
8837
|
-
const manager = profileManager ?? new ProfileManager();
|
|
8838
|
-
await manager.initialize();
|
|
8839
|
-
const license = await licenseService.getStatus();
|
|
8840
|
-
const profiles = await manager.listProfiles();
|
|
8841
|
-
const licenseWithCounts = licenseService.applyProfileCount(license, profiles.length);
|
|
8842
|
-
if (typeof licenseWithCounts.profileLimit === "number" && licenseWithCounts.profilesRemaining !== null && licenseWithCounts.profilesRemaining <= 0) {
|
|
8843
|
-
throw new Error("CodexUse Free supports up to 2 profiles. Upgrade to CodexUse Pro for unlimited profiles.");
|
|
8844
|
-
}
|
|
8845
|
-
}
|
|
8846
|
-
|
|
8847
9602
|
// ../../packages/runtime-codex/src/codex/officialAppRestart.ts
|
|
8848
|
-
var
|
|
8849
|
-
var
|
|
9603
|
+
var import_node_child_process7 = require("child_process");
|
|
9604
|
+
var import_node_crypto8 = require("crypto");
|
|
8850
9605
|
var import_node_fs7 = require("fs");
|
|
8851
9606
|
var import_node_os3 = require("os");
|
|
8852
9607
|
var import_node_net = __toESM(require("net"), 1);
|
|
@@ -9121,7 +9876,7 @@ function hashProfileKey(profileKey) {
|
|
|
9121
9876
|
if (!profileKey) {
|
|
9122
9877
|
return null;
|
|
9123
9878
|
}
|
|
9124
|
-
return (0,
|
|
9879
|
+
return (0, import_node_crypto8.createHash)("sha256").update(profileKey).digest("hex").slice(0, 12);
|
|
9125
9880
|
}
|
|
9126
9881
|
function sanitizeOfficialCodexOverlayActionTargetsForRenderer(targets) {
|
|
9127
9882
|
const sanitized = [];
|
|
@@ -9744,6 +10499,7 @@ function buildInCodexStatusOverlayCssRules() {
|
|
|
9744
10499
|
`${chip} .codexuse-handoff-main{display:inline-flex;align-items:center;padding:5px 8px 5px 10px;border-radius:999px;cursor:pointer;}`,
|
|
9745
10500
|
`${chip} .codexuse-handoff-hide{display:inline-flex;width:20px;height:20px;align-items:center;justify-content:center;margin-right:3px;border-radius:50%;color:var(--cu-muted);cursor:pointer;font-size:12px;line-height:1;}`,
|
|
9746
10501
|
`${chip} .codexuse-handoff-hide:hover{background:var(--cu-border);color:var(--cu-fg,#ececf1);}`,
|
|
10502
|
+
`body>[data-radix-popper-content-wrapper],body>[data-floating-ui-portal]{z-index:2147483004!important;}`,
|
|
9747
10503
|
// Hand-off account picker is a standalone body child (not inside the pill),
|
|
9748
10504
|
// so the pill-scoped popover rules don't reach it — give it its own palette
|
|
9749
10505
|
// + popover styling, mirroring the chip.
|
|
@@ -12673,7 +13429,7 @@ async function runCdpCommandSequence(debuggerUrl, commands, timeoutMs = CODEX_CD
|
|
|
12673
13429
|
});
|
|
12674
13430
|
}
|
|
12675
13431
|
async function verifyCdpBindingRoundTrip(debuggerUrl) {
|
|
12676
|
-
const nonce = (0,
|
|
13432
|
+
const nonce = (0, import_node_crypto8.randomUUID)();
|
|
12677
13433
|
const expression = `(() => {
|
|
12678
13434
|
const binding = window[${JSON.stringify(CODEXUSE_BRIDGE_BINDING_NAME)}];
|
|
12679
13435
|
if (typeof binding !== "function") return false;
|
|
@@ -13446,7 +14202,7 @@ async function recordOfficialCodexActivity(input) {
|
|
|
13446
14202
|
}
|
|
13447
14203
|
function runCommand2(command, args, timeoutMs = COMMAND_TIMEOUT_MS) {
|
|
13448
14204
|
return new Promise((resolve) => {
|
|
13449
|
-
const child = (0,
|
|
14205
|
+
const child = (0, import_node_child_process7.spawn)(command, args, {
|
|
13450
14206
|
stdio: ["ignore", "pipe", "pipe"]
|
|
13451
14207
|
});
|
|
13452
14208
|
let stdout = "";
|
|
@@ -13745,7 +14501,7 @@ async function openCodexWithProfileHome(candidate, profileHome, profileName, cdp
|
|
|
13745
14501
|
if (appUserDataDir) {
|
|
13746
14502
|
(0, import_node_fs7.mkdirSync)(appUserDataDir, { recursive: true });
|
|
13747
14503
|
}
|
|
13748
|
-
child = (0,
|
|
14504
|
+
child = (0, import_node_child_process7.spawn)(
|
|
13749
14505
|
"/usr/bin/open",
|
|
13750
14506
|
[
|
|
13751
14507
|
"-n",
|
|
@@ -14524,6 +15280,13 @@ function formatProfileLabel(name, displayName) {
|
|
|
14524
15280
|
}
|
|
14525
15281
|
return name;
|
|
14526
15282
|
}
|
|
15283
|
+
var AUTO_ROLL_ACCESS_REQUIRED_MESSAGE = "Auto-roll requires a Pro license or an active trial.";
|
|
15284
|
+
async function assertAutoRollAccess() {
|
|
15285
|
+
const license = await licenseService.getStatus();
|
|
15286
|
+
if (!license.capabilities.autoRoll) {
|
|
15287
|
+
throw new Error(AUTO_ROLL_ACCESS_REQUIRED_MESSAGE);
|
|
15288
|
+
}
|
|
15289
|
+
}
|
|
14527
15290
|
function profileRateLimitKey(profile) {
|
|
14528
15291
|
return resolveProfileIdentityKeyFromProfile(profile);
|
|
14529
15292
|
}
|
|
@@ -15180,6 +15943,7 @@ async function collectUsageMap(manager, profiles, codexPath) {
|
|
|
15180
15943
|
return usageMap;
|
|
15181
15944
|
}
|
|
15182
15945
|
async function runAutoRollPass(manager, options) {
|
|
15946
|
+
await assertAutoRollAccess();
|
|
15183
15947
|
const profiles = await manager.listProfiles();
|
|
15184
15948
|
if (!profiles.length) {
|
|
15185
15949
|
return { switched: false, message: "No profiles found.", source: null, target: null };
|
|
@@ -15304,9 +16068,13 @@ async function handleProfileCommand(args, version) {
|
|
|
15304
16068
|
const manager = new ProfileManager();
|
|
15305
16069
|
switch (sub) {
|
|
15306
16070
|
case "list": {
|
|
15307
|
-
const
|
|
16071
|
+
const visible = await loadLicenseVisibleProfiles(manager, { freshLicense: true });
|
|
16072
|
+
const profiles = visible.profiles;
|
|
15308
16073
|
const current = await manager.getCurrentProfile();
|
|
15309
16074
|
if (!profiles.length) {
|
|
16075
|
+
if (visible.allProfiles.length) {
|
|
16076
|
+
throw new Error(PROFILE_ACCESS_DENIED_MESSAGE);
|
|
16077
|
+
}
|
|
15310
16078
|
console.log("No profiles found.");
|
|
15311
16079
|
return;
|
|
15312
16080
|
}
|
|
@@ -15348,6 +16116,7 @@ async function handleProfileCommand(args, version) {
|
|
|
15348
16116
|
console.log("No active profile.");
|
|
15349
16117
|
return;
|
|
15350
16118
|
}
|
|
16119
|
+
await assertProfileAccessAllowed(manager, current.name);
|
|
15351
16120
|
console.log(current.name);
|
|
15352
16121
|
return;
|
|
15353
16122
|
}
|
|
@@ -15371,6 +16140,7 @@ async function handleProfileCommand(args, version) {
|
|
|
15371
16140
|
if (!name) {
|
|
15372
16141
|
throw new Error("Profile name is required.");
|
|
15373
16142
|
}
|
|
16143
|
+
await assertProfileAccessAllowed(manager, name);
|
|
15374
16144
|
if (!hasFlag(flags, "--skip-login")) {
|
|
15375
16145
|
const loginMode = resolveLoginMode(parseLoginMode(flags));
|
|
15376
16146
|
await runCodexLogin(loginMode);
|
|
@@ -15385,6 +16155,7 @@ async function handleProfileCommand(args, version) {
|
|
|
15385
16155
|
if (!name) {
|
|
15386
16156
|
throw new Error("Profile name is required.");
|
|
15387
16157
|
}
|
|
16158
|
+
await assertProfileAccessAllowed(manager, name);
|
|
15388
16159
|
const activateOfficialCodex = resolveActivateCodexFlag(flags, false);
|
|
15389
16160
|
const profiles = await manager.listProfiles();
|
|
15390
16161
|
const targetProfileKey = findProfileKey(profiles, name);
|
|
@@ -15404,10 +16175,7 @@ async function handleProfileCommand(args, version) {
|
|
|
15404
16175
|
}
|
|
15405
16176
|
case "autoroll":
|
|
15406
16177
|
case "auto-roll": {
|
|
15407
|
-
|
|
15408
|
-
if (!license.isPro) {
|
|
15409
|
-
throw new Error("Auto-roll requires a Pro license.");
|
|
15410
|
-
}
|
|
16178
|
+
await assertAutoRollAccess();
|
|
15411
16179
|
const watch = hasFlag(flags, "--watch");
|
|
15412
16180
|
const dryRun = hasFlag(flags, "--dry-run");
|
|
15413
16181
|
const settings = await getStoredAutoRollSettings().catch(() => null);
|
|
@@ -15487,6 +16255,7 @@ async function handleProfileCommand(args, version) {
|
|
|
15487
16255
|
if (!from || !to) {
|
|
15488
16256
|
throw new Error("Old and new profile names are required.");
|
|
15489
16257
|
}
|
|
16258
|
+
await assertProfileAccessAllowed(manager, from);
|
|
15490
16259
|
await manager.renameProfile(from, to);
|
|
15491
16260
|
console.log(`Profile renamed: ${from} -> ${to}`);
|
|
15492
16261
|
return;
|
|
@@ -15615,7 +16384,7 @@ function normalizeSnapshot(value) {
|
|
|
15615
16384
|
settingsJson
|
|
15616
16385
|
};
|
|
15617
16386
|
}
|
|
15618
|
-
async function requestJson(method, pathname,
|
|
16387
|
+
async function requestJson(method, pathname, access, body, rawBody) {
|
|
15619
16388
|
const controller = new AbortController();
|
|
15620
16389
|
const timeout = setTimeout(() => controller.abort(), CLOUD_SYNC_TIMEOUT_MS);
|
|
15621
16390
|
try {
|
|
@@ -15623,7 +16392,10 @@ async function requestJson(method, pathname, licenseKey, body, rawBody) {
|
|
|
15623
16392
|
method,
|
|
15624
16393
|
headers: {
|
|
15625
16394
|
"Content-Type": "application/json",
|
|
15626
|
-
"x-codexuse-license-key":
|
|
16395
|
+
"x-codexuse-license-key": access.syncKey,
|
|
16396
|
+
...access.trialDeviceId ? { "x-codexuse-trial-device-id": access.trialDeviceId } : {},
|
|
16397
|
+
...access.trialToken ? { "x-codexuse-trial-token": access.trialToken } : {},
|
|
16398
|
+
...access.trialMigration ? { "x-codexuse-sync-migrate-trial": "1" } : {}
|
|
15627
16399
|
},
|
|
15628
16400
|
body: typeof rawBody === "string" ? rawBody : body === void 0 ? void 0 : JSON.stringify(body),
|
|
15629
16401
|
signal: controller.signal
|
|
@@ -15650,15 +16422,15 @@ async function requestJson(method, pathname, licenseKey, body, rawBody) {
|
|
|
15650
16422
|
clearTimeout(timeout);
|
|
15651
16423
|
}
|
|
15652
16424
|
}
|
|
15653
|
-
async function fetchRemoteSnapshot(
|
|
15654
|
-
const response = await requestJson("GET", "/v1/sync/snapshot",
|
|
16425
|
+
async function fetchRemoteSnapshot(access) {
|
|
16426
|
+
const response = await requestJson("GET", "/v1/sync/snapshot", access);
|
|
15655
16427
|
return normalizeSnapshot(response.snapshot);
|
|
15656
16428
|
}
|
|
15657
|
-
async function fetchRemoteSnapshotMeta(
|
|
16429
|
+
async function fetchRemoteSnapshotMeta(access) {
|
|
15658
16430
|
const response = await requestJson(
|
|
15659
16431
|
"GET",
|
|
15660
16432
|
"/v1/sync/snapshot/meta",
|
|
15661
|
-
|
|
16433
|
+
access
|
|
15662
16434
|
);
|
|
15663
16435
|
const updatedAt = toIsoOrNull(response.updatedAt);
|
|
15664
16436
|
const snapshotKind = normalizeSnapshotKind(response.snapshotKind);
|
|
@@ -15667,12 +16439,12 @@ async function fetchRemoteSnapshotMeta(licenseKey) {
|
|
|
15667
16439
|
snapshotKind: updatedAt && response.snapshotKind === void 0 ? "legacy-plaintext" : snapshotKind
|
|
15668
16440
|
};
|
|
15669
16441
|
}
|
|
15670
|
-
async function pushRemoteSnapshot(
|
|
16442
|
+
async function pushRemoteSnapshot(access, snapshot, options) {
|
|
15671
16443
|
const serializedSnapshot = typeof options?.serializedSnapshot === "string" ? options.serializedSnapshot : JSON.stringify(snapshot);
|
|
15672
16444
|
const response = await requestJson(
|
|
15673
16445
|
"PUT",
|
|
15674
16446
|
"/v1/sync/snapshot",
|
|
15675
|
-
|
|
16447
|
+
access,
|
|
15676
16448
|
void 0,
|
|
15677
16449
|
`{"snapshot":${serializedSnapshot}}`
|
|
15678
16450
|
);
|
|
@@ -15691,8 +16463,8 @@ async function pushRemoteSnapshot(licenseKey, snapshot, options) {
|
|
|
15691
16463
|
var import_toml3 = __toESM(require_toml(), 1);
|
|
15692
16464
|
|
|
15693
16465
|
// ../../packages/runtime-codex/src/codex/config-metadata.ts
|
|
15694
|
-
var
|
|
15695
|
-
var
|
|
16466
|
+
var import_node_child_process8 = require("child_process");
|
|
16467
|
+
var import_promises2 = require("fs/promises");
|
|
15696
16468
|
var import_node_path11 = __toESM(require("path"), 1);
|
|
15697
16469
|
var CONFIG_SCHEMA_URL = "https://raw.githubusercontent.com/openai/codex/main/codex-rs/core/config.schema.json";
|
|
15698
16470
|
var METADATA_CACHE_TTL_MS = 6e4;
|
|
@@ -15803,7 +16575,7 @@ function uniqueSorted(values) {
|
|
|
15803
16575
|
}
|
|
15804
16576
|
async function runCommand3(command, args, timeoutMs = 3e3) {
|
|
15805
16577
|
return new Promise((resolve, reject) => {
|
|
15806
|
-
const child = (0,
|
|
16578
|
+
const child = (0, import_node_child_process8.spawn)(command, args, {
|
|
15807
16579
|
stdio: ["ignore", "pipe", "pipe"],
|
|
15808
16580
|
env: process.env
|
|
15809
16581
|
});
|
|
@@ -15884,7 +16656,7 @@ async function readSchemaFromLocal() {
|
|
|
15884
16656
|
].filter((candidate) => Boolean(candidate));
|
|
15885
16657
|
for (const candidate of candidates) {
|
|
15886
16658
|
try {
|
|
15887
|
-
return await (0,
|
|
16659
|
+
return await (0, import_promises2.readFile)(candidate, "utf8");
|
|
15888
16660
|
} catch {
|
|
15889
16661
|
}
|
|
15890
16662
|
}
|
|
@@ -15984,7 +16756,7 @@ async function getCodexConfigMetadata(forceRefresh = false) {
|
|
|
15984
16756
|
}
|
|
15985
16757
|
|
|
15986
16758
|
// ../../packages/runtime-codex/src/codex/config-io.ts
|
|
15987
|
-
var
|
|
16759
|
+
var import_promises3 = require("fs/promises");
|
|
15988
16760
|
var import_node_path13 = __toESM(require("path"), 1);
|
|
15989
16761
|
|
|
15990
16762
|
// ../../packages/runtime-codex/src/codex/home.ts
|
|
@@ -16094,7 +16866,7 @@ function getConfigPath(options) {
|
|
|
16094
16866
|
}
|
|
16095
16867
|
async function ensureConfigDirExists(filePath) {
|
|
16096
16868
|
const dir = import_node_path13.default.dirname(filePath);
|
|
16097
|
-
await (0,
|
|
16869
|
+
await (0, import_promises3.mkdir)(dir, { recursive: true });
|
|
16098
16870
|
}
|
|
16099
16871
|
function normalizeLineEndings(content) {
|
|
16100
16872
|
return content.replace(/\r\n/g, "\n");
|
|
@@ -16106,7 +16878,7 @@ function ensureTrailingNewline(content) {
|
|
|
16106
16878
|
async function readConfigContent(options) {
|
|
16107
16879
|
const configPath = getConfigPath(options);
|
|
16108
16880
|
try {
|
|
16109
|
-
const content = await (0,
|
|
16881
|
+
const content = await (0, import_promises3.readFile)(configPath, "utf8");
|
|
16110
16882
|
return {
|
|
16111
16883
|
exists: true,
|
|
16112
16884
|
content: normalizeLineEndings(content)
|
|
@@ -16126,14 +16898,14 @@ async function writeConfigContent(content, options) {
|
|
|
16126
16898
|
const configPath = getConfigPath(options);
|
|
16127
16899
|
await ensureConfigDirExists(configPath);
|
|
16128
16900
|
const normalized = ensureTrailingNewline(normalizeLineEndings(content));
|
|
16129
|
-
await (0,
|
|
16901
|
+
await (0, import_promises3.writeFile)(configPath, normalized, "utf8");
|
|
16130
16902
|
}
|
|
16131
16903
|
async function getUpdatedAt(configPath, exists) {
|
|
16132
16904
|
if (!exists) {
|
|
16133
16905
|
return null;
|
|
16134
16906
|
}
|
|
16135
16907
|
try {
|
|
16136
|
-
const stats = await (0,
|
|
16908
|
+
const stats = await (0, import_promises3.stat)(configPath);
|
|
16137
16909
|
return stats.mtimeMs;
|
|
16138
16910
|
} catch (error) {
|
|
16139
16911
|
const nodeError = error;
|
|
@@ -16647,10 +17419,10 @@ async function saveCodexConfigContent(content, options) {
|
|
|
16647
17419
|
}
|
|
16648
17420
|
|
|
16649
17421
|
// ../../packages/runtime-profiles/src/cloud-sync/encryption.ts
|
|
16650
|
-
var
|
|
16651
|
-
var
|
|
16652
|
-
var
|
|
16653
|
-
var
|
|
17422
|
+
var import_node_child_process9 = require("child_process");
|
|
17423
|
+
var import_node_crypto9 = require("crypto");
|
|
17424
|
+
var KEYCHAIN_SERVICE2 = "CODEXUSE_CLOUD_SYNC_PASSPHRASE";
|
|
17425
|
+
var KEYCHAIN_ACCOUNT_PREFIX2 = "license-sha256:";
|
|
16654
17426
|
var ENCRYPTION_AAD = Buffer.from("codexuse-cloud-sync-v2", "utf8");
|
|
16655
17427
|
var SCRYPT_PARAMS = {
|
|
16656
17428
|
N: 32768,
|
|
@@ -16673,7 +17445,7 @@ function requireCloudSyncPassphrase(value) {
|
|
|
16673
17445
|
}
|
|
16674
17446
|
async function deriveKey(passphrase, salt, params = SCRYPT_PARAMS) {
|
|
16675
17447
|
return new Promise((resolve, reject) => {
|
|
16676
|
-
(0,
|
|
17448
|
+
(0, import_node_crypto9.scrypt)(passphrase, salt, params.keyLength, {
|
|
16677
17449
|
N: params.N,
|
|
16678
17450
|
r: params.r,
|
|
16679
17451
|
p: params.p,
|
|
@@ -16710,10 +17482,10 @@ function normalizeDecryptedSnapshot(value) {
|
|
|
16710
17482
|
};
|
|
16711
17483
|
}
|
|
16712
17484
|
async function encryptCloudSyncSnapshot(snapshot, passphrase) {
|
|
16713
|
-
const salt = (0,
|
|
16714
|
-
const iv = (0,
|
|
17485
|
+
const salt = (0, import_node_crypto9.randomBytes)(16);
|
|
17486
|
+
const iv = (0, import_node_crypto9.randomBytes)(12);
|
|
16715
17487
|
const key = await deriveKey(requireCloudSyncPassphrase(passphrase), salt);
|
|
16716
|
-
const cipher = (0,
|
|
17488
|
+
const cipher = (0, import_node_crypto9.createCipheriv)("aes-256-gcm", key, iv);
|
|
16717
17489
|
cipher.setAAD(ENCRYPTION_AAD);
|
|
16718
17490
|
const plaintext = Buffer.from(JSON.stringify(snapshot), "utf8");
|
|
16719
17491
|
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
@@ -16740,7 +17512,7 @@ async function decryptCloudSyncSnapshot(snapshot, passphrase) {
|
|
|
16740
17512
|
const tag = fromBase64(snapshot.encryption.tag);
|
|
16741
17513
|
const ciphertext = fromBase64(snapshot.encryption.ciphertext);
|
|
16742
17514
|
const key = await deriveKey(requireCloudSyncPassphrase(passphrase), salt, snapshot.encryption.kdfParams);
|
|
16743
|
-
const decipher = (0,
|
|
17515
|
+
const decipher = (0, import_node_crypto9.createDecipheriv)("aes-256-gcm", key, iv);
|
|
16744
17516
|
decipher.setAAD(ENCRYPTION_AAD);
|
|
16745
17517
|
decipher.setAuthTag(tag);
|
|
16746
17518
|
const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
@@ -16755,12 +17527,12 @@ async function decryptCloudSyncSnapshot(snapshot, passphrase) {
|
|
|
16755
17527
|
}
|
|
16756
17528
|
}
|
|
16757
17529
|
function keychainAccountForLicense(licenseKey) {
|
|
16758
|
-
const digest = (0,
|
|
16759
|
-
return
|
|
17530
|
+
const digest = (0, import_node_crypto9.createHash)("sha256").update(licenseKey).digest("hex");
|
|
17531
|
+
return KEYCHAIN_ACCOUNT_PREFIX2 + digest;
|
|
16760
17532
|
}
|
|
16761
|
-
function
|
|
17533
|
+
function runSecurityCommand2(args) {
|
|
16762
17534
|
return new Promise((resolve, reject) => {
|
|
16763
|
-
const child = (0,
|
|
17535
|
+
const child = (0, import_node_child_process9.spawn)("security", args, { stdio: ["ignore", "pipe", "ignore"] });
|
|
16764
17536
|
let stdout = "";
|
|
16765
17537
|
child.stdout?.on("data", (chunk) => {
|
|
16766
17538
|
stdout += String(chunk);
|
|
@@ -16777,12 +17549,12 @@ async function readRememberedCloudSyncPassphrase(licenseKey) {
|
|
|
16777
17549
|
if (process.platform !== "darwin") {
|
|
16778
17550
|
return null;
|
|
16779
17551
|
}
|
|
16780
|
-
const result = await
|
|
17552
|
+
const result = await runSecurityCommand2([
|
|
16781
17553
|
"find-generic-password",
|
|
16782
17554
|
"-a",
|
|
16783
17555
|
keychainAccountForLicense(licenseKey),
|
|
16784
17556
|
"-s",
|
|
16785
|
-
|
|
17557
|
+
KEYCHAIN_SERVICE2,
|
|
16786
17558
|
"-w"
|
|
16787
17559
|
]);
|
|
16788
17560
|
if (result.code !== 0) {
|
|
@@ -16795,13 +17567,13 @@ async function writeRememberedCloudSyncPassphrase(licenseKey, passphrase) {
|
|
|
16795
17567
|
throw new Error("keychain-unsupported");
|
|
16796
17568
|
}
|
|
16797
17569
|
const value = requireCloudSyncPassphrase(passphrase);
|
|
16798
|
-
const result = await
|
|
17570
|
+
const result = await runSecurityCommand2([
|
|
16799
17571
|
"add-generic-password",
|
|
16800
17572
|
"-U",
|
|
16801
17573
|
"-a",
|
|
16802
17574
|
keychainAccountForLicense(licenseKey),
|
|
16803
17575
|
"-s",
|
|
16804
|
-
|
|
17576
|
+
KEYCHAIN_SERVICE2,
|
|
16805
17577
|
"-w",
|
|
16806
17578
|
value
|
|
16807
17579
|
]);
|
|
@@ -16880,31 +17652,144 @@ async function writeState(patch) {
|
|
|
16880
17652
|
async function resolveEligibility() {
|
|
16881
17653
|
const stored = await getStoredLicense();
|
|
16882
17654
|
const licenseKey = typeof stored?.licenseKey === "string" ? stored.licenseKey.trim() : "";
|
|
16883
|
-
|
|
17655
|
+
const status = await licenseService.getStatus();
|
|
17656
|
+
if (licenseKey && status.isPro) {
|
|
17657
|
+
const active = status.state === "active" || status.state === "grace";
|
|
17658
|
+
if (active) {
|
|
17659
|
+
await migrateTrialCloudSyncToPaidLicenseSafe(licenseKey);
|
|
17660
|
+
}
|
|
16884
17661
|
return {
|
|
16885
|
-
canSync:
|
|
16886
|
-
|
|
16887
|
-
|
|
16888
|
-
|
|
17662
|
+
canSync: active,
|
|
17663
|
+
canDelete: active,
|
|
17664
|
+
reason: active ? null : "Cloud sync requires an active Pro license.",
|
|
17665
|
+
access: active ? { syncKey: licenseKey } : null,
|
|
17666
|
+
hasPaidLicenseKey: true,
|
|
17667
|
+
licenseState: status.state
|
|
16889
17668
|
};
|
|
16890
17669
|
}
|
|
16891
|
-
|
|
16892
|
-
|
|
16893
|
-
|
|
17670
|
+
if (status.trial?.active && status.capabilities.cloudSync) {
|
|
17671
|
+
const trialAccess = await resolveTrialCloudSyncAccess();
|
|
17672
|
+
if (trialAccess) {
|
|
17673
|
+
return {
|
|
17674
|
+
canSync: true,
|
|
17675
|
+
canDelete: true,
|
|
17676
|
+
reason: null,
|
|
17677
|
+
access: trialAccess,
|
|
17678
|
+
hasPaidLicenseKey: false,
|
|
17679
|
+
licenseState: status.state
|
|
17680
|
+
};
|
|
17681
|
+
}
|
|
16894
17682
|
return {
|
|
16895
17683
|
canSync: false,
|
|
16896
|
-
|
|
16897
|
-
|
|
17684
|
+
canDelete: false,
|
|
17685
|
+
reason: "Cloud sync trial identity is unavailable.",
|
|
17686
|
+
access: null,
|
|
17687
|
+
hasPaidLicenseKey: false,
|
|
16898
17688
|
licenseState: status.state
|
|
16899
17689
|
};
|
|
16900
17690
|
}
|
|
17691
|
+
if (status.trial?.expired) {
|
|
17692
|
+
const trialAccess = await resolveTrialCloudSyncAccess();
|
|
17693
|
+
if (trialAccess) {
|
|
17694
|
+
return {
|
|
17695
|
+
canSync: false,
|
|
17696
|
+
canDelete: true,
|
|
17697
|
+
reason: "Cloud sync requires an active trial or Pro license.",
|
|
17698
|
+
access: trialAccess,
|
|
17699
|
+
hasPaidLicenseKey: false,
|
|
17700
|
+
licenseState: status.state
|
|
17701
|
+
};
|
|
17702
|
+
}
|
|
17703
|
+
}
|
|
16901
17704
|
return {
|
|
16902
|
-
canSync:
|
|
16903
|
-
|
|
16904
|
-
|
|
17705
|
+
canSync: false,
|
|
17706
|
+
canDelete: false,
|
|
17707
|
+
reason: "Cloud sync requires an active trial or Pro license.",
|
|
17708
|
+
access: null,
|
|
17709
|
+
hasPaidLicenseKey: false,
|
|
16905
17710
|
licenseState: status.state
|
|
16906
17711
|
};
|
|
16907
17712
|
}
|
|
17713
|
+
async function hasRemoteSnapshot(access) {
|
|
17714
|
+
const meta = await fetchRemoteSnapshotMeta(access);
|
|
17715
|
+
return meta.snapshotKind !== "none" || Boolean(meta.updatedAt);
|
|
17716
|
+
}
|
|
17717
|
+
async function readRemoteSnapshotOrNull(access) {
|
|
17718
|
+
try {
|
|
17719
|
+
return await fetchRemoteSnapshot(access);
|
|
17720
|
+
} catch (error) {
|
|
17721
|
+
if (error instanceof CloudSyncClientError && (error.status === 403 || error.status === 404)) {
|
|
17722
|
+
return null;
|
|
17723
|
+
}
|
|
17724
|
+
throw error;
|
|
17725
|
+
}
|
|
17726
|
+
}
|
|
17727
|
+
async function migrateTrialPassphraseToPaidLicense(trialKey, licenseKey) {
|
|
17728
|
+
const paidPassphrase = await readRememberedCloudSyncPassphrase(licenseKey).catch(() => null);
|
|
17729
|
+
if (paidPassphrase) {
|
|
17730
|
+
return;
|
|
17731
|
+
}
|
|
17732
|
+
const trialPassphrase = await readRememberedCloudSyncPassphrase(trialKey).catch(() => null);
|
|
17733
|
+
if (!trialPassphrase) {
|
|
17734
|
+
return;
|
|
17735
|
+
}
|
|
17736
|
+
await writeRememberedCloudSyncPassphrase(licenseKey, trialPassphrase);
|
|
17737
|
+
}
|
|
17738
|
+
async function migrateTrialCloudSyncToPaidLicense(licenseKey) {
|
|
17739
|
+
const paidLicenseKey = licenseKey.trim();
|
|
17740
|
+
if (!paidLicenseKey) {
|
|
17741
|
+
return;
|
|
17742
|
+
}
|
|
17743
|
+
const trialAccess = await resolveTrialCloudSyncAccess();
|
|
17744
|
+
if (!trialAccess || trialAccess.syncKey === paidLicenseKey) {
|
|
17745
|
+
return;
|
|
17746
|
+
}
|
|
17747
|
+
if (await hasRemoteSnapshot({ syncKey: paidLicenseKey })) {
|
|
17748
|
+
await migrateTrialPassphraseToPaidLicense(trialAccess.syncKey, paidLicenseKey).catch((error) => {
|
|
17749
|
+
logWarn("[cloud-sync] failed to migrate remembered trial passphrase", {
|
|
17750
|
+
error: error instanceof Error ? error.message : String(error)
|
|
17751
|
+
});
|
|
17752
|
+
});
|
|
17753
|
+
return;
|
|
17754
|
+
}
|
|
17755
|
+
const trialSnapshot = await readRemoteSnapshotOrNull({
|
|
17756
|
+
...trialAccess,
|
|
17757
|
+
syncKey: paidLicenseKey,
|
|
17758
|
+
trialMigration: true
|
|
17759
|
+
});
|
|
17760
|
+
if (trialSnapshot) {
|
|
17761
|
+
await pushRemoteSnapshot({ syncKey: paidLicenseKey }, trialSnapshot, {
|
|
17762
|
+
serializedSnapshot: JSON.stringify(trialSnapshot)
|
|
17763
|
+
});
|
|
17764
|
+
}
|
|
17765
|
+
await migrateTrialPassphraseToPaidLicense(trialAccess.syncKey, paidLicenseKey);
|
|
17766
|
+
}
|
|
17767
|
+
async function migrateTrialCloudSyncToPaidLicenseSafe(licenseKey) {
|
|
17768
|
+
try {
|
|
17769
|
+
await migrateTrialCloudSyncToPaidLicense(licenseKey);
|
|
17770
|
+
} catch (error) {
|
|
17771
|
+
logWarn("[cloud-sync] trial snapshot migration to paid license failed", {
|
|
17772
|
+
error: error instanceof Error ? error.message : String(error)
|
|
17773
|
+
});
|
|
17774
|
+
}
|
|
17775
|
+
}
|
|
17776
|
+
async function resolveTrialCloudSyncAccess() {
|
|
17777
|
+
let deviceId;
|
|
17778
|
+
try {
|
|
17779
|
+
deviceId = await getDeviceId();
|
|
17780
|
+
} catch {
|
|
17781
|
+
return null;
|
|
17782
|
+
}
|
|
17783
|
+
const trialToken = await readStoredTrialToken(deviceId).catch(() => null);
|
|
17784
|
+
if (!trialToken) {
|
|
17785
|
+
return null;
|
|
17786
|
+
}
|
|
17787
|
+
return {
|
|
17788
|
+
syncKey: `trial:${deviceId}`,
|
|
17789
|
+
trialDeviceId: deviceId,
|
|
17790
|
+
trialToken
|
|
17791
|
+
};
|
|
17792
|
+
}
|
|
16908
17793
|
function formatMegabytes(bytes) {
|
|
16909
17794
|
return (bytes / MB_DIVISOR).toFixed(2);
|
|
16910
17795
|
}
|
|
@@ -16997,19 +17882,19 @@ async function getCloudSyncStatus() {
|
|
|
16997
17882
|
let remoteUpdatedAt = state.remoteUpdatedAt ?? null;
|
|
16998
17883
|
let remoteKind = state.remoteKind ?? "none";
|
|
16999
17884
|
let hasRememberedPassphrase = false;
|
|
17000
|
-
if (eligibility.canSync && eligibility.
|
|
17885
|
+
if (eligibility.canSync && eligibility.access) {
|
|
17001
17886
|
hasRememberedPassphrase = Boolean(
|
|
17002
|
-
await readRememberedCloudSyncPassphrase(eligibility.
|
|
17887
|
+
await readRememberedCloudSyncPassphrase(eligibility.access.syncKey).catch(() => null)
|
|
17003
17888
|
);
|
|
17004
17889
|
try {
|
|
17005
|
-
const remoteMeta = await fetchRemoteSnapshotMeta(eligibility.
|
|
17890
|
+
const remoteMeta = await fetchRemoteSnapshotMeta(eligibility.access);
|
|
17006
17891
|
remoteUpdatedAt = remoteMeta.updatedAt;
|
|
17007
17892
|
remoteKind = remoteMeta.snapshotKind;
|
|
17008
17893
|
await writeState({ remoteUpdatedAt, remoteKind });
|
|
17009
17894
|
} catch (error) {
|
|
17010
17895
|
if (error instanceof CloudSyncClientError && error.status === 404) {
|
|
17011
17896
|
try {
|
|
17012
|
-
const remote = await fetchRemoteSnapshot(eligibility.
|
|
17897
|
+
const remote = await fetchRemoteSnapshot(eligibility.access);
|
|
17013
17898
|
remoteUpdatedAt = remote?.updatedAt ?? null;
|
|
17014
17899
|
remoteKind = getSnapshotKind(remote);
|
|
17015
17900
|
} catch {
|
|
@@ -17019,9 +17904,10 @@ async function getCloudSyncStatus() {
|
|
|
17019
17904
|
}
|
|
17020
17905
|
return {
|
|
17021
17906
|
canSync: eligibility.canSync,
|
|
17907
|
+
canDelete: eligibility.canDelete,
|
|
17022
17908
|
reason: eligibility.reason,
|
|
17023
17909
|
licenseState: eligibility.licenseState,
|
|
17024
|
-
hasLicenseKey:
|
|
17910
|
+
hasLicenseKey: eligibility.hasPaidLicenseKey,
|
|
17025
17911
|
hasRememberedPassphrase,
|
|
17026
17912
|
lastPushAt: state.lastPushAt ?? null,
|
|
17027
17913
|
lastPullAt: state.lastPullAt ?? null,
|
|
@@ -17032,7 +17918,7 @@ async function getCloudSyncStatus() {
|
|
|
17032
17918
|
}
|
|
17033
17919
|
async function pushCloudSync(options = {}) {
|
|
17034
17920
|
const eligibility = await resolveEligibility();
|
|
17035
|
-
if (!eligibility.canSync || !eligibility.
|
|
17921
|
+
if (!eligibility.canSync || !eligibility.access) {
|
|
17036
17922
|
return {
|
|
17037
17923
|
mode: "push",
|
|
17038
17924
|
status: "skipped",
|
|
@@ -17045,7 +17931,7 @@ async function pushCloudSync(options = {}) {
|
|
|
17045
17931
|
try {
|
|
17046
17932
|
await profileManager.initialize();
|
|
17047
17933
|
const localSnapshot = await buildLocalSnapshot(profileManager, { enforcePushGuards: true });
|
|
17048
|
-
const passphrase = await resolvePassphrase(eligibility.
|
|
17934
|
+
const passphrase = await resolvePassphrase(eligibility.access.syncKey, options);
|
|
17049
17935
|
const encryptedSnapshot = await encryptCloudSyncSnapshot(localSnapshot, passphrase.value);
|
|
17050
17936
|
const serializedSnapshot = JSON.stringify(encryptedSnapshot);
|
|
17051
17937
|
const snapshotBytes = Buffer.byteLength(serializedSnapshot, "utf8");
|
|
@@ -17063,7 +17949,7 @@ async function pushCloudSync(options = {}) {
|
|
|
17063
17949
|
if (process.env.NODE_ENV !== "production") {
|
|
17064
17950
|
logInfo("[cloud-sync] push snapshot summary", summarizeSnapshot(localSnapshot));
|
|
17065
17951
|
}
|
|
17066
|
-
const remote = await pushRemoteSnapshot(eligibility.
|
|
17952
|
+
const remote = await pushRemoteSnapshot(eligibility.access, encryptedSnapshot, {
|
|
17067
17953
|
serializedSnapshot
|
|
17068
17954
|
});
|
|
17069
17955
|
const remoteKind = getSnapshotKind(remote.snapshot);
|
|
@@ -17082,7 +17968,7 @@ async function pushCloudSync(options = {}) {
|
|
|
17082
17968
|
remoteUpdatedAt: remote.snapshot.updatedAt
|
|
17083
17969
|
};
|
|
17084
17970
|
}
|
|
17085
|
-
await rememberPassphraseIfRequested(eligibility.
|
|
17971
|
+
await rememberPassphraseIfRequested(eligibility.access.syncKey, options, passphrase);
|
|
17086
17972
|
return {
|
|
17087
17973
|
mode: "push",
|
|
17088
17974
|
status: "applied",
|
|
@@ -17099,7 +17985,7 @@ async function pushCloudSync(options = {}) {
|
|
|
17099
17985
|
}
|
|
17100
17986
|
async function pullCloudSync(options = {}) {
|
|
17101
17987
|
const eligibility = await resolveEligibility();
|
|
17102
|
-
if (!eligibility.canSync || !eligibility.
|
|
17988
|
+
if (!eligibility.canSync || !eligibility.access) {
|
|
17103
17989
|
return {
|
|
17104
17990
|
mode: "pull",
|
|
17105
17991
|
status: "skipped",
|
|
@@ -17111,7 +17997,7 @@ async function pullCloudSync(options = {}) {
|
|
|
17111
17997
|
const profileManager = new ProfileManager();
|
|
17112
17998
|
try {
|
|
17113
17999
|
await profileManager.initialize();
|
|
17114
|
-
const remote = await fetchRemoteSnapshot(eligibility.
|
|
18000
|
+
const remote = await fetchRemoteSnapshot(eligibility.access);
|
|
17115
18001
|
if (!remote) {
|
|
17116
18002
|
await writeState({ remoteUpdatedAt: null, remoteKind: "none" });
|
|
17117
18003
|
return {
|
|
@@ -17126,7 +18012,7 @@ async function pullCloudSync(options = {}) {
|
|
|
17126
18012
|
let passphrase = null;
|
|
17127
18013
|
let plainSnapshot;
|
|
17128
18014
|
if (remote.schemaVersion === CLOUD_SYNC_ENCRYPTED_SCHEMA_VERSION) {
|
|
17129
|
-
passphrase = await resolvePassphrase(eligibility.
|
|
18015
|
+
passphrase = await resolvePassphrase(eligibility.access.syncKey, options);
|
|
17130
18016
|
plainSnapshot = await decryptCloudSyncSnapshot(remote, passphrase.value);
|
|
17131
18017
|
} else {
|
|
17132
18018
|
plainSnapshot = remote;
|
|
@@ -17139,7 +18025,7 @@ async function pullCloudSync(options = {}) {
|
|
|
17139
18025
|
lastError: void 0
|
|
17140
18026
|
});
|
|
17141
18027
|
if (passphrase) {
|
|
17142
|
-
await rememberPassphraseIfRequested(eligibility.
|
|
18028
|
+
await rememberPassphraseIfRequested(eligibility.access.syncKey, options, passphrase);
|
|
17143
18029
|
}
|
|
17144
18030
|
return {
|
|
17145
18031
|
mode: "pull",
|
|
@@ -17252,7 +18138,7 @@ async function ensureCliStorageReady() {
|
|
|
17252
18138
|
}
|
|
17253
18139
|
|
|
17254
18140
|
// src/app/main.ts
|
|
17255
|
-
var VERSION = true ? "5.1.
|
|
18141
|
+
var VERSION = true ? "5.1.1" : "0.0.0";
|
|
17256
18142
|
async function runCli() {
|
|
17257
18143
|
const args = process.argv.slice(2);
|
|
17258
18144
|
if (args.length === 0) {
|