@chakresh/kresh 0.1.3 → 0.1.6
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/package.json +1 -2
- package/src/commands/install.js +26 -7
- package/src/commands/login.js +24 -0
- package/src/index.js +8 -0
- package/src/services/api.js +11 -1
- package/src/services/auth.js +108 -0
package/package.json
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chakresh/kresh",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"bin": {
|
|
5
5
|
"kresh": "./src/index.js"
|
|
6
6
|
},
|
|
7
7
|
"type": "module",
|
|
8
8
|
"dependencies": {
|
|
9
|
-
"@chakresh/kresh": "^0.1.2",
|
|
10
9
|
"axios": "^1.6.8",
|
|
11
10
|
"chalk": "^5.3.0",
|
|
12
11
|
"commander": "^11.1.0",
|
package/src/commands/install.js
CHANGED
|
@@ -8,7 +8,9 @@ import { logger } from '../utils/logger.js';
|
|
|
8
8
|
/**
|
|
9
9
|
* Installs a skill from the registry locally.
|
|
10
10
|
*/
|
|
11
|
-
|
|
11
|
+
import { cliAuthFlow, getToken } from '../services/auth.js';
|
|
12
|
+
|
|
13
|
+
export async function installSkill(skillSlug, isRetry = false) {
|
|
12
14
|
const spinner = ora(`Fetching skill "${skillSlug}"...`).start();
|
|
13
15
|
try {
|
|
14
16
|
const response = await api.get(`/api/skills/${skillSlug}`);
|
|
@@ -20,15 +22,32 @@ export async function installSkill(skillSlug) {
|
|
|
20
22
|
spinner.succeed(`Successfully installed ${logger.bold(metadata.name)} (v${metadata.currentVersion}) by @${metadata.ownerUsername}`);
|
|
21
23
|
logger.info(`Saved to: ${logger.bold(savedDir)}`);
|
|
22
24
|
} catch (error) {
|
|
23
|
-
spinner.
|
|
24
|
-
if (error.response) {
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
spinner.stop();
|
|
26
|
+
if (error.response && error.response.status === 404 && !isRetry) {
|
|
27
|
+
// It might be a private skill, let's try authenticating
|
|
28
|
+
logger.info(`Skill "${skillSlug}" not found publicly. This might be a private skill.`);
|
|
29
|
+
|
|
30
|
+
const hasToken = getToken();
|
|
31
|
+
if (!hasToken) {
|
|
32
|
+
logger.info('Attempting to authenticate via browser to access private skills...');
|
|
33
|
+
try {
|
|
34
|
+
await cliAuthFlow();
|
|
35
|
+
// Retry the installation now that we have a token
|
|
36
|
+
logger.info('Authentication successful. Retrying download...');
|
|
37
|
+
return await installSkill(skillSlug, true);
|
|
38
|
+
} catch (authErr) {
|
|
39
|
+
logger.error('Authentication failed: ' + authErr.message);
|
|
40
|
+
}
|
|
27
41
|
} else {
|
|
28
|
-
logger.error(`
|
|
42
|
+
logger.error(`Skill "${skillSlug}" was not found in the registry, even with your authenticated session.`);
|
|
29
43
|
}
|
|
30
44
|
} else {
|
|
31
|
-
|
|
45
|
+
spinner.fail(`Installation failed for "${skillSlug}"`);
|
|
46
|
+
if (error.response) {
|
|
47
|
+
logger.error(`Registry error: ${error.response.data?.error || error.response.statusText}`);
|
|
48
|
+
} else {
|
|
49
|
+
logger.error(`Connection error: ${error.message}`);
|
|
50
|
+
}
|
|
32
51
|
}
|
|
33
52
|
}
|
|
34
53
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { cliAuthFlow, clearToken, getToken } from '../services/auth.js';
|
|
2
|
+
import { logger } from '../utils/logger.js';
|
|
3
|
+
|
|
4
|
+
export async function loginCommand(options) {
|
|
5
|
+
if (options.flush) {
|
|
6
|
+
clearToken();
|
|
7
|
+
logger.info('Authentication token has been flushed. You are now logged out of the CLI.');
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const existingToken = getToken();
|
|
12
|
+
if (existingToken) {
|
|
13
|
+
logger.info('You are already authenticated. To log in as someone else, run: kresh login --flush');
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
try {
|
|
18
|
+
logger.info('Starting browser authentication...');
|
|
19
|
+
await cliAuthFlow();
|
|
20
|
+
// cliAuthFlow already prints success and saves the token
|
|
21
|
+
} catch (error) {
|
|
22
|
+
logger.error('Authentication failed: ' + error.message);
|
|
23
|
+
}
|
|
24
|
+
}
|
package/src/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import { searchSkills } from './commands/search.js';
|
|
|
6
6
|
import { listInstalledSkills } from './commands/ls.js';
|
|
7
7
|
import { removeSkill } from './commands/remove.js';
|
|
8
8
|
import { publishSkill } from './commands/publish.js';
|
|
9
|
+
import { loginCommand } from './commands/login.js';
|
|
9
10
|
|
|
10
11
|
const program = new Command();
|
|
11
12
|
|
|
@@ -53,4 +54,11 @@ program
|
|
|
53
54
|
await publishSkill();
|
|
54
55
|
});
|
|
55
56
|
|
|
57
|
+
program
|
|
58
|
+
.command('login [action]')
|
|
59
|
+
.description('Authenticate with the Kresh registry. Use "kresh login flush" to clear your session.')
|
|
60
|
+
.action(async (action) => {
|
|
61
|
+
await loginCommand({ flush: action === 'flush' });
|
|
62
|
+
});
|
|
63
|
+
|
|
56
64
|
program.parse();
|
package/src/services/api.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import axios from 'axios';
|
|
2
|
+
import { getToken } from './auth.js';
|
|
2
3
|
|
|
3
|
-
const baseURL = process.env.KRESH_API_URL || '
|
|
4
|
+
const baseURL = process.env.KRESH_API_URL || 'http://localhost:3000'; // Assume localhost for local testing
|
|
4
5
|
|
|
5
6
|
export const api = axios.create({
|
|
6
7
|
baseURL,
|
|
@@ -10,3 +11,12 @@ export const api = axios.create({
|
|
|
10
11
|
'Content-Type': 'application/json'
|
|
11
12
|
}
|
|
12
13
|
});
|
|
14
|
+
|
|
15
|
+
api.interceptors.request.use((config) => {
|
|
16
|
+
const token = getToken();
|
|
17
|
+
if (token) {
|
|
18
|
+
config.headers.Authorization = `Bearer ${token}`;
|
|
19
|
+
}
|
|
20
|
+
return config;
|
|
21
|
+
});
|
|
22
|
+
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import http from 'http';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import os from 'os';
|
|
5
|
+
import { exec } from 'child_process';
|
|
6
|
+
import { logger } from '../utils/logger.js';
|
|
7
|
+
import ora from 'ora';
|
|
8
|
+
|
|
9
|
+
const KRESH_DIR = path.join(os.homedir(), '.kresh');
|
|
10
|
+
const CONFIG_FILE = path.join(KRESH_DIR, 'config.json');
|
|
11
|
+
|
|
12
|
+
export function getToken() {
|
|
13
|
+
try {
|
|
14
|
+
if (fs.existsSync(CONFIG_FILE)) {
|
|
15
|
+
const config = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
|
|
16
|
+
return config.token || null;
|
|
17
|
+
}
|
|
18
|
+
} catch (err) {
|
|
19
|
+
// ignore
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function setToken(token) {
|
|
25
|
+
try {
|
|
26
|
+
if (!fs.existsSync(KRESH_DIR)) {
|
|
27
|
+
fs.mkdirSync(KRESH_DIR, { recursive: true });
|
|
28
|
+
}
|
|
29
|
+
const config = fs.existsSync(CONFIG_FILE) ? JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8')) : {};
|
|
30
|
+
config.token = token;
|
|
31
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), 'utf-8');
|
|
32
|
+
} catch (err) {
|
|
33
|
+
logger.error('Failed to save config: ' + err.message);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function clearToken() {
|
|
38
|
+
try {
|
|
39
|
+
if (fs.existsSync(CONFIG_FILE)) {
|
|
40
|
+
const config = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
|
|
41
|
+
delete config.token;
|
|
42
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2), 'utf-8');
|
|
43
|
+
}
|
|
44
|
+
} catch (err) {
|
|
45
|
+
logger.error('Failed to clear config: ' + err.message);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function openBrowser(url) {
|
|
50
|
+
const startCmd = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';
|
|
51
|
+
exec(`${startCmd} "${url}"`, (err) => {
|
|
52
|
+
if (err) {
|
|
53
|
+
logger.warn(`Could not open browser automatically. Please visit: ${url}`);
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export async function cliAuthFlow() {
|
|
59
|
+
return new Promise((resolve, reject) => {
|
|
60
|
+
const server = http.createServer((req, res) => {
|
|
61
|
+
const url = new URL(req.url, `http://${req.headers.host}`);
|
|
62
|
+
if (url.pathname === '/callback') {
|
|
63
|
+
const token = url.searchParams.get('token');
|
|
64
|
+
if (token) {
|
|
65
|
+
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
66
|
+
res.end(`
|
|
67
|
+
<html>
|
|
68
|
+
<head><title>Kresh CLI Authenticated</title></head>
|
|
69
|
+
<body style="font-family: monospace; padding: 2rem; text-align: center; background: #000; color: #fff;">
|
|
70
|
+
<h2 style="color: #4ade80;">Success!</h2>
|
|
71
|
+
<p>The Kresh CLI has been authenticated. You can close this window and return to your terminal.</p>
|
|
72
|
+
</body>
|
|
73
|
+
</html>
|
|
74
|
+
`);
|
|
75
|
+
|
|
76
|
+
setToken(token);
|
|
77
|
+
|
|
78
|
+
setTimeout(() => {
|
|
79
|
+
server.close();
|
|
80
|
+
resolve(token);
|
|
81
|
+
}, 500);
|
|
82
|
+
} else {
|
|
83
|
+
res.writeHead(400, { 'Content-Type': 'text/plain' });
|
|
84
|
+
res.end('Missing token in callback.');
|
|
85
|
+
server.close();
|
|
86
|
+
reject(new Error('Missing token'));
|
|
87
|
+
}
|
|
88
|
+
} else {
|
|
89
|
+
res.writeHead(404);
|
|
90
|
+
res.end();
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
server.listen(0, '127.0.0.1', () => {
|
|
95
|
+
const port = server.address().port;
|
|
96
|
+
const baseUrl = process.env.KRESH_API_URL || 'https://kresh.vercel.app';
|
|
97
|
+
const authUrl = `${baseUrl}/cli/auth?port=${port}`;
|
|
98
|
+
|
|
99
|
+
const spinner = ora('Opening browser for authentication...').start();
|
|
100
|
+
openBrowser(authUrl);
|
|
101
|
+
spinner.succeed(`Waiting for authentication... (if browser doesn't open, visit ${authUrl})`);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
server.on('error', (err) => {
|
|
105
|
+
reject(err);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
}
|