axusage 3.0.0 → 3.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.
- package/README.md +78 -194
- package/dist/adapters/claude.js +9 -8
- package/dist/adapters/coalesce-claude-usage-response.js +5 -7
- package/dist/adapters/{chatgpt.d.ts → codex.d.ts} +1 -1
- package/dist/adapters/{chatgpt.js → codex.js} +5 -5
- package/dist/adapters/copilot.d.ts +7 -0
- package/dist/adapters/copilot.js +58 -0
- package/dist/adapters/{parse-chatgpt-usage.d.ts → parse-codex-usage.d.ts} +3 -3
- package/dist/adapters/parse-copilot-usage.d.ts +15 -0
- package/dist/adapters/parse-copilot-usage.js +61 -0
- package/dist/cli.js +4 -21
- package/dist/commands/auth-setup-command.d.ts +1 -2
- package/dist/commands/auth-setup-command.js +44 -67
- package/dist/commands/auth-status-command.js +18 -38
- package/dist/commands/fetch-service-usage.d.ts +0 -1
- package/dist/commands/fetch-service-usage.js +1 -2
- package/dist/commands/run-auth-setup.d.ts +0 -10
- package/dist/commands/run-auth-setup.js +3 -80
- package/dist/commands/usage-command.d.ts +2 -7
- package/dist/commands/usage-command.js +7 -39
- package/dist/config/credential-sources.d.ts +12 -12
- package/dist/config/credential-sources.js +15 -2
- package/dist/services/get-service-access-token.d.ts +3 -3
- package/dist/services/get-service-access-token.js +11 -11
- package/dist/services/service-adapter-registry.d.ts +2 -2
- package/dist/services/service-adapter-registry.js +4 -4
- package/dist/services/supported-service.d.ts +6 -2
- package/dist/services/supported-service.js +2 -6
- package/dist/types/{chatgpt.d.ts → codex.d.ts} +4 -4
- package/dist/types/{chatgpt.js → codex.js} +6 -6
- package/dist/types/copilot.d.ts +14 -0
- package/dist/types/copilot.js +21 -0
- package/dist/utils/check-cli-dependency.d.ts +2 -4
- package/dist/utils/check-cli-dependency.js +7 -4
- package/dist/utils/copilot-gh-token.d.ts +1 -0
- package/dist/utils/copilot-gh-token.js +38 -0
- package/dist/utils/validate-root-options.d.ts +0 -3
- package/dist/utils/validate-root-options.js +2 -6
- package/package.json +15 -19
- package/dist/adapters/github-copilot.d.ts +0 -6
- package/dist/adapters/github-copilot.js +0 -57
- package/dist/adapters/parse-github-copilot-usage.d.ts +0 -23
- package/dist/adapters/parse-github-copilot-usage.js +0 -78
- package/dist/commands/auth-clear-command.d.ts +0 -7
- package/dist/commands/auth-clear-command.js +0 -84
- package/dist/commands/fetch-service-usage-with-reauth.d.ts +0 -7
- package/dist/commands/fetch-service-usage-with-reauth.js +0 -45
- package/dist/services/app-paths.d.ts +0 -9
- package/dist/services/app-paths.js +0 -39
- package/dist/services/auth-storage-path.d.ts +0 -3
- package/dist/services/auth-storage-path.js +0 -7
- package/dist/services/auth-timeouts.d.ts +0 -4
- package/dist/services/auth-timeouts.js +0 -4
- package/dist/services/browser-auth-manager.d.ts +0 -49
- package/dist/services/browser-auth-manager.js +0 -113
- package/dist/services/create-auth-context.d.ts +0 -8
- package/dist/services/create-auth-context.js +0 -35
- package/dist/services/do-setup-auth.d.ts +0 -3
- package/dist/services/do-setup-auth.js +0 -19
- package/dist/services/fetch-json-with-context.d.ts +0 -5
- package/dist/services/fetch-json-with-context.js +0 -37
- package/dist/services/launch-chromium.d.ts +0 -6
- package/dist/services/launch-chromium.js +0 -20
- package/dist/services/persist-storage-state.d.ts +0 -6
- package/dist/services/persist-storage-state.js +0 -16
- package/dist/services/request-service.d.ts +0 -3
- package/dist/services/request-service.js +0 -4
- package/dist/services/service-auth-configs.d.ts +0 -15
- package/dist/services/service-auth-configs.js +0 -26
- package/dist/services/setup-auth-flow.d.ts +0 -3
- package/dist/services/setup-auth-flow.js +0 -67
- package/dist/services/shared-browser-auth-manager.d.ts +0 -4
- package/dist/services/shared-browser-auth-manager.js +0 -80
- package/dist/services/verify-session.d.ts +0 -2
- package/dist/services/verify-session.js +0 -25
- package/dist/services/wait-for-login.d.ts +0 -6
- package/dist/services/wait-for-login.js +0 -115
- package/dist/types/github-copilot.d.ts +0 -21
- package/dist/types/github-copilot.js +0 -27
- package/dist/utils/resolve-prompt-capability.d.ts +0 -1
- package/dist/utils/resolve-prompt-capability.js +0 -3
- package/dist/utils/write-atomic-json.d.ts +0 -1
- package/dist/utils/write-atomic-json.js +0 -56
- /package/dist/adapters/{parse-chatgpt-usage.js → parse-codex-usage.js} +0 -0
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { BrowserAuthManager } from "./browser-auth-manager.js";
|
|
2
|
-
let manager;
|
|
3
|
-
let references = 0;
|
|
4
|
-
let cleanupInstalled = false;
|
|
5
|
-
let closing = false;
|
|
6
|
-
let exitInitiated = false;
|
|
7
|
-
export function acquireAuthManager() {
|
|
8
|
-
if (!manager)
|
|
9
|
-
manager = new BrowserAuthManager();
|
|
10
|
-
references++;
|
|
11
|
-
return manager;
|
|
12
|
-
}
|
|
13
|
-
export async function releaseAuthManager() {
|
|
14
|
-
if (references <= 0) {
|
|
15
|
-
// Over-release guard: ignore unmatched release
|
|
16
|
-
if (references === 0) {
|
|
17
|
-
console.warn("releaseAuthManager() called without a matching acquire; ignoring");
|
|
18
|
-
}
|
|
19
|
-
// Avoid closing the manager in an over-release state
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
references -= 1;
|
|
23
|
-
if (references === 0 && manager) {
|
|
24
|
-
await manager.close();
|
|
25
|
-
manager = undefined;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
async function forceClose() {
|
|
29
|
-
if (closing)
|
|
30
|
-
return;
|
|
31
|
-
closing = true;
|
|
32
|
-
references = 0;
|
|
33
|
-
if (manager) {
|
|
34
|
-
try {
|
|
35
|
-
await manager.close();
|
|
36
|
-
}
|
|
37
|
-
catch {
|
|
38
|
-
// ignore
|
|
39
|
-
}
|
|
40
|
-
finally {
|
|
41
|
-
manager = undefined;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
export function installAuthManagerCleanup() {
|
|
46
|
-
if (cleanupInstalled)
|
|
47
|
-
return;
|
|
48
|
-
cleanupInstalled = true;
|
|
49
|
-
/* eslint-disable unicorn/no-process-exit */
|
|
50
|
-
const safeExit = (code) => {
|
|
51
|
-
if (exitInitiated)
|
|
52
|
-
return;
|
|
53
|
-
exitInitiated = true;
|
|
54
|
-
process.exit(code);
|
|
55
|
-
};
|
|
56
|
-
/* eslint-enable unicorn/no-process-exit */
|
|
57
|
-
process.on("SIGINT", () => {
|
|
58
|
-
// Ensure browser is actually closed before exiting
|
|
59
|
-
void forceClose().finally(() => {
|
|
60
|
-
safeExit(130);
|
|
61
|
-
});
|
|
62
|
-
});
|
|
63
|
-
process.on("SIGTERM", () => {
|
|
64
|
-
// Ensure browser is actually closed before exiting
|
|
65
|
-
void forceClose().finally(() => {
|
|
66
|
-
safeExit(143);
|
|
67
|
-
});
|
|
68
|
-
});
|
|
69
|
-
process.on("beforeExit", () => {
|
|
70
|
-
// Best-effort cleanup on natural process exit; ensure we schedule a macrotask
|
|
71
|
-
// so the event loop stays alive until the async close finishes.
|
|
72
|
-
if (process.exitCode === undefined)
|
|
73
|
-
process.exitCode = 0;
|
|
74
|
-
setImmediate(() => {
|
|
75
|
-
void forceClose().finally(() => {
|
|
76
|
-
safeExit(Number(process.exitCode ?? 0));
|
|
77
|
-
});
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { fetchJsonWithContext } from "./fetch-json-with-context.js";
|
|
2
|
-
/**
|
|
3
|
-
* Tries to fetch the given URL within the authenticated context.
|
|
4
|
-
* Returns true if the request succeeds, false if it keeps failing.
|
|
5
|
-
*/
|
|
6
|
-
const DEFAULT_MAX_ATTEMPTS = 5;
|
|
7
|
-
const DEFAULT_RETRY_DELAY_MS = 1500;
|
|
8
|
-
export async function verifySessionByFetching(context, url, maxAttempts = DEFAULT_MAX_ATTEMPTS, delayMs = DEFAULT_RETRY_DELAY_MS) {
|
|
9
|
-
// Try up to `maxAttempts` times; some providers need a brief
|
|
10
|
-
// delay for session cookies/tokens to settle after login.
|
|
11
|
-
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
12
|
-
try {
|
|
13
|
-
await fetchJsonWithContext(context, url);
|
|
14
|
-
return true;
|
|
15
|
-
}
|
|
16
|
-
catch {
|
|
17
|
-
// Wait a bit and try again; tokens/cookies may not be settled yet
|
|
18
|
-
// Skip the delay after the final attempt to avoid unnecessary wait
|
|
19
|
-
if (attempt < maxAttempts - 1) {
|
|
20
|
-
await new Promise((resolve) => setTimeout(resolve, delayMs));
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { type Page } from "playwright";
|
|
2
|
-
/**
|
|
3
|
-
* Waits until one of the selectors appears on the page, or the user presses Enter to continue.
|
|
4
|
-
*/
|
|
5
|
-
export type LoginWaitOutcome = "selector" | "manual" | "timeout" | "closed" | "aborted" | "skipped";
|
|
6
|
-
export declare function waitForLogin(page: Page, selectors: readonly string[]): Promise<LoginWaitOutcome>;
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import { errors } from "playwright";
|
|
2
|
-
import { LOGIN_TIMEOUT_MS } from "./auth-timeouts.js";
|
|
3
|
-
import { input } from "@inquirer/prompts";
|
|
4
|
-
function isTimeoutError(error) {
|
|
5
|
-
return error instanceof errors.TimeoutError;
|
|
6
|
-
}
|
|
7
|
-
const SELECTOR_CLOSED_MESSAGES = [
|
|
8
|
-
"target closed",
|
|
9
|
-
"page closed",
|
|
10
|
-
"context closed",
|
|
11
|
-
"execution context was destroyed",
|
|
12
|
-
];
|
|
13
|
-
function isSelectorClosedError(error) {
|
|
14
|
-
if (!(error instanceof Error))
|
|
15
|
-
return false;
|
|
16
|
-
const message = error.message.toLowerCase();
|
|
17
|
-
return SELECTOR_CLOSED_MESSAGES.some((snippet) => message.includes(snippet));
|
|
18
|
-
}
|
|
19
|
-
function classifySelectorFailure(error) {
|
|
20
|
-
if (isTimeoutError(error))
|
|
21
|
-
return "timeout";
|
|
22
|
-
if (isSelectorClosedError(error))
|
|
23
|
-
return "closed";
|
|
24
|
-
return undefined;
|
|
25
|
-
}
|
|
26
|
-
function classifySelectorAggregate(error) {
|
|
27
|
-
if (error instanceof AggregateError) {
|
|
28
|
-
const outcomes = error.errors.map((item) => classifySelectorFailure(item));
|
|
29
|
-
if (outcomes.every((item) => item === "timeout"))
|
|
30
|
-
return "timeout";
|
|
31
|
-
if (outcomes.every((item) => item === "timeout" || item === "closed") &&
|
|
32
|
-
outcomes.includes("closed")) {
|
|
33
|
-
return "closed";
|
|
34
|
-
}
|
|
35
|
-
return undefined;
|
|
36
|
-
}
|
|
37
|
-
return classifySelectorFailure(error);
|
|
38
|
-
}
|
|
39
|
-
export async function waitForLogin(page, selectors) {
|
|
40
|
-
const timeoutMs = LOGIN_TIMEOUT_MS;
|
|
41
|
-
const canPrompt = process.stdin.isTTY && process.stdout.isTTY;
|
|
42
|
-
// Non-TTY sessions rely solely on selector waits (no manual continuation).
|
|
43
|
-
if (!canPrompt && selectors.length === 0) {
|
|
44
|
-
return "skipped";
|
|
45
|
-
}
|
|
46
|
-
const waiters = selectors.map((sel) => page.waitForSelector(sel, { timeout: timeoutMs }));
|
|
47
|
-
const shouldShowCountdown = process.stderr.isTTY && waiters.length > 0;
|
|
48
|
-
let interval;
|
|
49
|
-
const manualController = canPrompt ? new AbortController() : undefined;
|
|
50
|
-
const manualPromise = manualController
|
|
51
|
-
? input({
|
|
52
|
-
message: "Press Enter after completing login in the browser...",
|
|
53
|
-
default: "",
|
|
54
|
-
}, { signal: manualController.signal })
|
|
55
|
-
.then(() => "manual")
|
|
56
|
-
.catch((error) => {
|
|
57
|
-
if (error instanceof Error &&
|
|
58
|
-
(error.name === "AbortPromptError" || error.name === "AbortError")) {
|
|
59
|
-
// Expected when we cancel the prompt after a selector wins.
|
|
60
|
-
// Returning "manual" keeps the promise resolved for the race.
|
|
61
|
-
return "manual";
|
|
62
|
-
}
|
|
63
|
-
if (error instanceof Error && error.name === "ExitPromptError") {
|
|
64
|
-
return "aborted";
|
|
65
|
-
}
|
|
66
|
-
throw error;
|
|
67
|
-
})
|
|
68
|
-
: undefined;
|
|
69
|
-
if (shouldShowCountdown) {
|
|
70
|
-
const deadline = Date.now() + timeoutMs;
|
|
71
|
-
interval = setInterval(() => {
|
|
72
|
-
const remaining = deadline - Date.now();
|
|
73
|
-
if (remaining <= 0) {
|
|
74
|
-
// Stop logging once timeout elapses to avoid confusing "0 minute(s)" spam
|
|
75
|
-
if (interval)
|
|
76
|
-
clearInterval(interval);
|
|
77
|
-
console.error("Login wait timed out; finishing up...");
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
// Round up to the next minute for clearer UX, ensure at least 1
|
|
81
|
-
const minutes = Math.max(1, Math.ceil(remaining / 60_000));
|
|
82
|
-
console.error(`Still waiting for login... ${String(minutes)} minute(s) remaining`);
|
|
83
|
-
}, 60_000);
|
|
84
|
-
}
|
|
85
|
-
try {
|
|
86
|
-
const selectorPromise = waiters.length > 0
|
|
87
|
-
? Promise.any(waiters)
|
|
88
|
-
.then(() => "selector")
|
|
89
|
-
.catch((error) => {
|
|
90
|
-
// Promise.any only rejects once all selectors have settled.
|
|
91
|
-
const outcome = classifySelectorAggregate(error);
|
|
92
|
-
if (outcome)
|
|
93
|
-
return outcome;
|
|
94
|
-
throw error;
|
|
95
|
-
})
|
|
96
|
-
: undefined;
|
|
97
|
-
if (selectorPromise) {
|
|
98
|
-
// Avoid unhandled rejections if the manual prompt wins the race.
|
|
99
|
-
void selectorPromise.catch(() => { });
|
|
100
|
-
}
|
|
101
|
-
const raceTargets = [];
|
|
102
|
-
if (manualPromise)
|
|
103
|
-
raceTargets.push(manualPromise);
|
|
104
|
-
if (selectorPromise)
|
|
105
|
-
raceTargets.push(selectorPromise);
|
|
106
|
-
if (raceTargets.length === 0)
|
|
107
|
-
return "skipped";
|
|
108
|
-
return await Promise.race(raceTargets);
|
|
109
|
-
}
|
|
110
|
-
finally {
|
|
111
|
-
if (interval)
|
|
112
|
-
clearInterval(interval);
|
|
113
|
-
manualController?.abort();
|
|
114
|
-
}
|
|
115
|
-
}
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
export declare const GitHubCopilotUsageResponse: z.ZodObject<{
|
|
3
|
-
licenseType: z.ZodString;
|
|
4
|
-
quotas: z.ZodObject<{
|
|
5
|
-
limits: z.ZodObject<{
|
|
6
|
-
premiumInteractions: z.ZodNumber;
|
|
7
|
-
}, z.core.$strip>;
|
|
8
|
-
remaining: z.ZodObject<{
|
|
9
|
-
premiumInteractions: z.ZodNumber;
|
|
10
|
-
chatPercentage: z.ZodOptional<z.ZodNumber>;
|
|
11
|
-
premiumInteractionsPercentage: z.ZodNumber;
|
|
12
|
-
}, z.core.$strip>;
|
|
13
|
-
resetDate: z.ZodString;
|
|
14
|
-
overagesEnabled: z.ZodOptional<z.ZodBoolean>;
|
|
15
|
-
}, z.core.$strip>;
|
|
16
|
-
plan: z.ZodString;
|
|
17
|
-
trial: z.ZodOptional<z.ZodObject<{
|
|
18
|
-
eligible: z.ZodBoolean;
|
|
19
|
-
}, z.core.$strip>>;
|
|
20
|
-
}, z.core.$strip>;
|
|
21
|
-
export type GitHubCopilotUsageResponse = z.infer<typeof GitHubCopilotUsageResponse>;
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
/**
|
|
3
|
-
* GitHub Copilot API response schemas
|
|
4
|
-
*/
|
|
5
|
-
const GitHubCopilotQuotaLimits = z.object({
|
|
6
|
-
premiumInteractions: z.number(),
|
|
7
|
-
});
|
|
8
|
-
const GitHubCopilotQuotaRemaining = z.object({
|
|
9
|
-
premiumInteractions: z.number(),
|
|
10
|
-
chatPercentage: z.number().optional(),
|
|
11
|
-
premiumInteractionsPercentage: z.number(),
|
|
12
|
-
});
|
|
13
|
-
const GitHubCopilotQuotas = z.object({
|
|
14
|
-
limits: GitHubCopilotQuotaLimits,
|
|
15
|
-
remaining: GitHubCopilotQuotaRemaining,
|
|
16
|
-
resetDate: z.string(), // Format: "YYYY-MM-DD"
|
|
17
|
-
overagesEnabled: z.boolean().optional(),
|
|
18
|
-
});
|
|
19
|
-
const GitHubCopilotTrial = z.object({
|
|
20
|
-
eligible: z.boolean(),
|
|
21
|
-
});
|
|
22
|
-
export const GitHubCopilotUsageResponse = z.object({
|
|
23
|
-
licenseType: z.string(),
|
|
24
|
-
quotas: GitHubCopilotQuotas,
|
|
25
|
-
plan: z.string(),
|
|
26
|
-
trial: GitHubCopilotTrial.optional(),
|
|
27
|
-
});
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function resolvePromptCapability(): boolean;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export declare function writeAtomicJson(filePath: string, data: unknown, mode?: number): Promise<void>;
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { chmod, rename, unlink, writeFile } from "node:fs/promises";
|
|
2
|
-
import { randomUUID } from "node:crypto";
|
|
3
|
-
function getErrorCode(error) {
|
|
4
|
-
if (error instanceof Error && "code" in error) {
|
|
5
|
-
return error.code;
|
|
6
|
-
}
|
|
7
|
-
return undefined;
|
|
8
|
-
}
|
|
9
|
-
export async function writeAtomicJson(filePath, data, mode) {
|
|
10
|
-
const temporaryPath = `${filePath}.${randomUUID()}.tmp`;
|
|
11
|
-
const writeOptions = mode === undefined ? "utf8" : { encoding: "utf8", mode };
|
|
12
|
-
await writeFile(temporaryPath, JSON.stringify(data), writeOptions);
|
|
13
|
-
if (mode !== undefined) {
|
|
14
|
-
await chmod(temporaryPath, mode).catch(() => {
|
|
15
|
-
// Best-effort: some filesystems ignore chmod, but the mode was set at write.
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
try {
|
|
19
|
-
await rename(temporaryPath, filePath);
|
|
20
|
-
}
|
|
21
|
-
catch (error) {
|
|
22
|
-
const code = getErrorCode(error);
|
|
23
|
-
if (code === "EPERM" || code === "EACCES" || code === "EEXIST") {
|
|
24
|
-
// Windows can reject rename over an existing file; fall back to a backup swap.
|
|
25
|
-
// Best-effort: not fully atomic and assumes a single writer. Backups are
|
|
26
|
-
// cleaned up by auth-clear when possible.
|
|
27
|
-
const backupPath = `${filePath}.${randomUUID()}.bak`;
|
|
28
|
-
let hasBackup = false;
|
|
29
|
-
try {
|
|
30
|
-
await rename(filePath, backupPath);
|
|
31
|
-
hasBackup = true;
|
|
32
|
-
}
|
|
33
|
-
catch {
|
|
34
|
-
// Best-effort: source file may not exist or be locked.
|
|
35
|
-
}
|
|
36
|
-
try {
|
|
37
|
-
await rename(temporaryPath, filePath);
|
|
38
|
-
}
|
|
39
|
-
catch (fallbackError) {
|
|
40
|
-
if (hasBackup) {
|
|
41
|
-
await rename(backupPath, filePath).catch(() => {
|
|
42
|
-
console.warn(`Warning: Failed to restore backup from ${backupPath}`);
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
await unlink(temporaryPath).catch(() => { });
|
|
46
|
-
throw fallbackError;
|
|
47
|
-
}
|
|
48
|
-
if (hasBackup) {
|
|
49
|
-
await unlink(backupPath).catch(() => { });
|
|
50
|
-
}
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
await unlink(temporaryPath).catch(() => { });
|
|
54
|
-
throw error;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
File without changes
|