tinyagent 1.0.5 → 1.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 +63 -6
- package/dist/auth-client.d.ts +14 -0
- package/dist/auth-client.d.ts.map +1 -0
- package/dist/auth-client.js +165 -0
- package/dist/auth-client.js.map +1 -0
- package/dist/cli.js +36 -29
- package/dist/cli.js.map +1 -1
- package/dist/firebase-auth-simple.d.ts +21 -0
- package/dist/firebase-auth-simple.d.ts.map +1 -0
- package/dist/firebase-auth-simple.js +218 -0
- package/dist/firebase-auth-simple.js.map +1 -0
- package/dist/firebase-auth.d.ts +22 -0
- package/dist/firebase-auth.d.ts.map +1 -0
- package/dist/firebase-auth.js +211 -0
- package/dist/firebase-auth.js.map +1 -0
- package/dist/qr-generator.d.ts.map +1 -1
- package/dist/qr-generator.js +1 -3
- package/dist/qr-generator.js.map +1 -1
- package/dist/shared-types/messages.d.ts +40 -2
- package/dist/shared-types/messages.d.ts.map +1 -1
- package/dist/shared-types/messages.js +5 -0
- package/dist/shared-types/messages.js.map +1 -1
- package/dist/shared-types/session.d.ts +1 -0
- package/dist/shared-types/session.d.ts.map +1 -1
- package/dist/shell-client-v2.d.ts +27 -0
- package/dist/shell-client-v2.d.ts.map +1 -1
- package/dist/shell-client-v2.js +466 -84
- package/dist/shell-client-v2.js.map +1 -1
- package/package.json +13 -4
package/README.md
CHANGED
|
@@ -24,7 +24,7 @@ tinyagent my-session
|
|
|
24
24
|
|
|
25
25
|
3. Your local terminal is now accessible from your phone!
|
|
26
26
|
|
|
27
|
-
**Bonus**: If you have Claude CLI installed, it will automatically start Claude for you!
|
|
27
|
+
**Bonus**: If you have Claude CLI installed, it will automatically start Claude for you! Claude will be able to see and interact with your terminal session.
|
|
28
28
|
|
|
29
29
|
## Features
|
|
30
30
|
|
|
@@ -64,24 +64,53 @@ tinyagent --shell /bin/zsh
|
|
|
64
64
|
tinyagent --no-claude
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
-
###
|
|
68
|
-
|
|
67
|
+
### Advanced: Run a server alongside your shell
|
|
68
|
+
```bash
|
|
69
|
+
tinyagent -c "npm run dev" -p 3000
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
This runs a dev server in parallel with your shell session (legacy feature - HTTP tunneling now auto-detects running servers).
|
|
73
|
+
|
|
74
|
+
### All Options:
|
|
75
|
+
- `[sessionId]` - Session ID as first argument (optional, auto-generated if not provided)
|
|
76
|
+
- `--session-id <id>` - Session ID (alternative to positional argument)
|
|
69
77
|
- `-r, --relay <url>` - Relay server URL (default: wss://relay.tinyagent.app)
|
|
70
78
|
- `-s, --shell <shell>` - Shell to use (default: $SHELL or /bin/bash)
|
|
79
|
+
- `-c, --command <cmd>` - Run a server command in parallel (legacy - auto-detection preferred)
|
|
80
|
+
- `-p, --port <port>` - Port for the server command (default: 3000)
|
|
81
|
+
- `--no-tunnel` - Disable localtunnel integration
|
|
71
82
|
- `--no-claude` - Do not auto-start Claude CLI
|
|
72
83
|
- `-v, --verbose` - Show detailed debug output
|
|
84
|
+
- `-V, --version` - Show version number
|
|
85
|
+
- `-h, --help` - Show help
|
|
73
86
|
|
|
74
87
|
## HTTP Tunneling
|
|
75
88
|
|
|
76
|
-
|
|
89
|
+
Tinyagent automatically detects and exposes your local development servers! No configuration needed.
|
|
90
|
+
|
|
91
|
+
When you have servers running on common ports (3000, 3001, 4000, 4200, 5000, 5173, 8000, 8080, 8081, 9000), you'll see:
|
|
77
92
|
|
|
78
93
|
```
|
|
79
|
-
[HTTP] Exposing ports: 3000
|
|
94
|
+
[HTTP] Exposing ports: 3000, 5173
|
|
80
95
|
[HTTP] Access your dev server at:
|
|
81
96
|
https://my-session-3000.tinyagent.app/
|
|
97
|
+
https://my-session-5173.tinyagent.app/
|
|
82
98
|
```
|
|
83
99
|
|
|
84
|
-
|
|
100
|
+
Access your local dev servers from any browser - perfect for testing on mobile devices!
|
|
101
|
+
|
|
102
|
+
## Environment Variables
|
|
103
|
+
|
|
104
|
+
You can set these environment variables to customize default behavior:
|
|
105
|
+
|
|
106
|
+
- `RELAY_URL` - Default relay server URL (instead of using `-r` flag each time)
|
|
107
|
+
- `SHELL` - Default shell to use (automatically detected from your system)
|
|
108
|
+
|
|
109
|
+
Example:
|
|
110
|
+
```bash
|
|
111
|
+
export RELAY_URL=ws://localhost:8080
|
|
112
|
+
tinyagent
|
|
113
|
+
```
|
|
85
114
|
|
|
86
115
|
## Security
|
|
87
116
|
|
|
@@ -89,6 +118,34 @@ You can access your local dev server from any browser using this URL.
|
|
|
89
118
|
- Session IDs are private - only those with the ID can connect
|
|
90
119
|
- Connections are encrypted with WSS (WebSocket Secure)
|
|
91
120
|
|
|
121
|
+
## Troubleshooting
|
|
122
|
+
|
|
123
|
+
### Debug Mode
|
|
124
|
+
If you're experiencing issues, run with verbose output:
|
|
125
|
+
```bash
|
|
126
|
+
tinyagent -v
|
|
127
|
+
# or
|
|
128
|
+
tinyagent --verbose
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
This will show:
|
|
132
|
+
- WebSocket connection details
|
|
133
|
+
- Terminal resize events
|
|
134
|
+
- Input source tracking (local vs mobile)
|
|
135
|
+
- HTTP port detection
|
|
136
|
+
|
|
137
|
+
### Common Issues
|
|
138
|
+
|
|
139
|
+
**Claude not starting automatically?**
|
|
140
|
+
- Make sure Claude CLI is installed and in your PATH
|
|
141
|
+
- Try running `which claude` to verify installation
|
|
142
|
+
- Use `--no-claude` to disable auto-start
|
|
143
|
+
|
|
144
|
+
**Dev server not accessible?**
|
|
145
|
+
- Ensure your server is running on a standard port (3000, 3001, 4000, 4200, 5000, 5173, 8000, 8080, 8081, 9000)
|
|
146
|
+
- Check that the server is bound to `localhost` or `0.0.0.0`, not just `127.0.0.1`
|
|
147
|
+
- Look for the `[HTTP] Exposing ports:` message in the output
|
|
148
|
+
|
|
92
149
|
## Learn More
|
|
93
150
|
|
|
94
151
|
- Website: https://tinyagent.app
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export declare class AuthClient {
|
|
2
|
+
private authUrl;
|
|
3
|
+
private configPath;
|
|
4
|
+
private token?;
|
|
5
|
+
constructor(authUrl?: string);
|
|
6
|
+
private loadToken;
|
|
7
|
+
private saveToken;
|
|
8
|
+
isAuthenticated(): boolean;
|
|
9
|
+
getToken(): string | undefined;
|
|
10
|
+
authenticate(): Promise<boolean>;
|
|
11
|
+
logout(): void;
|
|
12
|
+
whoami(): void;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=auth-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-client.d.ts","sourceRoot":"","sources":["../src/auth-client.ts"],"names":[],"mappings":"AAkBA,qBAAa,UAAU;IACrB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,KAAK,CAAC,CAAY;gBAEd,OAAO,GAAE,MAAqC;IAM1D,OAAO,CAAC,SAAS;IAWjB,OAAO,CAAC,SAAS;IAaV,eAAe,IAAI,OAAO;IAQ1B,QAAQ,IAAI,MAAM,GAAG,SAAS;IAIxB,YAAY,IAAI,OAAO,CAAC,OAAO,CAAC;IA+FtC,MAAM,IAAI,IAAI;IAYd,MAAM,IAAI,IAAI;CAiBtB"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.AuthClient = void 0;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const path_1 = __importDefault(require("path"));
|
|
11
|
+
const os_1 = __importDefault(require("os"));
|
|
12
|
+
const open_1 = __importDefault(require("open"));
|
|
13
|
+
class AuthClient {
|
|
14
|
+
authUrl;
|
|
15
|
+
configPath;
|
|
16
|
+
token;
|
|
17
|
+
constructor(authUrl = 'https://auth.tinyagent.app') {
|
|
18
|
+
this.authUrl = process.env.AUTH_URL || authUrl;
|
|
19
|
+
this.configPath = path_1.default.join(os_1.default.homedir(), '.tinyagent', 'auth.json');
|
|
20
|
+
this.loadToken();
|
|
21
|
+
}
|
|
22
|
+
loadToken() {
|
|
23
|
+
try {
|
|
24
|
+
if (fs_1.default.existsSync(this.configPath)) {
|
|
25
|
+
const data = fs_1.default.readFileSync(this.configPath, 'utf-8');
|
|
26
|
+
this.token = JSON.parse(data);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
// Ignore errors, token will be undefined
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
saveToken(token) {
|
|
34
|
+
try {
|
|
35
|
+
const dir = path_1.default.dirname(this.configPath);
|
|
36
|
+
if (!fs_1.default.existsSync(dir)) {
|
|
37
|
+
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
38
|
+
}
|
|
39
|
+
fs_1.default.writeFileSync(this.configPath, JSON.stringify(token, null, 2));
|
|
40
|
+
this.token = token;
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
console.error(chalk_1.default.yellow('Warning: Could not save auth token'));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
isAuthenticated() {
|
|
47
|
+
if (!this.token)
|
|
48
|
+
return false;
|
|
49
|
+
if (this.token.expiresAt && this.token.expiresAt < Date.now()) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
getToken() {
|
|
55
|
+
return this.isAuthenticated() ? this.token?.accessToken : undefined;
|
|
56
|
+
}
|
|
57
|
+
async authenticate() {
|
|
58
|
+
console.log(chalk_1.default.cyan('\n🔐 Authentication required'));
|
|
59
|
+
// Generate PKCE challenge
|
|
60
|
+
const codeVerifier = crypto_1.default.randomBytes(32).toString('base64url');
|
|
61
|
+
const codeChallenge = crypto_1.default
|
|
62
|
+
.createHash('sha256')
|
|
63
|
+
.update(codeVerifier)
|
|
64
|
+
.digest('base64url');
|
|
65
|
+
try {
|
|
66
|
+
// Request device code
|
|
67
|
+
const deviceResponse = await fetch(`${this.authUrl}/api/auth/device`, {
|
|
68
|
+
method: 'POST',
|
|
69
|
+
headers: { 'Content-Type': 'application/json' },
|
|
70
|
+
body: JSON.stringify({ codeChallenge }),
|
|
71
|
+
});
|
|
72
|
+
if (!deviceResponse.ok) {
|
|
73
|
+
throw new Error('Failed to get device code');
|
|
74
|
+
}
|
|
75
|
+
const deviceData = await deviceResponse.json();
|
|
76
|
+
console.log(chalk_1.default.white('\nTo authenticate, visit:'));
|
|
77
|
+
console.log(chalk_1.default.green.bold(deviceData.verificationUrl));
|
|
78
|
+
console.log(chalk_1.default.white('\nVerification code: ') + chalk_1.default.yellow.bold(deviceData.userCode));
|
|
79
|
+
// Open browser
|
|
80
|
+
try {
|
|
81
|
+
await (0, open_1.default)(deviceData.verificationUrl);
|
|
82
|
+
console.log(chalk_1.default.gray('(Browser should open automatically)'));
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
console.log(chalk_1.default.gray('(Please open the URL manually in your browser)'));
|
|
86
|
+
}
|
|
87
|
+
console.log(chalk_1.default.gray('\nWaiting for authentication...'));
|
|
88
|
+
// Poll for authentication
|
|
89
|
+
const startTime = Date.now();
|
|
90
|
+
const expiresIn = deviceData.expiresIn * 1000; // Convert to ms
|
|
91
|
+
const interval = deviceData.interval * 1000; // Convert to ms
|
|
92
|
+
while (Date.now() - startTime < expiresIn) {
|
|
93
|
+
await new Promise(resolve => setTimeout(resolve, interval));
|
|
94
|
+
const pollResponse = await fetch(`${this.authUrl}/api/auth/cli?device_code=${deviceData.deviceCode}`);
|
|
95
|
+
if (!pollResponse.ok)
|
|
96
|
+
continue;
|
|
97
|
+
const pollData = await pollResponse.json();
|
|
98
|
+
if (pollData.status === 'authenticated') {
|
|
99
|
+
// Exchange for tokens
|
|
100
|
+
const tokenResponse = await fetch(`${this.authUrl}/api/auth/token`, {
|
|
101
|
+
method: 'POST',
|
|
102
|
+
headers: { 'Content-Type': 'application/json' },
|
|
103
|
+
body: JSON.stringify({
|
|
104
|
+
grantType: 'authorization_code',
|
|
105
|
+
code: pollData.accessToken, // Using token as code for simplicity
|
|
106
|
+
codeVerifier,
|
|
107
|
+
}),
|
|
108
|
+
});
|
|
109
|
+
if (!tokenResponse.ok) {
|
|
110
|
+
throw new Error('Failed to exchange code for tokens');
|
|
111
|
+
}
|
|
112
|
+
const tokenData = await tokenResponse.json();
|
|
113
|
+
// Save token
|
|
114
|
+
this.saveToken({
|
|
115
|
+
accessToken: tokenData.accessToken,
|
|
116
|
+
refreshToken: tokenData.refreshToken,
|
|
117
|
+
user: tokenData.user,
|
|
118
|
+
expiresAt: Date.now() + (tokenData.expiresIn * 1000),
|
|
119
|
+
});
|
|
120
|
+
console.log(chalk_1.default.green('\n✓ Authentication successful!'));
|
|
121
|
+
if (tokenData.user?.email) {
|
|
122
|
+
console.log(chalk_1.default.gray(`Logged in as: ${tokenData.user.email}`));
|
|
123
|
+
}
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
console.log(chalk_1.default.red('\n✗ Authentication timed out'));
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
console.error(chalk_1.default.red('\n✗ Authentication failed:'), error);
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
logout() {
|
|
136
|
+
try {
|
|
137
|
+
if (fs_1.default.existsSync(this.configPath)) {
|
|
138
|
+
fs_1.default.unlinkSync(this.configPath);
|
|
139
|
+
}
|
|
140
|
+
this.token = undefined;
|
|
141
|
+
console.log(chalk_1.default.green('✓ Logged out successfully'));
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
console.error(chalk_1.default.red('✗ Logout failed:'), error);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
whoami() {
|
|
148
|
+
if (!this.isAuthenticated()) {
|
|
149
|
+
console.log(chalk_1.default.yellow('Not authenticated. Run "tinyagent login" to authenticate.'));
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (this.token?.user) {
|
|
153
|
+
console.log(chalk_1.default.cyan('Current user:'));
|
|
154
|
+
console.log(` ID: ${this.token.user.id}`);
|
|
155
|
+
if (this.token.user.email) {
|
|
156
|
+
console.log(` Email: ${this.token.user.email}`);
|
|
157
|
+
}
|
|
158
|
+
if (this.token.user.name) {
|
|
159
|
+
console.log(` Name: ${this.token.user.name}`);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
exports.AuthClient = AuthClient;
|
|
165
|
+
//# sourceMappingURL=auth-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-client.js","sourceRoot":"","sources":["../src/auth-client.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -7,37 +7,41 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
7
7
|
const commander_1 = require("commander");
|
|
8
8
|
const chalk_1 = __importDefault(require("chalk"));
|
|
9
9
|
const dotenv_1 = require("dotenv");
|
|
10
|
-
const child_process_1 = require("child_process");
|
|
11
10
|
const shell_client_v2_1 = require("./shell-client-v2");
|
|
12
11
|
const qr_generator_1 = require("./qr-generator");
|
|
12
|
+
const firebase_auth_simple_1 = require("./firebase-auth-simple");
|
|
13
13
|
(0, dotenv_1.config)();
|
|
14
|
-
// Function to check if Claude is available and start it
|
|
15
|
-
function checkAndStartClaude(sessionId) {
|
|
16
|
-
try {
|
|
17
|
-
// Check if claude command exists
|
|
18
|
-
(0, child_process_1.execSync)('which claude', { stdio: 'ignore' });
|
|
19
|
-
console.log(chalk_1.default.green('\n✓ Claude detected - starting Claude session...'));
|
|
20
|
-
// Start claude in the background
|
|
21
|
-
const claudeProcess = (0, child_process_1.spawn)('claude', [], {
|
|
22
|
-
detached: true,
|
|
23
|
-
stdio: 'ignore'
|
|
24
|
-
});
|
|
25
|
-
claudeProcess.unref();
|
|
26
|
-
console.log(chalk_1.default.gray('Claude is running in the background'));
|
|
27
|
-
console.log(chalk_1.default.yellow(`\nTip: You can now ask Claude to connect to your shell session "${sessionId}"`));
|
|
28
|
-
}
|
|
29
|
-
catch (error) {
|
|
30
|
-
// Claude not found or failed to start - silently continue
|
|
31
|
-
if (process.env.DEBUG || process.argv.includes('--verbose')) {
|
|
32
|
-
console.log(chalk_1.default.gray('Claude not found or could not be started'));
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
14
|
const program = new commander_1.Command();
|
|
15
|
+
// Authentication commands
|
|
16
|
+
program
|
|
17
|
+
.command('login')
|
|
18
|
+
.description('Authenticate with Tinyagent')
|
|
19
|
+
.action(async () => {
|
|
20
|
+
const authClient = new firebase_auth_simple_1.FirebaseAuthClient();
|
|
21
|
+
const success = await authClient.authenticate();
|
|
22
|
+
process.exit(success ? 0 : 1);
|
|
23
|
+
});
|
|
24
|
+
program
|
|
25
|
+
.command('logout')
|
|
26
|
+
.description('Log out from Tinyagent')
|
|
27
|
+
.action(() => {
|
|
28
|
+
const authClient = new firebase_auth_simple_1.FirebaseAuthClient();
|
|
29
|
+
authClient.logout();
|
|
30
|
+
process.exit(0);
|
|
31
|
+
});
|
|
32
|
+
program
|
|
33
|
+
.command('whoami')
|
|
34
|
+
.description('Display current user information')
|
|
35
|
+
.action(() => {
|
|
36
|
+
const authClient = new firebase_auth_simple_1.FirebaseAuthClient();
|
|
37
|
+
authClient.whoami();
|
|
38
|
+
process.exit(0);
|
|
39
|
+
});
|
|
40
|
+
// Main shell command
|
|
37
41
|
program
|
|
38
42
|
.name('tinyagent')
|
|
39
43
|
.description('Connect your local shell to any device')
|
|
40
|
-
.version('1.0
|
|
44
|
+
.version('1.2.0')
|
|
41
45
|
.argument('[sessionId]', 'Session ID to connect to (optional, auto-generated if not provided)')
|
|
42
46
|
.option('--session-id <id>', 'Session ID (alternative to positional argument)')
|
|
43
47
|
.option('-r, --relay <url>', 'Relay server URL', process.env.RELAY_URL || 'wss://relay.tinyagent.app')
|
|
@@ -47,6 +51,9 @@ program
|
|
|
47
51
|
.option('--no-tunnel', 'Disable tunnel creation')
|
|
48
52
|
.option('--no-claude', 'Do not auto-start Claude')
|
|
49
53
|
.option('-v, --verbose', 'Show detailed debug output')
|
|
54
|
+
.option('--password <password>', 'Password required for mobile clients to connect')
|
|
55
|
+
.option('--resume', 'Resume the last Claude session')
|
|
56
|
+
.option('--continue', 'Continue the last Claude session (alias for --resume)')
|
|
50
57
|
.action(async (sessionIdArg, options) => {
|
|
51
58
|
// Determine session ID from argument or option, or generate one
|
|
52
59
|
let sessionId = sessionIdArg || options.sessionId;
|
|
@@ -65,13 +72,13 @@ program
|
|
|
65
72
|
shell: options.shell,
|
|
66
73
|
serverCommand: options.command,
|
|
67
74
|
serverPort: parseInt(options.port),
|
|
68
|
-
createTunnel: options.tunnel
|
|
75
|
+
createTunnel: options.tunnel,
|
|
76
|
+
autoStartClaude: !options.noClaude,
|
|
77
|
+
password: options.password,
|
|
78
|
+
resumeClaude: options.resume || options.continue,
|
|
79
|
+
verbose: options.verbose
|
|
69
80
|
});
|
|
70
81
|
await client.connect();
|
|
71
|
-
// Auto-start Claude if available and not disabled
|
|
72
|
-
if (!options.noClaude) {
|
|
73
|
-
checkAndStartClaude(sessionId);
|
|
74
|
-
}
|
|
75
82
|
process.on('SIGINT', () => {
|
|
76
83
|
console.log(chalk_1.default.yellow('\nDisconnecting...'));
|
|
77
84
|
client.disconnect();
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;;;;;AAEA,yCAAoC;AACpC,kDAA0B;AAC1B,mCAAgC;AAChC,
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
interface FirebaseUser {
|
|
2
|
+
uid: string;
|
|
3
|
+
email?: string | null;
|
|
4
|
+
displayName?: string | null;
|
|
5
|
+
photoURL?: string | null;
|
|
6
|
+
}
|
|
7
|
+
export declare class FirebaseAuthClient {
|
|
8
|
+
private configPath;
|
|
9
|
+
private token?;
|
|
10
|
+
constructor();
|
|
11
|
+
private loadToken;
|
|
12
|
+
private saveToken;
|
|
13
|
+
isAuthenticated(): boolean;
|
|
14
|
+
getToken(): string | undefined;
|
|
15
|
+
getUser(): FirebaseUser | undefined;
|
|
16
|
+
authenticate(): Promise<boolean>;
|
|
17
|
+
logout(): void;
|
|
18
|
+
whoami(): void;
|
|
19
|
+
}
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=firebase-auth-simple.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"firebase-auth-simple.d.ts","sourceRoot":"","sources":["../src/firebase-auth-simple.ts"],"names":[],"mappings":"AAQA,UAAU,YAAY;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AASD,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,KAAK,CAAC,CAAY;;IAO1B,OAAO,CAAC,SAAS;IAWjB,OAAO,CAAC,SAAS;IAaV,eAAe,IAAI,OAAO;IAQ1B,QAAQ,IAAI,MAAM,GAAG,SAAS;IAI9B,OAAO,IAAI,YAAY,GAAG,SAAS;IAI7B,YAAY,IAAI,OAAO,CAAC,OAAO,CAAC;IA0ItC,MAAM,IAAI,IAAI;IAYd,MAAM,IAAI,IAAI;CAkBtB"}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.FirebaseAuthClient = void 0;
|
|
7
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const os_1 = __importDefault(require("os"));
|
|
11
|
+
const open_1 = __importDefault(require("open"));
|
|
12
|
+
const http_1 = __importDefault(require("http"));
|
|
13
|
+
const url_1 = require("url");
|
|
14
|
+
class FirebaseAuthClient {
|
|
15
|
+
configPath;
|
|
16
|
+
token;
|
|
17
|
+
constructor() {
|
|
18
|
+
this.configPath = path_1.default.join(os_1.default.homedir(), '.tinyagent', 'auth.json');
|
|
19
|
+
this.loadToken();
|
|
20
|
+
}
|
|
21
|
+
loadToken() {
|
|
22
|
+
try {
|
|
23
|
+
if (fs_1.default.existsSync(this.configPath)) {
|
|
24
|
+
const data = fs_1.default.readFileSync(this.configPath, 'utf-8');
|
|
25
|
+
this.token = JSON.parse(data);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
// Ignore errors, token will be undefined
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
saveToken(token) {
|
|
33
|
+
try {
|
|
34
|
+
const dir = path_1.default.dirname(this.configPath);
|
|
35
|
+
if (!fs_1.default.existsSync(dir)) {
|
|
36
|
+
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
37
|
+
}
|
|
38
|
+
fs_1.default.writeFileSync(this.configPath, JSON.stringify(token, null, 2));
|
|
39
|
+
this.token = token;
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
console.error(chalk_1.default.yellow('Warning: Could not save auth token'));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
isAuthenticated() {
|
|
46
|
+
if (!this.token)
|
|
47
|
+
return false;
|
|
48
|
+
if (this.token.expiresAt && this.token.expiresAt < Date.now()) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
getToken() {
|
|
54
|
+
return this.isAuthenticated() ? this.token?.idToken : undefined;
|
|
55
|
+
}
|
|
56
|
+
getUser() {
|
|
57
|
+
return this.isAuthenticated() ? this.token?.user : undefined;
|
|
58
|
+
}
|
|
59
|
+
async authenticate() {
|
|
60
|
+
console.log(chalk_1.default.cyan('\n🔐 Authenticating with Firebase'));
|
|
61
|
+
try {
|
|
62
|
+
// Create local server to receive the callback
|
|
63
|
+
const server = http_1.default.createServer();
|
|
64
|
+
const port = 9005;
|
|
65
|
+
return new Promise((resolve) => {
|
|
66
|
+
let resolved = false;
|
|
67
|
+
server.on('request', async (req, res) => {
|
|
68
|
+
const url = new url_1.URL(req.url, `http://localhost:${port}`);
|
|
69
|
+
if (url.pathname === '/auth/callback') {
|
|
70
|
+
const idToken = url.searchParams.get('token');
|
|
71
|
+
const uid = url.searchParams.get('uid');
|
|
72
|
+
const email = url.searchParams.get('email');
|
|
73
|
+
const name = url.searchParams.get('name');
|
|
74
|
+
if (idToken && uid) {
|
|
75
|
+
try {
|
|
76
|
+
// Save token
|
|
77
|
+
this.saveToken({
|
|
78
|
+
idToken,
|
|
79
|
+
user: {
|
|
80
|
+
uid,
|
|
81
|
+
email: email || null,
|
|
82
|
+
displayName: name || null,
|
|
83
|
+
photoURL: null,
|
|
84
|
+
},
|
|
85
|
+
expiresAt: Date.now() + (60 * 60 * 1000), // 1 hour
|
|
86
|
+
});
|
|
87
|
+
// Send success response
|
|
88
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
89
|
+
res.end(`
|
|
90
|
+
<!DOCTYPE html>
|
|
91
|
+
<html>
|
|
92
|
+
<head>
|
|
93
|
+
<title>Authentication Successful</title>
|
|
94
|
+
<style>
|
|
95
|
+
body {
|
|
96
|
+
font-family: -apple-system, system-ui, sans-serif;
|
|
97
|
+
display: flex;
|
|
98
|
+
justify-content: center;
|
|
99
|
+
align-items: center;
|
|
100
|
+
height: 100vh;
|
|
101
|
+
margin: 0;
|
|
102
|
+
background: #000;
|
|
103
|
+
color: #0f0;
|
|
104
|
+
}
|
|
105
|
+
.success { text-align: center; }
|
|
106
|
+
h1 { font-size: 48px; margin: 0; }
|
|
107
|
+
p { color: #888; margin-top: 10px; }
|
|
108
|
+
</style>
|
|
109
|
+
</head>
|
|
110
|
+
<body>
|
|
111
|
+
<div class="success">
|
|
112
|
+
<h1>✓</h1>
|
|
113
|
+
<p>Authentication successful! You can close this window.</p>
|
|
114
|
+
</div>
|
|
115
|
+
<script>setTimeout(() => window.close(), 2000)</script>
|
|
116
|
+
</body>
|
|
117
|
+
</html>
|
|
118
|
+
`);
|
|
119
|
+
console.log(chalk_1.default.green('\n✓ Authentication successful!'));
|
|
120
|
+
if (email) {
|
|
121
|
+
console.log(chalk_1.default.gray(`Logged in as: ${email}`));
|
|
122
|
+
}
|
|
123
|
+
if (!resolved) {
|
|
124
|
+
resolved = true;
|
|
125
|
+
server.close();
|
|
126
|
+
resolve(true);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
console.error(chalk_1.default.red('Failed to process authentication'));
|
|
131
|
+
res.writeHead(400);
|
|
132
|
+
res.end('Authentication failed');
|
|
133
|
+
if (!resolved) {
|
|
134
|
+
resolved = true;
|
|
135
|
+
server.close();
|
|
136
|
+
resolve(false);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
res.writeHead(400);
|
|
142
|
+
res.end('Missing authentication data');
|
|
143
|
+
if (!resolved) {
|
|
144
|
+
resolved = true;
|
|
145
|
+
server.close();
|
|
146
|
+
resolve(false);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
res.writeHead(404);
|
|
152
|
+
res.end('Not found');
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
server.listen(port, async () => {
|
|
156
|
+
// For now, we'll use a simple auth page hosted on Firebase
|
|
157
|
+
// In production, this would be your Firebase Hosting URL
|
|
158
|
+
const authUrl = `https://tinyagent-fab3c.firebaseapp.com/auth/cli?redirect=http://localhost:${port}/auth/callback`;
|
|
159
|
+
console.log(chalk_1.default.white('\nTo authenticate, visit:'));
|
|
160
|
+
console.log(chalk_1.default.green.bold(authUrl));
|
|
161
|
+
// Open browser
|
|
162
|
+
try {
|
|
163
|
+
await (0, open_1.default)(authUrl);
|
|
164
|
+
console.log(chalk_1.default.gray('\n(Browser should open automatically)'));
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
console.log(chalk_1.default.gray('\n(Please open the URL manually in your browser)'));
|
|
168
|
+
}
|
|
169
|
+
console.log(chalk_1.default.gray('\nWaiting for authentication...'));
|
|
170
|
+
});
|
|
171
|
+
// Timeout after 5 minutes
|
|
172
|
+
setTimeout(() => {
|
|
173
|
+
if (server.listening && !resolved) {
|
|
174
|
+
console.log(chalk_1.default.red('\n✗ Authentication timed out'));
|
|
175
|
+
resolved = true;
|
|
176
|
+
server.close();
|
|
177
|
+
resolve(false);
|
|
178
|
+
}
|
|
179
|
+
}, 5 * 60 * 1000);
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
console.error(chalk_1.default.red('\n✗ Authentication failed:'), error);
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
logout() {
|
|
188
|
+
try {
|
|
189
|
+
if (fs_1.default.existsSync(this.configPath)) {
|
|
190
|
+
fs_1.default.unlinkSync(this.configPath);
|
|
191
|
+
}
|
|
192
|
+
this.token = undefined;
|
|
193
|
+
console.log(chalk_1.default.green('✓ Logged out successfully'));
|
|
194
|
+
}
|
|
195
|
+
catch (error) {
|
|
196
|
+
console.error(chalk_1.default.red('✗ Logout failed:'), error);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
whoami() {
|
|
200
|
+
if (!this.isAuthenticated()) {
|
|
201
|
+
console.log(chalk_1.default.yellow('Not authenticated. Run "tinyagent login" to authenticate.'));
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
const user = this.getUser();
|
|
205
|
+
if (user) {
|
|
206
|
+
console.log(chalk_1.default.cyan('Current user:'));
|
|
207
|
+
console.log(` UID: ${user.uid}`);
|
|
208
|
+
if (user.email) {
|
|
209
|
+
console.log(` Email: ${user.email}`);
|
|
210
|
+
}
|
|
211
|
+
if (user.displayName) {
|
|
212
|
+
console.log(` Name: ${user.displayName}`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
exports.FirebaseAuthClient = FirebaseAuthClient;
|
|
218
|
+
//# sourceMappingURL=firebase-auth-simple.js.map
|