@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.
Files changed (43) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts/authority-import-contract.d.ts +207 -0
  3. package/dist_ts/authority-import-contract.js +18 -0
  4. package/dist_ts/classes.authoritycli.d.ts +4 -0
  5. package/dist_ts/classes.authoritycli.js +148 -11
  6. package/dist_ts/classes.authorityclient.d.ts +7 -0
  7. package/dist_ts/classes.authorityclient.js +13 -1
  8. package/dist_ts/classes.authoritydaemon.d.ts +4 -0
  9. package/dist_ts/classes.authoritydaemon.js +72 -1
  10. package/dist_ts/classes.authoritydatabase.d.ts +5 -0
  11. package/dist_ts/classes.authoritydatabase.js +20 -5
  12. package/dist_ts/classes.authorityimport.d.ts +161 -0
  13. package/dist_ts/classes.authorityimport.js +638 -0
  14. package/dist_ts/classes.authoritymodels.d.ts +12 -0
  15. package/dist_ts/classes.authoritymodels.js +17 -1
  16. package/dist_ts/index.d.ts +1 -0
  17. package/dist_ts/index.js +6 -3
  18. package/dist_ts/ts_migration/legacysources/authswitchstores.d.ts +2 -0
  19. package/dist_ts/ts_migration/legacysources/authswitchstores.js +244 -0
  20. package/dist_ts/ts_migration/legacysources/index.d.ts +18 -0
  21. package/dist_ts/ts_migration/legacysources/index.js +24 -0
  22. package/dist_ts/ts_migration/legacysources/material.d.ts +37 -0
  23. package/dist_ts/ts_migration/legacysources/material.js +120 -0
  24. package/dist_ts/ts_migration/legacysources/nativestores.d.ts +2 -0
  25. package/dist_ts/ts_migration/legacysources/nativestores.js +207 -0
  26. package/dist_ts/ts_migration/legacysources/shared.d.ts +124 -0
  27. package/dist_ts/ts_migration/legacysources/shared.js +142 -0
  28. package/package.json +5 -1
  29. package/readme.md +46 -0
  30. package/ts/00_commitinfo_data.ts +1 -1
  31. package/ts/authority-import-contract.ts +216 -0
  32. package/ts/classes.authoritycli.ts +164 -10
  33. package/ts/classes.authorityclient.ts +22 -0
  34. package/ts/classes.authoritydaemon.ts +72 -0
  35. package/ts/classes.authoritydatabase.ts +20 -4
  36. package/ts/classes.authorityimport.ts +718 -0
  37. package/ts/classes.authoritymodels.ts +21 -0
  38. package/ts/index.ts +5 -2
  39. package/ts/ts_migration/legacysources/authswitchstores.ts +211 -0
  40. package/ts/ts_migration/legacysources/index.ts +35 -0
  41. package/ts/ts_migration/legacysources/material.ts +134 -0
  42. package/ts/ts_migration/legacysources/nativestores.ts +204 -0
  43. package/ts/ts_migration/legacysources/shared.ts +220 -0
@@ -3,7 +3,7 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@modelprofile.com/authswitch',
6
- version: '8.0.0',
6
+ version: '8.1.0',
7
7
  description: 'Manage Codex, OpenCode and Claude Code accounts with guided switching and live usage status'
8
8
  };
9
9
  //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiMDBfY29tbWl0aW5mb19kYXRhLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvMDBfY29tbWl0aW5mb19kYXRhLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOztHQUVHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sVUFBVSxHQUFHO0lBQ3hCLElBQUksRUFBRSw4QkFBOEI7SUFDcEMsT0FBTyxFQUFFLE9BQU87SUFDaEIsV0FBVyxFQUFFLDZGQUE2RjtDQUMzRyxDQUFBIn0=
@@ -0,0 +1,207 @@
1
+ import type { ITypedRequest } from '@api.global/typedrequest-interfaces';
2
+ /**
3
+ * The one-time account import: reading the legacy stores that existed before the authority, and the
4
+ * credential-free report an owner reads before anything is imported.
5
+ *
6
+ * Importing a refresh token is a transfer of ownership, not a copy. The provider rotates a refresh token on
7
+ * use, so the moment one holder refreshes, every other copy of that token is dead. This contract therefore
8
+ * separates the classes of legacy source by who refreshes them after the import, and names the proof each
9
+ * class can offer without rotating anything.
10
+ */
11
+ /** Mirrors the migration ledger's source kinds, which are the only sources a ledger row can describe. */
12
+ export type TAuthSwitchImportSourceKind = 'authswitch_stash' | 'authswitch_backup' | 'agl_flex' | 'codex_native' | 'opencode_native' | 'claude_native';
13
+ /**
14
+ * The ledger statuses a source can already hold when an inventory runs.
15
+ *
16
+ * - `prepared`: the source is named in the authority and nothing has been sent for it yet.
17
+ * - `pending_native_owner`: the account is registered and its native tool keeps the refresher, but the
18
+ * store could not be proven live. Nothing rotating was ever sent, so it is proven by submitting the
19
+ * source again once that tool has refreshed its own store.
20
+ * - `verified` and `complete`: the import settled; `complete` is the tombstone for that source.
21
+ * - `quarantined`: a rotating refresh may already have been consumed for this source, so it is never
22
+ * submitted again and its account is repaired by a device sign-in.
23
+ */
24
+ export type TAuthSwitchImportLedgerStatus = 'pending_native_owner' | 'prepared' | 'verified' | 'complete' | 'quarantined';
25
+ /**
26
+ * What the ownership rule allows for a source.
27
+ *
28
+ * - `import`: the authority becomes the single refresher. Nothing else owns this copy, so taking it over
29
+ * strands no other holder.
30
+ * - `project`: a native tool keeps refreshing its own store; the authority registers the account and holds
31
+ * the grant as a projection it must never refresh. Two refreshers on one refresh token is the failure this
32
+ * avoids.
33
+ * - `refuse`: the source cannot become an authority grant at all, and the owner signs in again instead.
34
+ */
35
+ export type TAuthSwitchImportTreatment = 'import' | 'project' | 'refuse';
36
+ /**
37
+ * What can make this source's ledger row `verified`.
38
+ *
39
+ * - `rotating_refresh`: one real refresh through the authority. It proves the refresh token was live and
40
+ * makes the authority its holder in the same step -- and it is the point of no return, because the legacy
41
+ * copy is stale from that moment.
42
+ * - `access_token_read`: a provider read with the stored access token alone, matched against the identity
43
+ * derived locally from the same source bytes. It rotates nothing, so a native refresher keeps its grant.
44
+ * It is available only while that access token is still valid.
45
+ * - `none`: no proof exists for this source; it cannot be verified and cannot be imported.
46
+ */
47
+ export type TAuthSwitchImportProof = 'rotating_refresh' | 'access_token_read' | 'none';
48
+ /**
49
+ * One legacy source as the inventory found it. Credential-free by construction: identity claims and a digest
50
+ * of the source bytes, never a token, a token digest, or the source path.
51
+ */
52
+ export interface IAuthSwitchImportSource {
53
+ /** The migration ledger row this source would use. Derived from its kind and path, so it is stable. */
54
+ id: string;
55
+ sourceKind: TAuthSwitchImportSourceKind;
56
+ /** The source location as a hash. The ledger never stores a path and this DTO never exposes one. */
57
+ sourcePathHash: string;
58
+ /** Binds a later import to exactly these bytes. Changes whenever the source changes. */
59
+ sourceDigest: string;
60
+ treatment: TAuthSwitchImportTreatment;
61
+ proof: TAuthSwitchImportProof;
62
+ /** The authority account this source resolves to, derived locally from its identity claims. */
63
+ accountId: string | null;
64
+ providerId: string | null;
65
+ email: string | null;
66
+ plan: string | null;
67
+ label: string | null;
68
+ /** Expiry claim of the stored access token. It decides whether a non-rotating proof is available now. */
69
+ accessExpiresAt: string | null;
70
+ /** Remote-control enrollments carried by this source; `null` when they could not be counted. */
71
+ enrollmentCount: number | null;
72
+ /** The ledger row for this source, when one already exists. */
73
+ ledgerStatus: TAuthSwitchImportLedgerStatus | null;
74
+ /** Why this source cannot be submitted as it stands. An empty list means it can. */
75
+ problems: string[];
76
+ }
77
+ /** The complete credential-free report. `notice` is what the owner must know before importing anything. */
78
+ export interface IAuthSwitchImportInventory {
79
+ generatedAt: string;
80
+ /** Identifies the credential locations this inventory read, as `authSwitchEnvironmentId` computes them. */
81
+ environmentId: string;
82
+ notice: string[];
83
+ sources: IAuthSwitchImportSource[];
84
+ /** Locations that could not be read at all, so an empty result never looks like an empty store. */
85
+ problems: string[];
86
+ }
87
+ /** Management-only. Reads legacy stores and the ledger; changes nothing, and carries no credential. */
88
+ export interface IReq_AuthSwitchImportInventory extends ITypedRequest {
89
+ method: 'authswitch.authority.import.inventory';
90
+ request: Record<string, never>;
91
+ response: {
92
+ inventory: IAuthSwitchImportInventory;
93
+ };
94
+ }
95
+ /** A ChatGPT login held by a store outside this package, submitted by the backend that owns its format. */
96
+ export interface IAuthSwitchImportOpenAiCredential {
97
+ providerId: 'openai';
98
+ accessToken: string;
99
+ refreshToken: string;
100
+ idToken: string;
101
+ }
102
+ /**
103
+ * What is being imported.
104
+ *
105
+ * A source this daemon can read itself is named only by its id: the daemon re-runs its readers, resolves the
106
+ * location from the hash and re-checks the digest, so no credential crosses the socket and no caller can
107
+ * substitute bytes the owner never approved. Only a store this package cannot read -- AGL's sealed Flex
108
+ * records -- submits material, and that is the whole reason submit lives on the backend-only runtime socket.
109
+ */
110
+ export type TAuthSwitchImportSubmission = {
111
+ kind: 'local';
112
+ sourceId: string;
113
+ sourceDigest: string;
114
+ } | {
115
+ kind: 'external';
116
+ sourceKind: 'agl_flex';
117
+ sourcePathHash: string;
118
+ sourceDigest: string;
119
+ credential: IAuthSwitchImportOpenAiCredential;
120
+ };
121
+ /** The outcome of one source. `quarantined` always names what to do instead, never a silent failure. */
122
+ export interface IAuthSwitchImportResult {
123
+ sourceId: string;
124
+ sourceKind: TAuthSwitchImportSourceKind;
125
+ treatment: TAuthSwitchImportTreatment;
126
+ status: TAuthSwitchImportLedgerStatus;
127
+ accountId: string | null;
128
+ loginId: string | null;
129
+ problems: string[];
130
+ }
131
+ /**
132
+ * What the owner does next with one source.
133
+ *
134
+ * - `none`: the import settled; nothing is outstanding.
135
+ * - `resume`: submit the same source again. Nothing rotating has been sent for this source, so a second
136
+ * attempt is safe: an interrupted adopt continues from its durable record, and a native store that could
137
+ * not be proven live is proven the moment its own tool has refreshed it.
138
+ * - `device_login`: this source is finished as a source. Its account needs a fresh device sign-in, because a
139
+ * rotating refresh token whose outcome is unknown is never sent a second time.
140
+ */
141
+ export type TAuthSwitchImportAction = 'none' | 'resume' | 'device_login';
142
+ /**
143
+ * Marks an error answer whose text is an instruction for the owner, not a report of a fault.
144
+ *
145
+ * The daemon sets it on the refusals the importer authors and on nothing else. Every other failure answers
146
+ * with the transport's own sanitised text, which a caller must never present as if it said what to do next --
147
+ * for a submit in particular, an unmarked failure means the outcome is unknown and the source must be read
148
+ * with `authswitch.authority.import.status` rather than submitted again.
149
+ */
150
+ export declare const authSwitchImportRefusalReason = "authswitch_import_refusal";
151
+ /** True when the daemon answered with an importer refusal, so `error.message` is the instruction to show. */
152
+ export declare const isAuthSwitchImportRefusal: (error: unknown) => boolean;
153
+ /**
154
+ * Where one source stands, read from the migration ledger, the grant it produced and its handoff.
155
+ *
156
+ * Only sources the authority has been told about appear here. A source that was never submitted has no
157
+ * ledger row; `authority import inventory` is the read that looks at the host and lists those.
158
+ */
159
+ export interface IAuthSwitchImportStatusEntry {
160
+ sourceId: string;
161
+ sourceKind: TAuthSwitchImportSourceKind;
162
+ status: TAuthSwitchImportLedgerStatus;
163
+ accountId: string | null;
164
+ loginId: string | null;
165
+ /**
166
+ * Who refreshes that login now, or `null` when the ledger row names no grant yet. `none` is the absence of
167
+ * a refresher: the grant needs a device sign-in before anything can use it again.
168
+ */
169
+ owner: 'daemon' | 'claude_native' | 'legacy_native' | 'none' | null;
170
+ action: TAuthSwitchImportAction;
171
+ statusObservedAt: string;
172
+ }
173
+ export interface IAuthSwitchImportStatus {
174
+ generatedAt: string;
175
+ entries: IAuthSwitchImportStatusEntry[];
176
+ nextCursor: string | null;
177
+ }
178
+ /** Management-only. Reads durable records only; it opens no legacy store and carries no credential. */
179
+ export interface IReq_AuthSwitchImportStatus extends ITypedRequest {
180
+ method: 'authswitch.authority.import.status';
181
+ request: {
182
+ after?: string;
183
+ limit?: number;
184
+ };
185
+ response: {
186
+ status: IAuthSwitchImportStatus;
187
+ };
188
+ }
189
+ /**
190
+ * Backend-only. One source, one ownership decision.
191
+ *
192
+ * Both flags are required and both must be `true`. `callerQuiescent` is the caller's own assertion, because
193
+ * this daemon can observe a watch, a native process and Codex's app-server but cannot observe a host that
194
+ * uses the legacy library in its own process. `acknowledgeRunOrder` is the owner's acknowledgement that an
195
+ * imported source's legacy copy is dead and that the everyday commands must already run on the authority.
196
+ */
197
+ export interface IReq_AuthSwitchImportSubmit extends ITypedRequest {
198
+ method: 'authswitch.authority.import.submit';
199
+ request: {
200
+ submission: TAuthSwitchImportSubmission;
201
+ callerQuiescent: true;
202
+ acknowledgeRunOrder: true;
203
+ };
204
+ response: {
205
+ result: IAuthSwitchImportResult;
206
+ };
207
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Marks an error answer whose text is an instruction for the owner, not a report of a fault.
3
+ *
4
+ * The daemon sets it on the refusals the importer authors and on nothing else. Every other failure answers
5
+ * with the transport's own sanitised text, which a caller must never present as if it said what to do next --
6
+ * for a submit in particular, an unmarked failure means the outcome is unknown and the source must be read
7
+ * with `authswitch.authority.import.status` rather than submitted again.
8
+ */
9
+ export const authSwitchImportRefusalReason = 'authswitch_import_refusal';
10
+ /** True when the daemon answered with an importer refusal, so `error.message` is the instruction to show. */
11
+ export const isAuthSwitchImportRefusal = (error) => {
12
+ if (typeof error !== 'object' || error === null || !('errorData' in error))
13
+ return false;
14
+ const data = error.errorData;
15
+ return typeof data === 'object' && data !== null && 'reason' in data
16
+ && data.reason === authSwitchImportRefusalReason;
17
+ };
18
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXV0aG9yaXR5LWltcG9ydC1jb250cmFjdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2F1dGhvcml0eS1pbXBvcnQtY29udHJhY3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBbUpBOzs7Ozs7O0dBT0c7QUFDSCxNQUFNLENBQUMsTUFBTSw2QkFBNkIsR0FBRywyQkFBMkIsQ0FBQztBQUV6RSw2R0FBNkc7QUFDN0csTUFBTSxDQUFDLE1BQU0seUJBQXlCLEdBQUcsQ0FBQyxLQUFjLEVBQVcsRUFBRTtJQUNuRSxJQUFJLE9BQU8sS0FBSyxLQUFLLFFBQVEsSUFBSSxLQUFLLEtBQUssSUFBSSxJQUFJLENBQUMsQ0FBQyxXQUFXLElBQUksS0FBSyxDQUFDO1FBQUUsT0FBTyxLQUFLLENBQUM7SUFDekYsTUFBTSxJQUFJLEdBQUcsS0FBSyxDQUFDLFNBQVMsQ0FBQztJQUM3QixPQUFPLE9BQU8sSUFBSSxLQUFLLFFBQVEsSUFBSSxJQUFJLEtBQUssSUFBSSxJQUFJLFFBQVEsSUFBSSxJQUFJO1dBQy9ELElBQUksQ0FBQyxNQUFNLEtBQUssNkJBQTZCLENBQUM7QUFDckQsQ0FBQyxDQUFDIn0=
@@ -1,7 +1,11 @@
1
1
  import type { IAuthSwitchDoctorPage, IAuthSwitchOperation, IAuthSwitchPreuseOperation, IAuthSwitchSnapshot, IReq_AuthSwitchDoctor, IReq_AuthSwitchStartPreuse } from './authority-contract.js';
2
+ import type { IAuthSwitchImportInventory, IAuthSwitchImportResult, IAuthSwitchImportStatus, IReq_AuthSwitchImportStatus } from './authority-import-contract.js';
2
3
  export interface IAuthSwitchAuthorityCliClient {
3
4
  snapshotAll(): Promise<IAuthSwitchSnapshot>;
4
5
  doctorPage(request: IReq_AuthSwitchDoctor['request']): Promise<IAuthSwitchDoctorPage>;
6
+ importInventory(): Promise<IAuthSwitchImportInventory>;
7
+ importStatus(request: IReq_AuthSwitchImportStatus['request']): Promise<IAuthSwitchImportStatus>;
8
+ submitImport(sourceId: string, sourceDigest: string): Promise<IAuthSwitchImportResult>;
5
9
  beginAddOpenAi(operationId: string): Promise<IAuthSwitchOperation>;
6
10
  beginReauthOpenAi(operationId: string, accountId: string, loginId: string, purpose: 'openai_managed'): Promise<IAuthSwitchOperation>;
7
11
  getOperation(operationId: string): Promise<IAuthSwitchOperation>;