codexuse-cli 5.0.9 → 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 +1041 -138
- 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;
|
|
@@ -4862,7 +5531,7 @@ function parseRateLimitSnapshotFromOAuthUsage(value) {
|
|
|
4862
5531
|
}
|
|
4863
5532
|
return {
|
|
4864
5533
|
lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4865
|
-
source: "codex-oauth",
|
|
5534
|
+
source: "codex-oauth-estimate",
|
|
4866
5535
|
...primary ? { primary } : {},
|
|
4867
5536
|
...secondary ? { secondary } : {},
|
|
4868
5537
|
...extraWindows.length > 0 ? { extraWindows } : {}
|
|
@@ -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);
|
|
@@ -7048,6 +7717,25 @@ var ProfileManager = class {
|
|
|
7048
7717
|
});
|
|
7049
7718
|
}
|
|
7050
7719
|
}
|
|
7720
|
+
async readConfirmedRateLimits(name, options = {}) {
|
|
7721
|
+
const profileName = this.normalizeProfileName(name);
|
|
7722
|
+
try {
|
|
7723
|
+
return await this.enqueueProfileOperation(
|
|
7724
|
+
profileName,
|
|
7725
|
+
() => this.runWithPreparedProfileHome(
|
|
7726
|
+
profileName,
|
|
7727
|
+
(env) => fetchRateLimitsViaRpc(env, { codexPath: options.codexPath }),
|
|
7728
|
+
{ syncFromActiveAuthBeforeAction: false }
|
|
7729
|
+
)
|
|
7730
|
+
);
|
|
7731
|
+
} finally {
|
|
7732
|
+
await this.enqueueAuthSwap(async () => {
|
|
7733
|
+
await this.syncActiveAuthFromProfileIfCurrent(profileName);
|
|
7734
|
+
}).catch((error) => {
|
|
7735
|
+
logWarn(`Failed to sync active auth after rate-limit confirmation for '${profileName}':`, error);
|
|
7736
|
+
});
|
|
7737
|
+
}
|
|
7738
|
+
}
|
|
7051
7739
|
async activateResetWindow(name, options = {}) {
|
|
7052
7740
|
const profileName = this.normalizeProfileName(name);
|
|
7053
7741
|
try {
|
|
@@ -7266,7 +7954,7 @@ var ProfileManager = class {
|
|
|
7266
7954
|
}
|
|
7267
7955
|
if (options.canAutoImport === false) {
|
|
7268
7956
|
return this.buildActiveCodexAuthStatus("unmanaged", description, {
|
|
7269
|
-
importBlockedReason: options.importBlockedReason ?? "
|
|
7957
|
+
importBlockedReason: options.importBlockedReason ?? "Your CodexUse trial has ended. Upgrade to Pro before importing this Codex login."
|
|
7270
7958
|
});
|
|
7271
7959
|
}
|
|
7272
7960
|
const records = await this.listProfileRecords();
|
|
@@ -7454,7 +8142,7 @@ Flags:
|
|
|
7454
8142
|
--runtime=NAME Accounts Pool runtime store: desktop
|
|
7455
8143
|
--state-dir=PATH Override the runtime state dir for Accounts Pool inspection
|
|
7456
8144
|
--passphrase-stdin Read Cloud Sync passphrase from stdin
|
|
7457
|
-
Note: profile autoroll requires Pro.
|
|
8145
|
+
Note: profile autoroll requires Pro or an active trial.
|
|
7458
8146
|
Cloud Sync: set CODEXUSE_SYNC_PASSPHRASE or use --passphrase-stdin unless the passphrase is saved in Keychain.
|
|
7459
8147
|
`);
|
|
7460
8148
|
}
|
|
@@ -7506,7 +8194,7 @@ function parseStringFlag(flags, name) {
|
|
|
7506
8194
|
|
|
7507
8195
|
// src/commands/accountPool.ts
|
|
7508
8196
|
var ACCOUNT_POOL_DOCUMENT = "desktop.account-pool.v2";
|
|
7509
|
-
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.";
|
|
7510
8198
|
var DESKTOP_RPC_STATE_DIR = "desktop-rpc";
|
|
7511
8199
|
var ACCOUNT_POOL_STORE_VERSION = 3;
|
|
7512
8200
|
var MAX_RESPONSE_IDS_PER_SESSION = 64;
|
|
@@ -7894,13 +8582,13 @@ async function listApiKeys(stateDir) {
|
|
|
7894
8582
|
return Object.values(store.apiKeysById).filter((key) => key.revokedAt === null).sort((left, right) => right.createdAt.localeCompare(left.createdAt)).map(accountPoolApiKeyView);
|
|
7895
8583
|
}
|
|
7896
8584
|
function hashToken(token) {
|
|
7897
|
-
return (0,
|
|
8585
|
+
return (0, import_node_crypto7.createHash)("sha256").update(token).digest("hex");
|
|
7898
8586
|
}
|
|
7899
8587
|
async function createApiKey(stateDir) {
|
|
7900
|
-
const plaintextToken = `cux_pool_${(0,
|
|
8588
|
+
const plaintextToken = `cux_pool_${(0, import_node_crypto7.randomBytes)(24).toString("hex")}`;
|
|
7901
8589
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7902
8590
|
const keyRecord = {
|
|
7903
|
-
id: (0,
|
|
8591
|
+
id: (0, import_node_crypto7.randomUUID)(),
|
|
7904
8592
|
tokenHash: hashToken(plaintextToken),
|
|
7905
8593
|
tokenPreview: `${plaintextToken.slice(0, 8)}...${plaintextToken.slice(-6)}`,
|
|
7906
8594
|
createdAt: now,
|
|
@@ -7994,7 +8682,7 @@ async function readAccountPoolRuntimeSummary(input = {}) {
|
|
|
7994
8682
|
stateDir,
|
|
7995
8683
|
baseUrl: resolveBaseUrlFromPort(runtime, input.port ?? null),
|
|
7996
8684
|
enabled: normalized.accountPoolEnabled,
|
|
7997
|
-
|
|
8685
|
+
hasAccountPoolAccess: license?.capabilities.accountPool === true,
|
|
7998
8686
|
routingStrategy: normalized.accountPoolRoutingStrategy,
|
|
7999
8687
|
activeApiKeyCount: apiKeys.length,
|
|
8000
8688
|
activeSessionCount: sessions.length
|
|
@@ -8011,7 +8699,7 @@ async function updateSettings(transform) {
|
|
|
8011
8699
|
const { raw, normalized } = await readSettingsSnapshot();
|
|
8012
8700
|
const nextSettings = transform(normalized);
|
|
8013
8701
|
const license = await licenseService.getStatus().catch(() => null);
|
|
8014
|
-
const forcedDisabledByLicense =
|
|
8702
|
+
const forcedDisabledByLicense = license?.capabilities.accountPool !== true && nextSettings.accountPoolEnabled === true;
|
|
8015
8703
|
const sanitizedSettings = {
|
|
8016
8704
|
...nextSettings,
|
|
8017
8705
|
accountPoolEnabled: forcedDisabledByLicense ? false : nextSettings.accountPoolEnabled,
|
|
@@ -8038,7 +8726,7 @@ async function updateSettings(transform) {
|
|
|
8038
8726
|
}
|
|
8039
8727
|
async function requireProAccess() {
|
|
8040
8728
|
const license = await licenseService.getStatus();
|
|
8041
|
-
if (!license.
|
|
8729
|
+
if (!license.capabilities.accountPool) {
|
|
8042
8730
|
throw new Error(ACCOUNT_POOL_PRO_REQUIRED_MESSAGE);
|
|
8043
8731
|
}
|
|
8044
8732
|
}
|
|
@@ -8100,7 +8788,7 @@ async function handleStatus(flags) {
|
|
|
8100
8788
|
console.log(`Runtime: ${runtime}`);
|
|
8101
8789
|
console.log(`State dir: ${stateDir}`);
|
|
8102
8790
|
console.log(`Routing: ${formatRoutingStrategy(normalized.accountPoolRoutingStrategy)}`);
|
|
8103
|
-
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"}`);
|
|
8104
8792
|
console.log(`API keys: ${apiKeys.length}`);
|
|
8105
8793
|
console.log(`Sessions: ${sessions.length}`);
|
|
8106
8794
|
console.log(`Selection mode: ${explicitSelection.length > 0 ? "explicit" : "all saved profiles"}`);
|
|
@@ -8116,7 +8804,7 @@ async function handleStatus(flags) {
|
|
|
8116
8804
|
printStringList("Published models", normalized.accountPoolExposedModels);
|
|
8117
8805
|
printStringList("Reasoning aliases", normalized.accountPoolExposedReasoningEfforts);
|
|
8118
8806
|
printStringList("Fast-mode aliases", normalized.accountPoolExposedFastModeReasoningEfforts);
|
|
8119
|
-
if (normalized.accountPoolEnabled && license?.
|
|
8807
|
+
if (normalized.accountPoolEnabled && license?.capabilities.accountPool !== true) {
|
|
8120
8808
|
console.log(ACCOUNT_POOL_PRO_REQUIRED_MESSAGE);
|
|
8121
8809
|
}
|
|
8122
8810
|
}
|
|
@@ -8321,7 +9009,7 @@ async function handleQuickstart(flags) {
|
|
|
8321
9009
|
console.log("Accounts Pool quickstart");
|
|
8322
9010
|
console.log(`Runtime: ${summary.runtime}`);
|
|
8323
9011
|
console.log(`Enabled: ${summary.enabled ? "yes" : "no"}`);
|
|
8324
|
-
console.log(`
|
|
9012
|
+
console.log(`Access: ${summary.hasAccountPoolAccess ? "yes" : "no"}`);
|
|
8325
9013
|
console.log(`API keys: ${summary.activeApiKeyCount}`);
|
|
8326
9014
|
console.log(`Base URL: ${baseUrl}`);
|
|
8327
9015
|
console.log("");
|
|
@@ -8405,7 +9093,7 @@ async function handleAccountPoolCommand(args, version) {
|
|
|
8405
9093
|
}
|
|
8406
9094
|
|
|
8407
9095
|
// src/commands/codex.ts
|
|
8408
|
-
var
|
|
9096
|
+
var import_node_child_process6 = require("child_process");
|
|
8409
9097
|
|
|
8410
9098
|
// ../../packages/runtime-profiles/src/profiles/rate-limit-notifier.ts
|
|
8411
9099
|
function maxUsedPercent(snapshot) {
|
|
@@ -8414,8 +9102,7 @@ function maxUsedPercent(snapshot) {
|
|
|
8414
9102
|
}
|
|
8415
9103
|
const candidates = [
|
|
8416
9104
|
snapshot.primary?.usedPercent,
|
|
8417
|
-
snapshot.secondary?.usedPercent
|
|
8418
|
-
...(snapshot.extraWindows ?? []).map((window) => window.usedPercent)
|
|
9105
|
+
snapshot.secondary?.usedPercent
|
|
8419
9106
|
].filter((value) => typeof value === "number" && Number.isFinite(value));
|
|
8420
9107
|
if (candidates.length === 0) {
|
|
8421
9108
|
return null;
|
|
@@ -8423,8 +9110,108 @@ function maxUsedPercent(snapshot) {
|
|
|
8423
9110
|
return Math.max(...candidates);
|
|
8424
9111
|
}
|
|
8425
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
|
+
|
|
8426
9213
|
// src/platform/codexCli.ts
|
|
8427
|
-
var
|
|
9214
|
+
var import_node_child_process5 = require("child_process");
|
|
8428
9215
|
var import_node_fs5 = require("fs");
|
|
8429
9216
|
var import_node_path8 = __toESM(require("path"));
|
|
8430
9217
|
var ENV_HINTS2 = ["CODEX_BINARY", "CODEX_CLI_PATH", "CODEX_PATH"];
|
|
@@ -8496,7 +9283,7 @@ async function runCodexLogin(mode) {
|
|
|
8496
9283
|
const resolvedMode = resolveLoginMode(mode ?? null);
|
|
8497
9284
|
const loginArgs = resolvedMode === "device" ? ["login", "--device-auth"] : ["login"];
|
|
8498
9285
|
const { command, args, shell } = buildCodexCommand2(codexPath, loginArgs);
|
|
8499
|
-
const child = (0,
|
|
9286
|
+
const child = (0, import_node_child_process5.spawn)(command, args, {
|
|
8500
9287
|
stdio: "inherit",
|
|
8501
9288
|
env: process.env,
|
|
8502
9289
|
shell
|
|
@@ -8544,8 +9331,7 @@ function formatUsagePercent(value) {
|
|
|
8544
9331
|
function snapshotResetSeconds(snapshot) {
|
|
8545
9332
|
const values = [
|
|
8546
9333
|
snapshot?.primary?.resetsInSeconds,
|
|
8547
|
-
snapshot?.secondary?.resetsInSeconds
|
|
8548
|
-
...(snapshot?.extraWindows ?? []).map((window) => window.resetsInSeconds)
|
|
9334
|
+
snapshot?.secondary?.resetsInSeconds
|
|
8549
9335
|
].filter((value) => typeof value === "number" && Number.isFinite(value));
|
|
8550
9336
|
return values.length > 0 ? Math.min(...values) : null;
|
|
8551
9337
|
}
|
|
@@ -8569,7 +9355,7 @@ function compareCandidates(left, right) {
|
|
|
8569
9355
|
async function runCommand(codexPath, args, timeoutMs) {
|
|
8570
9356
|
const command = buildCodexCommand2(codexPath, args);
|
|
8571
9357
|
return new Promise((resolve) => {
|
|
8572
|
-
const child = (0,
|
|
9358
|
+
const child = (0, import_node_child_process6.spawn)(command.command, command.args, {
|
|
8573
9359
|
env: process.env,
|
|
8574
9360
|
shell: command.shell,
|
|
8575
9361
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -8630,12 +9416,13 @@ async function runCodexWithProfile(manager, profileName, codexArgs) {
|
|
|
8630
9416
|
if (!codexPath) {
|
|
8631
9417
|
throw new Error("Codex CLI not found. Install Codex CLI or set CODEX_BINARY.");
|
|
8632
9418
|
}
|
|
9419
|
+
await assertProfileAccessAllowed(manager, profileName);
|
|
8633
9420
|
const runtime = await manager.prepareProfileRuntime(profileName, {
|
|
8634
9421
|
syncFromActiveAuthBeforeAction: false
|
|
8635
9422
|
});
|
|
8636
9423
|
const command = buildCodexCommand2(codexPath, codexArgs);
|
|
8637
9424
|
try {
|
|
8638
|
-
const child = (0,
|
|
9425
|
+
const child = (0, import_node_child_process6.spawn)(command.command, command.args, {
|
|
8639
9426
|
env: runtime.env,
|
|
8640
9427
|
shell: command.shell,
|
|
8641
9428
|
stdio: "inherit"
|
|
@@ -8736,7 +9523,7 @@ async function handleBestCommand(args) {
|
|
|
8736
9523
|
console.log(`Usage:
|
|
8737
9524
|
codexuse best -- <codex args>
|
|
8738
9525
|
|
|
8739
|
-
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.`);
|
|
8740
9527
|
return;
|
|
8741
9528
|
}
|
|
8742
9529
|
const codexPath = resolveCodexBinary();
|
|
@@ -8744,20 +9531,17 @@ Picks the healthiest saved profile, then runs Codex. Auto-run requires Pro.`);
|
|
|
8744
9531
|
throw new Error("Codex CLI not found. Install Codex CLI or set CODEX_BINARY.");
|
|
8745
9532
|
}
|
|
8746
9533
|
const manager = new ProfileManager();
|
|
8747
|
-
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;
|
|
8748
9539
|
const best = await selectBestProfile(manager, profiles, codexPath);
|
|
8749
9540
|
if (!best) {
|
|
8750
9541
|
throw new Error("No valid saved profiles found. Add one with `codexuse profile add <name>`.");
|
|
8751
9542
|
}
|
|
8752
9543
|
const dryRun = hasFlag(flags, "--dry-run");
|
|
8753
|
-
const license = await licenseService.getStatus();
|
|
8754
9544
|
const summary = `${best.profile.name} (${formatUsagePercent(best.usedPercent)} used)`;
|
|
8755
|
-
if (!license.isPro) {
|
|
8756
|
-
console.log(`Best profile: ${summary}`);
|
|
8757
|
-
console.log("Auto-run is Pro. Free path:");
|
|
8758
|
-
console.log(` codexuse run --profile ${best.profile.name} -- ${codexArgs.join(" ")}`.trimEnd());
|
|
8759
|
-
return;
|
|
8760
|
-
}
|
|
8761
9545
|
if (dryRun) {
|
|
8762
9546
|
console.log(`Would run with profile: ${summary}`);
|
|
8763
9547
|
return;
|
|
@@ -8815,21 +9599,9 @@ async function handleLicense(args, version) {
|
|
|
8815
9599
|
}
|
|
8816
9600
|
}
|
|
8817
9601
|
|
|
8818
|
-
// ../../packages/runtime-profiles/src/license/guard.ts
|
|
8819
|
-
async function assertProfileCreationAllowed(profileManager) {
|
|
8820
|
-
const manager = profileManager ?? new ProfileManager();
|
|
8821
|
-
await manager.initialize();
|
|
8822
|
-
const license = await licenseService.getStatus();
|
|
8823
|
-
const profiles = await manager.listProfiles();
|
|
8824
|
-
const licenseWithCounts = licenseService.applyProfileCount(license, profiles.length);
|
|
8825
|
-
if (typeof licenseWithCounts.profileLimit === "number" && licenseWithCounts.profilesRemaining !== null && licenseWithCounts.profilesRemaining <= 0) {
|
|
8826
|
-
throw new Error("CodexUse Free supports up to 2 profiles. Upgrade to CodexUse Pro for unlimited profiles.");
|
|
8827
|
-
}
|
|
8828
|
-
}
|
|
8829
|
-
|
|
8830
9602
|
// ../../packages/runtime-codex/src/codex/officialAppRestart.ts
|
|
8831
|
-
var
|
|
8832
|
-
var
|
|
9603
|
+
var import_node_child_process7 = require("child_process");
|
|
9604
|
+
var import_node_crypto8 = require("crypto");
|
|
8833
9605
|
var import_node_fs7 = require("fs");
|
|
8834
9606
|
var import_node_os3 = require("os");
|
|
8835
9607
|
var import_node_net = __toESM(require("net"), 1);
|
|
@@ -9104,7 +9876,7 @@ function hashProfileKey(profileKey) {
|
|
|
9104
9876
|
if (!profileKey) {
|
|
9105
9877
|
return null;
|
|
9106
9878
|
}
|
|
9107
|
-
return (0,
|
|
9879
|
+
return (0, import_node_crypto8.createHash)("sha256").update(profileKey).digest("hex").slice(0, 12);
|
|
9108
9880
|
}
|
|
9109
9881
|
function sanitizeOfficialCodexOverlayActionTargetsForRenderer(targets) {
|
|
9110
9882
|
const sanitized = [];
|
|
@@ -9727,6 +10499,7 @@ function buildInCodexStatusOverlayCssRules() {
|
|
|
9727
10499
|
`${chip} .codexuse-handoff-main{display:inline-flex;align-items:center;padding:5px 8px 5px 10px;border-radius:999px;cursor:pointer;}`,
|
|
9728
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;}`,
|
|
9729
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;}`,
|
|
9730
10503
|
// Hand-off account picker is a standalone body child (not inside the pill),
|
|
9731
10504
|
// so the pill-scoped popover rules don't reach it — give it its own palette
|
|
9732
10505
|
// + popover styling, mirroring the chip.
|
|
@@ -12656,7 +13429,7 @@ async function runCdpCommandSequence(debuggerUrl, commands, timeoutMs = CODEX_CD
|
|
|
12656
13429
|
});
|
|
12657
13430
|
}
|
|
12658
13431
|
async function verifyCdpBindingRoundTrip(debuggerUrl) {
|
|
12659
|
-
const nonce = (0,
|
|
13432
|
+
const nonce = (0, import_node_crypto8.randomUUID)();
|
|
12660
13433
|
const expression = `(() => {
|
|
12661
13434
|
const binding = window[${JSON.stringify(CODEXUSE_BRIDGE_BINDING_NAME)}];
|
|
12662
13435
|
if (typeof binding !== "function") return false;
|
|
@@ -13429,7 +14202,7 @@ async function recordOfficialCodexActivity(input) {
|
|
|
13429
14202
|
}
|
|
13430
14203
|
function runCommand2(command, args, timeoutMs = COMMAND_TIMEOUT_MS) {
|
|
13431
14204
|
return new Promise((resolve) => {
|
|
13432
|
-
const child = (0,
|
|
14205
|
+
const child = (0, import_node_child_process7.spawn)(command, args, {
|
|
13433
14206
|
stdio: ["ignore", "pipe", "pipe"]
|
|
13434
14207
|
});
|
|
13435
14208
|
let stdout = "";
|
|
@@ -13728,7 +14501,7 @@ async function openCodexWithProfileHome(candidate, profileHome, profileName, cdp
|
|
|
13728
14501
|
if (appUserDataDir) {
|
|
13729
14502
|
(0, import_node_fs7.mkdirSync)(appUserDataDir, { recursive: true });
|
|
13730
14503
|
}
|
|
13731
|
-
child = (0,
|
|
14504
|
+
child = (0, import_node_child_process7.spawn)(
|
|
13732
14505
|
"/usr/bin/open",
|
|
13733
14506
|
[
|
|
13734
14507
|
"-n",
|
|
@@ -14507,6 +15280,13 @@ function formatProfileLabel(name, displayName) {
|
|
|
14507
15280
|
}
|
|
14508
15281
|
return name;
|
|
14509
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
|
+
}
|
|
14510
15290
|
function profileRateLimitKey(profile) {
|
|
14511
15291
|
return resolveProfileIdentityKeyFromProfile(profile);
|
|
14512
15292
|
}
|
|
@@ -15163,6 +15943,7 @@ async function collectUsageMap(manager, profiles, codexPath) {
|
|
|
15163
15943
|
return usageMap;
|
|
15164
15944
|
}
|
|
15165
15945
|
async function runAutoRollPass(manager, options) {
|
|
15946
|
+
await assertAutoRollAccess();
|
|
15166
15947
|
const profiles = await manager.listProfiles();
|
|
15167
15948
|
if (!profiles.length) {
|
|
15168
15949
|
return { switched: false, message: "No profiles found.", source: null, target: null };
|
|
@@ -15287,9 +16068,13 @@ async function handleProfileCommand(args, version) {
|
|
|
15287
16068
|
const manager = new ProfileManager();
|
|
15288
16069
|
switch (sub) {
|
|
15289
16070
|
case "list": {
|
|
15290
|
-
const
|
|
16071
|
+
const visible = await loadLicenseVisibleProfiles(manager, { freshLicense: true });
|
|
16072
|
+
const profiles = visible.profiles;
|
|
15291
16073
|
const current = await manager.getCurrentProfile();
|
|
15292
16074
|
if (!profiles.length) {
|
|
16075
|
+
if (visible.allProfiles.length) {
|
|
16076
|
+
throw new Error(PROFILE_ACCESS_DENIED_MESSAGE);
|
|
16077
|
+
}
|
|
15293
16078
|
console.log("No profiles found.");
|
|
15294
16079
|
return;
|
|
15295
16080
|
}
|
|
@@ -15331,6 +16116,7 @@ async function handleProfileCommand(args, version) {
|
|
|
15331
16116
|
console.log("No active profile.");
|
|
15332
16117
|
return;
|
|
15333
16118
|
}
|
|
16119
|
+
await assertProfileAccessAllowed(manager, current.name);
|
|
15334
16120
|
console.log(current.name);
|
|
15335
16121
|
return;
|
|
15336
16122
|
}
|
|
@@ -15354,6 +16140,7 @@ async function handleProfileCommand(args, version) {
|
|
|
15354
16140
|
if (!name) {
|
|
15355
16141
|
throw new Error("Profile name is required.");
|
|
15356
16142
|
}
|
|
16143
|
+
await assertProfileAccessAllowed(manager, name);
|
|
15357
16144
|
if (!hasFlag(flags, "--skip-login")) {
|
|
15358
16145
|
const loginMode = resolveLoginMode(parseLoginMode(flags));
|
|
15359
16146
|
await runCodexLogin(loginMode);
|
|
@@ -15368,6 +16155,7 @@ async function handleProfileCommand(args, version) {
|
|
|
15368
16155
|
if (!name) {
|
|
15369
16156
|
throw new Error("Profile name is required.");
|
|
15370
16157
|
}
|
|
16158
|
+
await assertProfileAccessAllowed(manager, name);
|
|
15371
16159
|
const activateOfficialCodex = resolveActivateCodexFlag(flags, false);
|
|
15372
16160
|
const profiles = await manager.listProfiles();
|
|
15373
16161
|
const targetProfileKey = findProfileKey(profiles, name);
|
|
@@ -15387,10 +16175,7 @@ async function handleProfileCommand(args, version) {
|
|
|
15387
16175
|
}
|
|
15388
16176
|
case "autoroll":
|
|
15389
16177
|
case "auto-roll": {
|
|
15390
|
-
|
|
15391
|
-
if (!license.isPro) {
|
|
15392
|
-
throw new Error("Auto-roll requires a Pro license.");
|
|
15393
|
-
}
|
|
16178
|
+
await assertAutoRollAccess();
|
|
15394
16179
|
const watch = hasFlag(flags, "--watch");
|
|
15395
16180
|
const dryRun = hasFlag(flags, "--dry-run");
|
|
15396
16181
|
const settings = await getStoredAutoRollSettings().catch(() => null);
|
|
@@ -15470,6 +16255,7 @@ async function handleProfileCommand(args, version) {
|
|
|
15470
16255
|
if (!from || !to) {
|
|
15471
16256
|
throw new Error("Old and new profile names are required.");
|
|
15472
16257
|
}
|
|
16258
|
+
await assertProfileAccessAllowed(manager, from);
|
|
15473
16259
|
await manager.renameProfile(from, to);
|
|
15474
16260
|
console.log(`Profile renamed: ${from} -> ${to}`);
|
|
15475
16261
|
return;
|
|
@@ -15598,7 +16384,7 @@ function normalizeSnapshot(value) {
|
|
|
15598
16384
|
settingsJson
|
|
15599
16385
|
};
|
|
15600
16386
|
}
|
|
15601
|
-
async function requestJson(method, pathname,
|
|
16387
|
+
async function requestJson(method, pathname, access, body, rawBody) {
|
|
15602
16388
|
const controller = new AbortController();
|
|
15603
16389
|
const timeout = setTimeout(() => controller.abort(), CLOUD_SYNC_TIMEOUT_MS);
|
|
15604
16390
|
try {
|
|
@@ -15606,7 +16392,10 @@ async function requestJson(method, pathname, licenseKey, body, rawBody) {
|
|
|
15606
16392
|
method,
|
|
15607
16393
|
headers: {
|
|
15608
16394
|
"Content-Type": "application/json",
|
|
15609
|
-
"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" } : {}
|
|
15610
16399
|
},
|
|
15611
16400
|
body: typeof rawBody === "string" ? rawBody : body === void 0 ? void 0 : JSON.stringify(body),
|
|
15612
16401
|
signal: controller.signal
|
|
@@ -15633,15 +16422,15 @@ async function requestJson(method, pathname, licenseKey, body, rawBody) {
|
|
|
15633
16422
|
clearTimeout(timeout);
|
|
15634
16423
|
}
|
|
15635
16424
|
}
|
|
15636
|
-
async function fetchRemoteSnapshot(
|
|
15637
|
-
const response = await requestJson("GET", "/v1/sync/snapshot",
|
|
16425
|
+
async function fetchRemoteSnapshot(access) {
|
|
16426
|
+
const response = await requestJson("GET", "/v1/sync/snapshot", access);
|
|
15638
16427
|
return normalizeSnapshot(response.snapshot);
|
|
15639
16428
|
}
|
|
15640
|
-
async function fetchRemoteSnapshotMeta(
|
|
16429
|
+
async function fetchRemoteSnapshotMeta(access) {
|
|
15641
16430
|
const response = await requestJson(
|
|
15642
16431
|
"GET",
|
|
15643
16432
|
"/v1/sync/snapshot/meta",
|
|
15644
|
-
|
|
16433
|
+
access
|
|
15645
16434
|
);
|
|
15646
16435
|
const updatedAt = toIsoOrNull(response.updatedAt);
|
|
15647
16436
|
const snapshotKind = normalizeSnapshotKind(response.snapshotKind);
|
|
@@ -15650,12 +16439,12 @@ async function fetchRemoteSnapshotMeta(licenseKey) {
|
|
|
15650
16439
|
snapshotKind: updatedAt && response.snapshotKind === void 0 ? "legacy-plaintext" : snapshotKind
|
|
15651
16440
|
};
|
|
15652
16441
|
}
|
|
15653
|
-
async function pushRemoteSnapshot(
|
|
16442
|
+
async function pushRemoteSnapshot(access, snapshot, options) {
|
|
15654
16443
|
const serializedSnapshot = typeof options?.serializedSnapshot === "string" ? options.serializedSnapshot : JSON.stringify(snapshot);
|
|
15655
16444
|
const response = await requestJson(
|
|
15656
16445
|
"PUT",
|
|
15657
16446
|
"/v1/sync/snapshot",
|
|
15658
|
-
|
|
16447
|
+
access,
|
|
15659
16448
|
void 0,
|
|
15660
16449
|
`{"snapshot":${serializedSnapshot}}`
|
|
15661
16450
|
);
|
|
@@ -15674,8 +16463,8 @@ async function pushRemoteSnapshot(licenseKey, snapshot, options) {
|
|
|
15674
16463
|
var import_toml3 = __toESM(require_toml(), 1);
|
|
15675
16464
|
|
|
15676
16465
|
// ../../packages/runtime-codex/src/codex/config-metadata.ts
|
|
15677
|
-
var
|
|
15678
|
-
var
|
|
16466
|
+
var import_node_child_process8 = require("child_process");
|
|
16467
|
+
var import_promises2 = require("fs/promises");
|
|
15679
16468
|
var import_node_path11 = __toESM(require("path"), 1);
|
|
15680
16469
|
var CONFIG_SCHEMA_URL = "https://raw.githubusercontent.com/openai/codex/main/codex-rs/core/config.schema.json";
|
|
15681
16470
|
var METADATA_CACHE_TTL_MS = 6e4;
|
|
@@ -15786,7 +16575,7 @@ function uniqueSorted(values) {
|
|
|
15786
16575
|
}
|
|
15787
16576
|
async function runCommand3(command, args, timeoutMs = 3e3) {
|
|
15788
16577
|
return new Promise((resolve, reject) => {
|
|
15789
|
-
const child = (0,
|
|
16578
|
+
const child = (0, import_node_child_process8.spawn)(command, args, {
|
|
15790
16579
|
stdio: ["ignore", "pipe", "pipe"],
|
|
15791
16580
|
env: process.env
|
|
15792
16581
|
});
|
|
@@ -15867,7 +16656,7 @@ async function readSchemaFromLocal() {
|
|
|
15867
16656
|
].filter((candidate) => Boolean(candidate));
|
|
15868
16657
|
for (const candidate of candidates) {
|
|
15869
16658
|
try {
|
|
15870
|
-
return await (0,
|
|
16659
|
+
return await (0, import_promises2.readFile)(candidate, "utf8");
|
|
15871
16660
|
} catch {
|
|
15872
16661
|
}
|
|
15873
16662
|
}
|
|
@@ -15967,7 +16756,7 @@ async function getCodexConfigMetadata(forceRefresh = false) {
|
|
|
15967
16756
|
}
|
|
15968
16757
|
|
|
15969
16758
|
// ../../packages/runtime-codex/src/codex/config-io.ts
|
|
15970
|
-
var
|
|
16759
|
+
var import_promises3 = require("fs/promises");
|
|
15971
16760
|
var import_node_path13 = __toESM(require("path"), 1);
|
|
15972
16761
|
|
|
15973
16762
|
// ../../packages/runtime-codex/src/codex/home.ts
|
|
@@ -16077,7 +16866,7 @@ function getConfigPath(options) {
|
|
|
16077
16866
|
}
|
|
16078
16867
|
async function ensureConfigDirExists(filePath) {
|
|
16079
16868
|
const dir = import_node_path13.default.dirname(filePath);
|
|
16080
|
-
await (0,
|
|
16869
|
+
await (0, import_promises3.mkdir)(dir, { recursive: true });
|
|
16081
16870
|
}
|
|
16082
16871
|
function normalizeLineEndings(content) {
|
|
16083
16872
|
return content.replace(/\r\n/g, "\n");
|
|
@@ -16089,7 +16878,7 @@ function ensureTrailingNewline(content) {
|
|
|
16089
16878
|
async function readConfigContent(options) {
|
|
16090
16879
|
const configPath = getConfigPath(options);
|
|
16091
16880
|
try {
|
|
16092
|
-
const content = await (0,
|
|
16881
|
+
const content = await (0, import_promises3.readFile)(configPath, "utf8");
|
|
16093
16882
|
return {
|
|
16094
16883
|
exists: true,
|
|
16095
16884
|
content: normalizeLineEndings(content)
|
|
@@ -16109,14 +16898,14 @@ async function writeConfigContent(content, options) {
|
|
|
16109
16898
|
const configPath = getConfigPath(options);
|
|
16110
16899
|
await ensureConfigDirExists(configPath);
|
|
16111
16900
|
const normalized = ensureTrailingNewline(normalizeLineEndings(content));
|
|
16112
|
-
await (0,
|
|
16901
|
+
await (0, import_promises3.writeFile)(configPath, normalized, "utf8");
|
|
16113
16902
|
}
|
|
16114
16903
|
async function getUpdatedAt(configPath, exists) {
|
|
16115
16904
|
if (!exists) {
|
|
16116
16905
|
return null;
|
|
16117
16906
|
}
|
|
16118
16907
|
try {
|
|
16119
|
-
const stats = await (0,
|
|
16908
|
+
const stats = await (0, import_promises3.stat)(configPath);
|
|
16120
16909
|
return stats.mtimeMs;
|
|
16121
16910
|
} catch (error) {
|
|
16122
16911
|
const nodeError = error;
|
|
@@ -16630,10 +17419,10 @@ async function saveCodexConfigContent(content, options) {
|
|
|
16630
17419
|
}
|
|
16631
17420
|
|
|
16632
17421
|
// ../../packages/runtime-profiles/src/cloud-sync/encryption.ts
|
|
16633
|
-
var
|
|
16634
|
-
var
|
|
16635
|
-
var
|
|
16636
|
-
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:";
|
|
16637
17426
|
var ENCRYPTION_AAD = Buffer.from("codexuse-cloud-sync-v2", "utf8");
|
|
16638
17427
|
var SCRYPT_PARAMS = {
|
|
16639
17428
|
N: 32768,
|
|
@@ -16656,7 +17445,7 @@ function requireCloudSyncPassphrase(value) {
|
|
|
16656
17445
|
}
|
|
16657
17446
|
async function deriveKey(passphrase, salt, params = SCRYPT_PARAMS) {
|
|
16658
17447
|
return new Promise((resolve, reject) => {
|
|
16659
|
-
(0,
|
|
17448
|
+
(0, import_node_crypto9.scrypt)(passphrase, salt, params.keyLength, {
|
|
16660
17449
|
N: params.N,
|
|
16661
17450
|
r: params.r,
|
|
16662
17451
|
p: params.p,
|
|
@@ -16693,10 +17482,10 @@ function normalizeDecryptedSnapshot(value) {
|
|
|
16693
17482
|
};
|
|
16694
17483
|
}
|
|
16695
17484
|
async function encryptCloudSyncSnapshot(snapshot, passphrase) {
|
|
16696
|
-
const salt = (0,
|
|
16697
|
-
const iv = (0,
|
|
17485
|
+
const salt = (0, import_node_crypto9.randomBytes)(16);
|
|
17486
|
+
const iv = (0, import_node_crypto9.randomBytes)(12);
|
|
16698
17487
|
const key = await deriveKey(requireCloudSyncPassphrase(passphrase), salt);
|
|
16699
|
-
const cipher = (0,
|
|
17488
|
+
const cipher = (0, import_node_crypto9.createCipheriv)("aes-256-gcm", key, iv);
|
|
16700
17489
|
cipher.setAAD(ENCRYPTION_AAD);
|
|
16701
17490
|
const plaintext = Buffer.from(JSON.stringify(snapshot), "utf8");
|
|
16702
17491
|
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
@@ -16723,7 +17512,7 @@ async function decryptCloudSyncSnapshot(snapshot, passphrase) {
|
|
|
16723
17512
|
const tag = fromBase64(snapshot.encryption.tag);
|
|
16724
17513
|
const ciphertext = fromBase64(snapshot.encryption.ciphertext);
|
|
16725
17514
|
const key = await deriveKey(requireCloudSyncPassphrase(passphrase), salt, snapshot.encryption.kdfParams);
|
|
16726
|
-
const decipher = (0,
|
|
17515
|
+
const decipher = (0, import_node_crypto9.createDecipheriv)("aes-256-gcm", key, iv);
|
|
16727
17516
|
decipher.setAAD(ENCRYPTION_AAD);
|
|
16728
17517
|
decipher.setAuthTag(tag);
|
|
16729
17518
|
const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
@@ -16738,12 +17527,12 @@ async function decryptCloudSyncSnapshot(snapshot, passphrase) {
|
|
|
16738
17527
|
}
|
|
16739
17528
|
}
|
|
16740
17529
|
function keychainAccountForLicense(licenseKey) {
|
|
16741
|
-
const digest = (0,
|
|
16742
|
-
return
|
|
17530
|
+
const digest = (0, import_node_crypto9.createHash)("sha256").update(licenseKey).digest("hex");
|
|
17531
|
+
return KEYCHAIN_ACCOUNT_PREFIX2 + digest;
|
|
16743
17532
|
}
|
|
16744
|
-
function
|
|
17533
|
+
function runSecurityCommand2(args) {
|
|
16745
17534
|
return new Promise((resolve, reject) => {
|
|
16746
|
-
const child = (0,
|
|
17535
|
+
const child = (0, import_node_child_process9.spawn)("security", args, { stdio: ["ignore", "pipe", "ignore"] });
|
|
16747
17536
|
let stdout = "";
|
|
16748
17537
|
child.stdout?.on("data", (chunk) => {
|
|
16749
17538
|
stdout += String(chunk);
|
|
@@ -16760,12 +17549,12 @@ async function readRememberedCloudSyncPassphrase(licenseKey) {
|
|
|
16760
17549
|
if (process.platform !== "darwin") {
|
|
16761
17550
|
return null;
|
|
16762
17551
|
}
|
|
16763
|
-
const result = await
|
|
17552
|
+
const result = await runSecurityCommand2([
|
|
16764
17553
|
"find-generic-password",
|
|
16765
17554
|
"-a",
|
|
16766
17555
|
keychainAccountForLicense(licenseKey),
|
|
16767
17556
|
"-s",
|
|
16768
|
-
|
|
17557
|
+
KEYCHAIN_SERVICE2,
|
|
16769
17558
|
"-w"
|
|
16770
17559
|
]);
|
|
16771
17560
|
if (result.code !== 0) {
|
|
@@ -16778,13 +17567,13 @@ async function writeRememberedCloudSyncPassphrase(licenseKey, passphrase) {
|
|
|
16778
17567
|
throw new Error("keychain-unsupported");
|
|
16779
17568
|
}
|
|
16780
17569
|
const value = requireCloudSyncPassphrase(passphrase);
|
|
16781
|
-
const result = await
|
|
17570
|
+
const result = await runSecurityCommand2([
|
|
16782
17571
|
"add-generic-password",
|
|
16783
17572
|
"-U",
|
|
16784
17573
|
"-a",
|
|
16785
17574
|
keychainAccountForLicense(licenseKey),
|
|
16786
17575
|
"-s",
|
|
16787
|
-
|
|
17576
|
+
KEYCHAIN_SERVICE2,
|
|
16788
17577
|
"-w",
|
|
16789
17578
|
value
|
|
16790
17579
|
]);
|
|
@@ -16863,31 +17652,144 @@ async function writeState(patch) {
|
|
|
16863
17652
|
async function resolveEligibility() {
|
|
16864
17653
|
const stored = await getStoredLicense();
|
|
16865
17654
|
const licenseKey = typeof stored?.licenseKey === "string" ? stored.licenseKey.trim() : "";
|
|
16866
|
-
|
|
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
|
+
}
|
|
16867
17661
|
return {
|
|
16868
|
-
canSync:
|
|
16869
|
-
|
|
16870
|
-
|
|
16871
|
-
|
|
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
|
|
16872
17668
|
};
|
|
16873
17669
|
}
|
|
16874
|
-
|
|
16875
|
-
|
|
16876
|
-
|
|
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
|
+
}
|
|
16877
17682
|
return {
|
|
16878
17683
|
canSync: false,
|
|
16879
|
-
|
|
16880
|
-
|
|
17684
|
+
canDelete: false,
|
|
17685
|
+
reason: "Cloud sync trial identity is unavailable.",
|
|
17686
|
+
access: null,
|
|
17687
|
+
hasPaidLicenseKey: false,
|
|
16881
17688
|
licenseState: status.state
|
|
16882
17689
|
};
|
|
16883
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
|
+
}
|
|
16884
17704
|
return {
|
|
16885
|
-
canSync:
|
|
16886
|
-
|
|
16887
|
-
|
|
17705
|
+
canSync: false,
|
|
17706
|
+
canDelete: false,
|
|
17707
|
+
reason: "Cloud sync requires an active trial or Pro license.",
|
|
17708
|
+
access: null,
|
|
17709
|
+
hasPaidLicenseKey: false,
|
|
16888
17710
|
licenseState: status.state
|
|
16889
17711
|
};
|
|
16890
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
|
+
}
|
|
16891
17793
|
function formatMegabytes(bytes) {
|
|
16892
17794
|
return (bytes / MB_DIVISOR).toFixed(2);
|
|
16893
17795
|
}
|
|
@@ -16980,19 +17882,19 @@ async function getCloudSyncStatus() {
|
|
|
16980
17882
|
let remoteUpdatedAt = state.remoteUpdatedAt ?? null;
|
|
16981
17883
|
let remoteKind = state.remoteKind ?? "none";
|
|
16982
17884
|
let hasRememberedPassphrase = false;
|
|
16983
|
-
if (eligibility.canSync && eligibility.
|
|
17885
|
+
if (eligibility.canSync && eligibility.access) {
|
|
16984
17886
|
hasRememberedPassphrase = Boolean(
|
|
16985
|
-
await readRememberedCloudSyncPassphrase(eligibility.
|
|
17887
|
+
await readRememberedCloudSyncPassphrase(eligibility.access.syncKey).catch(() => null)
|
|
16986
17888
|
);
|
|
16987
17889
|
try {
|
|
16988
|
-
const remoteMeta = await fetchRemoteSnapshotMeta(eligibility.
|
|
17890
|
+
const remoteMeta = await fetchRemoteSnapshotMeta(eligibility.access);
|
|
16989
17891
|
remoteUpdatedAt = remoteMeta.updatedAt;
|
|
16990
17892
|
remoteKind = remoteMeta.snapshotKind;
|
|
16991
17893
|
await writeState({ remoteUpdatedAt, remoteKind });
|
|
16992
17894
|
} catch (error) {
|
|
16993
17895
|
if (error instanceof CloudSyncClientError && error.status === 404) {
|
|
16994
17896
|
try {
|
|
16995
|
-
const remote = await fetchRemoteSnapshot(eligibility.
|
|
17897
|
+
const remote = await fetchRemoteSnapshot(eligibility.access);
|
|
16996
17898
|
remoteUpdatedAt = remote?.updatedAt ?? null;
|
|
16997
17899
|
remoteKind = getSnapshotKind(remote);
|
|
16998
17900
|
} catch {
|
|
@@ -17002,9 +17904,10 @@ async function getCloudSyncStatus() {
|
|
|
17002
17904
|
}
|
|
17003
17905
|
return {
|
|
17004
17906
|
canSync: eligibility.canSync,
|
|
17907
|
+
canDelete: eligibility.canDelete,
|
|
17005
17908
|
reason: eligibility.reason,
|
|
17006
17909
|
licenseState: eligibility.licenseState,
|
|
17007
|
-
hasLicenseKey:
|
|
17910
|
+
hasLicenseKey: eligibility.hasPaidLicenseKey,
|
|
17008
17911
|
hasRememberedPassphrase,
|
|
17009
17912
|
lastPushAt: state.lastPushAt ?? null,
|
|
17010
17913
|
lastPullAt: state.lastPullAt ?? null,
|
|
@@ -17015,7 +17918,7 @@ async function getCloudSyncStatus() {
|
|
|
17015
17918
|
}
|
|
17016
17919
|
async function pushCloudSync(options = {}) {
|
|
17017
17920
|
const eligibility = await resolveEligibility();
|
|
17018
|
-
if (!eligibility.canSync || !eligibility.
|
|
17921
|
+
if (!eligibility.canSync || !eligibility.access) {
|
|
17019
17922
|
return {
|
|
17020
17923
|
mode: "push",
|
|
17021
17924
|
status: "skipped",
|
|
@@ -17028,7 +17931,7 @@ async function pushCloudSync(options = {}) {
|
|
|
17028
17931
|
try {
|
|
17029
17932
|
await profileManager.initialize();
|
|
17030
17933
|
const localSnapshot = await buildLocalSnapshot(profileManager, { enforcePushGuards: true });
|
|
17031
|
-
const passphrase = await resolvePassphrase(eligibility.
|
|
17934
|
+
const passphrase = await resolvePassphrase(eligibility.access.syncKey, options);
|
|
17032
17935
|
const encryptedSnapshot = await encryptCloudSyncSnapshot(localSnapshot, passphrase.value);
|
|
17033
17936
|
const serializedSnapshot = JSON.stringify(encryptedSnapshot);
|
|
17034
17937
|
const snapshotBytes = Buffer.byteLength(serializedSnapshot, "utf8");
|
|
@@ -17046,7 +17949,7 @@ async function pushCloudSync(options = {}) {
|
|
|
17046
17949
|
if (process.env.NODE_ENV !== "production") {
|
|
17047
17950
|
logInfo("[cloud-sync] push snapshot summary", summarizeSnapshot(localSnapshot));
|
|
17048
17951
|
}
|
|
17049
|
-
const remote = await pushRemoteSnapshot(eligibility.
|
|
17952
|
+
const remote = await pushRemoteSnapshot(eligibility.access, encryptedSnapshot, {
|
|
17050
17953
|
serializedSnapshot
|
|
17051
17954
|
});
|
|
17052
17955
|
const remoteKind = getSnapshotKind(remote.snapshot);
|
|
@@ -17065,7 +17968,7 @@ async function pushCloudSync(options = {}) {
|
|
|
17065
17968
|
remoteUpdatedAt: remote.snapshot.updatedAt
|
|
17066
17969
|
};
|
|
17067
17970
|
}
|
|
17068
|
-
await rememberPassphraseIfRequested(eligibility.
|
|
17971
|
+
await rememberPassphraseIfRequested(eligibility.access.syncKey, options, passphrase);
|
|
17069
17972
|
return {
|
|
17070
17973
|
mode: "push",
|
|
17071
17974
|
status: "applied",
|
|
@@ -17082,7 +17985,7 @@ async function pushCloudSync(options = {}) {
|
|
|
17082
17985
|
}
|
|
17083
17986
|
async function pullCloudSync(options = {}) {
|
|
17084
17987
|
const eligibility = await resolveEligibility();
|
|
17085
|
-
if (!eligibility.canSync || !eligibility.
|
|
17988
|
+
if (!eligibility.canSync || !eligibility.access) {
|
|
17086
17989
|
return {
|
|
17087
17990
|
mode: "pull",
|
|
17088
17991
|
status: "skipped",
|
|
@@ -17094,7 +17997,7 @@ async function pullCloudSync(options = {}) {
|
|
|
17094
17997
|
const profileManager = new ProfileManager();
|
|
17095
17998
|
try {
|
|
17096
17999
|
await profileManager.initialize();
|
|
17097
|
-
const remote = await fetchRemoteSnapshot(eligibility.
|
|
18000
|
+
const remote = await fetchRemoteSnapshot(eligibility.access);
|
|
17098
18001
|
if (!remote) {
|
|
17099
18002
|
await writeState({ remoteUpdatedAt: null, remoteKind: "none" });
|
|
17100
18003
|
return {
|
|
@@ -17109,7 +18012,7 @@ async function pullCloudSync(options = {}) {
|
|
|
17109
18012
|
let passphrase = null;
|
|
17110
18013
|
let plainSnapshot;
|
|
17111
18014
|
if (remote.schemaVersion === CLOUD_SYNC_ENCRYPTED_SCHEMA_VERSION) {
|
|
17112
|
-
passphrase = await resolvePassphrase(eligibility.
|
|
18015
|
+
passphrase = await resolvePassphrase(eligibility.access.syncKey, options);
|
|
17113
18016
|
plainSnapshot = await decryptCloudSyncSnapshot(remote, passphrase.value);
|
|
17114
18017
|
} else {
|
|
17115
18018
|
plainSnapshot = remote;
|
|
@@ -17122,7 +18025,7 @@ async function pullCloudSync(options = {}) {
|
|
|
17122
18025
|
lastError: void 0
|
|
17123
18026
|
});
|
|
17124
18027
|
if (passphrase) {
|
|
17125
|
-
await rememberPassphraseIfRequested(eligibility.
|
|
18028
|
+
await rememberPassphraseIfRequested(eligibility.access.syncKey, options, passphrase);
|
|
17126
18029
|
}
|
|
17127
18030
|
return {
|
|
17128
18031
|
mode: "pull",
|
|
@@ -17235,7 +18138,7 @@ async function ensureCliStorageReady() {
|
|
|
17235
18138
|
}
|
|
17236
18139
|
|
|
17237
18140
|
// src/app/main.ts
|
|
17238
|
-
var VERSION = true ? "5.
|
|
18141
|
+
var VERSION = true ? "5.1.1" : "0.0.0";
|
|
17239
18142
|
async function runCli() {
|
|
17240
18143
|
const args = process.argv.slice(2);
|
|
17241
18144
|
if (args.length === 0) {
|