@modelprofile.com/authswitch 3.1.0 → 3.3.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/accounts.d.ts +25 -2
- package/dist_ts/accounts.js +112 -15
- package/dist_ts/classes.accountlist.d.ts +10 -3
- package/dist_ts/classes.accountlist.js +71 -34
- package/dist_ts/classes.claudecodeharness.d.ts +2 -2
- package/dist_ts/classes.claudecodeharness.js +3 -3
- package/dist_ts/classes.claudestatus.d.ts +2 -2
- package/dist_ts/classes.claudestatus.js +7 -6
- package/dist_ts/classes.cli.d.ts +15 -0
- package/dist_ts/classes.cli.js +51 -16
- package/dist_ts/classes.codexharness.d.ts +2 -2
- package/dist_ts/classes.codexharness.js +3 -3
- package/dist_ts/classes.codexstatus.d.ts +3 -3
- package/dist_ts/classes.codexstatus.js +12 -11
- package/dist_ts/classes.fileharness.d.ts +2 -2
- package/dist_ts/classes.limits.js +56 -18
- package/dist_ts/classes.opencodeharness.d.ts +2 -2
- package/dist_ts/classes.opencodeharness.js +3 -3
- package/dist_ts/classes.tui.js +7 -4
- package/dist_ts/interfaces.harness.d.ts +17 -5
- package/dist_ts/interfaces.list.d.ts +5 -1
- package/package.json +2 -2
- package/readme.md +102 -22
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/accounts.ts +105 -12
- package/ts/classes.accountlist.ts +74 -35
- package/ts/classes.claudecodeharness.ts +3 -3
- package/ts/classes.claudestatus.ts +7 -6
- package/ts/classes.cli.ts +56 -15
- package/ts/classes.codexharness.ts +3 -3
- package/ts/classes.codexstatus.ts +12 -11
- package/ts/classes.fileharness.ts +2 -2
- package/ts/classes.limits.ts +63 -19
- package/ts/classes.opencodeharness.ts +3 -3
- package/ts/classes.tui.ts +6 -3
- package/ts/interfaces.harness.ts +19 -2
- package/ts/interfaces.list.ts +5 -1
package/ts/classes.limits.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as plugins from './plugins.js';
|
|
2
|
-
import { compactSince, compactUntil, credentialDriftNote, orderedUsageWindows } from './accounts.js';
|
|
2
|
+
import { accountTypeText, compactSince, compactUntil, credentialDriftNote, orderedUsageWindows } from './accounts.js';
|
|
3
3
|
import { consoleHeading, consoleTable } from './classes.accountlist.js';
|
|
4
4
|
import type { IAccountList, IAccountLimitRow, IAccountLimits, IActiveAccountDrift, IActiveAccountRow, IActiveAccounts, IHarnessAccountList } from './interfaces.list.js';
|
|
5
|
-
import type { IHarnessCredentialDrift, TUsageSeverity } from './interfaces.harness.js';
|
|
5
|
+
import type { IAccountType, IHarnessCredentialDrift, TUsageSeverity } from './interfaces.harness.js';
|
|
6
6
|
import { dim, plainText } from './formatting.js';
|
|
7
7
|
|
|
8
8
|
type TListedAccount = IHarnessAccountList['accounts'][number];
|
|
@@ -46,13 +46,47 @@ const accountKeys = (harnessArg: IHarnessAccountList, accountArg: TListedAccount
|
|
|
46
46
|
...(accountArg.slotId === undefined ? {} : { slotId: plainText(accountArg.slotId) }),
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
+
/** The account's plan, sanitised like every other label and copied so that no two rows share one object. */
|
|
50
|
+
const accountType = (accountArg: TListedAccount): IAccountType | null => {
|
|
51
|
+
const subscription = accountArg.status.summary?.subscription;
|
|
52
|
+
return subscription === undefined ? null : { plan: plainText(subscription.plan), source: subscription.source };
|
|
53
|
+
};
|
|
54
|
+
|
|
49
55
|
const harnessProblem = (harnessArg: IHarnessAccountList): string | null =>
|
|
50
56
|
harnessArg.problems.length ? harnessArg.problems.map(plainText).join(' ') : null;
|
|
51
57
|
|
|
52
|
-
/**
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
58
|
+
/** The identity fields both condensed row types share; labels are for display, these are for order and grouping. */
|
|
59
|
+
type TCondensedRow = Pick<IAccountLimitRow, 'harnessId' | 'provider' | 'account' | 'accountId' | 'slotId'>;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Provider then account, keeping each account's windows in the order the adapter ranked them.
|
|
63
|
+
*
|
|
64
|
+
* Every harness and every account stays one consecutive run for the table's grouping: a harness is placed
|
|
65
|
+
* by its first provider name, so no other label sorts between its slots, and ids break label ties, so two
|
|
66
|
+
* harnesses sharing a label or two accounts sharing an email never interleave.
|
|
67
|
+
*/
|
|
68
|
+
const byProviderThenAccount = <TRow extends TCondensedRow>(rowsArg: TRow[]): TRow[] => {
|
|
69
|
+
const firstProvider = new Map<string, string>();
|
|
70
|
+
for (const row of rowsArg) {
|
|
71
|
+
const first = firstProvider.get(row.harnessId);
|
|
72
|
+
if (first === undefined || row.provider.localeCompare(first) < 0) firstProvider.set(row.harnessId, row.provider);
|
|
73
|
+
}
|
|
74
|
+
return [...rowsArg].sort((left, right) => firstProvider.get(left.harnessId)!.localeCompare(firstProvider.get(right.harnessId)!)
|
|
75
|
+
|| left.harnessId.localeCompare(right.harnessId)
|
|
76
|
+
|| left.provider.localeCompare(right.provider)
|
|
77
|
+
|| (left.account ?? '').localeCompare(right.account ?? '')
|
|
78
|
+
|| (left.slotId ?? '').localeCompare(right.slotId ?? '')
|
|
79
|
+
|| (left.accountId ?? '').localeCompare(right.accountId ?? ''));
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
/** One colour line per harness: an OpenCode installation's slots share it. */
|
|
83
|
+
const harnessRuns = (rowArg: TCondensedRow): string => rowArg.harnessId;
|
|
84
|
+
|
|
85
|
+
/** One Provider cell per provider name within a harness; two harnesses that share a label stay apart. */
|
|
86
|
+
const providerRuns = (rowArg: TCondensedRow): string => JSON.stringify([rowArg.harnessId, rowArg.provider]);
|
|
87
|
+
|
|
88
|
+
/** One divider run per account identity; a provider-level row without an account is its own run. */
|
|
89
|
+
const accountRuns = (rowArg: TCondensedRow): string => JSON.stringify([rowArg.harnessId, rowArg.slotId ?? null, rowArg.accountId]);
|
|
56
90
|
|
|
57
91
|
const noteFor = (rowArg: { provider: string; account: string | null; unavailableReason: string | null }): string | null =>
|
|
58
92
|
rowArg.unavailableReason === null ? null
|
|
@@ -83,23 +117,23 @@ export const accountLimits = (listArg: IAccountList): IAccountLimits => {
|
|
|
83
117
|
limitType: null, scope: null, windowSeconds: null, usedPercent: null, resetAt: null, resetsIn: UNAVAILABLE, severity: null, headline: false,
|
|
84
118
|
} as const;
|
|
85
119
|
if (problem !== null) {
|
|
86
|
-
rows.push({ harnessId: harness.id, provider: providerName(harness), account: null, accountId: null, isActive: false, ...empty, unavailableReason: problem });
|
|
120
|
+
rows.push({ harnessId: harness.id, provider: providerName(harness), account: null, accountId: null, isActive: false, accountType: null, ...empty, unavailableReason: problem });
|
|
87
121
|
continue;
|
|
88
122
|
}
|
|
89
123
|
if (!harness.accounts.length) {
|
|
90
|
-
rows.push({ harnessId: harness.id, provider: providerName(harness), account: null, accountId: null, isActive: false, ...empty,
|
|
124
|
+
rows.push({ harnessId: harness.id, provider: providerName(harness), account: null, accountId: null, isActive: false, accountType: null, ...empty,
|
|
91
125
|
unavailableReason: harness.saveUnavailableReason === null ? 'No accounts are known for this harness.' : plainText(harness.saveUnavailableReason) });
|
|
92
126
|
continue;
|
|
93
127
|
}
|
|
94
128
|
for (const account of harness.accounts) {
|
|
95
129
|
const windows = orderedUsageWindows(account.status.summary?.usageWindows);
|
|
96
130
|
if (!windows.length) {
|
|
97
|
-
rows.push({ ...accountKeys(harness, account), isActive: account.isActive, ...empty, unavailableReason: missingLimitReason(account) });
|
|
131
|
+
rows.push({ ...accountKeys(harness, account), isActive: account.isActive, accountType: accountType(account), ...empty, unavailableReason: missingLimitReason(account) });
|
|
98
132
|
continue;
|
|
99
133
|
}
|
|
100
134
|
for (const window of windows) {
|
|
101
135
|
rows.push({
|
|
102
|
-
...accountKeys(harness, account), isActive: account.isActive,
|
|
136
|
+
...accountKeys(harness, account), isActive: account.isActive, accountType: accountType(account),
|
|
103
137
|
limitType: plainText(window.label), scope: window.scope ?? 'account', windowSeconds: window.durationSeconds,
|
|
104
138
|
usedPercent: window.usedPercent, resetAt: window.resetAt, resetsIn: compactUntil(window.resetAt, now, UNAVAILABLE),
|
|
105
139
|
severity: window.severity ?? null, headline: window.headline === true, unavailableReason: null,
|
|
@@ -129,14 +163,14 @@ export const activeAccounts = (listArg: IAccountList): IActiveAccounts => {
|
|
|
129
163
|
return index < 0 ? null : activeDrift(harness, drifts.splice(index, 1)[0]);
|
|
130
164
|
};
|
|
131
165
|
if (problem !== null) {
|
|
132
|
-
rows.push({ harnessId: harness.id, provider: providerName(harness), account: null, accountId: null, savedAt: null, savedAgo: UNAVAILABLE, source: 'none', unavailableReason: problem, drift: null });
|
|
166
|
+
rows.push({ harnessId: harness.id, provider: providerName(harness), account: null, accountId: null, accountType: null, savedAt: null, savedAgo: UNAVAILABLE, source: 'none', unavailableReason: problem, drift: null });
|
|
133
167
|
continue;
|
|
134
168
|
}
|
|
135
169
|
const active = harness.accounts.filter(account => account.isActive);
|
|
136
170
|
if (!active.length) {
|
|
137
171
|
const stashed = harness.accounts.filter(account => account.isStashed);
|
|
138
172
|
rows.push({
|
|
139
|
-
harnessId: harness.id, provider: providerName(harness), account: null, accountId: null,
|
|
173
|
+
harnessId: harness.id, provider: providerName(harness), account: null, accountId: null, accountType: null,
|
|
140
174
|
savedAt: null, savedAgo: UNAVAILABLE, source: stashed.length ? 'stash only' : 'none',
|
|
141
175
|
unavailableReason: harness.saveUnavailableReason !== null ? plainText(harness.saveUnavailableReason)
|
|
142
176
|
: stashed.length ? `No login is active; ${stashed.length} saved account(s) can be activated with authswitch ${plainText(harness.id)} use.`
|
|
@@ -146,7 +180,7 @@ export const activeAccounts = (listArg: IAccountList): IActiveAccounts => {
|
|
|
146
180
|
} else {
|
|
147
181
|
for (const account of active) {
|
|
148
182
|
rows.push({
|
|
149
|
-
...accountKeys(harness, account),
|
|
183
|
+
...accountKeys(harness, account), accountType: accountType(account),
|
|
150
184
|
savedAt: account.savedAt, savedAgo: compactSince(account.savedAt, now, UNAVAILABLE),
|
|
151
185
|
source: 'credential file',
|
|
152
186
|
unavailableReason: account.isStashed ? null : 'This active login is not saved yet; authswitch cannot restore it after a switch.',
|
|
@@ -163,6 +197,13 @@ export const activeAccounts = (listArg: IAccountList): IActiveAccounts => {
|
|
|
163
197
|
return { schemaVersion: 1, generatedAt: listArg.generatedAt, complete: listArg.complete, active, notes: [...collectNotes(active), ...driftNotes, ...unattached] };
|
|
164
198
|
};
|
|
165
199
|
|
|
200
|
+
/**
|
|
201
|
+
* The Account cell: the label, and the account type beneath it, dim. An unknown type keeps its line as
|
|
202
|
+
* `n/a`, so every account cell has the same shape; a provider row without an account has no type to name.
|
|
203
|
+
*/
|
|
204
|
+
const accountCell = (rowArg: IAccountLimitRow): plugins.smartconsole.TText => rowArg.account === null ? UNAVAILABLE
|
|
205
|
+
: plugins.smartconsole.color.concat(`${rowArg.account}\n`, plugins.smartconsole.color.text(accountTypeText(rowArg.accountType) ?? UNAVAILABLE, { dim: true, bold: false }));
|
|
206
|
+
|
|
166
207
|
const percent = (rowArg: IAccountLimitRow): string => rowArg.usedPercent === null ? UNAVAILABLE : `${rowArg.usedPercent}%`;
|
|
167
208
|
|
|
168
209
|
const SEVERITY_COLORS: Record<TUsageSeverity, plugins.smartconsole.TColorName | undefined> = { normal: undefined, warning: 'orange', critical: 'red' };
|
|
@@ -184,15 +225,18 @@ export class CondensedRenderer {
|
|
|
184
225
|
|
|
185
226
|
public async renderLimits(limitsArg: IAccountLimits): Promise<void> {
|
|
186
227
|
consoleHeading('Account limits');
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
228
|
+
// The span callbacks record these exact rows, so the table renders this array as it is.
|
|
229
|
+
const rows = limitsArg.limits;
|
|
230
|
+
if (!rows.length) { process.stdout.write(`${dim('No harnesses registered.')}\n`); return; }
|
|
231
|
+
await consoleTable(this.out, rows, [
|
|
232
|
+
{ key: 'provider', title: 'Provider', value: row => row.provider, rowSpan: plugins.smartconsole.rowSpansBy(rows, providerRuns) },
|
|
233
|
+
{ key: 'account', title: 'Account', value: row => row.account ?? UNAVAILABLE, render: accountCell,
|
|
234
|
+
style: row => ({ bold: row.isActive }), rowSpan: plugins.smartconsole.rowSpansBy(rows, accountRuns) },
|
|
191
235
|
{ key: 'limit', title: 'Limit type', value: row => row.limitType ?? UNAVAILABLE },
|
|
192
236
|
{ key: 'used', title: 'Used %', value: percent, align: 'right',
|
|
193
237
|
style: row => ({ foreground: percentColor(row) }) },
|
|
194
238
|
{ key: 'reset', title: 'Resets in', value: row => row.resetsIn, align: 'right' },
|
|
195
|
-
]);
|
|
239
|
+
], { divider: accountRuns, colorLine: harnessRuns });
|
|
196
240
|
this.notes(limitsArg.notes, 'n/a — ');
|
|
197
241
|
}
|
|
198
242
|
|
|
@@ -210,7 +254,7 @@ export class CondensedRenderer {
|
|
|
210
254
|
style: row => ({ foreground: row.savedAt === null && row.account !== null ? 'orange' : undefined }) },
|
|
211
255
|
{ key: 'source', title: 'Source', value: row => row.drift === null ? row.source : `${row.source} (changed)`,
|
|
212
256
|
style: row => ({ foreground: row.drift !== null ? 'red' : row.source === 'credential file' ? 'green' : 'orange' }) },
|
|
213
|
-
]);
|
|
257
|
+
], { colorLine: harnessRuns });
|
|
214
258
|
this.notes(activeArg.notes, 'note — ');
|
|
215
259
|
}
|
|
216
260
|
}
|
|
@@ -4,7 +4,7 @@ import { CredentialStore, credentialHash, credentialRecord, credentialText, read
|
|
|
4
4
|
import { HarnessProcesses } from './classes.harnessprocesses.js';
|
|
5
5
|
import { CodexAccountStatus } from './classes.codexstatus.js';
|
|
6
6
|
import { writeSecretFileAtomically } from './helpers.js';
|
|
7
|
-
import type { IHarnessAccountStatus, IHarnessLoginOptions, IHarnessLoginProvider, IHarnessProcessControl } from './interfaces.harness.js';
|
|
7
|
+
import type { IHarnessAccountStatus, IHarnessLoginOptions, IHarnessLoginProvider, IHarnessProcessControl, IHarnessStatusOptions } from './interfaces.harness.js';
|
|
8
8
|
import { beginSavedOpenAiLogin, openCodeCredentialFromLogin } from './classes.login.js';
|
|
9
9
|
import { plainText } from './formatting.js';
|
|
10
10
|
|
|
@@ -100,12 +100,12 @@ export class OpenCodeHarness extends FileHarness {
|
|
|
100
100
|
writeSecretFileAtomically(this.file, raw);
|
|
101
101
|
if (readCredentialDocument(this.file).raw !== raw) throw new Error('Active login verification failed. The outgoing login remains saved.');
|
|
102
102
|
}
|
|
103
|
-
public async readAccountStatus(id: string): Promise<IHarnessAccountStatus> {
|
|
103
|
+
public async readAccountStatus(id: string, optionsArg?: IHarnessStatusOptions): Promise<IHarnessAccountStatus> {
|
|
104
104
|
const account = this.readCredential(id);
|
|
105
105
|
if (account.slotId === 'openai' && account.credential.type === 'oauth') {
|
|
106
106
|
const info = this.openAiInfo(account.credential);
|
|
107
107
|
if (!info.accountId) return { facts: [], problems: ['The OpenAI login has no account ID for a status lookup.'] };
|
|
108
|
-
return this.status.readCredential(credentialText(account.credential.access)!, info.accountId, info.plan);
|
|
108
|
+
return this.status.readCredential(credentialText(account.credential.access)!, info.accountId, info.plan, optionsArg);
|
|
109
109
|
}
|
|
110
110
|
return { facts: [
|
|
111
111
|
{ section: 'Authentication', label: 'Provider', value: account.slotId },
|
package/ts/classes.tui.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as plugins from './plugins.js';
|
|
2
2
|
import type { IAuthHarness, IHarnessOutcome, IHarnessProcess, IHarnessState, IHarnessStopOutcome } from './interfaces.harness.js';
|
|
3
|
-
import { accountDetails, accountPlan, accountQuotaSummary, accountResets, accountState, accountUsageWindows, usageWindowName, until, readAccountRows, type IAccountRow } from './accounts.js';
|
|
3
|
+
import { accountBadge, accountDetails, accountPlan, accountQuotaSummary, accountResets, accountState, accountUsageWindows, usageWindowName, until, readAccountRows, type IAccountRow } from './accounts.js';
|
|
4
4
|
import { plainText } from './formatting.js';
|
|
5
5
|
import { AuthSwitchOperations } from './classes.operations.js';
|
|
6
6
|
import { describeHarnessProcesses, describeStopOutcome } from './classes.harnessprocesses.js';
|
|
@@ -115,11 +115,14 @@ export class AuthSwitchTui {
|
|
|
115
115
|
const verified = await harness.readState();
|
|
116
116
|
if (!verified.accounts.some(account => account.isActive && account.isStashed && account.id === active.id)) { footer.setText('The current login could not be verified as saved. Switching was cancelled.'); return; }
|
|
117
117
|
}
|
|
118
|
-
|
|
118
|
+
// The dashboard already holds this account's status, so the badge costs no further lookup.
|
|
119
|
+
const badge = accountBadge(rows.find(row => row.account.id === target.id)?.status);
|
|
120
|
+
if (!await screen.confirm(`Switch ${plainText(harness.label)} to ${plainText(target.label)}${badge}?`, { confirmLabel: 'Switch' })) return;
|
|
119
121
|
if (screen.signal.aborted) return;
|
|
120
122
|
const outcome = await this.operations.run(harness, { harnessId: harness.id, action: 'switch', accountId: target.id },
|
|
121
123
|
message => screen.confirm(message), settleInstances(screen));
|
|
122
|
-
await refresh(screen);
|
|
124
|
+
await refresh(screen);
|
|
125
|
+
if (showOutcome(outcome)) ui.logs.append(`Now active: ${plainText(target.label)}${badge}`);
|
|
123
126
|
};
|
|
124
127
|
const remove = async (screen: plugins.smartconsole.ITuiContext): Promise<void> => {
|
|
125
128
|
const selected = table.selected;
|
package/ts/interfaces.harness.ts
CHANGED
|
@@ -127,9 +127,20 @@ export interface IHarnessUsageWindow {
|
|
|
127
127
|
headline?: true;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
/**
|
|
131
|
+
* The provider's own name for an account's plan, and where that name comes from.
|
|
132
|
+
*
|
|
133
|
+
* `live` was read from the provider in this lookup; `stored` is the last name the saved login
|
|
134
|
+
* carries, which is the only answer available while a provider cannot be reached.
|
|
135
|
+
*/
|
|
136
|
+
export interface IAccountType {
|
|
137
|
+
plan: string;
|
|
138
|
+
source: 'live' | 'stored';
|
|
139
|
+
}
|
|
140
|
+
|
|
130
141
|
export interface IHarnessStatusSummary {
|
|
131
142
|
/** A plan name does not imply an active billing status. */
|
|
132
|
-
subscription?:
|
|
143
|
+
subscription?: IAccountType;
|
|
133
144
|
/** Live billing information only. Never infer these fields from a plan or entitlement expiry. */
|
|
134
145
|
billing?: { hasActiveSubscription?: boolean; autoRenew?: boolean; renewsAt?: string; cancelsAt?: string; expiresAt?: string };
|
|
135
146
|
/** Missing windows are unknown, not unused. Times are ISO 8601 UTC. */
|
|
@@ -146,6 +157,12 @@ export interface IHarnessOutcome {
|
|
|
146
157
|
problems: string[];
|
|
147
158
|
}
|
|
148
159
|
|
|
160
|
+
/** A read-only status lookup. */
|
|
161
|
+
export interface IHarnessStatusOptions {
|
|
162
|
+
/** Cancels the lookup's requests; an adapter still bounds each request with its own timeout. */
|
|
163
|
+
signal?: AbortSignal;
|
|
164
|
+
}
|
|
165
|
+
|
|
149
166
|
/** A single text-only inference request. Credentials remain inside the adapter. */
|
|
150
167
|
export interface IHarnessPreuseOptions {
|
|
151
168
|
prompt: string;
|
|
@@ -171,7 +188,7 @@ export interface IAuthHarness {
|
|
|
171
188
|
readState(): THarnessResult<IHarnessState>;
|
|
172
189
|
/** Running instances of this harness, when the adapter owns their lifecycle. */
|
|
173
190
|
readonly processes?: IHarnessProcessControl;
|
|
174
|
-
readAccountStatus(accountIdArg: string): Promise<IHarnessAccountStatus>;
|
|
191
|
+
readAccountStatus(accountIdArg: string, optionsArg?: IHarnessStatusOptions): Promise<IHarnessAccountStatus>;
|
|
175
192
|
readonly loginProviders?: IHarnessLoginProvider[];
|
|
176
193
|
beginLogin?(options: IHarnessLoginOptions): Promise<IHarnessLoginHandle>;
|
|
177
194
|
/** Optional quota-consuming capability; never changes the active login. */
|
package/ts/interfaces.list.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { IHarnessAccount, IHarnessAccountStatus, IHarnessCredentialDrift, TUsageSeverity } from './interfaces.harness.js';
|
|
1
|
+
import type { IAccountType, IHarnessAccount, IHarnessAccountStatus, IHarnessCredentialDrift, TUsageSeverity } from './interfaces.harness.js';
|
|
2
2
|
|
|
3
3
|
/** Credential-free, versioned output of list --json. Missing status fields stay omitted. */
|
|
4
4
|
export interface IAccountList {
|
|
@@ -39,6 +39,8 @@ export interface IAccountLimitRow {
|
|
|
39
39
|
accountId: string | null;
|
|
40
40
|
slotId?: string;
|
|
41
41
|
isActive: boolean;
|
|
42
|
+
/** The adapter's plan name (`max`, `pro`), sanitised but not display-formatted; null when none is known or the row names no account. */
|
|
43
|
+
accountType: IAccountType | null;
|
|
42
44
|
/** The provider's own window name, or null when no limit data is available. */
|
|
43
45
|
limitType: string | null;
|
|
44
46
|
scope: 'account' | 'feature' | null;
|
|
@@ -70,6 +72,8 @@ export interface IActiveAccountRow {
|
|
|
70
72
|
account: string | null;
|
|
71
73
|
accountId: string | null;
|
|
72
74
|
slotId?: string;
|
|
75
|
+
/** The adapter's plan name (`max`, `pro`), sanitised but not display-formatted; null when none is known or the row names no account. */
|
|
76
|
+
accountType: IAccountType | null;
|
|
73
77
|
/** When authswitch last saved this login; null when it was never stashed. */
|
|
74
78
|
savedAt: string | null;
|
|
75
79
|
/** Human elapsed time for the same `savedAt`, relative to `generatedAt`. */
|