sidekick-shared 0.27.4 → 0.27.6
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 +43 -0
- package/README.md +92 -45
- 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/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/claudeProfiles.d.ts +14 -0
- package/dist/claudeProfiles.js +22 -0
- 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 +8 -2
- package/dist/codexPaths.js +51 -7
- package/dist/codexProfiles.d.ts +37 -31
- package/dist/codexProfiles.js +842 -671
- package/dist/credentialIO.d.ts +67 -7
- package/dist/credentialIO.js +211 -47
- package/dist/ensureDefaultAccounts.js +1 -8
- package/dist/index.d.ts +17 -4
- package/dist/index.js +36 -11
- 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 +3 -0
- package/dist/node.js +4 -2
- package/dist/processDetection.d.ts +10 -3
- package/dist/processDetection.js +44 -11
- package/dist/promptHistory.d.ts +66 -0
- package/dist/promptHistory.js +37 -18
- 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/sessionPromptHistory.d.ts +120 -0
- package/dist/sessionPromptHistory.js +278 -0
- package/dist/sessionPromptSignals.d.ts +56 -0
- package/dist/sessionPromptSignals.js +355 -0
- package/dist/terminalSync.d.ts +12 -2
- package/dist/terminalSync.js +27 -2
- package/package.json +1 -1
package/dist/accountSync.js
CHANGED
|
@@ -37,6 +37,7 @@ exports.syncLiveAccountStateSync = syncLiveAccountStateSync;
|
|
|
37
37
|
exports.syncLiveAccountState = syncLiveAccountState;
|
|
38
38
|
exports._resetSyncFingerprints = _resetSyncFingerprints;
|
|
39
39
|
exports.cleanupAbandonedLogins = cleanupAbandonedLogins;
|
|
40
|
+
exports.cleanupAbandonedLoginsAsync = cleanupAbandonedLoginsAsync;
|
|
40
41
|
/**
|
|
41
42
|
* Continuous reconciliation between the live provider logins and sidekick's
|
|
42
43
|
* saved profiles. One engine replaces the old bootstrap/reconcile/backup
|
|
@@ -50,6 +51,7 @@ const accounts_1 = require("./accounts");
|
|
|
50
51
|
const claudeProfiles_1 = require("./claudeProfiles");
|
|
51
52
|
const codexProfiles_1 = require("./codexProfiles");
|
|
52
53
|
const codexPaths_1 = require("./codexPaths");
|
|
54
|
+
const processDetection_1 = require("./processDetection");
|
|
53
55
|
const accountSyncTypes_1 = require("./accountSyncTypes");
|
|
54
56
|
const DEFAULT_KEYCHAIN_FOLD_INTERVAL_MS = 10 * 60 * 1000;
|
|
55
57
|
const fingerprints = {
|
|
@@ -89,7 +91,7 @@ function sourceForReason(reason) {
|
|
|
89
91
|
return reason === 'pre-switch' ? 'switch' : 'live-sync';
|
|
90
92
|
}
|
|
91
93
|
function shouldSync(provider, fingerprint, options) {
|
|
92
|
-
if (!options.onlyIfChanged)
|
|
94
|
+
if (!options.onlyIfChanged || (provider === 'codex' && (0, codexProfiles_1.hasPendingCodexRepair)()))
|
|
93
95
|
return true;
|
|
94
96
|
const state = fingerprints[provider];
|
|
95
97
|
if (state.fingerprint !== fingerprint)
|
|
@@ -101,7 +103,7 @@ function shouldSync(provider, fingerprint, options) {
|
|
|
101
103
|
return false;
|
|
102
104
|
}
|
|
103
105
|
/** Sync one provider under its swap lock; never throws. */
|
|
104
|
-
function syncProvider(provider, reason, options) {
|
|
106
|
+
function syncProvider(provider, reason, options, scan) {
|
|
105
107
|
const fingerprint = provider === 'codex' ? codexFingerprint() : claudeFingerprint();
|
|
106
108
|
if (!shouldSync(provider, fingerprint, options)) {
|
|
107
109
|
return { ...(0, accountSyncTypes_1.emptyProviderSyncReport)(), skipped: 'unchanged' };
|
|
@@ -109,9 +111,11 @@ function syncProvider(provider, reason, options) {
|
|
|
109
111
|
const source = sourceForReason(reason);
|
|
110
112
|
let report;
|
|
111
113
|
try {
|
|
114
|
+
if (provider === 'codex')
|
|
115
|
+
scan ??= (0, processDetection_1.inspectRunningAccountConsumersSync)('codex');
|
|
112
116
|
report =
|
|
113
117
|
provider === 'codex'
|
|
114
|
-
? (0, codexProfiles_1.withCodexAuthSwapLock)(() => (0, codexProfiles_1.syncCodexLiveStateUnlocked)(source))
|
|
118
|
+
? (0, codexProfiles_1.withCodexAuthSwapLock)(() => (0, codexProfiles_1.syncCodexLiveStateUnlocked)(source, { scan }))
|
|
115
119
|
: (0, accounts_1.withClaudeAuthSwapLock)(() => (0, accounts_1.syncClaudeLiveStateUnlocked)(source));
|
|
116
120
|
}
|
|
117
121
|
catch (err) {
|
|
@@ -130,7 +134,7 @@ function syncProvider(provider, reason, options) {
|
|
|
130
134
|
* Safe to call from watchers: re-entrant calls return an empty report, and
|
|
131
135
|
* `onlyIfChanged` short-circuits when the live files are unchanged.
|
|
132
136
|
*/
|
|
133
|
-
function
|
|
137
|
+
function syncLiveAccountStateCore(options = {}, scan) {
|
|
134
138
|
const reason = options.reason ?? 'manual';
|
|
135
139
|
const now = options.now ?? Date.now();
|
|
136
140
|
const resolved = {
|
|
@@ -155,16 +159,22 @@ function syncLiveAccountStateSync(options = {}) {
|
|
|
155
159
|
if (providers.includes('claude-code'))
|
|
156
160
|
report.claude = syncProvider('claude-code', reason, resolved);
|
|
157
161
|
if (providers.includes('codex'))
|
|
158
|
-
report.codex = syncProvider('codex', reason, resolved);
|
|
162
|
+
report.codex = syncProvider('codex', reason, resolved, scan);
|
|
159
163
|
}
|
|
160
164
|
finally {
|
|
161
165
|
syncInProgress = false;
|
|
162
166
|
}
|
|
163
167
|
return report;
|
|
164
168
|
}
|
|
169
|
+
function syncLiveAccountStateSync(options = {}) {
|
|
170
|
+
return syncLiveAccountStateCore(options);
|
|
171
|
+
}
|
|
165
172
|
/** Promise-returning twin of {@link syncLiveAccountStateSync} for async hosts. */
|
|
166
173
|
async function syncLiveAccountState(options = {}) {
|
|
167
|
-
|
|
174
|
+
const scan = !options.providers || options.providers.includes('codex')
|
|
175
|
+
? await (0, processDetection_1.inspectRunningAccountConsumers)('codex')
|
|
176
|
+
: undefined;
|
|
177
|
+
return syncLiveAccountStateCore(options, scan);
|
|
168
178
|
}
|
|
169
179
|
/** Forget the change fingerprints (tests, or after an external reset). */
|
|
170
180
|
function _resetSyncFingerprints() {
|
|
@@ -178,3 +188,9 @@ function cleanupAbandonedLogins(olderThanMs = 6 * 60 * 60 * 1000, now = Date.now
|
|
|
178
188
|
codex: (0, codexProfiles_1.cleanupAbandonedCodexLogins)(olderThanMs, now),
|
|
179
189
|
};
|
|
180
190
|
}
|
|
191
|
+
async function cleanupAbandonedLoginsAsync(olderThanMs = 24 * 60 * 60 * 1000, now = Date.now()) {
|
|
192
|
+
return {
|
|
193
|
+
claude: (0, accounts_1.cleanupAbandonedClaudeLogins)(olderThanMs, now),
|
|
194
|
+
codex: await (0, codexProfiles_1.cleanupAbandonedCodexLoginsAsync)(olderThanMs, now),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
@@ -11,6 +11,15 @@ export interface ProviderSyncReport {
|
|
|
11
11
|
repointed?: string;
|
|
12
12
|
/** Duplicate profiles merged away (ids of the removed losers). */
|
|
13
13
|
merged?: string[];
|
|
14
|
+
/**
|
|
15
|
+
* The live account's profile home held a CLI-usable copy of its login; it
|
|
16
|
+
* was removed (the account that is live keeps exactly one copy: live).
|
|
17
|
+
*/
|
|
18
|
+
demoted?: string;
|
|
19
|
+
/** That copy was strictly newer than live, so it replaced the dying live login first. */
|
|
20
|
+
repaired?: string;
|
|
21
|
+
/** A switch that crashed mid-way was finished or rolled back from its journal. */
|
|
22
|
+
journal?: 'finished' | 'rolled-back' | 'cleared';
|
|
14
23
|
/** Why nothing was synced (`logged-out`, `keyring`, `no-identity`, `unchanged`). */
|
|
15
24
|
skipped?: string;
|
|
16
25
|
warnings: string[];
|
package/dist/accounts.d.ts
CHANGED
|
@@ -4,10 +4,17 @@
|
|
|
4
4
|
* Handles saving, listing, switching, syncing, and removing Claude accounts.
|
|
5
5
|
* Both the VS Code extension and CLI consume this module.
|
|
6
6
|
*
|
|
7
|
-
* Model:
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
7
|
+
* Model: Claude Code rotates the refresh token on every refresh, so every
|
|
8
|
+
* refresh-token chain may have exactly one copy a `claude` process can use.
|
|
9
|
+
*
|
|
10
|
+
* - The live home (`CLAUDE_CONFIG_DIR` or `~/.claude`) is the only CLI-usable
|
|
11
|
+
* copy of the account that is logged in right now. Its profile home holds
|
|
12
|
+
* identity and health metadata but never a credential; Sidekick mirrors the
|
|
13
|
+
* live credential into a private backup Claude Code never reads.
|
|
14
|
+
* - An inactive account's profile home holds its single CLI-usable copy (the
|
|
15
|
+
* Claude login only; MCP tokens follow the live store).
|
|
16
|
+
* - Writes into the live store go through one guarded installer that refuses
|
|
17
|
+
* downgrades, and a switch saves the outgoing login before replacing it.
|
|
11
18
|
*/
|
|
12
19
|
import { type ResolvedActiveAccount } from './accountRegistry';
|
|
13
20
|
import { type AccountHealthSource } from './accountHealth';
|
|
@@ -39,11 +46,21 @@ export interface AccountManagerResult {
|
|
|
39
46
|
/** Store a profile's identity (`oauthAccount`) in its home and the flat backup. */
|
|
40
47
|
export declare function storeClaudeProfileIdentity(uuid: string, oauthAccount: unknown): void;
|
|
41
48
|
/**
|
|
42
|
-
* The single chokepoint for storing a Claude credential in a profile
|
|
43
|
-
*
|
|
44
|
-
*
|
|
49
|
+
* The single chokepoint for storing a Claude credential in a profile. Always
|
|
50
|
+
* writes the private backup and the secret-free health sidecar. Writes the
|
|
51
|
+
* profile-home store (suffixed Keychain item on macOS, file elsewhere) only
|
|
52
|
+
* when the account is NOT the live login: the live store is that account's one
|
|
53
|
+
* CLI-usable copy, and a second one would fight it over the refresh token.
|
|
54
|
+
* Caller holds the swap lock.
|
|
45
55
|
*/
|
|
56
|
+
export declare function storeClaudeProfileCredentialsUnlocked(uuid: string, credentials: unknown, source: AccountHealthSource): void;
|
|
57
|
+
/** {@link storeClaudeProfileCredentialsUnlocked} under the swap lock. */
|
|
46
58
|
export declare function storeClaudeProfileCredentials(uuid: string, credentials: unknown, source: AccountHealthSource): void;
|
|
59
|
+
/**
|
|
60
|
+
* The saved credential and identity of a profile: the fresher of its
|
|
61
|
+
* profile-home store and its private backup. Read-only — it never
|
|
62
|
+
* re-materialises a backup into a CLI-visible place.
|
|
63
|
+
*/
|
|
47
64
|
export declare function readStoredClaudeAccount(uuid: string): {
|
|
48
65
|
credentials: unknown;
|
|
49
66
|
oauthAccount: unknown;
|
|
@@ -54,37 +71,77 @@ export declare function readActiveClaudeAccount(): ActiveAccountInfo | null;
|
|
|
54
71
|
export declare function addCurrentAccount(label?: string): AccountManagerResult;
|
|
55
72
|
/**
|
|
56
73
|
* Serializes live Claude credential swaps across processes so two concurrent
|
|
57
|
-
* switchers cannot interleave their backup/write/rollback sequences.
|
|
58
|
-
*
|
|
59
|
-
* ordering: this lock first, the
|
|
60
|
-
*
|
|
74
|
+
* switchers cannot interleave their backup/write/rollback sequences. Claude
|
|
75
|
+
* Code's own refreshes are kept out separately by borrowing its refresh lock
|
|
76
|
+
* (claudeRefreshLock.ts). Lock ordering: this lock first, then the refresh
|
|
77
|
+
* locks, the registry lock (inside setActiveSavedAccount), and the ledger
|
|
78
|
+
* lock — never the reverse. Never spawn a `claude` process while holding it.
|
|
61
79
|
*/
|
|
62
80
|
export declare function withClaudeAuthSwapLock<T>(operation: () => T): T;
|
|
63
81
|
/**
|
|
64
|
-
*
|
|
65
|
-
* (label = email), refresh the matching
|
|
66
|
-
*
|
|
67
|
-
*
|
|
82
|
+
* Reconcile the live login with the saved profiles: finish an interrupted
|
|
83
|
+
* switch, register an unknown login (label = email), refresh the matching
|
|
84
|
+
* profile's backup when the live credential is newer, remove any CLI-usable
|
|
85
|
+
* copy of the live account from its profile home, and re-point the active
|
|
86
|
+
* pointer. Caller holds the swap lock. Never throws; problems land in
|
|
87
|
+
* `warnings`.
|
|
68
88
|
*/
|
|
69
89
|
export declare function syncClaudeLiveStateUnlocked(source?: AccountHealthSource): ProviderSyncReport;
|
|
70
90
|
/** {@link syncClaudeLiveStateUnlocked} under the swap lock. */
|
|
71
91
|
export declare function syncClaudeLiveState(source?: AccountHealthSource): ProviderSyncReport;
|
|
72
92
|
/**
|
|
73
|
-
* Remove isolated login homes that never
|
|
74
|
-
* browser, the login timed out) once they
|
|
93
|
+
* Remove isolated login homes that never finished: ones that never
|
|
94
|
+
* authenticated (the user closed the browser, the login timed out) once they
|
|
95
|
+
* are older than `olderThanMs`, and ones that authenticated but were never
|
|
96
|
+
* finalized (the host quit) after a day. Their Keychain item goes with them.
|
|
75
97
|
* Returns the removed login ids.
|
|
76
98
|
*/
|
|
77
99
|
export declare function cleanupAbandonedClaudeLogins(olderThanMs?: number, now?: number): string[];
|
|
100
|
+
/** {@link beginClaudeKeepAlive} without taking the lease (dry runs). */
|
|
101
|
+
export declare function getClaudeKeepAliveBlocker(accountId: string): string | null;
|
|
102
|
+
/**
|
|
103
|
+
* Check a Claude profile for keep-alive and take its lease, both under the
|
|
104
|
+
* swap lock, so no switch can make it live (or copy it into live) while the
|
|
105
|
+
* CLI refreshes it. Pair with {@link finishClaudeKeepAlive}.
|
|
106
|
+
*/
|
|
107
|
+
export declare function beginClaudeKeepAlive(accountId: string, leaseMs: number): {
|
|
108
|
+
ok: true;
|
|
109
|
+
} | {
|
|
110
|
+
ok: false;
|
|
111
|
+
reason: string;
|
|
112
|
+
};
|
|
113
|
+
/** Record what the CLI refreshed (backup + sidecar) and release the lease. */
|
|
114
|
+
export declare function finishClaudeKeepAlive(accountId: string): void;
|
|
115
|
+
/**
|
|
116
|
+
* Give an inactive profile a CLI-usable copy from its backup when its profile
|
|
117
|
+
* home has none (it was live when it was last seen, or a switch could not
|
|
118
|
+
* write it). User-initiated launches only; keep-alive never does this.
|
|
119
|
+
*/
|
|
120
|
+
export declare function restoreClaudeProfileFromBackup(accountId: string): 'restored' | 'present' | 'live' | 'none';
|
|
78
121
|
export declare function switchToAccount(uuid: string, options?: SwitchAccountOptions): SwitchAccountResult;
|
|
79
122
|
/** {@link switchToAccount} with the process probe off the event loop. */
|
|
80
123
|
export declare function switchToAccountAsync(uuid: string, options?: SwitchAccountOptions): Promise<SwitchAccountResult>;
|
|
81
124
|
/**
|
|
82
125
|
* Install a saved profile's credentials into the live home without changing
|
|
83
126
|
* which account is active: used right after a re-login of the live account,
|
|
84
|
-
* whose freshly minted token must replace the (possibly dead) live one.
|
|
127
|
+
* whose freshly minted token must replace the (possibly dead) live one. The
|
|
128
|
+
* guard only lets a strictly fresher credential of the same account through;
|
|
129
|
+
* when live is already at least as new, nothing is written.
|
|
85
130
|
*/
|
|
86
131
|
export declare function applyClaudeProfileToLiveHome(uuid: string): AccountManagerResult;
|
|
132
|
+
/**
|
|
133
|
+
* @deprecated The home `claude` should use for the active account. The live
|
|
134
|
+
* account's profile home holds no credential, so this is the live home for the
|
|
135
|
+
* live account; kept for API compatibility.
|
|
136
|
+
*/
|
|
87
137
|
export declare function resolveActiveClaudeHome(): string;
|
|
138
|
+
/**
|
|
139
|
+
* @deprecated Re-install the active profile's credential into the live home.
|
|
140
|
+
* It never follows the registry pointer across accounts (after a native
|
|
141
|
+
* `claude /login` the pointer can be stale): when the live login is another
|
|
142
|
+
* account it refuses — use {@link switchToAccount}. For the same account it is
|
|
143
|
+
* a guarded re-login that writes only a strictly fresher credential.
|
|
144
|
+
*/
|
|
88
145
|
export declare function applyActiveClaudeToLiveHome(): AccountManagerResult;
|
|
89
146
|
export declare function reconcileClaudeAuthState(): void;
|
|
90
147
|
export declare function removeAccount(uuid: string): AccountManagerResult;
|