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.
- 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 +9 -40
- 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 +3 -11
- package/dist/config/credential-sources.js +1 -1
- 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/service-diagnostics.d.ts +11 -0
- package/dist/services/service-diagnostics.js +29 -0
- 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/format-requires-help-text.d.ts +17 -0
- package/dist/utils/format-requires-help-text.js +62 -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 -87
- package/dist/services/verify-session.d.ts +0 -2
- package/dist/services/verify-session.js +0 -27
- 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,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
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { ServiceResult } from "../types/domain.js";
|
|
2
|
-
/**
|
|
3
|
-
* Fetch usage for a service, with automatic re-authentication on auth errors.
|
|
4
|
-
* Prompts the user to re-authenticate if the initial fetch fails with an auth error,
|
|
5
|
-
* then retries the fetch. Returns the original result if re-authentication fails.
|
|
6
|
-
*/
|
|
7
|
-
export declare function fetchServiceUsageWithAutoReauth(serviceName: string, interactive: boolean): Promise<ServiceResult>;
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { fetchServiceUsage } from "./fetch-service-usage.js";
|
|
2
|
-
import { isAuthFailure, runAuthSetup } from "./run-auth-setup.js";
|
|
3
|
-
import { validateService } from "../services/supported-service.js";
|
|
4
|
-
import { chalk } from "../utils/color.js";
|
|
5
|
-
/**
|
|
6
|
-
* Fetch usage for a service, with automatic re-authentication on auth errors.
|
|
7
|
-
* Prompts the user to re-authenticate if the initial fetch fails with an auth error,
|
|
8
|
-
* then retries the fetch. Returns the original result if re-authentication fails.
|
|
9
|
-
*/
|
|
10
|
-
export async function fetchServiceUsageWithAutoReauth(serviceName, interactive) {
|
|
11
|
-
const result = await fetchServiceUsage(serviceName);
|
|
12
|
-
if (!interactive) {
|
|
13
|
-
return { service: serviceName, result };
|
|
14
|
-
}
|
|
15
|
-
// If auth error, try to re-authenticate and retry
|
|
16
|
-
if (isAuthFailure(result)) {
|
|
17
|
-
console.error(chalk.yellow(`⚠ Authentication failed for ${serviceName}. Attempting to re-authenticate...`));
|
|
18
|
-
try {
|
|
19
|
-
const service = validateService(serviceName);
|
|
20
|
-
const authSuccess = await runAuthSetup(service);
|
|
21
|
-
if (authSuccess) {
|
|
22
|
-
if (process.stderr.isTTY) {
|
|
23
|
-
console.error(chalk.blue(`Retrying ${serviceName} usage fetch...\n`));
|
|
24
|
-
}
|
|
25
|
-
const retryResult = await fetchServiceUsage(serviceName);
|
|
26
|
-
return { service: serviceName, result: retryResult };
|
|
27
|
-
}
|
|
28
|
-
else {
|
|
29
|
-
console.error(chalk.red(`Re-authentication failed for ${serviceName}.`));
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
catch (error) {
|
|
33
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
34
|
-
// Distinguish validation errors from auth setup errors
|
|
35
|
-
const isValidationError = errorMessage.includes("Unsupported service") ||
|
|
36
|
-
errorMessage.includes("Service is required");
|
|
37
|
-
const prefix = isValidationError
|
|
38
|
-
? "Invalid service"
|
|
39
|
-
: "Failed to re-authenticate";
|
|
40
|
-
console.error(chalk.red(`${prefix}: ${errorMessage}`));
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
// Return original result if re-authentication failed or was not attempted
|
|
44
|
-
return { service: serviceName, result };
|
|
45
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Directory for storing browser authentication contexts
|
|
3
|
-
*/
|
|
4
|
-
export declare function getBrowserContextsDirectory(): string;
|
|
5
|
-
/**
|
|
6
|
-
* Ensure a directory exists with restricted permissions (owner-only access).
|
|
7
|
-
* Creates the directory recursively if needed and sets mode 0o700.
|
|
8
|
-
*/
|
|
9
|
-
export declare function ensureSecureDirectory(directoryPath: string): Promise<void>;
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import envPaths from "env-paths";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { mkdir, chmod } from "node:fs/promises";
|
|
4
|
-
/**
|
|
5
|
-
* env-paths resolves directories during module initialization, so changes to
|
|
6
|
-
* environment variables (like XDG_DATA_HOME) after the first import will not
|
|
7
|
-
* be picked up without restarting the process.
|
|
8
|
-
*/
|
|
9
|
-
const paths = envPaths("axusage", { suffix: "" });
|
|
10
|
-
/**
|
|
11
|
-
* Directory for storing browser authentication contexts
|
|
12
|
-
*/
|
|
13
|
-
export function getBrowserContextsDirectory() {
|
|
14
|
-
return path.join(paths.data, "browser-contexts");
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Ensure a directory exists with restricted permissions (owner-only access).
|
|
18
|
-
* Creates the directory recursively if needed and sets mode 0o700.
|
|
19
|
-
*/
|
|
20
|
-
export async function ensureSecureDirectory(directoryPath) {
|
|
21
|
-
try {
|
|
22
|
-
await mkdir(directoryPath, { recursive: true, mode: 0o700 });
|
|
23
|
-
}
|
|
24
|
-
catch (error) {
|
|
25
|
-
// Only ignore EEXIST; re-throw everything else
|
|
26
|
-
// mkdir may ignore mode due to umask; we'll enforce via chmod below
|
|
27
|
-
const isEexist = error instanceof Error && "code" in error && error.code === "EEXIST";
|
|
28
|
-
if (!isEexist) {
|
|
29
|
-
throw error;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
try {
|
|
33
|
-
await chmod(directoryPath, 0o700);
|
|
34
|
-
}
|
|
35
|
-
catch {
|
|
36
|
-
// Best effort: some filesystems (network mounts, containers) may not
|
|
37
|
-
// support chmod or have different permission models
|
|
38
|
-
}
|
|
39
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import path from "node:path";
|
|
2
|
-
export function getStorageStatePathFor(dataDirectory, service) {
|
|
3
|
-
return path.join(dataDirectory, `${service}-auth.json`);
|
|
4
|
-
}
|
|
5
|
-
export function getAuthMetaPathFor(dataDirectory, service) {
|
|
6
|
-
return path.join(dataDirectory, `${service}-auth.meta.json`);
|
|
7
|
-
}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
import type { BrowserContext } from "playwright";
|
|
2
|
-
import type { SupportedService } from "./supported-service.js";
|
|
3
|
-
/**
|
|
4
|
-
* Configuration for browser authentication manager
|
|
5
|
-
*/
|
|
6
|
-
type BrowserAuthConfig = {
|
|
7
|
-
readonly dataDir?: string;
|
|
8
|
-
readonly headless?: boolean;
|
|
9
|
-
};
|
|
10
|
-
/**
|
|
11
|
-
* Manages browser contexts for persistent authentication across services
|
|
12
|
-
*/
|
|
13
|
-
export declare class BrowserAuthManager {
|
|
14
|
-
private readonly dataDir;
|
|
15
|
-
private readonly headless;
|
|
16
|
-
private browser;
|
|
17
|
-
private browserPromise;
|
|
18
|
-
constructor(config?: BrowserAuthConfig);
|
|
19
|
-
/**
|
|
20
|
-
* Get the storage state file path for a service
|
|
21
|
-
*/
|
|
22
|
-
private getStorageStatePath;
|
|
23
|
-
/**
|
|
24
|
-
* Check if a service has saved authentication
|
|
25
|
-
*/
|
|
26
|
-
hasAuth(service: SupportedService): boolean;
|
|
27
|
-
/**
|
|
28
|
-
* Ensure a Chromium browser instance is available
|
|
29
|
-
*/
|
|
30
|
-
private ensureBrowser;
|
|
31
|
-
private launchAndStoreBrowser;
|
|
32
|
-
/**
|
|
33
|
-
* Set up authentication for a service by launching a browser for the user to log in
|
|
34
|
-
*/
|
|
35
|
-
setupAuth(service: SupportedService): Promise<void>;
|
|
36
|
-
/**
|
|
37
|
-
* Get a browser context with saved authentication for a service
|
|
38
|
-
*/
|
|
39
|
-
getAuthContext(service: SupportedService): Promise<BrowserContext>;
|
|
40
|
-
/**
|
|
41
|
-
* Make an authenticated request to a URL using the browser context
|
|
42
|
-
*/
|
|
43
|
-
makeAuthenticatedRequest(service: SupportedService, url: string): Promise<string>;
|
|
44
|
-
/**
|
|
45
|
-
* Close the browser and clean up resources
|
|
46
|
-
*/
|
|
47
|
-
close(): Promise<void>;
|
|
48
|
-
}
|
|
49
|
-
export {};
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
import { getServiceAuthConfig } from "./service-auth-configs.js";
|
|
3
|
-
import { launchChromium } from "./launch-chromium.js";
|
|
4
|
-
import { requestService } from "./request-service.js";
|
|
5
|
-
import { doSetupAuth } from "./do-setup-auth.js";
|
|
6
|
-
import { getStorageStatePathFor } from "./auth-storage-path.js";
|
|
7
|
-
import { createAuthContext, loadStoredUserAgent, } from "./create-auth-context.js";
|
|
8
|
-
import { getBrowserContextsDirectory, ensureSecureDirectory, } from "./app-paths.js";
|
|
9
|
-
import { persistStorageState } from "./persist-storage-state.js";
|
|
10
|
-
/**
|
|
11
|
-
* Manages browser contexts for persistent authentication across services
|
|
12
|
-
*/
|
|
13
|
-
export class BrowserAuthManager {
|
|
14
|
-
dataDir;
|
|
15
|
-
headless;
|
|
16
|
-
browser = undefined;
|
|
17
|
-
browserPromise = undefined;
|
|
18
|
-
constructor(config = {}) {
|
|
19
|
-
this.dataDir = config.dataDir || getBrowserContextsDirectory();
|
|
20
|
-
// Default to headless for non-interactive usage flows; auth setup passes headless: false
|
|
21
|
-
this.headless = config.headless ?? true;
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Get the storage state file path for a service
|
|
25
|
-
*/
|
|
26
|
-
getStorageStatePath(service) {
|
|
27
|
-
return getStorageStatePathFor(this.dataDir, service);
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Check if a service has saved authentication
|
|
31
|
-
*/
|
|
32
|
-
hasAuth(service) {
|
|
33
|
-
return existsSync(this.getStorageStatePath(service));
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Ensure a Chromium browser instance is available
|
|
37
|
-
*/
|
|
38
|
-
async ensureBrowser() {
|
|
39
|
-
if (!this.browserPromise) {
|
|
40
|
-
this.browserPromise = this.launchAndStoreBrowser();
|
|
41
|
-
}
|
|
42
|
-
return this.browserPromise;
|
|
43
|
-
}
|
|
44
|
-
async launchAndStoreBrowser() {
|
|
45
|
-
try {
|
|
46
|
-
const browser = await launchChromium(this.headless);
|
|
47
|
-
this.browser = browser;
|
|
48
|
-
return browser;
|
|
49
|
-
}
|
|
50
|
-
catch (error) {
|
|
51
|
-
// Allow retries on subsequent calls if the launch fails
|
|
52
|
-
this.browserPromise = undefined;
|
|
53
|
-
throw error;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Set up authentication for a service by launching a browser for the user to log in
|
|
58
|
-
*/
|
|
59
|
-
async setupAuth(service) {
|
|
60
|
-
const config = getServiceAuthConfig(service);
|
|
61
|
-
await ensureSecureDirectory(this.dataDir);
|
|
62
|
-
const browser = await this.ensureBrowser();
|
|
63
|
-
const storagePath = this.getStorageStatePath(service);
|
|
64
|
-
// Load existing storage state if available - this gives the browser a chance
|
|
65
|
-
// to refresh expired cookies/tokens during the login flow
|
|
66
|
-
const storageState = existsSync(storagePath) ? storagePath : undefined;
|
|
67
|
-
const userAgent = await loadStoredUserAgent(this.dataDir, service);
|
|
68
|
-
let context;
|
|
69
|
-
try {
|
|
70
|
-
context = await browser.newContext({ storageState, userAgent });
|
|
71
|
-
}
|
|
72
|
-
catch {
|
|
73
|
-
// Corrupted storage state - fall back to fresh context
|
|
74
|
-
context = await browser.newContext({ userAgent });
|
|
75
|
-
}
|
|
76
|
-
try {
|
|
77
|
-
await doSetupAuth(service, context, this.getStorageStatePath(service), config.instructions);
|
|
78
|
-
}
|
|
79
|
-
finally {
|
|
80
|
-
await context.close();
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
/**
|
|
84
|
-
* Get a browser context with saved authentication for a service
|
|
85
|
-
*/
|
|
86
|
-
async getAuthContext(service) {
|
|
87
|
-
const browser = await this.ensureBrowser();
|
|
88
|
-
return createAuthContext(browser, this.dataDir, service);
|
|
89
|
-
}
|
|
90
|
-
/**
|
|
91
|
-
* Make an authenticated request to a URL using the browser context
|
|
92
|
-
*/
|
|
93
|
-
async makeAuthenticatedRequest(service, url) {
|
|
94
|
-
const context = await this.getAuthContext(service);
|
|
95
|
-
try {
|
|
96
|
-
return await requestService(service, url, () => Promise.resolve(context));
|
|
97
|
-
}
|
|
98
|
-
finally {
|
|
99
|
-
await persistStorageState(context, this.getStorageStatePath(service));
|
|
100
|
-
await context.close();
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
/**
|
|
104
|
-
* Close the browser and clean up resources
|
|
105
|
-
*/
|
|
106
|
-
async close() {
|
|
107
|
-
if (this.browser) {
|
|
108
|
-
await this.browser.close();
|
|
109
|
-
this.browser = undefined;
|
|
110
|
-
this.browserPromise = undefined;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { Browser, BrowserContext } from "playwright";
|
|
2
|
-
import type { SupportedService } from "./supported-service.js";
|
|
3
|
-
/**
|
|
4
|
-
* Load the stored userAgent from the auth meta file for a service.
|
|
5
|
-
* Returns undefined if meta file doesn't exist or is invalid.
|
|
6
|
-
*/
|
|
7
|
-
export declare function loadStoredUserAgent(dataDirectory: string, service: SupportedService): Promise<string | undefined>;
|
|
8
|
-
export declare function createAuthContext(browser: Browser, dataDirectory: string, service: SupportedService): Promise<BrowserContext>;
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
import { readFile } from "node:fs/promises";
|
|
3
|
-
import { getAuthMetaPathFor, getStorageStatePathFor, } from "./auth-storage-path.js";
|
|
4
|
-
/**
|
|
5
|
-
* Load the stored userAgent from the auth meta file for a service.
|
|
6
|
-
* Returns undefined if meta file doesn't exist or is invalid.
|
|
7
|
-
*/
|
|
8
|
-
export async function loadStoredUserAgent(dataDirectory, service) {
|
|
9
|
-
try {
|
|
10
|
-
const metaPath = getAuthMetaPathFor(dataDirectory, service);
|
|
11
|
-
const metaRaw = await readFile(metaPath, "utf8");
|
|
12
|
-
const meta = JSON.parse(metaRaw);
|
|
13
|
-
// Validate the parsed structure at runtime
|
|
14
|
-
if (meta &&
|
|
15
|
-
typeof meta === "object" &&
|
|
16
|
-
"userAgent" in meta &&
|
|
17
|
-
typeof meta.userAgent === "string") {
|
|
18
|
-
return meta.userAgent;
|
|
19
|
-
}
|
|
20
|
-
return undefined;
|
|
21
|
-
}
|
|
22
|
-
catch {
|
|
23
|
-
// Meta file missing, unreadable, or contains invalid JSON
|
|
24
|
-
return undefined;
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
export async function createAuthContext(browser, dataDirectory, service) {
|
|
28
|
-
const storageStatePath = getStorageStatePathFor(dataDirectory, service);
|
|
29
|
-
if (!existsSync(storageStatePath)) {
|
|
30
|
-
throw new Error(`No saved authentication for ${service}. ` +
|
|
31
|
-
`Run 'axusage --auth-setup ${service} --interactive' first.`);
|
|
32
|
-
}
|
|
33
|
-
const userAgent = await loadStoredUserAgent(dataDirectory, service);
|
|
34
|
-
return browser.newContext({ storageState: storageStatePath, userAgent });
|
|
35
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { setupAuthInContext } from "./setup-auth-flow.js";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { getAuthMetaPathFor } from "./auth-storage-path.js";
|
|
4
|
-
import { writeAtomicJson } from "../utils/write-atomic-json.js";
|
|
5
|
-
export async function doSetupAuth(service, context, storagePath, instructions) {
|
|
6
|
-
console.error(`\n${instructions}`);
|
|
7
|
-
console.error("Waiting for login to complete (or press Enter to continue)\n");
|
|
8
|
-
const userAgent = await setupAuthInContext(service, context, storagePath);
|
|
9
|
-
try {
|
|
10
|
-
if (userAgent) {
|
|
11
|
-
const metaPath = getAuthMetaPathFor(path.dirname(storagePath), service);
|
|
12
|
-
await writeAtomicJson(metaPath, { userAgent }, 0o600);
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
catch {
|
|
16
|
-
// ignore errors when writing meta; not critical
|
|
17
|
-
}
|
|
18
|
-
console.error(`\n✓ Authentication saved for ${service}. You can now close the browser.`);
|
|
19
|
-
}
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Fetch JSON using the page's fetch API so cookies/session are included without navigation.
|
|
3
|
-
*/
|
|
4
|
-
export async function fetchJsonWithContext(context, url) {
|
|
5
|
-
const page = await context.newPage();
|
|
6
|
-
try {
|
|
7
|
-
const origin = new URL(url).origin;
|
|
8
|
-
await page.goto(origin + "/", { waitUntil: "domcontentloaded" });
|
|
9
|
-
const result = await page.evaluate(async (targetUrl) => {
|
|
10
|
-
const response = await fetch(targetUrl, {
|
|
11
|
-
credentials: "include",
|
|
12
|
-
headers: {
|
|
13
|
-
Accept: "application/json, text/plain, */*",
|
|
14
|
-
"X-Requested-With": "XMLHttpRequest",
|
|
15
|
-
},
|
|
16
|
-
});
|
|
17
|
-
const text = await response.text();
|
|
18
|
-
return {
|
|
19
|
-
ok: response.ok,
|
|
20
|
-
status: response.status,
|
|
21
|
-
statusText: response.statusText,
|
|
22
|
-
contentType: response.headers.get("content-type") || "",
|
|
23
|
-
text,
|
|
24
|
-
};
|
|
25
|
-
}, url);
|
|
26
|
-
if (!result.ok) {
|
|
27
|
-
throw new Error(`Request failed: ${String(result.status)} ${result.statusText}`);
|
|
28
|
-
}
|
|
29
|
-
if (!result.contentType.toLowerCase().startsWith("application/json")) {
|
|
30
|
-
throw new Error(`Expected JSON response, got ${result.contentType}`);
|
|
31
|
-
}
|
|
32
|
-
return result.text;
|
|
33
|
-
}
|
|
34
|
-
finally {
|
|
35
|
-
await page.close();
|
|
36
|
-
}
|
|
37
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { chromium } from "playwright";
|
|
2
|
-
/**
|
|
3
|
-
* Launch Chromium with automation indicators disabled to reduce Cloudflare bot detection
|
|
4
|
-
* during the authentication flow.
|
|
5
|
-
*/
|
|
6
|
-
export async function launchChromium(headless) {
|
|
7
|
-
try {
|
|
8
|
-
return await chromium.launch({
|
|
9
|
-
headless,
|
|
10
|
-
args: ["--disable-blink-features=AutomationControlled"],
|
|
11
|
-
});
|
|
12
|
-
}
|
|
13
|
-
catch (error) {
|
|
14
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
15
|
-
if (/Executable doesn't exist|playwright\s+install/iu.test(message)) {
|
|
16
|
-
throw new Error("Playwright browsers are not installed. This is usually handled automatically by the postinstall script in package.json. Please try reinstalling the package or check if the postinstall script ran successfully. If the problem persists, you can manually run `pnpm exec playwright install chromium` or `npx playwright install chromium`, then retry.");
|
|
17
|
-
}
|
|
18
|
-
throw error;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { BrowserContext } from "playwright";
|
|
2
|
-
/**
|
|
3
|
-
* Persist context storage state to disk with secure permissions (0o600).
|
|
4
|
-
* Errors are logged as warnings to avoid blocking the main operation.
|
|
5
|
-
*/
|
|
6
|
-
export declare function persistStorageState(context: BrowserContext, storagePath: string): Promise<void>;
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { writeAtomicJson } from "../utils/write-atomic-json.js";
|
|
2
|
-
import { chalk } from "../utils/color.js";
|
|
3
|
-
/**
|
|
4
|
-
* Persist context storage state to disk with secure permissions (0o600).
|
|
5
|
-
* Errors are logged as warnings to avoid blocking the main operation.
|
|
6
|
-
*/
|
|
7
|
-
export async function persistStorageState(context, storagePath) {
|
|
8
|
-
try {
|
|
9
|
-
const state = await context.storageState();
|
|
10
|
-
await writeAtomicJson(storagePath, state, 0o600);
|
|
11
|
-
}
|
|
12
|
-
catch (error) {
|
|
13
|
-
const details = error instanceof Error ? error.message : String(error);
|
|
14
|
-
console.error(chalk.yellow(`Warning: Failed to persist auth state to ${storagePath} (${details}).`));
|
|
15
|
-
}
|
|
16
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { SupportedService } from "./supported-service.js";
|
|
2
|
-
import type { BrowserContext } from "playwright";
|
|
3
|
-
/**
|
|
4
|
-
* Service-specific configuration for authentication
|
|
5
|
-
*/
|
|
6
|
-
type ServiceAuthConfig = {
|
|
7
|
-
readonly url: string;
|
|
8
|
-
readonly waitForSelector?: string;
|
|
9
|
-
readonly waitForSelectors?: readonly string[];
|
|
10
|
-
readonly verifyUrl?: string;
|
|
11
|
-
readonly verifyFunction?: (context: BrowserContext, url: string) => Promise<boolean>;
|
|
12
|
-
readonly instructions: string;
|
|
13
|
-
};
|
|
14
|
-
export declare function getServiceAuthConfig(service: SupportedService): ServiceAuthConfig;
|
|
15
|
-
export {};
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
const SERVICE_AUTH_CONFIGS = {
|
|
2
|
-
// Claude uses CLI-based auth via Claude Code credentials
|
|
3
|
-
claude: {
|
|
4
|
-
url: "",
|
|
5
|
-
instructions: "Claude uses Claude Code authentication. Run 'claude' in your terminal to authenticate.",
|
|
6
|
-
},
|
|
7
|
-
// ChatGPT uses CLI-based auth via Codex credentials
|
|
8
|
-
chatgpt: {
|
|
9
|
-
url: "",
|
|
10
|
-
instructions: "ChatGPT uses Codex CLI authentication. Run 'codex' in your terminal to authenticate.",
|
|
11
|
-
},
|
|
12
|
-
"github-copilot": {
|
|
13
|
-
url: "https://github.com/login",
|
|
14
|
-
waitForSelector: 'img[alt*="@"]',
|
|
15
|
-
verifyUrl: "https://github.com/github-copilot/chat/entitlement",
|
|
16
|
-
instructions: "Please log in to your GitHub account in the browser window.",
|
|
17
|
-
},
|
|
18
|
-
// Gemini uses CLI-based auth, not browser auth. This entry exists only to satisfy the Record type.
|
|
19
|
-
gemini: {
|
|
20
|
-
url: "",
|
|
21
|
-
instructions: "Gemini uses CLI-based authentication. Run 'gemini' in your terminal.",
|
|
22
|
-
},
|
|
23
|
-
};
|
|
24
|
-
export function getServiceAuthConfig(service) {
|
|
25
|
-
return SERVICE_AUTH_CONFIGS[service];
|
|
26
|
-
}
|