@modelprofile.com/authswitch 3.3.0 → 5.0.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 +3 -3
- package/dist_ts/accounts.d.ts +52 -15
- package/dist_ts/accounts.js +110 -27
- package/dist_ts/classes.accountlist.d.ts +0 -25
- package/dist_ts/classes.accountlist.js +2 -216
- package/dist_ts/classes.claudecodeharness.d.ts +81 -2
- package/dist_ts/classes.claudecodeharness.js +208 -14
- package/dist_ts/classes.claudecodelocks.d.ts +38 -0
- package/dist_ts/classes.claudecodelocks.js +118 -0
- package/dist_ts/classes.claudestatus.d.ts +15 -2
- package/dist_ts/classes.claudestatus.js +100 -83
- package/dist_ts/classes.claudetokenrefresh.d.ts +32 -0
- package/dist_ts/classes.claudetokenrefresh.js +77 -0
- package/dist_ts/classes.cli.d.ts +14 -7
- package/dist_ts/classes.cli.js +126 -66
- package/dist_ts/classes.codexharness.d.ts +4 -0
- package/dist_ts/classes.codexharness.js +5 -1
- package/dist_ts/classes.codexstatus.d.ts +7 -2
- package/dist_ts/classes.codexstatus.js +49 -21
- package/dist_ts/classes.credentialstore.d.ts +43 -2
- package/dist_ts/classes.credentialstore.js +60 -13
- package/dist_ts/classes.fileharness.d.ts +45 -21
- package/dist_ts/classes.fileharness.js +64 -26
- package/dist_ts/classes.limits.d.ts +46 -7
- package/dist_ts/classes.limits.js +94 -36
- package/dist_ts/classes.listrenderer.d.ts +17 -0
- package/dist_ts/classes.listrenderer.js +313 -0
- package/dist_ts/classes.login.d.ts +1 -1
- package/dist_ts/classes.login.js +1 -1
- package/dist_ts/classes.opencodeharness.d.ts +4 -0
- package/dist_ts/classes.opencodeharness.js +8 -4
- package/dist_ts/classes.operations.js +3 -2
- package/dist_ts/classes.tui.js +4 -3
- package/dist_ts/classes.watch.d.ts +108 -0
- package/dist_ts/classes.watch.js +219 -0
- package/dist_ts/classes.watchlock.d.ts +33 -0
- package/dist_ts/classes.watchlock.js +118 -0
- package/dist_ts/claudehttp.d.ts +39 -0
- package/dist_ts/claudehttp.js +83 -0
- package/dist_ts/cliargs.d.ts +36 -0
- package/dist_ts/cliargs.js +60 -0
- package/dist_ts/consoletable.d.ts +21 -0
- package/dist_ts/consoletable.js +63 -0
- package/dist_ts/helpers.d.ts +7 -0
- package/dist_ts/helpers.js +16 -1
- package/dist_ts/index.d.ts +3 -0
- package/dist_ts/index.js +4 -1
- package/dist_ts/interfaces.harness.d.ts +60 -11
- package/dist_ts/interfaces.list.d.ts +3 -1
- package/dist_ts/plugins.d.ts +7 -0
- package/dist_ts/plugins.js +6 -1
- package/dist_ts/ratelimit.d.ts +8 -0
- package/dist_ts/ratelimit.js +13 -0
- package/dist_ts/watchpolicy.d.ts +44 -0
- package/dist_ts/watchpolicy.js +82 -0
- package/package.json +5 -3
- package/readme.md +349 -115
- package/ts/00_commitinfo_data.ts +3 -3
- package/ts/accounts.ts +122 -35
- package/ts/classes.accountlist.ts +2 -219
- package/ts/classes.claudecodeharness.ts +199 -13
- package/ts/classes.claudecodelocks.ts +133 -0
- package/ts/classes.claudestatus.ts +100 -65
- package/ts/classes.claudetokenrefresh.ts +85 -0
- package/ts/classes.cli.ts +114 -54
- package/ts/classes.codexharness.ts +4 -0
- package/ts/classes.codexstatus.ts +40 -18
- package/ts/classes.credentialstore.ts +78 -10
- package/ts/classes.fileharness.ts +76 -34
- package/ts/classes.limits.ts +110 -36
- package/ts/classes.listrenderer.ts +328 -0
- package/ts/classes.login.ts +1 -1
- package/ts/classes.opencodeharness.ts +7 -3
- package/ts/classes.operations.ts +2 -1
- package/ts/classes.tui.ts +3 -2
- package/ts/classes.watch.ts +263 -0
- package/ts/classes.watchlock.ts +100 -0
- package/ts/claudehttp.ts +92 -0
- package/ts/cliargs.ts +71 -0
- package/ts/consoletable.ts +62 -0
- package/ts/helpers.ts +14 -0
- package/ts/index.ts +3 -0
- package/ts/interfaces.harness.ts +60 -5
- package/ts/interfaces.list.ts +3 -1
- package/ts/plugins.ts +9 -0
- package/ts/ratelimit.ts +14 -0
- package/ts/watchpolicy.ts +121 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/** A fixed, local diagnostic. Nothing a service sent (a body, a header) and no request header ever becomes one. */
|
|
2
|
+
export declare class ClaudeRequestError extends Error {
|
|
3
|
+
}
|
|
4
|
+
/** How every Claude lookup reports a login the service does not accept (HTTP 401, or a refresh grant it rejected). */
|
|
5
|
+
export declare const CLAUDE_LOGIN_REJECTED = "Login expired or was rejected. Log in again with Claude Code and save it.";
|
|
6
|
+
/** The service did not accept the login. */
|
|
7
|
+
export declare class ClaudeLoginRejectedError extends ClaudeRequestError {
|
|
8
|
+
constructor();
|
|
9
|
+
}
|
|
10
|
+
/** The service refused a request for too many requests. That says nothing about the account's usage or login. */
|
|
11
|
+
export declare class ClaudeRateLimitError extends ClaudeRequestError {
|
|
12
|
+
readonly retryAt: string | null;
|
|
13
|
+
constructor(subjectArg: string, retryAt: string | null);
|
|
14
|
+
}
|
|
15
|
+
export interface IClaudeRequest {
|
|
16
|
+
url: string;
|
|
17
|
+
method: 'GET' | 'POST';
|
|
18
|
+
headers: Record<string, string>;
|
|
19
|
+
body?: string;
|
|
20
|
+
/** What diagnostics call the service: `Claude account` reads `The Claude account service could not be reached.` */
|
|
21
|
+
subject: string;
|
|
22
|
+
timeoutMs: number;
|
|
23
|
+
signal?: AbortSignal;
|
|
24
|
+
/** Whether an error status's JSON body is read for the caller, such as an OAuth error code; otherwise it is discarded. */
|
|
25
|
+
readErrorBody?: boolean;
|
|
26
|
+
/** The clock a `Retry-After` delay is counted from. */
|
|
27
|
+
now: () => number;
|
|
28
|
+
}
|
|
29
|
+
export interface IClaudeResponse {
|
|
30
|
+
status: number;
|
|
31
|
+
/** The JSON object the service answered with; null for an empty body, a body that is not a JSON object, or an unread one. */
|
|
32
|
+
body: Record<string, unknown> | null;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* One request to a Claude account service, shaped like Claude Code's own but naming authswitch in its User-Agent:
|
|
36
|
+
* redirects refused, bounded in time and in size, and cancelled with the caller's signal. A rate limit is reported as
|
|
37
|
+
* `ClaudeRateLimitError`; every other status is the caller's to interpret.
|
|
38
|
+
*/
|
|
39
|
+
export declare const claudeRequest: (fetcherArg: typeof fetch, requestArg: IClaudeRequest) => Promise<IClaudeResponse>;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { commitinfo } from './00_commitinfo_data.js';
|
|
2
|
+
import { credentialRecord } from './classes.credentialstore.js';
|
|
3
|
+
import { retryAtFrom } from './ratelimit.js';
|
|
4
|
+
/** A fixed, local diagnostic. Nothing a service sent (a body, a header) and no request header ever becomes one. */
|
|
5
|
+
export class ClaudeRequestError extends Error {
|
|
6
|
+
}
|
|
7
|
+
/** How every Claude lookup reports a login the service does not accept (HTTP 401, or a refresh grant it rejected). */
|
|
8
|
+
export const CLAUDE_LOGIN_REJECTED = 'Login expired or was rejected. Log in again with Claude Code and save it.';
|
|
9
|
+
/** The service did not accept the login. */
|
|
10
|
+
export class ClaudeLoginRejectedError extends ClaudeRequestError {
|
|
11
|
+
constructor() { super(CLAUDE_LOGIN_REJECTED); }
|
|
12
|
+
}
|
|
13
|
+
/** The service refused a request for too many requests. That says nothing about the account's usage or login. */
|
|
14
|
+
export class ClaudeRateLimitError extends ClaudeRequestError {
|
|
15
|
+
retryAt;
|
|
16
|
+
constructor(subjectArg, retryAt) {
|
|
17
|
+
super(`The ${subjectArg} service is rate limiting requests (HTTP 429); try again later.`);
|
|
18
|
+
this.retryAt = retryAt;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const MAX_RESPONSE_BYTES = 1024 * 1024;
|
|
22
|
+
/** The body as a JSON object, read to at most 1 MiB; null when it is empty or not a JSON object. */
|
|
23
|
+
const jsonBody = async (responseArg, subjectArg) => {
|
|
24
|
+
const reader = responseArg.body?.getReader();
|
|
25
|
+
if (!reader)
|
|
26
|
+
return null;
|
|
27
|
+
const chunks = [];
|
|
28
|
+
let size = 0;
|
|
29
|
+
try {
|
|
30
|
+
while (true) {
|
|
31
|
+
const part = await reader.read();
|
|
32
|
+
if (part.done)
|
|
33
|
+
break;
|
|
34
|
+
size += part.value.byteLength;
|
|
35
|
+
if (size > MAX_RESPONSE_BYTES) {
|
|
36
|
+
await reader.cancel();
|
|
37
|
+
throw new ClaudeRequestError(`The ${subjectArg} response is too large.`);
|
|
38
|
+
}
|
|
39
|
+
chunks.push(part.value);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
reader.releaseLock();
|
|
44
|
+
}
|
|
45
|
+
try {
|
|
46
|
+
return credentialRecord(JSON.parse(Buffer.concat(chunks).toString('utf8')));
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* One request to a Claude account service, shaped like Claude Code's own but naming authswitch in its User-Agent:
|
|
54
|
+
* redirects refused, bounded in time and in size, and cancelled with the caller's signal. A rate limit is reported as
|
|
55
|
+
* `ClaudeRateLimitError`; every other status is the caller's to interpret.
|
|
56
|
+
*/
|
|
57
|
+
export const claudeRequest = async (fetcherArg, requestArg) => {
|
|
58
|
+
const timeout = AbortSignal.timeout(requestArg.timeoutMs);
|
|
59
|
+
const signal = requestArg.signal ? AbortSignal.any([requestArg.signal, timeout]) : timeout;
|
|
60
|
+
try {
|
|
61
|
+
const response = await fetcherArg(requestArg.url, {
|
|
62
|
+
method: requestArg.method, redirect: 'error', signal, body: requestArg.body,
|
|
63
|
+
headers: { ...requestArg.headers, 'User-Agent': `authswitch/${commitinfo.version}` },
|
|
64
|
+
});
|
|
65
|
+
if (response.status === 429) {
|
|
66
|
+
await response.body?.cancel();
|
|
67
|
+
throw new ClaudeRateLimitError(requestArg.subject, retryAtFrom(response.headers.get('retry-after'), requestArg.now()));
|
|
68
|
+
}
|
|
69
|
+
if (!response.ok && !requestArg.readErrorBody) {
|
|
70
|
+
await response.body?.cancel();
|
|
71
|
+
return { status: response.status, body: null };
|
|
72
|
+
}
|
|
73
|
+
return { status: response.status, body: await jsonBody(response, requestArg.subject) };
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
if (signal.aborted)
|
|
77
|
+
throw new ClaudeRequestError(`The ${requestArg.subject} request ${timeout.aborted ? 'timed out' : 'was cancelled'}.`);
|
|
78
|
+
if (error instanceof ClaudeRequestError)
|
|
79
|
+
throw error;
|
|
80
|
+
throw new ClaudeRequestError(`The ${requestArg.subject} service could not be reached.`);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhdWRlaHR0cC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2NsYXVkZWh0dHAudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQ3JELE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBQ2hFLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSxnQkFBZ0IsQ0FBQztBQUU3QyxtSEFBbUg7QUFDbkgsTUFBTSxPQUFPLGtCQUFtQixTQUFRLEtBQUs7Q0FBRztBQUVoRCxzSEFBc0g7QUFDdEgsTUFBTSxDQUFDLE1BQU0scUJBQXFCLEdBQUcsMkVBQTJFLENBQUM7QUFFakgsNENBQTRDO0FBQzVDLE1BQU0sT0FBTyx3QkFBeUIsU0FBUSxrQkFBa0I7SUFDOUQsZ0JBQWdCLEtBQUssQ0FBQyxxQkFBcUIsQ0FBQyxDQUFDLENBQUMsQ0FBQztDQUNoRDtBQUVELGlIQUFpSDtBQUNqSCxNQUFNLE9BQU8sb0JBQXFCLFNBQVEsa0JBQWtCO0lBQ1Y7SUFBaEQsWUFBWSxVQUFrQixFQUFrQixPQUFzQjtRQUNwRSxLQUFLLENBQUMsT0FBTyxVQUFVLGlFQUFpRSxDQUFDLENBQUM7UUFENUMsWUFBTyxHQUFQLE9BQU8sQ0FBZTtJQUV0RSxDQUFDO0NBQ0Y7QUF1QkQsTUFBTSxrQkFBa0IsR0FBRyxJQUFJLEdBQUcsSUFBSSxDQUFDO0FBRXZDLG9HQUFvRztBQUNwRyxNQUFNLFFBQVEsR0FBRyxLQUFLLEVBQUUsV0FBcUIsRUFBRSxVQUFrQixFQUEyQyxFQUFFO0lBQzVHLE1BQU0sTUFBTSxHQUFHLFdBQVcsQ0FBQyxJQUFJLEVBQUUsU0FBUyxFQUFFLENBQUM7SUFDN0MsSUFBSSxDQUFDLE1BQU07UUFBRSxPQUFPLElBQUksQ0FBQztJQUN6QixNQUFNLE1BQU0sR0FBaUIsRUFBRSxDQUFDO0lBQ2hDLElBQUksSUFBSSxHQUFHLENBQUMsQ0FBQztJQUNiLElB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
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** A command line a command cannot run with. The command prints the message and exits with status 2. */
|
|
2
|
+
export declare class UsageError extends Error {
|
|
3
|
+
}
|
|
4
|
+
export interface ICommandArgsSpec {
|
|
5
|
+
/** Options that take a value: `--name value` or `--name=value`. */
|
|
6
|
+
values: readonly string[];
|
|
7
|
+
/** Options that take none. */
|
|
8
|
+
flags: readonly string[];
|
|
9
|
+
maxPositionals: number;
|
|
10
|
+
/** The message for an unknown option or one positional too many. */
|
|
11
|
+
usage: string;
|
|
12
|
+
}
|
|
13
|
+
export interface ICommandArgs {
|
|
14
|
+
/** Each given value option's value, by option name. */
|
|
15
|
+
values: ReadonlyMap<string, string>;
|
|
16
|
+
flags: ReadonlySet<string>;
|
|
17
|
+
positionals: readonly string[];
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* A command's own arguments: value options, flags and positionals. Every option is accepted at most once, and a value
|
|
21
|
+
* is taken as it is, even when it starts with `-`, so a prompt can itself contain an option.
|
|
22
|
+
*/
|
|
23
|
+
export declare const parseCommandArgs: (argsArg: readonly string[], specArg: ICommandArgsSpec) => ICommandArgs;
|
|
24
|
+
/**
|
|
25
|
+
* A duration option in milliseconds: whole seconds (`120`), or a number with a unit (`90s`, `2m`, `1h`, `1d`),
|
|
26
|
+
* within the bounds. Every unit the bounds message can name is accepted, so a value the message suggests parses.
|
|
27
|
+
*/
|
|
28
|
+
export declare const parseDurationOption: (nameArg: string, valueArg: string, boundsArg: {
|
|
29
|
+
minMs: number;
|
|
30
|
+
maxMs: number;
|
|
31
|
+
}) => number;
|
|
32
|
+
/** A whole-number option within the bounds. */
|
|
33
|
+
export declare const parseIntegerOption: (nameArg: string, valueArg: string, boundsArg: {
|
|
34
|
+
min: number;
|
|
35
|
+
max: number;
|
|
36
|
+
}) => number;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { duration } from './accounts.js';
|
|
2
|
+
/** A command line a command cannot run with. The command prints the message and exits with status 2. */
|
|
3
|
+
export class UsageError extends Error {
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* A command's own arguments: value options, flags and positionals. Every option is accepted at most once, and a value
|
|
7
|
+
* is taken as it is, even when it starts with `-`, so a prompt can itself contain an option.
|
|
8
|
+
*/
|
|
9
|
+
export const parseCommandArgs = (argsArg, specArg) => {
|
|
10
|
+
const values = new Map();
|
|
11
|
+
const flags = new Set();
|
|
12
|
+
const positionals = [];
|
|
13
|
+
for (let index = 0; index < argsArg.length; index++) {
|
|
14
|
+
const argument = argsArg[index];
|
|
15
|
+
const separator = argument.startsWith('--') ? argument.indexOf('=') : -1;
|
|
16
|
+
const name = separator < 0 ? argument : argument.slice(0, separator);
|
|
17
|
+
if (specArg.values.includes(name)) {
|
|
18
|
+
if (values.has(name))
|
|
19
|
+
throw new UsageError(`Use ${name} only once.`);
|
|
20
|
+
const value = separator < 0 ? argsArg[++index] : argument.slice(separator + 1);
|
|
21
|
+
if (value === undefined)
|
|
22
|
+
throw new UsageError(`${name} requires a value.`);
|
|
23
|
+
values.set(name, value);
|
|
24
|
+
}
|
|
25
|
+
else if (separator < 0 && specArg.flags.includes(argument)) {
|
|
26
|
+
if (flags.has(argument))
|
|
27
|
+
throw new UsageError(`Use ${argument} only once.`);
|
|
28
|
+
flags.add(argument);
|
|
29
|
+
}
|
|
30
|
+
else if (argument.startsWith('-') || positionals.length >= specArg.maxPositionals) {
|
|
31
|
+
throw new UsageError(specArg.usage);
|
|
32
|
+
}
|
|
33
|
+
else
|
|
34
|
+
positionals.push(argument);
|
|
35
|
+
}
|
|
36
|
+
return { values, flags, positionals };
|
|
37
|
+
};
|
|
38
|
+
const DURATION_UNITS = { '': 1000, s: 1000, m: 60_000, h: 3_600_000, d: 86_400_000 };
|
|
39
|
+
/**
|
|
40
|
+
* A duration option in milliseconds: whole seconds (`120`), or a number with a unit (`90s`, `2m`, `1h`, `1d`),
|
|
41
|
+
* within the bounds. Every unit the bounds message can name is accepted, so a value the message suggests parses.
|
|
42
|
+
*/
|
|
43
|
+
export const parseDurationOption = (nameArg, valueArg, boundsArg) => {
|
|
44
|
+
const match = /^(\d{1,7})([smhd]?)$/.exec(valueArg);
|
|
45
|
+
if (!match)
|
|
46
|
+
throw new UsageError(`${nameArg} takes a duration such as 120, 90s or 2m.`);
|
|
47
|
+
const milliseconds = Number(match[1]) * DURATION_UNITS[match[2]];
|
|
48
|
+
if (milliseconds < boundsArg.minMs || milliseconds > boundsArg.maxMs) {
|
|
49
|
+
throw new UsageError(`${nameArg} must be from ${duration(boundsArg.minMs / 1000)} to ${duration(boundsArg.maxMs / 1000)}.`);
|
|
50
|
+
}
|
|
51
|
+
return milliseconds;
|
|
52
|
+
};
|
|
53
|
+
/** A whole-number option within the bounds. */
|
|
54
|
+
export const parseIntegerOption = (nameArg, valueArg, boundsArg) => {
|
|
55
|
+
const value = /^\d{1,6}$/.test(valueArg) ? Number(valueArg) : Number.NaN;
|
|
56
|
+
if (!(value >= boundsArg.min && value <= boundsArg.max))
|
|
57
|
+
throw new UsageError(`${nameArg} takes a whole number from ${boundsArg.min} to ${boundsArg.max}.`);
|
|
58
|
+
return value;
|
|
59
|
+
};
|
|
60
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xpYXJncy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2NsaWFyZ3MudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLGVBQWUsQ0FBQztBQUV6Qyx3R0FBd0c7QUFDeEcsTUFBTSxPQUFPLFVBQVcsU0FBUSxLQUFLO0NBQUc7QUFtQnhDOzs7R0FHRztBQUNILE1BQU0sQ0FBQyxNQUFNLGdCQUFnQixHQUFHLENBQUMsT0FBMEIsRUFBRSxPQUF5QixFQUFnQixFQUFFO0lBQ3RHLE1BQU0sTUFBTSxHQUFHLElBQUksR0FBRyxFQUFrQixDQUFDO0lBQ3pDLE1BQU0sS0FBSyxHQUFHLElBQUksR0FBRyxFQUFVLENBQUM7SUFDaEMsTUFBTSxXQUFXLEdBQWEsRUFBRSxDQUFDO0lBQ2pDLEtBQUssSUFBSSxLQUFLLEdBQUcsQ0FBQyxFQUFFLEtBQUssR0FBRyxPQUFPLENBQUMsTUFBTSxFQUFFLEtBQUssRUFBRSxFQUFFLENBQUM7UUFDcEQsTUFBTSxRQUFRLEdBQUcsT0FBTyxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBQ2hDLE1BQU0sU0FBUyxHQUFHLFFBQVEsQ0FBQyxVQUFVLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO1FBQ3pFLE1BQU0sSUFBSSxHQUFHLFNBQVMsR0FBRyxDQUFDLENBQUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxDQUFDLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxDQUFDLEVBQUUsU0FBUyxDQUFDLENBQUM7UUFDckUsSUFBSSxPQUFPLENBQUMsTUFBTSxDQUFDLFFBQVEsQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDO1lBQ2xDLElBQUksTUFBTSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUM7Z0JBQUUsTUFBTSxJQUFJLFVBQVUsQ0FBQyxPQUFPLElBQUksYUFB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
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
export declare const consoleWidth: () => number;
|
|
3
|
+
export declare const consoleHeading: (textArg: string) => void;
|
|
4
|
+
/**
|
|
5
|
+
* One note line beneath a table: dim, wrapped to the terminal, continuation lines under the prefix.
|
|
6
|
+
*
|
|
7
|
+
* Table cells wrap themselves (`overflow: 'wrap'` below), so a note is the only text a view writes that could run
|
|
8
|
+
* past the terminal. Words stay whole: one longer than the remaining width keeps its own line rather than being cut,
|
|
9
|
+
* so an account id or a URL stays selectable.
|
|
10
|
+
*/
|
|
11
|
+
export declare const consoleNote: (textArg: string, prefixArg?: string) => void;
|
|
12
|
+
/**
|
|
13
|
+
* One table implementation for every command, including the label-per-line fallback on narrow terminals.
|
|
14
|
+
*
|
|
15
|
+
* `groupsArg` is smartconsole's optional visual grouping: a divider between runs of consecutive rows with
|
|
16
|
+
* different keys, and a colour line along the left edge per run. Keys are identities, never display labels,
|
|
17
|
+
* and the caller orders the rows so that every group is one run. Below 40 columns the fallback separates
|
|
18
|
+
* divider runs with one dim rule line; it has no left edge, so it draws no colour line, and it repeats a
|
|
19
|
+
* spanning cell on every row it covers. A column's `render` content is what both layouts show.
|
|
20
|
+
*/
|
|
21
|
+
export declare const consoleTable: <TRow>(outArg: plugins.smartconsole.SmartConsole, rowsArg: TRow[], columnsArg: plugins.smartconsole.IBackendTableColumn<TRow>[], groupsArg?: NoInfer<plugins.smartconsole.IBackendTableGroups<TRow>>) => Promise<void>;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import { bold, dim } from './formatting.js';
|
|
3
|
+
export const consoleWidth = () => process.stdout.columns ?? 100;
|
|
4
|
+
export const consoleHeading = (textArg) => { process.stdout.write(`\n${bold(textArg)}\n`); };
|
|
5
|
+
/**
|
|
6
|
+
* One note line beneath a table: dim, wrapped to the terminal, continuation lines under the prefix.
|
|
7
|
+
*
|
|
8
|
+
* Table cells wrap themselves (`overflow: 'wrap'` below), so a note is the only text a view writes that could run
|
|
9
|
+
* past the terminal. Words stay whole: one longer than the remaining width keeps its own line rather than being cut,
|
|
10
|
+
* so an account id or a URL stays selectable.
|
|
11
|
+
*/
|
|
12
|
+
export const consoleNote = (textArg, prefixArg = '') => {
|
|
13
|
+
const words = textArg.split(/\s+/).filter(Boolean);
|
|
14
|
+
if (!words.length)
|
|
15
|
+
return;
|
|
16
|
+
const width = consoleWidth();
|
|
17
|
+
const indent = ' '.repeat(prefixArg.length);
|
|
18
|
+
const lines = [];
|
|
19
|
+
let line = `${prefixArg}${words[0]}`;
|
|
20
|
+
for (const word of words.slice(1)) {
|
|
21
|
+
if (line.length + 1 + word.length > width) {
|
|
22
|
+
lines.push(line);
|
|
23
|
+
line = `${indent}${word}`;
|
|
24
|
+
}
|
|
25
|
+
else
|
|
26
|
+
line += ` ${word}`;
|
|
27
|
+
}
|
|
28
|
+
lines.push(line);
|
|
29
|
+
for (const written of lines)
|
|
30
|
+
process.stdout.write(`${dim(written)}\n`);
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* One table implementation for every command, including the label-per-line fallback on narrow terminals.
|
|
34
|
+
*
|
|
35
|
+
* `groupsArg` is smartconsole's optional visual grouping: a divider between runs of consecutive rows with
|
|
36
|
+
* different keys, and a colour line along the left edge per run. Keys are identities, never display labels,
|
|
37
|
+
* and the caller orders the rows so that every group is one run. Below 40 columns the fallback separates
|
|
38
|
+
* divider runs with one dim rule line; it has no left edge, so it draws no colour line, and it repeats a
|
|
39
|
+
* spanning cell on every row it covers. A column's `render` content is what both layouts show.
|
|
40
|
+
*/
|
|
41
|
+
export const consoleTable = async (outArg, rowsArg, columnsArg, groupsArg) => {
|
|
42
|
+
if (!rowsArg.length)
|
|
43
|
+
return;
|
|
44
|
+
const width = consoleWidth();
|
|
45
|
+
if (width < 40) {
|
|
46
|
+
// smartconsole's run semantics: null and undefined are the same key.
|
|
47
|
+
const dividerKeys = rowsArg.map(row => groupsArg?.divider?.(row) ?? null);
|
|
48
|
+
const rule = ` ${dim('─'.repeat(Math.max(1, width - 4)))}\n\n`;
|
|
49
|
+
const content = (column, row) => column.render
|
|
50
|
+
? plugins.smartconsole.color.plain(column.render(row)) : String(column.value(row) ?? 'Unavailable');
|
|
51
|
+
rowsArg.forEach((row, index) => {
|
|
52
|
+
if (index > 0 && dividerKeys[index] !== dividerKeys[index - 1])
|
|
53
|
+
process.stdout.write(rule);
|
|
54
|
+
process.stdout.write(columnsArg.map(column => ` ${column.title}: ${content(column, row).replace(/\n/g, '\n ')}`).join('\n') + '\n\n');
|
|
55
|
+
});
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
await outArg.table(rowsArg, {
|
|
59
|
+
columns: columnsArg, overflow: 'wrap', groups: groupsArg,
|
|
60
|
+
theme: { header: { bold: true, foreground: 'cyan' }, border: { dim: true } },
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29uc29sZXRhYmxlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvY29uc29sZXRhYmxlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxPQUFPLE1BQU0sY0FBYyxDQUFDO0FBQ3hDLE9BQU8sRUFBRSxJQUFJLEVBQUUsR0FBRyxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFFNUMsTUFBTSxDQUFDLE1BQU0sWUFBWSxHQUFHLEdBQVcsRUFBRSxDQUFDLE9BQU8sQ0FBQyxNQUFNLENBQUMsT0FBTyxJQUFJLEdBQUcsQ0FBQztBQUN4RSxNQUFNLENBQUMsTUFBTSxjQUFjLEdBQUcsQ0FBQyxPQUFlLEVBQVEsRUFBRSxHQUFHLE9BQU8sQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLEtBQUssSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQztBQUUzRzs7Ozs7O0dBTUc7QUFDSCxNQUFNLENBQUMsTUFBTSxXQUFXLEdBQUcsQ0FBQyxPQUFlLEVBQUUsU0FBUyxHQUFHLEVBQUUsRUFBUSxFQUFFO0lBQ25FLE1BQU0sS0FBSyxHQUFHLE9BQU8sQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUMsTUFBTSxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQ25ELElBQUksQ0FBQyxLQUFLLENBQUMsTUFBTTtRQUFFLE9BQU87SUFDMUIsTUFBTSxLQUFLLEdBQUcsWUFBWSxFQUFFLENBQUM7SUFDN0IsTUFBTSxNQUFNLEdBQUcsR0FBRyxDQUFDLE1BQU0sQ0FBQyxTQUFTLENBQUMsTUFBTSxDQUFDLENBQUM7SUFDNUMsTUFBTSxLQUFLLEdBQWEsRUFBRSxDQUFDO0lBQzNCLElBQUksSUFBSSxHQUFHLEdBQUcsU0FBUyxHQUFHLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDO0lBQ3JDLEtBQUssTUFBTSxJQUFJLElBQUksS0FBSyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFD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
|
package/dist_ts/helpers.d.ts
CHANGED
|
@@ -17,6 +17,8 @@ import type { IRemoteControlEnrollment, TEnrollmentState } from './interfaces.js
|
|
|
17
17
|
* content or the new one, never a partial write.
|
|
18
18
|
*/
|
|
19
19
|
export declare const writeSecretFileAtomically: (destinationPathArg: string, contentArg: string) => void;
|
|
20
|
+
/** A Node system error's code, for the distinctions the callers make (`EEXIST`, `ENOENT`, `ELOCKED`, `ERELEASED`). */
|
|
21
|
+
export declare const errorCode: (errorArg: unknown) => string | undefined;
|
|
20
22
|
/**
|
|
21
23
|
* Blocks the calling thread for `millisecondsArg`.
|
|
22
24
|
*
|
|
@@ -26,6 +28,11 @@ export declare const writeSecretFileAtomically: (destinationPathArg: string, con
|
|
|
26
28
|
* and the callers bound both the count and the duration.
|
|
27
29
|
*/
|
|
28
30
|
export declare const pauseSynchronously: (millisecondsArg: number) => void;
|
|
31
|
+
/**
|
|
32
|
+
* Waits `millisecondsArg` without blocking the thread. An aborted signal rejects the wait with its reason and clears
|
|
33
|
+
* the timer at once, so an abandoned wait never keeps the process alive.
|
|
34
|
+
*/
|
|
35
|
+
export declare const pause: (millisecondsArg: number, signalArg?: AbortSignal) => Promise<void>;
|
|
29
36
|
/**
|
|
30
37
|
* Trims third-party process output down to something safe to put in an error
|
|
31
38
|
* message: one line, length-capped, and never empty.
|
package/dist_ts/helpers.js
CHANGED
|
@@ -48,6 +48,8 @@ export const writeSecretFileAtomically = (destinationPathArg, contentArg) => {
|
|
|
48
48
|
}
|
|
49
49
|
plugins.fs.chmodSync(destinationPathArg, 0o600);
|
|
50
50
|
};
|
|
51
|
+
/** A Node system error's code, for the distinctions the callers make (`EEXIST`, `ENOENT`, `ELOCKED`, `ERELEASED`). */
|
|
52
|
+
export const errorCode = (errorArg) => errorArg?.code;
|
|
51
53
|
/**
|
|
52
54
|
* Blocks the calling thread for `millisecondsArg`.
|
|
53
55
|
*
|
|
@@ -59,6 +61,19 @@ export const writeSecretFileAtomically = (destinationPathArg, contentArg) => {
|
|
|
59
61
|
export const pauseSynchronously = (millisecondsArg) => {
|
|
60
62
|
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, millisecondsArg);
|
|
61
63
|
};
|
|
64
|
+
/**
|
|
65
|
+
* Waits `millisecondsArg` without blocking the thread. An aborted signal rejects the wait with its reason and clears
|
|
66
|
+
* the timer at once, so an abandoned wait never keeps the process alive.
|
|
67
|
+
*/
|
|
68
|
+
export const pause = (millisecondsArg, signalArg) => new Promise((resolve, reject) => {
|
|
69
|
+
if (signalArg?.aborted) {
|
|
70
|
+
reject(signalArg.reason);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const abort = () => { clearTimeout(timer); reject(signalArg.reason); };
|
|
74
|
+
const timer = setTimeout(() => { signalArg?.removeEventListener('abort', abort); resolve(); }, Math.max(0, millisecondsArg));
|
|
75
|
+
signalArg?.addEventListener('abort', abort, { once: true });
|
|
76
|
+
});
|
|
62
77
|
/**
|
|
63
78
|
* Trims third-party process output down to something safe to put in an error
|
|
64
79
|
* message: one line, length-capped, and never empty.
|
|
@@ -100,4 +115,4 @@ export const describeCodexFailure = (binaryArg, stdoutArg, stderrArg, spawnError
|
|
|
100
115
|
}
|
|
101
116
|
return summariseProcessOutput(stdoutArg, stderrArg, spawnErrorArg);
|
|
102
117
|
};
|
|
103
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
118
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVscGVycy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL2hlbHBlcnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxLQUFLLE9BQU8sTUFBTSxjQUFjLENBQUM7QUFHeEM7Ozs7Ozs7Ozs7Ozs7Ozs7R0FnQkc7QUFDSCxNQUFNLENBQUMsTUFBTSx5QkFBeUIsR0FBRyxDQUN2QyxrQkFBMEIsRUFDMUIsVUFBa0IsRUFDWixFQUFFO0lBQ1IsTUFBTSxRQUFRLEdBQUcsR0FBRyxrQkFBa0IsaUJBQWlCLENBQUM7SUFDeEQsd0VBQXdFO0lBQ3hFLDBFQUEwRTtJQUMxRSxzREFBc0Q7SUFDdEQsSUFBSSxPQUFPLENBQUMsRUFBRSxDQUFDLFVBQVUsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDO1FBQ3BDLE9BQU8sQ0FBQyxFQUFFLENBQUMsTUFBTSxDQUFDLFFBQVEsRUFBRSxFQUFFLEtBQUssRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQy9DLENBQUM7SUFFRCxJQUFJLE1BQU0sR0FBa0IsSUFBSSxDQUFDO0lBQ2pDLElBQUksQ0FBQztRQUNILE1BQU0sR0FBRyxPQUFPLENBQUMsRUFBRSxDQUFDLFFBQVEsQ0FBQyxRQUFRLEVBQUUsSUFBSSxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBQ3BELE9BQU8sQ0FBQyxFQUFFLENBQUMsVUFBVSxDQUFDLE1BQU0sRUFBRSxLQUFLLENBQUMsQ0FBQztRQUNyQyxPQUFPLENBQUMsRUFBRSxDQUFDLGFBQWEsQ0FBQyxNQUFNLEVBQUUsVUFBVSxFQUFFLEVBQUUsUUFBUSxFQUFFLE1BQU0sRUFBRSxDQUFDLENBQUM7UUFDbkUsT0FBTyxDQUFDLEVBQUUsQ0FBQyxTQUFTLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDN0IsT0FBTyxDQUFDLEVBQUUsQ0FBQyxTQUFTLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDN0IsTUFBTSxHQUFHLElBQUksQ0FBQztRQUNkLE9BQU8sQ0FBQyxFQUFFLENBQUMs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
|
package/dist_ts/index.d.ts
CHANGED
|
@@ -20,4 +20,7 @@ export * from './classes.cli.js';
|
|
|
20
20
|
export * from './classes.login.js';
|
|
21
21
|
export * from './classes.operations.js';
|
|
22
22
|
export * from './classes.service.js';
|
|
23
|
+
export * from './classes.watch.js';
|
|
24
|
+
export * from './classes.watchlock.js';
|
|
25
|
+
export * from './watchpolicy.js';
|
|
23
26
|
export declare const runCli: (argvArg?: string[]) => Promise<void>;
|
package/dist_ts/index.js
CHANGED
|
@@ -20,9 +20,12 @@ export * from './classes.cli.js';
|
|
|
20
20
|
export * from './classes.login.js';
|
|
21
21
|
export * from './classes.operations.js';
|
|
22
22
|
export * from './classes.service.js';
|
|
23
|
+
export * from './classes.watch.js';
|
|
24
|
+
export * from './classes.watchlock.js';
|
|
25
|
+
export * from './watchpolicy.js';
|
|
23
26
|
import { AuthSwitchCli } from './classes.cli.js';
|
|
24
27
|
export const runCli = async (argvArg = process.argv.slice(2)) => {
|
|
25
28
|
const exitCode = await new AuthSwitchCli().run(argvArg);
|
|
26
29
|
process.exitCode = exitCode;
|
|
27
30
|
};
|
|
28
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
31
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLGlCQUFpQixDQUFDO0FBQ2hDLGNBQWMseUJBQXlCLENBQUM7QUFDeEMsY0FBYyxzQkFBc0IsQ0FBQztBQUNyQyxjQUFjLDJCQUEyQixDQUFDO0FBQzFDLGNBQWMsOEJBQThCLENBQUM7QUFDN0MsY0FBYyxnQ0FBZ0MsQ0FBQztBQUMvQyxjQUFjLCtCQUErQixDQUFDO0FBQzlDLGNBQWMsMkJBQTJCLENBQUM7QUFDMUMsY0FBYywwQkFBMEIsQ0FBQztBQUN6QyxjQUFjLDBCQUEwQixDQUFDO0FBQ3pDLGNBQWMsYUFBYSxDQUFDO0FBQzVCLGNBQWMsY0FBYyxDQUFDO0FBQzdCLGNBQWMsd0JBQXdCLENBQUM7QUFDdkMsY0FBYyx3QkFBd0IsQ0FBQztBQUN2QyxjQUFjLDRCQUE0QixDQUFDO0FBQzNDLGNBQWMseUJBQXlCLENBQUM7QUFDeEMsY0FBYywwQkFBMEIsQ0FBQztBQUN6QyxjQUFjLDRCQUE0QixDQUFDO0FBQzNDLGNBQWMsa0JBQWtCLENBQUM7QUFDakMsY0FBYyxvQkFBb0IsQ0FBQztBQUNuQyxjQUFjLHlCQUF5QixDQUFDO0FBQ3hDLGNBQWMsc0JBQXNCLENBQUM7QUFDckMsY0FBYyxvQkFBb0IsQ0FBQztBQUNuQyxjQUFjLHdCQUF3QixDQUFDO0FBQ3ZDLGNBQWMsa0JBQWtCLENBQUM7QUFFakMsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBRWpELE1BQU0sQ0FBQyxNQUFNLE1BQU0sR0FBRyxLQUFLLEVBQUUsVUFBb0IsT0FBTyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDLEVBQWlCLEVBQUU7SUFDdkYsTUFBTSxRQUFRLEdBQUcsTUFBTSxJQUFJLGFBQWEsRUFBRSxDQUFDLEdBQUcsQ0FBQyxPQUFPLENBQUMsQ0FBQztJQUN4RCxPQUFPLENBQUMsUUFBUSxHQUFHLFFBQVEsQ0FBQztBQUM5QixDQUFDLENBQUMifQ==
|
|
@@ -60,8 +60,9 @@ export interface IHarnessProcessControl {
|
|
|
60
60
|
* A live credential that no longer holds the account the last switch activated.
|
|
61
61
|
*
|
|
62
62
|
* Authswitch records the hash of what it wrote, in its own store. A different hash with the same
|
|
63
|
-
* account is that account's own token refresh; a different account
|
|
64
|
-
*
|
|
63
|
+
* account is that account's own token refresh; a different account means the login changed outside
|
|
64
|
+
* authswitch -- a new native login, or, for a harness that does not pick up swaps live, a running
|
|
65
|
+
* instance writing its in-memory login back.
|
|
65
66
|
*/
|
|
66
67
|
export interface IHarnessCredentialDrift {
|
|
67
68
|
slotId?: string;
|
|
@@ -101,6 +102,14 @@ export interface IHarnessAccountStatus {
|
|
|
101
102
|
problems: string[];
|
|
102
103
|
/** Optional structured data for shared presentation; never parse the display facts. */
|
|
103
104
|
summary?: IHarnessStatusSummary;
|
|
105
|
+
/**
|
|
106
|
+
* Set when the provider refused a lookup for too many requests (HTTP 429). It says nothing about the account's
|
|
107
|
+
* usage: callers back off, never read it as an exhausted or expired login. `retryAt` is when the provider asked to
|
|
108
|
+
* be tried again (ISO 8601 UTC), or null when it did not say.
|
|
109
|
+
*/
|
|
110
|
+
rateLimit?: {
|
|
111
|
+
retryAt: string | null;
|
|
112
|
+
};
|
|
104
113
|
}
|
|
105
114
|
export interface IHarnessStatusFact {
|
|
106
115
|
label: string;
|
|
@@ -137,17 +146,18 @@ export interface IAccountType {
|
|
|
137
146
|
plan: string;
|
|
138
147
|
source: 'live' | 'stored';
|
|
139
148
|
}
|
|
149
|
+
/** Live billing information only. Never infer these fields from a plan or entitlement expiry. Dates are ISO 8601 UTC. */
|
|
150
|
+
export interface IHarnessBilling {
|
|
151
|
+
hasActiveSubscription?: boolean;
|
|
152
|
+
autoRenew?: boolean;
|
|
153
|
+
renewsAt?: string;
|
|
154
|
+
cancelsAt?: string;
|
|
155
|
+
expiresAt?: string;
|
|
156
|
+
}
|
|
140
157
|
export interface IHarnessStatusSummary {
|
|
141
158
|
/** A plan name does not imply an active billing status. */
|
|
142
159
|
subscription?: IAccountType;
|
|
143
|
-
|
|
144
|
-
billing?: {
|
|
145
|
-
hasActiveSubscription?: boolean;
|
|
146
|
-
autoRenew?: boolean;
|
|
147
|
-
renewsAt?: string;
|
|
148
|
-
cancelsAt?: string;
|
|
149
|
-
expiresAt?: string;
|
|
150
|
-
};
|
|
160
|
+
billing?: IHarnessBilling;
|
|
151
161
|
/** Missing windows are unknown, not unused. Times are ISO 8601 UTC. */
|
|
152
162
|
usageWindows?: IHarnessUsageWindow[];
|
|
153
163
|
resets?: {
|
|
@@ -164,10 +174,24 @@ export interface IHarnessOutcome {
|
|
|
164
174
|
lines: string[];
|
|
165
175
|
problems: string[];
|
|
166
176
|
}
|
|
167
|
-
/**
|
|
177
|
+
/**
|
|
178
|
+
* A caller's status-reading session, such as one `authswitch watch`. Within it an adapter may reuse a lookup whose
|
|
179
|
+
* answer rarely changes (a Claude profile) instead of repeating it on every read; outside a session every lookup is live.
|
|
180
|
+
*/
|
|
181
|
+
export interface IHarnessStatusSession {
|
|
182
|
+
/**
|
|
183
|
+
* The value `lookupArg` resolved to under `keyArg` less than `maxAgeMsArg` ago, otherwise the lookup's fresh result,
|
|
184
|
+
* which is then remembered. A rejected lookup is never remembered. Keys belong to the adapter that uses them, which
|
|
185
|
+
* prefixes them with its own id, so a key always names values of one type.
|
|
186
|
+
*/
|
|
187
|
+
reuse<T>(keyArg: string, maxAgeMsArg: number, lookupArg: () => Promise<T>): Promise<T>;
|
|
188
|
+
}
|
|
189
|
+
/** One status lookup; see `IAuthHarness.readAccountStatus` for what it may and may not change. */
|
|
168
190
|
export interface IHarnessStatusOptions {
|
|
169
191
|
/** Cancels the lookup's requests; an adapter still bounds each request with its own timeout. */
|
|
170
192
|
signal?: AbortSignal;
|
|
193
|
+
/** The session the read belongs to, when the caller reads the same accounts repeatedly. */
|
|
194
|
+
session?: IHarnessStatusSession;
|
|
171
195
|
}
|
|
172
196
|
/** A single text-only inference request. Credentials remain inside the adapter. */
|
|
173
197
|
export interface IHarnessPreuseOptions {
|
|
@@ -191,6 +215,31 @@ export interface IAuthHarness {
|
|
|
191
215
|
readState(): THarnessResult<IHarnessState>;
|
|
192
216
|
/** Running instances of this harness, when the adapter owns their lifecycle. */
|
|
193
217
|
readonly processes?: IHarnessProcessControl;
|
|
218
|
+
/**
|
|
219
|
+
* Whether running instances pick up a credential swap by themselves, on their next request.
|
|
220
|
+
*
|
|
221
|
+
* Such a harness is never offered a stop before a switch, and its outcomes carry no restart advice;
|
|
222
|
+
* an explicit stop request is still honoured through `processes`. Absent means running instances keep
|
|
223
|
+
* the login they loaded until they are restarted.
|
|
224
|
+
*/
|
|
225
|
+
readonly liveSwap?: boolean;
|
|
226
|
+
/**
|
|
227
|
+
* Set when this harness's provider never reports renewal or cancellation dates, as a lower-case clause
|
|
228
|
+
* that completes "<label>: ...". Views then explain the missing dates once instead of per account.
|
|
229
|
+
*/
|
|
230
|
+
readonly renewalUnavailableReason?: string;
|
|
231
|
+
/**
|
|
232
|
+
* Whether `authswitch watch` may switch this harness's login by itself when the active account runs out: every
|
|
233
|
+
* saved account's usage can be read, and a switch needs no session of the user's stopped. A helper process the
|
|
234
|
+
* adapter manages itself may still be restarted by its own switch -- Codex' app-server is, because its
|
|
235
|
+
* credential file cannot be rewritten underneath it. Absent means the watch never switches this harness.
|
|
236
|
+
*/
|
|
237
|
+
readonly autoSwitch?: boolean;
|
|
238
|
+
/**
|
|
239
|
+
* The account's live status. It must never activate, switch or clear a login. It may renew the tokens of the
|
|
240
|
+
* saved copy it reads when the provider leaves it no other way to answer -- the Claude Code adapter does, for
|
|
241
|
+
* saved and inactive logins only, under its store's lock -- and must leave the active login's credential alone.
|
|
242
|
+
*/
|
|
194
243
|
readAccountStatus(accountIdArg: string, optionsArg?: IHarnessStatusOptions): Promise<IHarnessAccountStatus>;
|
|
195
244
|
readonly loginProviders?: IHarnessLoginProvider[];
|
|
196
245
|
beginLogin?(options: IHarnessLoginOptions): Promise<IHarnessLoginHandle>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { IAccountType, IHarnessAccount, IHarnessAccountStatus, IHarnessCredentialDrift, TUsageSeverity } from './interfaces.harness.js';
|
|
1
|
+
import type { IAccountType, IHarnessAccount, IHarnessAccountStatus, IHarnessBilling, IHarnessCredentialDrift, TUsageSeverity } from './interfaces.harness.js';
|
|
2
2
|
/** Credential-free, versioned output of list --json. Missing status fields stay omitted. */
|
|
3
3
|
export interface IAccountList {
|
|
4
4
|
schemaVersion: 2;
|
|
@@ -39,6 +39,8 @@ export interface IAccountLimitRow {
|
|
|
39
39
|
isActive: boolean;
|
|
40
40
|
/** The adapter's plan name (`max`, `pro`), sanitised but not display-formatted; null when none is known or the row names no account. */
|
|
41
41
|
accountType: IAccountType | null;
|
|
42
|
+
/** The account's live billing fields as its provider reported them (ISO UTC dates); null when none were reported or the row names no account. */
|
|
43
|
+
billing: IHarnessBilling | 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;
|
package/dist_ts/plugins.d.ts
CHANGED
|
@@ -8,6 +8,13 @@ export { childProcess, crypto, fs, os, path };
|
|
|
8
8
|
export type { DatabaseSync };
|
|
9
9
|
/** SQLite is needed only when accessing a Codex state database, not when hosting account APIs. */
|
|
10
10
|
export declare const loadSqlite: () => typeof import("node:sqlite");
|
|
11
|
+
import type * as properLockfile from 'proper-lockfile';
|
|
12
|
+
export type TProperLockfile = typeof properLockfile;
|
|
13
|
+
/**
|
|
14
|
+
* Claude Code's own lock library, loaded on first use: it installs process exit hooks when it loads, which a
|
|
15
|
+
* process that never changes a Claude Code login (such as a host embedding the account service) does not need.
|
|
16
|
+
*/
|
|
17
|
+
export declare const loadProperLockfile: () => Promise<TProperLockfile>;
|
|
11
18
|
import * as smartconsole from '@push.rocks/smartconsole';
|
|
12
19
|
export { smartconsole };
|
|
13
20
|
import * as flexModels from '@modelprofile.com/flexharness-models';
|
package/dist_ts/plugins.js
CHANGED
|
@@ -9,6 +9,11 @@ export { childProcess, crypto, fs, os, path };
|
|
|
9
9
|
const nativeRequire = createRequire(import.meta.url);
|
|
10
10
|
/** SQLite is needed only when accessing a Codex state database, not when hosting account APIs. */
|
|
11
11
|
export const loadSqlite = () => nativeRequire('node:sqlite');
|
|
12
|
+
/**
|
|
13
|
+
* Claude Code's own lock library, loaded on first use: it installs process exit hooks when it loads, which a
|
|
14
|
+
* process that never changes a Claude Code login (such as a host embedding the account service) does not need.
|
|
15
|
+
*/
|
|
16
|
+
export const loadProperLockfile = () => import('proper-lockfile');
|
|
12
17
|
// @push.rocks modules
|
|
13
18
|
import * as smartconsole from '@push.rocks/smartconsole';
|
|
14
19
|
export { smartconsole };
|
|
@@ -18,4 +23,4 @@ import * as flexOpenAi from '@modelprofile.com/flexharness-providers/openai';
|
|
|
18
23
|
import * as flexAuth from '@modelprofile.com/flexharness-providers/auth';
|
|
19
24
|
import * as flexAccounts from '@modelprofile.com/flexharness-providers/accounts';
|
|
20
25
|
export { flexModels, flexOpenAi, flexAuth, flexAccounts };
|
|
21
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
26
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGx1Z2lucy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3RzL3BsdWdpbnMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsc0JBQXNCO0FBQ3RCLE9BQU8sS0FBSyxFQUFFLE1BQU0sU0FBUyxDQUFDO0FBQzlCLE9BQU8sS0FBSyxFQUFFLE1BQU0sU0FBUyxDQUFDO0FBQzlCLE9BQU8sS0FBSyxJQUFJLE1BQU0sV0FBVyxDQUFDO0FBQ2xDLE9BQU8sS0FBSyxZQUFZLE1BQU0sb0JBQW9CLENBQUM7QUFDbkQsT0FBTyxLQUFLLE1BQU0sTUFBTSxhQUFhLENBQUM7QUFDdEMsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUc1QyxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sRUFBRSxFQUFFLEVBQUUsRUFBRSxFQUFFLElBQUksRUFBRSxDQUFDO0FBRTlDLE1BQU0sYUFBYSxHQUFHLGFBQWEsQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDO0FBQ3JELGtHQUFrRztBQUNsRyxNQUFNLENBQUMsTUFBTSxVQUFVLEdBQUcsR0FBaUMsRUFBRSxDQUFDLGFBQWEsQ0FBQyxhQUFhLENBQUMsQ0FBQztBQUszRjs7O0dBR0c7QUFDSCxNQUFNLENBQUMsTUFBTSxrQkFBa0IsR0FBRyxHQUE2QixFQUFFLENBQUMsTUFBTSxDQUFDLGlCQUFpQixDQUFDLENBQUM7QUFFNUYsc0JBQXNCO0FBQ3RCLE9BQU8sS0FBSyxZQUFZLE1BQU0sMEJBQTBCLENBQUM7QUFDekQsT0FBTyxFQUFFLFlBQVksRUFBRSxDQUFDO0FBRXhCLDRCQUE0QjtBQUM1QixPQUFPLEtBQUssVUFBVSxNQUFNLHNDQUFzQyxDQUFDO0FBQ25FLE9BQU8sS0FBSyxVQUFVLE1BQU0sZ0RBQWdELENBQUM7QUFDN0UsT0FBTyxLQUFLLFFBQVEsTUFBTSw4Q0FBOEMsQ0FBQztBQUN6RSxPQUFPLEtBQUssWUFBWSxNQUFNLGtEQUFrRCxDQUFDO0FBQ2pGLE9BQU8sRUFBRSxVQUFVLEVBQUUsVUFBVSxFQUFFLFFBQVEsRUFBRSxZQUFZLEVBQUUsQ0FBQyJ9
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* When a provider that answered HTTP 429 asked to be tried again, read from its `Retry-After` header (RFC 9110: a
|
|
3
|
+
* number of seconds, or an HTTP date) and returned as ISO 8601 UTC. An absent or unreadable header is null: the
|
|
4
|
+
* provider did not say, and the caller picks its own backoff.
|
|
5
|
+
*/
|
|
6
|
+
export declare const retryAtFrom: (headerArg: string | null, nowArg: number) => string | null;
|
|
7
|
+
/** The latest retry time several refused requests asked for; null when none of them said. */
|
|
8
|
+
export declare const latestRetryAt: (retryAtsArg: readonly (string | null)[]) => string | null;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* When a provider that answered HTTP 429 asked to be tried again, read from its `Retry-After` header (RFC 9110: a
|
|
3
|
+
* number of seconds, or an HTTP date) and returned as ISO 8601 UTC. An absent or unreadable header is null: the
|
|
4
|
+
* provider did not say, and the caller picks its own backoff.
|
|
5
|
+
*/
|
|
6
|
+
export const retryAtFrom = (headerArg, nowArg) => {
|
|
7
|
+
const value = headerArg?.trim() ?? '';
|
|
8
|
+
const time = /^\d{1,9}$/.test(value) ? nowArg + Number(value) * 1000 : /^[A-Za-z]{3}, .+ GMT$/.test(value) ? Date.parse(value) : Number.NaN;
|
|
9
|
+
return Number.isFinite(time) ? new Date(time).toISOString() : null;
|
|
10
|
+
};
|
|
11
|
+
/** The latest retry time several refused requests asked for; null when none of them said. */
|
|
12
|
+
export const latestRetryAt = (retryAtsArg) => retryAtsArg.filter((value) => value !== null).sort().at(-1) ?? null;
|
|
13
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmF0ZWxpbWl0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvcmF0ZWxpbWl0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7O0dBSUc7QUFDSCxNQUFNLENBQUMsTUFBTSxXQUFXLEdBQUcsQ0FBQyxTQUF3QixFQUFFLE1BQWMsRUFBaUIsRUFBRTtJQUNyRixNQUFNLEtBQUssR0FBRyxTQUFTLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxDQUFDO0lBQ3RDLE1BQU0sSUFBSSxHQUFHLFdBQVcsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLE1BQU0sR0FBRyxNQUFNLENBQUMsS0FBSyxDQUFDLEdBQUcsSUFBSSxDQUFDLENBQUMsQ0FBQyx1QkFBdUIsQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxHQUFHLENBQUM7SUFDNUksT0FBTyxNQUFNLENBQUMsUUFBUSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxXQUFXLEVBQUUsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDO0FBQ3JFLENBQUMsQ0FBQztBQUVGLDZGQUE2RjtBQUM3RixNQUFNLENBQUMsTUFBTSxhQUFhLEdBQUcsQ0FBQyxXQUF1QyxFQUFpQixFQUFFLENBQ3RGLFdBQVcsQ0FBQyxNQUFNLENBQUMsQ0FBQyxLQUFLLEVBQW1CLEVBQUUsQ0FBQyxLQUFLLEtBQUssSUFBSSxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxDQUFDLElBQUksSUFBSSxDQUFDIn0=
|