@modelprofile.com/authswitch 6.2.0 → 6.4.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 (50) hide show
  1. package/dist_ts/00_commitinfo_data.js +1 -1
  2. package/dist_ts/authority-contract.d.ts +142 -0
  3. package/dist_ts/authority-contract.js +3 -0
  4. package/dist_ts/authority-runtime-contract.d.ts +32 -0
  5. package/dist_ts/authority-runtime-contract.js +2 -0
  6. package/dist_ts/classes.authoritybroker.d.ts +73 -0
  7. package/dist_ts/classes.authoritybroker.js +548 -0
  8. package/dist_ts/classes.authorityclient.d.ts +34 -0
  9. package/dist_ts/classes.authorityclient.js +200 -0
  10. package/dist_ts/classes.authoritydaemon.d.ts +26 -0
  11. package/dist_ts/classes.authoritydaemon.js +249 -0
  12. package/dist_ts/classes.authoritydatabase.d.ts +94 -0
  13. package/dist_ts/classes.authoritydatabase.js +736 -0
  14. package/dist_ts/classes.authorityframing.d.ts +8 -0
  15. package/dist_ts/classes.authorityframing.js +22 -0
  16. package/dist_ts/classes.authoritymodels.d.ts +215 -0
  17. package/dist_ts/classes.authoritymodels.js +818 -0
  18. package/dist_ts/classes.authoritysecrets.d.ts +9 -0
  19. package/dist_ts/classes.authoritysecrets.js +30 -0
  20. package/dist_ts/classes.authorityservice.d.ts +28 -0
  21. package/dist_ts/classes.authorityservice.js +71 -0
  22. package/dist_ts/classes.cli.d.ts +56 -0
  23. package/dist_ts/classes.cli.js +238 -63
  24. package/dist_ts/classes.codexpreuse.js +2 -2
  25. package/dist_ts/classes.tui.js +51 -2
  26. package/dist_ts/index.d.ts +2 -0
  27. package/dist_ts/index.js +26 -1
  28. package/dist_ts/plugins.d.ts +9 -2
  29. package/dist_ts/plugins.js +10 -3
  30. package/dist_ts/preuse.d.ts +89 -2
  31. package/dist_ts/preuse.js +107 -2
  32. package/package.json +17 -3
  33. package/readme.md +118 -16
  34. package/ts/00_commitinfo_data.ts +1 -1
  35. package/ts/authority-contract.ts +109 -0
  36. package/ts/authority-runtime-contract.ts +27 -0
  37. package/ts/classes.authoritybroker.ts +530 -0
  38. package/ts/classes.authorityclient.ts +183 -0
  39. package/ts/classes.authoritydaemon.ts +219 -0
  40. package/ts/classes.authoritydatabase.ts +725 -0
  41. package/ts/classes.authorityframing.ts +21 -0
  42. package/ts/classes.authoritymodels.ts +367 -0
  43. package/ts/classes.authoritysecrets.ts +27 -0
  44. package/ts/classes.authorityservice.ts +83 -0
  45. package/ts/classes.cli.ts +236 -54
  46. package/ts/classes.codexpreuse.ts +1 -1
  47. package/ts/classes.tui.ts +39 -2
  48. package/ts/index.ts +25 -0
  49. package/ts/plugins.ts +9 -2
  50. package/ts/preuse.ts +141 -3
package/readme.md CHANGED
@@ -24,12 +24,47 @@ pnpm install -g @modelprofile.com/authswitch
24
24
 
25
25
  Node.js 24 or newer is required.
26
26
 
27
+ ## Authority SDK (new account registry)
28
+
29
+ The package also exports a typed client for a per-user authswitch authority daemon. Its
30
+ SmartData registry stores account identity separately from sealed refresh grants, runtime
31
+ bindings, Codex enrollment records, and native-handoff and migration journals. A daemon
32
+ owns OpenAI device login, targeted reauthentication, and proactive refresh for grants
33
+ added through this API. Management snapshots and events contain no credentials.
34
+
35
+ ```ts
36
+ import { AuthSwitchClient } from '@modelprofile.com/authswitch/authority-client';
37
+ import { resolveAuthSwitchAuthorityPaths } from '@modelprofile.com/authswitch';
38
+
39
+ const paths = resolveAuthSwitchAuthorityPaths();
40
+ const client = new AuthSwitchClient(paths.authoritySocketPath, paths.runtimeSocketPath);
41
+ const snapshot = await client.snapshotAll();
42
+ const operation = await client.beginAddOpenAi();
43
+ ```
44
+
45
+ Browser code can import the credential-free DTOs from
46
+ `@modelprofile.com/authswitch/authority-contract`. A backend binds an account to a
47
+ runtime incarnation and keeps the returned capability private. The runtime-only socket
48
+ resolves that binding to a current access token; its directory can be mounted into a
49
+ container without exposing the management socket. Reconnect and reload a snapshot after
50
+ an event gap or daemon restart.
51
+
52
+ This SDK is additive at this stage. The commands documented below still use the existing
53
+ credential stores. The authority daemon does not import those stores automatically,
54
+ replace their active native sessions, or take over their refresh grants. An active
55
+ Codex/OpenCode native grant remains pending until its owner is idle and an explicit
56
+ handoff verifies the latest native source. Claude Code's own native credential files
57
+ remain vendor-owned; a container setup-token grant is a separate grant under the same
58
+ account. The one-time migration and deletion of authswitch's old stores are separate
59
+ cutover steps.
60
+
27
61
  ## Usage
28
62
 
29
63
  Run `authswitch`, `authswitch -i`, or `authswitch --interactive` for an interactive guide. Use the arrow keys and
30
64
  Enter to switch accounts, save the current login, list accounts and live status, check remote
31
- control, or remove a saved account. The guide shows the current account and returns
32
- to the menu after each successful action; choose **Back** in a submenu or **Exit** to finish.
65
+ control, preuse one or all accounts, or remove a saved account. The guide shows the current
66
+ account and returns to the menu after each successful action; choose **Back** in a submenu or
67
+ **Exit** to finish.
33
68
 
34
69
  Before showing the actions, the guide checks whether the current credential has a
35
70
  readable saved copy of its own account. A login without one gets an offer to save it
@@ -62,6 +97,7 @@ authswitch codex use [email] # activate one; prompts when no email is given
62
97
  authswitch opencode use [email] --stop # stop running OpenCode instances first
63
98
  authswitch opencode use [email] --keep-running # switch without stopping anything
64
99
  authswitch codex preuse <email> # send the default prompt without activating the account
100
+ authswitch codex preuse --all # the same for every Codex account, one account at a time
65
101
  authswitch watch # check usage every 2 minutes and switch when an account runs out
66
102
  authswitch claude watch --threshold 90 --dry-run # report what it would switch, for one harness
67
103
  authswitch codex current # print the account currently in use
@@ -381,12 +417,58 @@ to make the first token-consuming request for the next usage window:
381
417
  authswitch codex preuse alice@example.com
382
418
  authswitch codex preuse alice@example.com --prompt "Write 2000 words about strawberries."
383
419
  authswitch codex preuse alice@example.com --model gpt-5.5 --prompt "Describe a strawberry in one sentence."
420
+ authswitch codex preuse --all
421
+ authswitch codex preuse --all --model gpt-5.5
384
422
  ```
385
423
 
386
424
  The default prompt is `Write 2000 words about strawberries.` Without an account,
387
- an interactive terminal shows an account picker. Scripts must specify an account
388
- and, when multiple harnesses are registered, the harness. Active and saved
389
- accounts are supported, including an active login that has not been stashed.
425
+ an interactive terminal shows a picker of the harness's accounts, plus **All
426
+ accounts** when it has more than one and **Back**. Choosing one account runs it
427
+ exactly as naming it would; choosing **All accounts** confirms once and then runs
428
+ as `--all` does, table and exit status included. Scripts must specify an account
429
+ or `--all` and, when multiple harnesses are registered, the harness. Active and
430
+ saved accounts are supported, including an active login that has not been stashed.
431
+
432
+ `--all` preuses every account the harness lists, active and saved, each exactly
433
+ once -- an active login and its own saved record are one account -- and it cannot
434
+ be combined with an account reference. The accounts run strictly one after
435
+ another, never in parallel, each with one request and no retry, and each one is
436
+ reported as it goes. The run ends with a table and a counts line:
437
+
438
+ ```
439
+ [1/3] Preusing alice@example.com (Codex)…
440
+ Prompt completed with gpt-5.5. Tokens: 19 input, 2711 output, 2730 total.
441
+ [2/3] Preusing bob@example.com (Codex)…
442
+ Prompt completed with gpt-5.5. Tokens: 19 input, 2711 output, 2730 total.
443
+ [3/3] Preusing apikey:9f2 (Codex)…
444
+ Codex preuse requires a ChatGPT subscription login; API-key logins have no subscription reset window.
445
+
446
+ Preuse results
447
+ ┌────────────────────┬───────────┬─────────┬─────────────────────┬──────────────┐
448
+ │ Account │ Outcome │ Model │ Tokens in/out/total │ Next reset │
449
+ ├────────────────────┼───────────┼─────────┼─────────────────────┼──────────────┤
450
+ │ alice@example.com │ Completed │ gpt-5.5 │ 19/2711/2730 │ 5d 18h 53min │
451
+ │ bob@example.com │ Completed │ gpt-5.5 │ 19/2711/2730 │ 5d 18h 53min │
452
+ │ apikey:9f2 │ Skipped │ - │ - │ - │
453
+ └────────────────────┴───────────┴─────────┴─────────────────────┴──────────────┘
454
+ 2 completed, 1 skipped.
455
+ ```
456
+
457
+ `Next reset` is the lead window of the reset schedule read after that account's
458
+ prompt, the same window the single-account reset table lists first. An account
459
+ that sent no prompt has no model, token count or deadline of its own, and says so
460
+ rather than showing a zero. `Skipped` is a refusal the adapter stated before any
461
+ request -- an API-key login, a missing or expired credential, a model catalog
462
+ that could not be read -- so nothing was consumed for it. `Failed` and
463
+ `Interrupted` may both have consumed tokens and are never retried; `Not run` is
464
+ an account the interruption never reached.
465
+
466
+ The guide (`authswitch`, `authswitch -i`) offers **Preuse an account or all
467
+ accounts (consumes quota)** for a harness that supports it. It asks which account
468
+ or **All accounts**, confirms once and names what the prompt will be sent to, then
469
+ reports exactly as the command line does: one account's reset schedule, or the
470
+ results table for all of them. **Back** or a declined confirmation sends nothing,
471
+ and an account the adapter refuses returns to the menu rather than ending the guide.
390
472
 
391
473
  Codex inference uses the published FlexHarness models/providers packages and the
392
474
  selected ChatGPT login's access token and account ID. It selects the account's
@@ -395,21 +477,25 @@ low reasoning setting when the catalog provides one. API-key logins are rejected
395
477
  because they do not have ChatGPT subscription reset windows. Expired credentials
396
478
  must be renewed through Codex and saved again.
397
479
 
398
- Each invocation consumes quota and sends at most one inference request. It does
399
- not watch for resets or schedule future requests. No active login is switched,
480
+ Each invocation consumes quota and sends one inference request per account. It
481
+ does not watch for resets or schedule future requests. No active login is switched,
400
482
  credential refreshed or written, app-server restarted, or earned reset consumed.
401
483
  The prompt has no tools, files or conversation history. The generated prose is
402
484
  drained without being printed or stored; the command reports provider token
403
485
  counts and then reads the account's reset schedule. It does not infer that a
404
486
  timer started merely from a zero-percent usage reading or a successful response.
405
487
 
406
- Ctrl-C cancels the request. Codex preuse has a three-minute deadline, with a
407
- ten-second model-catalog limit. Failed or interrupted requests can have consumed
408
- tokens and are never retried automatically. A completed prompt exits with 0 even
409
- if the subsequent reset lookup fails, reporting that uncertainty separately;
410
- inference failure exits with 1, invalid arguments or unsupported harnesses with
411
- 2, and an interrupted prompt with 130. Check `authswitch list` before repeating
412
- an uncertain request. `--json` remains a list-only option.
488
+ Ctrl-C cancels the request, and in a `--all` run it stops the run before the next
489
+ account starts. Codex preuse has a three-minute deadline, with a ten-second
490
+ model-catalog limit. Failed or interrupted requests can have consumed tokens and
491
+ are never retried automatically. A completed prompt exits with 0 even if the
492
+ subsequent reset lookup fails, reporting that uncertainty separately; inference
493
+ failure exits with 1, invalid arguments or unsupported harnesses with 2, and an
494
+ interrupted prompt with 130. A named single account also exits 1 when it was
495
+ refused, because it is the account the command was asked to preuse; `--all` exits
496
+ 0 when every account either completed or was skipped for a stated reason. Check
497
+ `authswitch list` before repeating an uncertain request. `--json` remains a
498
+ list-only option.
413
499
 
414
500
  ### Watching usage and switching automatically
415
501
 
@@ -492,10 +578,18 @@ incrementally through each account's status API, without activating it.
492
578
  | `/`, `s`, `S` | Filter the table, cycle its sort column, reverse sorting |
493
579
  | `a` / `c` | Save the current login and keep it active / save and clear it |
494
580
  | `d` | Remove the selected saved copy, preserving the active login |
581
+ | `p` / `P` | Preuse the selected account / every account of this harness (consumes quota) |
495
582
  | `r` / `g` | Refresh status / run harness diagnostics |
496
583
  | `q` / Ctrl-C | Close and restore the terminal |
497
584
 
498
- Save, switch and removal actions require confirmation. Confirmations start on
585
+ The preuse keys appear in the key line only for a harness that supports preuse,
586
+ and each run states how many accounts it will consume quota on before it starts.
587
+ The footer follows the run account by account, the activity log keeps each
588
+ account's result, and the table is read again afterwards because the prompts
589
+ moved the usage it shows. Closing the dashboard stops the run before its next
590
+ account.
591
+
592
+ Save, switch, preuse and removal actions require confirmation. Confirmations start on
499
593
  Cancel; use Left/Right or Tab to select the action, then Enter. Switching first
500
594
  checks the current credential and offers to save an unsaved login. A declined,
501
595
  failed or unverified save prevents switching. Plain character shortcuts remain
@@ -993,7 +1087,15 @@ model identifier and cancellation signal; `IHarnessPreuseResult` returns the
993
1087
  selected model and optional input/output/total token counts. The adapter owns
994
1088
  credential selection and inference and must leave the active login unchanged.
995
1089
  Unsupported adapters reject the command before account selection. Prompt results
996
- never contain credentials or raw provider responses.
1090
+ never contain credentials or raw provider responses. A refusal that sent no
1091
+ request must be a `PreuseError` with `requestStarted` false, which is what a run
1092
+ over several accounts reports as skipped rather than failed; every other error,
1093
+ `PreuseError` or not, is treated as a request that may have consumed tokens.
1094
+ `preuseAccounts(harness, accounts, options, report)` is that run: it sends one
1095
+ prompt per account in turn, reports a `started` and a `finished` event for each,
1096
+ reads the account's reset schedule after a completed prompt, stops before the
1097
+ next account once the signal aborts, and returns the per-account results with
1098
+ their counts.
997
1099
 
998
1100
  Adapters can also return an optional `IHarnessAccountStatus.summary` with a
999
1101
  subscription plan and its live/stored provenance, labelled usage windows with
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@modelprofile.com/authswitch',
6
- version: '6.2.0',
6
+ version: '6.4.0',
7
7
  description: 'Manage Codex, OpenCode and Claude Code accounts with guided switching and live usage status'
8
8
  }
@@ -0,0 +1,109 @@
1
+ import type { ITypedRequest } from '@api.global/typedrequest-interfaces';
2
+
3
+ /** Credential-free account management contract. Safe to import in browser code. */
4
+ export type TAuthSwitchAccountHealth = 'ready' | 'refreshing' | 'retry_wait' | 'needs_reauth' | 'native' | 'legacy_native_pending' | 'removed';
5
+
6
+ export interface IAuthSwitchAccount {
7
+ id: string;
8
+ providerId: string;
9
+ label: string;
10
+ email: string | null;
11
+ plan: string | null;
12
+ health: TAuthSwitchAccountHealth;
13
+ owner: 'daemon' | 'claude_native' | 'legacy_native' | 'none';
14
+ grantGeneration: number;
15
+ revision: number;
16
+ accessExpiresAt: string | null;
17
+ retryAt: string | null;
18
+ problem: 'none' | 'provider_unavailable' | 'exchange_uncertain' | 'provider_rejected' | 'native_owner';
19
+ statusObservedAt: string;
20
+ }
21
+
22
+ export interface IAuthSwitchBinding {
23
+ id: string;
24
+ accountId: string;
25
+ runtime: 'flex' | 'codex' | 'opencode' | 'claude';
26
+ scopeId: string;
27
+ incarnationId: string;
28
+ revision: number;
29
+ }
30
+
31
+ export interface IAuthSwitchSnapshot {
32
+ schemaVersion: 1;
33
+ epoch: string;
34
+ revision: number;
35
+ generatedAt: string;
36
+ accounts: IAuthSwitchAccount[];
37
+ bindings: IAuthSwitchBinding[];
38
+ nextAccountCursor: string | null;
39
+ nextBindingCursor: string | null;
40
+ }
41
+
42
+ export interface IAuthSwitchAccountEvent {
43
+ epoch: string;
44
+ revision: number;
45
+ kind: 'account' | 'binding';
46
+ accountId: string;
47
+ }
48
+
49
+ export type TAuthSwitchLoginPrompt =
50
+ | { flow: 'device'; verificationUrl: string; userCode: string }
51
+ | { flow: 'browser'; authUrl: string };
52
+
53
+ export interface IAuthSwitchOperation {
54
+ id: string;
55
+ state: 'pending' | 'complete' | 'failed' | 'cancelled';
56
+ prompt: TAuthSwitchLoginPrompt | null;
57
+ account: IAuthSwitchAccount | null;
58
+ error: string | null;
59
+ }
60
+
61
+ export interface IReq_AuthSwitchSnapshot extends ITypedRequest {
62
+ method: 'authswitch.authority.snapshot';
63
+ request: { accountAfter?: string; bindingAfter?: string; limit?: number };
64
+ response: { snapshot: IAuthSwitchSnapshot };
65
+ }
66
+
67
+ export interface IReq_AuthSwitchEvents extends ITypedRequest {
68
+ method: 'authswitch.authority.events';
69
+ request: { epoch: string; afterRevision: number; waitMs: number };
70
+ response: { epoch: string; revision: number; resyncRequired: boolean; events: IAuthSwitchAccountEvent[] };
71
+ }
72
+
73
+ export interface IReq_AuthSwitchBeginAdd extends ITypedRequest {
74
+ method: 'authswitch.authority.add';
75
+ request: { providerId: 'openai'; flow: 'device' };
76
+ response: { operation: IAuthSwitchOperation };
77
+ }
78
+
79
+ export interface IReq_AuthSwitchBeginReauth extends ITypedRequest {
80
+ method: 'authswitch.authority.reauth';
81
+ request: { accountId: string; flow: 'device' };
82
+ response: { operation: IAuthSwitchOperation };
83
+ }
84
+
85
+ export interface IReq_AuthSwitchGetOperation extends ITypedRequest {
86
+ method: 'authswitch.authority.operation';
87
+ request: { operationId: string };
88
+ response: { operation: IAuthSwitchOperation };
89
+ }
90
+
91
+ export interface IReq_AuthSwitchCancelOperation extends ITypedRequest {
92
+ method: 'authswitch.authority.cancel';
93
+ request: { operationId: string };
94
+ response: { operation: IAuthSwitchOperation };
95
+ }
96
+
97
+ export interface IReq_AuthSwitchRenameAccount extends ITypedRequest {
98
+ method: 'authswitch.authority.rename';
99
+ request: { accountId: string; expectedRevision: number; label: string };
100
+ response: { account: IAuthSwitchAccount };
101
+ }
102
+
103
+ export interface IReq_AuthSwitchRemoveAccount extends ITypedRequest {
104
+ method: 'authswitch.authority.remove';
105
+ request: { accountId: string; expectedRevision: number };
106
+ response: { account: IAuthSwitchAccount };
107
+ }
108
+
109
+ /** Runtime capability is returned only to trusted backend callers; do not expose this response in a browser API. */
@@ -0,0 +1,27 @@
1
+ import type { ITypedRequest } from '@api.global/typedrequest-interfaces';
2
+ import type { IAuthSwitchBinding } from './authority-contract.js';
3
+
4
+ /** Backend-only binding operation. The capability must never enter a browser-facing response. */
5
+ export interface IReq_AuthSwitchBindAccount extends ITypedRequest {
6
+ method: 'authswitch.authority.bind';
7
+ request: {
8
+ accountId: string;
9
+ runtime: IAuthSwitchBinding['runtime'];
10
+ scopeId: string;
11
+ incarnationId: string;
12
+ };
13
+ response: { binding: IAuthSwitchBinding; capability: string };
14
+ }
15
+
16
+ /** Backend-only access operation. Refresh grants never cross this interface. */
17
+ export interface IReq_AuthSwitchResolveAccess extends ITypedRequest {
18
+ method: 'authswitch.authority.resolveAccess';
19
+ request: { bindingId: string; capability: string; minValidityMs: number };
20
+ response: {
21
+ accessToken: string;
22
+ accountId: string;
23
+ isFedrampAccount: boolean;
24
+ expiresAt: string;
25
+ grantGeneration: number;
26
+ };
27
+ }