axusage 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.
Files changed (88) hide show
  1. package/README.md +78 -194
  2. package/dist/adapters/claude.js +9 -8
  3. package/dist/adapters/coalesce-claude-usage-response.js +5 -7
  4. package/dist/adapters/{chatgpt.d.ts → codex.d.ts} +1 -1
  5. package/dist/adapters/{chatgpt.js → codex.js} +5 -5
  6. package/dist/adapters/copilot.d.ts +7 -0
  7. package/dist/adapters/copilot.js +58 -0
  8. package/dist/adapters/{parse-chatgpt-usage.d.ts → parse-codex-usage.d.ts} +3 -3
  9. package/dist/adapters/parse-copilot-usage.d.ts +15 -0
  10. package/dist/adapters/parse-copilot-usage.js +61 -0
  11. package/dist/cli.js +4 -21
  12. package/dist/commands/auth-setup-command.d.ts +1 -2
  13. package/dist/commands/auth-setup-command.js +44 -67
  14. package/dist/commands/auth-status-command.js +9 -40
  15. package/dist/commands/fetch-service-usage.d.ts +0 -1
  16. package/dist/commands/fetch-service-usage.js +1 -2
  17. package/dist/commands/run-auth-setup.d.ts +0 -10
  18. package/dist/commands/run-auth-setup.js +3 -80
  19. package/dist/commands/usage-command.d.ts +2 -7
  20. package/dist/commands/usage-command.js +7 -39
  21. package/dist/config/credential-sources.d.ts +3 -11
  22. package/dist/config/credential-sources.js +1 -1
  23. package/dist/services/get-service-access-token.d.ts +3 -3
  24. package/dist/services/get-service-access-token.js +11 -11
  25. package/dist/services/service-adapter-registry.d.ts +2 -2
  26. package/dist/services/service-adapter-registry.js +4 -4
  27. package/dist/services/service-diagnostics.d.ts +11 -0
  28. package/dist/services/service-diagnostics.js +29 -0
  29. package/dist/services/supported-service.d.ts +6 -2
  30. package/dist/services/supported-service.js +2 -6
  31. package/dist/types/{chatgpt.d.ts → codex.d.ts} +4 -4
  32. package/dist/types/{chatgpt.js → codex.js} +6 -6
  33. package/dist/types/copilot.d.ts +14 -0
  34. package/dist/types/copilot.js +21 -0
  35. package/dist/utils/check-cli-dependency.d.ts +2 -4
  36. package/dist/utils/check-cli-dependency.js +7 -4
  37. package/dist/utils/copilot-gh-token.d.ts +1 -0
  38. package/dist/utils/copilot-gh-token.js +38 -0
  39. package/dist/utils/format-requires-help-text.d.ts +17 -0
  40. package/dist/utils/format-requires-help-text.js +62 -0
  41. package/dist/utils/validate-root-options.d.ts +0 -3
  42. package/dist/utils/validate-root-options.js +2 -6
  43. package/package.json +15 -19
  44. package/dist/adapters/github-copilot.d.ts +0 -6
  45. package/dist/adapters/github-copilot.js +0 -57
  46. package/dist/adapters/parse-github-copilot-usage.d.ts +0 -23
  47. package/dist/adapters/parse-github-copilot-usage.js +0 -78
  48. package/dist/commands/auth-clear-command.d.ts +0 -7
  49. package/dist/commands/auth-clear-command.js +0 -84
  50. package/dist/commands/fetch-service-usage-with-reauth.d.ts +0 -7
  51. package/dist/commands/fetch-service-usage-with-reauth.js +0 -45
  52. package/dist/services/app-paths.d.ts +0 -9
  53. package/dist/services/app-paths.js +0 -39
  54. package/dist/services/auth-storage-path.d.ts +0 -3
  55. package/dist/services/auth-storage-path.js +0 -7
  56. package/dist/services/auth-timeouts.d.ts +0 -4
  57. package/dist/services/auth-timeouts.js +0 -4
  58. package/dist/services/browser-auth-manager.d.ts +0 -49
  59. package/dist/services/browser-auth-manager.js +0 -113
  60. package/dist/services/create-auth-context.d.ts +0 -8
  61. package/dist/services/create-auth-context.js +0 -35
  62. package/dist/services/do-setup-auth.d.ts +0 -3
  63. package/dist/services/do-setup-auth.js +0 -19
  64. package/dist/services/fetch-json-with-context.d.ts +0 -5
  65. package/dist/services/fetch-json-with-context.js +0 -37
  66. package/dist/services/launch-chromium.d.ts +0 -6
  67. package/dist/services/launch-chromium.js +0 -20
  68. package/dist/services/persist-storage-state.d.ts +0 -6
  69. package/dist/services/persist-storage-state.js +0 -16
  70. package/dist/services/request-service.d.ts +0 -3
  71. package/dist/services/request-service.js +0 -4
  72. package/dist/services/service-auth-configs.d.ts +0 -15
  73. package/dist/services/service-auth-configs.js +0 -26
  74. package/dist/services/setup-auth-flow.d.ts +0 -3
  75. package/dist/services/setup-auth-flow.js +0 -67
  76. package/dist/services/shared-browser-auth-manager.d.ts +0 -4
  77. package/dist/services/shared-browser-auth-manager.js +0 -87
  78. package/dist/services/verify-session.d.ts +0 -2
  79. package/dist/services/verify-session.js +0 -27
  80. package/dist/services/wait-for-login.d.ts +0 -6
  81. package/dist/services/wait-for-login.js +0 -115
  82. package/dist/types/github-copilot.d.ts +0 -21
  83. package/dist/types/github-copilot.js +0 -27
  84. package/dist/utils/resolve-prompt-capability.d.ts +0 -1
  85. package/dist/utils/resolve-prompt-capability.js +0 -3
  86. package/dist/utils/write-atomic-json.d.ts +0 -1
  87. package/dist/utils/write-atomic-json.js +0 -56
  88. /package/dist/adapters/{parse-chatgpt-usage.js → parse-codex-usage.js} +0 -0
@@ -1,67 +0,0 @@
1
- import { getServiceAuthConfig } from "./service-auth-configs.js";
2
- import { waitForLogin } from "./wait-for-login.js";
3
- import { verifySessionByFetching } from "./verify-session.js";
4
- import { writeAtomicJson } from "../utils/write-atomic-json.js";
5
- function describeLoginOutcome(outcome) {
6
- switch (outcome) {
7
- case "manual": {
8
- return "after manual continuation";
9
- }
10
- case "timeout": {
11
- return "after login timeout";
12
- }
13
- case "closed": {
14
- return "after the browser window closed";
15
- }
16
- case "aborted": {
17
- return "after prompt cancellation";
18
- }
19
- case "selector": {
20
- return "after detecting a login signal";
21
- }
22
- case "skipped": {
23
- return "without waiting for a login signal";
24
- }
25
- }
26
- }
27
- export async function setupAuthInContext(service, context, storagePath) {
28
- const page = await context.newPage();
29
- try {
30
- const config = getServiceAuthConfig(service);
31
- await page.goto(config.url);
32
- const selectors = config.waitForSelectors ??
33
- (config.waitForSelector ? [config.waitForSelector] : []);
34
- const loginOutcome = await waitForLoginForService(page, selectors);
35
- const outcomeLabel = describeLoginOutcome(loginOutcome);
36
- if (loginOutcome === "aborted") {
37
- throw new Error("Authentication was canceled. Authentication was not saved.");
38
- }
39
- if (config.verifyUrl) {
40
- const ok = config.verifyFunction
41
- ? await config.verifyFunction(context, config.verifyUrl)
42
- : await verifySessionByFetching(context, config.verifyUrl);
43
- if (!ok) {
44
- throw new Error(`Unable to verify session via ${config.verifyUrl} ${outcomeLabel}. Authentication was not saved. Ensure login completed successfully and retry.`);
45
- }
46
- }
47
- else if (selectors.length > 0 && loginOutcome !== "selector") {
48
- // Without a verification URL, we only persist when a login selector confirms success.
49
- throw new Error(`Login was not confirmed ${outcomeLabel}. Authentication was not saved.`);
50
- }
51
- // Capture user agent for future headless contexts
52
- const userAgent = await page.evaluate(() => navigator.userAgent);
53
- const state = await context.storageState();
54
- await writeAtomicJson(storagePath, state, 0o600);
55
- return userAgent;
56
- }
57
- finally {
58
- await page.close();
59
- }
60
- }
61
- async function waitForLoginForService(page, selectors) {
62
- if (selectors.length > 0) {
63
- return waitForLogin(page, selectors);
64
- }
65
- // When no selectors are configured, skip waiting and rely on verification if available.
66
- return "skipped";
67
- }
@@ -1,4 +0,0 @@
1
- import { BrowserAuthManager } from "./browser-auth-manager.js";
2
- export declare function acquireAuthManager(): BrowserAuthManager;
3
- export declare function releaseAuthManager(): Promise<void>;
4
- export declare function installAuthManagerCleanup(): void;
@@ -1,87 +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
- const closingManager = manager;
25
- manager = undefined;
26
- try {
27
- await closingManager.close();
28
- }
29
- catch (error) {
30
- // Best-effort cleanup: log but don't propagate (prevents masking prior errors in finally blocks)
31
- console.warn("Failed to close browser auth manager:", error instanceof Error ? error.message : String(error));
32
- }
33
- }
34
- }
35
- async function forceClose() {
36
- if (closing)
37
- return;
38
- closing = true;
39
- references = 0;
40
- if (manager) {
41
- const closingManager = manager;
42
- manager = undefined;
43
- try {
44
- await closingManager.close();
45
- }
46
- catch (error) {
47
- // Best-effort cleanup: log but don't propagate during shutdown
48
- console.warn("Failed to close browser auth manager during shutdown:", error instanceof Error ? error.message : String(error));
49
- }
50
- }
51
- }
52
- export function installAuthManagerCleanup() {
53
- if (cleanupInstalled)
54
- return;
55
- cleanupInstalled = true;
56
- /* eslint-disable unicorn/no-process-exit */
57
- const safeExit = (code) => {
58
- if (exitInitiated)
59
- return;
60
- exitInitiated = true;
61
- process.exit(code);
62
- };
63
- /* eslint-enable unicorn/no-process-exit */
64
- process.on("SIGINT", () => {
65
- // Ensure browser is actually closed before exiting
66
- void forceClose().finally(() => {
67
- safeExit(130);
68
- });
69
- });
70
- process.on("SIGTERM", () => {
71
- // Ensure browser is actually closed before exiting
72
- void forceClose().finally(() => {
73
- safeExit(143);
74
- });
75
- });
76
- process.on("beforeExit", () => {
77
- // Best-effort cleanup on natural process exit; ensure we schedule a macrotask
78
- // so the event loop stays alive until the async close finishes.
79
- if (process.exitCode === undefined)
80
- process.exitCode = 0;
81
- setImmediate(() => {
82
- void forceClose().finally(() => {
83
- safeExit(Number(process.exitCode ?? 0));
84
- });
85
- });
86
- });
87
- }
@@ -1,2 +0,0 @@
1
- import type { BrowserContext } from "playwright";
2
- export declare function verifySessionByFetching(context: BrowserContext, url: string, maxAttempts?: number, delayMs?: number): Promise<boolean>;
@@ -1,27 +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) => {
21
- setTimeout(resolve, delayMs);
22
- });
23
- }
24
- }
25
- }
26
- return false;
27
- }
@@ -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,3 +0,0 @@
1
- export function resolvePromptCapability() {
2
- return process.stdin.isTTY && process.stdout.isTTY;
3
- }
@@ -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
- }