@promptcellar/pc 0.2.0 → 0.3.1
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/bin/pc.js +3 -2
- package/package.json +1 -1
- package/src/commands/login.js +106 -41
package/bin/pc.js
CHANGED
|
@@ -13,11 +13,12 @@ import { update } from '../src/commands/update.js';
|
|
|
13
13
|
program
|
|
14
14
|
.name('pc')
|
|
15
15
|
.description('PromptCellar CLI - sync prompts between your terminal and the cloud')
|
|
16
|
-
.version('0.1
|
|
16
|
+
.version('0.3.1');
|
|
17
17
|
|
|
18
18
|
program
|
|
19
19
|
.command('login')
|
|
20
|
-
.description('Authenticate with PromptCellar')
|
|
20
|
+
.description('Authenticate with PromptCellar via browser')
|
|
21
|
+
.option('-u, --url <url>', 'API URL (for self-hosted instances)')
|
|
21
22
|
.action(login);
|
|
22
23
|
|
|
23
24
|
program
|
package/package.json
CHANGED
package/src/commands/login.js
CHANGED
|
@@ -1,56 +1,121 @@
|
|
|
1
|
-
import inquirer from 'inquirer';
|
|
2
1
|
import ora from 'ora';
|
|
3
2
|
import chalk from 'chalk';
|
|
4
3
|
import { setApiKey, setApiUrl, isLoggedIn } from '../lib/config.js';
|
|
5
|
-
import { testConnection } from '../lib/api.js';
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
return;
|
|
19
|
-
}
|
|
5
|
+
const DEFAULT_API_URL = 'https://prompts.weldedanvil.com';
|
|
6
|
+
|
|
7
|
+
async function initiateDeviceAuth(apiUrl) {
|
|
8
|
+
const response = await fetch(`${apiUrl}/auth/device`, {
|
|
9
|
+
method: 'POST',
|
|
10
|
+
headers: { 'Content-Type': 'application/json' },
|
|
11
|
+
body: JSON.stringify({ device_name: getDeviceName() })
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
if (!response.ok) {
|
|
15
|
+
throw new Error('Failed to initiate device authentication');
|
|
20
16
|
}
|
|
21
17
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
18
|
+
return response.json();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function pollForApproval(apiUrl, deviceCode, interval, expiresIn) {
|
|
22
|
+
const startTime = Date.now();
|
|
23
|
+
const expiresAt = startTime + (expiresIn * 1000);
|
|
24
|
+
|
|
25
|
+
while (Date.now() < expiresAt) {
|
|
26
|
+
await sleep(interval * 1000);
|
|
27
|
+
|
|
28
|
+
const response = await fetch(`${apiUrl}/auth/device/poll`, {
|
|
29
|
+
method: 'POST',
|
|
30
|
+
headers: { 'Content-Type': 'application/json' },
|
|
31
|
+
body: JSON.stringify({ device_code: deviceCode })
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const data = await response.json();
|
|
35
|
+
|
|
36
|
+
if (response.ok && data.access_token) {
|
|
37
|
+
return data;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (data.error === 'authorization_pending') {
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (data.error === 'expired_token') {
|
|
45
|
+
throw new Error('Authorization request expired. Please try again.');
|
|
38
46
|
}
|
|
39
|
-
]);
|
|
40
47
|
|
|
41
|
-
|
|
48
|
+
if (data.error === 'access_denied') {
|
|
49
|
+
throw new Error('Authorization denied.');
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
throw new Error('Authorization request timed out. Please try again.');
|
|
54
|
+
}
|
|
42
55
|
|
|
43
|
-
|
|
44
|
-
|
|
56
|
+
function getDeviceName() {
|
|
57
|
+
const os = process.platform;
|
|
58
|
+
const hostname = process.env.HOSTNAME || process.env.COMPUTERNAME || 'CLI';
|
|
45
59
|
|
|
46
|
-
const
|
|
60
|
+
const osNames = {
|
|
61
|
+
darwin: 'macOS',
|
|
62
|
+
linux: 'Linux',
|
|
63
|
+
win32: 'Windows'
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
return `${osNames[os] || os} - ${hostname}`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function sleep(ms) {
|
|
70
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export async function login(options) {
|
|
74
|
+
const apiUrl = options?.url || DEFAULT_API_URL;
|
|
75
|
+
|
|
76
|
+
if (isLoggedIn()) {
|
|
77
|
+
console.log(chalk.yellow('Already logged in.'));
|
|
78
|
+
console.log('Run ' + chalk.cyan('pc logout') + ' first to switch accounts.\n');
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
console.log(chalk.bold('\nPromptCellar CLI Login\n'));
|
|
83
|
+
|
|
84
|
+
const spinner = ora('Connecting...').start();
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
// Step 1: Request device code
|
|
88
|
+
const authData = await initiateDeviceAuth(apiUrl);
|
|
89
|
+
spinner.stop();
|
|
90
|
+
|
|
91
|
+
// Step 2: Show login URL and code
|
|
92
|
+
const verifyUrl = `${authData.verification_url}?code=${authData.user_code}`;
|
|
93
|
+
|
|
94
|
+
console.log('Open this URL in your browser to log in:\n');
|
|
95
|
+
console.log(chalk.cyan.bold(` ${verifyUrl}\n`));
|
|
96
|
+
console.log(chalk.dim(`Or go to ${authData.verification_url} and enter code: ${authData.user_code}\n`));
|
|
97
|
+
|
|
98
|
+
// Step 3: Poll for approval
|
|
99
|
+
spinner.start('Waiting for you to authorize in browser...');
|
|
100
|
+
|
|
101
|
+
const result = await pollForApproval(
|
|
102
|
+
apiUrl,
|
|
103
|
+
authData.device_code,
|
|
104
|
+
authData.interval,
|
|
105
|
+
authData.expires_in
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
// Step 4: Save credentials
|
|
109
|
+
setApiKey(result.access_token);
|
|
110
|
+
setApiUrl(apiUrl);
|
|
47
111
|
|
|
48
|
-
if (result.success) {
|
|
49
112
|
spinner.succeed(chalk.green('Logged in successfully!'));
|
|
50
|
-
console.log(
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
113
|
+
console.log(`\nWelcome, ${chalk.cyan(result.user_email)}!`);
|
|
114
|
+
console.log('\nRun ' + chalk.cyan('pc setup') + ' to configure auto-capture for your CLI tools.\n');
|
|
115
|
+
|
|
116
|
+
} catch (error) {
|
|
117
|
+
spinner.fail(chalk.red('Login failed: ' + error.message));
|
|
118
|
+
console.log('\nPlease try again or visit ' + chalk.cyan(apiUrl) + ' to sign up.\n');
|
|
54
119
|
}
|
|
55
120
|
}
|
|
56
121
|
|