@open-vibe-lab/open-sub-auth 0.1.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/LICENSE +21 -0
- package/README.md +218 -0
- package/dist/cli/claude-3WKImhqM.mjs +56 -0
- package/dist/cli/claude-3WKImhqM.mjs.map +1 -0
- package/dist/cli/index.d.mts +2 -0
- package/dist/cli/index.mjs +90 -0
- package/dist/cli/index.mjs.map +1 -0
- package/dist/cli/login-_HdTW5J_.mjs +33 -0
- package/dist/cli/login-_HdTW5J_.mjs.map +1 -0
- package/dist/cli/logout-CZm-tSVH.mjs +23 -0
- package/dist/cli/logout-CZm-tSVH.mjs.map +1 -0
- package/dist/cli/manager-CKGbp7Yz.mjs +331 -0
- package/dist/cli/manager-CKGbp7Yz.mjs.map +1 -0
- package/dist/cli/oauth-pkce-Bi02-h23.mjs +282 -0
- package/dist/cli/oauth-pkce-Bi02-h23.mjs.map +1 -0
- package/dist/cli/openai-codex-DJi_Q6Zm.mjs +102 -0
- package/dist/cli/openai-codex-DJi_Q6Zm.mjs.map +1 -0
- package/dist/cli/registry-Cp-_Ipc6.mjs +96 -0
- package/dist/cli/registry-Cp-_Ipc6.mjs.map +1 -0
- package/dist/cli/status-C-vkcjVM.mjs +47 -0
- package/dist/cli/status-C-vkcjVM.mjs.map +1 -0
- package/dist/cli/token-DF_-h4Rb.mjs +23 -0
- package/dist/cli/token-DF_-h4Rb.mjs.map +1 -0
- package/dist/cli/ui-CdGEuLwh.mjs +34 -0
- package/dist/cli/ui-CdGEuLwh.mjs.map +1 -0
- package/dist/index.cjs +859 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +328 -0
- package/dist/index.d.mts +328 -0
- package/dist/index.mjs +827 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +65 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 open-sub-auth contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# open-sub-auth
|
|
2
|
+
|
|
3
|
+
**Open Source Subscription OAuth Authentication Library for AI Providers**
|
|
4
|
+
|
|
5
|
+
> Login once with your subscription. Call native APIs directly.
|
|
6
|
+
|
|
7
|
+
A lightweight, open-source TypeScript library that handles OAuth login for AI subscription plans (Claude Pro/Max, ChatGPT Plus/Pro) and provides authenticated headers to call each provider's native API directly.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- **OAuth 2.0 + PKCE** — Secure authorization code flow with PKCE for Claude and OpenAI Codex
|
|
12
|
+
- **Automatic & Manual modes** — Browser-based auto-callback or manual code paste for headless/CI environments
|
|
13
|
+
- **Secure token storage** — OS keychain (macOS Keychain, Windows Credential Manager, Linux Secret Service) with encrypted file fallback
|
|
14
|
+
- **Auto token refresh** — Transparent refresh with concurrency-safe mutex
|
|
15
|
+
- **Multi-provider, multi-account** — Manage credentials for multiple providers and accounts
|
|
16
|
+
- **Minimal dependencies** — Only 1 runtime dependency (`cross-keychain`)
|
|
17
|
+
- **Full TypeScript** — Complete type safety throughout
|
|
18
|
+
- **CLI included** — `open-sub-auth login claude` and you're done
|
|
19
|
+
|
|
20
|
+
## Supported Providers
|
|
21
|
+
|
|
22
|
+
| Provider | Auth Method | Status |
|
|
23
|
+
| ------------------------------- | ----------- | ------- |
|
|
24
|
+
| Claude Pro/Max | OAuth PKCE | MVP |
|
|
25
|
+
| OpenAI ChatGPT Plus/Pro (Codex) | OAuth PKCE | MVP |
|
|
26
|
+
| GitHub Copilot | Device Code | Planned |
|
|
27
|
+
|
|
28
|
+
## Installation
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
npm install @open-vibe-lab/open-sub-auth
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Quick Start
|
|
35
|
+
|
|
36
|
+
### CLI
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# Login to Claude
|
|
40
|
+
npx open-sub-auth login claude
|
|
41
|
+
|
|
42
|
+
# Login to OpenAI Codex
|
|
43
|
+
npx open-sub-auth login openai-codex
|
|
44
|
+
|
|
45
|
+
# Check status
|
|
46
|
+
npx open-sub-auth status
|
|
47
|
+
|
|
48
|
+
# Get a token for piping
|
|
49
|
+
npx open-sub-auth token claude | pbcopy
|
|
50
|
+
|
|
51
|
+
# Headless/CI mode (manual code paste)
|
|
52
|
+
npx open-sub-auth login claude --manual
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Library
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
import { TokenManager, createTokenStore } from "@open-vibe-lab/open-sub-auth";
|
|
59
|
+
|
|
60
|
+
// Initialize
|
|
61
|
+
const store = await createTokenStore();
|
|
62
|
+
const manager = new TokenManager(store);
|
|
63
|
+
|
|
64
|
+
// Login (opens browser)
|
|
65
|
+
await manager.login("claude");
|
|
66
|
+
|
|
67
|
+
// Get authenticated headers for API calls
|
|
68
|
+
const headers = await manager.getAuthHeaders("claude");
|
|
69
|
+
|
|
70
|
+
// Call the API directly with fetch
|
|
71
|
+
const response = await fetch("https://api.anthropic.com/v1/messages", {
|
|
72
|
+
method: "POST",
|
|
73
|
+
headers, // Includes Authorization: Bearer + anthropic-beta header automatically
|
|
74
|
+
body: JSON.stringify({
|
|
75
|
+
model: "claude-sonnet-4-20250514",
|
|
76
|
+
max_tokens: 1024,
|
|
77
|
+
messages: [{ role: "user", content: "Hello!" }],
|
|
78
|
+
}),
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### OpenAI Codex
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
await manager.login("openai-codex");
|
|
86
|
+
const headers = await manager.getAuthHeaders("openai-codex");
|
|
87
|
+
|
|
88
|
+
const response = await fetch("https://chatgpt.com/backend-api/codex/responses", {
|
|
89
|
+
method: "POST",
|
|
90
|
+
headers, // Includes Authorization: Bearer automatically
|
|
91
|
+
body: JSON.stringify({
|
|
92
|
+
model: "gpt-5-codex-mini",
|
|
93
|
+
input: [{ role: "user", type: "message", content: "Hello!" }],
|
|
94
|
+
stream: false,
|
|
95
|
+
}),
|
|
96
|
+
});
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### Import Existing Codex CLI Tokens
|
|
100
|
+
|
|
101
|
+
If you've already authenticated with OpenAI's Codex CLI, you can import those tokens:
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
import { importFromCodexCli } from "@open-vibe-lab/open-sub-auth";
|
|
105
|
+
|
|
106
|
+
const tokens = importFromCodexCli(); // Reads ~/.codex/auth.json
|
|
107
|
+
if (tokens) {
|
|
108
|
+
console.log("Found existing tokens!");
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## API Reference
|
|
113
|
+
|
|
114
|
+
### `TokenManager`
|
|
115
|
+
|
|
116
|
+
The central class for managing OAuth tokens.
|
|
117
|
+
|
|
118
|
+
```typescript
|
|
119
|
+
const manager = new TokenManager(store: TokenStore);
|
|
120
|
+
|
|
121
|
+
// Interactive login
|
|
122
|
+
await manager.login(provider: string, options?: LoginOptions): Promise<StoredCredential>;
|
|
123
|
+
|
|
124
|
+
// Get a valid access token (auto-refreshes if needed)
|
|
125
|
+
await manager.getToken(provider: string, accountId?: string): Promise<string>;
|
|
126
|
+
|
|
127
|
+
// Get authenticated headers for API calls
|
|
128
|
+
await manager.getAuthHeaders(provider: string, accountId?: string): Promise<AuthHeaders>;
|
|
129
|
+
|
|
130
|
+
// Remove stored tokens
|
|
131
|
+
await manager.logout(provider: string, accountId?: string): Promise<void>;
|
|
132
|
+
|
|
133
|
+
// Check status of all credentials
|
|
134
|
+
await manager.status(): Promise<CredentialStatus[]>;
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### `createTokenStore()`
|
|
138
|
+
|
|
139
|
+
Creates a token store with OS keychain preferred, encrypted file fallback.
|
|
140
|
+
|
|
141
|
+
```typescript
|
|
142
|
+
const store = await createTokenStore(preferKeychain?: boolean): Promise<TokenStore>;
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### `LoginOptions`
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
interface LoginOptions {
|
|
149
|
+
port?: number; // Override callback server port
|
|
150
|
+
timeout?: number; // Login timeout in ms (default: 120000)
|
|
151
|
+
manual?: boolean; // Use manual code paste mode
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Architecture
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
open-sub-auth/
|
|
159
|
+
├── src/
|
|
160
|
+
│ ├── core/ # OAuth flow engines (PKCE, callback server, crypto)
|
|
161
|
+
│ ├── providers/ # Provider implementations (Claude, OpenAI Codex)
|
|
162
|
+
│ ├── storage/ # Token storage (keychain, encrypted file)
|
|
163
|
+
│ ├── token/ # Token manager, JWT utilities
|
|
164
|
+
│ └── cli/ # CLI tool
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Design Decisions
|
|
168
|
+
|
|
169
|
+
- **`getAuthHeaders()` instead of high-level Client API** — Each provider's API surface is completely different (Claude uses `x-api-key`, OpenAI Codex uses a private endpoint with non-standard format). A unified client would be a leaky abstraction. Instead, we provide authenticated headers and let you use `fetch` directly.
|
|
170
|
+
|
|
171
|
+
- **OS keychain with encrypted file fallback** — Tokens are stored in the system keychain by default. On systems without keychain access, an AES-256-GCM encrypted file is used.
|
|
172
|
+
|
|
173
|
+
- **Concurrent refresh protection** — A per-account mutex prevents multiple simultaneous token refresh requests.
|
|
174
|
+
|
|
175
|
+
## Important Warnings
|
|
176
|
+
|
|
177
|
+
### Terms of Service
|
|
178
|
+
|
|
179
|
+
Using subscription OAuth tokens in third-party tools may violate provider Terms of Service:
|
|
180
|
+
|
|
181
|
+
- **Claude**: Anthropic restricts subscription OAuth tokens to official tooling. Third-party use may result in account restrictions.
|
|
182
|
+
- **OpenAI Codex**: The `chatgpt.com/backend-api/codex/responses` endpoint is private and undocumented. It may change or be restricted at any time.
|
|
183
|
+
|
|
184
|
+
**This library is provided for personal learning and local use only. Use at your own risk.**
|
|
185
|
+
|
|
186
|
+
### API Endpoint Stability
|
|
187
|
+
|
|
188
|
+
The API endpoints used by subscription tokens are not the same as the standard public APIs:
|
|
189
|
+
|
|
190
|
+
| Provider | Endpoint | Notes |
|
|
191
|
+
| ------------ | ----------------------------------------- | ---------------------------------------------------------------- |
|
|
192
|
+
| Claude | `api.anthropic.com/v1/messages` | Requires `Authorization: Bearer` header + `anthropic-beta: oauth-2025-04-20` |
|
|
193
|
+
| OpenAI Codex | `chatgpt.com/backend-api/codex/responses` | Private endpoint, non-standard request format |
|
|
194
|
+
|
|
195
|
+
These endpoints may change without notice.
|
|
196
|
+
|
|
197
|
+
## Development
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
# Install dependencies
|
|
201
|
+
npm install
|
|
202
|
+
|
|
203
|
+
# Run tests
|
|
204
|
+
npm test
|
|
205
|
+
|
|
206
|
+
# Type check
|
|
207
|
+
npm run typecheck
|
|
208
|
+
|
|
209
|
+
# Build
|
|
210
|
+
npm run build
|
|
211
|
+
|
|
212
|
+
# Lint
|
|
213
|
+
npm run lint
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
## License
|
|
217
|
+
|
|
218
|
+
MIT
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { r as registerProvider } from "./registry-Cp-_Ipc6.mjs";
|
|
3
|
+
import { n as refreshAccessToken, t as executePKCEFlow } from "./oauth-pkce-Bi02-h23.mjs";
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
5
|
+
//#region src/providers/claude.ts
|
|
6
|
+
const CLAUDE_CONFIG = {
|
|
7
|
+
name: "claude",
|
|
8
|
+
displayName: "Claude Pro/Max",
|
|
9
|
+
authorizationEndpoint: "https://claude.ai/oauth/authorize",
|
|
10
|
+
tokenEndpoint: "https://console.anthropic.com/v1/oauth/token",
|
|
11
|
+
clientId: "9d1c250a-e61b-44d9-88ed-5944d1962f5e",
|
|
12
|
+
scopes: [
|
|
13
|
+
"org:create_api_key",
|
|
14
|
+
"user:profile",
|
|
15
|
+
"user:inference"
|
|
16
|
+
],
|
|
17
|
+
grantType: "authorization_code",
|
|
18
|
+
tokenBodyFormat: "json",
|
|
19
|
+
stateIsVerifier: true
|
|
20
|
+
};
|
|
21
|
+
/** Redirect URI used in manual/headless mode (user copies code from browser) */
|
|
22
|
+
const MANUAL_REDIRECT_URI = "https://console.anthropic.com/oauth/code/callback";
|
|
23
|
+
var ClaudeProvider = class {
|
|
24
|
+
config = CLAUDE_CONFIG;
|
|
25
|
+
async login(options) {
|
|
26
|
+
return executePKCEFlow({
|
|
27
|
+
config: this.config,
|
|
28
|
+
loginOptions: {
|
|
29
|
+
...options,
|
|
30
|
+
manual: true
|
|
31
|
+
},
|
|
32
|
+
manualRedirectUri: MANUAL_REDIRECT_URI
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
async refresh(refreshToken) {
|
|
36
|
+
return refreshAccessToken(this.config, refreshToken);
|
|
37
|
+
}
|
|
38
|
+
getAuthHeaders(accessToken) {
|
|
39
|
+
return {
|
|
40
|
+
authorization: `Bearer ${accessToken}`,
|
|
41
|
+
"anthropic-version": "2023-06-01",
|
|
42
|
+
"anthropic-beta": "oauth-2025-04-20",
|
|
43
|
+
"content-type": "application/json"
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
getAccountId(tokenSet) {
|
|
47
|
+
const source = tokenSet.refreshToken ?? tokenSet.accessToken;
|
|
48
|
+
return createHash("sha256").update(source).digest("hex").slice(0, 16);
|
|
49
|
+
}
|
|
50
|
+
getAccountLabel(_tokenSet) {}
|
|
51
|
+
};
|
|
52
|
+
registerProvider("claude", () => new ClaudeProvider());
|
|
53
|
+
//#endregion
|
|
54
|
+
export { ClaudeProvider };
|
|
55
|
+
|
|
56
|
+
//# sourceMappingURL=claude-3WKImhqM.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claude-3WKImhqM.mjs","names":[],"sources":["../../src/providers/claude.ts"],"sourcesContent":["import { createHash } from \"node:crypto\";\nimport { executePKCEFlow, refreshAccessToken } from \"@/core/oauth-pkce.ts\";\nimport type { AuthHeaders, LoginOptions, Provider, ProviderConfig, TokenSet } from \"@/types.ts\";\nimport { registerProvider } from \"@/providers/registry.ts\";\n\nconst CLAUDE_CONFIG: ProviderConfig = {\n name: \"claude\",\n displayName: \"Claude Pro/Max\",\n authorizationEndpoint: \"https://claude.ai/oauth/authorize\",\n tokenEndpoint: \"https://console.anthropic.com/v1/oauth/token\",\n clientId: \"9d1c250a-e61b-44d9-88ed-5944d1962f5e\",\n scopes: [\"org:create_api_key\", \"user:profile\", \"user:inference\"],\n grantType: \"authorization_code\",\n // Claude only supports console.anthropic.com redirect URI (no localhost),\n // and its token endpoint requires JSON body with state field\n tokenBodyFormat: \"json\",\n stateIsVerifier: true,\n};\n\n/** Redirect URI used in manual/headless mode (user copies code from browser) */\nconst MANUAL_REDIRECT_URI = \"https://console.anthropic.com/oauth/code/callback\";\n\nexport class ClaudeProvider implements Provider {\n readonly config = CLAUDE_CONFIG;\n\n async login(options?: LoginOptions): Promise<TokenSet> {\n // Claude only allows console.anthropic.com/oauth/code/callback as redirect URI.\n // Local callback server is not supported, so always use manual (code-paste) mode.\n return executePKCEFlow({\n config: this.config,\n loginOptions: { ...options, manual: true },\n manualRedirectUri: MANUAL_REDIRECT_URI,\n });\n }\n\n async refresh(refreshToken: string): Promise<TokenSet> {\n return refreshAccessToken(this.config, refreshToken);\n }\n\n getAuthHeaders(accessToken: string): AuthHeaders {\n return {\n authorization: `Bearer ${accessToken}`,\n \"anthropic-version\": \"2023-06-01\",\n \"anthropic-beta\": \"oauth-2025-04-20\",\n \"content-type\": \"application/json\",\n };\n }\n\n getAccountId(tokenSet: TokenSet): string {\n // Claude doesn't provide a profile endpoint, so derive ID from refresh token hash\n const source = tokenSet.refreshToken ?? tokenSet.accessToken;\n return createHash(\"sha256\").update(source).digest(\"hex\").slice(0, 16);\n }\n\n getAccountLabel(_tokenSet: TokenSet): string | undefined {\n // Claude tokens don't carry user identity info\n return undefined;\n }\n}\n\n// Auto-register\nregisterProvider(\"claude\", () => new ClaudeProvider());\n"],"mappings":";;;;;AAKA,MAAM,gBAAgC;CACpC,MAAM;CACN,aAAa;CACb,uBAAuB;CACvB,eAAe;CACf,UAAU;CACV,QAAQ;EAAC;EAAsB;EAAgB;EAAiB;CAChE,WAAW;CAGX,iBAAiB;CACjB,iBAAiB;CAClB;;AAGD,MAAM,sBAAsB;AAE5B,IAAa,iBAAb,MAAgD;CAC9C,SAAkB;CAElB,MAAM,MAAM,SAA2C;AAGrD,SAAO,gBAAgB;GACrB,QAAQ,KAAK;GACb,cAAc;IAAE,GAAG;IAAS,QAAQ;IAAM;GAC1C,mBAAmB;GACpB,CAAC;;CAGJ,MAAM,QAAQ,cAAyC;AACrD,SAAO,mBAAmB,KAAK,QAAQ,aAAa;;CAGtD,eAAe,aAAkC;AAC/C,SAAO;GACL,eAAe,UAAU;GACzB,qBAAqB;GACrB,kBAAkB;GAClB,gBAAgB;GACjB;;CAGH,aAAa,UAA4B;EAEvC,MAAM,SAAS,SAAS,gBAAgB,SAAS;AACjD,SAAO,WAAW,SAAS,CAAC,OAAO,OAAO,CAAC,OAAO,MAAM,CAAC,MAAM,GAAG,GAAG;;CAGvE,gBAAgB,WAAyC;;AAO3D,iBAAiB,gBAAgB,IAAI,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { t as printError } from "./ui-CdGEuLwh.mjs";
|
|
3
|
+
import { parseArgs } from "node:util";
|
|
4
|
+
//#region src/cli/index.ts
|
|
5
|
+
const HELP = `
|
|
6
|
+
open-sub-auth - OAuth authentication for AI subscription APIs
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
open-sub-auth <command> [provider] [options]
|
|
10
|
+
|
|
11
|
+
Commands:
|
|
12
|
+
login [provider] Login to an AI provider via OAuth
|
|
13
|
+
logout [provider] Remove stored tokens for a provider
|
|
14
|
+
status Show status of all stored credentials
|
|
15
|
+
token [provider] Output a valid access token to stdout
|
|
16
|
+
providers List available providers
|
|
17
|
+
|
|
18
|
+
Options:
|
|
19
|
+
--manual Use manual code paste mode (for headless/CI)
|
|
20
|
+
--help, -h Show this help message
|
|
21
|
+
--version, -v Show version
|
|
22
|
+
|
|
23
|
+
Examples:
|
|
24
|
+
open-sub-auth login claude
|
|
25
|
+
open-sub-auth login openai-codex
|
|
26
|
+
open-sub-auth login claude --manual
|
|
27
|
+
open-sub-auth token claude | pbcopy
|
|
28
|
+
open-sub-auth status
|
|
29
|
+
`.trim();
|
|
30
|
+
async function main() {
|
|
31
|
+
const { positionals } = parseArgs({
|
|
32
|
+
allowPositionals: true,
|
|
33
|
+
strict: false
|
|
34
|
+
});
|
|
35
|
+
if (process.argv.includes("--help") || process.argv.includes("-h")) {
|
|
36
|
+
console.log(HELP);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (process.argv.includes("--version") || process.argv.includes("-v")) {
|
|
40
|
+
console.log("0.1.0");
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const command = positionals[0];
|
|
44
|
+
const providerArg = positionals[1];
|
|
45
|
+
switch (command) {
|
|
46
|
+
case "login": {
|
|
47
|
+
const { loginCommand } = await import("./login-_HdTW5J_.mjs");
|
|
48
|
+
await loginCommand(providerArg);
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
case "logout": {
|
|
52
|
+
const { logoutCommand } = await import("./logout-CZm-tSVH.mjs");
|
|
53
|
+
await logoutCommand(providerArg);
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
case "status": {
|
|
57
|
+
const { statusCommand } = await import("./status-C-vkcjVM.mjs");
|
|
58
|
+
await statusCommand();
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
case "token": {
|
|
62
|
+
const { tokenCommand } = await import("./token-DF_-h4Rb.mjs");
|
|
63
|
+
await tokenCommand(providerArg);
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
case "providers": {
|
|
67
|
+
await Promise.all([import("./claude-3WKImhqM.mjs"), import("./openai-codex-DJi_Q6Zm.mjs")]);
|
|
68
|
+
const { listProviders } = await import("./registry-Cp-_Ipc6.mjs").then((n) => n.i);
|
|
69
|
+
const providers = listProviders();
|
|
70
|
+
console.log("Available providers:");
|
|
71
|
+
for (const p of providers) console.log(` - ${p}`);
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
default:
|
|
75
|
+
if (command) printError(`Unknown command: ${command}`);
|
|
76
|
+
console.log(HELP);
|
|
77
|
+
process.exit(command ? 1 : 0);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
process.stdout.on("error", (err) => {
|
|
81
|
+
if (err.code === "EPIPE") process.exit(0);
|
|
82
|
+
});
|
|
83
|
+
main().catch((err) => {
|
|
84
|
+
printError(err instanceof Error ? err.message : String(err));
|
|
85
|
+
process.exit(1);
|
|
86
|
+
});
|
|
87
|
+
//#endregion
|
|
88
|
+
export {};
|
|
89
|
+
|
|
90
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/cli/index.ts"],"sourcesContent":["import { parseArgs } from \"node:util\";\nimport { printError } from \"@/cli/ui.ts\";\n\nconst HELP = `\nopen-sub-auth - OAuth authentication for AI subscription APIs\n\nUsage:\n open-sub-auth <command> [provider] [options]\n\nCommands:\n login [provider] Login to an AI provider via OAuth\n logout [provider] Remove stored tokens for a provider\n status Show status of all stored credentials\n token [provider] Output a valid access token to stdout\n providers List available providers\n\nOptions:\n --manual Use manual code paste mode (for headless/CI)\n --help, -h Show this help message\n --version, -v Show version\n\nExamples:\n open-sub-auth login claude\n open-sub-auth login openai-codex\n open-sub-auth login claude --manual\n open-sub-auth token claude | pbcopy\n open-sub-auth status\n`.trim();\n\nasync function main(): Promise<void> {\n const { positionals } = parseArgs({\n allowPositionals: true,\n strict: false,\n });\n\n if (process.argv.includes(\"--help\") || process.argv.includes(\"-h\")) {\n console.log(HELP);\n return;\n }\n\n if (process.argv.includes(\"--version\") || process.argv.includes(\"-v\")) {\n console.log(\"0.1.0\");\n return;\n }\n\n const command = positionals[0];\n const providerArg = positionals[1];\n\n switch (command) {\n case \"login\": {\n const { loginCommand } = await import(\"@/cli/commands/login.ts\");\n await loginCommand(providerArg);\n break;\n }\n case \"logout\": {\n const { logoutCommand } = await import(\"@/cli/commands/logout.ts\");\n await logoutCommand(providerArg);\n break;\n }\n case \"status\": {\n const { statusCommand } = await import(\"@/cli/commands/status.ts\");\n await statusCommand();\n break;\n }\n case \"token\": {\n const { tokenCommand } = await import(\"@/cli/commands/token.ts\");\n await tokenCommand(providerArg);\n break;\n }\n case \"providers\": {\n await Promise.all([import(\"@/providers/claude.ts\"), import(\"@/providers/openai-codex.ts\")]);\n const { listProviders } = await import(\"@/providers/registry.ts\");\n const providers = listProviders();\n console.log(\"Available providers:\");\n for (const p of providers) {\n console.log(` - ${p}`);\n }\n break;\n }\n default: {\n if (command) {\n printError(`Unknown command: ${command}`);\n }\n console.log(HELP);\n process.exit(command ? 1 : 0);\n }\n }\n}\n\n// Suppress EPIPE errors (e.g. when stdout is piped to a command that exits early)\nprocess.stdout.on(\"error\", (err: NodeJS.ErrnoException) => {\n if (err.code === \"EPIPE\") process.exit(0);\n});\n\nmain().catch((err) => {\n printError(err instanceof Error ? err.message : String(err));\n process.exit(1);\n});\n"],"mappings":";;;;AAGA,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;EAwBX,MAAM;AAER,eAAe,OAAsB;CACnC,MAAM,EAAE,gBAAgB,UAAU;EAChC,kBAAkB;EAClB,QAAQ;EACT,CAAC;AAEF,KAAI,QAAQ,KAAK,SAAS,SAAS,IAAI,QAAQ,KAAK,SAAS,KAAK,EAAE;AAClE,UAAQ,IAAI,KAAK;AACjB;;AAGF,KAAI,QAAQ,KAAK,SAAS,YAAY,IAAI,QAAQ,KAAK,SAAS,KAAK,EAAE;AACrE,UAAQ,IAAI,QAAQ;AACpB;;CAGF,MAAM,UAAU,YAAY;CAC5B,MAAM,cAAc,YAAY;AAEhC,SAAQ,SAAR;EACE,KAAK,SAAS;GACZ,MAAM,EAAE,iBAAiB,MAAM,OAAO;AACtC,SAAM,aAAa,YAAY;AAC/B;;EAEF,KAAK,UAAU;GACb,MAAM,EAAE,kBAAkB,MAAM,OAAO;AACvC,SAAM,cAAc,YAAY;AAChC;;EAEF,KAAK,UAAU;GACb,MAAM,EAAE,kBAAkB,MAAM,OAAO;AACvC,SAAM,eAAe;AACrB;;EAEF,KAAK,SAAS;GACZ,MAAM,EAAE,iBAAiB,MAAM,OAAO;AACtC,SAAM,aAAa,YAAY;AAC/B;;EAEF,KAAK,aAAa;AAChB,SAAM,QAAQ,IAAI,CAAC,OAAO,0BAA0B,OAAO,+BAA+B,CAAC;GAC3F,MAAM,EAAE,kBAAkB,MAAM,OAAO,2BAAA,MAAA,MAAA,EAAA,EAAA;GACvC,MAAM,YAAY,eAAe;AACjC,WAAQ,IAAI,uBAAuB;AACnC,QAAK,MAAM,KAAK,UACd,SAAQ,IAAI,OAAO,IAAI;AAEzB;;EAEF;AACE,OAAI,QACF,YAAW,oBAAoB,UAAU;AAE3C,WAAQ,IAAI,KAAK;AACjB,WAAQ,KAAK,UAAU,IAAI,EAAE;;;AAMnC,QAAQ,OAAO,GAAG,UAAU,QAA+B;AACzD,KAAI,IAAI,SAAS,QAAS,SAAQ,KAAK,EAAE;EACzC;AAEF,MAAM,CAAC,OAAO,QAAQ;AACpB,YAAW,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,CAAC;AAC5D,SAAQ,KAAK,EAAE;EACf"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { n as listProviders } from "./registry-Cp-_Ipc6.mjs";
|
|
3
|
+
import { n as printSuccess, r as promptSelect, t as printError } from "./ui-CdGEuLwh.mjs";
|
|
4
|
+
import { n as createTokenStore, t as TokenManager } from "./manager-CKGbp7Yz.mjs";
|
|
5
|
+
//#region src/cli/commands/login.ts
|
|
6
|
+
async function loginCommand(providerArg) {
|
|
7
|
+
await Promise.all([import("./claude-3WKImhqM.mjs"), import("./openai-codex-DJi_Q6Zm.mjs")]);
|
|
8
|
+
const providers = listProviders();
|
|
9
|
+
if (providers.length === 0) {
|
|
10
|
+
printError("No providers registered.");
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
13
|
+
let providerName = providerArg;
|
|
14
|
+
if (!providerName) providerName = await promptSelect("Select a provider:", providers);
|
|
15
|
+
if (!providers.includes(providerName)) {
|
|
16
|
+
printError(`Unknown provider "${providerName}". Available: ${providers.join(", ")}`);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
const manual = process.argv.includes("--manual");
|
|
20
|
+
const manager = new TokenManager(await createTokenStore());
|
|
21
|
+
try {
|
|
22
|
+
const credential = await manager.login(providerName, { manual });
|
|
23
|
+
const label = credential.metadata.accountLabel ? ` (${credential.metadata.accountLabel})` : "";
|
|
24
|
+
printSuccess(`Logged in to ${providerName}${label}. Token stored securely.`);
|
|
25
|
+
} catch (err) {
|
|
26
|
+
printError(`Login failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
export { loginCommand };
|
|
32
|
+
|
|
33
|
+
//# sourceMappingURL=login-_HdTW5J_.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login-_HdTW5J_.mjs","names":[],"sources":["../../src/cli/commands/login.ts"],"sourcesContent":["import { listProviders } from \"@/providers/registry.ts\";\nimport { createTokenStore } from \"@/storage/store.ts\";\nimport { TokenManager } from \"@/token/manager.ts\";\nimport { promptSelect, printError, printSuccess } from \"@/cli/ui.ts\";\n\nexport async function loginCommand(providerArg?: string): Promise<void> {\n await Promise.all([import(\"@/providers/claude.ts\"), import(\"@/providers/openai-codex.ts\")]);\n\n const providers = listProviders();\n if (providers.length === 0) {\n printError(\"No providers registered.\");\n process.exit(1);\n }\n\n let providerName = providerArg;\n if (!providerName) {\n providerName = await promptSelect(\"Select a provider:\", providers);\n }\n\n if (!providers.includes(providerName)) {\n printError(`Unknown provider \"${providerName}\". Available: ${providers.join(\", \")}`);\n process.exit(1);\n }\n\n const manual = process.argv.includes(\"--manual\");\n const store = await createTokenStore();\n const manager = new TokenManager(store);\n\n try {\n const credential = await manager.login(providerName, { manual });\n const label = credential.metadata.accountLabel ? ` (${credential.metadata.accountLabel})` : \"\";\n printSuccess(`Logged in to ${providerName}${label}. Token stored securely.`);\n } catch (err) {\n printError(`Login failed: ${err instanceof Error ? err.message : String(err)}`);\n process.exit(1);\n }\n}\n"],"mappings":";;;;;AAKA,eAAsB,aAAa,aAAqC;AACtE,OAAM,QAAQ,IAAI,CAAC,OAAO,0BAA0B,OAAO,+BAA+B,CAAC;CAE3F,MAAM,YAAY,eAAe;AACjC,KAAI,UAAU,WAAW,GAAG;AAC1B,aAAW,2BAA2B;AACtC,UAAQ,KAAK,EAAE;;CAGjB,IAAI,eAAe;AACnB,KAAI,CAAC,aACH,gBAAe,MAAM,aAAa,sBAAsB,UAAU;AAGpE,KAAI,CAAC,UAAU,SAAS,aAAa,EAAE;AACrC,aAAW,qBAAqB,aAAa,gBAAgB,UAAU,KAAK,KAAK,GAAG;AACpF,UAAQ,KAAK,EAAE;;CAGjB,MAAM,SAAS,QAAQ,KAAK,SAAS,WAAW;CAEhD,MAAM,UAAU,IAAI,aADN,MAAM,kBAAkB,CACC;AAEvC,KAAI;EACF,MAAM,aAAa,MAAM,QAAQ,MAAM,cAAc,EAAE,QAAQ,CAAC;EAChE,MAAM,QAAQ,WAAW,SAAS,eAAe,KAAK,WAAW,SAAS,aAAa,KAAK;AAC5F,eAAa,gBAAgB,eAAe,MAAM,0BAA0B;UACrE,KAAK;AACZ,aAAW,iBAAiB,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GAAG;AAC/E,UAAQ,KAAK,EAAE"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { n as listProviders } from "./registry-Cp-_Ipc6.mjs";
|
|
3
|
+
import { n as printSuccess, r as promptSelect, t as printError } from "./ui-CdGEuLwh.mjs";
|
|
4
|
+
import { n as createTokenStore, t as TokenManager } from "./manager-CKGbp7Yz.mjs";
|
|
5
|
+
//#region src/cli/commands/logout.ts
|
|
6
|
+
async function logoutCommand(providerArg) {
|
|
7
|
+
await Promise.all([import("./claude-3WKImhqM.mjs"), import("./openai-codex-DJi_Q6Zm.mjs")]);
|
|
8
|
+
const providers = listProviders();
|
|
9
|
+
let providerName = providerArg;
|
|
10
|
+
if (!providerName) providerName = await promptSelect("Select a provider to logout:", providers);
|
|
11
|
+
const manager = new TokenManager(await createTokenStore());
|
|
12
|
+
try {
|
|
13
|
+
await manager.logout(providerName);
|
|
14
|
+
printSuccess(`Logged out of ${providerName}. Stored tokens removed.`);
|
|
15
|
+
} catch (err) {
|
|
16
|
+
printError(`Logout failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
//#endregion
|
|
21
|
+
export { logoutCommand };
|
|
22
|
+
|
|
23
|
+
//# sourceMappingURL=logout-CZm-tSVH.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logout-CZm-tSVH.mjs","names":[],"sources":["../../src/cli/commands/logout.ts"],"sourcesContent":["import { listProviders } from \"@/providers/registry.ts\";\nimport { createTokenStore } from \"@/storage/store.ts\";\nimport { TokenManager } from \"@/token/manager.ts\";\nimport { printError, printSuccess, promptSelect } from \"@/cli/ui.ts\";\n\nexport async function logoutCommand(providerArg?: string): Promise<void> {\n await Promise.all([import(\"@/providers/claude.ts\"), import(\"@/providers/openai-codex.ts\")]);\n\n const providers = listProviders();\n let providerName = providerArg;\n if (!providerName) {\n providerName = await promptSelect(\"Select a provider to logout:\", providers);\n }\n\n const store = await createTokenStore();\n const manager = new TokenManager(store);\n\n try {\n await manager.logout(providerName);\n printSuccess(`Logged out of ${providerName}. Stored tokens removed.`);\n } catch (err) {\n printError(`Logout failed: ${err instanceof Error ? err.message : String(err)}`);\n process.exit(1);\n }\n}\n"],"mappings":";;;;;AAKA,eAAsB,cAAc,aAAqC;AACvE,OAAM,QAAQ,IAAI,CAAC,OAAO,0BAA0B,OAAO,+BAA+B,CAAC;CAE3F,MAAM,YAAY,eAAe;CACjC,IAAI,eAAe;AACnB,KAAI,CAAC,aACH,gBAAe,MAAM,aAAa,gCAAgC,UAAU;CAI9E,MAAM,UAAU,IAAI,aADN,MAAM,kBAAkB,CACC;AAEvC,KAAI;AACF,QAAM,QAAQ,OAAO,aAAa;AAClC,eAAa,iBAAiB,aAAa,0BAA0B;UAC9D,KAAK;AACZ,aAAW,kBAAkB,eAAe,QAAQ,IAAI,UAAU,OAAO,IAAI,GAAG;AAChF,UAAQ,KAAK,EAAE"}
|