sidekick-shared 0.27.5 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +58 -0
- package/README.md +93 -52
- package/dist/accountHealth.d.ts +14 -0
- package/dist/accountHealth.js +61 -14
- package/dist/accountLaunch.d.ts +6 -0
- package/dist/accountLaunch.js +61 -4
- package/dist/accountLease.d.ts +5 -0
- package/dist/accountLease.js +98 -0
- package/dist/accountManager.d.ts +7 -0
- package/dist/accountManager.js +226 -89
- package/dist/accountSync.d.ts +1 -5
- package/dist/accountSync.js +22 -6
- package/dist/accountSyncTypes.d.ts +9 -0
- package/dist/accounts.d.ts +75 -18
- package/dist/accounts.js +712 -189
- package/dist/browser.d.ts +13 -2
- package/dist/browser.js +30 -2
- package/dist/claudeCredentialDoctor.d.ts +50 -0
- package/dist/claudeCredentialDoctor.js +277 -0
- package/dist/claudeCredentialLedger.d.ts +15 -0
- package/dist/claudeCredentialLedger.js +130 -0
- package/dist/claudeCredentials.d.ts +20 -3
- package/dist/claudeCredentials.js +50 -3
- package/dist/claudeLiveGuard.d.ts +33 -0
- package/dist/claudeLiveGuard.js +46 -0
- package/dist/claudePaths.d.ts +11 -0
- package/dist/claudePaths.js +61 -0
- package/dist/claudeProfiles.d.ts +15 -8
- package/dist/claudeProfiles.js +30 -23
- package/dist/claudeRefreshLock.d.ts +15 -0
- package/dist/claudeRefreshLock.js +145 -0
- package/dist/codexAuth.d.ts +4 -0
- package/dist/codexAuth.js +18 -0
- package/dist/codexCredentialDoctor.d.ts +33 -0
- package/dist/codexCredentialDoctor.js +302 -0
- package/dist/codexCredentialStore.d.ts +29 -0
- package/dist/codexCredentialStore.js +213 -0
- package/dist/codexKeepAlive.d.ts +2 -0
- package/dist/codexKeepAlive.js +116 -0
- package/dist/codexPaths.d.ts +30 -2
- package/dist/codexPaths.js +118 -7
- package/dist/codexProfiles.d.ts +38 -35
- package/dist/codexProfiles.js +848 -715
- package/dist/credentialIO.d.ts +67 -7
- package/dist/credentialIO.js +211 -47
- package/dist/doctor.js +2 -2
- package/dist/ensureDefaultAccounts.js +1 -8
- package/dist/esm/accountChangeSignal.js +15 -0
- package/dist/esm/accountChanges.js +152 -0
- package/dist/esm/accountHealth.js +288 -0
- package/dist/esm/accountLaunch.js +145 -0
- package/dist/esm/accountLease.js +60 -0
- package/dist/esm/accountManager.js +606 -0
- package/dist/esm/accountRegistry.js +214 -0
- package/dist/esm/accountStatus.js +51 -0
- package/dist/esm/accountSwitch.js +74 -0
- package/dist/esm/accountSync.js +156 -0
- package/dist/esm/accountSyncTypes.js +4 -0
- package/dist/esm/accounts.js +1291 -0
- package/dist/esm/aggregation/EventAggregator.js +2034 -0
- package/dist/esm/aggregation/FrequencyTracker.js +69 -0
- package/dist/esm/aggregation/HeatmapTracker.js +89 -0
- package/dist/esm/aggregation/PatternExtractor.js +168 -0
- package/dist/esm/aggregation/costProvenance.js +31 -0
- package/dist/esm/aggregation/snapshot.js +161 -0
- package/dist/esm/aggregation/types.js +5 -0
- package/dist/esm/analytics/codeImpact.js +18 -0
- package/dist/esm/analytics/compactionLedger.js +37 -0
- package/dist/esm/analytics/qualityScore.js +112 -0
- package/dist/esm/autoSwitch.js +185 -0
- package/dist/esm/browser.js +34 -0
- package/dist/esm/claudeCredentialDoctor.js +239 -0
- package/dist/esm/claudeCredentialLedger.js +91 -0
- package/dist/esm/claudeCredentials.js +111 -0
- package/dist/esm/claudeLiveGuard.js +43 -0
- package/dist/esm/claudePaths.js +22 -0
- package/dist/esm/claudeProfiles.js +142 -0
- package/dist/esm/claudeRefreshLock.js +106 -0
- package/dist/esm/codexAuth.js +101 -0
- package/dist/esm/codexCredentialDoctor.js +265 -0
- package/dist/esm/codexCredentialStore.js +164 -0
- package/dist/esm/codexKeepAlive.js +112 -0
- package/dist/esm/codexPaths.js +172 -0
- package/dist/esm/codexProfiles.js +1251 -0
- package/dist/esm/codexQuota.js +564 -0
- package/dist/esm/codexQuotaWatcher.js +363 -0
- package/dist/esm/context/composer.js +71 -0
- package/dist/esm/context/sessionContext.js +546 -0
- package/dist/esm/credentialIO.js +283 -0
- package/dist/esm/credentials.js +49 -0
- package/dist/esm/doctor.js +305 -0
- package/dist/esm/ensureDefaultAccounts.js +62 -0
- package/dist/esm/errorHistory.js +56 -0
- package/dist/esm/extractors/errorTaxonomy.js +43 -0
- package/dist/esm/extractors/gatherAssets.js +32 -0
- package/dist/esm/extractors/sessionAssets.js +176 -0
- package/dist/esm/extractors/sources/claudeAssets.js +158 -0
- package/dist/esm/extractors/sources/codexAssets.js +234 -0
- package/dist/esm/extractors/tokenUsage.js +41 -0
- package/dist/esm/extractors/toolCall.js +142 -0
- package/dist/esm/failingTools.js +65 -0
- package/dist/esm/fileHead.js +27 -0
- package/dist/esm/formatters/eventHighlighter.js +332 -0
- package/dist/esm/formatters/noiseClassifier.js +209 -0
- package/dist/esm/formatters/sessionDump.js +328 -0
- package/dist/esm/formatters/toolSummary.js +226 -0
- package/dist/esm/formatting.js +82 -0
- package/dist/esm/handoffUrl.js +23 -0
- package/dist/esm/historicalStore.js +305 -0
- package/dist/esm/humanPrompt.js +88 -0
- package/dist/esm/index.js +213 -0
- package/dist/esm/keepAlive.js +189 -0
- package/dist/esm/liveCredentialGuard.js +33 -0
- package/dist/esm/modelAliases.js +51 -0
- package/dist/esm/modelCatalog.js +139 -0
- package/dist/esm/modelContext.js +280 -0
- package/dist/esm/modelInfo.js +832 -0
- package/dist/esm/multiProviderQuotaService.js +450 -0
- package/dist/esm/node.js +46 -0
- package/dist/esm/observedContextWindows.js +165 -0
- package/dist/esm/observedSessionCollector.js +939 -0
- package/dist/esm/parsers/changelogParser.js +71 -0
- package/dist/esm/parsers/codexHistory.js +89 -0
- package/dist/esm/parsers/codexParser.js +906 -0
- package/dist/esm/parsers/debugLogParser.js +126 -0
- package/dist/esm/parsers/jsonl.js +66 -0
- package/dist/esm/parsers/mcpToolName.js +28 -0
- package/dist/esm/parsers/openCodeParser.js +625 -0
- package/dist/esm/parsers/planExtractor.js +335 -0
- package/dist/esm/parsers/sessionActivityDetector.js +246 -0
- package/dist/esm/parsers/sessionPathResolver.js +713 -0
- package/dist/esm/parsers/subagentScanner.js +360 -0
- package/dist/esm/parsers/subagentStatsCache.js +45 -0
- package/dist/esm/parsers/subagentTraceParser.js +325 -0
- package/dist/esm/paths.js +134 -0
- package/dist/esm/peakHours.js +194 -0
- package/dist/esm/phrases.js +1330 -0
- package/dist/esm/pricingCatalog.js +247 -0
- package/dist/esm/processDetection.js +200 -0
- package/dist/esm/projectMigration.js +59 -0
- package/dist/esm/promptHistory.js +1221 -0
- package/dist/esm/providerFailure.js +362 -0
- package/dist/esm/providerIds.js +6 -0
- package/dist/esm/providerQuota.js +1 -0
- package/dist/esm/providerServiceStatus.js +145 -0
- package/dist/esm/providerServiceStatusTypes.js +1 -0
- package/dist/esm/providerStatus.js +117 -0
- package/dist/esm/providers/claudeCode.js +575 -0
- package/dist/esm/providers/claudeListing.js +121 -0
- package/dist/esm/providers/codex.js +1034 -0
- package/dist/esm/providers/codexDatabase.js +297 -0
- package/dist/esm/providers/codexRollouts.js +215 -0
- package/dist/esm/providers/detect.js +185 -0
- package/dist/esm/providers/diagnostics.js +83 -0
- package/dist/esm/providers/directoryListingCache.js +206 -0
- package/dist/esm/providers/factory.js +54 -0
- package/dist/esm/providers/openCode.js +2008 -0
- package/dist/esm/providers/openCodeDatabase.js +419 -0
- package/dist/esm/providers/rolloutWalker.js +252 -0
- package/dist/esm/providers/types.js +12 -0
- package/dist/esm/quota.js +182 -0
- package/dist/esm/quotaHistory.js +364 -0
- package/dist/esm/quotaPoller.js +230 -0
- package/dist/esm/quotaPresentation.js +97 -0
- package/dist/esm/quotaResolver.js +231 -0
- package/dist/esm/quotaSnapshots.js +130 -0
- package/dist/esm/quotaThresholds.js +85 -0
- package/dist/esm/readers/decisions.js +24 -0
- package/dist/esm/readers/handoff.js +20 -0
- package/dist/esm/readers/helpers.js +27 -0
- package/dist/esm/readers/history.js +9 -0
- package/dist/esm/readers/notes.js +48 -0
- package/dist/esm/readers/plans.js +225 -0
- package/dist/esm/readers/tasks.js +19 -0
- package/dist/esm/report/htmlHelpers.js +280 -0
- package/dist/esm/report/htmlReportGenerator.js +704 -0
- package/dist/esm/report/index.js +7 -0
- package/dist/esm/report/logo.js +2 -0
- package/dist/esm/report/openBrowser.js +19 -0
- package/dist/esm/report/sessionReportInputs.js +33 -0
- package/dist/esm/report/transcriptParser.js +83 -0
- package/dist/esm/report/types.js +4 -0
- package/dist/esm/schemas/accountManager.js +204 -0
- package/dist/esm/schemas/accountStatus.js +25 -0
- package/dist/esm/schemas/assistantTurn.js +74 -0
- package/dist/esm/schemas/index.js +25 -0
- package/dist/esm/schemas/observedSessionV1.js +77 -0
- package/dist/esm/schemas/providerFailure.js +37 -0
- package/dist/esm/schemas/quota.js +132 -0
- package/dist/esm/schemas/quotaHistory.js +40 -0
- package/dist/esm/schemas/sessionEvent.js +216 -0
- package/dist/esm/schemas/stateFile.js +75 -0
- package/dist/esm/schemas/tokenEstimation.js +7 -0
- package/dist/esm/schemas/transcript.js +86 -0
- package/dist/esm/schemas/usageNormalization.js +38 -0
- package/dist/esm/search/advancedFilter.js +197 -0
- package/dist/esm/search/sessionSearch.js +66 -0
- package/dist/esm/sessionFingerprint.js +40 -0
- package/dist/esm/sessionMonitor.js +198 -0
- package/dist/esm/sessionPreviews.js +522 -0
- package/dist/esm/sessionPromptHistory.js +254 -0
- package/dist/esm/sessionPromptSignals.js +364 -0
- package/dist/esm/sessionStats.js +160 -0
- package/dist/esm/sessionTokenTotals.js +98 -0
- package/dist/esm/sessionTranscripts.js +49 -0
- package/dist/esm/stateFile.js +119 -0
- package/dist/esm/statusline/BurnRateCalculator.js +40 -0
- package/dist/esm/statusline/claudeStatuslinePayload.js +148 -0
- package/dist/esm/statusline/formatter.js +122 -0
- package/dist/esm/statusline/index.js +8 -0
- package/dist/esm/terminalSync.js +95 -0
- package/dist/esm/testing/credentialGuard.js +197 -0
- package/dist/esm/testing/index.js +12 -0
- package/dist/esm/testing/sessionLogFixtures.js +128 -0
- package/dist/esm/tokenEstimation.js +67 -0
- package/dist/esm/tokenSummary.js +113 -0
- package/dist/esm/toolCategory.js +82 -0
- package/dist/esm/transcript.js +257 -0
- package/dist/esm/turns/assistantTurn.js +373 -0
- package/dist/esm/types/codex.js +23 -0
- package/dist/esm/types/decisionLog.js +5 -0
- package/dist/esm/types/historicalData.js +47 -0
- package/dist/esm/types/knowledgeNote.js +15 -0
- package/dist/esm/types/observedSessionV1.js +257 -0
- package/dist/esm/types/opencode.js +12 -0
- package/dist/esm/types/plan.js +7 -0
- package/dist/esm/types/sessionEvent.js +10 -0
- package/dist/esm/types/taskPersistence.js +12 -0
- package/dist/esm/usage/billingBlocks.js +148 -0
- package/dist/esm/usage/claudeUsageDedupe.js +189 -0
- package/dist/esm/usage/importSessionHistory.js +84 -0
- package/dist/esm/usage/subagentUsage.js +18 -0
- package/dist/esm/usage/usageEvents.js +402 -0
- package/dist/esm/usage/usageReports.js +202 -0
- package/dist/esm/usageNormalization.js +95 -0
- package/dist/esm/watchers/eventBridge.js +219 -0
- package/dist/esm/watchers/factory.js +63 -0
- package/dist/esm/watchers/jsonlTail.js +289 -0
- package/dist/esm/watchers/jsonlWatcher.js +435 -0
- package/dist/esm/watchers/providerReaderWatcher.js +118 -0
- package/dist/esm/watchers/sqliteWatcher.js +285 -0
- package/dist/esm/watchers/types.js +4 -0
- package/dist/esm/workspaceRoots.js +56 -0
- package/dist/esm/writers/atomic.js +375 -0
- package/dist/esm/writers/decisions.js +51 -0
- package/dist/esm/writers/notes.js +46 -0
- package/dist/esm/writers/tasks.js +63 -0
- package/dist/esm/zaiQuota.js +323 -0
- package/dist/esm/zaiQuotaApi.js +271 -0
- package/dist/esm/zaiQuotaWatcher.js +265 -0
- package/dist/fileHead.d.ts +22 -0
- package/dist/fileHead.js +33 -0
- package/dist/index.d.ts +30 -11
- package/dist/index.js +72 -15
- package/dist/keepAlive.d.ts +2 -1
- package/dist/keepAlive.js +116 -29
- package/dist/liveCredentialGuard.d.ts +28 -0
- package/dist/liveCredentialGuard.js +36 -0
- package/dist/node.d.ts +13 -1
- package/dist/node.js +43 -2
- package/dist/parsers/codexHistory.js +2 -2
- package/dist/parsers/codexParser.d.ts +12 -0
- package/dist/parsers/codexParser.js +34 -12
- package/dist/parsers/debugLogParser.js +2 -2
- package/dist/parsers/sessionPathResolver.js +5 -4
- package/dist/processDetection.d.ts +10 -3
- package/dist/processDetection.js +44 -11
- package/dist/promptHistory.d.ts +32 -0
- package/dist/promptHistory.js +115 -59
- package/dist/providerFailure.d.ts +12 -6
- package/dist/providerFailure.js +45 -0
- package/dist/providers/claudeCode.js +7 -99
- package/dist/providers/claudeListing.d.ts +22 -0
- package/dist/providers/claudeListing.js +158 -0
- package/dist/providers/codex.d.ts +3 -1
- package/dist/providers/codex.js +26 -227
- package/dist/providers/codexRollouts.d.ts +44 -0
- package/dist/providers/codexRollouts.js +261 -0
- package/dist/providers/detect.js +4 -4
- package/dist/readers/plans.js +3 -3
- package/dist/schemas/accountManager.d.ts +22 -0
- package/dist/schemas/accountManager.js +4 -0
- package/dist/schemas/assistantTurn.d.ts +2 -2
- package/dist/schemas/index.d.ts +2 -0
- package/dist/schemas/index.js +7 -2
- package/dist/schemas/providerFailure.d.ts +154 -0
- package/dist/schemas/providerFailure.js +40 -0
- package/dist/sessionPromptHistory.d.ts +12 -7
- package/dist/sessionPromptHistory.js +15 -3
- package/dist/sessionPromptSignals.d.ts +9 -0
- package/dist/sessionPromptSignals.js +13 -0
- package/dist/terminalSync.d.ts +12 -2
- package/dist/terminalSync.js +27 -2
- package/dist/testing/credentialGuard.d.ts +23 -0
- package/dist/testing/credentialGuard.js +233 -0
- package/dist/testing/index.d.ts +14 -0
- package/dist/testing/index.js +22 -0
- package/dist/testing/sessionLogFixtures.d.ts +28 -0
- package/dist/testing/sessionLogFixtures.js +169 -0
- package/dist/tokenEstimation.d.ts +2 -0
- package/dist/tokenEstimation.js +21 -11
- package/dist/toolCategory.d.ts +18 -0
- package/dist/toolCategory.js +86 -0
- package/dist/types/codex.d.ts +4 -0
- package/dist/usage/usageEvents.d.ts +16 -0
- package/dist/usage/usageEvents.js +53 -9
- package/dist/watchers/jsonlTail.d.ts +26 -1
- package/dist/watchers/jsonlTail.js +136 -28
- package/dist/watchers/jsonlWatcher.js +7 -0
- package/dist/watchers/providerReaderWatcher.js +1 -0
- package/dist/watchers/types.d.ts +6 -0
- package/dist/workspaceRoots.d.ts +23 -0
- package/dist/workspaceRoots.js +93 -0
- package/package.json +22 -2
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.inspectCodexCredentialStores = inspectCodexCredentialStores;
|
|
37
|
+
exports.repairCodexCredentialStores = repairCodexCredentialStores;
|
|
38
|
+
/** Inspection is read-only. Repairs touch only credential paths Sidekick owns. */
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const accountRegistry_1 = require("./accountRegistry");
|
|
42
|
+
const codexAuth_1 = require("./codexAuth");
|
|
43
|
+
const codexPaths_1 = require("./codexPaths");
|
|
44
|
+
const codexCredentialStore_1 = require("./codexCredentialStore");
|
|
45
|
+
const accountLease_1 = require("./accountLease");
|
|
46
|
+
const processDetection_1 = require("./processDetection");
|
|
47
|
+
const codexProfiles_1 = require("./codexProfiles");
|
|
48
|
+
function within(child, root) {
|
|
49
|
+
const resolved = (0, codexCredentialStore_1.canonicalCodexPath)(child), base = (0, codexCredentialStore_1.canonicalCodexPath)(root);
|
|
50
|
+
return resolved === base || resolved.startsWith(base + path.sep);
|
|
51
|
+
}
|
|
52
|
+
function ownedDirectories(root) {
|
|
53
|
+
try {
|
|
54
|
+
return fs
|
|
55
|
+
.readdirSync(root, { withFileTypes: true })
|
|
56
|
+
.filter((e) => e.isDirectory() && !e.isSymbolicLink())
|
|
57
|
+
.map((e) => path.join(root, e.name))
|
|
58
|
+
.filter((dir) => within(dir, root));
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function readFinding(f) {
|
|
65
|
+
if (!f.file)
|
|
66
|
+
return (0, codexCredentialStore_1.readCodexCredentialStore)(f.configDir);
|
|
67
|
+
try {
|
|
68
|
+
const raw = fs.readFileSync(f.file, 'utf8');
|
|
69
|
+
const legacy = path.basename(f.file).startsWith('credentials-');
|
|
70
|
+
const snapshot = { authRaw: legacy ? null : raw, legacyRaw: legacy ? raw : null };
|
|
71
|
+
return {
|
|
72
|
+
status: (legacy ? (0, codexCredentialStore_1.isLegacyCodexLogin)(raw) : (0, codexCredentialStore_1.isCodexLogin)(raw)) ? 'ok' : 'corrupt',
|
|
73
|
+
snapshot,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
return error.code === 'ENOENT'
|
|
78
|
+
? { status: 'absent', snapshot: { authRaw: null, legacyRaw: null } }
|
|
79
|
+
: { status: 'error' };
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function inspectCodexCredentialStores(options = {}) {
|
|
83
|
+
const now = options.now ?? Date.now();
|
|
84
|
+
const findings = [];
|
|
85
|
+
const live = (0, codexCredentialStore_1.readCodexCredentialStore)();
|
|
86
|
+
if (live.status === 'corrupt' || live.status === 'error')
|
|
87
|
+
findings.push({
|
|
88
|
+
kind: live.status === 'error' ? 'unreadable' : 'corrupt',
|
|
89
|
+
configDir: (0, codexPaths_1.getSystemCodexHome)(),
|
|
90
|
+
ownership: 'live',
|
|
91
|
+
detail: 'The live Codex login could not be read; it may be mid-write.',
|
|
92
|
+
action: 'Close Codex and inspect again; live data is never deleted by doctor.',
|
|
93
|
+
});
|
|
94
|
+
const profiles = (0, accountRegistry_1.listSavedAccountProfiles)('codex');
|
|
95
|
+
for (const profile of profiles) {
|
|
96
|
+
const backup = (0, codexCredentialStore_1.readCodexBackup)(profile.id);
|
|
97
|
+
if (backup.status === 'error' || backup.status === 'corrupt')
|
|
98
|
+
findings.push({
|
|
99
|
+
kind: backup.status === 'error' ? 'unreadable' : 'corrupt',
|
|
100
|
+
ownership: 'backup',
|
|
101
|
+
accountId: profile.id,
|
|
102
|
+
email: profile.email,
|
|
103
|
+
configDir: path.dirname((0, codexPaths_1.getCodexBackupPath)(profile.id)),
|
|
104
|
+
file: (0, codexPaths_1.getCodexBackupPath)(profile.id),
|
|
105
|
+
detail: 'The private Codex backup could not be read safely.',
|
|
106
|
+
action: 'Inspect the backup or sign in again; unreadable credentials are preserved.',
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
const candidates = [];
|
|
110
|
+
for (const root of [(0, codexPaths_1.getCodexProfilesDir)(), (0, codexPaths_1.getCodexLoginsDir)()]) {
|
|
111
|
+
for (const dir of ownedDirectories(root)) {
|
|
112
|
+
const pending = path.join(dir, 'profile.json');
|
|
113
|
+
if (fs.existsSync(pending)) {
|
|
114
|
+
try {
|
|
115
|
+
const at = Date.parse(JSON.parse(fs.readFileSync(pending, 'utf8')).addedAt);
|
|
116
|
+
if (!Number.isFinite(at) || now - at < 24 * 60 * 60 * 1000)
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const home = path.join(dir, 'codex-home');
|
|
124
|
+
if (within(home, root) &&
|
|
125
|
+
(0, codexCredentialStore_1.canonicalCodexPath)(home) !== (0, codexCredentialStore_1.canonicalCodexPath)((0, codexPaths_1.getSystemCodexHome)())) {
|
|
126
|
+
candidates.push({
|
|
127
|
+
configDir: home,
|
|
128
|
+
ownership: root === (0, codexPaths_1.getCodexProfilesDir)() ? 'profile-home' : 'login-home',
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const stash = path.join((0, accountRegistry_1.getAccountsDir)(), 'codex', 'stash');
|
|
134
|
+
for (const dir of ownedDirectories(stash)) {
|
|
135
|
+
const home = path.join(dir, 'codex-home');
|
|
136
|
+
if (within(home, stash))
|
|
137
|
+
candidates.push({ configDir: home, ownership: 'stash' });
|
|
138
|
+
}
|
|
139
|
+
try {
|
|
140
|
+
for (const entry of fs.readdirSync(stash, { withFileTypes: true })) {
|
|
141
|
+
if (entry.isFile() && /^(auth|credentials)-.*\.json$/.test(entry.name))
|
|
142
|
+
candidates.push({
|
|
143
|
+
configDir: stash,
|
|
144
|
+
file: path.join(stash, entry.name),
|
|
145
|
+
ownership: 'stash',
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
/* no old stash */
|
|
151
|
+
}
|
|
152
|
+
for (const candidate of candidates) {
|
|
153
|
+
const store = readFinding(candidate);
|
|
154
|
+
if (store.status === 'absent')
|
|
155
|
+
continue;
|
|
156
|
+
const identity = store.status === 'ok' ? (0, codexAuth_1.readAuthIdentityFromRaw)(store.snapshot.authRaw) : null;
|
|
157
|
+
const profile = profiles.find((p) => (0, codexAuth_1.identitiesMatch)(identity, {
|
|
158
|
+
email: p.email ?? p.metadata?.email,
|
|
159
|
+
workspaceId: p.metadata?.workspaceId,
|
|
160
|
+
})) ??
|
|
161
|
+
profiles.find((p) => (0, codexCredentialStore_1.canonicalCodexPath)((0, codexPaths_1.getCodexProfileHome)(p.id)) === (0, codexCredentialStore_1.canonicalCodexPath)(candidate.configDir));
|
|
162
|
+
const base = { ...candidate, accountId: profile?.id, email: identity?.email ?? profile?.email };
|
|
163
|
+
if (store.status === 'error' || store.status === 'corrupt') {
|
|
164
|
+
findings.push({
|
|
165
|
+
...base,
|
|
166
|
+
kind: store.status === 'error' ? 'unreadable' : 'corrupt',
|
|
167
|
+
detail: 'This credential store could not be read safely.',
|
|
168
|
+
action: 'Inspect or sign in again; unreadable credentials are preserved.',
|
|
169
|
+
});
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
const fingerprint = (0, codexCredentialStore_1.codexSnapshotFingerprint)(store.snapshot);
|
|
173
|
+
const liveDuplicate = live.status === 'ok' &&
|
|
174
|
+
((0, codexAuth_1.identitiesMatch)(identity, (0, codexAuth_1.readAuthIdentityFromRaw)(live.snapshot.authRaw)) ||
|
|
175
|
+
fingerprint === (0, codexCredentialStore_1.codexSnapshotFingerprint)(live.snapshot));
|
|
176
|
+
if (candidate.ownership === 'stash')
|
|
177
|
+
findings.push({
|
|
178
|
+
...base,
|
|
179
|
+
fingerprint,
|
|
180
|
+
kind: 'stale-stash',
|
|
181
|
+
detail: 'An old stash holds another account credential.',
|
|
182
|
+
action: 'Preserve the freshest known login privately, then remove this credential.',
|
|
183
|
+
});
|
|
184
|
+
else if (liveDuplicate)
|
|
185
|
+
findings.push({
|
|
186
|
+
...base,
|
|
187
|
+
fingerprint,
|
|
188
|
+
kind: 'live-duplicate',
|
|
189
|
+
detail: 'This home holds a second usable copy of the live Codex login.',
|
|
190
|
+
action: 'Repair a newer login first, then remove the duplicate when Codex is stopped.',
|
|
191
|
+
});
|
|
192
|
+
else if (!profile || candidate.ownership === 'login-home')
|
|
193
|
+
findings.push({
|
|
194
|
+
...base,
|
|
195
|
+
fingerprint,
|
|
196
|
+
kind: 'orphan',
|
|
197
|
+
detail: 'An abandoned home holds a login outside a saved profile.',
|
|
198
|
+
action: 'Preserve it under its known account before removing it; unknown identities need manual review.',
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
if (fs.existsSync(path.join((0, accountRegistry_1.getAccountsDir)(), 'codex', 'switch-journal.json')))
|
|
202
|
+
findings.push({
|
|
203
|
+
kind: 'pending-switch',
|
|
204
|
+
ownership: 'journal',
|
|
205
|
+
configDir: path.join((0, accountRegistry_1.getAccountsDir)(), 'codex'),
|
|
206
|
+
detail: 'An interrupted account switch needs reconciliation.',
|
|
207
|
+
action: 'Close Codex and finish recovery from the live fingerprint.',
|
|
208
|
+
});
|
|
209
|
+
return { findings, homesChecked: candidates.length + 1 };
|
|
210
|
+
}
|
|
211
|
+
function repairCodexCredentialStores(options = {}) {
|
|
212
|
+
const inspection = inspectCodexCredentialStores(options);
|
|
213
|
+
const result = {
|
|
214
|
+
...inspection,
|
|
215
|
+
dryRun: options.dryRun ?? true,
|
|
216
|
+
repaired: [],
|
|
217
|
+
};
|
|
218
|
+
if (result.dryRun)
|
|
219
|
+
return result;
|
|
220
|
+
const scan = (0, processDetection_1.inspectRunningAccountConsumersSync)('codex');
|
|
221
|
+
const mode = (0, codexPaths_1.getCodexCredentialStoreMode)();
|
|
222
|
+
if (!scan.ok || scan.consumers.length || (mode !== 'file' && mode !== 'unknown')) {
|
|
223
|
+
result.repaired = inspection.findings.map((finding) => ({
|
|
224
|
+
finding,
|
|
225
|
+
ok: false,
|
|
226
|
+
error: 'Repair requires stopped Codex consumers, a successful process scan, and readable file-mode configuration.',
|
|
227
|
+
}));
|
|
228
|
+
return result;
|
|
229
|
+
}
|
|
230
|
+
(0, codexProfiles_1.withCodexAuthSwapLock)(() => {
|
|
231
|
+
result.sync = (0, codexProfiles_1.syncCodexLiveStateUnlocked)('migration', { scan });
|
|
232
|
+
for (const finding of inspection.findings) {
|
|
233
|
+
let error;
|
|
234
|
+
try {
|
|
235
|
+
if (finding.kind === 'pending-switch') {
|
|
236
|
+
if (fs.existsSync(path.join(finding.configDir, 'switch-journal.json')))
|
|
237
|
+
throw new Error();
|
|
238
|
+
}
|
|
239
|
+
else if (finding.kind === 'live-duplicate' && finding.ownership === 'profile-home') {
|
|
240
|
+
if (readFinding(finding).status !== 'absent')
|
|
241
|
+
throw new Error();
|
|
242
|
+
}
|
|
243
|
+
else if (finding.kind === 'stale-stash' ||
|
|
244
|
+
finding.kind === 'orphan' ||
|
|
245
|
+
finding.kind === 'live-duplicate') {
|
|
246
|
+
if (!within(finding.file ?? finding.configDir, path.join((0, accountRegistry_1.getAccountsDir)(), 'codex')) ||
|
|
247
|
+
(0, codexCredentialStore_1.canonicalCodexPath)(finding.configDir) === (0, codexCredentialStore_1.canonicalCodexPath)((0, codexPaths_1.getSystemCodexHome)()))
|
|
248
|
+
throw new Error();
|
|
249
|
+
const current = readFinding(finding);
|
|
250
|
+
if (current.status === 'absent') {
|
|
251
|
+
result.repaired.push({ finding, ok: true });
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
if (current.status !== 'ok' ||
|
|
255
|
+
(0, codexCredentialStore_1.codexSnapshotFingerprint)(current.snapshot) !== finding.fingerprint ||
|
|
256
|
+
!finding.accountId ||
|
|
257
|
+
(0, accountLease_1.isAccountLeaseHeld)('codex', finding.accountId))
|
|
258
|
+
throw new Error();
|
|
259
|
+
const backup = (0, codexCredentialStore_1.readCodexBackup)(finding.accountId);
|
|
260
|
+
if (backup.status === 'ok' &&
|
|
261
|
+
(0, codexAuth_1.compareCodexAuthFreshness)(backup.snapshot.authRaw, current.snapshot.authRaw) === 0 &&
|
|
262
|
+
(0, codexCredentialStore_1.codexSnapshotFingerprint)(backup.snapshot) !==
|
|
263
|
+
(0, codexCredentialStore_1.codexSnapshotFingerprint)(current.snapshot) &&
|
|
264
|
+
(!(0, codexAuth_1.codexRefreshFingerprint)(backup.snapshot.authRaw) ||
|
|
265
|
+
(0, codexAuth_1.codexRefreshFingerprint)(backup.snapshot.authRaw) !==
|
|
266
|
+
(0, codexAuth_1.codexRefreshFingerprint)(current.snapshot.authRaw))) {
|
|
267
|
+
throw new Error(); // Two incomparable chains: deletion would guess which is usable.
|
|
268
|
+
}
|
|
269
|
+
const chosen = backup.status === 'ok' &&
|
|
270
|
+
(0, codexAuth_1.compareCodexAuthFreshness)(backup.snapshot.authRaw, current.snapshot.authRaw) >= 0
|
|
271
|
+
? backup.snapshot
|
|
272
|
+
: current.snapshot;
|
|
273
|
+
(0, codexCredentialStore_1.writeCodexBackup)(finding.accountId, chosen);
|
|
274
|
+
const live = (0, codexCredentialStore_1.readCodexCredentialStore)();
|
|
275
|
+
if (live.status === 'ok' &&
|
|
276
|
+
(0, codexAuth_1.identitiesMatch)((0, codexAuth_1.readAuthIdentityFromRaw)(live.snapshot.authRaw), (0, codexAuth_1.readAuthIdentityFromRaw)(chosen.authRaw)) &&
|
|
277
|
+
(0, codexAuth_1.compareCodexAuthFreshness)(chosen.authRaw, live.snapshot.authRaw) > 0) {
|
|
278
|
+
result.sync = (0, codexProfiles_1.syncCodexLiveStateUnlocked)('migration', { scan });
|
|
279
|
+
const repairedLive = (0, codexCredentialStore_1.readCodexCredentialStore)();
|
|
280
|
+
if (repairedLive.status !== 'ok' ||
|
|
281
|
+
(0, codexAuth_1.compareCodexAuthFreshness)(repairedLive.snapshot.authRaw, chosen.authRaw) < 0)
|
|
282
|
+
throw new Error();
|
|
283
|
+
}
|
|
284
|
+
if (finding.file)
|
|
285
|
+
fs.rmSync(finding.file);
|
|
286
|
+
else
|
|
287
|
+
(0, codexCredentialStore_1.removeCodexProfileCredentials)(finding.configDir, current.snapshot);
|
|
288
|
+
if (finding.ownership === 'login-home')
|
|
289
|
+
fs.rmSync(path.dirname(finding.configDir), { recursive: true, force: true });
|
|
290
|
+
}
|
|
291
|
+
else
|
|
292
|
+
throw new Error();
|
|
293
|
+
}
|
|
294
|
+
catch {
|
|
295
|
+
error =
|
|
296
|
+
'Preserved for review: unreadable, ambiguous, changed, leased, or not safely repairable yet.';
|
|
297
|
+
}
|
|
298
|
+
result.repaired.push({ finding, ok: !error, error });
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
return result;
|
|
302
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export interface CodexAuthSnapshot {
|
|
2
|
+
authRaw: string | null;
|
|
3
|
+
/** Includes unrelated MCP data when reading a home; writers preserve it. */
|
|
4
|
+
legacyRaw: string | null;
|
|
5
|
+
}
|
|
6
|
+
export type CodexCredentialStoreRead = {
|
|
7
|
+
status: 'ok' | 'absent' | 'corrupt';
|
|
8
|
+
snapshot: CodexAuthSnapshot;
|
|
9
|
+
} | {
|
|
10
|
+
status: 'error';
|
|
11
|
+
};
|
|
12
|
+
export declare function isLegacyCodexLogin(raw: string | null): boolean;
|
|
13
|
+
export declare function isCodexLogin(raw: string | null): boolean;
|
|
14
|
+
export declare function codexLoginOnly(snapshot: CodexAuthSnapshot): CodexAuthSnapshot;
|
|
15
|
+
export declare function readCodexCredentialStore(home?: string): CodexCredentialStoreRead;
|
|
16
|
+
export declare function readCodexBackup(id: string): CodexCredentialStoreRead;
|
|
17
|
+
export declare function codexSnapshotFingerprint(snapshot: CodexAuthSnapshot): string;
|
|
18
|
+
/** The whole snapshot, including absence, must agree before destructive operations. */
|
|
19
|
+
export declare function codexStoreMatches(read: CodexCredentialStoreRead, snapshot: CodexAuthSnapshot): boolean;
|
|
20
|
+
export declare function writeCodexBackup(id: string, snapshot: CodexAuthSnapshot): void;
|
|
21
|
+
/** Resolve existing parents too, so nonexistent children of a symlink cannot bypass ownership. */
|
|
22
|
+
export declare function canonicalCodexPath(value: string): string;
|
|
23
|
+
export declare function isSidekickCodexHome(home: string): boolean;
|
|
24
|
+
/** Install only account data; an unrelated MCP credential file is never copied or deleted. */
|
|
25
|
+
export declare function codexInstalledSnapshot(before: CodexAuthSnapshot, candidate: CodexAuthSnapshot): CodexAuthSnapshot;
|
|
26
|
+
export declare function writeInactiveCodexCredentialStore(home: string, candidate: CodexAuthSnapshot): void;
|
|
27
|
+
/** Internal capability: only the guarded installer in codexProfiles may call this. */
|
|
28
|
+
export declare function writeLiveCodexCredentialStoreUnguarded(snapshot: CodexAuthSnapshot): void;
|
|
29
|
+
export declare function removeCodexProfileCredentials(home: string, expected: CodexAuthSnapshot): void;
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.isLegacyCodexLogin = isLegacyCodexLogin;
|
|
37
|
+
exports.isCodexLogin = isCodexLogin;
|
|
38
|
+
exports.codexLoginOnly = codexLoginOnly;
|
|
39
|
+
exports.readCodexCredentialStore = readCodexCredentialStore;
|
|
40
|
+
exports.readCodexBackup = readCodexBackup;
|
|
41
|
+
exports.codexSnapshotFingerprint = codexSnapshotFingerprint;
|
|
42
|
+
exports.codexStoreMatches = codexStoreMatches;
|
|
43
|
+
exports.writeCodexBackup = writeCodexBackup;
|
|
44
|
+
exports.canonicalCodexPath = canonicalCodexPath;
|
|
45
|
+
exports.isSidekickCodexHome = isSidekickCodexHome;
|
|
46
|
+
exports.codexInstalledSnapshot = codexInstalledSnapshot;
|
|
47
|
+
exports.writeInactiveCodexCredentialStore = writeInactiveCodexCredentialStore;
|
|
48
|
+
exports.writeLiveCodexCredentialStoreUnguarded = writeLiveCodexCredentialStoreUnguarded;
|
|
49
|
+
exports.removeCodexProfileCredentials = removeCodexProfileCredentials;
|
|
50
|
+
/** Raw credential I/O. Backups are never Codex homes and never launchable. */
|
|
51
|
+
const fs = __importStar(require("fs"));
|
|
52
|
+
const path = __importStar(require("path"));
|
|
53
|
+
const crypto_1 = require("crypto");
|
|
54
|
+
const accountRegistry_1 = require("./accountRegistry");
|
|
55
|
+
const codexPaths_1 = require("./codexPaths");
|
|
56
|
+
const codexAuth_1 = require("./codexAuth");
|
|
57
|
+
const atomic_1 = require("./writers/atomic");
|
|
58
|
+
function isLegacyCodexLogin(raw) {
|
|
59
|
+
const value = (0, codexAuth_1.parseAuthJson)(raw);
|
|
60
|
+
return Boolean(value &&
|
|
61
|
+
['OPENAI_API_KEY', 'CODEX_API_KEY'].some((key) => typeof value[key] === 'string' && value[key].length > 0));
|
|
62
|
+
}
|
|
63
|
+
function isCodexLogin(raw) {
|
|
64
|
+
const parsed = (0, codexAuth_1.parseAuthJson)(raw);
|
|
65
|
+
if (!parsed || Array.isArray(parsed))
|
|
66
|
+
return false;
|
|
67
|
+
if (typeof parsed.OPENAI_API_KEY === 'string' && parsed.OPENAI_API_KEY)
|
|
68
|
+
return true;
|
|
69
|
+
return Boolean(typeof parsed.tokens?.access_token === 'string' &&
|
|
70
|
+
parsed.tokens.access_token &&
|
|
71
|
+
typeof parsed.tokens.refresh_token === 'string' &&
|
|
72
|
+
parsed.tokens.refresh_token);
|
|
73
|
+
}
|
|
74
|
+
function codexLoginOnly(snapshot) {
|
|
75
|
+
return {
|
|
76
|
+
authRaw: snapshot.authRaw,
|
|
77
|
+
legacyRaw: isLegacyCodexLogin(snapshot.legacyRaw) ? snapshot.legacyRaw : null,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function readRaw(file) {
|
|
81
|
+
try {
|
|
82
|
+
return fs.readFileSync(file, 'utf8');
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
if (error.code === 'ENOENT')
|
|
86
|
+
return null;
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function readFiles(auth, legacy) {
|
|
91
|
+
try {
|
|
92
|
+
const snapshot = { authRaw: readRaw(auth), legacyRaw: readRaw(legacy) };
|
|
93
|
+
const status = snapshot.authRaw !== null
|
|
94
|
+
? isCodexLogin(snapshot.authRaw)
|
|
95
|
+
? 'ok'
|
|
96
|
+
: 'corrupt'
|
|
97
|
+
: isLegacyCodexLogin(snapshot.legacyRaw)
|
|
98
|
+
? 'ok'
|
|
99
|
+
: 'absent';
|
|
100
|
+
return { status, snapshot };
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
return { status: 'error' };
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function readCodexCredentialStore(home = (0, codexPaths_1.getSystemCodexHome)()) {
|
|
107
|
+
return readFiles(path.join(home, 'auth.json'), path.join(home, '.credentials.json'));
|
|
108
|
+
}
|
|
109
|
+
function readCodexBackup(id) {
|
|
110
|
+
return readFiles((0, codexPaths_1.getCodexBackupPath)(id), (0, codexPaths_1.getCodexBackupPath)(id, true));
|
|
111
|
+
}
|
|
112
|
+
function codexSnapshotFingerprint(snapshot) {
|
|
113
|
+
return (0, crypto_1.createHash)('sha256').update(JSON.stringify(snapshot)).digest('hex');
|
|
114
|
+
}
|
|
115
|
+
/** The whole snapshot, including absence, must agree before destructive operations. */
|
|
116
|
+
function codexStoreMatches(read, snapshot) {
|
|
117
|
+
return (read.status !== 'error' &&
|
|
118
|
+
codexSnapshotFingerprint(read.snapshot) === codexSnapshotFingerprint(snapshot));
|
|
119
|
+
}
|
|
120
|
+
function writeCodexBackup(id, snapshot) {
|
|
121
|
+
const login = codexLoginOnly(snapshot);
|
|
122
|
+
if (!isCodexLogin(login.authRaw) && !isLegacyCodexLogin(login.legacyRaw)) {
|
|
123
|
+
throw new Error('Refusing to back up an invalid Codex login.');
|
|
124
|
+
}
|
|
125
|
+
for (const [file, raw] of [
|
|
126
|
+
[(0, codexPaths_1.getCodexBackupPath)(id), login.authRaw],
|
|
127
|
+
[(0, codexPaths_1.getCodexBackupPath)(id, true), login.legacyRaw],
|
|
128
|
+
]) {
|
|
129
|
+
if (raw !== null)
|
|
130
|
+
(0, atomic_1.atomicWriteFileSync)(file, raw);
|
|
131
|
+
else
|
|
132
|
+
fs.rmSync(file, { force: true });
|
|
133
|
+
}
|
|
134
|
+
if (!codexStoreMatches(readCodexBackup(id), login))
|
|
135
|
+
throw new Error('The private Codex backup did not verify.');
|
|
136
|
+
}
|
|
137
|
+
/** Resolve existing parents too, so nonexistent children of a symlink cannot bypass ownership. */
|
|
138
|
+
function canonicalCodexPath(value) {
|
|
139
|
+
const resolved = path.resolve(value);
|
|
140
|
+
try {
|
|
141
|
+
return fs.realpathSync(resolved);
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
const parent = path.dirname(resolved);
|
|
145
|
+
return parent === resolved
|
|
146
|
+
? resolved
|
|
147
|
+
: path.join(canonicalCodexPath(parent), path.basename(resolved));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
function isSidekickCodexHome(home) {
|
|
151
|
+
const root = canonicalCodexPath(path.join((0, accountRegistry_1.getAccountsDir)(), 'codex'));
|
|
152
|
+
const resolved = canonicalCodexPath(home);
|
|
153
|
+
return resolved === root || resolved.startsWith(root + path.sep);
|
|
154
|
+
}
|
|
155
|
+
/** Install only account data; an unrelated MCP credential file is never copied or deleted. */
|
|
156
|
+
function codexInstalledSnapshot(before, candidate) {
|
|
157
|
+
const login = codexLoginOnly(candidate);
|
|
158
|
+
if (login.legacyRaw && before.legacyRaw && !isLegacyCodexLogin(before.legacyRaw)) {
|
|
159
|
+
throw new Error('Legacy Codex credentials would replace MCP data; sign in with Codex again.');
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
authRaw: login.authRaw,
|
|
163
|
+
legacyRaw: before.legacyRaw && !isLegacyCodexLogin(before.legacyRaw)
|
|
164
|
+
? before.legacyRaw
|
|
165
|
+
: login.legacyRaw,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
function writeFiles(home, snapshot) {
|
|
169
|
+
for (const [name, raw] of [
|
|
170
|
+
['auth.json', snapshot.authRaw],
|
|
171
|
+
['.credentials.json', snapshot.legacyRaw],
|
|
172
|
+
]) {
|
|
173
|
+
const file = path.join(home, name);
|
|
174
|
+
// Do not rewrite even identical MCP data; its own refresh writer owns it.
|
|
175
|
+
if (readRaw(file) === raw)
|
|
176
|
+
continue;
|
|
177
|
+
if (raw === null)
|
|
178
|
+
fs.rmSync(file, { force: true });
|
|
179
|
+
else
|
|
180
|
+
(0, atomic_1.atomicWriteFileSync)(file, raw);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
function writeInactiveCodexCredentialStore(home, candidate) {
|
|
184
|
+
if (canonicalCodexPath(home) === canonicalCodexPath((0, codexPaths_1.getSystemCodexHome)())) {
|
|
185
|
+
throw new Error('A profile credential writer cannot write the live Codex home.');
|
|
186
|
+
}
|
|
187
|
+
const before = readCodexCredentialStore(home);
|
|
188
|
+
if (before.status === 'error')
|
|
189
|
+
throw new Error('The Codex profile could not be read.');
|
|
190
|
+
const expected = codexInstalledSnapshot(before.snapshot, candidate);
|
|
191
|
+
writeFiles(home, expected);
|
|
192
|
+
if (!codexStoreMatches(readCodexCredentialStore(home), expected))
|
|
193
|
+
throw new Error('The Codex profile write did not verify.');
|
|
194
|
+
}
|
|
195
|
+
/** Internal capability: only the guarded installer in codexProfiles may call this. */
|
|
196
|
+
function writeLiveCodexCredentialStoreUnguarded(snapshot) {
|
|
197
|
+
const home = (0, codexPaths_1.getSystemCodexHome)();
|
|
198
|
+
if (isSidekickCodexHome(home))
|
|
199
|
+
throw new Error('Unset CODEX_HOME before changing the live login; it names a Sidekick-owned home.');
|
|
200
|
+
writeFiles(home, snapshot);
|
|
201
|
+
}
|
|
202
|
+
function removeCodexProfileCredentials(home, expected) {
|
|
203
|
+
if (canonicalCodexPath(home) === canonicalCodexPath((0, codexPaths_1.getSystemCodexHome)()))
|
|
204
|
+
throw new Error('Refusing to remove live Codex credentials.');
|
|
205
|
+
if (!codexStoreMatches(readCodexCredentialStore(home), expected))
|
|
206
|
+
throw new Error('The Codex profile changed; its credential was kept.');
|
|
207
|
+
if (expected.authRaw !== null)
|
|
208
|
+
fs.rmSync(path.join(home, 'auth.json'), { force: true });
|
|
209
|
+
if (isLegacyCodexLogin(expected.legacyRaw))
|
|
210
|
+
fs.rmSync(path.join(home, '.credentials.json'), { force: true });
|
|
211
|
+
if (readCodexCredentialStore(home).status !== 'absent')
|
|
212
|
+
throw new Error('The Codex profile credential could not be removed.');
|
|
213
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runCodexKeepAlive = void 0;
|
|
4
|
+
/** A bounded, inference-free official CLI refresh. Never expose child output. */
|
|
5
|
+
const child_process_1 = require("child_process");
|
|
6
|
+
const runCodexKeepAlive = (command, args, env, timeoutMs) => new Promise((resolve) => {
|
|
7
|
+
let child;
|
|
8
|
+
try {
|
|
9
|
+
child = (0, child_process_1.spawn)(command, args, {
|
|
10
|
+
env,
|
|
11
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
12
|
+
windowsHide: true,
|
|
13
|
+
cwd: env.CODEX_HOME,
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
resolve({ status: null, stdout: '' });
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
let buffer = '';
|
|
21
|
+
let bytes = 0;
|
|
22
|
+
let initialized = false;
|
|
23
|
+
let result = null;
|
|
24
|
+
let finishing = false;
|
|
25
|
+
let settled = false;
|
|
26
|
+
let killTimer;
|
|
27
|
+
const timer = setTimeout(() => finish(null), timeoutMs);
|
|
28
|
+
function finish(status) {
|
|
29
|
+
if (finishing)
|
|
30
|
+
return;
|
|
31
|
+
finishing = true;
|
|
32
|
+
result = status;
|
|
33
|
+
child.stdin?.end();
|
|
34
|
+
// The lease stays held until the child exits, including timeout/error paths.
|
|
35
|
+
killTimer = setTimeout(() => child.kill('SIGKILL'), Math.min(1000, timeoutMs));
|
|
36
|
+
if (status !== 0)
|
|
37
|
+
child.kill('SIGKILL');
|
|
38
|
+
}
|
|
39
|
+
function settle() {
|
|
40
|
+
if (settled)
|
|
41
|
+
return;
|
|
42
|
+
settled = true;
|
|
43
|
+
clearTimeout(timer);
|
|
44
|
+
if (killTimer)
|
|
45
|
+
clearTimeout(killTimer);
|
|
46
|
+
child.stdin?.destroy();
|
|
47
|
+
child.stdout?.destroy();
|
|
48
|
+
child.stderr?.destroy();
|
|
49
|
+
resolve({ status: result, stdout: '' });
|
|
50
|
+
}
|
|
51
|
+
const send = (message) => {
|
|
52
|
+
if (!child.stdin?.writable) {
|
|
53
|
+
finish(null);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
child.stdin.write(JSON.stringify(message) + '\n');
|
|
57
|
+
};
|
|
58
|
+
child.once('error', settle);
|
|
59
|
+
child.once('close', settle);
|
|
60
|
+
child.once('exit', settle);
|
|
61
|
+
child.stdin?.on('error', () => finish(null));
|
|
62
|
+
child.stderr?.on('data', () => {
|
|
63
|
+
/* Drain, never log possibly sensitive diagnostics. */
|
|
64
|
+
});
|
|
65
|
+
child.stdout?.setEncoding('utf8');
|
|
66
|
+
child.stdout?.on('data', (chunk) => {
|
|
67
|
+
if (finishing)
|
|
68
|
+
return;
|
|
69
|
+
bytes += Buffer.byteLength(chunk);
|
|
70
|
+
if (bytes > 1024 * 1024) {
|
|
71
|
+
finish(null);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
buffer += chunk;
|
|
75
|
+
let newline;
|
|
76
|
+
while (!finishing && (newline = buffer.indexOf('\n')) >= 0) {
|
|
77
|
+
const line = buffer.slice(0, newline);
|
|
78
|
+
buffer = buffer.slice(newline + 1);
|
|
79
|
+
if (!line.trim())
|
|
80
|
+
continue;
|
|
81
|
+
let message;
|
|
82
|
+
try {
|
|
83
|
+
message = JSON.parse(line);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
finish(null);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (!message || typeof message !== 'object') {
|
|
90
|
+
finish(null);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
if (message.id === 0 && !initialized) {
|
|
94
|
+
if (message.error || !message.result) {
|
|
95
|
+
finish(1);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
initialized = true;
|
|
99
|
+
send({ method: 'initialized', params: {} });
|
|
100
|
+
send({ method: 'account/read', id: 1, params: { refreshToken: true } });
|
|
101
|
+
}
|
|
102
|
+
else if (message.id === 1 && initialized) {
|
|
103
|
+
finish(!message.error && message.result?.account?.type === 'chatgpt' ? 0 : 1);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
send({
|
|
108
|
+
method: 'initialize',
|
|
109
|
+
id: 0,
|
|
110
|
+
params: {
|
|
111
|
+
clientInfo: { name: 'sidekick_agent_hub', title: 'Sidekick Agent Hub', version: '1' },
|
|
112
|
+
capabilities: null,
|
|
113
|
+
},
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
exports.runCodexKeepAlive = runCodexKeepAlive;
|