@clawapps/cli 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.
Files changed (61) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +148 -0
  3. package/bin/claw.js +2 -0
  4. package/dist/auth/apple.d.ts +5 -0
  5. package/dist/auth/apple.js +15 -0
  6. package/dist/auth/apple.js.map +1 -0
  7. package/dist/auth/exchange.d.ts +6 -0
  8. package/dist/auth/exchange.js +25 -0
  9. package/dist/auth/exchange.js.map +1 -0
  10. package/dist/auth/google.d.ts +5 -0
  11. package/dist/auth/google.js +16 -0
  12. package/dist/auth/google.js.map +1 -0
  13. package/dist/auth/login-server.d.ts +10 -0
  14. package/dist/auth/login-server.js +62 -0
  15. package/dist/auth/login-server.js.map +1 -0
  16. package/dist/auth/server.d.ts +17 -0
  17. package/dist/auth/server.js +102 -0
  18. package/dist/auth/server.js.map +1 -0
  19. package/dist/commands/credit.d.ts +1 -0
  20. package/dist/commands/credit.js +28 -0
  21. package/dist/commands/credit.js.map +1 -0
  22. package/dist/commands/helpers/ensure-token.d.ts +6 -0
  23. package/dist/commands/helpers/ensure-token.js +29 -0
  24. package/dist/commands/helpers/ensure-token.js.map +1 -0
  25. package/dist/commands/login.d.ts +1 -0
  26. package/dist/commands/login.js +77 -0
  27. package/dist/commands/login.js.map +1 -0
  28. package/dist/commands/logout.d.ts +1 -0
  29. package/dist/commands/logout.js +24 -0
  30. package/dist/commands/logout.js.map +1 -0
  31. package/dist/commands/membership.d.ts +1 -0
  32. package/dist/commands/membership.js +28 -0
  33. package/dist/commands/membership.js.map +1 -0
  34. package/dist/commands/token.d.ts +1 -0
  35. package/dist/commands/token.js +17 -0
  36. package/dist/commands/token.js.map +1 -0
  37. package/dist/commands/whoami.d.ts +1 -0
  38. package/dist/commands/whoami.js +59 -0
  39. package/dist/commands/whoami.js.map +1 -0
  40. package/dist/html/callback.d.ts +12 -0
  41. package/dist/html/callback.js +246 -0
  42. package/dist/html/callback.js.map +1 -0
  43. package/dist/html/logo-data.d.ts +1 -0
  44. package/dist/html/logo-data.js +2 -0
  45. package/dist/html/logo-data.js.map +1 -0
  46. package/dist/index.d.ts +1 -0
  47. package/dist/index.js +38 -0
  48. package/dist/index.js.map +1 -0
  49. package/dist/lib/api.d.ts +13 -0
  50. package/dist/lib/api.js +26 -0
  51. package/dist/lib/api.js.map +1 -0
  52. package/dist/lib/config.d.ts +15 -0
  53. package/dist/lib/config.js +21 -0
  54. package/dist/lib/config.js.map +1 -0
  55. package/dist/lib/credentials.d.ts +4 -0
  56. package/dist/lib/credentials.js +35 -0
  57. package/dist/lib/credentials.js.map +1 -0
  58. package/dist/lib/types.d.ts +24 -0
  59. package/dist/lib/types.js +2 -0
  60. package/dist/lib/types.js.map +1 -0
  61. package/package.json +39 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ClawApps
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,148 @@
1
+ # @clawapps/cli
2
+
3
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
4
+ [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D18-green.svg)](https://nodejs.org/)
5
+
6
+ **[中文文档](README_zh.md)**
7
+
8
+ A command-line tool for authenticating with the [ClawApps](https://www.clawapps.ai) platform. Sign in via Google or Apple directly from your terminal — tokens are stored locally for use by AI agents and scripts.
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ npm install -g @clawapps/cli
14
+ ```
15
+
16
+ > **Not yet on npm?** Install from source:
17
+ > ```bash
18
+ > git clone git@github.com:ClawApps/clawapps-cli.git
19
+ > cd clawapps-cli && npm install && npm run build && npm link
20
+ > ```
21
+
22
+ ## Commands
23
+
24
+ ### `claw login`
25
+
26
+ Sign in with Google or Apple. Opens a browser for OAuth, then stores tokens locally.
27
+
28
+ ```bash
29
+ $ claw login
30
+ ? Choose login method: Google
31
+ Opening browser for Google login...
32
+ ✔ Logged in as user@gmail.com
33
+ ```
34
+
35
+ ### `claw whoami`
36
+
37
+ Show current account info. Auto-refreshes expired tokens.
38
+
39
+ ```bash
40
+ $ claw whoami
41
+ ClawApps Account
42
+ ──────────────────────────────
43
+ Name: Username
44
+ Email: user@gmail.com
45
+ ID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
46
+ Provider: google
47
+ ```
48
+
49
+ ### `claw logout`
50
+
51
+ Sign out and clear local credentials.
52
+
53
+ ```bash
54
+ $ claw logout
55
+ Logged out successfully.
56
+ ```
57
+
58
+ ## How It Works
59
+
60
+ ```
61
+ claw login
62
+ → Choose Google or Apple
63
+ → Local HTTP server starts on localhost (random port)
64
+ → Browser opens for OAuth
65
+ → Callback returns token to local server
66
+ → Token exchange: Google/Apple → OpenDigits → ClawApps
67
+ → Credentials saved to ~/.clawapps/credentials.json (0600)
68
+ ```
69
+
70
+ **Google flow**: Implicit OAuth → local callback page extracts token from URL hash → POST to local server → exchange for ClawApps tokens.
71
+
72
+ **Apple flow**: OpenDigits handles Apple OAuth → redirects to local callback with tokens in query params → exchange for ClawApps tokens.
73
+
74
+ ## Credentials
75
+
76
+ Tokens are stored at `~/.clawapps/credentials.json` with file permissions `0600`.
77
+
78
+ ```json
79
+ {
80
+ "provider": "google",
81
+ "access_token": "eyJ...",
82
+ "refresh_token": "eyJ...",
83
+ "logged_in_at": "2026-02-24T11:11:35.871Z"
84
+ }
85
+ ```
86
+
87
+ Use in scripts:
88
+
89
+ ```bash
90
+ TOKEN=$(cat ~/.clawapps/credentials.json | jq -r .access_token)
91
+ curl -H "Authorization: Bearer $TOKEN" https://api.clawapps.ai/api/v1/...
92
+ ```
93
+
94
+ ## Project Structure
95
+
96
+ ```
97
+ clawapps-cli/
98
+ ├── bin/claw.js # Entry point
99
+ ├── src/
100
+ │ ├── index.ts # Commander setup
101
+ │ ├── commands/
102
+ │ │ ├── login.ts # OAuth flow orchestration
103
+ │ │ ├── logout.ts # Clear credentials
104
+ │ │ └── whoami.ts # User info with auto-refresh
105
+ │ ├── auth/
106
+ │ │ ├── server.ts # Local HTTP callback server
107
+ │ │ ├── google.ts # Google OAuth URL builder
108
+ │ │ ├── apple.ts # Apple OAuth URL builder (via OD)
109
+ │ │ └── exchange.ts # Token exchange (OD → ClawApps)
110
+ │ ├── lib/
111
+ │ │ ├── config.ts # API endpoints & constants
112
+ │ │ ├── credentials.ts # Read/write ~/.clawapps/credentials.json
113
+ │ │ ├── api.ts # HTTP request helpers
114
+ │ │ └── types.ts # TypeScript interfaces
115
+ │ └── html/
116
+ │ ├── callback.ts # OAuth callback HTML templates
117
+ │ └── logo-data.ts # Logo (base64 embedded)
118
+ ├── package.json
119
+ └── tsconfig.json
120
+ ```
121
+
122
+ ## Development
123
+
124
+ ```bash
125
+ npm install # Install dependencies
126
+ npm run build # Compile TypeScript
127
+ npm run dev # Watch mode
128
+ node bin/claw.js # Run locally
129
+ ```
130
+
131
+ ## Requirements
132
+
133
+ - **Node.js >= 18** (uses native `fetch`)
134
+
135
+ ## Related
136
+
137
+ - [clawapps-skill](https://github.com/ClawApps/clawapps-skill) — Agent Skill for managing apps on ClawApps
138
+
139
+ ## Contributing
140
+
141
+ 1. Fork the repository
142
+ 2. Create a feature branch (`git checkout -b feat/my-feature`)
143
+ 3. Commit your changes (use [Conventional Commits](https://www.conventionalcommits.org/))
144
+ 4. Push and open a Pull Request
145
+
146
+ ## License
147
+
148
+ [MIT](LICENSE) - Copyright 2026 ClawApps
package/bin/claw.js ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import '../dist/index.js';
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Build the Apple OAuth URL via OpenDigits.
3
+ * OD handles the Apple OAuth flow and redirects back to our localhost with tokens.
4
+ */
5
+ export declare function buildAppleAuthUrl(redirectUri: string): string;
@@ -0,0 +1,15 @@
1
+ import { CONFIG } from '../lib/config.js';
2
+ import { randomBytes } from 'node:crypto';
3
+ /**
4
+ * Build the Apple OAuth URL via OpenDigits.
5
+ * OD handles the Apple OAuth flow and redirects back to our localhost with tokens.
6
+ */
7
+ export function buildAppleAuthUrl(redirectUri) {
8
+ const state = randomBytes(16).toString('hex');
9
+ const params = new URLSearchParams({
10
+ redirect_uri: redirectUri,
11
+ state,
12
+ });
13
+ return `${CONFIG.OD_APPLE_AUTHORIZE}?${params.toString()}`;
14
+ }
15
+ //# sourceMappingURL=apple.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apple.js","sourceRoot":"","sources":["../../src/auth/apple.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1C;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,WAAmB;IACnD,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAE9C,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;QACjC,YAAY,EAAE,WAAW;QACzB,KAAK;KACN,CAAC,CAAC;IAEH,OAAO,GAAG,MAAM,CAAC,kBAAkB,IAAI,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;AAC7D,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { ODTokens, ClawTokens } from '../lib/types.js';
2
+ /**
3
+ * Exchange a Google access_token for OpenDigits tokens.
4
+ */
5
+ export declare function googleToOD(googleAccessToken: string): Promise<ODTokens>;
6
+ export declare function odToClawApps(odTokens: ODTokens): Promise<ClawTokens>;
@@ -0,0 +1,25 @@
1
+ import { CONFIG } from '../lib/config.js';
2
+ import { apiPost } from '../lib/api.js';
3
+ /**
4
+ * Exchange a Google access_token for OpenDigits tokens.
5
+ */
6
+ export async function googleToOD(googleAccessToken) {
7
+ const res = await apiPost(CONFIG.OD_GOOGLE_AUTH, {
8
+ access_token: googleAccessToken,
9
+ });
10
+ if (!res.ok) {
11
+ throw new Error(`Google → OD exchange failed (${res.status}): ${JSON.stringify(res.data)}`);
12
+ }
13
+ return res.data.data;
14
+ }
15
+ export async function odToClawApps(odTokens) {
16
+ const res = await apiPost(CONFIG.CLAW_EXCHANGE, {
17
+ od_token: odTokens.access_token,
18
+ od_refresh_token: odTokens.refresh_token,
19
+ });
20
+ if (!res.ok) {
21
+ throw new Error(`OD → ClawApps exchange failed (${res.status}): ${JSON.stringify(res.data)}`);
22
+ }
23
+ return res.data.data;
24
+ }
25
+ //# sourceMappingURL=exchange.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"exchange.js","sourceRoot":"","sources":["../../src/auth/exchange.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAUxC;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,iBAAyB;IACxD,MAAM,GAAG,GAAG,MAAM,OAAO,CAA0B,MAAM,CAAC,cAAc,EAAE;QACxE,YAAY,EAAE,iBAAiB;KAChC,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,gCAAgC,GAAG,CAAC,MAAM,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC9F,CAAC;IAED,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;AACvB,CAAC;AAYD,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,QAAkB;IACnD,MAAM,GAAG,GAAG,MAAM,OAAO,CAA8B,MAAM,CAAC,aAAa,EAAE;QAC3E,QAAQ,EAAE,QAAQ,CAAC,YAAY;QAC/B,gBAAgB,EAAE,QAAQ,CAAC,aAAa;KACzC,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,kCAAkC,GAAG,CAAC,MAAM,MAAM,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAChG,CAAC;IAED,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;AACvB,CAAC"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Build the Google OAuth implicit flow URL.
3
+ * Uses response_type=token to get access_token in the URL hash fragment.
4
+ */
5
+ export declare function buildGoogleAuthUrl(redirectUri: string): string;
@@ -0,0 +1,16 @@
1
+ import { CONFIG } from '../lib/config.js';
2
+ /**
3
+ * Build the Google OAuth implicit flow URL.
4
+ * Uses response_type=token to get access_token in the URL hash fragment.
5
+ */
6
+ export function buildGoogleAuthUrl(redirectUri) {
7
+ const params = new URLSearchParams({
8
+ client_id: CONFIG.GOOGLE_CLIENT_ID,
9
+ redirect_uri: redirectUri,
10
+ response_type: 'token',
11
+ scope: 'openid email profile',
12
+ prompt: 'select_account',
13
+ });
14
+ return `https://accounts.google.com/o/oauth2/v2/auth?${params.toString()}`;
15
+ }
16
+ //# sourceMappingURL=google.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google.js","sourceRoot":"","sources":["../../src/auth/google.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,WAAmB;IACpD,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC;QACjC,SAAS,EAAE,MAAM,CAAC,gBAAgB;QAClC,YAAY,EAAE,WAAW;QACzB,aAAa,EAAE,OAAO;QACtB,KAAK,EAAE,sBAAsB;QAC7B,MAAM,EAAE,gBAAgB;KACzB,CAAC,CAAC;IAEH,OAAO,gDAAgD,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;AAC7E,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { ClawTokens } from '../lib/types.js';
2
+ /**
3
+ * Start a local HTTP server that receives ClawApps tokens from the web login page redirect.
4
+ * The web page redirects to: http://localhost:{port}/callback?access_token=xxx&refresh_token=xxx
5
+ */
6
+ export declare function startLoginCallbackServer(): Promise<{
7
+ port: number;
8
+ result: Promise<ClawTokens>;
9
+ close: () => void;
10
+ }>;
@@ -0,0 +1,62 @@
1
+ import { createServer } from 'node:http';
2
+ import { URL } from 'node:url';
3
+ import { CONFIG } from '../lib/config.js';
4
+ import { getSuccessHtml, getErrorHtml } from '../html/callback.js';
5
+ /**
6
+ * Start a local HTTP server that receives ClawApps tokens from the web login page redirect.
7
+ * The web page redirects to: http://localhost:{port}/callback?access_token=xxx&refresh_token=xxx
8
+ */
9
+ export function startLoginCallbackServer() {
10
+ return new Promise((resolveServer, rejectServer) => {
11
+ let resultResolve;
12
+ let resultReject;
13
+ const resultPromise = new Promise((resolve, reject) => {
14
+ resultResolve = resolve;
15
+ resultReject = reject;
16
+ });
17
+ const server = createServer((req, res) => {
18
+ const port = server.address().port;
19
+ const url = new URL(req.url || '/', `http://localhost:${port}`);
20
+ if (url.pathname === '/callback') {
21
+ const accessToken = url.searchParams.get('access_token');
22
+ const refreshToken = url.searchParams.get('refresh_token');
23
+ if (accessToken && refreshToken) {
24
+ res.writeHead(200, { 'Content-Type': 'text/html' });
25
+ res.end(getSuccessHtml());
26
+ resultResolve({ access_token: accessToken, refresh_token: refreshToken });
27
+ }
28
+ else {
29
+ res.writeHead(200, { 'Content-Type': 'text/html' });
30
+ res.end(getErrorHtml('Missing tokens. Please try again.'));
31
+ resultReject(new Error('Callback missing tokens'));
32
+ }
33
+ return;
34
+ }
35
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
36
+ res.end('Not found');
37
+ });
38
+ server.listen(0, '127.0.0.1', () => {
39
+ const addr = server.address();
40
+ const timeout = setTimeout(() => {
41
+ resultReject(new Error('Authentication timed out. Please try again.'));
42
+ server.close();
43
+ }, CONFIG.AUTH_TIMEOUT_MS);
44
+ resultPromise.finally(() => {
45
+ clearTimeout(timeout);
46
+ setTimeout(() => server.close(), 500);
47
+ });
48
+ resolveServer({
49
+ port: addr.port,
50
+ result: resultPromise,
51
+ close: () => {
52
+ clearTimeout(timeout);
53
+ server.close();
54
+ },
55
+ });
56
+ });
57
+ server.on('error', (err) => {
58
+ rejectServer(err);
59
+ });
60
+ });
61
+ }
62
+ //# sourceMappingURL=login-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login-server.js","sourceRoot":"","sources":["../../src/auth/login-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import type { AuthProvider, ODTokens } from '../lib/types.js';
2
+ export interface CallbackResult {
3
+ provider: AuthProvider;
4
+ /** For Google: the Google access_token; For Apple: not used */
5
+ googleAccessToken?: string;
6
+ /** For Apple: OD tokens come directly in the callback */
7
+ odTokens?: ODTokens;
8
+ }
9
+ /**
10
+ * Start a local HTTP server to receive the OAuth callback.
11
+ * Returns a promise that resolves with the callback result.
12
+ */
13
+ export declare function startCallbackServer(provider: AuthProvider): Promise<{
14
+ port: number;
15
+ result: Promise<CallbackResult>;
16
+ close: () => void;
17
+ }>;
@@ -0,0 +1,102 @@
1
+ import { createServer } from 'node:http';
2
+ import { URL } from 'node:url';
3
+ import { CONFIG } from '../lib/config.js';
4
+ import { getGoogleCallbackHtml, getSuccessHtml, getErrorHtml } from '../html/callback.js';
5
+ function handleRequest(ctx, req, res) {
6
+ const url = new URL(req.url || '/', `http://localhost:${ctx.port}`);
7
+ // Google: /callback serves HTML that extracts token from hash
8
+ if (url.pathname === '/callback' && ctx.provider === 'google') {
9
+ res.writeHead(200, { 'Content-Type': 'text/html' });
10
+ res.end(getGoogleCallbackHtml(ctx.port));
11
+ return;
12
+ }
13
+ // Google: /token receives the token POSTed by the callback HTML page
14
+ if (url.pathname === '/token' && req.method === 'POST') {
15
+ let body = '';
16
+ req.on('data', (chunk) => { body += chunk; });
17
+ req.on('end', () => {
18
+ try {
19
+ const data = JSON.parse(body);
20
+ const accessToken = data.access_token;
21
+ if (!accessToken) {
22
+ res.writeHead(400, { 'Content-Type': 'application/json' });
23
+ res.end(JSON.stringify({ error: 'Missing access_token' }));
24
+ return;
25
+ }
26
+ res.writeHead(200, { 'Content-Type': 'application/json' });
27
+ res.end(JSON.stringify({ ok: true }));
28
+ ctx.resolve({ provider: 'google', googleAccessToken: accessToken });
29
+ }
30
+ catch {
31
+ res.writeHead(400, { 'Content-Type': 'application/json' });
32
+ res.end(JSON.stringify({ error: 'Invalid JSON' }));
33
+ }
34
+ });
35
+ return;
36
+ }
37
+ // Apple: /callback receives OD tokens as query params
38
+ if (url.pathname === '/callback' && ctx.provider === 'apple') {
39
+ const accessToken = url.searchParams.get('access_token');
40
+ const refreshToken = url.searchParams.get('refresh_token');
41
+ if (!accessToken || !refreshToken) {
42
+ res.writeHead(200, { 'Content-Type': 'text/html' });
43
+ res.end(getErrorHtml('Missing tokens in callback. Please try again.'));
44
+ ctx.reject(new Error('Apple callback missing tokens'));
45
+ return;
46
+ }
47
+ res.writeHead(200, { 'Content-Type': 'text/html' });
48
+ res.end(getSuccessHtml());
49
+ ctx.resolve({
50
+ provider: 'apple',
51
+ odTokens: { access_token: accessToken, refresh_token: refreshToken },
52
+ });
53
+ return;
54
+ }
55
+ // Fallback
56
+ res.writeHead(404, { 'Content-Type': 'text/plain' });
57
+ res.end('Not found');
58
+ }
59
+ /**
60
+ * Start a local HTTP server to receive the OAuth callback.
61
+ * Returns a promise that resolves with the callback result.
62
+ */
63
+ export function startCallbackServer(provider) {
64
+ return new Promise((resolveServer, rejectServer) => {
65
+ let resultResolve;
66
+ let resultReject;
67
+ const resultPromise = new Promise((resolve, reject) => {
68
+ resultResolve = resolve;
69
+ resultReject = reject;
70
+ });
71
+ const server = createServer((req, res) => {
72
+ handleRequest({ port: server.address().port, provider, resolve: resultResolve, reject: resultReject }, req, res);
73
+ });
74
+ // Bind to 127.0.0.1 only for security, port 0 = system assigns
75
+ server.listen(0, '127.0.0.1', () => {
76
+ const addr = server.address();
77
+ // Set up timeout
78
+ const timeout = setTimeout(() => {
79
+ resultReject(new Error('Authentication timed out. Please try again.'));
80
+ server.close();
81
+ }, CONFIG.AUTH_TIMEOUT_MS);
82
+ // When result resolves or rejects, clean up
83
+ resultPromise.finally(() => {
84
+ clearTimeout(timeout);
85
+ // Give the browser a moment to receive the response
86
+ setTimeout(() => server.close(), 500);
87
+ });
88
+ resolveServer({
89
+ port: addr.port,
90
+ result: resultPromise,
91
+ close: () => {
92
+ clearTimeout(timeout);
93
+ server.close();
94
+ },
95
+ });
96
+ });
97
+ server.on('error', (err) => {
98
+ rejectServer(err);
99
+ });
100
+ });
101
+ }
102
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/auth/server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function creditCommand(): Promise<void>;
@@ -0,0 +1,28 @@
1
+ import chalk from 'chalk';
2
+ import open from 'open';
3
+ import { loadCredentials } from '../lib/credentials.js';
4
+ import { CONFIG } from '../lib/config.js';
5
+ import { ensureValidToken } from './helpers/ensure-token.js';
6
+ export async function creditCommand() {
7
+ const credentials = await loadCredentials();
8
+ if (!credentials) {
9
+ console.log(chalk.yellow('Not logged in. Run `claw login` first.'));
10
+ process.exit(1);
11
+ }
12
+ const token = await ensureValidToken(credentials);
13
+ if (!token) {
14
+ console.log(chalk.red('Session expired. Please run `claw login` again.'));
15
+ process.exit(1);
16
+ }
17
+ const url = `${CONFIG.CLAW_WEB_BASE}/credit?access_token=${encodeURIComponent(token.access_token)}&refresh_token=${encodeURIComponent(token.refresh_token)}`;
18
+ console.log(chalk.gray('Opening credit recharge page...'));
19
+ try {
20
+ await open(url);
21
+ console.log(chalk.green('Page opened in your browser.'));
22
+ }
23
+ catch {
24
+ console.log(chalk.yellow('Could not open browser automatically.'));
25
+ console.log(chalk.gray(`Please open this URL manually:\n${url}`));
26
+ }
27
+ }
28
+ //# sourceMappingURL=credit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credit.js","sourceRoot":"","sources":["../../src/commands/credit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAE7D,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,MAAM,WAAW,GAAG,MAAM,eAAe,EAAE,CAAC;IAE5C,IAAI,CAAC,WAAW,EAAE,CAAC;QACjB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,wCAAwC,CAAC,CAAC,CAAC;QACpE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,gBAAgB,CAAC,WAAW,CAAC,CAAC;IAClD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,iDAAiD,CAAC,CAAC,CAAC;QAC1E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,GAAG,GAAG,GAAG,MAAM,CAAC,aAAa,wBAAwB,kBAAkB,CAAC,KAAK,CAAC,YAAY,CAAC,kBAAkB,kBAAkB,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE,CAAC;IAE7J,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC,CAAC;IAE3D,IAAI,CAAC;QACH,MAAM,IAAI,CAAC,GAAG,CAAC,CAAC;QAChB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC,CAAC;IAC3D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,uCAAuC,CAAC,CAAC,CAAC;QACnE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,mCAAmC,GAAG,EAAE,CAAC,CAAC,CAAC;IACpE,CAAC;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { Credentials } from '../../lib/types.js';
2
+ /**
3
+ * Validates the current token, refreshes if expired.
4
+ * Returns updated credentials or null if session is invalid.
5
+ */
6
+ export declare function ensureValidToken(credentials: Credentials): Promise<Credentials | null>;
@@ -0,0 +1,29 @@
1
+ import { apiGet, apiPost } from '../../lib/api.js';
2
+ import { saveCredentials } from '../../lib/credentials.js';
3
+ import { CONFIG } from '../../lib/config.js';
4
+ /**
5
+ * Validates the current token, refreshes if expired.
6
+ * Returns updated credentials or null if session is invalid.
7
+ */
8
+ export async function ensureValidToken(credentials) {
9
+ // Try current token
10
+ const res = await apiGet(CONFIG.CLAW_ME, credentials.access_token);
11
+ if (res.ok)
12
+ return credentials;
13
+ // Token expired — try refresh
14
+ if (res.status === 401) {
15
+ const refreshRes = await apiPost(CONFIG.CLAW_REFRESH, { refresh_token: credentials.refresh_token });
16
+ if (!refreshRes.ok)
17
+ return null;
18
+ const newTokens = refreshRes.data.data;
19
+ const updated = {
20
+ ...credentials,
21
+ access_token: newTokens.access_token,
22
+ refresh_token: newTokens.refresh_token,
23
+ };
24
+ await saveCredentials(updated);
25
+ return updated;
26
+ }
27
+ return null;
28
+ }
29
+ //# sourceMappingURL=ensure-token.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ensure-token.js","sourceRoot":"","sources":["../../../src/commands/helpers/ensure-token.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAwB,MAAM,kBAAkB,CAAC;AACzE,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAC3D,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAG7C;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,WAAwB;IAExB,oBAAoB;IACpB,MAAM,GAAG,GAAG,MAAM,MAAM,CACtB,MAAM,CAAC,OAAO,EACd,WAAW,CAAC,YAAY,CACzB,CAAC;IAEF,IAAI,GAAG,CAAC,EAAE;QAAE,OAAO,WAAW,CAAC;IAE/B,8BAA8B;IAC9B,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QACvB,MAAM,UAAU,GAAG,MAAM,OAAO,CAC9B,MAAM,CAAC,YAAY,EACnB,EAAE,aAAa,EAAE,WAAW,CAAC,aAAa,EAAE,CAC7C,CAAC;QAEF,IAAI,CAAC,UAAU,CAAC,EAAE;YAAE,OAAO,IAAI,CAAC;QAEhC,MAAM,SAAS,GAAG,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC;QACvC,MAAM,OAAO,GAAgB;YAC3B,GAAG,WAAW;YACd,YAAY,EAAE,SAAS,CAAC,YAAY;YACpC,aAAa,EAAE,SAAS,CAAC,aAAa;SACvC,CAAC;QAEF,MAAM,eAAe,CAAC,OAAO,CAAC,CAAC;QAC/B,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function loginCommand(): Promise<void>;
@@ -0,0 +1,77 @@
1
+ import chalk from 'chalk';
2
+ import ora from 'ora';
3
+ import open from 'open';
4
+ import { loadCredentials, saveCredentials } from '../lib/credentials.js';
5
+ import { apiGet } from '../lib/api.js';
6
+ import { CONFIG } from '../lib/config.js';
7
+ import { startLoginCallbackServer } from '../auth/login-server.js';
8
+ import { ensureValidToken } from './helpers/ensure-token.js';
9
+ export async function loginCommand() {
10
+ // Check if already logged in, refresh token if needed
11
+ const existing = await loadCredentials();
12
+ if (existing) {
13
+ try {
14
+ const validated = await ensureValidToken(existing);
15
+ if (validated) {
16
+ const res = await apiGet(CONFIG.CLAW_ME, validated.access_token);
17
+ if (res.ok) {
18
+ const user = res.data.data;
19
+ console.log(chalk.green(`Already logged in as ${chalk.bold(user.email || user.name || 'user')}.`));
20
+ console.log(chalk.gray('Session refreshed. Run `claw logout` first to switch accounts.'));
21
+ return;
22
+ }
23
+ }
24
+ }
25
+ catch {
26
+ // Token validation/refresh failed, continue with browser login
27
+ }
28
+ }
29
+ // Start local callback server to receive tokens from web
30
+ const { port, result, close } = await startLoginCallbackServer();
31
+ const callbackUrl = `http://localhost:${port}/callback`;
32
+ // Open web login page with callback
33
+ const loginUrl = `${CONFIG.CLAW_WEB_BASE}/login?callback=${encodeURIComponent(callbackUrl)}`;
34
+ console.log(chalk.gray('\nOpening browser for login...'));
35
+ console.log(chalk.gray(`If the browser doesn't open, visit:\n${loginUrl}\n`));
36
+ try {
37
+ await open(loginUrl);
38
+ }
39
+ catch {
40
+ console.log(chalk.yellow('Could not open browser automatically.'));
41
+ console.log(chalk.yellow('Please open the URL above manually.'));
42
+ }
43
+ const spinner = ora('Waiting for authentication...').start();
44
+ try {
45
+ const tokens = await result;
46
+ // Save credentials
47
+ await saveCredentials({
48
+ provider: 'google', // web login handles provider selection
49
+ access_token: tokens.access_token,
50
+ refresh_token: tokens.refresh_token,
51
+ logged_in_at: new Date().toISOString(),
52
+ });
53
+ spinner.text = 'Fetching user info...';
54
+ const userRes = await apiGet(CONFIG.CLAW_ME, tokens.access_token);
55
+ spinner.stop();
56
+ if (userRes.ok) {
57
+ const user = userRes.data.data;
58
+ const name = user.name || user.email || 'user';
59
+ console.log(chalk.green(`\nLogged in as ${chalk.bold(name)}`));
60
+ if (user.email) {
61
+ console.log(chalk.gray(`Email: ${user.email}`));
62
+ }
63
+ }
64
+ else {
65
+ console.log(chalk.green('\nLogin successful!'));
66
+ console.log(chalk.gray('Run `claw whoami` to see your account info.'));
67
+ }
68
+ }
69
+ catch (err) {
70
+ spinner.stop();
71
+ close();
72
+ const message = err instanceof Error ? err.message : 'Unknown error';
73
+ console.error(chalk.red(`\nLogin failed: ${message}`));
74
+ process.exit(1);
75
+ }
76
+ }
77
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"login.js","sourceRoot":"","sources":["../../src/commands/login.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function logoutCommand(): Promise<void>;