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
|
@@ -7,13 +7,8 @@
|
|
|
7
7
|
* - "auto": Try vault first if configured, fallback to local
|
|
8
8
|
*/
|
|
9
9
|
import { fetchVaultCredentials, getAgentAccessToken, isVaultConfigured, } from "axauth";
|
|
10
|
-
import { getServiceSourceConfig
|
|
11
|
-
|
|
12
|
-
const SERVICE_TO_AGENT = {
|
|
13
|
-
claude: "claude",
|
|
14
|
-
chatgpt: "codex", // ChatGPT and Codex both use OpenAI API credentials
|
|
15
|
-
gemini: "gemini",
|
|
16
|
-
};
|
|
10
|
+
import { getServiceSourceConfig } from "../config/credential-sources.js";
|
|
11
|
+
import { getCopilotTokenFromCustomGhPath } from "../utils/copilot-gh-token.js";
|
|
17
12
|
/**
|
|
18
13
|
* Extract access token from vault credentials.
|
|
19
14
|
*
|
|
@@ -84,12 +79,17 @@ async function fetchFromVault(agentId, credentialName) {
|
|
|
84
79
|
*/
|
|
85
80
|
async function fetchFromLocal(agentId) {
|
|
86
81
|
try {
|
|
87
|
-
|
|
82
|
+
const token = await getAgentAccessToken(agentId);
|
|
83
|
+
if (token)
|
|
84
|
+
return token;
|
|
88
85
|
}
|
|
89
86
|
catch (error) {
|
|
90
87
|
console.error(`[axusage] Local credential fetch error for ${agentId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
91
|
-
return undefined;
|
|
92
88
|
}
|
|
89
|
+
if (agentId === "copilot") {
|
|
90
|
+
return getCopilotTokenFromCustomGhPath();
|
|
91
|
+
}
|
|
92
|
+
return undefined;
|
|
93
93
|
}
|
|
94
94
|
/**
|
|
95
95
|
* Get access token for a service.
|
|
@@ -99,7 +99,7 @@ async function fetchFromLocal(agentId) {
|
|
|
99
99
|
* - "vault": Fetch from axvault server (requires credential name)
|
|
100
100
|
* - "auto": Try vault if configured and name provided, fallback to local
|
|
101
101
|
*
|
|
102
|
-
* @param service - Service ID (e.g., "claude", "
|
|
102
|
+
* @param service - Service ID (e.g., "claude", "codex", "gemini")
|
|
103
103
|
* @returns Access token string or undefined if not available
|
|
104
104
|
*
|
|
105
105
|
* @example
|
|
@@ -110,7 +110,7 @@ async function fetchFromLocal(agentId) {
|
|
|
110
110
|
*/
|
|
111
111
|
async function getServiceAccessToken(service) {
|
|
112
112
|
const config = getServiceSourceConfig(service);
|
|
113
|
-
const agentId =
|
|
113
|
+
const agentId = service;
|
|
114
114
|
switch (config.source) {
|
|
115
115
|
case "local": {
|
|
116
116
|
return fetchFromLocal(agentId);
|
|
@@ -4,8 +4,8 @@ import type { ServiceAdapter } from "../types/domain.js";
|
|
|
4
4
|
*/
|
|
5
5
|
export declare const SERVICE_ADAPTERS: {
|
|
6
6
|
readonly claude: ServiceAdapter;
|
|
7
|
-
readonly
|
|
8
|
-
readonly
|
|
7
|
+
readonly codex: ServiceAdapter;
|
|
8
|
+
readonly copilot: ServiceAdapter;
|
|
9
9
|
readonly gemini: ServiceAdapter;
|
|
10
10
|
};
|
|
11
11
|
/**
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { codexAdapter } from "../adapters/codex.js";
|
|
2
2
|
import { claudeAdapter } from "../adapters/claude.js";
|
|
3
3
|
import { geminiAdapter } from "../adapters/gemini.js";
|
|
4
|
-
import {
|
|
4
|
+
import { copilotAdapter } from "../adapters/copilot.js";
|
|
5
5
|
/**
|
|
6
6
|
* Registry of available service adapters
|
|
7
7
|
*/
|
|
8
8
|
export const SERVICE_ADAPTERS = {
|
|
9
9
|
claude: claudeAdapter,
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
codex: codexAdapter,
|
|
11
|
+
copilot: copilotAdapter,
|
|
12
12
|
gemini: geminiAdapter,
|
|
13
13
|
};
|
|
14
14
|
/**
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
import type { AgentCli } from "axauth";
|
|
1
2
|
/**
|
|
2
|
-
* Supported service names for usage tracking
|
|
3
|
+
* Supported service names for usage tracking.
|
|
4
|
+
*
|
|
5
|
+
* Derived from the canonical AGENT_CLIS list, excluding agents
|
|
6
|
+
* that axusage doesn't yet support (opencode).
|
|
3
7
|
*/
|
|
4
|
-
export type SupportedService =
|
|
8
|
+
export type SupportedService = Exclude<AgentCli, "opencode">;
|
|
5
9
|
export declare const SUPPORTED_SERVICES: SupportedService[];
|
|
6
10
|
export declare function validateService(service: string | undefined): SupportedService;
|
|
@@ -1,9 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
"chatgpt",
|
|
4
|
-
"github-copilot",
|
|
5
|
-
"gemini",
|
|
6
|
-
];
|
|
1
|
+
import { AGENT_CLIS } from "axauth";
|
|
2
|
+
export const SUPPORTED_SERVICES = AGENT_CLIS.filter((cli) => cli !== "opencode");
|
|
7
3
|
export function validateService(service) {
|
|
8
4
|
if (!service) {
|
|
9
5
|
throw new Error(`Service is required. Supported services: ${SUPPORTED_SERVICES.join(", ")}. ` +
|
|
@@ -2,14 +2,14 @@ import { z } from "zod";
|
|
|
2
2
|
/**
|
|
3
3
|
* ChatGPT API response schemas
|
|
4
4
|
*/
|
|
5
|
-
export declare const
|
|
5
|
+
export declare const CodexRateLimitWindow: z.ZodObject<{
|
|
6
6
|
used_percent: z.ZodNumber;
|
|
7
7
|
limit_window_seconds: z.ZodNumber;
|
|
8
8
|
reset_after_seconds: z.ZodNumber;
|
|
9
9
|
reset_at: z.ZodNumber;
|
|
10
10
|
}, z.core.$strip>;
|
|
11
|
-
export type
|
|
12
|
-
export declare const
|
|
11
|
+
export type CodexRateLimitWindow = z.infer<typeof CodexRateLimitWindow>;
|
|
12
|
+
export declare const CodexUsageResponse: z.ZodObject<{
|
|
13
13
|
plan_type: z.ZodString;
|
|
14
14
|
rate_limit: z.ZodObject<{
|
|
15
15
|
allowed: z.ZodBoolean;
|
|
@@ -29,4 +29,4 @@ export declare const ChatGPTUsageResponse: z.ZodObject<{
|
|
|
29
29
|
}, z.core.$strip>;
|
|
30
30
|
credits: z.ZodNullable<z.ZodUnknown>;
|
|
31
31
|
}, z.core.$strip>;
|
|
32
|
-
export type
|
|
32
|
+
export type CodexUsageResponse = z.infer<typeof CodexUsageResponse>;
|
|
@@ -2,20 +2,20 @@ import { z } from "zod";
|
|
|
2
2
|
/**
|
|
3
3
|
* ChatGPT API response schemas
|
|
4
4
|
*/
|
|
5
|
-
export const
|
|
5
|
+
export const CodexRateLimitWindow = z.object({
|
|
6
6
|
used_percent: z.number(),
|
|
7
7
|
limit_window_seconds: z.number(),
|
|
8
8
|
reset_after_seconds: z.number(),
|
|
9
9
|
reset_at: z.number(), // Unix timestamp
|
|
10
10
|
});
|
|
11
|
-
const
|
|
11
|
+
const CodexRateLimit = z.object({
|
|
12
12
|
allowed: z.boolean(),
|
|
13
13
|
limit_reached: z.boolean(),
|
|
14
|
-
primary_window:
|
|
15
|
-
secondary_window:
|
|
14
|
+
primary_window: CodexRateLimitWindow,
|
|
15
|
+
secondary_window: CodexRateLimitWindow,
|
|
16
16
|
});
|
|
17
|
-
export const
|
|
17
|
+
export const CodexUsageResponse = z.object({
|
|
18
18
|
plan_type: z.string(),
|
|
19
|
-
rate_limit:
|
|
19
|
+
rate_limit: CodexRateLimit,
|
|
20
20
|
credits: z.unknown().nullable(),
|
|
21
21
|
});
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const CopilotUsageResponse: z.ZodObject<{
|
|
3
|
+
quota_reset_date_utc: z.ZodString;
|
|
4
|
+
copilot_plan: z.ZodString;
|
|
5
|
+
quota_snapshots: z.ZodObject<{
|
|
6
|
+
premium_interactions: z.ZodObject<{
|
|
7
|
+
entitlement: z.ZodNumber;
|
|
8
|
+
remaining: z.ZodNumber;
|
|
9
|
+
percent_remaining: z.ZodNumber;
|
|
10
|
+
unlimited: z.ZodOptional<z.ZodBoolean>;
|
|
11
|
+
}, z.core.$strip>;
|
|
12
|
+
}, z.core.$strip>;
|
|
13
|
+
}, z.core.$strip>;
|
|
14
|
+
export type CopilotUsageResponse = z.infer<typeof CopilotUsageResponse>;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* GitHub Copilot internal API response schema
|
|
4
|
+
* Endpoint: GET https://api.github.com/copilot_internal/user
|
|
5
|
+
*/
|
|
6
|
+
const PremiumInteractionsSnapshot = z.object({
|
|
7
|
+
entitlement: z.number(),
|
|
8
|
+
remaining: z.number(),
|
|
9
|
+
percent_remaining: z.number(),
|
|
10
|
+
unlimited: z.boolean().optional(),
|
|
11
|
+
});
|
|
12
|
+
const QuotaSnapshots = z.object({
|
|
13
|
+
premium_interactions: PremiumInteractionsSnapshot,
|
|
14
|
+
});
|
|
15
|
+
export const CopilotUsageResponse = z.object({
|
|
16
|
+
quota_reset_date_utc: z
|
|
17
|
+
.string()
|
|
18
|
+
.refine((s) => !Number.isNaN(new Date(s).getTime()), "Invalid date"),
|
|
19
|
+
copilot_plan: z.string(),
|
|
20
|
+
quota_snapshots: QuotaSnapshots,
|
|
21
|
+
});
|
|
@@ -3,8 +3,7 @@ type CliDependency = {
|
|
|
3
3
|
readonly envVar: string;
|
|
4
4
|
readonly installHint: string;
|
|
5
5
|
};
|
|
6
|
-
|
|
7
|
-
type AuthCliService = (typeof AUTH_CLI_SERVICES)[number];
|
|
6
|
+
type AuthCliService = "claude" | "codex" | "copilot" | "gemini";
|
|
8
7
|
export declare function getAuthCliDependency(service: AuthCliService): CliDependency;
|
|
9
8
|
export declare function checkCliDependency(dep: CliDependency): {
|
|
10
9
|
ok: boolean;
|
|
@@ -21,5 +20,4 @@ export declare function ensureAuthCliDependency(service: AuthCliService): {
|
|
|
21
20
|
export declare function resolveAuthCliDependencyOrReport(service: AuthCliService, options?: {
|
|
22
21
|
readonly setExitCode?: boolean;
|
|
23
22
|
}): string | undefined;
|
|
24
|
-
export {
|
|
25
|
-
export type { AuthCliService };
|
|
23
|
+
export {};
|
|
@@ -11,13 +11,17 @@ const CLI_DEPENDENCIES = {
|
|
|
11
11
|
envVar: "AXUSAGE_CODEX_PATH",
|
|
12
12
|
installHint: "npm install -g @openai/codex",
|
|
13
13
|
},
|
|
14
|
+
gh: {
|
|
15
|
+
command: "gh",
|
|
16
|
+
envVar: "AXUSAGE_GH_PATH",
|
|
17
|
+
installHint: "https://cli.github.com/ or brew install gh",
|
|
18
|
+
},
|
|
14
19
|
gemini: {
|
|
15
20
|
command: "gemini",
|
|
16
21
|
envVar: "AXUSAGE_GEMINI_PATH",
|
|
17
22
|
installHint: "npm install -g @google/gemini-cli",
|
|
18
23
|
},
|
|
19
24
|
};
|
|
20
|
-
const AUTH_CLI_SERVICES = ["claude", "chatgpt", "gemini"];
|
|
21
25
|
function resolveCliDependencyTimeout() {
|
|
22
26
|
const raw = process.env.AXUSAGE_CLI_TIMEOUT_MS;
|
|
23
27
|
if (!raw)
|
|
@@ -28,8 +32,8 @@ function resolveCliDependencyTimeout() {
|
|
|
28
32
|
return Math.round(parsed);
|
|
29
33
|
}
|
|
30
34
|
export function getAuthCliDependency(service) {
|
|
31
|
-
if (service === "
|
|
32
|
-
return CLI_DEPENDENCIES.
|
|
35
|
+
if (service === "copilot")
|
|
36
|
+
return CLI_DEPENDENCIES.gh;
|
|
33
37
|
return CLI_DEPENDENCIES[service];
|
|
34
38
|
}
|
|
35
39
|
function resolveCliDependencyPath(dep) {
|
|
@@ -78,4 +82,3 @@ function reportMissingCliDependency(dependency, path) {
|
|
|
78
82
|
console.error(chalk.gray(` 2. Set ${dependency.envVar}=/path/to/${dependency.command}`));
|
|
79
83
|
console.error(chalk.gray("Try 'axusage --help' for requirements and overrides."));
|
|
80
84
|
}
|
|
81
|
-
export { AUTH_CLI_SERVICES };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getCopilotTokenFromCustomGhPath(): string | undefined;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
|
+
const GH_PATH_OVERRIDE_ENV = "AXUSAGE_GH_PATH";
|
|
3
|
+
const DEFAULT_TIMEOUT_MS = 5000;
|
|
4
|
+
function resolveCliTimeoutMs() {
|
|
5
|
+
const rawValue = process.env.AXUSAGE_CLI_TIMEOUT_MS;
|
|
6
|
+
if (!rawValue)
|
|
7
|
+
return DEFAULT_TIMEOUT_MS;
|
|
8
|
+
const parsed = Number(rawValue);
|
|
9
|
+
if (!Number.isFinite(parsed) || parsed <= 0)
|
|
10
|
+
return DEFAULT_TIMEOUT_MS;
|
|
11
|
+
return Math.round(parsed);
|
|
12
|
+
}
|
|
13
|
+
function resolveGhOverridePath() {
|
|
14
|
+
const value = process.env[GH_PATH_OVERRIDE_ENV]?.trim();
|
|
15
|
+
if (!value)
|
|
16
|
+
return undefined;
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
export function getCopilotTokenFromCustomGhPath() {
|
|
20
|
+
const path = resolveGhOverridePath();
|
|
21
|
+
if (!path)
|
|
22
|
+
return undefined;
|
|
23
|
+
try {
|
|
24
|
+
const token = execFileSync(path, ["auth", "token"], {
|
|
25
|
+
encoding: "utf8",
|
|
26
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
27
|
+
timeout: resolveCliTimeoutMs(),
|
|
28
|
+
}).trim();
|
|
29
|
+
// Copilot requires fine-grained tokens; reject classic PATs.
|
|
30
|
+
if (token && !token.startsWith("ghp_")) {
|
|
31
|
+
return token;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// Ignore command failures and fall back to other auth sources.
|
|
36
|
+
}
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
@@ -2,10 +2,7 @@ import type { UsageCommandOptions } from "../commands/fetch-service-usage.js";
|
|
|
2
2
|
export type RootOptions = {
|
|
3
3
|
readonly authSetup?: string;
|
|
4
4
|
readonly authStatus?: string | boolean;
|
|
5
|
-
readonly authClear?: string;
|
|
6
|
-
readonly force?: boolean;
|
|
7
5
|
readonly service?: UsageCommandOptions["service"];
|
|
8
6
|
readonly format?: UsageCommandOptions["format"];
|
|
9
|
-
readonly interactive?: UsageCommandOptions["interactive"];
|
|
10
7
|
};
|
|
11
8
|
export declare function getRootOptionsError(options: RootOptions, formatSource?: string): string | undefined;
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
export function getRootOptionsError(options, formatSource) {
|
|
2
2
|
// Commander sets optional args to `true` when provided without a value.
|
|
3
3
|
const authSelectionCount = Number(Boolean(options.authSetup)) +
|
|
4
|
-
Number(options.authStatus !== undefined)
|
|
5
|
-
Number(Boolean(options.authClear));
|
|
4
|
+
Number(options.authStatus !== undefined);
|
|
6
5
|
if (authSelectionCount > 1) {
|
|
7
|
-
return "Use only one of --auth-setup
|
|
8
|
-
}
|
|
9
|
-
if (options.force && !options.authClear) {
|
|
10
|
-
return "--force is only supported with --auth-clear.";
|
|
6
|
+
return "Use only one of --auth-setup or --auth-status.";
|
|
11
7
|
}
|
|
12
8
|
const hasExplicitFormat = formatSource === "cli";
|
|
13
9
|
const hasUsageOptions = Boolean(options.service) || hasExplicitFormat;
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "axusage",
|
|
3
3
|
"author": "Łukasz Jerciński",
|
|
4
4
|
"license": "MIT",
|
|
5
|
-
"version": "3.
|
|
5
|
+
"version": "3.2.0",
|
|
6
6
|
"description": "Monitor API usage across Claude, ChatGPT, GitHub Copilot, and Gemini from a single CLI",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -19,7 +19,6 @@
|
|
|
19
19
|
"LICENSE"
|
|
20
20
|
],
|
|
21
21
|
"scripts": {
|
|
22
|
-
"postinstall": "playwright install chromium",
|
|
23
22
|
"prepare": "git config core.hooksPath .githooks",
|
|
24
23
|
"prepublishOnly": "pnpm run rebuild",
|
|
25
24
|
"build": "tsc -p tsconfig.app.json",
|
|
@@ -48,35 +47,32 @@
|
|
|
48
47
|
"llm",
|
|
49
48
|
"monitoring"
|
|
50
49
|
],
|
|
51
|
-
"packageManager": "pnpm@10.
|
|
50
|
+
"packageManager": "pnpm@10.30.1",
|
|
52
51
|
"engines": {
|
|
53
52
|
"node": ">=22.14.0"
|
|
54
53
|
},
|
|
55
54
|
"dependencies": {
|
|
56
55
|
"@commander-js/extra-typings": "^14.0.0",
|
|
57
|
-
"
|
|
58
|
-
"axauth": "^1.11.2",
|
|
56
|
+
"axauth": "^3.1.6",
|
|
59
57
|
"chalk": "^5.6.2",
|
|
60
|
-
"commander": "^14.0.
|
|
61
|
-
"conf": "^15.0
|
|
62
|
-
"env-paths": "^
|
|
63
|
-
"playwright": "^1.57.0",
|
|
58
|
+
"commander": "^14.0.3",
|
|
59
|
+
"conf": "^15.1.0",
|
|
60
|
+
"env-paths": "^4.0.0",
|
|
64
61
|
"prom-client": "^15.1.3",
|
|
65
|
-
"
|
|
66
|
-
"zod": "^4.3.5"
|
|
62
|
+
"zod": "^4.3.6"
|
|
67
63
|
},
|
|
68
64
|
"devDependencies": {
|
|
69
65
|
"@total-typescript/ts-reset": "^0.6.1",
|
|
70
|
-
"@types/node": "^25.0
|
|
71
|
-
"@vitest/coverage-v8": "^4.0.
|
|
72
|
-
"eslint": "^
|
|
73
|
-
"eslint-config-axkit": "^1.
|
|
66
|
+
"@types/node": "^25.3.0",
|
|
67
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
68
|
+
"eslint": "^10.0.1",
|
|
69
|
+
"eslint-config-axkit": "^1.2.1",
|
|
74
70
|
"fta-check": "^1.5.1",
|
|
75
71
|
"fta-cli": "^3.0.0",
|
|
76
|
-
"knip": "^5.
|
|
77
|
-
"prettier": "3.
|
|
78
|
-
"semantic-release": "^25.0.
|
|
72
|
+
"knip": "^5.85.0",
|
|
73
|
+
"prettier": "3.8.1",
|
|
74
|
+
"semantic-release": "^25.0.3",
|
|
79
75
|
"typescript": "^5.9.3",
|
|
80
|
-
"vitest": "^4.0.
|
|
76
|
+
"vitest": "^4.0.18"
|
|
81
77
|
}
|
|
82
78
|
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { ApiError } from "../types/domain.js";
|
|
2
|
-
import { GitHubCopilotUsageResponse as GitHubCopilotUsageResponseSchema } from "../types/github-copilot.js";
|
|
3
|
-
import { toServiceUsageData } from "./parse-github-copilot-usage.js";
|
|
4
|
-
import { acquireAuthManager, releaseAuthManager, } from "../services/shared-browser-auth-manager.js";
|
|
5
|
-
// Copilot web fetches entitlements from this endpoint (requires GitHub session cookies)
|
|
6
|
-
const API_URL = "https://github.com/github-copilot/chat/entitlement";
|
|
7
|
-
/** Functional core is extracted to ./parse-github-copilot-usage.ts */
|
|
8
|
-
/**
|
|
9
|
-
* GitHub Copilot service adapter
|
|
10
|
-
*/
|
|
11
|
-
export const githubCopilotAdapter = {
|
|
12
|
-
name: "GitHub Copilot",
|
|
13
|
-
async fetchUsage() {
|
|
14
|
-
const manager = acquireAuthManager();
|
|
15
|
-
try {
|
|
16
|
-
if (!manager.hasAuth("github-copilot")) {
|
|
17
|
-
return {
|
|
18
|
-
ok: false,
|
|
19
|
-
error: new ApiError("No saved authentication for github-copilot. " +
|
|
20
|
-
"Run 'axusage --auth-setup github-copilot --interactive' first."),
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
const body = await manager.makeAuthenticatedRequest("github-copilot", API_URL);
|
|
24
|
-
const data = JSON.parse(body);
|
|
25
|
-
const parseResult = GitHubCopilotUsageResponseSchema.safeParse(data);
|
|
26
|
-
if (!parseResult.success) {
|
|
27
|
-
return {
|
|
28
|
-
ok: false,
|
|
29
|
-
error: new ApiError(`Invalid response format: ${parseResult.error.message}`, undefined, data),
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
try {
|
|
33
|
-
return {
|
|
34
|
-
ok: true,
|
|
35
|
-
value: toServiceUsageData(parseResult.data),
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
catch (error) {
|
|
39
|
-
return {
|
|
40
|
-
ok: false,
|
|
41
|
-
error: new ApiError(error instanceof Error
|
|
42
|
-
? error.message
|
|
43
|
-
: "Unable to parse GitHub Copilot reset date", undefined, parseResult.data.quotas.resetDate),
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
catch (error) {
|
|
48
|
-
return {
|
|
49
|
-
ok: false,
|
|
50
|
-
error: new ApiError(`Browser authentication failed: ${error instanceof Error ? error.message : String(error)}`),
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
finally {
|
|
54
|
-
await releaseAuthManager();
|
|
55
|
-
}
|
|
56
|
-
},
|
|
57
|
-
};
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import type { ServiceUsageData } from "../types/domain.js";
|
|
2
|
-
import type { GitHubCopilotUsageResponse } from "../types/github-copilot.js";
|
|
3
|
-
/**
|
|
4
|
-
* Parses GitHub reset date (YYYY-MM-DD) into a UTC Date
|
|
5
|
-
*/
|
|
6
|
-
export declare function parseResetDate(resetDateString: string): Date;
|
|
7
|
-
/**
|
|
8
|
-
* Calculates monthly period duration ending at the reset date
|
|
9
|
-
*
|
|
10
|
-
* Determines the period start by going back one month from the reset
|
|
11
|
-
* date and clamping the day to the last valid day of that previous
|
|
12
|
-
* month. This handles edge cases like Jan 31 → Feb 28/29 where the
|
|
13
|
-
* target month has fewer days than the reset date's day component.
|
|
14
|
-
*
|
|
15
|
-
* Example:
|
|
16
|
-
* For resetDate = 2025-03-31, calculates duration from 2025-02-28 (clamped)
|
|
17
|
-
* to 2025-03-31.
|
|
18
|
-
*/
|
|
19
|
-
export declare function calculatePeriodDuration(resetDate: Date): number;
|
|
20
|
-
/**
|
|
21
|
-
* Converts GitHub Copilot response to common domain model
|
|
22
|
-
*/
|
|
23
|
-
export declare function toServiceUsageData(response: GitHubCopilotUsageResponse): ServiceUsageData;
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
const MS_PER_DAY = 24 * 60 * 60 * 1000;
|
|
2
|
-
/**
|
|
3
|
-
* Parses GitHub reset date (YYYY-MM-DD) into a UTC Date
|
|
4
|
-
*/
|
|
5
|
-
export function parseResetDate(resetDateString) {
|
|
6
|
-
const parts = resetDateString.split("-");
|
|
7
|
-
if (parts.length !== 3) {
|
|
8
|
-
throw new Error(`Invalid reset date format: ${resetDateString}`);
|
|
9
|
-
}
|
|
10
|
-
const [yearString, monthString, dayString] = parts;
|
|
11
|
-
if (!yearString || !monthString || !dayString) {
|
|
12
|
-
throw new Error(`Invalid reset date components: ${resetDateString}`);
|
|
13
|
-
}
|
|
14
|
-
const year = Number(yearString);
|
|
15
|
-
const month = Number(monthString);
|
|
16
|
-
const day = Number(dayString);
|
|
17
|
-
if (Number.isNaN(year) ||
|
|
18
|
-
Number.isNaN(month) ||
|
|
19
|
-
Number.isNaN(day) ||
|
|
20
|
-
month < 1 ||
|
|
21
|
-
month > 12 ||
|
|
22
|
-
day < 1 ||
|
|
23
|
-
day > 31) {
|
|
24
|
-
throw new Error(`Invalid reset date components: ${resetDateString}`);
|
|
25
|
-
}
|
|
26
|
-
return new Date(Date.UTC(year, month - 1, day, 0, 0, 0));
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Calculates monthly period duration ending at the reset date
|
|
30
|
-
*
|
|
31
|
-
* Determines the period start by going back one month from the reset
|
|
32
|
-
* date and clamping the day to the last valid day of that previous
|
|
33
|
-
* month. This handles edge cases like Jan 31 → Feb 28/29 where the
|
|
34
|
-
* target month has fewer days than the reset date's day component.
|
|
35
|
-
*
|
|
36
|
-
* Example:
|
|
37
|
-
* For resetDate = 2025-03-31, calculates duration from 2025-02-28 (clamped)
|
|
38
|
-
* to 2025-03-31.
|
|
39
|
-
*/
|
|
40
|
-
export function calculatePeriodDuration(resetDate) {
|
|
41
|
-
const periodEnd = resetDate.getTime();
|
|
42
|
-
// Determine previous month and clamp day to its last day
|
|
43
|
-
const year = resetDate.getUTCFullYear();
|
|
44
|
-
const month = resetDate.getUTCMonth(); // 0-based
|
|
45
|
-
const day = resetDate.getUTCDate();
|
|
46
|
-
// First day of current month in UTC
|
|
47
|
-
const firstOfCurrentMonth = Date.UTC(year, month, 1, 0, 0, 0);
|
|
48
|
-
// Last day of previous month: subtract 1 day from first of current month
|
|
49
|
-
const lastPreviousMonthDate = new Date(firstOfCurrentMonth - MS_PER_DAY);
|
|
50
|
-
const lastPreviousMonthDay = lastPreviousMonthDate.getUTCDate();
|
|
51
|
-
const previousMonth = lastPreviousMonthDate.getUTCMonth();
|
|
52
|
-
const previousYear = lastPreviousMonthDate.getUTCFullYear();
|
|
53
|
-
const targetDay = Math.min(day, lastPreviousMonthDay);
|
|
54
|
-
const periodStart = Date.UTC(previousYear, previousMonth, targetDay, 0, 0, 0);
|
|
55
|
-
return Math.max(periodEnd - periodStart, 0);
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Converts GitHub Copilot response to common domain model
|
|
59
|
-
*/
|
|
60
|
-
export function toServiceUsageData(response) {
|
|
61
|
-
const resetDate = parseResetDate(response.quotas.resetDate);
|
|
62
|
-
const periodDurationMs = calculatePeriodDuration(resetDate);
|
|
63
|
-
const used = response.quotas.limits.premiumInteractions -
|
|
64
|
-
response.quotas.remaining.premiumInteractions;
|
|
65
|
-
const utilization = (used / response.quotas.limits.premiumInteractions) * 100;
|
|
66
|
-
return {
|
|
67
|
-
service: "GitHub Copilot",
|
|
68
|
-
planType: response.plan,
|
|
69
|
-
windows: [
|
|
70
|
-
{
|
|
71
|
-
name: "Monthly Premium Interactions",
|
|
72
|
-
utilization: Math.round(utilization * 100) / 100,
|
|
73
|
-
resetsAt: resetDate,
|
|
74
|
-
periodDurationMs,
|
|
75
|
-
},
|
|
76
|
-
],
|
|
77
|
-
};
|
|
78
|
-
}
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { confirm } from "@inquirer/prompts";
|
|
2
|
-
import { existsSync, readdirSync } from "node:fs";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import trash from "trash";
|
|
5
|
-
import { validateService } from "../services/supported-service.js";
|
|
6
|
-
import { getAuthMetaPathFor, getStorageStatePathFor, } from "../services/auth-storage-path.js";
|
|
7
|
-
import { getBrowserContextsDirectory } from "../services/app-paths.js";
|
|
8
|
-
import { chalk } from "../utils/color.js";
|
|
9
|
-
import { resolvePromptCapability } from "../utils/resolve-prompt-capability.js";
|
|
10
|
-
function isPromptCancellation(error) {
|
|
11
|
-
return (error instanceof Error &&
|
|
12
|
-
(error.name === "AbortPromptError" ||
|
|
13
|
-
error.name === "CancelPromptError" ||
|
|
14
|
-
error.name === "ExitPromptError"));
|
|
15
|
-
}
|
|
16
|
-
function collectRelatedArtifacts(filePath) {
|
|
17
|
-
const directory = path.dirname(filePath);
|
|
18
|
-
const baseName = path.basename(filePath);
|
|
19
|
-
try {
|
|
20
|
-
return readdirSync(directory)
|
|
21
|
-
.filter((entry) => entry.startsWith(`${baseName}.`) &&
|
|
22
|
-
(entry.endsWith(".bak") || entry.endsWith(".tmp")))
|
|
23
|
-
.map((entry) => path.join(directory, entry));
|
|
24
|
-
}
|
|
25
|
-
catch {
|
|
26
|
-
return [];
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
export async function authClearCommand(options) {
|
|
30
|
-
const service = validateService(options.service);
|
|
31
|
-
const dataDirectory = getBrowserContextsDirectory();
|
|
32
|
-
const storage = getStorageStatePathFor(dataDirectory, service);
|
|
33
|
-
const meta = getAuthMetaPathFor(dataDirectory, service);
|
|
34
|
-
try {
|
|
35
|
-
const artifactTargets = [storage, meta].filter((p) => existsSync(p));
|
|
36
|
-
const backupTargets = [storage, meta].flatMap((filePath) => collectRelatedArtifacts(filePath));
|
|
37
|
-
const targets = [...new Set([...artifactTargets, ...backupTargets])];
|
|
38
|
-
if (targets.length === 0) {
|
|
39
|
-
console.error(chalk.gray(`\nNo saved authentication found for ${service}.`));
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
if (!options.force) {
|
|
43
|
-
if (!options.interactive) {
|
|
44
|
-
console.error(chalk.red("Error: Clearing saved authentication requires confirmation."));
|
|
45
|
-
console.error(chalk.gray("Re-run with --interactive to confirm, or use --force to skip confirmation."));
|
|
46
|
-
console.error(chalk.gray("Try 'axusage --help' for details."));
|
|
47
|
-
process.exitCode = 1;
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
if (!resolvePromptCapability()) {
|
|
51
|
-
console.error(chalk.red("Error: --interactive requires a TTY-enabled terminal."));
|
|
52
|
-
console.error(chalk.gray("Re-run in a terminal or pass --force instead."));
|
|
53
|
-
console.error(chalk.gray("Try 'axusage --help' for details."));
|
|
54
|
-
process.exitCode = 1;
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
let confirmed = false;
|
|
58
|
-
try {
|
|
59
|
-
confirmed = await confirm({
|
|
60
|
-
message: `Remove saved authentication for ${service}?`,
|
|
61
|
-
default: false,
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
catch (error) {
|
|
65
|
-
if (isPromptCancellation(error)) {
|
|
66
|
-
console.error(chalk.gray("Aborted."));
|
|
67
|
-
process.exitCode = 1;
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
throw error;
|
|
71
|
-
}
|
|
72
|
-
if (!confirmed) {
|
|
73
|
-
console.error(chalk.gray("Aborted."));
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
await trash(targets, { glob: false });
|
|
78
|
-
console.error(chalk.green(`\n✓ Cleared authentication for ${service}`));
|
|
79
|
-
}
|
|
80
|
-
catch (error) {
|
|
81
|
-
console.error(chalk.red(`\n✗ Failed to clear authentication for ${service}: ${error instanceof Error ? error.message : String(error)}`));
|
|
82
|
-
process.exitCode = 1;
|
|
83
|
-
}
|
|
84
|
-
}
|