codexuse-cli 5.1.0 → 5.1.5
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 +1305 -245
- 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
|
}
|
|
@@ -3732,9 +4327,19 @@ async function requestGumroadVerify(licenseKey, mode) {
|
|
|
3732
4327
|
clearTimeout(timeout);
|
|
3733
4328
|
}
|
|
3734
4329
|
}
|
|
3735
|
-
function
|
|
4330
|
+
function isExplicitRevocationMessage(message) {
|
|
4331
|
+
if (!message) {
|
|
4332
|
+
return false;
|
|
4333
|
+
}
|
|
4334
|
+
const normalized = message.toLowerCase();
|
|
4335
|
+
return normalized.includes("revoked") || normalized.includes("refunded") || normalized.includes("chargeback") || normalized.includes("disabled") || normalized.includes("subscription") || normalized.includes("no longer active");
|
|
4336
|
+
}
|
|
4337
|
+
function normalizeVerificationResult(response, mode) {
|
|
3736
4338
|
if (!response.success) {
|
|
3737
4339
|
const message = response.message ?? "License key is invalid.";
|
|
4340
|
+
if (mode === "refresh" && !isExplicitRevocationMessage(message)) {
|
|
4341
|
+
throw new LicenseError(message, "network");
|
|
4342
|
+
}
|
|
3738
4343
|
throw new LicenseError(message, "invalid");
|
|
3739
4344
|
}
|
|
3740
4345
|
if (!response.purchase) {
|
|
@@ -3763,6 +4368,47 @@ function ensureWithinUseLimit(response) {
|
|
|
3763
4368
|
);
|
|
3764
4369
|
}
|
|
3765
4370
|
}
|
|
4371
|
+
function computeCapabilities(opts) {
|
|
4372
|
+
return {
|
|
4373
|
+
unlimitedProfiles: opts.entitled,
|
|
4374
|
+
accountPool: opts.entitled,
|
|
4375
|
+
telegram: opts.entitled,
|
|
4376
|
+
autoRoll: opts.entitled,
|
|
4377
|
+
cloudSync: opts.licensed || opts.entitled
|
|
4378
|
+
};
|
|
4379
|
+
}
|
|
4380
|
+
function applyTrial(base, trial) {
|
|
4381
|
+
if (base.isPro) {
|
|
4382
|
+
return { ...base, trial: null };
|
|
4383
|
+
}
|
|
4384
|
+
const { info, locked } = trial;
|
|
4385
|
+
if (!info) {
|
|
4386
|
+
return base;
|
|
4387
|
+
}
|
|
4388
|
+
if (info.active) {
|
|
4389
|
+
return {
|
|
4390
|
+
...base,
|
|
4391
|
+
tier: "trial",
|
|
4392
|
+
state: "trial",
|
|
4393
|
+
capabilities: computeCapabilities({ licensed: false, entitled: true }),
|
|
4394
|
+
profileLimit: null,
|
|
4395
|
+
profilesRemaining: null,
|
|
4396
|
+
trial: info
|
|
4397
|
+
};
|
|
4398
|
+
}
|
|
4399
|
+
if (locked) {
|
|
4400
|
+
return {
|
|
4401
|
+
...base,
|
|
4402
|
+
tier: "free",
|
|
4403
|
+
state: "locked",
|
|
4404
|
+
capabilities: computeCapabilities({ licensed: false, entitled: false }),
|
|
4405
|
+
profileLimit: 0,
|
|
4406
|
+
profilesRemaining: 0,
|
|
4407
|
+
trial: info
|
|
4408
|
+
};
|
|
4409
|
+
}
|
|
4410
|
+
return { ...base, trial: info };
|
|
4411
|
+
}
|
|
3766
4412
|
function toLicenseStatus(stored, overrides = {}) {
|
|
3767
4413
|
const tier = determineTierFromStored(stored);
|
|
3768
4414
|
const isPro = tier === "pro";
|
|
@@ -3771,6 +4417,8 @@ function toLicenseStatus(stored, overrides = {}) {
|
|
|
3771
4417
|
tier,
|
|
3772
4418
|
state,
|
|
3773
4419
|
isPro,
|
|
4420
|
+
capabilities: computeCapabilities({ licensed: isPro, entitled: isPro }),
|
|
4421
|
+
trial: null,
|
|
3774
4422
|
profileLimit: isPro ? null : FREE_PROFILE_LIMIT,
|
|
3775
4423
|
profilesRemaining: null,
|
|
3776
4424
|
purchaseEmail: stored?.purchaseEmail ?? null,
|
|
@@ -3812,15 +4460,27 @@ var LicenseService = class {
|
|
|
3812
4460
|
const refreshInBackground = Boolean(options.refreshInBackground);
|
|
3813
4461
|
const secret = await getLicenseSecret();
|
|
3814
4462
|
const stored = await getStoredLicense();
|
|
4463
|
+
const finalize = async (status) => {
|
|
4464
|
+
if (status.isPro) {
|
|
4465
|
+
return { ...status, trial: null };
|
|
4466
|
+
}
|
|
4467
|
+
return applyTrial(
|
|
4468
|
+
status,
|
|
4469
|
+
await resolveTrialState(Date.now(), {
|
|
4470
|
+
allowNetwork: !allowStale || forceRefresh,
|
|
4471
|
+
refreshInBackground
|
|
4472
|
+
})
|
|
4473
|
+
);
|
|
4474
|
+
};
|
|
3815
4475
|
if (!stored?.licenseKey) {
|
|
3816
|
-
return toLicenseStatus(null);
|
|
4476
|
+
return finalize(toLicenseStatus(null));
|
|
3817
4477
|
}
|
|
3818
4478
|
const now = Date.now();
|
|
3819
4479
|
const nextCheckTs = parseTimestamp(stored.nextCheckAt);
|
|
3820
4480
|
const lastVerifiedTs = parseTimestamp(stored.lastVerifiedAt);
|
|
3821
4481
|
const graceStale = (stored.status === "grace" || stored.status === "error") && (lastVerifiedTs === null || lastVerifiedTs + GRACE_MAX_AGE_MS < now);
|
|
3822
4482
|
let workingStored = { ...stored };
|
|
3823
|
-
const signatureValid =
|
|
4483
|
+
const signatureValid = isSignatureValid2(workingStored, secret);
|
|
3824
4484
|
let cappedNextCheckTs = nextCheckTs;
|
|
3825
4485
|
if (nextCheckTs !== null && nextCheckTs > now + MAX_NEXT_CHECK_MS) {
|
|
3826
4486
|
cappedNextCheckTs = now;
|
|
@@ -3843,7 +4503,7 @@ var LicenseService = class {
|
|
|
3843
4503
|
if (allowStale && shouldRefresh && refreshInBackground) {
|
|
3844
4504
|
this.refreshStatusInBackground({ force: forceRefresh });
|
|
3845
4505
|
}
|
|
3846
|
-
return cached;
|
|
4506
|
+
return finalize(cached);
|
|
3847
4507
|
}
|
|
3848
4508
|
if (this.verificationPromise && !forceRefresh) {
|
|
3849
4509
|
return this.verificationPromise;
|
|
@@ -3852,7 +4512,7 @@ var LicenseService = class {
|
|
|
3852
4512
|
try {
|
|
3853
4513
|
const result = await requestGumroadVerify(workingStored.licenseKey, "refresh");
|
|
3854
4514
|
ensureWithinUseLimit(result);
|
|
3855
|
-
const normalized = normalizeVerificationResult(result);
|
|
4515
|
+
const normalized = normalizeVerificationResult(result, "refresh");
|
|
3856
4516
|
const updated = {
|
|
3857
4517
|
licenseKey: workingStored.licenseKey,
|
|
3858
4518
|
purchaseEmail: normalized.email,
|
|
@@ -3863,7 +4523,7 @@ var LicenseService = class {
|
|
|
3863
4523
|
};
|
|
3864
4524
|
const signed = withSignature(updated, secret);
|
|
3865
4525
|
await persistLicense(signed);
|
|
3866
|
-
return toLicenseStatus(signed);
|
|
4526
|
+
return finalize(toLicenseStatus(signed));
|
|
3867
4527
|
} catch (error) {
|
|
3868
4528
|
const message = error instanceof Error ? error.message : "License verification failed.";
|
|
3869
4529
|
if (error instanceof LicenseError && error.code === "network") {
|
|
@@ -3881,7 +4541,7 @@ var LicenseService = class {
|
|
|
3881
4541
|
state: fallbackStored2.status ?? "grace",
|
|
3882
4542
|
error: message
|
|
3883
4543
|
});
|
|
3884
|
-
return fallback;
|
|
4544
|
+
return finalize(fallback);
|
|
3885
4545
|
}
|
|
3886
4546
|
const fallbackStored = withSignature({
|
|
3887
4547
|
licenseKey: workingStored.licenseKey,
|
|
@@ -3892,7 +4552,7 @@ var LicenseService = class {
|
|
|
3892
4552
|
lastVerificationError: message
|
|
3893
4553
|
}, secret);
|
|
3894
4554
|
await persistLicense(fallbackStored);
|
|
3895
|
-
return toLicenseStatus(fallbackStored, { error: message });
|
|
4555
|
+
return finalize(toLicenseStatus(fallbackStored, { error: message }));
|
|
3896
4556
|
}
|
|
3897
4557
|
};
|
|
3898
4558
|
this.verificationPromise = verify();
|
|
@@ -3908,10 +4568,10 @@ var LicenseService = class {
|
|
|
3908
4568
|
throw new Error("License key is required.");
|
|
3909
4569
|
}
|
|
3910
4570
|
const secret = await getLicenseSecret();
|
|
3911
|
-
const digest =
|
|
4571
|
+
const digest = import_node_crypto5.default.createHash("sha256").update(trimmed).digest("hex");
|
|
3912
4572
|
const result = await requestGumroadVerify(trimmed, "activation");
|
|
3913
4573
|
ensureWithinUseLimit(result);
|
|
3914
|
-
const normalized = normalizeVerificationResult(result);
|
|
4574
|
+
const normalized = normalizeVerificationResult(result, "activation");
|
|
3915
4575
|
const stored = {
|
|
3916
4576
|
licenseKey: trimmed,
|
|
3917
4577
|
purchaseEmail: normalized.email,
|
|
@@ -3950,9 +4610,28 @@ var LicenseService = class {
|
|
|
3950
4610
|
}
|
|
3951
4611
|
};
|
|
3952
4612
|
var licenseService = new LicenseService();
|
|
4613
|
+
function isExplicitInvalidation(message) {
|
|
4614
|
+
if (!message) {
|
|
4615
|
+
return false;
|
|
4616
|
+
}
|
|
4617
|
+
const normalized = message.toLowerCase();
|
|
4618
|
+
return normalized.includes("revoked") || normalized.includes("refunded") || normalized.includes("invalid") || normalized.includes("no longer active");
|
|
4619
|
+
}
|
|
4620
|
+
function shouldEnforceProfileLimit(status) {
|
|
4621
|
+
if (status.capabilities.unlimitedProfiles) {
|
|
4622
|
+
return false;
|
|
4623
|
+
}
|
|
4624
|
+
if (status.state === "grace" || status.state === "verifying" || status.state === "error") {
|
|
4625
|
+
return false;
|
|
4626
|
+
}
|
|
4627
|
+
if (isExplicitInvalidation(status.error)) {
|
|
4628
|
+
return true;
|
|
4629
|
+
}
|
|
4630
|
+
return status.state === "inactive" || status.state === "locked";
|
|
4631
|
+
}
|
|
3953
4632
|
|
|
3954
4633
|
// ../../packages/runtime-profiles/src/profiles/profile-manager.ts
|
|
3955
|
-
var
|
|
4634
|
+
var import_node_crypto6 = require("crypto");
|
|
3956
4635
|
var import_fs = require("fs");
|
|
3957
4636
|
var import_path = __toESM(require("path"), 1);
|
|
3958
4637
|
var import_path2 = require("path");
|
|
@@ -4001,11 +4680,15 @@ function resolveProfileIdentityKey(profile) {
|
|
|
4001
4680
|
return profileName ? `profile:${profileName}` : null;
|
|
4002
4681
|
}
|
|
4003
4682
|
function resolveProfileIdentityKeyFromProfile(profile) {
|
|
4683
|
+
const rateLimitKey = normalizeValue(profile.rateLimitKey);
|
|
4684
|
+
if (rateLimitKey) {
|
|
4685
|
+
return rateLimitKey;
|
|
4686
|
+
}
|
|
4004
4687
|
return resolveProfileIdentityKey(profile) ?? `profile:${profile.name}`;
|
|
4005
4688
|
}
|
|
4006
4689
|
|
|
4007
4690
|
// ../../packages/runtime-codex/src/codex/rpc.ts
|
|
4008
|
-
var
|
|
4691
|
+
var import_node_child_process4 = require("child_process");
|
|
4009
4692
|
var import_node_readline = __toESM(require("readline"), 1);
|
|
4010
4693
|
var import_node_fs3 = require("fs");
|
|
4011
4694
|
var import_node_os2 = __toESM(require("os"), 1);
|
|
@@ -4137,7 +4820,7 @@ async function requireCodexCli() {
|
|
|
4137
4820
|
}
|
|
4138
4821
|
|
|
4139
4822
|
// ../../packages/runtime-codex/src/codex/command.ts
|
|
4140
|
-
var
|
|
4823
|
+
var import_node_child_process3 = require("child_process");
|
|
4141
4824
|
var import_node_path4 = __toESM(require("path"), 1);
|
|
4142
4825
|
var cachedNodeRuntime = null;
|
|
4143
4826
|
function isJavaScriptEntrypoint(binaryPath) {
|
|
@@ -4180,7 +4863,7 @@ function resolveNodeRuntime(env) {
|
|
|
4180
4863
|
)
|
|
4181
4864
|
);
|
|
4182
4865
|
for (const candidate of candidates) {
|
|
4183
|
-
const probe = (0,
|
|
4866
|
+
const probe = (0, import_node_child_process3.spawnSync)(candidate, ["-v"], { env: runtimeEnv, stdio: "ignore" });
|
|
4184
4867
|
if (!probe.error && probe.status === 0) {
|
|
4185
4868
|
cachedNodeRuntime = candidate;
|
|
4186
4869
|
return candidate;
|
|
@@ -4204,6 +4887,17 @@ function buildCodexCommand(binaryPath, args, env) {
|
|
|
4204
4887
|
};
|
|
4205
4888
|
}
|
|
4206
4889
|
|
|
4890
|
+
// ../../packages/runtime-codex/src/cli/env.ts
|
|
4891
|
+
function buildCodexChildEnv(overrides = {}) {
|
|
4892
|
+
const env = { ...process.env };
|
|
4893
|
+
for (const key of Object.keys(env)) {
|
|
4894
|
+
if (key.startsWith("OPENAI_")) {
|
|
4895
|
+
delete env[key];
|
|
4896
|
+
}
|
|
4897
|
+
}
|
|
4898
|
+
return { ...env, ...overrides };
|
|
4899
|
+
}
|
|
4900
|
+
|
|
4207
4901
|
// ../../packages/shared/src/core/logger.ts
|
|
4208
4902
|
var isTestEnv = process.env.NODE_ENV === "test" || process.env.VITEST === "true" || process.env.VITEST === "1";
|
|
4209
4903
|
function isMocked(fn) {
|
|
@@ -5046,20 +5740,19 @@ async function fetchRateLimitsViaRpc(envOverride, options = {}) {
|
|
|
5046
5740
|
});
|
|
5047
5741
|
return null;
|
|
5048
5742
|
}
|
|
5049
|
-
const childEnv = {
|
|
5050
|
-
...process.env,
|
|
5743
|
+
const childEnv = buildCodexChildEnv({
|
|
5051
5744
|
...envOverride ?? {},
|
|
5052
5745
|
HOME: tempHome,
|
|
5053
5746
|
CODEX_HOME: tempHome,
|
|
5054
5747
|
CODEX_TELEMETRY_LABEL: "codex-rpc",
|
|
5055
5748
|
ELECTRON_RUN_AS_NODE: "1"
|
|
5056
|
-
};
|
|
5749
|
+
});
|
|
5057
5750
|
const command = buildCodexCommand(
|
|
5058
5751
|
binaryPath,
|
|
5059
5752
|
["-s", "read-only", "-a", "untrusted", "app-server"],
|
|
5060
5753
|
childEnv
|
|
5061
5754
|
);
|
|
5062
|
-
const child = (0,
|
|
5755
|
+
const child = (0, import_node_child_process4.spawn)(command.command, command.args, {
|
|
5063
5756
|
stdio: ["pipe", "pipe", "pipe"],
|
|
5064
5757
|
env: childEnv,
|
|
5065
5758
|
shell: command.shell
|
|
@@ -5156,20 +5849,19 @@ async function activateResetWindowViaRpc(envOverride, options = {}) {
|
|
|
5156
5849
|
});
|
|
5157
5850
|
return { status: "skipped", reason: "auth-missing" };
|
|
5158
5851
|
}
|
|
5159
|
-
const childEnv = {
|
|
5160
|
-
...process.env,
|
|
5852
|
+
const childEnv = buildCodexChildEnv({
|
|
5161
5853
|
...envOverride ?? {},
|
|
5162
5854
|
HOME: tempHome,
|
|
5163
5855
|
CODEX_HOME: tempHome,
|
|
5164
5856
|
CODEX_TELEMETRY_LABEL: "codex-reset-activation",
|
|
5165
5857
|
ELECTRON_RUN_AS_NODE: "1"
|
|
5166
|
-
};
|
|
5858
|
+
});
|
|
5167
5859
|
const command = buildCodexCommand(
|
|
5168
5860
|
binaryPath,
|
|
5169
5861
|
["-s", "read-only", "-a", "untrusted", "app-server"],
|
|
5170
5862
|
childEnv
|
|
5171
5863
|
);
|
|
5172
|
-
const child = (0,
|
|
5864
|
+
const child = (0, import_node_child_process4.spawn)(command.command, command.args, {
|
|
5173
5865
|
stdio: ["pipe", "pipe", "pipe"],
|
|
5174
5866
|
env: childEnv,
|
|
5175
5867
|
shell: command.shell
|
|
@@ -5486,6 +6178,7 @@ var ProfileManager = class {
|
|
|
5486
6178
|
displayName: record.displayName ?? null,
|
|
5487
6179
|
data: record.data,
|
|
5488
6180
|
metadata: record.metadata,
|
|
6181
|
+
rateLimitKey: record.rateLimitKey ?? null,
|
|
5489
6182
|
accountId: record.accountId ?? null,
|
|
5490
6183
|
workspaceId: record.workspaceId ?? null,
|
|
5491
6184
|
workspaceName: record.workspaceName ?? null,
|
|
@@ -5503,11 +6196,14 @@ var ProfileManager = class {
|
|
|
5503
6196
|
if (!dataRaw || typeof dataRaw !== "object") {
|
|
5504
6197
|
return null;
|
|
5505
6198
|
}
|
|
5506
|
-
|
|
6199
|
+
const data = cloneJson(dataRaw);
|
|
6200
|
+
const metadata = raw.metadata ? cloneJson(raw.metadata) : void 0;
|
|
6201
|
+
const fallbackRecord = {
|
|
5507
6202
|
name: profileName,
|
|
5508
6203
|
displayName: typeof raw.displayName === "string" ? raw.displayName : null,
|
|
5509
|
-
data
|
|
5510
|
-
metadata
|
|
6204
|
+
data,
|
|
6205
|
+
metadata,
|
|
6206
|
+
rateLimitKey: typeof raw.rateLimitKey === "string" ? raw.rateLimitKey : null,
|
|
5511
6207
|
accountId: typeof raw.accountId === "string" ? raw.accountId : null,
|
|
5512
6208
|
workspaceId: typeof raw.workspaceId === "string" ? raw.workspaceId : null,
|
|
5513
6209
|
workspaceName: typeof raw.workspaceName === "string" ? raw.workspaceName : null,
|
|
@@ -5516,6 +6212,10 @@ var ProfileManager = class {
|
|
|
5516
6212
|
createdAt: typeof raw.createdAt === "string" ? raw.createdAt : null,
|
|
5517
6213
|
updatedAt: typeof raw.updatedAt === "string" ? raw.updatedAt : null
|
|
5518
6214
|
};
|
|
6215
|
+
return {
|
|
6216
|
+
...fallbackRecord,
|
|
6217
|
+
rateLimitKey: fallbackRecord.rateLimitKey ?? this.resolveProfileIdentityFromRecord(fallbackRecord)
|
|
6218
|
+
};
|
|
5519
6219
|
}
|
|
5520
6220
|
async readProfilesStateSource() {
|
|
5521
6221
|
const state = await getAppState();
|
|
@@ -5565,10 +6265,11 @@ var ProfileManager = class {
|
|
|
5565
6265
|
}
|
|
5566
6266
|
rateLimitSnapshotKeysForRecord(record) {
|
|
5567
6267
|
return Array.from(
|
|
5568
|
-
|
|
6268
|
+
new Set([
|
|
5569
6269
|
this.resolveProfileIdentityFromRecord(record),
|
|
6270
|
+
record.rateLimitKey,
|
|
5570
6271
|
resolveFallbackAccountKey(record.name)
|
|
5571
|
-
])
|
|
6272
|
+
].filter((key) => Boolean(key)))
|
|
5572
6273
|
);
|
|
5573
6274
|
}
|
|
5574
6275
|
rateLimitSnapshotKeysForStateMap(map) {
|
|
@@ -5633,7 +6334,7 @@ var ProfileManager = class {
|
|
|
5633
6334
|
if (!trimmed) {
|
|
5634
6335
|
return this.emptyAuthSnapshot();
|
|
5635
6336
|
}
|
|
5636
|
-
const fingerprint = (0,
|
|
6337
|
+
const fingerprint = (0, import_node_crypto6.createHash)("sha256").update(trimmed).digest("hex");
|
|
5637
6338
|
try {
|
|
5638
6339
|
const parsed = JSON.parse(trimmed);
|
|
5639
6340
|
const normalized = this.normalizeProfileData(parsed);
|
|
@@ -6281,6 +6982,7 @@ var ProfileManager = class {
|
|
|
6281
6982
|
displayName,
|
|
6282
6983
|
data,
|
|
6283
6984
|
metadata: resolvedMetadata,
|
|
6985
|
+
rateLimitKey: existingRecord?.rateLimitKey ?? this.resolveProfileIdentityFromData(resolvedName, data, resolvedMetadata),
|
|
6284
6986
|
accountId,
|
|
6285
6987
|
workspaceId: data.workspace_id ?? workspaceId,
|
|
6286
6988
|
workspaceName: data.workspace_name ?? workspaceName,
|
|
@@ -6299,6 +7001,7 @@ var ProfileManager = class {
|
|
|
6299
7001
|
createdAt: row.createdAt ?? void 0,
|
|
6300
7002
|
authMethod: row.authMethod ?? void 0,
|
|
6301
7003
|
accountId: row.accountId ?? void 0,
|
|
7004
|
+
rateLimitKey: row.rateLimitKey ?? void 0,
|
|
6302
7005
|
workspaceId: row.workspaceId ?? void 0,
|
|
6303
7006
|
workspaceName: row.workspaceName ?? void 0,
|
|
6304
7007
|
metadata: row.metadata
|
|
@@ -6335,6 +7038,7 @@ var ProfileManager = class {
|
|
|
6335
7038
|
name,
|
|
6336
7039
|
displayName: fallback?.displayName ?? name,
|
|
6337
7040
|
isValid: Boolean(data && Object.keys(data).length > 0),
|
|
7041
|
+
rateLimitKey: fallback?.rateLimitKey,
|
|
6338
7042
|
accountId,
|
|
6339
7043
|
workspaceId,
|
|
6340
7044
|
workspaceName,
|
|
@@ -6726,11 +7430,10 @@ var ProfileManager = class {
|
|
|
6726
7430
|
const codexFormat = this.convertToCodexFormat(profileData);
|
|
6727
7431
|
const profileHome = await this.prepareProfileHome(profileName, codexFormat);
|
|
6728
7432
|
const authPath = import_path.default.join(profileHome, "auth.json");
|
|
6729
|
-
const envOverrides = {
|
|
6730
|
-
...process.env,
|
|
7433
|
+
const envOverrides = buildCodexChildEnv({
|
|
6731
7434
|
HOME: profileHome,
|
|
6732
7435
|
CODEX_HOME: profileHome
|
|
6733
|
-
};
|
|
7436
|
+
});
|
|
6734
7437
|
try {
|
|
6735
7438
|
return await action(envOverrides);
|
|
6736
7439
|
} finally {
|
|
@@ -6780,11 +7483,10 @@ var ProfileManager = class {
|
|
|
6780
7483
|
const profileHome = await this.prepareProfileHome(profileName, codexFormat);
|
|
6781
7484
|
return {
|
|
6782
7485
|
profileHome,
|
|
6783
|
-
env: {
|
|
6784
|
-
...process.env,
|
|
7486
|
+
env: buildCodexChildEnv({
|
|
6785
7487
|
HOME: profileHome,
|
|
6786
7488
|
CODEX_HOME: profileHome
|
|
6787
|
-
}
|
|
7489
|
+
})
|
|
6788
7490
|
};
|
|
6789
7491
|
})
|
|
6790
7492
|
);
|
|
@@ -7029,13 +7731,13 @@ var ProfileManager = class {
|
|
|
7029
7731
|
profileName,
|
|
7030
7732
|
async (env) => {
|
|
7031
7733
|
try {
|
|
7032
|
-
const
|
|
7033
|
-
if (
|
|
7034
|
-
return
|
|
7734
|
+
const rpcSnapshot = await fetchRateLimitsViaRpc(env, { codexPath: options.codexPath });
|
|
7735
|
+
if (rpcSnapshot) {
|
|
7736
|
+
return rpcSnapshot;
|
|
7035
7737
|
}
|
|
7036
7738
|
} catch {
|
|
7037
7739
|
}
|
|
7038
|
-
return
|
|
7740
|
+
return fetchRateLimitsViaOAuth(env);
|
|
7039
7741
|
},
|
|
7040
7742
|
{ syncFromActiveAuthBeforeAction: false }
|
|
7041
7743
|
)
|
|
@@ -7285,7 +7987,7 @@ var ProfileManager = class {
|
|
|
7285
7987
|
}
|
|
7286
7988
|
if (options.canAutoImport === false) {
|
|
7287
7989
|
return this.buildActiveCodexAuthStatus("unmanaged", description, {
|
|
7288
|
-
importBlockedReason: options.importBlockedReason ?? "
|
|
7990
|
+
importBlockedReason: options.importBlockedReason ?? "Your CodexUse trial has ended. Upgrade to Pro before importing this Codex login."
|
|
7289
7991
|
});
|
|
7290
7992
|
}
|
|
7291
7993
|
const records = await this.listProfileRecords();
|
|
@@ -7380,6 +8082,7 @@ var ProfileManager = class {
|
|
|
7380
8082
|
displayName: typeof candidate.displayName === "string" ? candidate.displayName : null,
|
|
7381
8083
|
data,
|
|
7382
8084
|
metadata,
|
|
8085
|
+
rateLimitKey: this.resolveProfileIdentityFromData(name, data, metadata),
|
|
7383
8086
|
accountId: typeof candidate.accountId === "string" ? candidate.accountId : null,
|
|
7384
8087
|
workspaceId: typeof candidate.workspaceId === "string" ? candidate.workspaceId : null,
|
|
7385
8088
|
workspaceName: typeof candidate.workspaceName === "string" ? candidate.workspaceName : null,
|
|
@@ -7473,7 +8176,7 @@ Flags:
|
|
|
7473
8176
|
--runtime=NAME Accounts Pool runtime store: desktop
|
|
7474
8177
|
--state-dir=PATH Override the runtime state dir for Accounts Pool inspection
|
|
7475
8178
|
--passphrase-stdin Read Cloud Sync passphrase from stdin
|
|
7476
|
-
Note: profile autoroll requires Pro.
|
|
8179
|
+
Note: profile autoroll requires Pro or an active trial.
|
|
7477
8180
|
Cloud Sync: set CODEXUSE_SYNC_PASSPHRASE or use --passphrase-stdin unless the passphrase is saved in Keychain.
|
|
7478
8181
|
`);
|
|
7479
8182
|
}
|
|
@@ -7525,7 +8228,7 @@ function parseStringFlag(flags, name) {
|
|
|
7525
8228
|
|
|
7526
8229
|
// src/commands/accountPool.ts
|
|
7527
8230
|
var ACCOUNT_POOL_DOCUMENT = "desktop.account-pool.v2";
|
|
7528
|
-
var ACCOUNT_POOL_PRO_REQUIRED_MESSAGE = "Accounts Pool requires a Pro license.";
|
|
8231
|
+
var ACCOUNT_POOL_PRO_REQUIRED_MESSAGE = "Accounts Pool requires a Pro license or an active trial.";
|
|
7529
8232
|
var DESKTOP_RPC_STATE_DIR = "desktop-rpc";
|
|
7530
8233
|
var ACCOUNT_POOL_STORE_VERSION = 3;
|
|
7531
8234
|
var MAX_RESPONSE_IDS_PER_SESSION = 64;
|
|
@@ -7913,13 +8616,13 @@ async function listApiKeys(stateDir) {
|
|
|
7913
8616
|
return Object.values(store.apiKeysById).filter((key) => key.revokedAt === null).sort((left, right) => right.createdAt.localeCompare(left.createdAt)).map(accountPoolApiKeyView);
|
|
7914
8617
|
}
|
|
7915
8618
|
function hashToken(token) {
|
|
7916
|
-
return (0,
|
|
8619
|
+
return (0, import_node_crypto7.createHash)("sha256").update(token).digest("hex");
|
|
7917
8620
|
}
|
|
7918
8621
|
async function createApiKey(stateDir) {
|
|
7919
|
-
const plaintextToken = `cux_pool_${(0,
|
|
8622
|
+
const plaintextToken = `cux_pool_${(0, import_node_crypto7.randomBytes)(24).toString("hex")}`;
|
|
7920
8623
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
7921
8624
|
const keyRecord = {
|
|
7922
|
-
id: (0,
|
|
8625
|
+
id: (0, import_node_crypto7.randomUUID)(),
|
|
7923
8626
|
tokenHash: hashToken(plaintextToken),
|
|
7924
8627
|
tokenPreview: `${plaintextToken.slice(0, 8)}...${plaintextToken.slice(-6)}`,
|
|
7925
8628
|
createdAt: now,
|
|
@@ -8013,7 +8716,7 @@ async function readAccountPoolRuntimeSummary(input = {}) {
|
|
|
8013
8716
|
stateDir,
|
|
8014
8717
|
baseUrl: resolveBaseUrlFromPort(runtime, input.port ?? null),
|
|
8015
8718
|
enabled: normalized.accountPoolEnabled,
|
|
8016
|
-
|
|
8719
|
+
hasAccountPoolAccess: license?.capabilities.accountPool === true,
|
|
8017
8720
|
routingStrategy: normalized.accountPoolRoutingStrategy,
|
|
8018
8721
|
activeApiKeyCount: apiKeys.length,
|
|
8019
8722
|
activeSessionCount: sessions.length
|
|
@@ -8030,7 +8733,7 @@ async function updateSettings(transform) {
|
|
|
8030
8733
|
const { raw, normalized } = await readSettingsSnapshot();
|
|
8031
8734
|
const nextSettings = transform(normalized);
|
|
8032
8735
|
const license = await licenseService.getStatus().catch(() => null);
|
|
8033
|
-
const forcedDisabledByLicense =
|
|
8736
|
+
const forcedDisabledByLicense = license?.capabilities.accountPool !== true && nextSettings.accountPoolEnabled === true;
|
|
8034
8737
|
const sanitizedSettings = {
|
|
8035
8738
|
...nextSettings,
|
|
8036
8739
|
accountPoolEnabled: forcedDisabledByLicense ? false : nextSettings.accountPoolEnabled,
|
|
@@ -8057,7 +8760,7 @@ async function updateSettings(transform) {
|
|
|
8057
8760
|
}
|
|
8058
8761
|
async function requireProAccess() {
|
|
8059
8762
|
const license = await licenseService.getStatus();
|
|
8060
|
-
if (!license.
|
|
8763
|
+
if (!license.capabilities.accountPool) {
|
|
8061
8764
|
throw new Error(ACCOUNT_POOL_PRO_REQUIRED_MESSAGE);
|
|
8062
8765
|
}
|
|
8063
8766
|
}
|
|
@@ -8119,7 +8822,7 @@ async function handleStatus(flags) {
|
|
|
8119
8822
|
console.log(`Runtime: ${runtime}`);
|
|
8120
8823
|
console.log(`State dir: ${stateDir}`);
|
|
8121
8824
|
console.log(`Routing: ${formatRoutingStrategy(normalized.accountPoolRoutingStrategy)}`);
|
|
8122
|
-
console.log(`Public API ready: ${normalized.accountPoolEnabled && license?.
|
|
8825
|
+
console.log(`Public API ready: ${normalized.accountPoolEnabled && license?.capabilities.accountPool === true && apiKeys.length > 0 ? "yes" : "no"}`);
|
|
8123
8826
|
console.log(`API keys: ${apiKeys.length}`);
|
|
8124
8827
|
console.log(`Sessions: ${sessions.length}`);
|
|
8125
8828
|
console.log(`Selection mode: ${explicitSelection.length > 0 ? "explicit" : "all saved profiles"}`);
|
|
@@ -8135,7 +8838,7 @@ async function handleStatus(flags) {
|
|
|
8135
8838
|
printStringList("Published models", normalized.accountPoolExposedModels);
|
|
8136
8839
|
printStringList("Reasoning aliases", normalized.accountPoolExposedReasoningEfforts);
|
|
8137
8840
|
printStringList("Fast-mode aliases", normalized.accountPoolExposedFastModeReasoningEfforts);
|
|
8138
|
-
if (normalized.accountPoolEnabled && license?.
|
|
8841
|
+
if (normalized.accountPoolEnabled && license?.capabilities.accountPool !== true) {
|
|
8139
8842
|
console.log(ACCOUNT_POOL_PRO_REQUIRED_MESSAGE);
|
|
8140
8843
|
}
|
|
8141
8844
|
}
|
|
@@ -8340,7 +9043,7 @@ async function handleQuickstart(flags) {
|
|
|
8340
9043
|
console.log("Accounts Pool quickstart");
|
|
8341
9044
|
console.log(`Runtime: ${summary.runtime}`);
|
|
8342
9045
|
console.log(`Enabled: ${summary.enabled ? "yes" : "no"}`);
|
|
8343
|
-
console.log(`
|
|
9046
|
+
console.log(`Access: ${summary.hasAccountPoolAccess ? "yes" : "no"}`);
|
|
8344
9047
|
console.log(`API keys: ${summary.activeApiKeyCount}`);
|
|
8345
9048
|
console.log(`Base URL: ${baseUrl}`);
|
|
8346
9049
|
console.log("");
|
|
@@ -8424,7 +9127,7 @@ async function handleAccountPoolCommand(args, version) {
|
|
|
8424
9127
|
}
|
|
8425
9128
|
|
|
8426
9129
|
// src/commands/codex.ts
|
|
8427
|
-
var
|
|
9130
|
+
var import_node_child_process6 = require("child_process");
|
|
8428
9131
|
|
|
8429
9132
|
// ../../packages/runtime-profiles/src/profiles/rate-limit-notifier.ts
|
|
8430
9133
|
function maxUsedPercent(snapshot) {
|
|
@@ -8441,8 +9144,108 @@ function maxUsedPercent(snapshot) {
|
|
|
8441
9144
|
return Math.max(...candidates);
|
|
8442
9145
|
}
|
|
8443
9146
|
|
|
9147
|
+
// ../../packages/runtime-profiles/src/profiles/profile-visibility.ts
|
|
9148
|
+
function resolveLicenseVisibleProfiles(args) {
|
|
9149
|
+
const { profiles, license, currentProfile } = args;
|
|
9150
|
+
const licenseWithCounts = licenseService.applyProfileCount(license, profiles.length);
|
|
9151
|
+
const enforceProfileLimit = shouldEnforceProfileLimit(licenseWithCounts);
|
|
9152
|
+
const visibleProfiles = enforceProfileLimit ? resolveVisibleProfiles(profiles, licenseWithCounts, currentProfile) : profiles;
|
|
9153
|
+
return {
|
|
9154
|
+
allProfiles: profiles,
|
|
9155
|
+
profiles: licenseWithCounts.capabilities.unlimitedProfiles ? profiles : visibleProfiles,
|
|
9156
|
+
license,
|
|
9157
|
+
licenseWithCounts,
|
|
9158
|
+
enforceProfileLimit
|
|
9159
|
+
};
|
|
9160
|
+
}
|
|
9161
|
+
async function loadLicenseVisibleProfiles(profileManager, options = {}) {
|
|
9162
|
+
await profileManager.initialize();
|
|
9163
|
+
const license = options.freshLicense ? await licenseService.getStatus() : await licenseService.getCachedStatus();
|
|
9164
|
+
const profiles = await profileManager.listProfiles();
|
|
9165
|
+
const currentProfile = options.currentProfile !== void 0 ? options.currentProfile : (await profileManager.getCurrentProfile()).name;
|
|
9166
|
+
return resolveLicenseVisibleProfiles({
|
|
9167
|
+
profiles,
|
|
9168
|
+
license,
|
|
9169
|
+
currentProfile
|
|
9170
|
+
});
|
|
9171
|
+
}
|
|
9172
|
+
function resolveVisibleProfiles(profiles, license, currentProfile) {
|
|
9173
|
+
if (license.capabilities.unlimitedProfiles) {
|
|
9174
|
+
return profiles;
|
|
9175
|
+
}
|
|
9176
|
+
const limit = typeof license.profileLimit === "number" && license.profileLimit >= 0 ? license.profileLimit : FREE_PROFILE_LIMIT;
|
|
9177
|
+
if (profiles.length <= limit) {
|
|
9178
|
+
return profiles;
|
|
9179
|
+
}
|
|
9180
|
+
const normalizedCurrent = currentProfile ?? null;
|
|
9181
|
+
const sorted = profiles.slice().sort((a, b) => compareProfilesForLimit(a, b, normalizedCurrent));
|
|
9182
|
+
return sorted.slice(0, limit);
|
|
9183
|
+
}
|
|
9184
|
+
function compareProfilesForLimit(a, b, currentProfile) {
|
|
9185
|
+
if (currentProfile) {
|
|
9186
|
+
if (a.name === currentProfile && b.name !== currentProfile) {
|
|
9187
|
+
return -1;
|
|
9188
|
+
}
|
|
9189
|
+
if (b.name === currentProfile && a.name !== currentProfile) {
|
|
9190
|
+
return 1;
|
|
9191
|
+
}
|
|
9192
|
+
}
|
|
9193
|
+
if (Boolean(a.isValid) !== Boolean(b.isValid)) {
|
|
9194
|
+
return a.isValid ? -1 : 1;
|
|
9195
|
+
}
|
|
9196
|
+
const aTimestamp = parseTimestamp3(a.createdAt ?? null);
|
|
9197
|
+
const bTimestamp = parseTimestamp3(b.createdAt ?? null);
|
|
9198
|
+
if (aTimestamp !== null && bTimestamp !== null) {
|
|
9199
|
+
if (aTimestamp === bTimestamp) {
|
|
9200
|
+
return a.name.localeCompare(b.name);
|
|
9201
|
+
}
|
|
9202
|
+
return bTimestamp - aTimestamp;
|
|
9203
|
+
}
|
|
9204
|
+
if (aTimestamp !== null) {
|
|
9205
|
+
return -1;
|
|
9206
|
+
}
|
|
9207
|
+
if (bTimestamp !== null) {
|
|
9208
|
+
return 1;
|
|
9209
|
+
}
|
|
9210
|
+
return a.name.localeCompare(b.name);
|
|
9211
|
+
}
|
|
9212
|
+
function parseTimestamp3(value) {
|
|
9213
|
+
if (typeof value !== "string" || value.trim() === "") {
|
|
9214
|
+
return null;
|
|
9215
|
+
}
|
|
9216
|
+
const parsed = Date.parse(value);
|
|
9217
|
+
return Number.isNaN(parsed) ? null : parsed;
|
|
9218
|
+
}
|
|
9219
|
+
|
|
9220
|
+
// ../../packages/runtime-profiles/src/license/guard.ts
|
|
9221
|
+
var PROFILE_ACCESS_DENIED_MESSAGE = "Your CodexUse trial has ended. Upgrade to Pro to use saved profiles.";
|
|
9222
|
+
async function assertProfileCreationAllowed(profileManager) {
|
|
9223
|
+
const manager = profileManager ?? new ProfileManager();
|
|
9224
|
+
await manager.initialize();
|
|
9225
|
+
const license = await licenseService.getStatus();
|
|
9226
|
+
const profiles = await manager.listProfiles();
|
|
9227
|
+
const licenseWithCounts = licenseService.applyProfileCount(license, profiles.length);
|
|
9228
|
+
if (typeof licenseWithCounts.profileLimit === "number" && licenseWithCounts.profilesRemaining !== null && licenseWithCounts.profilesRemaining <= 0) {
|
|
9229
|
+
throw new Error("Your CodexUse trial has ended. Upgrade to Pro for unlimited profiles.");
|
|
9230
|
+
}
|
|
9231
|
+
}
|
|
9232
|
+
async function assertProfileAccessAllowed(profileManager, profileName) {
|
|
9233
|
+
const name = profileName.trim();
|
|
9234
|
+
if (!name) {
|
|
9235
|
+
throw new Error("Profile name is required.");
|
|
9236
|
+
}
|
|
9237
|
+
const visible = await loadLicenseVisibleProfiles(profileManager, {
|
|
9238
|
+
freshLicense: true
|
|
9239
|
+
});
|
|
9240
|
+
const exists = visible.allProfiles.some((profile) => profile.name === name);
|
|
9241
|
+
const allowed = visible.profiles.some((profile) => profile.name === name);
|
|
9242
|
+
if (exists && !allowed) {
|
|
9243
|
+
throw new Error(PROFILE_ACCESS_DENIED_MESSAGE);
|
|
9244
|
+
}
|
|
9245
|
+
}
|
|
9246
|
+
|
|
8444
9247
|
// src/platform/codexCli.ts
|
|
8445
|
-
var
|
|
9248
|
+
var import_node_child_process5 = require("child_process");
|
|
8446
9249
|
var import_node_fs5 = require("fs");
|
|
8447
9250
|
var import_node_path8 = __toESM(require("path"));
|
|
8448
9251
|
var ENV_HINTS2 = ["CODEX_BINARY", "CODEX_CLI_PATH", "CODEX_PATH"];
|
|
@@ -8514,7 +9317,7 @@ async function runCodexLogin(mode) {
|
|
|
8514
9317
|
const resolvedMode = resolveLoginMode(mode ?? null);
|
|
8515
9318
|
const loginArgs = resolvedMode === "device" ? ["login", "--device-auth"] : ["login"];
|
|
8516
9319
|
const { command, args, shell } = buildCodexCommand2(codexPath, loginArgs);
|
|
8517
|
-
const child = (0,
|
|
9320
|
+
const child = (0, import_node_child_process5.spawn)(command, args, {
|
|
8518
9321
|
stdio: "inherit",
|
|
8519
9322
|
env: process.env,
|
|
8520
9323
|
shell
|
|
@@ -8586,7 +9389,7 @@ function compareCandidates(left, right) {
|
|
|
8586
9389
|
async function runCommand(codexPath, args, timeoutMs) {
|
|
8587
9390
|
const command = buildCodexCommand2(codexPath, args);
|
|
8588
9391
|
return new Promise((resolve) => {
|
|
8589
|
-
const child = (0,
|
|
9392
|
+
const child = (0, import_node_child_process6.spawn)(command.command, command.args, {
|
|
8590
9393
|
env: process.env,
|
|
8591
9394
|
shell: command.shell,
|
|
8592
9395
|
stdio: ["ignore", "pipe", "pipe"]
|
|
@@ -8647,12 +9450,13 @@ async function runCodexWithProfile(manager, profileName, codexArgs) {
|
|
|
8647
9450
|
if (!codexPath) {
|
|
8648
9451
|
throw new Error("Codex CLI not found. Install Codex CLI or set CODEX_BINARY.");
|
|
8649
9452
|
}
|
|
9453
|
+
await assertProfileAccessAllowed(manager, profileName);
|
|
8650
9454
|
const runtime = await manager.prepareProfileRuntime(profileName, {
|
|
8651
9455
|
syncFromActiveAuthBeforeAction: false
|
|
8652
9456
|
});
|
|
8653
9457
|
const command = buildCodexCommand2(codexPath, codexArgs);
|
|
8654
9458
|
try {
|
|
8655
|
-
const child = (0,
|
|
9459
|
+
const child = (0, import_node_child_process6.spawn)(command.command, command.args, {
|
|
8656
9460
|
env: runtime.env,
|
|
8657
9461
|
shell: command.shell,
|
|
8658
9462
|
stdio: "inherit"
|
|
@@ -8753,7 +9557,7 @@ async function handleBestCommand(args) {
|
|
|
8753
9557
|
console.log(`Usage:
|
|
8754
9558
|
codexuse best -- <codex args>
|
|
8755
9559
|
|
|
8756
|
-
Picks the healthiest saved profile, then runs Codex. Auto-run requires Pro.`);
|
|
9560
|
+
Picks the healthiest saved profile, then runs Codex. Auto-run requires Pro or an active trial.`);
|
|
8757
9561
|
return;
|
|
8758
9562
|
}
|
|
8759
9563
|
const codexPath = resolveCodexBinary();
|
|
@@ -8761,20 +9565,17 @@ Picks the healthiest saved profile, then runs Codex. Auto-run requires Pro.`);
|
|
|
8761
9565
|
throw new Error("Codex CLI not found. Install Codex CLI or set CODEX_BINARY.");
|
|
8762
9566
|
}
|
|
8763
9567
|
const manager = new ProfileManager();
|
|
8764
|
-
const
|
|
9568
|
+
const visible = await loadLicenseVisibleProfiles(manager, { freshLicense: true });
|
|
9569
|
+
if (!visible.licenseWithCounts.capabilities.autoRoll) {
|
|
9570
|
+
throw new Error("Auto-run requires Pro or an active trial.");
|
|
9571
|
+
}
|
|
9572
|
+
const profiles = visible.profiles;
|
|
8765
9573
|
const best = await selectBestProfile(manager, profiles, codexPath);
|
|
8766
9574
|
if (!best) {
|
|
8767
9575
|
throw new Error("No valid saved profiles found. Add one with `codexuse profile add <name>`.");
|
|
8768
9576
|
}
|
|
8769
9577
|
const dryRun = hasFlag(flags, "--dry-run");
|
|
8770
|
-
const license = await licenseService.getStatus();
|
|
8771
9578
|
const summary = `${best.profile.name} (${formatUsagePercent(best.usedPercent)} used)`;
|
|
8772
|
-
if (!license.isPro) {
|
|
8773
|
-
console.log(`Best profile: ${summary}`);
|
|
8774
|
-
console.log("Auto-run is Pro. Free path:");
|
|
8775
|
-
console.log(` codexuse run --profile ${best.profile.name} -- ${codexArgs.join(" ")}`.trimEnd());
|
|
8776
|
-
return;
|
|
8777
|
-
}
|
|
8778
9579
|
if (dryRun) {
|
|
8779
9580
|
console.log(`Would run with profile: ${summary}`);
|
|
8780
9581
|
return;
|
|
@@ -8832,21 +9633,9 @@ async function handleLicense(args, version) {
|
|
|
8832
9633
|
}
|
|
8833
9634
|
}
|
|
8834
9635
|
|
|
8835
|
-
// ../../packages/runtime-profiles/src/license/guard.ts
|
|
8836
|
-
async function assertProfileCreationAllowed(profileManager) {
|
|
8837
|
-
const manager = profileManager ?? new ProfileManager();
|
|
8838
|
-
await manager.initialize();
|
|
8839
|
-
const license = await licenseService.getStatus();
|
|
8840
|
-
const profiles = await manager.listProfiles();
|
|
8841
|
-
const licenseWithCounts = licenseService.applyProfileCount(license, profiles.length);
|
|
8842
|
-
if (typeof licenseWithCounts.profileLimit === "number" && licenseWithCounts.profilesRemaining !== null && licenseWithCounts.profilesRemaining <= 0) {
|
|
8843
|
-
throw new Error("CodexUse Free supports up to 2 profiles. Upgrade to CodexUse Pro for unlimited profiles.");
|
|
8844
|
-
}
|
|
8845
|
-
}
|
|
8846
|
-
|
|
8847
9636
|
// ../../packages/runtime-codex/src/codex/officialAppRestart.ts
|
|
8848
|
-
var
|
|
8849
|
-
var
|
|
9637
|
+
var import_node_child_process7 = require("child_process");
|
|
9638
|
+
var import_node_crypto8 = require("crypto");
|
|
8850
9639
|
var import_node_fs7 = require("fs");
|
|
8851
9640
|
var import_node_os3 = require("os");
|
|
8852
9641
|
var import_node_net = __toESM(require("net"), 1);
|
|
@@ -8856,6 +9645,7 @@ var import_node_path10 = __toESM(require("path"), 1);
|
|
|
8856
9645
|
var import_node_fs6 = require("fs");
|
|
8857
9646
|
var import_node_path9 = __toESM(require("path"), 1);
|
|
8858
9647
|
var ROLLOUT_DIRS = ["sessions", "archived_sessions"];
|
|
9648
|
+
var SQLITE_EXTENSIONS = /* @__PURE__ */ new Set([".db", ".sqlite", ".sqlite3"]);
|
|
8859
9649
|
function isRecord5(value) {
|
|
8860
9650
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
8861
9651
|
}
|
|
@@ -8974,8 +9764,91 @@ function resolveRolloutPath(profileHome, rolloutPath) {
|
|
|
8974
9764
|
}
|
|
8975
9765
|
return import_node_path9.default.isAbsolute(rolloutPath) ? rolloutPath : import_node_path9.default.join(profileHome, rolloutPath);
|
|
8976
9766
|
}
|
|
8977
|
-
async function
|
|
8978
|
-
const
|
|
9767
|
+
async function collectSessionDatabasePaths(profileHome) {
|
|
9768
|
+
const paths = [];
|
|
9769
|
+
const sqliteDir = import_node_path9.default.join(profileHome, "sqlite");
|
|
9770
|
+
try {
|
|
9771
|
+
const entries = await import_node_fs6.promises.readdir(sqliteDir, {
|
|
9772
|
+
withFileTypes: true,
|
|
9773
|
+
encoding: "utf8"
|
|
9774
|
+
});
|
|
9775
|
+
for (const entry of entries) {
|
|
9776
|
+
if (!entry.isFile()) {
|
|
9777
|
+
continue;
|
|
9778
|
+
}
|
|
9779
|
+
const entryName = String(entry.name);
|
|
9780
|
+
if (SQLITE_EXTENSIONS.has(import_node_path9.default.extname(entryName))) {
|
|
9781
|
+
paths.push(import_node_path9.default.join(sqliteDir, entryName));
|
|
9782
|
+
}
|
|
9783
|
+
}
|
|
9784
|
+
} catch {
|
|
9785
|
+
}
|
|
9786
|
+
paths.sort((left, right) => {
|
|
9787
|
+
const leftName = import_node_path9.default.basename(left);
|
|
9788
|
+
const rightName = import_node_path9.default.basename(right);
|
|
9789
|
+
if (leftName === "codex-dev.db" && rightName !== "codex-dev.db") return -1;
|
|
9790
|
+
if (rightName === "codex-dev.db" && leftName !== "codex-dev.db") return 1;
|
|
9791
|
+
return leftName.localeCompare(rightName);
|
|
9792
|
+
});
|
|
9793
|
+
const legacy = import_node_path9.default.join(profileHome, "state_5.sqlite");
|
|
9794
|
+
if (!paths.includes(legacy)) {
|
|
9795
|
+
paths.push(legacy);
|
|
9796
|
+
}
|
|
9797
|
+
return paths;
|
|
9798
|
+
}
|
|
9799
|
+
function tableColumns(db, table) {
|
|
9800
|
+
try {
|
|
9801
|
+
return new Set(
|
|
9802
|
+
db.prepare(`PRAGMA table_info(${table})`).all().map((row) => isRecord5(row) ? recordString(row.name) : null).filter((name) => Boolean(name))
|
|
9803
|
+
);
|
|
9804
|
+
} catch {
|
|
9805
|
+
return /* @__PURE__ */ new Set();
|
|
9806
|
+
}
|
|
9807
|
+
}
|
|
9808
|
+
function firstColumn(columns, names) {
|
|
9809
|
+
return names.find((name) => columns.has(name)) ?? null;
|
|
9810
|
+
}
|
|
9811
|
+
function sqlColumnOrNull(column, alias) {
|
|
9812
|
+
return column ? `${column} AS ${alias}` : `NULL AS ${alias}`;
|
|
9813
|
+
}
|
|
9814
|
+
function queryThreadRecordFromTable(db, profileHome, threadId, config) {
|
|
9815
|
+
const columns = tableColumns(db, config.table);
|
|
9816
|
+
const idColumn = firstColumn(columns, config.id);
|
|
9817
|
+
if (!idColumn) {
|
|
9818
|
+
return null;
|
|
9819
|
+
}
|
|
9820
|
+
const variants = threadIdVariants(threadId);
|
|
9821
|
+
const placeholders = variants.map(() => "?").join(", ");
|
|
9822
|
+
const orderByColumn = firstColumn(columns, [
|
|
9823
|
+
"updated_at_ms",
|
|
9824
|
+
"updated_at",
|
|
9825
|
+
"created_at_ms",
|
|
9826
|
+
"created_at"
|
|
9827
|
+
]);
|
|
9828
|
+
const select = [
|
|
9829
|
+
sqlColumnOrNull(firstColumn(columns, config.rolloutPath), "rollout_path"),
|
|
9830
|
+
sqlColumnOrNull(firstColumn(columns, config.title), "title"),
|
|
9831
|
+
sqlColumnOrNull(firstColumn(columns, config.cwd), "cwd"),
|
|
9832
|
+
sqlColumnOrNull(firstColumn(columns, ["updated_at"]), "updated_at"),
|
|
9833
|
+
sqlColumnOrNull(firstColumn(columns, ["updated_at_ms"]), "updated_at_ms"),
|
|
9834
|
+
sqlColumnOrNull(firstColumn(columns, ["created_at_ms", "created_at"]), "created_at_ms")
|
|
9835
|
+
].join(", ");
|
|
9836
|
+
const orderBy = orderByColumn ? ` ORDER BY ${orderByColumn} DESC, ${idColumn} DESC` : ` ORDER BY ${idColumn} DESC`;
|
|
9837
|
+
const row = db.prepare(
|
|
9838
|
+
`SELECT ${select} FROM ${config.table} WHERE ${idColumn} IN (${placeholders})${orderBy} LIMIT 1`
|
|
9839
|
+
).all(...variants).find(isRecord5);
|
|
9840
|
+
if (!row) {
|
|
9841
|
+
return null;
|
|
9842
|
+
}
|
|
9843
|
+
return {
|
|
9844
|
+
rolloutPath: resolveRolloutPath(profileHome, recordString(row.rollout_path)),
|
|
9845
|
+
title: recordString(row.title),
|
|
9846
|
+
cwd: recordString(row.cwd),
|
|
9847
|
+
updatedAt: recordString(row.updated_at),
|
|
9848
|
+
updatedAtMs: normalizeTimestampMs(row.updated_at_ms) ?? normalizeTimestampMs(row.updated_at) ?? normalizeTimestampMs(row.created_at_ms)
|
|
9849
|
+
};
|
|
9850
|
+
}
|
|
9851
|
+
async function readThreadRecordFromDb(profileHome, dbPath, threadId) {
|
|
8979
9852
|
try {
|
|
8980
9853
|
await import_node_fs6.promises.access(dbPath);
|
|
8981
9854
|
} catch {
|
|
@@ -8984,42 +9857,28 @@ async function readThreadRecordFromStateDb(profileHome, threadId) {
|
|
|
8984
9857
|
let db = null;
|
|
8985
9858
|
try {
|
|
8986
9859
|
db = await openReadonlyDatabase(dbPath);
|
|
8987
|
-
const
|
|
8988
|
-
|
|
8989
|
-
|
|
8990
|
-
|
|
8991
|
-
|
|
8992
|
-
|
|
8993
|
-
|
|
8994
|
-
|
|
8995
|
-
|
|
8996
|
-
|
|
8997
|
-
|
|
8998
|
-
|
|
8999
|
-
|
|
9000
|
-
|
|
9001
|
-
|
|
9002
|
-
|
|
9003
|
-
|
|
9004
|
-
|
|
9005
|
-
|
|
9006
|
-
|
|
9007
|
-
`SELECT ${select} FROM threads WHERE ${idColumn} IN (${placeholders})${orderBy} LIMIT 1`
|
|
9008
|
-
).all(...variants);
|
|
9009
|
-
const row = rows.find(isRecord5);
|
|
9010
|
-
if (!row) {
|
|
9011
|
-
return null;
|
|
9860
|
+
for (const config of [
|
|
9861
|
+
{
|
|
9862
|
+
table: "threads",
|
|
9863
|
+
id: ["id", "thread_id"],
|
|
9864
|
+
title: ["title", "thread_title"],
|
|
9865
|
+
cwd: ["cwd", "source_cwd"],
|
|
9866
|
+
rolloutPath: ["rollout_path"]
|
|
9867
|
+
},
|
|
9868
|
+
{
|
|
9869
|
+
table: "automation_runs",
|
|
9870
|
+
id: ["thread_id", "id"],
|
|
9871
|
+
title: ["thread_title", "title", "inbox_title"],
|
|
9872
|
+
cwd: ["source_cwd", "cwd"],
|
|
9873
|
+
rolloutPath: []
|
|
9874
|
+
}
|
|
9875
|
+
]) {
|
|
9876
|
+
const record = queryThreadRecordFromTable(db, profileHome, threadId, config);
|
|
9877
|
+
if (record) {
|
|
9878
|
+
return record;
|
|
9879
|
+
}
|
|
9012
9880
|
}
|
|
9013
|
-
return
|
|
9014
|
-
rolloutPath: resolveRolloutPath(
|
|
9015
|
-
profileHome,
|
|
9016
|
-
recordString(row.rollout_path)
|
|
9017
|
-
),
|
|
9018
|
-
title: recordString(row.title),
|
|
9019
|
-
cwd: recordString(row.cwd),
|
|
9020
|
-
updatedAt: recordString(row.updated_at),
|
|
9021
|
-
updatedAtMs: normalizeTimestampMs(row.updated_at_ms) ?? normalizeTimestampMs(row.updated_at) ?? normalizeTimestampMs(row.created_at_ms)
|
|
9022
|
-
};
|
|
9881
|
+
return null;
|
|
9023
9882
|
} catch {
|
|
9024
9883
|
return null;
|
|
9025
9884
|
} finally {
|
|
@@ -9027,9 +9886,16 @@ async function readThreadRecordFromStateDb(profileHome, threadId) {
|
|
|
9027
9886
|
}
|
|
9028
9887
|
}
|
|
9029
9888
|
async function findOfficialCodexThreadRecord(profileHome, threadId) {
|
|
9030
|
-
const
|
|
9031
|
-
|
|
9032
|
-
|
|
9889
|
+
const records = [];
|
|
9890
|
+
for (const dbPath of await collectSessionDatabasePaths(profileHome)) {
|
|
9891
|
+
const stateRecord = await readThreadRecordFromDb(profileHome, dbPath, threadId);
|
|
9892
|
+
if (stateRecord) {
|
|
9893
|
+
records.push(stateRecord);
|
|
9894
|
+
}
|
|
9895
|
+
}
|
|
9896
|
+
if (records.length > 0) {
|
|
9897
|
+
records.sort((left, right) => (right.updatedAtMs ?? 0) - (left.updatedAtMs ?? 0));
|
|
9898
|
+
return records[0] ?? null;
|
|
9033
9899
|
}
|
|
9034
9900
|
const rolloutMatch = await findRolloutFileByScan(profileHome, threadId);
|
|
9035
9901
|
if (!rolloutMatch) {
|
|
@@ -9121,7 +9987,7 @@ function hashProfileKey(profileKey) {
|
|
|
9121
9987
|
if (!profileKey) {
|
|
9122
9988
|
return null;
|
|
9123
9989
|
}
|
|
9124
|
-
return (0,
|
|
9990
|
+
return (0, import_node_crypto8.createHash)("sha256").update(profileKey).digest("hex").slice(0, 12);
|
|
9125
9991
|
}
|
|
9126
9992
|
function sanitizeOfficialCodexOverlayActionTargetsForRenderer(targets) {
|
|
9127
9993
|
const sanitized = [];
|
|
@@ -9395,13 +10261,14 @@ async function readCdpTargets(port) {
|
|
|
9395
10261
|
}
|
|
9396
10262
|
throw lastError instanceof Error ? lastError : new Error("cdp-list-failed");
|
|
9397
10263
|
}
|
|
9398
|
-
function pickCodexCdpTarget(targets) {
|
|
10264
|
+
function pickCodexCdpTarget(targets, options = {}) {
|
|
9399
10265
|
const pages = targets.filter(
|
|
9400
10266
|
(target) => target.webSocketDebuggerUrl && (target.type === "page" || target.type === "webview" || !target.type)
|
|
9401
10267
|
);
|
|
9402
|
-
|
|
10268
|
+
const codexPage = pages.find(
|
|
9403
10269
|
(target) => `${target.title ?? ""} ${target.url ?? ""}`.toLowerCase().includes("codex")
|
|
9404
|
-
) ??
|
|
10270
|
+
) ?? null;
|
|
10271
|
+
return codexPage ?? (options.allowAnyPage === true ? pages[0] ?? null : null);
|
|
9405
10272
|
}
|
|
9406
10273
|
function buildCdpDevToolsUrl(port, target) {
|
|
9407
10274
|
const frontendUrl = target.devtoolsFrontendUrl?.trim();
|
|
@@ -9744,6 +10611,9 @@ function buildInCodexStatusOverlayCssRules() {
|
|
|
9744
10611
|
`${chip} .codexuse-handoff-main{display:inline-flex;align-items:center;padding:5px 8px 5px 10px;border-radius:999px;cursor:pointer;}`,
|
|
9745
10612
|
`${chip} .codexuse-handoff-hide{display:inline-flex;width:20px;height:20px;align-items:center;justify-content:center;margin-right:3px;border-radius:50%;color:var(--cu-muted);cursor:pointer;font-size:12px;line-height:1;}`,
|
|
9746
10613
|
`${chip} .codexuse-handoff-hide:hover{background:var(--cu-border);color:var(--cu-fg,#ececf1);}`,
|
|
10614
|
+
`body>[data-radix-popper-content-wrapper],body>[data-floating-ui-portal]{z-index:2147483004!important;}`,
|
|
10615
|
+
// Official Codex marks its titlebar draggable; injected controls must opt out.
|
|
10616
|
+
`${id},${id} *,${chip},${chip} *,${picker},${picker} *{-webkit-app-region:no-drag;}`,
|
|
9747
10617
|
// Hand-off account picker is a standalone body child (not inside the pill),
|
|
9748
10618
|
// so the pill-scoped popover rules don't reach it — give it its own palette
|
|
9749
10619
|
// + popover styling, mirroring the chip.
|
|
@@ -10125,6 +10995,7 @@ function createBridgeApplyQolExpression(settings) {
|
|
|
10125
10995
|
const overlayPosKey = "codexuse:overlay:pos";
|
|
10126
10996
|
const handoffHiddenKey = "codexuse:handoff:hidden";
|
|
10127
10997
|
const handoffPosKey = "codexuse:handoff:pos:v2";
|
|
10998
|
+
const defaultPosKey = "codexuse:overlay:top-default:v1";
|
|
10128
10999
|
const readOverlayStore = (key) => {
|
|
10129
11000
|
try {
|
|
10130
11001
|
return window.localStorage.getItem(key);
|
|
@@ -10219,8 +11090,8 @@ function createBridgeApplyQolExpression(settings) {
|
|
|
10219
11090
|
};
|
|
10220
11091
|
const placeInitialOverlayPosition = (pill) => {
|
|
10221
11092
|
const rect = pill.getBoundingClientRect();
|
|
10222
|
-
const x = Math.
|
|
10223
|
-
const y = Math.max(
|
|
11093
|
+
const x = Math.max(8, window.innerWidth - rect.width - 32);
|
|
11094
|
+
const y = Math.max(96, Math.min(132, Math.round(window.innerHeight * 0.12)));
|
|
10224
11095
|
placeOverlay(pill, x, y);
|
|
10225
11096
|
};
|
|
10226
11097
|
const rectsOverlap = (a, b, gap = 0) =>
|
|
@@ -10234,30 +11105,17 @@ function createBridgeApplyQolExpression(settings) {
|
|
|
10234
11105
|
right: pos.x + rect.width,
|
|
10235
11106
|
bottom: pos.y + rect.height
|
|
10236
11107
|
});
|
|
10237
|
-
const placeInitialHandoffPosition = (chip
|
|
11108
|
+
const placeInitialHandoffPosition = (chip) => {
|
|
10238
11109
|
const rect = chip.getBoundingClientRect();
|
|
10239
|
-
|
|
10240
|
-
|
|
10241
|
-
const middleY = Math.round(anchorRect.top + (anchorRect.height - rect.height) / 2);
|
|
10242
|
-
const candidates = [
|
|
10243
|
-
{ x: anchorRect.right + gap, y: middleY },
|
|
10244
|
-
{ x: anchorRect.left - rect.width - gap, y: middleY },
|
|
10245
|
-
{ x: anchorRect.right - rect.width, y: anchorRect.top - rect.height - gap },
|
|
10246
|
-
{ x: anchorRect.left, y: anchorRect.top - rect.height - gap },
|
|
10247
|
-
{ x: anchorRect.right - rect.width, y: anchorRect.bottom + gap }
|
|
10248
|
-
];
|
|
10249
|
-
for (const candidate of candidates) {
|
|
10250
|
-
const next = clampOverlayXY(chip, candidate.x, candidate.y);
|
|
10251
|
-
if (!rectsOverlap(positionedRect(rect, next), anchorRect, 6)) {
|
|
10252
|
-
placeOverlay(chip, next.x, next.y);
|
|
10253
|
-
return;
|
|
10254
|
-
}
|
|
10255
|
-
}
|
|
10256
|
-
}
|
|
10257
|
-
const x = Math.max(8, window.innerWidth - rect.width - 16);
|
|
10258
|
-
const y = Math.max(64, Math.min(128, Math.round(window.innerHeight * 0.14)));
|
|
11110
|
+
const x = Math.round((window.innerWidth - rect.width) / 2);
|
|
11111
|
+
const y = 14;
|
|
10259
11112
|
placeOverlay(chip, x, y);
|
|
10260
11113
|
};
|
|
11114
|
+
if (readOverlayStore(defaultPosKey) !== "1") {
|
|
11115
|
+
writeOverlayStore(overlayPosKey, null);
|
|
11116
|
+
writeOverlayStore(handoffPosKey, null);
|
|
11117
|
+
writeOverlayStore(defaultPosKey, "1");
|
|
11118
|
+
}
|
|
10261
11119
|
const setOverlayMessage = (value) => {
|
|
10262
11120
|
const node = document.querySelector("#" + statusId + " [data-codexuse-action-status]");
|
|
10263
11121
|
if (node) node.textContent = value || "";
|
|
@@ -10387,7 +11245,8 @@ function createBridgeApplyQolExpression(settings) {
|
|
|
10387
11245
|
renderHandoff();
|
|
10388
11246
|
}, true);
|
|
10389
11247
|
document.body.appendChild(pill);
|
|
10390
|
-
applyStoredOverlayPosition(pill);
|
|
11248
|
+
if (hasStoredPosition) applyStoredOverlayPosition(pill);
|
|
11249
|
+
else placeInitialOverlayPosition(pill);
|
|
10391
11250
|
return true;
|
|
10392
11251
|
}
|
|
10393
11252
|
pill.dataset.open = currentAutoRollDecision || wasOpen || shouldShowIntro ? "true" : "false";
|
|
@@ -10692,6 +11551,8 @@ function createBridgeApplyQolExpression(settings) {
|
|
|
10692
11551
|
if (shouldShowIntro) {
|
|
10693
11552
|
placeInitialOverlayPosition(pill);
|
|
10694
11553
|
writeOverlayStore(overlayIntroKey, "1");
|
|
11554
|
+
} else if (!hasStoredPosition) {
|
|
11555
|
+
placeInitialOverlayPosition(pill);
|
|
10695
11556
|
} else {
|
|
10696
11557
|
applyStoredOverlayPosition(pill);
|
|
10697
11558
|
}
|
|
@@ -10936,18 +11797,18 @@ function createBridgeApplyQolExpression(settings) {
|
|
|
10936
11797
|
const next = clampOverlayXY(chip, pos.x, pos.y);
|
|
10937
11798
|
const chipRect = chip.getBoundingClientRect();
|
|
10938
11799
|
if (rectsOverlap(positionedRect(chipRect, next), rect, 6)) {
|
|
10939
|
-
placeInitialHandoffPosition(chip
|
|
11800
|
+
placeInitialHandoffPosition(chip);
|
|
10940
11801
|
} else {
|
|
10941
11802
|
placeOverlay(chip, next.x, next.y);
|
|
10942
11803
|
}
|
|
10943
11804
|
} else {
|
|
10944
|
-
placeInitialHandoffPosition(chip
|
|
11805
|
+
placeInitialHandoffPosition(chip);
|
|
10945
11806
|
}
|
|
10946
11807
|
} catch {
|
|
10947
|
-
placeInitialHandoffPosition(chip
|
|
11808
|
+
placeInitialHandoffPosition(chip);
|
|
10948
11809
|
}
|
|
10949
11810
|
} else {
|
|
10950
|
-
placeInitialHandoffPosition(chip
|
|
11811
|
+
placeInitialHandoffPosition(chip);
|
|
10951
11812
|
}
|
|
10952
11813
|
if (wasOpen) openPicker();
|
|
10953
11814
|
reportHandoff("rendered");
|
|
@@ -11299,11 +12160,23 @@ function createBridgeApplyPowerExpression(settings, settingsExpression) {
|
|
|
11299
12160
|
|| typeof item.name === "string"
|
|
11300
12161
|
)
|
|
11301
12162
|
);
|
|
11302
|
-
const
|
|
12163
|
+
const modelLabelLooksKnown = (value) =>
|
|
12164
|
+
typeof value === "string" && /(?:gpt|codex|chatgpt|claude|gemini|llama|mistral|qwen|deepseek|grok|davinci|\\bo[1-9]\\b)/i.test(value);
|
|
12165
|
+
const objectArrayLooksModelLike = (items) =>
|
|
12166
|
+
objectArrayLooksPatchable(items)
|
|
12167
|
+
&& items.some((item) =>
|
|
12168
|
+
modelLabelLooksKnown(item.model)
|
|
12169
|
+
|| modelLabelLooksKnown(item.id)
|
|
12170
|
+
|| modelLabelLooksKnown(item.slug)
|
|
12171
|
+
|| modelLabelLooksKnown(item.name)
|
|
12172
|
+
|| modelLabelLooksKnown(item.displayName)
|
|
12173
|
+
|| modelLabelLooksKnown(item.title)
|
|
12174
|
+
);
|
|
12175
|
+
const patchModelArray = (items, allowEmpty = false, requireModelLike = false) => {
|
|
11303
12176
|
if (!Array.isArray(items)) return false;
|
|
11304
12177
|
let localChanged = false;
|
|
11305
|
-
unhideArray(items);
|
|
11306
12178
|
if (stringArrayLooksPatchable(items)) {
|
|
12179
|
+
if (requireModelLike && !items.some(modelLabelLooksKnown)) return false;
|
|
11307
12180
|
for (const model of modelWhitelistModels) {
|
|
11308
12181
|
if (!items.includes(model)) {
|
|
11309
12182
|
items.push(model);
|
|
@@ -11312,7 +12185,8 @@ function createBridgeApplyPowerExpression(settings, settingsExpression) {
|
|
|
11312
12185
|
}
|
|
11313
12186
|
return localChanged;
|
|
11314
12187
|
}
|
|
11315
|
-
if (objectArrayLooksPatchable(items, allowEmpty)) {
|
|
12188
|
+
if (objectArrayLooksPatchable(items, allowEmpty) && (!requireModelLike || allowEmpty || objectArrayLooksModelLike(items))) {
|
|
12189
|
+
unhideArray(items);
|
|
11316
12190
|
const existing = new Set();
|
|
11317
12191
|
for (const item of items) {
|
|
11318
12192
|
existing.add(String(item.model || item.id || item.slug || item.name || ""));
|
|
@@ -11327,8 +12201,8 @@ function createBridgeApplyPowerExpression(settings, settingsExpression) {
|
|
|
11327
12201
|
return localChanged;
|
|
11328
12202
|
};
|
|
11329
12203
|
if (patchModelArray(value.models, "defaultModel" in value || "availableModels" in value)) changed = true;
|
|
11330
|
-
if (patchModelArray(value.data)) changed = true;
|
|
11331
|
-
if (patchModelArray(value.result)) changed = true;
|
|
12204
|
+
if (patchModelArray(value.data, false, true)) changed = true;
|
|
12205
|
+
if (patchModelArray(value.result, false, true)) changed = true;
|
|
11332
12206
|
if (patchModelArray(value.availableModels, true)) changed = true;
|
|
11333
12207
|
if (patchModelArray(value.available_models, true)) changed = true;
|
|
11334
12208
|
if (value.available_models instanceof Set) {
|
|
@@ -11445,11 +12319,23 @@ function createBridgeApplyPowerExpression(settings, settingsExpression) {
|
|
|
11445
12319
|
return changed;
|
|
11446
12320
|
};
|
|
11447
12321
|
const reactFiberKeys = (element) => Object.keys(element || {}).filter((key) => key.startsWith("__reactFiber") || key.startsWith("__reactInternalInstance") || key.startsWith("__reactProps"));
|
|
12322
|
+
const reactModelStatePatchSelector = "[role='menu'],[role='dialog'],[role='listbox'],[data-radix-popper-content-wrapper],[data-floating-ui-portal]";
|
|
12323
|
+
const isWorkspaceChromeNode = (node) => {
|
|
12324
|
+
if (!node || node.nodeType !== 1) return false;
|
|
12325
|
+
if (node.closest?.("[data-app-action-sidebar-section-heading='Chats'],[data-app-action-sidebar-section-heading='Projects'],[data-app-action-sidebar-thread-id],[data-app-action-sidebar-project-row],[data-app-action-sidebar-project-id]")) {
|
|
12326
|
+
return true;
|
|
12327
|
+
}
|
|
12328
|
+
return Boolean(node.closest?.("main aside"));
|
|
12329
|
+
};
|
|
12330
|
+
const reactModelStatePatchNodes = () => {
|
|
12331
|
+
return Array.from(document.querySelectorAll(reactModelStatePatchSelector))
|
|
12332
|
+
.filter((node) => node && !isWorkspaceChromeNode(node));
|
|
12333
|
+
};
|
|
11448
12334
|
const patchReactModelState = () => {
|
|
11449
12335
|
if (!enabled.modelWhitelistUnlock) return false;
|
|
11450
12336
|
try {
|
|
11451
12337
|
const visited = new WeakSet();
|
|
11452
|
-
const nodes =
|
|
12338
|
+
const nodes = reactModelStatePatchNodes();
|
|
11453
12339
|
let changed = false;
|
|
11454
12340
|
for (const node of nodes.slice(0, 220)) {
|
|
11455
12341
|
for (const key of reactFiberKeys(node)) {
|
|
@@ -11464,6 +12350,39 @@ function createBridgeApplyPowerExpression(settings, settingsExpression) {
|
|
|
11464
12350
|
return false;
|
|
11465
12351
|
}
|
|
11466
12352
|
};
|
|
12353
|
+
const installModelReactStateObserver = () => {
|
|
12354
|
+
if (typeof state.cleanupModelReactStateObserver === "function") {
|
|
12355
|
+
state.cleanupModelReactStateObserver();
|
|
12356
|
+
state.cleanupModelReactStateObserver = null;
|
|
12357
|
+
}
|
|
12358
|
+
if (!enabled.modelWhitelistUnlock) return false;
|
|
12359
|
+
let frame = 0;
|
|
12360
|
+
const schedulePatch = () => {
|
|
12361
|
+
if (frame) return;
|
|
12362
|
+
frame = requestAnimationFrame(() => {
|
|
12363
|
+
frame = 0;
|
|
12364
|
+
patchReactModelState();
|
|
12365
|
+
});
|
|
12366
|
+
};
|
|
12367
|
+
const isPatchableMutationNode = (node) => {
|
|
12368
|
+
const element = node?.nodeType === 1 ? node : node?.parentElement;
|
|
12369
|
+
return Boolean(
|
|
12370
|
+
element?.matches?.(reactModelStatePatchSelector)
|
|
12371
|
+
|| element?.querySelector?.(reactModelStatePatchSelector)
|
|
12372
|
+
);
|
|
12373
|
+
};
|
|
12374
|
+
const observer = new MutationObserver((mutations) => {
|
|
12375
|
+
if (mutations.some((mutation) => Array.from(mutation.addedNodes).some(isPatchableMutationNode))) {
|
|
12376
|
+
schedulePatch();
|
|
12377
|
+
}
|
|
12378
|
+
});
|
|
12379
|
+
observer.observe(document.body, { childList: true, subtree: true });
|
|
12380
|
+
state.cleanupModelReactStateObserver = () => {
|
|
12381
|
+
observer.disconnect();
|
|
12382
|
+
if (frame) cancelAnimationFrame(frame);
|
|
12383
|
+
};
|
|
12384
|
+
return true;
|
|
12385
|
+
};
|
|
11467
12386
|
const patchPluginContainer = (value, visited = new WeakSet(), depth = 0, inheritedMarketplaceName = null) => {
|
|
11468
12387
|
if (!enabled.pluginMarketplaceUnlock || !value || typeof value !== "object" || visited.has(value) || depth > 4) return false;
|
|
11469
12388
|
visited.add(value);
|
|
@@ -12007,6 +12926,7 @@ function createBridgeApplyPowerExpression(settings, settingsExpression) {
|
|
|
12007
12926
|
const modelStatsigPatchApplied = enabled.modelWhitelistUnlock
|
|
12008
12927
|
? patchStatsigModelWhitelist()
|
|
12009
12928
|
: false;
|
|
12929
|
+
installModelReactStateObserver();
|
|
12010
12930
|
const modelReactStatePatched = enabled.modelWhitelistUnlock
|
|
12011
12931
|
? patchReactModelState()
|
|
12012
12932
|
: false;
|
|
@@ -12673,7 +13593,7 @@ async function runCdpCommandSequence(debuggerUrl, commands, timeoutMs = CODEX_CD
|
|
|
12673
13593
|
});
|
|
12674
13594
|
}
|
|
12675
13595
|
async function verifyCdpBindingRoundTrip(debuggerUrl) {
|
|
12676
|
-
const nonce = (0,
|
|
13596
|
+
const nonce = (0, import_node_crypto8.randomUUID)();
|
|
12677
13597
|
const expression = `(() => {
|
|
12678
13598
|
const binding = window[${JSON.stringify(CODEXUSE_BRIDGE_BINDING_NAME)}];
|
|
12679
13599
|
if (typeof binding !== "function") return false;
|
|
@@ -13183,14 +14103,16 @@ async function reloadOfficialCodexBridgePage(debuggerUrl) {
|
|
|
13183
14103
|
return false;
|
|
13184
14104
|
}
|
|
13185
14105
|
}
|
|
13186
|
-
async function probeOfficialCodexBridge(instance, forceInstall = false) {
|
|
14106
|
+
async function probeOfficialCodexBridge(instance, forceInstall = false, allowAnyPageTarget = false) {
|
|
13187
14107
|
const checkedAt = Date.now();
|
|
13188
14108
|
if (!instance.cdpPort) {
|
|
13189
14109
|
return bridgeStateUnavailable();
|
|
13190
14110
|
}
|
|
13191
14111
|
try {
|
|
13192
14112
|
const targets = await readCdpTargets(instance.cdpPort);
|
|
13193
|
-
const target = pickCodexCdpTarget(targets
|
|
14113
|
+
const target = pickCodexCdpTarget(targets, {
|
|
14114
|
+
allowAnyPage: allowAnyPageTarget
|
|
14115
|
+
});
|
|
13194
14116
|
if (!target?.webSocketDebuggerUrl) {
|
|
13195
14117
|
return bridgeStateUnhealthy(
|
|
13196
14118
|
instance.cdpPort,
|
|
@@ -13260,14 +14182,16 @@ async function probeOfficialCodexBridge(instance, forceInstall = false) {
|
|
|
13260
14182
|
);
|
|
13261
14183
|
}
|
|
13262
14184
|
}
|
|
13263
|
-
async function reapplyDesiredBridgeSettings(instance, bridge) {
|
|
14185
|
+
async function reapplyDesiredBridgeSettings(instance, bridge, allowAnyPageTarget = false) {
|
|
13264
14186
|
const shouldClearPowerRuntime = !instance.desiredPowerSettings && hasEnabledBridgePowerRuntime(bridge.bridgePower);
|
|
13265
14187
|
if (bridge.bridgeStatus !== "healthy" || !instance.cdpPort || !instance.desiredQolSettings && !instance.desiredPowerSettings && !shouldClearPowerRuntime) {
|
|
13266
14188
|
return bridge;
|
|
13267
14189
|
}
|
|
13268
14190
|
try {
|
|
13269
14191
|
const targets = await readCdpTargets(instance.cdpPort);
|
|
13270
|
-
const target = pickCodexCdpTarget(targets
|
|
14192
|
+
const target = pickCodexCdpTarget(targets, {
|
|
14193
|
+
allowAnyPage: allowAnyPageTarget
|
|
14194
|
+
});
|
|
13271
14195
|
if (!target?.webSocketDebuggerUrl) {
|
|
13272
14196
|
return bridge;
|
|
13273
14197
|
}
|
|
@@ -13353,7 +14277,7 @@ async function reapplyDesiredBridgeSettings(instance, bridge) {
|
|
|
13353
14277
|
};
|
|
13354
14278
|
}
|
|
13355
14279
|
}
|
|
13356
|
-
async function refreshBridgeStateIfNeeded(instance, running, force = false, reapplyDesired = false) {
|
|
14280
|
+
async function refreshBridgeStateIfNeeded(instance, running, force = false, reapplyDesired = false, allowAnyPageTarget = false) {
|
|
13357
14281
|
if (!running || !instance.cdpPort) {
|
|
13358
14282
|
return bridgeStateUnavailable();
|
|
13359
14283
|
}
|
|
@@ -13361,12 +14285,16 @@ async function refreshBridgeStateIfNeeded(instance, running, force = false, reap
|
|
|
13361
14285
|
if (!force && checkedAt && Date.now() - checkedAt < CODEX_BRIDGE_PROBE_TTL_MS) {
|
|
13362
14286
|
return bridgeStateForStoredInstance(instance, true);
|
|
13363
14287
|
}
|
|
13364
|
-
const bridge = await probeOfficialCodexBridge(
|
|
13365
|
-
|
|
14288
|
+
const bridge = await probeOfficialCodexBridge(
|
|
14289
|
+
instance,
|
|
14290
|
+
force,
|
|
14291
|
+
allowAnyPageTarget
|
|
14292
|
+
);
|
|
14293
|
+
return reapplyDesired ? await reapplyDesiredBridgeSettings(instance, bridge, allowAnyPageTarget) : bridge;
|
|
13366
14294
|
}
|
|
13367
14295
|
async function waitForBridgeStateAfterLaunch(instance) {
|
|
13368
14296
|
const deadline = Date.now() + CODEX_BRIDGE_LAUNCH_WAIT_MS;
|
|
13369
|
-
let latest = await refreshBridgeStateIfNeeded(instance, true, true, true);
|
|
14297
|
+
let latest = await refreshBridgeStateIfNeeded(instance, true, true, true, true);
|
|
13370
14298
|
while (latest.bridgeStatus !== "healthy" && Date.now() < deadline) {
|
|
13371
14299
|
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
13372
14300
|
latest = await refreshBridgeStateIfNeeded(
|
|
@@ -13376,6 +14304,7 @@ async function waitForBridgeStateAfterLaunch(instance) {
|
|
|
13376
14304
|
},
|
|
13377
14305
|
true,
|
|
13378
14306
|
true,
|
|
14307
|
+
true,
|
|
13379
14308
|
true
|
|
13380
14309
|
);
|
|
13381
14310
|
}
|
|
@@ -13384,7 +14313,7 @@ async function waitForBridgeStateAfterLaunch(instance) {
|
|
|
13384
14313
|
async function waitForBridgeStateAfterReload(instance) {
|
|
13385
14314
|
const deadline = Date.now() + CODEX_BRIDGE_LAUNCH_WAIT_MS;
|
|
13386
14315
|
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
13387
|
-
let latest = await refreshBridgeStateIfNeeded(instance, true, true, true);
|
|
14316
|
+
let latest = await refreshBridgeStateIfNeeded(instance, true, true, true, true);
|
|
13388
14317
|
while (latest.bridgeStatus !== "healthy" && Date.now() < deadline) {
|
|
13389
14318
|
await new Promise((resolve) => setTimeout(resolve, POLL_INTERVAL_MS));
|
|
13390
14319
|
latest = await refreshBridgeStateIfNeeded(
|
|
@@ -13394,6 +14323,7 @@ async function waitForBridgeStateAfterReload(instance) {
|
|
|
13394
14323
|
},
|
|
13395
14324
|
true,
|
|
13396
14325
|
true,
|
|
14326
|
+
true,
|
|
13397
14327
|
true
|
|
13398
14328
|
);
|
|
13399
14329
|
}
|
|
@@ -13446,7 +14376,7 @@ async function recordOfficialCodexActivity(input) {
|
|
|
13446
14376
|
}
|
|
13447
14377
|
function runCommand2(command, args, timeoutMs = COMMAND_TIMEOUT_MS) {
|
|
13448
14378
|
return new Promise((resolve) => {
|
|
13449
|
-
const child = (0,
|
|
14379
|
+
const child = (0, import_node_child_process7.spawn)(command, args, {
|
|
13450
14380
|
stdio: ["ignore", "pipe", "pipe"]
|
|
13451
14381
|
});
|
|
13452
14382
|
let stdout = "";
|
|
@@ -13745,7 +14675,7 @@ async function openCodexWithProfileHome(candidate, profileHome, profileName, cdp
|
|
|
13745
14675
|
if (appUserDataDir) {
|
|
13746
14676
|
(0, import_node_fs7.mkdirSync)(appUserDataDir, { recursive: true });
|
|
13747
14677
|
}
|
|
13748
|
-
child = (0,
|
|
14678
|
+
child = (0, import_node_child_process7.spawn)(
|
|
13749
14679
|
"/usr/bin/open",
|
|
13750
14680
|
[
|
|
13751
14681
|
"-n",
|
|
@@ -13763,7 +14693,7 @@ async function openCodexWithProfileHome(candidate, profileHome, profileName, cdp
|
|
|
13763
14693
|
],
|
|
13764
14694
|
{
|
|
13765
14695
|
detached: true,
|
|
13766
|
-
env:
|
|
14696
|
+
env: buildCodexChildEnv(),
|
|
13767
14697
|
stdio: "ignore"
|
|
13768
14698
|
}
|
|
13769
14699
|
);
|
|
@@ -14524,6 +15454,13 @@ function formatProfileLabel(name, displayName) {
|
|
|
14524
15454
|
}
|
|
14525
15455
|
return name;
|
|
14526
15456
|
}
|
|
15457
|
+
var AUTO_ROLL_ACCESS_REQUIRED_MESSAGE = "Auto-roll requires a Pro license or an active trial.";
|
|
15458
|
+
async function assertAutoRollAccess() {
|
|
15459
|
+
const license = await licenseService.getStatus();
|
|
15460
|
+
if (!license.capabilities.autoRoll) {
|
|
15461
|
+
throw new Error(AUTO_ROLL_ACCESS_REQUIRED_MESSAGE);
|
|
15462
|
+
}
|
|
15463
|
+
}
|
|
14527
15464
|
function profileRateLimitKey(profile) {
|
|
14528
15465
|
return resolveProfileIdentityKeyFromProfile(profile);
|
|
14529
15466
|
}
|
|
@@ -15180,6 +16117,7 @@ async function collectUsageMap(manager, profiles, codexPath) {
|
|
|
15180
16117
|
return usageMap;
|
|
15181
16118
|
}
|
|
15182
16119
|
async function runAutoRollPass(manager, options) {
|
|
16120
|
+
await assertAutoRollAccess();
|
|
15183
16121
|
const profiles = await manager.listProfiles();
|
|
15184
16122
|
if (!profiles.length) {
|
|
15185
16123
|
return { switched: false, message: "No profiles found.", source: null, target: null };
|
|
@@ -15304,9 +16242,13 @@ async function handleProfileCommand(args, version) {
|
|
|
15304
16242
|
const manager = new ProfileManager();
|
|
15305
16243
|
switch (sub) {
|
|
15306
16244
|
case "list": {
|
|
15307
|
-
const
|
|
16245
|
+
const visible = await loadLicenseVisibleProfiles(manager, { freshLicense: true });
|
|
16246
|
+
const profiles = visible.profiles;
|
|
15308
16247
|
const current = await manager.getCurrentProfile();
|
|
15309
16248
|
if (!profiles.length) {
|
|
16249
|
+
if (visible.allProfiles.length) {
|
|
16250
|
+
throw new Error(PROFILE_ACCESS_DENIED_MESSAGE);
|
|
16251
|
+
}
|
|
15310
16252
|
console.log("No profiles found.");
|
|
15311
16253
|
return;
|
|
15312
16254
|
}
|
|
@@ -15348,6 +16290,7 @@ async function handleProfileCommand(args, version) {
|
|
|
15348
16290
|
console.log("No active profile.");
|
|
15349
16291
|
return;
|
|
15350
16292
|
}
|
|
16293
|
+
await assertProfileAccessAllowed(manager, current.name);
|
|
15351
16294
|
console.log(current.name);
|
|
15352
16295
|
return;
|
|
15353
16296
|
}
|
|
@@ -15371,6 +16314,7 @@ async function handleProfileCommand(args, version) {
|
|
|
15371
16314
|
if (!name) {
|
|
15372
16315
|
throw new Error("Profile name is required.");
|
|
15373
16316
|
}
|
|
16317
|
+
await assertProfileAccessAllowed(manager, name);
|
|
15374
16318
|
if (!hasFlag(flags, "--skip-login")) {
|
|
15375
16319
|
const loginMode = resolveLoginMode(parseLoginMode(flags));
|
|
15376
16320
|
await runCodexLogin(loginMode);
|
|
@@ -15385,6 +16329,7 @@ async function handleProfileCommand(args, version) {
|
|
|
15385
16329
|
if (!name) {
|
|
15386
16330
|
throw new Error("Profile name is required.");
|
|
15387
16331
|
}
|
|
16332
|
+
await assertProfileAccessAllowed(manager, name);
|
|
15388
16333
|
const activateOfficialCodex = resolveActivateCodexFlag(flags, false);
|
|
15389
16334
|
const profiles = await manager.listProfiles();
|
|
15390
16335
|
const targetProfileKey = findProfileKey(profiles, name);
|
|
@@ -15404,10 +16349,7 @@ async function handleProfileCommand(args, version) {
|
|
|
15404
16349
|
}
|
|
15405
16350
|
case "autoroll":
|
|
15406
16351
|
case "auto-roll": {
|
|
15407
|
-
|
|
15408
|
-
if (!license.isPro) {
|
|
15409
|
-
throw new Error("Auto-roll requires a Pro license.");
|
|
15410
|
-
}
|
|
16352
|
+
await assertAutoRollAccess();
|
|
15411
16353
|
const watch = hasFlag(flags, "--watch");
|
|
15412
16354
|
const dryRun = hasFlag(flags, "--dry-run");
|
|
15413
16355
|
const settings = await getStoredAutoRollSettings().catch(() => null);
|
|
@@ -15487,6 +16429,7 @@ async function handleProfileCommand(args, version) {
|
|
|
15487
16429
|
if (!from || !to) {
|
|
15488
16430
|
throw new Error("Old and new profile names are required.");
|
|
15489
16431
|
}
|
|
16432
|
+
await assertProfileAccessAllowed(manager, from);
|
|
15490
16433
|
await manager.renameProfile(from, to);
|
|
15491
16434
|
console.log(`Profile renamed: ${from} -> ${to}`);
|
|
15492
16435
|
return;
|
|
@@ -15615,7 +16558,7 @@ function normalizeSnapshot(value) {
|
|
|
15615
16558
|
settingsJson
|
|
15616
16559
|
};
|
|
15617
16560
|
}
|
|
15618
|
-
async function requestJson(method, pathname,
|
|
16561
|
+
async function requestJson(method, pathname, access, body, rawBody) {
|
|
15619
16562
|
const controller = new AbortController();
|
|
15620
16563
|
const timeout = setTimeout(() => controller.abort(), CLOUD_SYNC_TIMEOUT_MS);
|
|
15621
16564
|
try {
|
|
@@ -15623,7 +16566,10 @@ async function requestJson(method, pathname, licenseKey, body, rawBody) {
|
|
|
15623
16566
|
method,
|
|
15624
16567
|
headers: {
|
|
15625
16568
|
"Content-Type": "application/json",
|
|
15626
|
-
"x-codexuse-license-key":
|
|
16569
|
+
"x-codexuse-license-key": access.syncKey,
|
|
16570
|
+
...access.trialDeviceId ? { "x-codexuse-trial-device-id": access.trialDeviceId } : {},
|
|
16571
|
+
...access.trialToken ? { "x-codexuse-trial-token": access.trialToken } : {},
|
|
16572
|
+
...access.trialMigration ? { "x-codexuse-sync-migrate-trial": "1" } : {}
|
|
15627
16573
|
},
|
|
15628
16574
|
body: typeof rawBody === "string" ? rawBody : body === void 0 ? void 0 : JSON.stringify(body),
|
|
15629
16575
|
signal: controller.signal
|
|
@@ -15650,15 +16596,15 @@ async function requestJson(method, pathname, licenseKey, body, rawBody) {
|
|
|
15650
16596
|
clearTimeout(timeout);
|
|
15651
16597
|
}
|
|
15652
16598
|
}
|
|
15653
|
-
async function fetchRemoteSnapshot(
|
|
15654
|
-
const response = await requestJson("GET", "/v1/sync/snapshot",
|
|
16599
|
+
async function fetchRemoteSnapshot(access) {
|
|
16600
|
+
const response = await requestJson("GET", "/v1/sync/snapshot", access);
|
|
15655
16601
|
return normalizeSnapshot(response.snapshot);
|
|
15656
16602
|
}
|
|
15657
|
-
async function fetchRemoteSnapshotMeta(
|
|
16603
|
+
async function fetchRemoteSnapshotMeta(access) {
|
|
15658
16604
|
const response = await requestJson(
|
|
15659
16605
|
"GET",
|
|
15660
16606
|
"/v1/sync/snapshot/meta",
|
|
15661
|
-
|
|
16607
|
+
access
|
|
15662
16608
|
);
|
|
15663
16609
|
const updatedAt = toIsoOrNull(response.updatedAt);
|
|
15664
16610
|
const snapshotKind = normalizeSnapshotKind(response.snapshotKind);
|
|
@@ -15667,12 +16613,12 @@ async function fetchRemoteSnapshotMeta(licenseKey) {
|
|
|
15667
16613
|
snapshotKind: updatedAt && response.snapshotKind === void 0 ? "legacy-plaintext" : snapshotKind
|
|
15668
16614
|
};
|
|
15669
16615
|
}
|
|
15670
|
-
async function pushRemoteSnapshot(
|
|
16616
|
+
async function pushRemoteSnapshot(access, snapshot, options) {
|
|
15671
16617
|
const serializedSnapshot = typeof options?.serializedSnapshot === "string" ? options.serializedSnapshot : JSON.stringify(snapshot);
|
|
15672
16618
|
const response = await requestJson(
|
|
15673
16619
|
"PUT",
|
|
15674
16620
|
"/v1/sync/snapshot",
|
|
15675
|
-
|
|
16621
|
+
access,
|
|
15676
16622
|
void 0,
|
|
15677
16623
|
`{"snapshot":${serializedSnapshot}}`
|
|
15678
16624
|
);
|
|
@@ -15691,8 +16637,8 @@ async function pushRemoteSnapshot(licenseKey, snapshot, options) {
|
|
|
15691
16637
|
var import_toml3 = __toESM(require_toml(), 1);
|
|
15692
16638
|
|
|
15693
16639
|
// ../../packages/runtime-codex/src/codex/config-metadata.ts
|
|
15694
|
-
var
|
|
15695
|
-
var
|
|
16640
|
+
var import_node_child_process8 = require("child_process");
|
|
16641
|
+
var import_promises2 = require("fs/promises");
|
|
15696
16642
|
var import_node_path11 = __toESM(require("path"), 1);
|
|
15697
16643
|
var CONFIG_SCHEMA_URL = "https://raw.githubusercontent.com/openai/codex/main/codex-rs/core/config.schema.json";
|
|
15698
16644
|
var METADATA_CACHE_TTL_MS = 6e4;
|
|
@@ -15803,9 +16749,9 @@ function uniqueSorted(values) {
|
|
|
15803
16749
|
}
|
|
15804
16750
|
async function runCommand3(command, args, timeoutMs = 3e3) {
|
|
15805
16751
|
return new Promise((resolve, reject) => {
|
|
15806
|
-
const child = (0,
|
|
16752
|
+
const child = (0, import_node_child_process8.spawn)(command, args, {
|
|
15807
16753
|
stdio: ["ignore", "pipe", "pipe"],
|
|
15808
|
-
env:
|
|
16754
|
+
env: buildCodexChildEnv()
|
|
15809
16755
|
});
|
|
15810
16756
|
let stdout = "";
|
|
15811
16757
|
let stderr = "";
|
|
@@ -15884,7 +16830,7 @@ async function readSchemaFromLocal() {
|
|
|
15884
16830
|
].filter((candidate) => Boolean(candidate));
|
|
15885
16831
|
for (const candidate of candidates) {
|
|
15886
16832
|
try {
|
|
15887
|
-
return await (0,
|
|
16833
|
+
return await (0, import_promises2.readFile)(candidate, "utf8");
|
|
15888
16834
|
} catch {
|
|
15889
16835
|
}
|
|
15890
16836
|
}
|
|
@@ -15984,7 +16930,7 @@ async function getCodexConfigMetadata(forceRefresh = false) {
|
|
|
15984
16930
|
}
|
|
15985
16931
|
|
|
15986
16932
|
// ../../packages/runtime-codex/src/codex/config-io.ts
|
|
15987
|
-
var
|
|
16933
|
+
var import_promises3 = require("fs/promises");
|
|
15988
16934
|
var import_node_path13 = __toESM(require("path"), 1);
|
|
15989
16935
|
|
|
15990
16936
|
// ../../packages/runtime-codex/src/codex/home.ts
|
|
@@ -16094,7 +17040,7 @@ function getConfigPath(options) {
|
|
|
16094
17040
|
}
|
|
16095
17041
|
async function ensureConfigDirExists(filePath) {
|
|
16096
17042
|
const dir = import_node_path13.default.dirname(filePath);
|
|
16097
|
-
await (0,
|
|
17043
|
+
await (0, import_promises3.mkdir)(dir, { recursive: true });
|
|
16098
17044
|
}
|
|
16099
17045
|
function normalizeLineEndings(content) {
|
|
16100
17046
|
return content.replace(/\r\n/g, "\n");
|
|
@@ -16106,7 +17052,7 @@ function ensureTrailingNewline(content) {
|
|
|
16106
17052
|
async function readConfigContent(options) {
|
|
16107
17053
|
const configPath = getConfigPath(options);
|
|
16108
17054
|
try {
|
|
16109
|
-
const content = await (0,
|
|
17055
|
+
const content = await (0, import_promises3.readFile)(configPath, "utf8");
|
|
16110
17056
|
return {
|
|
16111
17057
|
exists: true,
|
|
16112
17058
|
content: normalizeLineEndings(content)
|
|
@@ -16126,14 +17072,14 @@ async function writeConfigContent(content, options) {
|
|
|
16126
17072
|
const configPath = getConfigPath(options);
|
|
16127
17073
|
await ensureConfigDirExists(configPath);
|
|
16128
17074
|
const normalized = ensureTrailingNewline(normalizeLineEndings(content));
|
|
16129
|
-
await (0,
|
|
17075
|
+
await (0, import_promises3.writeFile)(configPath, normalized, "utf8");
|
|
16130
17076
|
}
|
|
16131
17077
|
async function getUpdatedAt(configPath, exists) {
|
|
16132
17078
|
if (!exists) {
|
|
16133
17079
|
return null;
|
|
16134
17080
|
}
|
|
16135
17081
|
try {
|
|
16136
|
-
const stats = await (0,
|
|
17082
|
+
const stats = await (0, import_promises3.stat)(configPath);
|
|
16137
17083
|
return stats.mtimeMs;
|
|
16138
17084
|
} catch (error) {
|
|
16139
17085
|
const nodeError = error;
|
|
@@ -16647,10 +17593,10 @@ async function saveCodexConfigContent(content, options) {
|
|
|
16647
17593
|
}
|
|
16648
17594
|
|
|
16649
17595
|
// ../../packages/runtime-profiles/src/cloud-sync/encryption.ts
|
|
16650
|
-
var
|
|
16651
|
-
var
|
|
16652
|
-
var
|
|
16653
|
-
var
|
|
17596
|
+
var import_node_child_process9 = require("child_process");
|
|
17597
|
+
var import_node_crypto9 = require("crypto");
|
|
17598
|
+
var KEYCHAIN_SERVICE2 = "CODEXUSE_CLOUD_SYNC_PASSPHRASE";
|
|
17599
|
+
var KEYCHAIN_ACCOUNT_PREFIX2 = "license-sha256:";
|
|
16654
17600
|
var ENCRYPTION_AAD = Buffer.from("codexuse-cloud-sync-v2", "utf8");
|
|
16655
17601
|
var SCRYPT_PARAMS = {
|
|
16656
17602
|
N: 32768,
|
|
@@ -16673,7 +17619,7 @@ function requireCloudSyncPassphrase(value) {
|
|
|
16673
17619
|
}
|
|
16674
17620
|
async function deriveKey(passphrase, salt, params = SCRYPT_PARAMS) {
|
|
16675
17621
|
return new Promise((resolve, reject) => {
|
|
16676
|
-
(0,
|
|
17622
|
+
(0, import_node_crypto9.scrypt)(passphrase, salt, params.keyLength, {
|
|
16677
17623
|
N: params.N,
|
|
16678
17624
|
r: params.r,
|
|
16679
17625
|
p: params.p,
|
|
@@ -16710,10 +17656,10 @@ function normalizeDecryptedSnapshot(value) {
|
|
|
16710
17656
|
};
|
|
16711
17657
|
}
|
|
16712
17658
|
async function encryptCloudSyncSnapshot(snapshot, passphrase) {
|
|
16713
|
-
const salt = (0,
|
|
16714
|
-
const iv = (0,
|
|
17659
|
+
const salt = (0, import_node_crypto9.randomBytes)(16);
|
|
17660
|
+
const iv = (0, import_node_crypto9.randomBytes)(12);
|
|
16715
17661
|
const key = await deriveKey(requireCloudSyncPassphrase(passphrase), salt);
|
|
16716
|
-
const cipher = (0,
|
|
17662
|
+
const cipher = (0, import_node_crypto9.createCipheriv)("aes-256-gcm", key, iv);
|
|
16717
17663
|
cipher.setAAD(ENCRYPTION_AAD);
|
|
16718
17664
|
const plaintext = Buffer.from(JSON.stringify(snapshot), "utf8");
|
|
16719
17665
|
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
@@ -16740,7 +17686,7 @@ async function decryptCloudSyncSnapshot(snapshot, passphrase) {
|
|
|
16740
17686
|
const tag = fromBase64(snapshot.encryption.tag);
|
|
16741
17687
|
const ciphertext = fromBase64(snapshot.encryption.ciphertext);
|
|
16742
17688
|
const key = await deriveKey(requireCloudSyncPassphrase(passphrase), salt, snapshot.encryption.kdfParams);
|
|
16743
|
-
const decipher = (0,
|
|
17689
|
+
const decipher = (0, import_node_crypto9.createDecipheriv)("aes-256-gcm", key, iv);
|
|
16744
17690
|
decipher.setAAD(ENCRYPTION_AAD);
|
|
16745
17691
|
decipher.setAuthTag(tag);
|
|
16746
17692
|
const plaintext = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
|
@@ -16755,12 +17701,12 @@ async function decryptCloudSyncSnapshot(snapshot, passphrase) {
|
|
|
16755
17701
|
}
|
|
16756
17702
|
}
|
|
16757
17703
|
function keychainAccountForLicense(licenseKey) {
|
|
16758
|
-
const digest = (0,
|
|
16759
|
-
return
|
|
17704
|
+
const digest = (0, import_node_crypto9.createHash)("sha256").update(licenseKey).digest("hex");
|
|
17705
|
+
return KEYCHAIN_ACCOUNT_PREFIX2 + digest;
|
|
16760
17706
|
}
|
|
16761
|
-
function
|
|
17707
|
+
function runSecurityCommand2(args) {
|
|
16762
17708
|
return new Promise((resolve, reject) => {
|
|
16763
|
-
const child = (0,
|
|
17709
|
+
const child = (0, import_node_child_process9.spawn)("security", args, { stdio: ["ignore", "pipe", "ignore"] });
|
|
16764
17710
|
let stdout = "";
|
|
16765
17711
|
child.stdout?.on("data", (chunk) => {
|
|
16766
17712
|
stdout += String(chunk);
|
|
@@ -16777,12 +17723,12 @@ async function readRememberedCloudSyncPassphrase(licenseKey) {
|
|
|
16777
17723
|
if (process.platform !== "darwin") {
|
|
16778
17724
|
return null;
|
|
16779
17725
|
}
|
|
16780
|
-
const result = await
|
|
17726
|
+
const result = await runSecurityCommand2([
|
|
16781
17727
|
"find-generic-password",
|
|
16782
17728
|
"-a",
|
|
16783
17729
|
keychainAccountForLicense(licenseKey),
|
|
16784
17730
|
"-s",
|
|
16785
|
-
|
|
17731
|
+
KEYCHAIN_SERVICE2,
|
|
16786
17732
|
"-w"
|
|
16787
17733
|
]);
|
|
16788
17734
|
if (result.code !== 0) {
|
|
@@ -16795,13 +17741,13 @@ async function writeRememberedCloudSyncPassphrase(licenseKey, passphrase) {
|
|
|
16795
17741
|
throw new Error("keychain-unsupported");
|
|
16796
17742
|
}
|
|
16797
17743
|
const value = requireCloudSyncPassphrase(passphrase);
|
|
16798
|
-
const result = await
|
|
17744
|
+
const result = await runSecurityCommand2([
|
|
16799
17745
|
"add-generic-password",
|
|
16800
17746
|
"-U",
|
|
16801
17747
|
"-a",
|
|
16802
17748
|
keychainAccountForLicense(licenseKey),
|
|
16803
17749
|
"-s",
|
|
16804
|
-
|
|
17750
|
+
KEYCHAIN_SERVICE2,
|
|
16805
17751
|
"-w",
|
|
16806
17752
|
value
|
|
16807
17753
|
]);
|
|
@@ -16880,31 +17826,144 @@ async function writeState(patch) {
|
|
|
16880
17826
|
async function resolveEligibility() {
|
|
16881
17827
|
const stored = await getStoredLicense();
|
|
16882
17828
|
const licenseKey = typeof stored?.licenseKey === "string" ? stored.licenseKey.trim() : "";
|
|
16883
|
-
|
|
17829
|
+
const status = await licenseService.getStatus();
|
|
17830
|
+
if (licenseKey && status.isPro) {
|
|
17831
|
+
const active = status.state === "active" || status.state === "grace";
|
|
17832
|
+
if (active) {
|
|
17833
|
+
await migrateTrialCloudSyncToPaidLicenseSafe(licenseKey);
|
|
17834
|
+
}
|
|
16884
17835
|
return {
|
|
16885
|
-
canSync:
|
|
16886
|
-
|
|
16887
|
-
|
|
16888
|
-
|
|
17836
|
+
canSync: active,
|
|
17837
|
+
canDelete: active,
|
|
17838
|
+
reason: active ? null : "Cloud sync requires an active Pro license.",
|
|
17839
|
+
access: active ? { syncKey: licenseKey } : null,
|
|
17840
|
+
hasPaidLicenseKey: true,
|
|
17841
|
+
licenseState: status.state
|
|
16889
17842
|
};
|
|
16890
17843
|
}
|
|
16891
|
-
|
|
16892
|
-
|
|
16893
|
-
|
|
17844
|
+
if (status.trial?.active && status.capabilities.cloudSync) {
|
|
17845
|
+
const trialAccess = await resolveTrialCloudSyncAccess();
|
|
17846
|
+
if (trialAccess) {
|
|
17847
|
+
return {
|
|
17848
|
+
canSync: true,
|
|
17849
|
+
canDelete: true,
|
|
17850
|
+
reason: null,
|
|
17851
|
+
access: trialAccess,
|
|
17852
|
+
hasPaidLicenseKey: false,
|
|
17853
|
+
licenseState: status.state
|
|
17854
|
+
};
|
|
17855
|
+
}
|
|
16894
17856
|
return {
|
|
16895
17857
|
canSync: false,
|
|
16896
|
-
|
|
16897
|
-
|
|
17858
|
+
canDelete: false,
|
|
17859
|
+
reason: "Cloud sync trial identity is unavailable.",
|
|
17860
|
+
access: null,
|
|
17861
|
+
hasPaidLicenseKey: false,
|
|
16898
17862
|
licenseState: status.state
|
|
16899
17863
|
};
|
|
16900
17864
|
}
|
|
17865
|
+
if (status.trial?.expired) {
|
|
17866
|
+
const trialAccess = await resolveTrialCloudSyncAccess();
|
|
17867
|
+
if (trialAccess) {
|
|
17868
|
+
return {
|
|
17869
|
+
canSync: false,
|
|
17870
|
+
canDelete: true,
|
|
17871
|
+
reason: "Cloud sync requires an active trial or Pro license.",
|
|
17872
|
+
access: trialAccess,
|
|
17873
|
+
hasPaidLicenseKey: false,
|
|
17874
|
+
licenseState: status.state
|
|
17875
|
+
};
|
|
17876
|
+
}
|
|
17877
|
+
}
|
|
16901
17878
|
return {
|
|
16902
|
-
canSync:
|
|
16903
|
-
|
|
16904
|
-
|
|
17879
|
+
canSync: false,
|
|
17880
|
+
canDelete: false,
|
|
17881
|
+
reason: "Cloud sync requires an active trial or Pro license.",
|
|
17882
|
+
access: null,
|
|
17883
|
+
hasPaidLicenseKey: false,
|
|
16905
17884
|
licenseState: status.state
|
|
16906
17885
|
};
|
|
16907
17886
|
}
|
|
17887
|
+
async function hasRemoteSnapshot(access) {
|
|
17888
|
+
const meta = await fetchRemoteSnapshotMeta(access);
|
|
17889
|
+
return meta.snapshotKind !== "none" || Boolean(meta.updatedAt);
|
|
17890
|
+
}
|
|
17891
|
+
async function readRemoteSnapshotOrNull(access) {
|
|
17892
|
+
try {
|
|
17893
|
+
return await fetchRemoteSnapshot(access);
|
|
17894
|
+
} catch (error) {
|
|
17895
|
+
if (error instanceof CloudSyncClientError && (error.status === 403 || error.status === 404)) {
|
|
17896
|
+
return null;
|
|
17897
|
+
}
|
|
17898
|
+
throw error;
|
|
17899
|
+
}
|
|
17900
|
+
}
|
|
17901
|
+
async function migrateTrialPassphraseToPaidLicense(trialKey, licenseKey) {
|
|
17902
|
+
const paidPassphrase = await readRememberedCloudSyncPassphrase(licenseKey).catch(() => null);
|
|
17903
|
+
if (paidPassphrase) {
|
|
17904
|
+
return;
|
|
17905
|
+
}
|
|
17906
|
+
const trialPassphrase = await readRememberedCloudSyncPassphrase(trialKey).catch(() => null);
|
|
17907
|
+
if (!trialPassphrase) {
|
|
17908
|
+
return;
|
|
17909
|
+
}
|
|
17910
|
+
await writeRememberedCloudSyncPassphrase(licenseKey, trialPassphrase);
|
|
17911
|
+
}
|
|
17912
|
+
async function migrateTrialCloudSyncToPaidLicense(licenseKey) {
|
|
17913
|
+
const paidLicenseKey = licenseKey.trim();
|
|
17914
|
+
if (!paidLicenseKey) {
|
|
17915
|
+
return;
|
|
17916
|
+
}
|
|
17917
|
+
const trialAccess = await resolveTrialCloudSyncAccess();
|
|
17918
|
+
if (!trialAccess || trialAccess.syncKey === paidLicenseKey) {
|
|
17919
|
+
return;
|
|
17920
|
+
}
|
|
17921
|
+
if (await hasRemoteSnapshot({ syncKey: paidLicenseKey })) {
|
|
17922
|
+
await migrateTrialPassphraseToPaidLicense(trialAccess.syncKey, paidLicenseKey).catch((error) => {
|
|
17923
|
+
logWarn("[cloud-sync] failed to migrate remembered trial passphrase", {
|
|
17924
|
+
error: error instanceof Error ? error.message : String(error)
|
|
17925
|
+
});
|
|
17926
|
+
});
|
|
17927
|
+
return;
|
|
17928
|
+
}
|
|
17929
|
+
const trialSnapshot = await readRemoteSnapshotOrNull({
|
|
17930
|
+
...trialAccess,
|
|
17931
|
+
syncKey: paidLicenseKey,
|
|
17932
|
+
trialMigration: true
|
|
17933
|
+
});
|
|
17934
|
+
if (trialSnapshot) {
|
|
17935
|
+
await pushRemoteSnapshot({ syncKey: paidLicenseKey }, trialSnapshot, {
|
|
17936
|
+
serializedSnapshot: JSON.stringify(trialSnapshot)
|
|
17937
|
+
});
|
|
17938
|
+
}
|
|
17939
|
+
await migrateTrialPassphraseToPaidLicense(trialAccess.syncKey, paidLicenseKey);
|
|
17940
|
+
}
|
|
17941
|
+
async function migrateTrialCloudSyncToPaidLicenseSafe(licenseKey) {
|
|
17942
|
+
try {
|
|
17943
|
+
await migrateTrialCloudSyncToPaidLicense(licenseKey);
|
|
17944
|
+
} catch (error) {
|
|
17945
|
+
logWarn("[cloud-sync] trial snapshot migration to paid license failed", {
|
|
17946
|
+
error: error instanceof Error ? error.message : String(error)
|
|
17947
|
+
});
|
|
17948
|
+
}
|
|
17949
|
+
}
|
|
17950
|
+
async function resolveTrialCloudSyncAccess() {
|
|
17951
|
+
let deviceId;
|
|
17952
|
+
try {
|
|
17953
|
+
deviceId = await getDeviceId();
|
|
17954
|
+
} catch {
|
|
17955
|
+
return null;
|
|
17956
|
+
}
|
|
17957
|
+
const trialToken = await readStoredTrialToken(deviceId).catch(() => null);
|
|
17958
|
+
if (!trialToken) {
|
|
17959
|
+
return null;
|
|
17960
|
+
}
|
|
17961
|
+
return {
|
|
17962
|
+
syncKey: `trial:${deviceId}`,
|
|
17963
|
+
trialDeviceId: deviceId,
|
|
17964
|
+
trialToken
|
|
17965
|
+
};
|
|
17966
|
+
}
|
|
16908
17967
|
function formatMegabytes(bytes) {
|
|
16909
17968
|
return (bytes / MB_DIVISOR).toFixed(2);
|
|
16910
17969
|
}
|
|
@@ -16997,19 +18056,19 @@ async function getCloudSyncStatus() {
|
|
|
16997
18056
|
let remoteUpdatedAt = state.remoteUpdatedAt ?? null;
|
|
16998
18057
|
let remoteKind = state.remoteKind ?? "none";
|
|
16999
18058
|
let hasRememberedPassphrase = false;
|
|
17000
|
-
if (eligibility.canSync && eligibility.
|
|
18059
|
+
if (eligibility.canSync && eligibility.access) {
|
|
17001
18060
|
hasRememberedPassphrase = Boolean(
|
|
17002
|
-
await readRememberedCloudSyncPassphrase(eligibility.
|
|
18061
|
+
await readRememberedCloudSyncPassphrase(eligibility.access.syncKey).catch(() => null)
|
|
17003
18062
|
);
|
|
17004
18063
|
try {
|
|
17005
|
-
const remoteMeta = await fetchRemoteSnapshotMeta(eligibility.
|
|
18064
|
+
const remoteMeta = await fetchRemoteSnapshotMeta(eligibility.access);
|
|
17006
18065
|
remoteUpdatedAt = remoteMeta.updatedAt;
|
|
17007
18066
|
remoteKind = remoteMeta.snapshotKind;
|
|
17008
18067
|
await writeState({ remoteUpdatedAt, remoteKind });
|
|
17009
18068
|
} catch (error) {
|
|
17010
18069
|
if (error instanceof CloudSyncClientError && error.status === 404) {
|
|
17011
18070
|
try {
|
|
17012
|
-
const remote = await fetchRemoteSnapshot(eligibility.
|
|
18071
|
+
const remote = await fetchRemoteSnapshot(eligibility.access);
|
|
17013
18072
|
remoteUpdatedAt = remote?.updatedAt ?? null;
|
|
17014
18073
|
remoteKind = getSnapshotKind(remote);
|
|
17015
18074
|
} catch {
|
|
@@ -17019,9 +18078,10 @@ async function getCloudSyncStatus() {
|
|
|
17019
18078
|
}
|
|
17020
18079
|
return {
|
|
17021
18080
|
canSync: eligibility.canSync,
|
|
18081
|
+
canDelete: eligibility.canDelete,
|
|
17022
18082
|
reason: eligibility.reason,
|
|
17023
18083
|
licenseState: eligibility.licenseState,
|
|
17024
|
-
hasLicenseKey:
|
|
18084
|
+
hasLicenseKey: eligibility.hasPaidLicenseKey,
|
|
17025
18085
|
hasRememberedPassphrase,
|
|
17026
18086
|
lastPushAt: state.lastPushAt ?? null,
|
|
17027
18087
|
lastPullAt: state.lastPullAt ?? null,
|
|
@@ -17032,7 +18092,7 @@ async function getCloudSyncStatus() {
|
|
|
17032
18092
|
}
|
|
17033
18093
|
async function pushCloudSync(options = {}) {
|
|
17034
18094
|
const eligibility = await resolveEligibility();
|
|
17035
|
-
if (!eligibility.canSync || !eligibility.
|
|
18095
|
+
if (!eligibility.canSync || !eligibility.access) {
|
|
17036
18096
|
return {
|
|
17037
18097
|
mode: "push",
|
|
17038
18098
|
status: "skipped",
|
|
@@ -17045,7 +18105,7 @@ async function pushCloudSync(options = {}) {
|
|
|
17045
18105
|
try {
|
|
17046
18106
|
await profileManager.initialize();
|
|
17047
18107
|
const localSnapshot = await buildLocalSnapshot(profileManager, { enforcePushGuards: true });
|
|
17048
|
-
const passphrase = await resolvePassphrase(eligibility.
|
|
18108
|
+
const passphrase = await resolvePassphrase(eligibility.access.syncKey, options);
|
|
17049
18109
|
const encryptedSnapshot = await encryptCloudSyncSnapshot(localSnapshot, passphrase.value);
|
|
17050
18110
|
const serializedSnapshot = JSON.stringify(encryptedSnapshot);
|
|
17051
18111
|
const snapshotBytes = Buffer.byteLength(serializedSnapshot, "utf8");
|
|
@@ -17063,7 +18123,7 @@ async function pushCloudSync(options = {}) {
|
|
|
17063
18123
|
if (process.env.NODE_ENV !== "production") {
|
|
17064
18124
|
logInfo("[cloud-sync] push snapshot summary", summarizeSnapshot(localSnapshot));
|
|
17065
18125
|
}
|
|
17066
|
-
const remote = await pushRemoteSnapshot(eligibility.
|
|
18126
|
+
const remote = await pushRemoteSnapshot(eligibility.access, encryptedSnapshot, {
|
|
17067
18127
|
serializedSnapshot
|
|
17068
18128
|
});
|
|
17069
18129
|
const remoteKind = getSnapshotKind(remote.snapshot);
|
|
@@ -17082,7 +18142,7 @@ async function pushCloudSync(options = {}) {
|
|
|
17082
18142
|
remoteUpdatedAt: remote.snapshot.updatedAt
|
|
17083
18143
|
};
|
|
17084
18144
|
}
|
|
17085
|
-
await rememberPassphraseIfRequested(eligibility.
|
|
18145
|
+
await rememberPassphraseIfRequested(eligibility.access.syncKey, options, passphrase);
|
|
17086
18146
|
return {
|
|
17087
18147
|
mode: "push",
|
|
17088
18148
|
status: "applied",
|
|
@@ -17099,7 +18159,7 @@ async function pushCloudSync(options = {}) {
|
|
|
17099
18159
|
}
|
|
17100
18160
|
async function pullCloudSync(options = {}) {
|
|
17101
18161
|
const eligibility = await resolveEligibility();
|
|
17102
|
-
if (!eligibility.canSync || !eligibility.
|
|
18162
|
+
if (!eligibility.canSync || !eligibility.access) {
|
|
17103
18163
|
return {
|
|
17104
18164
|
mode: "pull",
|
|
17105
18165
|
status: "skipped",
|
|
@@ -17111,7 +18171,7 @@ async function pullCloudSync(options = {}) {
|
|
|
17111
18171
|
const profileManager = new ProfileManager();
|
|
17112
18172
|
try {
|
|
17113
18173
|
await profileManager.initialize();
|
|
17114
|
-
const remote = await fetchRemoteSnapshot(eligibility.
|
|
18174
|
+
const remote = await fetchRemoteSnapshot(eligibility.access);
|
|
17115
18175
|
if (!remote) {
|
|
17116
18176
|
await writeState({ remoteUpdatedAt: null, remoteKind: "none" });
|
|
17117
18177
|
return {
|
|
@@ -17126,7 +18186,7 @@ async function pullCloudSync(options = {}) {
|
|
|
17126
18186
|
let passphrase = null;
|
|
17127
18187
|
let plainSnapshot;
|
|
17128
18188
|
if (remote.schemaVersion === CLOUD_SYNC_ENCRYPTED_SCHEMA_VERSION) {
|
|
17129
|
-
passphrase = await resolvePassphrase(eligibility.
|
|
18189
|
+
passphrase = await resolvePassphrase(eligibility.access.syncKey, options);
|
|
17130
18190
|
plainSnapshot = await decryptCloudSyncSnapshot(remote, passphrase.value);
|
|
17131
18191
|
} else {
|
|
17132
18192
|
plainSnapshot = remote;
|
|
@@ -17139,7 +18199,7 @@ async function pullCloudSync(options = {}) {
|
|
|
17139
18199
|
lastError: void 0
|
|
17140
18200
|
});
|
|
17141
18201
|
if (passphrase) {
|
|
17142
|
-
await rememberPassphraseIfRequested(eligibility.
|
|
18202
|
+
await rememberPassphraseIfRequested(eligibility.access.syncKey, options, passphrase);
|
|
17143
18203
|
}
|
|
17144
18204
|
return {
|
|
17145
18205
|
mode: "pull",
|
|
@@ -17252,7 +18312,7 @@ async function ensureCliStorageReady() {
|
|
|
17252
18312
|
}
|
|
17253
18313
|
|
|
17254
18314
|
// src/app/main.ts
|
|
17255
|
-
var VERSION = true ? "5.1.
|
|
18315
|
+
var VERSION = true ? "5.1.5" : "0.0.0";
|
|
17256
18316
|
async function runCli() {
|
|
17257
18317
|
const args = process.argv.slice(2);
|
|
17258
18318
|
if (args.length === 0) {
|