@modelprofile.com/authswitch 8.0.0 → 8.2.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 (61) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts/authority-contract.d.ts +56 -0
  3. package/dist_ts/authority-contract.js +54 -2
  4. package/dist_ts/authority-import-contract.d.ts +211 -0
  5. package/dist_ts/authority-import-contract.js +17 -0
  6. package/dist_ts/classes.authoritybroker.js +3 -2
  7. package/dist_ts/classes.authoritycli.d.ts +4 -0
  8. package/dist_ts/classes.authoritycli.js +148 -11
  9. package/dist_ts/classes.authorityclient.d.ts +24 -11
  10. package/dist_ts/classes.authorityclient.js +40 -22
  11. package/dist_ts/classes.authoritydaemon.d.ts +12 -0
  12. package/dist_ts/classes.authoritydaemon.js +123 -36
  13. package/dist_ts/classes.authoritydatabase.d.ts +14 -1
  14. package/dist_ts/classes.authoritydatabase.js +48 -16
  15. package/dist_ts/classes.authorityimport.d.ts +176 -0
  16. package/dist_ts/classes.authorityimport.js +664 -0
  17. package/dist_ts/classes.authoritymodels.d.ts +16 -1
  18. package/dist_ts/classes.authoritymodels.js +31 -3
  19. package/dist_ts/classes.authorityservice.d.ts +10 -0
  20. package/dist_ts/classes.authorityservice.js +24 -1
  21. package/dist_ts/classes.claudeauthority.js +19 -11
  22. package/dist_ts/classes.claudenative.d.ts +63 -3
  23. package/dist_ts/classes.claudenative.js +68 -8
  24. package/dist_ts/index.d.ts +1 -0
  25. package/dist_ts/index.js +6 -3
  26. package/dist_ts/ts_migration/0003_claude_handoff_proof.d.ts +13 -0
  27. package/dist_ts/ts_migration/0003_claude_handoff_proof.js +20 -0
  28. package/dist_ts/ts_migration/index.js +3 -1
  29. package/dist_ts/ts_migration/legacysources/authswitchstores.d.ts +2 -0
  30. package/dist_ts/ts_migration/legacysources/authswitchstores.js +244 -0
  31. package/dist_ts/ts_migration/legacysources/index.d.ts +18 -0
  32. package/dist_ts/ts_migration/legacysources/index.js +24 -0
  33. package/dist_ts/ts_migration/legacysources/material.d.ts +37 -0
  34. package/dist_ts/ts_migration/legacysources/material.js +120 -0
  35. package/dist_ts/ts_migration/legacysources/nativestores.d.ts +2 -0
  36. package/dist_ts/ts_migration/legacysources/nativestores.js +214 -0
  37. package/dist_ts/ts_migration/legacysources/shared.d.ts +125 -0
  38. package/dist_ts/ts_migration/legacysources/shared.js +140 -0
  39. package/package.json +9 -1
  40. package/readme.md +111 -2
  41. package/ts/00_commitinfo_data.ts +1 -1
  42. package/ts/authority-contract.ts +106 -0
  43. package/ts/authority-import-contract.ts +217 -0
  44. package/ts/classes.authoritybroker.ts +2 -1
  45. package/ts/classes.authoritycli.ts +164 -10
  46. package/ts/classes.authorityclient.ts +65 -21
  47. package/ts/classes.authoritydaemon.ts +121 -34
  48. package/ts/classes.authoritydatabase.ts +49 -15
  49. package/ts/classes.authorityimport.ts +746 -0
  50. package/ts/classes.authoritymodels.ts +32 -2
  51. package/ts/classes.authorityservice.ts +27 -0
  52. package/ts/classes.claudeauthority.ts +22 -10
  53. package/ts/classes.claudenative.ts +104 -10
  54. package/ts/index.ts +5 -2
  55. package/ts/ts_migration/0003_claude_handoff_proof.ts +19 -0
  56. package/ts/ts_migration/index.ts +2 -0
  57. package/ts/ts_migration/legacysources/authswitchstores.ts +211 -0
  58. package/ts/ts_migration/legacysources/index.ts +35 -0
  59. package/ts/ts_migration/legacysources/material.ts +134 -0
  60. package/ts/ts_migration/legacysources/nativestores.ts +212 -0
  61. package/ts/ts_migration/legacysources/shared.ts +218 -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.2.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=
@@ -1,6 +1,52 @@
1
1
  import type { ITypedRequest } from '@api.global/typedrequest-interfaces';
2
2
  import type { IAuthSwitchUsageSnapshot } from './classes.authorityusage.js';
3
3
  export type { IAuthSwitchUsageSnapshot } from './classes.authorityusage.js';
4
+ /**
5
+ * Why the authority declined, as a closed set a client branches on without reading the text.
6
+ *
7
+ * A refusal is an answer: the daemon did the check, nothing was left half-done, and the message says what
8
+ * the owner does instead. Everything else stays a fault whose text the transport replaces, and a caller
9
+ * must never present a fault as if it said what to do next.
10
+ */
11
+ export type TAuthSwitchRefusalCode = 'authority_closing' | 'not_found' | 'account_changed' | 'account_busy' | 'login_unavailable' | 'native_owner_holds_login' | 'claude_home_unregistered' | 'claude_handoff_pending' | 'claude_receipt_missing' | 'import_refusal';
12
+ /** The marker the importer has published since 8.1.0; it keeps its own value on the wire. */
13
+ export declare const authSwitchImportRefusalMarker = "authswitch_import_refusal";
14
+ export declare const authSwitchRefusalReason = "authswitch_refusal";
15
+ export type TAuthSwitchRefusalReason = typeof authSwitchRefusalReason | typeof authSwitchImportRefusalMarker;
16
+ /** Everything a refusal puts on the wire beside its message: two literals, never a value of its own. */
17
+ export interface IAuthSwitchRefusalData {
18
+ reason: TAuthSwitchRefusalReason;
19
+ code: TAuthSwitchRefusalCode;
20
+ }
21
+ /** What the owner is told when the answer is an instruction rather than a fault. */
22
+ export interface IAuthSwitchRefusal {
23
+ code: TAuthSwitchRefusalCode;
24
+ /** The instruction, authored by the handler that declined. */
25
+ instruction: string;
26
+ }
27
+ /**
28
+ * One refusal, thrown by a handler and turned into a marked answer by the router that owns the wire.
29
+ *
30
+ * It accepts nothing but a code of the closed set and the text the owner is to read, and builds the marker
31
+ * from the code alone, so no handler can attach a value of its own to what a client branches on.
32
+ */
33
+ export declare class AuthSwitchRefusal extends Error {
34
+ readonly code: TAuthSwitchRefusalCode;
35
+ constructor(code: TAuthSwitchRefusalCode, instruction: string);
36
+ /** Everything this refusal puts on the wire beside its message. */
37
+ get data(): IAuthSwitchRefusalData;
38
+ }
39
+ /**
40
+ * The refusal an error answer carries, or null when it is a fault whose text says nothing.
41
+ *
42
+ * This is the only reader of `errorData` in the package: the importer's published predicate is expressed
43
+ * through it, so a caller never has to know which of the two markers a route uses.
44
+ *
45
+ * 8.1.0 marked the importer's refusal with the reason alone. A per-user daemon keeps running across an
46
+ * upgrade until its owner restarts it, so a newer client still meets that payload, and it names the one
47
+ * refusal that release could answer with -- which is why it is read rather than treated as a fault.
48
+ */
49
+ export declare const asAuthSwitchRefusal: (error: unknown) => IAuthSwitchRefusal | null;
4
50
  /** Credential-free account management contract. Safe to import in browser code. */
5
51
  export type TAuthSwitchLoginPurpose = 'openai_managed' | 'claude_host_native' | 'claude_container_setup' | 'opencode_native';
6
52
  export type TAuthSwitchLoginHealth = 'ready' | 'refreshing' | 'retry_wait' | 'needs_reauth' | 'unverified' | 'pending_handoff' | 'handoff_quarantined' | 'removed';
@@ -231,6 +277,14 @@ export interface IAuthSwitchPreuseOperation {
231
277
  finishedAt: string | null;
232
278
  revision: number;
233
279
  }
280
+ /**
281
+ * Why the host could not prove that Claude Code uses its own native login.
282
+ *
283
+ * Each value names one condition of that proof, so an owner is told what to change: install the supported
284
+ * release, sign in to a subscriber plan, remove a credential override or a settings source, let the
285
+ * profile read succeed, or close the running session.
286
+ */
287
+ export type TAuthSwitchClaudeProofFailure = 'unsupported_release' | 'override' | 'profile' | 'profile_unreadable' | 'settings' | 'subscription' | 'running_session';
234
288
  /** Credential-free native file ownership result. It makes no claim about a running session's active request. */
235
289
  export interface IAuthSwitchClaudeNativeHandoff {
236
290
  id: string;
@@ -240,6 +294,8 @@ export interface IAuthSwitchClaudeNativeHandoff {
240
294
  phase: 'reserved' | 'prepared' | 'committed' | 'aborted' | 'quarantined';
241
295
  problem: 'none' | 'native_uncertain' | 'foreign_or_torn' | 'unsupported_effective_auth' | 'database_uncertain';
242
296
  runningEffectiveAuth: 'no_scoped_sessions' | 'unsupported_effective_auth' | null;
297
+ /** Which condition of the native-login proof failed, when `problem` is `unsupported_effective_auth`. */
298
+ proofFailure?: TAuthSwitchClaudeProofFailure;
243
299
  updatedAt: string;
244
300
  }
245
301
  export interface IReq_AuthSwitchSwitchClaudeNative extends ITypedRequest {
@@ -1,3 +1,55 @@
1
- export {};
1
+ /** The marker the importer has published since 8.1.0; it keeps its own value on the wire. */
2
+ export const authSwitchImportRefusalMarker = 'authswitch_import_refusal';
3
+ export const authSwitchRefusalReason = 'authswitch_refusal';
4
+ const refusalCodes = new Set([
5
+ 'authority_closing', 'not_found', 'account_changed', 'account_busy',
6
+ 'login_unavailable', 'native_owner_holds_login', 'claude_home_unregistered',
7
+ 'claude_handoff_pending', 'claude_receipt_missing', 'import_refusal',
8
+ ]);
9
+ const isRefusalCode = (value) => typeof value === 'string' && refusalCodes.has(value);
10
+ /** The importer keeps the marker it published; every other code shares the authority's own. */
11
+ const reasonOf = (code) => code === 'import_refusal' ? authSwitchImportRefusalMarker : authSwitchRefusalReason;
12
+ /**
13
+ * One refusal, thrown by a handler and turned into a marked answer by the router that owns the wire.
14
+ *
15
+ * It accepts nothing but a code of the closed set and the text the owner is to read, and builds the marker
16
+ * from the code alone, so no handler can attach a value of its own to what a client branches on.
17
+ */
18
+ export class AuthSwitchRefusal extends Error {
19
+ code;
20
+ constructor(code, instruction) {
21
+ super(instruction);
22
+ this.code = code;
23
+ }
24
+ /** Everything this refusal puts on the wire beside its message. */
25
+ get data() { return { reason: reasonOf(this.code), code: this.code }; }
26
+ }
27
+ /**
28
+ * The refusal an error answer carries, or null when it is a fault whose text says nothing.
29
+ *
30
+ * This is the only reader of `errorData` in the package: the importer's published predicate is expressed
31
+ * through it, so a caller never has to know which of the two markers a route uses.
32
+ *
33
+ * 8.1.0 marked the importer's refusal with the reason alone. A per-user daemon keeps running across an
34
+ * upgrade until its owner restarts it, so a newer client still meets that payload, and it names the one
35
+ * refusal that release could answer with -- which is why it is read rather than treated as a fault.
36
+ */
37
+ export const asAuthSwitchRefusal = (error) => {
38
+ if (error instanceof AuthSwitchRefusal)
39
+ return { code: error.code, instruction: error.message };
40
+ if (typeof error !== 'object' || error === null || !('errorData' in error))
41
+ return null;
42
+ const data = error.errorData;
43
+ if (typeof data !== 'object' || data === null || !('reason' in data))
44
+ return null;
45
+ const code = 'code' in data ? data.code : undefined;
46
+ const instruction = 'message' in error && typeof error.message === 'string' ? error.message : '';
47
+ if (data.reason === authSwitchImportRefusalMarker) {
48
+ return code === undefined || code === 'import_refusal' ? { code: 'import_refusal', instruction } : null;
49
+ }
50
+ if (data.reason !== authSwitchRefusalReason || !isRefusalCode(code))
51
+ return null;
52
+ return { code, instruction };
53
+ };
2
54
  /** Runtime capability is returned only to trusted backend callers; do not expose this response in a browser API. */
3
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXV0aG9yaXR5LWNvbnRyYWN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvYXV0aG9yaXR5LWNvbnRyYWN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUF5V0Esb0hBQW9IIn0=
55
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXV0aG9yaXR5LWNvbnRyYWN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvYXV0aG9yaXR5LWNvbnRyYWN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQXVCQSw2RkFBNkY7QUFDN0YsTUFBTSxDQUFDLE1BQU0sNkJBQTZCLEdBQUcsMkJBQTJCLENBQUM7QUFDekUsTUFBTSxDQUFDLE1BQU0sdUJBQXVCLEdBQUcsb0JBQW9CLENBQUM7QUFpQjVELE1BQU0sWUFBWSxHQUF3QixJQUFJLEdBQUcsQ0FBeUI7SUFDeEUsbUJBQW1CLEVBQUUsV0FBVyxFQUFFLGlCQUFpQixFQUFFLGNBQWM7SUFDbkUsbUJBQW1CLEVBQUUsMEJBQTBCLEVBQUUsMEJBQTBCO0lBQzNFLHdCQUF3QixFQUFFLHdCQUF3QixFQUFFLGdCQUFnQjtDQUNyRSxDQUFDLENBQUM7QUFFSCxNQUFNLGFBQWEsR0FBRyxDQUFDLEtBQWMsRUFBbUMsRUFBRSxDQUN4RSxPQUFPLEtBQUssS0FBSyxRQUFRLElBQUksWUFBWSxDQUFDLEdBQUcsQ0FBQyxLQUFLLENBQUMsQ0FBQztBQUV2RCwrRkFBK0Y7QUFDL0YsTUFBTSxRQUFRLEdBQUcsQ0FBQyxJQUE0QixFQUE0QixFQUFFLENBQzFFLElBQUksS0FBSyxnQkFBZ0IsQ0FBQyxDQUFDLENBQUMsNkJBQTZCLENBQUMsQ0FBQyxDQUFDLHVCQUF1QixDQUFDO0FBRXRGOzs7OztHQUtHO0FBQ0gsTUFBTSxPQUFPLGlCQUFrQixTQUFRLEtBQUs7SUFDMUIsSUFBSSxDQUF5QjtJQUU3QyxZQUFZLElBQTRCLEVBQUUsV0FBbUI7UUFDM0QsS0FBSyxDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBQ25CLElBQUksQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDO0lBQ25CLENBQUM7SUFFRCxtRUFBbUU7SUFDbkUsSUFBVyxJQUFJLEtBQTZCLE9BQU8sRUFBRSxNQUFNLEVBQUUsUUFBUSxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxJQUFJLEVBQUUsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDLENBQUMsQ0FBQztDQUN2RztBQUVEOzs7Ozs7Ozs7R0FTRztBQUNILE1BQU0sQ0FBQyxNQUFNLG1CQUFtQixHQUFHLENBQUMsS0FBYyxFQUE2QixFQUFFO0lBQy9FLElBQUksS0FBSyxZQUFZLGlCQUFpQjtRQUFFLE9BQU8sRUFBRSxJQUFJLEVBQUUsS0FBSyxDQUFDLElBQUksRUFBRSxXQUFXLEVBQUUsS0FBSyxDQUFDLE9BQU8sRUFBRSxDQUFDO0lBQ2hHLElBQUksT0FBTyxLQUFLLEtBQUssUUFBUSxJQUFJLEtBQUssS0FBSyxJQUFJLElBQUksQ0FBQyxDQUFDLFdBQVcsSUFBSSxLQUFLLENBQUM7UUFBRSxPQUFPLElBQUksQ0FBQztJQUN4RixNQUFNLElBQUksR0FBWSxLQUFLLENBQUMsU0FBUyxDQUFDO0lBQ3RDLElBQUksT0FBTyxJQUFJLEtBQUssUUFBUSxJQUFJLElBQUksS0FBSyxJQUFJLElBQUksQ0FBQyxDQUFDLFFBQVEsSUFBSSxJQUFJLENBQUM7UUFBRSxPQUFPLElBQUksQ0FBQztJQUNsRixNQUFNLElBQUksR0FBWSxNQUFNLElBQUksSUFBSSxDQUFDLENBQUMsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUM7SUFDN0QsTUFBTSxXQUFXLEdBQUcsU0FBUyxJQUFJLEtBQUssSUFBSSxPQUFPLEtBQUssQ0FBQyxPQUFPLEtBQUssUUFBUSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7SUFDakcsSUFBSSxJQUFJLENBQUMsTUFBTSxLQUFLLDZCQUE2QixFQUFFLENBQUM7UUFDbEQsT0FBTyxJQUFJLEtBQUssU0FBUyxJQUFJLElBQUksS0FBSyxnQkFBZ0IsQ0FBQyxDQUFDLENBQUMsRUFBRSxJQUFJLEVBQUUsZ0JBQWdCLEVBQUUsV0FBVyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQztJQUMxRyxDQUFDO0lBQ0QsSUFBSSxJQUFJLENBQUMsTUFBTSxLQUFLLHVCQUF1QixJQUFJLENBQUMsYUFBYSxDQUFDLElBQUksQ0FBQztRQUFFLE9BQU8sSUFBSSxDQUFDO0lBQ2pGLE9BQU8sRUFBRSxJQUFJLEVBQUUsV0FBVyxFQUFFLENBQUM7QUFDL0IsQ0FBQyxDQUFDO0FBb1hGLG9IQUFvSCJ9
@@ -0,0 +1,211 @@
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: a refusal any other route
146
+ * authors carries the authority's own marker, and a failure neither of them decided answers with the
147
+ * transport's sanitised text, which a caller must never present as if it said what to do next. For a
148
+ * submit in particular, an unmarked failure means the outcome is unknown and the source must be read with
149
+ * `authswitch.authority.import.status` rather than submitted again.
150
+ *
151
+ * It is one code of the authority's refusal contract (`./authority-contract`), which every route now uses;
152
+ * this marker keeps its own value on the wire so a consumer written against 8.1.0 keeps working.
153
+ */
154
+ export declare const authSwitchImportRefusalReason = "authswitch_import_refusal";
155
+ /** True when the daemon answered with an importer refusal, so `error.message` is the instruction to show. */
156
+ export declare const isAuthSwitchImportRefusal: (error: unknown) => boolean;
157
+ /**
158
+ * Where one source stands, read from the migration ledger, the grant it produced and its handoff.
159
+ *
160
+ * Only sources the authority has been told about appear here. A source that was never submitted has no
161
+ * ledger row; `authority import inventory` is the read that looks at the host and lists those.
162
+ */
163
+ export interface IAuthSwitchImportStatusEntry {
164
+ sourceId: string;
165
+ sourceKind: TAuthSwitchImportSourceKind;
166
+ status: TAuthSwitchImportLedgerStatus;
167
+ accountId: string | null;
168
+ loginId: string | null;
169
+ /**
170
+ * Who refreshes that login now, or `null` when the ledger row names no grant yet. `none` is the absence of
171
+ * a refresher: the grant needs a device sign-in before anything can use it again.
172
+ */
173
+ owner: 'daemon' | 'claude_native' | 'legacy_native' | 'none' | null;
174
+ action: TAuthSwitchImportAction;
175
+ statusObservedAt: string;
176
+ }
177
+ export interface IAuthSwitchImportStatus {
178
+ generatedAt: string;
179
+ entries: IAuthSwitchImportStatusEntry[];
180
+ nextCursor: string | null;
181
+ }
182
+ /** Management-only. Reads durable records only; it opens no legacy store and carries no credential. */
183
+ export interface IReq_AuthSwitchImportStatus extends ITypedRequest {
184
+ method: 'authswitch.authority.import.status';
185
+ request: {
186
+ after?: string;
187
+ limit?: number;
188
+ };
189
+ response: {
190
+ status: IAuthSwitchImportStatus;
191
+ };
192
+ }
193
+ /**
194
+ * Backend-only. One source, one ownership decision.
195
+ *
196
+ * Both flags are required and both must be `true`. `callerQuiescent` is the caller's own assertion, because
197
+ * this daemon can observe a watch, a native process and Codex's app-server but cannot observe a host that
198
+ * uses the legacy library in its own process. `acknowledgeRunOrder` is the owner's acknowledgement that an
199
+ * imported source's legacy copy is dead and that the everyday commands must already run on the authority.
200
+ */
201
+ export interface IReq_AuthSwitchImportSubmit extends ITypedRequest {
202
+ method: 'authswitch.authority.import.submit';
203
+ request: {
204
+ submission: TAuthSwitchImportSubmission;
205
+ callerQuiescent: true;
206
+ acknowledgeRunOrder: true;
207
+ };
208
+ response: {
209
+ result: IAuthSwitchImportResult;
210
+ };
211
+ }
@@ -0,0 +1,17 @@
1
+ import { asAuthSwitchRefusal, authSwitchImportRefusalMarker } from './authority-contract.js';
2
+ /**
3
+ * Marks an error answer whose text is an instruction for the owner, not a report of a fault.
4
+ *
5
+ * The daemon sets it on the refusals the importer authors and on nothing else: a refusal any other route
6
+ * authors carries the authority's own marker, and a failure neither of them decided answers with the
7
+ * transport's sanitised text, which a caller must never present as if it said what to do next. For a
8
+ * submit in particular, an unmarked failure means the outcome is unknown and the source must be read with
9
+ * `authswitch.authority.import.status` rather than submitted again.
10
+ *
11
+ * It is one code of the authority's refusal contract (`./authority-contract`), which every route now uses;
12
+ * this marker keeps its own value on the wire so a consumer written against 8.1.0 keeps working.
13
+ */
14
+ export const authSwitchImportRefusalReason = authSwitchImportRefusalMarker;
15
+ /** True when the daemon answered with an importer refusal, so `error.message` is the instruction to show. */
16
+ export const isAuthSwitchImportRefusal = (error) => asAuthSwitchRefusal(error)?.code === 'import_refusal';
17
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXV0aG9yaXR5LWltcG9ydC1jb250cmFjdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2F1dGhvcml0eS1pbXBvcnQtY29udHJhY3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxFQUFFLG1CQUFtQixFQUFFLDZCQUE2QixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFtSjdGOzs7Ozs7Ozs7OztHQVdHO0FBQ0gsTUFBTSxDQUFDLE1BQU0sNkJBQTZCLEdBQUcsNkJBQTZCLENBQUM7QUFFM0UsNkdBQTZHO0FBQzdHLE1BQU0sQ0FBQyxNQUFNLHlCQUF5QixHQUFHLENBQUMsS0FBYyxFQUFXLEVBQUUsQ0FDbkUsbUJBQW1CLENBQUMsS0FBSyxDQUFDLEVBQUUsSUFBSSxLQUFLLGdCQUFnQixDQUFDIn0=