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.
Files changed (314) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/README.md +93 -52
  3. package/dist/accountHealth.d.ts +14 -0
  4. package/dist/accountHealth.js +61 -14
  5. package/dist/accountLaunch.d.ts +6 -0
  6. package/dist/accountLaunch.js +61 -4
  7. package/dist/accountLease.d.ts +5 -0
  8. package/dist/accountLease.js +98 -0
  9. package/dist/accountManager.d.ts +7 -0
  10. package/dist/accountManager.js +226 -89
  11. package/dist/accountSync.d.ts +1 -5
  12. package/dist/accountSync.js +22 -6
  13. package/dist/accountSyncTypes.d.ts +9 -0
  14. package/dist/accounts.d.ts +75 -18
  15. package/dist/accounts.js +712 -189
  16. package/dist/browser.d.ts +13 -2
  17. package/dist/browser.js +30 -2
  18. package/dist/claudeCredentialDoctor.d.ts +50 -0
  19. package/dist/claudeCredentialDoctor.js +277 -0
  20. package/dist/claudeCredentialLedger.d.ts +15 -0
  21. package/dist/claudeCredentialLedger.js +130 -0
  22. package/dist/claudeCredentials.d.ts +20 -3
  23. package/dist/claudeCredentials.js +50 -3
  24. package/dist/claudeLiveGuard.d.ts +33 -0
  25. package/dist/claudeLiveGuard.js +46 -0
  26. package/dist/claudePaths.d.ts +11 -0
  27. package/dist/claudePaths.js +61 -0
  28. package/dist/claudeProfiles.d.ts +15 -8
  29. package/dist/claudeProfiles.js +30 -23
  30. package/dist/claudeRefreshLock.d.ts +15 -0
  31. package/dist/claudeRefreshLock.js +145 -0
  32. package/dist/codexAuth.d.ts +4 -0
  33. package/dist/codexAuth.js +18 -0
  34. package/dist/codexCredentialDoctor.d.ts +33 -0
  35. package/dist/codexCredentialDoctor.js +302 -0
  36. package/dist/codexCredentialStore.d.ts +29 -0
  37. package/dist/codexCredentialStore.js +213 -0
  38. package/dist/codexKeepAlive.d.ts +2 -0
  39. package/dist/codexKeepAlive.js +116 -0
  40. package/dist/codexPaths.d.ts +30 -2
  41. package/dist/codexPaths.js +118 -7
  42. package/dist/codexProfiles.d.ts +38 -35
  43. package/dist/codexProfiles.js +848 -715
  44. package/dist/credentialIO.d.ts +67 -7
  45. package/dist/credentialIO.js +211 -47
  46. package/dist/doctor.js +2 -2
  47. package/dist/ensureDefaultAccounts.js +1 -8
  48. package/dist/esm/accountChangeSignal.js +15 -0
  49. package/dist/esm/accountChanges.js +152 -0
  50. package/dist/esm/accountHealth.js +288 -0
  51. package/dist/esm/accountLaunch.js +145 -0
  52. package/dist/esm/accountLease.js +60 -0
  53. package/dist/esm/accountManager.js +606 -0
  54. package/dist/esm/accountRegistry.js +214 -0
  55. package/dist/esm/accountStatus.js +51 -0
  56. package/dist/esm/accountSwitch.js +74 -0
  57. package/dist/esm/accountSync.js +156 -0
  58. package/dist/esm/accountSyncTypes.js +4 -0
  59. package/dist/esm/accounts.js +1291 -0
  60. package/dist/esm/aggregation/EventAggregator.js +2034 -0
  61. package/dist/esm/aggregation/FrequencyTracker.js +69 -0
  62. package/dist/esm/aggregation/HeatmapTracker.js +89 -0
  63. package/dist/esm/aggregation/PatternExtractor.js +168 -0
  64. package/dist/esm/aggregation/costProvenance.js +31 -0
  65. package/dist/esm/aggregation/snapshot.js +161 -0
  66. package/dist/esm/aggregation/types.js +5 -0
  67. package/dist/esm/analytics/codeImpact.js +18 -0
  68. package/dist/esm/analytics/compactionLedger.js +37 -0
  69. package/dist/esm/analytics/qualityScore.js +112 -0
  70. package/dist/esm/autoSwitch.js +185 -0
  71. package/dist/esm/browser.js +34 -0
  72. package/dist/esm/claudeCredentialDoctor.js +239 -0
  73. package/dist/esm/claudeCredentialLedger.js +91 -0
  74. package/dist/esm/claudeCredentials.js +111 -0
  75. package/dist/esm/claudeLiveGuard.js +43 -0
  76. package/dist/esm/claudePaths.js +22 -0
  77. package/dist/esm/claudeProfiles.js +142 -0
  78. package/dist/esm/claudeRefreshLock.js +106 -0
  79. package/dist/esm/codexAuth.js +101 -0
  80. package/dist/esm/codexCredentialDoctor.js +265 -0
  81. package/dist/esm/codexCredentialStore.js +164 -0
  82. package/dist/esm/codexKeepAlive.js +112 -0
  83. package/dist/esm/codexPaths.js +172 -0
  84. package/dist/esm/codexProfiles.js +1251 -0
  85. package/dist/esm/codexQuota.js +564 -0
  86. package/dist/esm/codexQuotaWatcher.js +363 -0
  87. package/dist/esm/context/composer.js +71 -0
  88. package/dist/esm/context/sessionContext.js +546 -0
  89. package/dist/esm/credentialIO.js +283 -0
  90. package/dist/esm/credentials.js +49 -0
  91. package/dist/esm/doctor.js +305 -0
  92. package/dist/esm/ensureDefaultAccounts.js +62 -0
  93. package/dist/esm/errorHistory.js +56 -0
  94. package/dist/esm/extractors/errorTaxonomy.js +43 -0
  95. package/dist/esm/extractors/gatherAssets.js +32 -0
  96. package/dist/esm/extractors/sessionAssets.js +176 -0
  97. package/dist/esm/extractors/sources/claudeAssets.js +158 -0
  98. package/dist/esm/extractors/sources/codexAssets.js +234 -0
  99. package/dist/esm/extractors/tokenUsage.js +41 -0
  100. package/dist/esm/extractors/toolCall.js +142 -0
  101. package/dist/esm/failingTools.js +65 -0
  102. package/dist/esm/fileHead.js +27 -0
  103. package/dist/esm/formatters/eventHighlighter.js +332 -0
  104. package/dist/esm/formatters/noiseClassifier.js +209 -0
  105. package/dist/esm/formatters/sessionDump.js +328 -0
  106. package/dist/esm/formatters/toolSummary.js +226 -0
  107. package/dist/esm/formatting.js +82 -0
  108. package/dist/esm/handoffUrl.js +23 -0
  109. package/dist/esm/historicalStore.js +305 -0
  110. package/dist/esm/humanPrompt.js +88 -0
  111. package/dist/esm/index.js +213 -0
  112. package/dist/esm/keepAlive.js +189 -0
  113. package/dist/esm/liveCredentialGuard.js +33 -0
  114. package/dist/esm/modelAliases.js +51 -0
  115. package/dist/esm/modelCatalog.js +139 -0
  116. package/dist/esm/modelContext.js +280 -0
  117. package/dist/esm/modelInfo.js +832 -0
  118. package/dist/esm/multiProviderQuotaService.js +450 -0
  119. package/dist/esm/node.js +46 -0
  120. package/dist/esm/observedContextWindows.js +165 -0
  121. package/dist/esm/observedSessionCollector.js +939 -0
  122. package/dist/esm/parsers/changelogParser.js +71 -0
  123. package/dist/esm/parsers/codexHistory.js +89 -0
  124. package/dist/esm/parsers/codexParser.js +906 -0
  125. package/dist/esm/parsers/debugLogParser.js +126 -0
  126. package/dist/esm/parsers/jsonl.js +66 -0
  127. package/dist/esm/parsers/mcpToolName.js +28 -0
  128. package/dist/esm/parsers/openCodeParser.js +625 -0
  129. package/dist/esm/parsers/planExtractor.js +335 -0
  130. package/dist/esm/parsers/sessionActivityDetector.js +246 -0
  131. package/dist/esm/parsers/sessionPathResolver.js +713 -0
  132. package/dist/esm/parsers/subagentScanner.js +360 -0
  133. package/dist/esm/parsers/subagentStatsCache.js +45 -0
  134. package/dist/esm/parsers/subagentTraceParser.js +325 -0
  135. package/dist/esm/paths.js +134 -0
  136. package/dist/esm/peakHours.js +194 -0
  137. package/dist/esm/phrases.js +1330 -0
  138. package/dist/esm/pricingCatalog.js +247 -0
  139. package/dist/esm/processDetection.js +200 -0
  140. package/dist/esm/projectMigration.js +59 -0
  141. package/dist/esm/promptHistory.js +1221 -0
  142. package/dist/esm/providerFailure.js +362 -0
  143. package/dist/esm/providerIds.js +6 -0
  144. package/dist/esm/providerQuota.js +1 -0
  145. package/dist/esm/providerServiceStatus.js +145 -0
  146. package/dist/esm/providerServiceStatusTypes.js +1 -0
  147. package/dist/esm/providerStatus.js +117 -0
  148. package/dist/esm/providers/claudeCode.js +575 -0
  149. package/dist/esm/providers/claudeListing.js +121 -0
  150. package/dist/esm/providers/codex.js +1034 -0
  151. package/dist/esm/providers/codexDatabase.js +297 -0
  152. package/dist/esm/providers/codexRollouts.js +215 -0
  153. package/dist/esm/providers/detect.js +185 -0
  154. package/dist/esm/providers/diagnostics.js +83 -0
  155. package/dist/esm/providers/directoryListingCache.js +206 -0
  156. package/dist/esm/providers/factory.js +54 -0
  157. package/dist/esm/providers/openCode.js +2008 -0
  158. package/dist/esm/providers/openCodeDatabase.js +419 -0
  159. package/dist/esm/providers/rolloutWalker.js +252 -0
  160. package/dist/esm/providers/types.js +12 -0
  161. package/dist/esm/quota.js +182 -0
  162. package/dist/esm/quotaHistory.js +364 -0
  163. package/dist/esm/quotaPoller.js +230 -0
  164. package/dist/esm/quotaPresentation.js +97 -0
  165. package/dist/esm/quotaResolver.js +231 -0
  166. package/dist/esm/quotaSnapshots.js +130 -0
  167. package/dist/esm/quotaThresholds.js +85 -0
  168. package/dist/esm/readers/decisions.js +24 -0
  169. package/dist/esm/readers/handoff.js +20 -0
  170. package/dist/esm/readers/helpers.js +27 -0
  171. package/dist/esm/readers/history.js +9 -0
  172. package/dist/esm/readers/notes.js +48 -0
  173. package/dist/esm/readers/plans.js +225 -0
  174. package/dist/esm/readers/tasks.js +19 -0
  175. package/dist/esm/report/htmlHelpers.js +280 -0
  176. package/dist/esm/report/htmlReportGenerator.js +704 -0
  177. package/dist/esm/report/index.js +7 -0
  178. package/dist/esm/report/logo.js +2 -0
  179. package/dist/esm/report/openBrowser.js +19 -0
  180. package/dist/esm/report/sessionReportInputs.js +33 -0
  181. package/dist/esm/report/transcriptParser.js +83 -0
  182. package/dist/esm/report/types.js +4 -0
  183. package/dist/esm/schemas/accountManager.js +204 -0
  184. package/dist/esm/schemas/accountStatus.js +25 -0
  185. package/dist/esm/schemas/assistantTurn.js +74 -0
  186. package/dist/esm/schemas/index.js +25 -0
  187. package/dist/esm/schemas/observedSessionV1.js +77 -0
  188. package/dist/esm/schemas/providerFailure.js +37 -0
  189. package/dist/esm/schemas/quota.js +132 -0
  190. package/dist/esm/schemas/quotaHistory.js +40 -0
  191. package/dist/esm/schemas/sessionEvent.js +216 -0
  192. package/dist/esm/schemas/stateFile.js +75 -0
  193. package/dist/esm/schemas/tokenEstimation.js +7 -0
  194. package/dist/esm/schemas/transcript.js +86 -0
  195. package/dist/esm/schemas/usageNormalization.js +38 -0
  196. package/dist/esm/search/advancedFilter.js +197 -0
  197. package/dist/esm/search/sessionSearch.js +66 -0
  198. package/dist/esm/sessionFingerprint.js +40 -0
  199. package/dist/esm/sessionMonitor.js +198 -0
  200. package/dist/esm/sessionPreviews.js +522 -0
  201. package/dist/esm/sessionPromptHistory.js +254 -0
  202. package/dist/esm/sessionPromptSignals.js +364 -0
  203. package/dist/esm/sessionStats.js +160 -0
  204. package/dist/esm/sessionTokenTotals.js +98 -0
  205. package/dist/esm/sessionTranscripts.js +49 -0
  206. package/dist/esm/stateFile.js +119 -0
  207. package/dist/esm/statusline/BurnRateCalculator.js +40 -0
  208. package/dist/esm/statusline/claudeStatuslinePayload.js +148 -0
  209. package/dist/esm/statusline/formatter.js +122 -0
  210. package/dist/esm/statusline/index.js +8 -0
  211. package/dist/esm/terminalSync.js +95 -0
  212. package/dist/esm/testing/credentialGuard.js +197 -0
  213. package/dist/esm/testing/index.js +12 -0
  214. package/dist/esm/testing/sessionLogFixtures.js +128 -0
  215. package/dist/esm/tokenEstimation.js +67 -0
  216. package/dist/esm/tokenSummary.js +113 -0
  217. package/dist/esm/toolCategory.js +82 -0
  218. package/dist/esm/transcript.js +257 -0
  219. package/dist/esm/turns/assistantTurn.js +373 -0
  220. package/dist/esm/types/codex.js +23 -0
  221. package/dist/esm/types/decisionLog.js +5 -0
  222. package/dist/esm/types/historicalData.js +47 -0
  223. package/dist/esm/types/knowledgeNote.js +15 -0
  224. package/dist/esm/types/observedSessionV1.js +257 -0
  225. package/dist/esm/types/opencode.js +12 -0
  226. package/dist/esm/types/plan.js +7 -0
  227. package/dist/esm/types/sessionEvent.js +10 -0
  228. package/dist/esm/types/taskPersistence.js +12 -0
  229. package/dist/esm/usage/billingBlocks.js +148 -0
  230. package/dist/esm/usage/claudeUsageDedupe.js +189 -0
  231. package/dist/esm/usage/importSessionHistory.js +84 -0
  232. package/dist/esm/usage/subagentUsage.js +18 -0
  233. package/dist/esm/usage/usageEvents.js +402 -0
  234. package/dist/esm/usage/usageReports.js +202 -0
  235. package/dist/esm/usageNormalization.js +95 -0
  236. package/dist/esm/watchers/eventBridge.js +219 -0
  237. package/dist/esm/watchers/factory.js +63 -0
  238. package/dist/esm/watchers/jsonlTail.js +289 -0
  239. package/dist/esm/watchers/jsonlWatcher.js +435 -0
  240. package/dist/esm/watchers/providerReaderWatcher.js +118 -0
  241. package/dist/esm/watchers/sqliteWatcher.js +285 -0
  242. package/dist/esm/watchers/types.js +4 -0
  243. package/dist/esm/workspaceRoots.js +56 -0
  244. package/dist/esm/writers/atomic.js +375 -0
  245. package/dist/esm/writers/decisions.js +51 -0
  246. package/dist/esm/writers/notes.js +46 -0
  247. package/dist/esm/writers/tasks.js +63 -0
  248. package/dist/esm/zaiQuota.js +323 -0
  249. package/dist/esm/zaiQuotaApi.js +271 -0
  250. package/dist/esm/zaiQuotaWatcher.js +265 -0
  251. package/dist/fileHead.d.ts +22 -0
  252. package/dist/fileHead.js +33 -0
  253. package/dist/index.d.ts +30 -11
  254. package/dist/index.js +72 -15
  255. package/dist/keepAlive.d.ts +2 -1
  256. package/dist/keepAlive.js +116 -29
  257. package/dist/liveCredentialGuard.d.ts +28 -0
  258. package/dist/liveCredentialGuard.js +36 -0
  259. package/dist/node.d.ts +13 -1
  260. package/dist/node.js +43 -2
  261. package/dist/parsers/codexHistory.js +2 -2
  262. package/dist/parsers/codexParser.d.ts +12 -0
  263. package/dist/parsers/codexParser.js +34 -12
  264. package/dist/parsers/debugLogParser.js +2 -2
  265. package/dist/parsers/sessionPathResolver.js +5 -4
  266. package/dist/processDetection.d.ts +10 -3
  267. package/dist/processDetection.js +44 -11
  268. package/dist/promptHistory.d.ts +32 -0
  269. package/dist/promptHistory.js +115 -59
  270. package/dist/providerFailure.d.ts +12 -6
  271. package/dist/providerFailure.js +45 -0
  272. package/dist/providers/claudeCode.js +7 -99
  273. package/dist/providers/claudeListing.d.ts +22 -0
  274. package/dist/providers/claudeListing.js +158 -0
  275. package/dist/providers/codex.d.ts +3 -1
  276. package/dist/providers/codex.js +26 -227
  277. package/dist/providers/codexRollouts.d.ts +44 -0
  278. package/dist/providers/codexRollouts.js +261 -0
  279. package/dist/providers/detect.js +4 -4
  280. package/dist/readers/plans.js +3 -3
  281. package/dist/schemas/accountManager.d.ts +22 -0
  282. package/dist/schemas/accountManager.js +4 -0
  283. package/dist/schemas/assistantTurn.d.ts +2 -2
  284. package/dist/schemas/index.d.ts +2 -0
  285. package/dist/schemas/index.js +7 -2
  286. package/dist/schemas/providerFailure.d.ts +154 -0
  287. package/dist/schemas/providerFailure.js +40 -0
  288. package/dist/sessionPromptHistory.d.ts +12 -7
  289. package/dist/sessionPromptHistory.js +15 -3
  290. package/dist/sessionPromptSignals.d.ts +9 -0
  291. package/dist/sessionPromptSignals.js +13 -0
  292. package/dist/terminalSync.d.ts +12 -2
  293. package/dist/terminalSync.js +27 -2
  294. package/dist/testing/credentialGuard.d.ts +23 -0
  295. package/dist/testing/credentialGuard.js +233 -0
  296. package/dist/testing/index.d.ts +14 -0
  297. package/dist/testing/index.js +22 -0
  298. package/dist/testing/sessionLogFixtures.d.ts +28 -0
  299. package/dist/testing/sessionLogFixtures.js +169 -0
  300. package/dist/tokenEstimation.d.ts +2 -0
  301. package/dist/tokenEstimation.js +21 -11
  302. package/dist/toolCategory.d.ts +18 -0
  303. package/dist/toolCategory.js +86 -0
  304. package/dist/types/codex.d.ts +4 -0
  305. package/dist/usage/usageEvents.d.ts +16 -0
  306. package/dist/usage/usageEvents.js +53 -9
  307. package/dist/watchers/jsonlTail.d.ts +26 -1
  308. package/dist/watchers/jsonlTail.js +136 -28
  309. package/dist/watchers/jsonlWatcher.js +7 -0
  310. package/dist/watchers/providerReaderWatcher.js +1 -0
  311. package/dist/watchers/types.d.ts +6 -0
  312. package/dist/workspaceRoots.d.ts +23 -0
  313. package/dist/workspaceRoots.js +93 -0
  314. package/package.json +22 -2
@@ -39,6 +39,7 @@ exports.claudeHealthSidecarFromBlob = claudeHealthSidecarFromBlob;
39
39
  exports.writeClaudeHealthSidecar = writeClaudeHealthSidecar;
40
40
  exports.codexHealthSidecarFromAuth = codexHealthSidecarFromAuth;
41
41
  exports.writeCodexHealthSidecar = writeCodexHealthSidecar;
42
+ exports.readEffectiveClaudeCredential = readEffectiveClaudeCredential;
42
43
  exports.getAccountHealth = getAccountHealth;
43
44
  exports.listAccountsWithHealth = listAccountsWithHealth;
44
45
  exports.describeClaudeCredential = describeClaudeCredential;
@@ -55,6 +56,7 @@ const credentialIO_1 = require("./credentialIO");
55
56
  const codexAuth_1 = require("./codexAuth");
56
57
  const codexPaths_1 = require("./codexPaths");
57
58
  const accountRegistry_1 = require("./accountRegistry");
59
+ const codexCredentialStore_1 = require("./codexCredentialStore");
58
60
  const atomic_1 = require("./writers/atomic");
59
61
  /** Refresh tokens that expire within this window count as `expiring`. */
60
62
  const REFRESH_EXPIRY_WARNING_MS = 3 * 24 * 60 * 60 * 1000;
@@ -93,6 +95,7 @@ function claudeHealthSidecarFromBlob(blob, source, now = Date.now()) {
93
95
  refreshExpiryEstimated: refresh?.estimated,
94
96
  hasRefreshToken: Boolean(credentials.refreshToken),
95
97
  authMode: 'oauth',
98
+ refreshFp: (0, claudeCredentials_1.claudeRefreshFingerprint)(credentials) ?? undefined,
96
99
  source,
97
100
  };
98
101
  }
@@ -114,7 +117,8 @@ function codexHealthSidecarFromAuth(authRaw, source, now = Date.now(), fallbackP
114
117
  capturedAt: now,
115
118
  accessExpiresAt: (0, codexAuth_1.readAccessTokenExpiry)(authRaw) ?? undefined,
116
119
  lastRefreshAt,
117
- hasRefreshToken: identity.authMode === 'chatgpt',
120
+ hasRefreshToken: (0, codexAuth_1.codexRefreshFingerprint)(authRaw) !== null,
121
+ refreshFp: (0, codexAuth_1.codexRefreshFingerprint)(authRaw) ?? undefined,
118
122
  authMode: identity.authMode,
119
123
  source,
120
124
  };
@@ -185,15 +189,54 @@ function isLiveCodexAccount(profile) {
185
189
  workspaceId: profile.metadata?.workspaceId,
186
190
  });
187
191
  }
188
- function probeClaudeStore(accountId, now) {
189
- const blob = (0, credentialIO_1.readActiveCredentials)((0, claudeProfiles_1.getClaudeProfileHome)(accountId));
190
- return blob ? writeClaudeHealthSidecar(accountId, blob, 'switch', now) : null;
192
+ function readClaudeBackupBlob(accountId) {
193
+ try {
194
+ return JSON.parse(fs.readFileSync((0, claudeProfiles_1.getClaudeCredentialBackupPath)(accountId), 'utf8'));
195
+ }
196
+ catch {
197
+ return null;
198
+ }
191
199
  }
192
- function probeCodexStore(accountId, now) {
193
- const authPath = path.join((0, codexPaths_1.getCodexProfileHome)(accountId), 'auth.json');
194
- const raw = (0, codexPaths_1.readFileOrNull)(authPath);
195
- const sidecar = codexHealthSidecarFromAuth(raw, 'switch', now, authPath);
196
- if (sidecar)
200
+ /**
201
+ * The credential a Claude profile would use right now: the live store for the
202
+ * live account (its profile home is empty by design), otherwise the fresher of
203
+ * the profile-home store and Sidekick's backup.
204
+ */
205
+ function readEffectiveClaudeCredential(accountId) {
206
+ if (isLiveClaudeAccount(accountId))
207
+ return (0, credentialIO_1.readActiveCredentials)();
208
+ const store = (0, credentialIO_1.readClaudeCredentialStore)((0, claudeProfiles_1.getClaudeProfileHome)(accountId));
209
+ const fromStore = store.status === 'ok' ? store.blob : null;
210
+ const backup = readClaudeBackupBlob(accountId);
211
+ const storeFreshness = (0, claudeCredentials_1.claudeCredentialFreshness)((0, claudeCredentials_1.parseClaudeCredentialBlob)(fromStore));
212
+ const backupFreshness = (0, claudeCredentials_1.claudeCredentialFreshness)((0, claudeCredentials_1.parseClaudeCredentialBlob)(backup));
213
+ if ((0, claudeCredentials_1.parseClaudeCredentialBlob)(backup) && backupFreshness > storeFreshness)
214
+ return backup;
215
+ return (0, claudeCredentials_1.parseClaudeCredentialBlob)(fromStore) ? fromStore : null;
216
+ }
217
+ function probeClaudeStore(accountId, now, persist = true) {
218
+ const blob = readEffectiveClaudeCredential(accountId);
219
+ if (!blob)
220
+ return null;
221
+ // A probe reads; it must not rewrite provenance (keep-alive relies on it).
222
+ const source = readHealthSidecar('claude-code', accountId)?.source ??
223
+ (isLiveClaudeAccount(accountId) ? 'live-sync' : 'migration');
224
+ return persist
225
+ ? writeClaudeHealthSidecar(accountId, blob, source, now)
226
+ : claudeHealthSidecarFromBlob(blob, source, now);
227
+ }
228
+ function probeCodexStore(accountId, now, persist = true) {
229
+ const profile = (0, accountRegistry_1.listSavedAccountProfiles)('codex').find((p) => p.id === accountId);
230
+ const live = profile !== undefined && isLiveCodexAccount(profile);
231
+ const home = (0, codexCredentialStore_1.readCodexCredentialStore)(live ? (0, codexPaths_1.getSystemCodexHome)() : (0, codexPaths_1.getCodexProfileHome)(accountId));
232
+ const backup = live ? null : (0, codexCredentialStore_1.readCodexBackup)(accountId);
233
+ let raw = home.status === 'ok' ? home.snapshot.authRaw : null;
234
+ if (backup?.status === 'ok' &&
235
+ (!raw || (0, codexAuth_1.compareCodexAuthFreshness)(backup.snapshot.authRaw, raw) > 0))
236
+ raw = backup.snapshot.authRaw;
237
+ const source = readHealthSidecar('codex', accountId)?.source ?? (live ? 'live-sync' : 'migration');
238
+ const sidecar = codexHealthSidecarFromAuth(raw, source, now);
239
+ if (sidecar && persist)
197
240
  writeSidecar('codex', accountId, sidecar);
198
241
  return sidecar;
199
242
  }
@@ -201,12 +244,14 @@ function hasStoredClaudeCredential(accountId) {
201
244
  const home = (0, claudeProfiles_1.getClaudeProfileHome)(accountId);
202
245
  return (fs.existsSync(path.join(home, '.credentials.json')) ||
203
246
  fs.existsSync(path.join((0, claudeProfiles_1.getClaudeProfileDir)(accountId), 'health.json')) ||
204
- fs.existsSync(path.join((0, accountRegistry_1.getAccountsDir)(), 'credentials', `${accountId}.credentials.json`)) ||
247
+ fs.existsSync((0, claudeProfiles_1.getClaudeCredentialBackupPath)(accountId)) ||
205
248
  (process.platform === 'darwin' && fs.existsSync(path.join(home, '.claude.json'))));
206
249
  }
207
250
  function hasStoredCodexCredential(accountId) {
208
251
  const home = (0, codexPaths_1.getCodexProfileHome)(accountId);
209
- return (fs.existsSync(path.join(home, 'auth.json')) ||
252
+ return (fs.existsSync((0, codexPaths_1.getCodexBackupPath)(accountId)) ||
253
+ fs.existsSync((0, codexPaths_1.getCodexBackupPath)(accountId, true)) ||
254
+ fs.existsSync(path.join(home, 'auth.json')) ||
210
255
  fs.existsSync(path.join(home, '.credentials.json')));
211
256
  }
212
257
  /**
@@ -224,17 +269,19 @@ function getAccountHealth(provider, accountId, options = {}) {
224
269
  let sidecar = null;
225
270
  if (probe === 'store') {
226
271
  sidecar =
227
- provider === 'codex' ? probeCodexStore(accountId, now) : probeClaudeStore(accountId, now);
272
+ provider === 'codex'
273
+ ? probeCodexStore(accountId, now, options.persist)
274
+ : probeClaudeStore(accountId, now, options.persist);
228
275
  }
229
276
  sidecar ??= readHealthSidecar(provider, accountId);
230
277
  if (!sidecar && provider === 'codex') {
231
278
  // Codex sidecars are cheap to derive from the plaintext file.
232
- sidecar = probeCodexStore(accountId, now);
279
+ sidecar = probeCodexStore(accountId, now, options.persist);
233
280
  }
234
281
  if (!sidecar && probe === 'auto' && hasStoredClaudeCredential(accountId)) {
235
282
  // First look at a profile that predates the health model: read its store
236
283
  // once and record the sidecar so later reads are cache hits.
237
- sidecar = probeClaudeStore(accountId, now);
284
+ sidecar = probeClaudeStore(accountId, now, options.persist);
238
285
  }
239
286
  const base = { providerId: provider, accountId, checkedAt: now, isLive };
240
287
  if (!sidecar) {
@@ -26,5 +26,11 @@ export interface AccountLaunchOptions {
26
26
  materialize?: boolean;
27
27
  /** Launch even when the stored credential is reported expired. */
28
28
  force?: boolean;
29
+ /**
30
+ * Claude: when the profile home has no credential but its backup does,
31
+ * restore it there first (default true). Keep-alive passes false: it never
32
+ * restores backups.
33
+ */
34
+ restoreBackup?: boolean;
29
35
  }
30
36
  export declare function getAccountLaunchEnv(provider: AccountProviderId, accountId: string, options?: AccountLaunchOptions): AccountLaunchEnv;
@@ -44,6 +44,9 @@ const path = __importStar(require("path"));
44
44
  const accountRegistry_1 = require("./accountRegistry");
45
45
  const accountHealth_1 = require("./accountHealth");
46
46
  const claudeProfiles_1 = require("./claudeProfiles");
47
+ const accounts_1 = require("./accounts");
48
+ const codexCredentialStore_1 = require("./codexCredentialStore");
49
+ const codexProfiles_1 = require("./codexProfiles");
47
50
  const codexPaths_1 = require("./codexPaths");
48
51
  const atomic_1 = require("./writers/atomic");
49
52
  function materializeClaudeHome(home) {
@@ -70,10 +73,11 @@ function materializeClaudeHome(home) {
70
73
  function materializeCodexHome(home) {
71
74
  fs.mkdirSync(home, { recursive: true, mode: 0o700 });
72
75
  const configTarget = path.join(home, 'config.toml');
73
- if (!fs.existsSync(configTarget)) {
74
- const source = (0, codexPaths_1.readFileOrNull)(path.join((0, codexPaths_1.getSystemCodexHome)(), 'config.toml')) ?? '';
75
- (0, atomic_1.atomicWriteFileSync)(configTarget, (0, codexPaths_1.forceFileCredentialStore)(source));
76
- }
76
+ const current = (0, codexPaths_1.readFileOrNull)(configTarget);
77
+ const source = current ?? (0, codexPaths_1.readFileOrNull)(path.join((0, codexPaths_1.getSystemCodexHome)(), 'config.toml')) ?? '';
78
+ const fileMode = (0, codexPaths_1.forceFileCredentialStore)(source);
79
+ if (fileMode !== current)
80
+ (0, atomic_1.atomicWriteFileSync)(configTarget, fileMode);
77
81
  }
78
82
  function getAccountLaunchEnv(provider, accountId, options = {}) {
79
83
  const profile = (0, accountRegistry_1.listSavedAccountProfiles)(provider).find((p) => p.id === accountId);
@@ -98,6 +102,40 @@ function getAccountLaunchEnv(provider, accountId, options = {}) {
98
102
  if (!profile) {
99
103
  return { ...base, error: `${provider} account ${accountId} not found.` };
100
104
  }
105
+ if (provider === 'codex' && (0, codexCredentialStore_1.isSidekickCodexHome)((0, codexPaths_1.getSystemCodexHome)())) {
106
+ return {
107
+ ...base,
108
+ error: 'Unset CODEX_HOME before managing accounts; it names a Sidekick-owned home.',
109
+ };
110
+ }
111
+ if (provider === 'codex' && !['file', 'unknown'].includes((0, codexPaths_1.getCodexCredentialStoreMode)())) {
112
+ return { ...base, error: 'Codex account launches require readable file-mode credentials.' };
113
+ }
114
+ if (provider === 'codex' && (0, codexProfiles_1.isLiveCodexProfile)(profile)) {
115
+ const liveHome = (0, codexPaths_1.getSystemCodexHome)();
116
+ return {
117
+ ...base,
118
+ home: liveHome,
119
+ env: { CODEX_HOME: liveHome },
120
+ warnings: ['This account is the live login, so the session uses the normal Codex home.'],
121
+ };
122
+ }
123
+ if (provider === 'claude-code' && health.isLive) {
124
+ // The live account's only CLI-usable copy is the live store; a session in
125
+ // its profile home would be a second copy racing it over the refresh
126
+ // token. Point the session at the live home instead.
127
+ const liveHome = (0, claudeProfiles_1.getLiveClaudeHome)();
128
+ const customHome = !(0, claudeProfiles_1.isDefaultClaudeHome)();
129
+ return {
130
+ ...base,
131
+ home: liveHome,
132
+ env: customHome ? { CLAUDE_CONFIG_DIR: liveHome } : {},
133
+ envUnset: customHome
134
+ ? [claudeProfiles_1.CLAUDE_SECURESTORAGE_CONFIG_DIR_ENV]
135
+ : ['CLAUDE_CONFIG_DIR', claudeProfiles_1.CLAUDE_SECURESTORAGE_CONFIG_DIR_ENV],
136
+ warnings: ['This account is the live login, so the session uses the normal Claude home.'],
137
+ };
138
+ }
101
139
  if ((health.state === 'expired' || health.state === 'missing') && !options.force) {
102
140
  return {
103
141
  ...base,
@@ -109,6 +147,25 @@ function getAccountLaunchEnv(provider, accountId, options = {}) {
109
147
  }
110
148
  if (health.state === 'expiring' && health.reason)
111
149
  warnings.push(health.reason);
150
+ if (provider === 'claude-code' && options.restoreBackup !== false) {
151
+ if ((0, accounts_1.restoreClaudeProfileFromBackup)(accountId) === 'restored') {
152
+ warnings.push("Restored this account's saved login into its profile home. If a session elsewhere still uses this login, close it first: two copies of one login invalidate each other.");
153
+ }
154
+ }
155
+ if (provider === 'codex') {
156
+ const restored = (0, codexProfiles_1.restoreCodexProfileFromBackup)(accountId, {
157
+ restore: options.restoreBackup !== false,
158
+ });
159
+ if (restored === 'none')
160
+ return { ...base, error: 'No safe inactive Codex credential is available; sign in again.' };
161
+ if (restored === 'live')
162
+ return { ...base, home: (0, codexPaths_1.getSystemCodexHome)(), env: { CODEX_HOME: (0, codexPaths_1.getSystemCodexHome)() } };
163
+ }
164
+ if (provider === 'codex' &&
165
+ options.materialize === false &&
166
+ !['file', 'unknown'].includes((0, codexPaths_1.getCodexCredentialStoreMode)(home))) {
167
+ return { ...base, error: 'The isolated Codex home must use file credential storage.' };
168
+ }
112
169
  if (options.materialize !== false) {
113
170
  try {
114
171
  if (provider === 'codex')
@@ -0,0 +1,5 @@
1
+ import type { AccountProviderId } from './accountRegistry';
2
+ export declare function isAccountLeaseHeld(provider: AccountProviderId, accountId: string, now?: number): boolean;
3
+ /** Take the lease; the caller holds the provider's swap lock. False when held elsewhere. */
4
+ export declare function acquireAccountLease(provider: AccountProviderId, accountId: string, durationMs: number, now?: number): boolean;
5
+ export declare function releaseAccountLease(provider: AccountProviderId, accountId: string): void;
@@ -0,0 +1,98 @@
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.isAccountLeaseHeld = isAccountLeaseHeld;
37
+ exports.acquireAccountLease = acquireAccountLease;
38
+ exports.releaseAccountLease = releaseAccountLease;
39
+ /**
40
+ * Keep-alive leases: while keep-alive runs a provider CLI inside a profile
41
+ * home, that home's credential may be mid-refresh. A switch to that account
42
+ * would copy the pre-refresh token into the live store just as the CLI kills
43
+ * it, so switches refuse a leased target. Leases are acquired under the
44
+ * provider's swap lock (the same lock a switch holds), expire on their own,
45
+ * and are ignored once their process is gone.
46
+ */
47
+ const fs = __importStar(require("fs"));
48
+ const path = __importStar(require("path"));
49
+ const claudeProfiles_1 = require("./claudeProfiles");
50
+ const codexPaths_1 = require("./codexPaths");
51
+ const atomic_1 = require("./writers/atomic");
52
+ function leasePath(provider, accountId) {
53
+ const dir = provider === 'codex' ? (0, codexPaths_1.getCodexProfileDir)(accountId) : (0, claudeProfiles_1.getClaudeProfileDir)(accountId);
54
+ return path.join(dir, 'keepalive.lease');
55
+ }
56
+ function processAlive(pid) {
57
+ if (pid === process.pid)
58
+ return true;
59
+ try {
60
+ process.kill(pid, 0);
61
+ return true;
62
+ }
63
+ catch (error) {
64
+ return error.code === 'EPERM';
65
+ }
66
+ }
67
+ function isAccountLeaseHeld(provider, accountId, now = Date.now()) {
68
+ try {
69
+ const lease = JSON.parse(fs.readFileSync(leasePath(provider, accountId), 'utf8'));
70
+ return (typeof lease.pid === 'number' &&
71
+ typeof lease.expiresAt === 'number' &&
72
+ lease.expiresAt > now &&
73
+ processAlive(lease.pid));
74
+ }
75
+ catch {
76
+ return false;
77
+ }
78
+ }
79
+ /** Take the lease; the caller holds the provider's swap lock. False when held elsewhere. */
80
+ function acquireAccountLease(provider, accountId, durationMs, now = Date.now()) {
81
+ if (isAccountLeaseHeld(provider, accountId, now))
82
+ return false;
83
+ const target = leasePath(provider, accountId);
84
+ fs.mkdirSync(path.dirname(target), { recursive: true, mode: 0o700 });
85
+ (0, atomic_1.atomicWriteJsonSync)(target, {
86
+ pid: process.pid,
87
+ expiresAt: now + durationMs,
88
+ });
89
+ return true;
90
+ }
91
+ function releaseAccountLease(provider, accountId) {
92
+ try {
93
+ fs.rmSync(leasePath(provider, accountId), { force: true });
94
+ }
95
+ catch {
96
+ /* expires on its own */
97
+ }
98
+ }
@@ -73,6 +73,13 @@ export declare function getAccountLoginStatusAsync(provider: AccountProviderId,
73
73
  export declare function finalizeAccountLogin(provider: AccountProviderId, loginId: string, opts?: FinalizeAccountLoginOptions): AccountManagerResult;
74
74
  /** {@link finalizeAccountLogin} without blocking the event loop on CLI probes. */
75
75
  export declare function finalizeAccountLoginAsync(provider: AccountProviderId, loginId: string, opts?: FinalizeAccountLoginOptions): Promise<AccountManagerResult>;
76
+ /**
77
+ * Abandon a login started with {@link beginAccountLogin} (cancelled, timed
78
+ * out, or failed): removes the temporary home and, for Claude, its Keychain
79
+ * item. Refuses ids that belong to a saved profile. Returns true when
80
+ * something was discarded.
81
+ */
82
+ export declare function discardAccountLogin(provider: AccountProviderId, loginId: string): boolean;
76
83
  export declare function spawnAccountLogin(provider: AccountProviderId, label: string, opts?: SpawnAccountLoginOptions): Promise<AccountManagerResult>;
77
84
  export declare function switchAccount(provider: AccountProviderId, id: string, options?: SwitchAccountOptions): SwitchAccountResult;
78
85
  /**