@modelprofile.com/authswitch 8.0.0 → 8.1.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/dist_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/authority-import-contract.d.ts +207 -0
- package/dist_ts/authority-import-contract.js +18 -0
- package/dist_ts/classes.authoritycli.d.ts +4 -0
- package/dist_ts/classes.authoritycli.js +148 -11
- package/dist_ts/classes.authorityclient.d.ts +7 -0
- package/dist_ts/classes.authorityclient.js +13 -1
- package/dist_ts/classes.authoritydaemon.d.ts +4 -0
- package/dist_ts/classes.authoritydaemon.js +72 -1
- package/dist_ts/classes.authoritydatabase.d.ts +5 -0
- package/dist_ts/classes.authoritydatabase.js +20 -5
- package/dist_ts/classes.authorityimport.d.ts +161 -0
- package/dist_ts/classes.authorityimport.js +638 -0
- package/dist_ts/classes.authoritymodels.d.ts +12 -0
- package/dist_ts/classes.authoritymodels.js +17 -1
- package/dist_ts/index.d.ts +1 -0
- package/dist_ts/index.js +6 -3
- package/dist_ts/ts_migration/legacysources/authswitchstores.d.ts +2 -0
- package/dist_ts/ts_migration/legacysources/authswitchstores.js +244 -0
- package/dist_ts/ts_migration/legacysources/index.d.ts +18 -0
- package/dist_ts/ts_migration/legacysources/index.js +24 -0
- package/dist_ts/ts_migration/legacysources/material.d.ts +37 -0
- package/dist_ts/ts_migration/legacysources/material.js +120 -0
- package/dist_ts/ts_migration/legacysources/nativestores.d.ts +2 -0
- package/dist_ts/ts_migration/legacysources/nativestores.js +207 -0
- package/dist_ts/ts_migration/legacysources/shared.d.ts +124 -0
- package/dist_ts/ts_migration/legacysources/shared.js +142 -0
- package/package.json +5 -1
- package/readme.md +46 -0
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/authority-import-contract.ts +216 -0
- package/ts/classes.authoritycli.ts +164 -10
- package/ts/classes.authorityclient.ts +22 -0
- package/ts/classes.authoritydaemon.ts +72 -0
- package/ts/classes.authoritydatabase.ts +20 -4
- package/ts/classes.authorityimport.ts +718 -0
- package/ts/classes.authoritymodels.ts +21 -0
- package/ts/index.ts +5 -2
- package/ts/ts_migration/legacysources/authswitchstores.ts +211 -0
- package/ts/ts_migration/legacysources/index.ts +35 -0
- package/ts/ts_migration/legacysources/material.ts +134 -0
- package/ts/ts_migration/legacysources/nativestores.ts +204 -0
- package/ts/ts_migration/legacysources/shared.ts +220 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import * as plugins from '../../plugins.js';
|
|
2
|
+
import { CodexHome } from '../../classes.codexhome.js';
|
|
3
|
+
import { accessProofAvailable, accessTokenExpiry, claudeSourceIdentity, EXPIRED_ACCESS_PROBLEM, legacyHash, legacySourceDigest, legacySourceId, openAiSourceIdentity, openCodeOpenAiIdentity, parseLegacyJson, readLegacyFile } from './shared.js';
|
|
4
|
+
/**
|
|
5
|
+
* Reads the native stores the vendor tools own: Codex's home, Claude Code's credential pair and OpenCode's
|
|
6
|
+
* provider file.
|
|
7
|
+
*
|
|
8
|
+
* These are never imported in the sense a stash record is. The native tool keeps refreshing its own store,
|
|
9
|
+
* so the authority registers the account and holds the grant as a projection it must not refresh -- two
|
|
10
|
+
* refreshers on one rotating refresh token strand each other. Their ledger rows are therefore earned with a
|
|
11
|
+
* proof that rotates nothing: a provider read with the access token already in the store.
|
|
12
|
+
*/
|
|
13
|
+
const ENROLLMENT_TABLE = 'remote_control_enrollments';
|
|
14
|
+
/**
|
|
15
|
+
* Counts Codex's remote-control enrollments without changing them.
|
|
16
|
+
*
|
|
17
|
+
* `RemoteControlStore.read()` turns every failure into an empty list, which is right for a switch and wrong
|
|
18
|
+
* for an inventory: it would report "no enrollments" for a database that is merely locked, and the owner
|
|
19
|
+
* would approve a cutover that loses pairings. This read distinguishes the two.
|
|
20
|
+
*/
|
|
21
|
+
const countCodexEnrollments = (codexHome) => {
|
|
22
|
+
const statePath = codexHome.statePath;
|
|
23
|
+
if (statePath === null)
|
|
24
|
+
return { count: 0, problem: null };
|
|
25
|
+
const { DatabaseSync } = plugins.loadSqlite();
|
|
26
|
+
let database;
|
|
27
|
+
try {
|
|
28
|
+
database = new DatabaseSync(statePath, { readOnly: true });
|
|
29
|
+
database.exec('pragma busy_timeout = 5000');
|
|
30
|
+
const table = database.prepare(`select name from sqlite_master where type = 'table' and name = ?`)
|
|
31
|
+
.get(ENROLLMENT_TABLE);
|
|
32
|
+
if (table === undefined)
|
|
33
|
+
return { count: 0, problem: null };
|
|
34
|
+
const row = database.prepare(`select count(*) as count from ${ENROLLMENT_TABLE}`).get();
|
|
35
|
+
const count = Number(row?.count);
|
|
36
|
+
return Number.isSafeInteger(count)
|
|
37
|
+
? { count, problem: null }
|
|
38
|
+
: { count: null, problem: 'the Codex state database returned no enrollment count' };
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// A WAL database Codex holds open cannot always be opened read-only; that is unknown, never zero.
|
|
42
|
+
return { count: null, problem: 'the Codex state database could not be read, so its enrollments are unknown' };
|
|
43
|
+
}
|
|
44
|
+
finally {
|
|
45
|
+
database?.close();
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
const nativeRecord = (sourceKind, path, files, problems) => {
|
|
49
|
+
const sourcePathHash = legacyHash(path);
|
|
50
|
+
return {
|
|
51
|
+
id: legacySourceId(sourceKind, sourcePathHash),
|
|
52
|
+
sourceKind,
|
|
53
|
+
path,
|
|
54
|
+
sourcePathHash,
|
|
55
|
+
sourceDigest: legacySourceDigest(files),
|
|
56
|
+
treatment: 'refuse',
|
|
57
|
+
proof: 'none',
|
|
58
|
+
accountId: null,
|
|
59
|
+
subject: null,
|
|
60
|
+
workspaceId: null,
|
|
61
|
+
providerId: null,
|
|
62
|
+
email: null,
|
|
63
|
+
plan: null,
|
|
64
|
+
label: null,
|
|
65
|
+
accessExpiresAt: null,
|
|
66
|
+
enrollmentCount: null,
|
|
67
|
+
problems,
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
/** Marks a projection source ready, or explains why its non-rotating proof cannot be taken right now. */
|
|
71
|
+
const asProjection = (record, now) => {
|
|
72
|
+
record.treatment = 'project';
|
|
73
|
+
record.proof = 'access_token_read';
|
|
74
|
+
if (!accessProofAvailable(record.accessExpiresAt, now))
|
|
75
|
+
record.problems.push(EXPIRED_ACCESS_PROBLEM);
|
|
76
|
+
return record;
|
|
77
|
+
};
|
|
78
|
+
const readCodexNative = (locations, now) => {
|
|
79
|
+
const path = plugins.path.join(locations.codexHome, 'auth.json');
|
|
80
|
+
const read = readLegacyFile(path, 'auth.json');
|
|
81
|
+
if (read.problem === null && read.file === null)
|
|
82
|
+
return null;
|
|
83
|
+
const files = read.file ? [read.file] : [];
|
|
84
|
+
const problems = read.problem ? [read.problem] : [];
|
|
85
|
+
const record = nativeRecord('codex_native', path, files, problems);
|
|
86
|
+
const enrollments = countCodexEnrollments(new CodexHome(locations.codexHome));
|
|
87
|
+
record.enrollmentCount = enrollments.count;
|
|
88
|
+
if (enrollments.problem)
|
|
89
|
+
problems.push(enrollments.problem);
|
|
90
|
+
if (!read.file)
|
|
91
|
+
return record;
|
|
92
|
+
const parsed = parseLegacyJson(read.file.raw);
|
|
93
|
+
const tokens = parsed?.tokens;
|
|
94
|
+
const tokenRecord = tokens && typeof tokens === 'object' && !Array.isArray(tokens)
|
|
95
|
+
? tokens : null;
|
|
96
|
+
const idToken = typeof tokenRecord?.id_token === 'string' ? tokenRecord.id_token : null;
|
|
97
|
+
const identity = idToken === null ? null : openAiSourceIdentity(idToken);
|
|
98
|
+
if (identity === null) {
|
|
99
|
+
problems.push('the active Codex credential carries no ChatGPT identity the authority can hold');
|
|
100
|
+
return record;
|
|
101
|
+
}
|
|
102
|
+
record.providerId = 'openai';
|
|
103
|
+
record.accountId = identity.accountId;
|
|
104
|
+
record.subject = identity.subject;
|
|
105
|
+
record.workspaceId = identity.workspaceId;
|
|
106
|
+
record.email = identity.email;
|
|
107
|
+
record.plan = identity.plan;
|
|
108
|
+
record.label = identity.email;
|
|
109
|
+
record.accessExpiresAt = accessTokenExpiry(typeof tokenRecord?.access_token === 'string' ? tokenRecord.access_token : null);
|
|
110
|
+
return asProjection(record, now);
|
|
111
|
+
};
|
|
112
|
+
const readClaudeNative = (locations, now) => {
|
|
113
|
+
const credentials = readLegacyFile(locations.claudeCredentialFile, '.credentials.json');
|
|
114
|
+
const config = readLegacyFile(locations.claudeConfigFile, '.claude.json');
|
|
115
|
+
if (credentials.problem === null && credentials.file === null)
|
|
116
|
+
return null;
|
|
117
|
+
const files = [credentials.file, config.file].filter((file) => file !== null);
|
|
118
|
+
const problems = [credentials.problem, config.problem].filter((problem) => problem !== null);
|
|
119
|
+
const record = nativeRecord('claude_native', locations.claudeCredentialFile, files, problems);
|
|
120
|
+
if (!credentials.file)
|
|
121
|
+
return record;
|
|
122
|
+
const credentialData = parseLegacyJson(credentials.file.raw);
|
|
123
|
+
const configData = config.file === null ? null : parseLegacyJson(config.file.raw);
|
|
124
|
+
if (configData === null)
|
|
125
|
+
problems.push('the Claude account metadata file is missing or not a JSON object');
|
|
126
|
+
const identity = claudeSourceIdentity({
|
|
127
|
+
claudeAiOauth: credentialData?.claudeAiOauth, oauthAccount: configData?.oauthAccount
|
|
128
|
+
});
|
|
129
|
+
if (identity === null) {
|
|
130
|
+
problems.push('the native Claude login or its account metadata is incomplete');
|
|
131
|
+
return record;
|
|
132
|
+
}
|
|
133
|
+
record.providerId = 'anthropic';
|
|
134
|
+
record.accountId = identity.accountId;
|
|
135
|
+
record.subject = identity.subject;
|
|
136
|
+
record.workspaceId = identity.workspaceId;
|
|
137
|
+
record.email = identity.email;
|
|
138
|
+
record.label = identity.email;
|
|
139
|
+
record.accessExpiresAt = identity.accessExpiresAt;
|
|
140
|
+
return asProjection(record, now);
|
|
141
|
+
};
|
|
142
|
+
/**
|
|
143
|
+
* One row per provider entry of OpenCode's `auth.json`.
|
|
144
|
+
*
|
|
145
|
+
* The entries share a file, so the source location names the entry -- `<file>#<provider>` -- and the digest
|
|
146
|
+
* covers that entry's bytes. A change to one provider therefore does not invalidate another's ledger row.
|
|
147
|
+
*/
|
|
148
|
+
const readOpenCodeNative = (locations, now) => {
|
|
149
|
+
const read = readLegacyFile(locations.openCodeAuthFile, 'auth.json');
|
|
150
|
+
if (read.problem !== null)
|
|
151
|
+
return { sources: [], problems: [`OpenCode ${read.problem}`] };
|
|
152
|
+
if (read.file === null)
|
|
153
|
+
return { sources: [], problems: [] };
|
|
154
|
+
const parsed = parseLegacyJson(read.file.raw);
|
|
155
|
+
if (parsed === null)
|
|
156
|
+
return { sources: [], problems: ['OpenCode auth.json is not a JSON object'] };
|
|
157
|
+
const sources = [];
|
|
158
|
+
for (const [provider, value] of Object.entries(parsed).sort(([left], [right]) => left.localeCompare(right))) {
|
|
159
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
160
|
+
continue;
|
|
161
|
+
const entry = value;
|
|
162
|
+
const path = `${locations.openCodeAuthFile}#${provider}`;
|
|
163
|
+
const record = nativeRecord('opencode_native', path, [{ name: provider, raw: JSON.stringify(entry) }], []);
|
|
164
|
+
record.label = provider;
|
|
165
|
+
if (entry.type !== 'oauth') {
|
|
166
|
+
record.problems.push(`the ${provider} entry is a ${String(entry.type)} credential, not a login the authority can hold`);
|
|
167
|
+
sources.push(record);
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
if (provider !== 'openai') {
|
|
171
|
+
// OpenCode identifies a non-OpenAI OAuth entry by a token fingerprint, which is not a provider
|
|
172
|
+
// identity: there is nothing to bind an authority account to. Its account is added from its own store.
|
|
173
|
+
record.problems.push(`the ${provider} entry carries no provider identity the authority can bind an account to`);
|
|
174
|
+
sources.push(record);
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
const { identity, accessExpiresAt } = openCodeOpenAiIdentity(entry);
|
|
178
|
+
record.accessExpiresAt = accessExpiresAt;
|
|
179
|
+
if (identity === null) {
|
|
180
|
+
record.problems.push('the OpenAI entry carries no identity the authority can hold');
|
|
181
|
+
sources.push(record);
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
record.providerId = 'openai';
|
|
185
|
+
record.accountId = identity.accountId;
|
|
186
|
+
record.subject = identity.subject;
|
|
187
|
+
record.workspaceId = identity.workspaceId;
|
|
188
|
+
record.email = identity.email;
|
|
189
|
+
record.plan = identity.plan;
|
|
190
|
+
record.label = identity.email ?? provider;
|
|
191
|
+
sources.push(asProjection(record, now));
|
|
192
|
+
}
|
|
193
|
+
return { sources, problems: [] };
|
|
194
|
+
};
|
|
195
|
+
export const readNativeStores = (locations, now) => {
|
|
196
|
+
const sources = [];
|
|
197
|
+
const problems = [];
|
|
198
|
+
for (const record of [readCodexNative(locations, now), readClaudeNative(locations, now)]) {
|
|
199
|
+
if (record !== null)
|
|
200
|
+
sources.push(record);
|
|
201
|
+
}
|
|
202
|
+
const openCode = readOpenCodeNative(locations, now);
|
|
203
|
+
sources.push(...openCode.sources);
|
|
204
|
+
problems.push(...openCode.problems);
|
|
205
|
+
return { sources, problems };
|
|
206
|
+
};
|
|
207
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmF0aXZlc3RvcmVzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvdHNfbWlncmF0aW9uL2xlZ2FjeXNvdXJjZXMvbmF0aXZlc3RvcmVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFDNUMsT0FBTyxFQUFFLFNBQVMsRUFBRSxNQUFNLDRCQUE0QixDQUFDO0FBQ3ZELE9BQU8sRUFBRSxvQkFBb0IsRUFBRSxpQkFBaUIsRUFBRSxvQkFBb0IsRUFBRSxzQkFBc0IsRUFBRSxVQUFVLEVBQ3hHLGtCQUFrQixFQUFFLGNBQWMsRUFBRSxvQkFBb0IsRUFBRSxzQkFBc0IsRUFBRSxlQUFlLEVBQ2pHLGNBQWMsRUFDWSxNQUFNLGFBQWEsQ0FBQztBQUdoRDs7Ozs7Ozs7R0FRRztBQUVILE1BQU0sZ0JBQWdCLEdBQUcsNEJBQTRCLENBQUM7QUFFdEQ7Ozs7OztHQU1HO0FBQ0gsTUFBTSxxQkFBcUIsR0FBRyxDQUFDLFNBQW9CLEVBQW9ELEVBQUU7SUFDdkcsTUFBTSxTQUFTLEdBQUcsU0FBUyxDQUFDLFNBQVMsQ0FBQztJQUN0QyxJQUFJLFNBQVMsS0FBSyxJQUFJO1FBQUUsT0FBTyxFQUFFLEtBQUssRUFBRSxDQUFDLEVBQUUsT0FBTyxFQUFFLElBQUksRUFBRSxDQUFDO0lBQzNELE1BQU0sRUFBRSxZQUFZLEVBQUUsR0FBRyxPQUFPLENBQUMsVUFBVSxFQUFFLENBQUM7SUFDOUMsSUFBSSxRQUF1RCxDQUFDO0lBQzVELElBQUksQ0FBQztRQUNILFFBQVEsR0FBRyxJQUFJLFlBQVksQ0FBQyxTQUFTLEVBQUUsRUFBRSxRQUFRLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQztRQUMzRCxRQUFRLENBQUMsSUFBSSxDQUFDLDRCQUE0QixDQUFDLENBQUM7UUFDNUMsTUFBTSxLQUFLLEdBQUcsUUFBUSxDQUFDLE9BQU8sQ0FBQyxrRUFBa0UsQ0FBQzthQUMvRixHQUFHLENB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
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import type { TAuthSwitchImportProof, TAuthSwitchImportSourceKind, TAuthSwitchImportTreatment } from '../../authority-import-contract.js';
|
|
2
|
+
/**
|
|
3
|
+
* Shared vocabulary of the legacy-source readers.
|
|
4
|
+
*
|
|
5
|
+
* These readers exist for one migration and are deliberately isolated from the runtime: nothing here is a
|
|
6
|
+
* fallback an ordinary command may reach for. They only read, and they report what they could not read
|
|
7
|
+
* instead of returning a shorter list -- a silently skipped entry is an account the owner would lose.
|
|
8
|
+
*/
|
|
9
|
+
/** One source as the readers see it. `path` stays inside the daemon; only `sourcePathHash` is published. */
|
|
10
|
+
export interface ILegacySourceRecord {
|
|
11
|
+
id: string;
|
|
12
|
+
sourceKind: TAuthSwitchImportSourceKind;
|
|
13
|
+
path: string;
|
|
14
|
+
sourcePathHash: string;
|
|
15
|
+
sourceDigest: string;
|
|
16
|
+
treatment: TAuthSwitchImportTreatment;
|
|
17
|
+
proof: TAuthSwitchImportProof;
|
|
18
|
+
accountId: string | null;
|
|
19
|
+
/** The provider's own stable identity, kept on the account so a sealed credential can be matched to it. */
|
|
20
|
+
subject: string | null;
|
|
21
|
+
workspaceId: string | null;
|
|
22
|
+
providerId: string | null;
|
|
23
|
+
email: string | null;
|
|
24
|
+
plan: string | null;
|
|
25
|
+
label: string | null;
|
|
26
|
+
accessExpiresAt: string | null;
|
|
27
|
+
enrollmentCount: number | null;
|
|
28
|
+
problems: string[];
|
|
29
|
+
}
|
|
30
|
+
export interface ILegacySourceReport {
|
|
31
|
+
sources: ILegacySourceRecord[];
|
|
32
|
+
/** Locations that could not be read at all, so an empty result is never mistaken for an empty store. */
|
|
33
|
+
problems: string[];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Where the legacy stores live.
|
|
37
|
+
*
|
|
38
|
+
* Each field repeats the rule of the class that owns that location, so a fixture can point the whole reader
|
|
39
|
+
* at a temporary directory: `authSwitchHome` (ts/classes.credentialstore.ts), `CodexHome`
|
|
40
|
+
* (ts/classes.codexhome.ts), `ClaudeCodeHarness` (ts/classes.claudecodeharness.ts) and `OpenCodeHarness`
|
|
41
|
+
* (ts/classes.opencodeharness.ts). Drift in any of those is a bug here, not a reason to guess.
|
|
42
|
+
*/
|
|
43
|
+
export interface ILegacySourceLocations {
|
|
44
|
+
authSwitchHome: string;
|
|
45
|
+
codexHome: string;
|
|
46
|
+
claudeCredentialFile: string;
|
|
47
|
+
claudeConfigFile: string;
|
|
48
|
+
openCodeAuthFile: string;
|
|
49
|
+
}
|
|
50
|
+
export declare const resolveLegacySourceLocations: (env: NodeJS.ProcessEnv, homeDirectory: string) => ILegacySourceLocations;
|
|
51
|
+
export declare const legacyHash: (value: string) => string;
|
|
52
|
+
/** The ledger row id of a source. Stable across inventory runs, so a resumed import finds its own row. */
|
|
53
|
+
export declare const legacySourceId: (sourceKind: TAuthSwitchImportSourceKind, sourcePathHash: string) => string;
|
|
54
|
+
/**
|
|
55
|
+
* The digest that binds an import to exactly these bytes.
|
|
56
|
+
*
|
|
57
|
+
* A source is often more than one file -- a stash directory, or Claude's credential/config pair -- so the
|
|
58
|
+
* digest covers an ordered list of per-file digests rather than one file. The file names are part of it: a
|
|
59
|
+
* source that gains or loses a file is a different source.
|
|
60
|
+
*/
|
|
61
|
+
export declare const legacySourceDigest: (files: ReadonlyArray<{
|
|
62
|
+
name: string;
|
|
63
|
+
raw: string;
|
|
64
|
+
}>) => string;
|
|
65
|
+
export interface ILegacySourceFile {
|
|
66
|
+
name: string;
|
|
67
|
+
raw: string;
|
|
68
|
+
}
|
|
69
|
+
/** A bounded read that reports unreadable and absent apart, through the store's own reader. */
|
|
70
|
+
export declare const readLegacyFile: (file: string, name: string) => {
|
|
71
|
+
file: ILegacySourceFile | null;
|
|
72
|
+
problem: string | null;
|
|
73
|
+
};
|
|
74
|
+
export declare const parseLegacyJson: (raw: string) => Record<string, unknown> | null;
|
|
75
|
+
export interface IOpenAiSourceIdentity {
|
|
76
|
+
accountId: string;
|
|
77
|
+
/** The stable provider identity the account record keeps, so a sealed credential can be matched to it. */
|
|
78
|
+
subject: string;
|
|
79
|
+
workspaceId: string;
|
|
80
|
+
email: string | null;
|
|
81
|
+
plan: string | null;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* The authority account a ChatGPT login resolves to.
|
|
85
|
+
*
|
|
86
|
+
* Derived from the id_token's claims exactly as `AuthSwitchAuthorityBroker.identity` derives it
|
|
87
|
+
* (ts/classes.authoritybroker.ts), because an import that produced a different account id would create a
|
|
88
|
+
* second account for a login the owner already has. No provider call is involved: this is a local read of
|
|
89
|
+
* claims the token already carries, which is what lets the inventory run offline.
|
|
90
|
+
*/
|
|
91
|
+
export declare const openAiSourceIdentity: (idToken: string) => IOpenAiSourceIdentity | null;
|
|
92
|
+
export interface IClaudeSourceIdentity {
|
|
93
|
+
accountId: string;
|
|
94
|
+
/** Claude's immutable pair, kept as the account's subject and workspace so a sealed login matches it. */
|
|
95
|
+
subject: string;
|
|
96
|
+
workspaceId: string;
|
|
97
|
+
email: string | null;
|
|
98
|
+
accessExpiresAt: string | null;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* The authority account a Claude subscriber login resolves to.
|
|
102
|
+
*
|
|
103
|
+
* Claude's immutable identity is the account/organization UUID pair that `ClaudeNativeAdapter` proves
|
|
104
|
+
* against the provider (ts/classes.claudenative.ts). Nothing in the package derived an authority account id
|
|
105
|
+
* from it before this import, so it follows the OpenAI rule -- provider, subject, workspace -- and stays the
|
|
106
|
+
* single formula both the inventory and the import use.
|
|
107
|
+
*/
|
|
108
|
+
export declare const claudeSourceIdentity: (credential: Record<string, unknown>) => IClaudeSourceIdentity | null;
|
|
109
|
+
/**
|
|
110
|
+
* The identity of an OpenCode OpenAI provider entry.
|
|
111
|
+
*
|
|
112
|
+
* OpenCode stores no id_token, so `OpenCodeHarness.openAiInfo` (ts/classes.opencodeharness.ts) reads the
|
|
113
|
+
* claims of the access token instead. An expired or opaque access token therefore leaves such an entry
|
|
114
|
+
* without a derivable identity, which is reported rather than guessed.
|
|
115
|
+
*/
|
|
116
|
+
export declare const openCodeOpenAiIdentity: (entry: Record<string, unknown>) => {
|
|
117
|
+
identity: IOpenAiSourceIdentity | null;
|
|
118
|
+
accessExpiresAt: string | null;
|
|
119
|
+
};
|
|
120
|
+
/** The expiry claim of an access token, as the broker reads it. Absent or unparsable means unknown. */
|
|
121
|
+
export declare const accessTokenExpiry: (accessToken: string | null) => string | null;
|
|
122
|
+
/** Whether a non-rotating proof can still be taken with this access token. */
|
|
123
|
+
export declare const accessProofAvailable: (expiresAt: string | null, now: number) => boolean;
|
|
124
|
+
export declare const EXPIRED_ACCESS_PROBLEM: string;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import * as plugins from '../../plugins.js';
|
|
2
|
+
import { readCredentialRaw } from '../../classes.credentialstore.js';
|
|
3
|
+
export const resolveLegacySourceLocations = (env, homeDirectory) => {
|
|
4
|
+
const claudeConfigDirectory = env.CLAUDE_CONFIG_DIR || plugins.path.join(homeDirectory, '.claude');
|
|
5
|
+
const dataHome = env.XDG_DATA_HOME || plugins.path.join(homeDirectory, '.local', 'share');
|
|
6
|
+
return {
|
|
7
|
+
authSwitchHome: env.AUTHSWITCH_HOME || plugins.path.join(homeDirectory, '.authswitch'),
|
|
8
|
+
codexHome: env.CODEX_HOME || plugins.path.join(homeDirectory, '.codex'),
|
|
9
|
+
claudeCredentialFile: plugins.path.join(claudeConfigDirectory, '.credentials.json'),
|
|
10
|
+
// Claude Code keeps its account metadata beside the home directory, not inside a custom config directory.
|
|
11
|
+
claudeConfigFile: plugins.path.join(env.CLAUDE_CONFIG_DIR || homeDirectory, '.claude.json'),
|
|
12
|
+
openCodeAuthFile: plugins.path.join(dataHome, 'opencode', 'auth.json'),
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
export const legacyHash = (value) => plugins.crypto.createHash('sha256').update(value).digest('hex');
|
|
16
|
+
/** The ledger row id of a source. Stable across inventory runs, so a resumed import finds its own row. */
|
|
17
|
+
export const legacySourceId = (sourceKind, sourcePathHash) => legacyHash(JSON.stringify(['authswitch-migration-v1', sourceKind, sourcePathHash]));
|
|
18
|
+
/**
|
|
19
|
+
* The digest that binds an import to exactly these bytes.
|
|
20
|
+
*
|
|
21
|
+
* A source is often more than one file -- a stash directory, or Claude's credential/config pair -- so the
|
|
22
|
+
* digest covers an ordered list of per-file digests rather than one file. The file names are part of it: a
|
|
23
|
+
* source that gains or loses a file is a different source.
|
|
24
|
+
*/
|
|
25
|
+
export const legacySourceDigest = (files) => legacyHash(JSON.stringify([...files]
|
|
26
|
+
.sort((left, right) => left.name.localeCompare(right.name))
|
|
27
|
+
.map(file => [file.name, legacyHash(file.raw)])));
|
|
28
|
+
/** A bounded read that reports unreadable and absent apart, through the store's own reader. */
|
|
29
|
+
export const readLegacyFile = (file, name) => {
|
|
30
|
+
try {
|
|
31
|
+
const raw = readCredentialRaw(file);
|
|
32
|
+
return { file: raw === null ? null : { name, raw }, problem: null };
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return { file: null, problem: `${name} is unreadable, malformed or too large` };
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
export const parseLegacyJson = (raw) => {
|
|
39
|
+
try {
|
|
40
|
+
const value = JSON.parse(raw);
|
|
41
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
42
|
+
? value : null;
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* The authority account a ChatGPT login resolves to.
|
|
50
|
+
*
|
|
51
|
+
* Derived from the id_token's claims exactly as `AuthSwitchAuthorityBroker.identity` derives it
|
|
52
|
+
* (ts/classes.authoritybroker.ts), because an import that produced a different account id would create a
|
|
53
|
+
* second account for a login the owner already has. No provider call is involved: this is a local read of
|
|
54
|
+
* claims the token already carries, which is what lets the inventory run offline.
|
|
55
|
+
*/
|
|
56
|
+
export const openAiSourceIdentity = (idToken) => {
|
|
57
|
+
let info;
|
|
58
|
+
try {
|
|
59
|
+
info = plugins.flexAuth.parseOpenAiChatGptTokenInfo(idToken);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
if (!info.chatgptUserId || !info.chatgptAccountId)
|
|
65
|
+
return null;
|
|
66
|
+
return {
|
|
67
|
+
accountId: legacyHash(JSON.stringify(['authswitch-account-v1', 'openai',
|
|
68
|
+
info.chatgptUserId, info.chatgptAccountId])),
|
|
69
|
+
subject: info.chatgptUserId,
|
|
70
|
+
workspaceId: info.chatgptAccountId,
|
|
71
|
+
email: info.email?.toLowerCase() ?? null,
|
|
72
|
+
plan: info.chatgptPlanType ?? null,
|
|
73
|
+
};
|
|
74
|
+
};
|
|
75
|
+
/**
|
|
76
|
+
* The authority account a Claude subscriber login resolves to.
|
|
77
|
+
*
|
|
78
|
+
* Claude's immutable identity is the account/organization UUID pair that `ClaudeNativeAdapter` proves
|
|
79
|
+
* against the provider (ts/classes.claudenative.ts). Nothing in the package derived an authority account id
|
|
80
|
+
* from it before this import, so it follows the OpenAI rule -- provider, subject, workspace -- and stays the
|
|
81
|
+
* single formula both the inventory and the import use.
|
|
82
|
+
*/
|
|
83
|
+
export const claudeSourceIdentity = (credential) => {
|
|
84
|
+
const oauth = credential.claudeAiOauth;
|
|
85
|
+
const account = credential.oauthAccount;
|
|
86
|
+
if (!oauth || typeof oauth !== 'object' || Array.isArray(oauth)
|
|
87
|
+
|| !account || typeof account !== 'object' || Array.isArray(account))
|
|
88
|
+
return null;
|
|
89
|
+
const oauthRecord = oauth;
|
|
90
|
+
const accountRecord = account;
|
|
91
|
+
const accountUuid = accountRecord.accountUuid;
|
|
92
|
+
const organizationUuid = accountRecord.organizationUuid;
|
|
93
|
+
if (typeof accountUuid !== 'string' || !accountUuid || typeof organizationUuid !== 'string'
|
|
94
|
+
|| !organizationUuid)
|
|
95
|
+
return null;
|
|
96
|
+
const expiresAt = oauthRecord.expiresAt;
|
|
97
|
+
const email = accountRecord.emailAddress;
|
|
98
|
+
return {
|
|
99
|
+
accountId: legacyHash(JSON.stringify(['authswitch-account-v1', 'anthropic',
|
|
100
|
+
accountUuid, organizationUuid])),
|
|
101
|
+
subject: accountUuid,
|
|
102
|
+
workspaceId: organizationUuid,
|
|
103
|
+
email: typeof email === 'string' && email ? email.toLowerCase() : null,
|
|
104
|
+
accessExpiresAt: typeof expiresAt === 'number' && Number.isFinite(expiresAt)
|
|
105
|
+
? new Date(expiresAt).toISOString() : null,
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* The identity of an OpenCode OpenAI provider entry.
|
|
110
|
+
*
|
|
111
|
+
* OpenCode stores no id_token, so `OpenCodeHarness.openAiInfo` (ts/classes.opencodeharness.ts) reads the
|
|
112
|
+
* claims of the access token instead. An expired or opaque access token therefore leaves such an entry
|
|
113
|
+
* without a derivable identity, which is reported rather than guessed.
|
|
114
|
+
*/
|
|
115
|
+
export const openCodeOpenAiIdentity = (entry) => {
|
|
116
|
+
const access = typeof entry.access === 'string' ? entry.access : null;
|
|
117
|
+
const expires = entry.expires;
|
|
118
|
+
const accessExpiresAt = typeof expires === 'number' && Number.isFinite(expires)
|
|
119
|
+
? new Date(expires).toISOString() : accessTokenExpiry(access);
|
|
120
|
+
return { identity: access === null ? null : openAiSourceIdentity(access), accessExpiresAt };
|
|
121
|
+
};
|
|
122
|
+
/** The expiry claim of an access token, as the broker reads it. Absent or unparsable means unknown. */
|
|
123
|
+
export const accessTokenExpiry = (accessToken) => {
|
|
124
|
+
if (!accessToken)
|
|
125
|
+
return null;
|
|
126
|
+
try {
|
|
127
|
+
return plugins.flexAuth.parseOpenAiChatGptTokenInfo(accessToken).expiresAt ?? null;
|
|
128
|
+
}
|
|
129
|
+
catch {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
/** Whether a non-rotating proof can still be taken with this access token. */
|
|
134
|
+
export const accessProofAvailable = (expiresAt, now) => {
|
|
135
|
+
if (expiresAt === null)
|
|
136
|
+
return false;
|
|
137
|
+
const value = Date.parse(expiresAt);
|
|
138
|
+
return Number.isFinite(value) && value > now;
|
|
139
|
+
};
|
|
140
|
+
export const EXPIRED_ACCESS_PROBLEM = 'the stored access token has expired, so the non-rotating proof is unavailable until the native tool '
|
|
141
|
+
+ 'refreshes its own store';
|
|
142
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2hhcmVkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vdHMvdHNfbWlncmF0aW9uL2xlZ2FjeXNvdXJjZXMvc2hhcmVkLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sa0JBQWtCLENBQUM7QUFDNUMsT0FBTyxFQUFFLGlCQUFpQixFQUFFLE1BQU0sa0NBQWtDLENBQUM7QUF3RHJFLE1BQU0sQ0FBQyxNQUFNLDRCQUE0QixHQUFHLENBQUMsR0FBc0IsRUFDakUsYUFBcUIsRUFBMEIsRUFBRTtJQUNqRCxNQUFNLHFCQUFxQixHQUFHLEdBQUcsQ0FBQyxpQkFBaUIsSUFBSSxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxhQUFhLEVBQUUsU0FBUyxDQUFDLENBQUM7SUFDbkcsTUFBTSxRQUFRLEdBQUcsR0FBRyxDQUFDLGFBQWEsSUFBSSxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxhQUFhLEVBQUUsUUFBUSxFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQzFGLE9BQU87UUFDTCxjQUFjLEVBQUUsR0FBRyxDQUFDLGVBQWUsSUFBSSxPQUFPLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxhQUFhLEVBQUUsYUFBYSxDQUFDO1FBQ3RGLFNBQVMsRUFBRSxHQUFHLENBQUMsVUFBVSxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLGFBQWEsRUFBRSxRQUFRLENBQUM7UUFDdkUsb0JBQW9CLEVBQUUsT0FBTyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMscUJBQXFCLEVBQUUsbUJBQW1CLENBQUM7UUFDbkYsMEdBQTBHO1FBQzFHLGdCQUFnQixFQUFFLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxpQkFBaUIsSUFBSSxhQUFhLEVBQUUsY0FBYyxDQUFDO1FBQzNGLGdCQUFnQixFQUFFLE9BQU8sQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLFFBQVEs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
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@modelprofile.com/authswitch",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.1.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Manage Codex, OpenCode and Claude Code accounts with guided switching and live usage status",
|
|
6
6
|
"main": "dist_ts/index.js",
|
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
"types": "./dist_ts/authority-contract.d.ts",
|
|
19
19
|
"import": "./dist_ts/authority-contract.js"
|
|
20
20
|
},
|
|
21
|
+
"./authority-import-contract": {
|
|
22
|
+
"types": "./dist_ts/authority-import-contract.d.ts",
|
|
23
|
+
"import": "./dist_ts/authority-import-contract.js"
|
|
24
|
+
},
|
|
21
25
|
"./authority-client": {
|
|
22
26
|
"types": "./dist_ts/classes.authorityclient.d.ts",
|
|
23
27
|
"import": "./dist_ts/classes.authorityclient.js"
|
package/readme.md
CHANGED
|
@@ -110,6 +110,9 @@ authswitch account preuse operation get <operation-id> [--json]
|
|
|
110
110
|
authswitch account preuse operation cancel <operation-id> [--json]
|
|
111
111
|
authswitch account preuse operation resume <operation-id> [--json]
|
|
112
112
|
authswitch authority doctor [--json]
|
|
113
|
+
authswitch authority import inventory [--json]
|
|
114
|
+
authswitch authority import status [--json]
|
|
115
|
+
authswitch authority import submit <source-id> --digest <source-digest> [--json]
|
|
113
116
|
```
|
|
114
117
|
|
|
115
118
|
`account list --json` prints the complete, consistent credential-free authority snapshot.
|
|
@@ -164,6 +167,49 @@ If the daemon is unavailable before the first response, it fails with a fixed di
|
|
|
164
167
|
stderr and leaves stdout empty. The older native-store commands documented below still coexist until the coordinated
|
|
165
168
|
major-version migration and removal; these authority routes never use them.
|
|
166
169
|
|
|
170
|
+
## The one-time account import
|
|
171
|
+
|
|
172
|
+
The three `authority import` commands move the accounts that existed before the authority into it.
|
|
173
|
+
|
|
174
|
+
`import inventory` reads this host and changes nothing: no authority record, no legacy file, no
|
|
175
|
+
credential. It lists every Codex stash directory, saved credential record and native Codex, Claude and
|
|
176
|
+
OpenCode store with the authority account it resolves to, derived from the claims the stored token already
|
|
177
|
+
carries -- no provider is contacted. A malformed or unrecognised entry is reported rather than skipped, so
|
|
178
|
+
an empty result is never mistaken for an empty store. The report carries no token, no token digest and no
|
|
179
|
+
source path; a source is named by a hash of its location and a digest of its bytes.
|
|
180
|
+
|
|
181
|
+
Each source is classified by who refreshes its login **after** the import.
|
|
182
|
+
|
|
183
|
+
- **adopt** -- a copy authswitch made for itself, and AGL's own records. Nothing else refreshes such a
|
|
184
|
+
copy, so the authority takes it over outright. It is verified by one real refresh, which rotates the
|
|
185
|
+
token: from that moment the legacy copy is dead. That is the point of no return for that account, and the
|
|
186
|
+
only fallback is one device sign-in.
|
|
187
|
+
- **project** -- a native Codex, Claude or OpenCode store. The vendor tool keeps refreshing its own store,
|
|
188
|
+
so the authority registers the account, leaves the grant with its native owner and holds no copy of the
|
|
189
|
+
credential. It is verified by a provider read with the access token already in the store, which rotates
|
|
190
|
+
nothing. Two refreshers on one rotating refresh token is the failure this avoids.
|
|
191
|
+
|
|
192
|
+
`import submit` takes the source id and the digest the owner read in the inventory. The daemon re-reads the
|
|
193
|
+
source itself and refuses if the digest changed, so approving one source can never import a different one;
|
|
194
|
+
no credential crosses the socket for a source this package can read. It refuses while an authswitch watch, a
|
|
195
|
+
native harness process or Codex's app-server is running, and an app-server it cannot ask counts as running.
|
|
196
|
+
An adopt whose refresh fails, or whose answer is lost, is never retried: the source is quarantined, its grant
|
|
197
|
+
is left needing re-authentication, and the command says so. A projection fails differently, because nothing
|
|
198
|
+
rotating was ever sent for it: a native store that could not be proven live stays a pending native owner and
|
|
199
|
+
is imported by submitting the same source again once its own tool has refreshed it.
|
|
200
|
+
|
|
201
|
+
`import status` reads the migration ledger, the grants and the handoffs -- never the host -- and reports, per
|
|
202
|
+
source, where it stands, which owner refreshes its login now, and what to do next: nothing, submit the same
|
|
203
|
+
source again, or sign in to that account again. Submitting a source again is how an interrupted run resumes,
|
|
204
|
+
and the answer comes from the same durable record the daemon decides on: a source whose sign-in was in
|
|
205
|
+
flight when the run died is reported as needing a device sign-in, not as resumable, because a rotating
|
|
206
|
+
refresh token is never sent a second time. Sources that were never submitted have no ledger row and appear
|
|
207
|
+
only in the inventory.
|
|
208
|
+
|
|
209
|
+
An import deletes nothing: retiring the legacy files is a separate, later step. The authority store is
|
|
210
|
+
deliberately not backed up, so losing it costs one device sign-in per account -- and the remote-control
|
|
211
|
+
enrollments and runtime bindings with it.
|
|
212
|
+
|
|
167
213
|
`AuthSwitchClient.getUsage(accountId, loginId)` reads OpenAI managed limits or a Claude
|
|
168
214
|
host login's profile and usage through the management socket. The tagged, credential-free
|
|
169
215
|
reading preserves Claude's labelled five-hour, weekly and model windows, plan provenance,
|