kaimon-cli 0.1.2 ā 0.1.4
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 +79 -32
- package/bin/index.js +1 -1
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +13 -4
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/pull.d.ts +4 -0
- package/dist/commands/pull.d.ts.map +1 -0
- package/dist/commands/pull.js +20 -0
- package/dist/commands/pull.js.map +1 -0
- package/dist/commands/push.d.ts +4 -0
- package/dist/commands/push.d.ts.map +1 -0
- package/dist/commands/push.js +24 -0
- package/dist/commands/push.js.map +1 -0
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +3 -14
- package/dist/commands/sync.js.map +1 -1
- package/dist/config/constants.d.ts +1 -1
- package/dist/config/constants.d.ts.map +1 -1
- package/dist/config/constants.js +7 -3
- package/dist/config/constants.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/markdown.d.ts +21 -0
- package/dist/utils/markdown.d.ts.map +1 -0
- package/dist/utils/markdown.js +180 -0
- package/dist/utils/markdown.js.map +1 -0
- package/dist/utils/projectConfig.d.ts +25 -0
- package/dist/utils/projectConfig.d.ts.map +1 -0
- package/dist/utils/projectConfig.js +160 -0
- package/dist/utils/projectConfig.js.map +1 -0
- package/dist/utils/pushOperations.d.ts +10 -0
- package/dist/utils/pushOperations.d.ts.map +1 -0
- package/dist/utils/pushOperations.js +119 -0
- package/dist/utils/pushOperations.js.map +1 -0
- package/dist/utils/supabaseClient.d.ts +11 -0
- package/dist/utils/supabaseClient.d.ts.map +1 -0
- package/dist/utils/supabaseClient.js +43 -0
- package/dist/utils/supabaseClient.js.map +1 -0
- package/dist/utils/syncOperations.d.ts +16 -0
- package/dist/utils/syncOperations.d.ts.map +1 -0
- package/dist/utils/syncOperations.js +179 -0
- package/dist/utils/syncOperations.js.map +1 -0
- package/dist/utils/tokenRefresh.d.ts +17 -0
- package/dist/utils/tokenRefresh.d.ts.map +1 -0
- package/dist/utils/tokenRefresh.js +80 -0
- package/dist/utils/tokenRefresh.js.map +1 -0
- package/package.json +15 -8
- package/src/commands/auth.ts +0 -170
- package/src/commands/config.ts +0 -72
- package/src/commands/sync.ts +0 -30
- package/src/config/constants.ts +0 -27
- package/src/index.ts +0 -20
- package/src/utils/config.ts +0 -81
- package/src/utils/requireAuth.ts +0 -65
- package/tsconfig.json +0 -21
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { getSupabaseClient } from './supabaseClient.js';
|
|
2
|
+
import { loadConfig, saveConfig } from './config.js';
|
|
3
|
+
/**
|
|
4
|
+
* Check if token is expired
|
|
5
|
+
*/
|
|
6
|
+
function isTokenExpired(expiresAt) {
|
|
7
|
+
if (!expiresAt)
|
|
8
|
+
return true;
|
|
9
|
+
const expirationTime = new Date(expiresAt).getTime();
|
|
10
|
+
const now = Date.now();
|
|
11
|
+
// Consider expired if less than 1 minute remaining
|
|
12
|
+
return now > expirationTime - 60000;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Refresh the user's access token using the refresh token
|
|
16
|
+
* Returns true if refresh was successful, false if it failed or wasn't needed
|
|
17
|
+
*/
|
|
18
|
+
export async function refreshAccessToken() {
|
|
19
|
+
try {
|
|
20
|
+
const config = loadConfig();
|
|
21
|
+
// If no tokens stored, nothing to refresh
|
|
22
|
+
if (!config.access_token) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
// Check if token is still valid
|
|
26
|
+
if (!isTokenExpired(config.expires_at)) {
|
|
27
|
+
console.log('ā Token still valid');
|
|
28
|
+
return true; // Token is good
|
|
29
|
+
}
|
|
30
|
+
console.log('š Token expired, refreshing...');
|
|
31
|
+
// If no refresh token, user needs to login again
|
|
32
|
+
if (!config.refresh_token) {
|
|
33
|
+
console.log('ā ļø No refresh token available');
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
// Get Supabase client (uses old token temporarily)
|
|
37
|
+
const supabase = getSupabaseClient();
|
|
38
|
+
// Try to refresh the session
|
|
39
|
+
const { data, error } = await supabase.auth.refreshSession({
|
|
40
|
+
refresh_token: config.refresh_token
|
|
41
|
+
});
|
|
42
|
+
if (error || !data.session) {
|
|
43
|
+
console.error('ā Token refresh failed:', error?.message);
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
// Update config with new tokens
|
|
47
|
+
const newConfig = {
|
|
48
|
+
access_token: data.session.access_token,
|
|
49
|
+
refresh_token: data.session.refresh_token || config.refresh_token,
|
|
50
|
+
expires_at: new Date(Date.now() + (data.session.expires_in * 1000)).toISOString(),
|
|
51
|
+
user_email: config.user_email
|
|
52
|
+
};
|
|
53
|
+
saveConfig(newConfig);
|
|
54
|
+
console.log('ā
Token refreshed successfully');
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
console.error('ā Token refresh error:', error.message);
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Ensure user has a valid token before executing commands
|
|
64
|
+
* Will attempt to refresh if expired
|
|
65
|
+
*/
|
|
66
|
+
export async function ensureValidToken() {
|
|
67
|
+
const config = loadConfig();
|
|
68
|
+
// Check if token exists
|
|
69
|
+
if (!config.access_token) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
// If token is still valid, return success
|
|
73
|
+
if (!isTokenExpired(config.expires_at)) {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
// Token expired, try to refresh it
|
|
77
|
+
const refreshed = await refreshAccessToken();
|
|
78
|
+
return refreshed;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=tokenRefresh.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenRefresh.js","sourceRoot":"","sources":["../../src/utils/tokenRefresh.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kaimon-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "CLI tool to sync Kaimon documentation with your codebase",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -23,23 +23,30 @@
|
|
|
23
23
|
],
|
|
24
24
|
"author": "Kaimon",
|
|
25
25
|
"license": "MIT",
|
|
26
|
+
"files": [
|
|
27
|
+
"bin",
|
|
28
|
+
"dist",
|
|
29
|
+
"README.md",
|
|
30
|
+
"LICENSE"
|
|
31
|
+
],
|
|
26
32
|
"dependencies": {
|
|
27
|
-
"
|
|
33
|
+
"@supabase/supabase-js": "^2.97.0",
|
|
28
34
|
"axios": "^1.7.0",
|
|
29
35
|
"chalk": "^5.3.0",
|
|
30
|
-
"
|
|
31
|
-
"dotenv": "^17.0.0",
|
|
36
|
+
"commander": "^14.0.0",
|
|
32
37
|
"conf": "^15.0.0",
|
|
33
|
-
"
|
|
38
|
+
"dotenv": "^17.0.0",
|
|
39
|
+
"open": "^10.0.0",
|
|
40
|
+
"ora": "^9.0.0"
|
|
34
41
|
},
|
|
35
42
|
"devDependencies": {
|
|
36
43
|
"@types/node": "^25.0.0",
|
|
37
|
-
"typescript": "^5.4.0",
|
|
38
|
-
"ts-node": "^10.9.2",
|
|
39
|
-
"eslint": "^10.0.0",
|
|
40
44
|
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
|
41
45
|
"@typescript-eslint/parser": "^8.0.0",
|
|
46
|
+
"eslint": "^10.0.0",
|
|
42
47
|
"prettier": "^3.2.0",
|
|
48
|
+
"ts-node": "^10.9.2",
|
|
49
|
+
"typescript": "^5.4.0",
|
|
43
50
|
"vitest": "^4.0.0"
|
|
44
51
|
},
|
|
45
52
|
"engines": {
|
package/src/commands/auth.ts
DELETED
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import open from 'open';
|
|
4
|
-
import { getAuthUrl, CLI_AUTH_PATH } from '../config/constants.js';
|
|
5
|
-
import { loadConfig, saveConfig, clearConfig, isAuthenticated } from '../utils/config.js';
|
|
6
|
-
|
|
7
|
-
// Mask token for display (show first 8 and last 8 characters)
|
|
8
|
-
function maskToken(token: string): string {
|
|
9
|
-
if (!token) return '';
|
|
10
|
-
if (token.length <= 20) return token; // Don't mask if too short
|
|
11
|
-
const start = token.substring(0, 8);
|
|
12
|
-
const end = token.substring(token.length - 8);
|
|
13
|
-
const maskLength = Math.min(token.length - 16, 40); // Max 40 dots
|
|
14
|
-
return `${start}${'ā¢'.repeat(maskLength)}${end}`;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const auth = new Command('auth')
|
|
18
|
-
.description('Authenticate with Kaimon');
|
|
19
|
-
|
|
20
|
-
// Login command
|
|
21
|
-
auth
|
|
22
|
-
.command('login')
|
|
23
|
-
.description('Login to your Kaimon account')
|
|
24
|
-
.option('--dev', 'Use development URL (localhost)')
|
|
25
|
-
.action(async (options) => {
|
|
26
|
-
try {
|
|
27
|
-
console.log(chalk.blue('š Opening browser for authentication...\n'));
|
|
28
|
-
|
|
29
|
-
// Construct auth URL
|
|
30
|
-
const baseUrl = options.dev ? 'http://localhost:5173' : getAuthUrl();
|
|
31
|
-
const authUrl = `${baseUrl}${CLI_AUTH_PATH}`;
|
|
32
|
-
|
|
33
|
-
console.log(chalk.gray(`URL: ${authUrl}\n`));
|
|
34
|
-
|
|
35
|
-
// Open browser
|
|
36
|
-
await open(authUrl);
|
|
37
|
-
|
|
38
|
-
console.log(chalk.yellow('Please complete authentication in your browser.'));
|
|
39
|
-
console.log(chalk.gray('After logging in, copy the access token and paste it here.\n'));
|
|
40
|
-
|
|
41
|
-
// Wait for user to paste token with hidden input
|
|
42
|
-
const readline = await import('readline');
|
|
43
|
-
const rl = readline.createInterface({
|
|
44
|
-
input: process.stdin,
|
|
45
|
-
output: process.stdout
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
// Mute output to hide token input
|
|
49
|
-
const mutableStdout = process.stdout as any;
|
|
50
|
-
console.log(chalk.cyan('Paste your access token below:'));
|
|
51
|
-
console.log(chalk.dim('(Your paste will be invisible for security - press Enter when done)'));
|
|
52
|
-
mutableStdout.write(chalk.cyan('> '));
|
|
53
|
-
|
|
54
|
-
let token = '';
|
|
55
|
-
rl.on('line', (input) => {
|
|
56
|
-
token = input;
|
|
57
|
-
rl.close();
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
// Mute the output while typing
|
|
61
|
-
const oldWrite = mutableStdout.write;
|
|
62
|
-
mutableStdout.write = (chunk: string) => {
|
|
63
|
-
// Only show the prompt, hide everything else
|
|
64
|
-
return true;
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
rl.on('close', () => {
|
|
68
|
-
// Restore normal output
|
|
69
|
-
mutableStdout.write = oldWrite;
|
|
70
|
-
|
|
71
|
-
if (!token || token.trim() === '') {
|
|
72
|
-
console.log(chalk.red('\nā No token provided. Authentication cancelled.'));
|
|
73
|
-
process.exit(1);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Show confirmation that token was received
|
|
77
|
-
console.log(chalk.green(`\nā Token received (${token.trim().length} characters)`));
|
|
78
|
-
console.log(chalk.gray('Verifying and saving...\n'));
|
|
79
|
-
|
|
80
|
-
// Save token to config
|
|
81
|
-
saveConfig({
|
|
82
|
-
access_token: token.trim(),
|
|
83
|
-
expires_at: new Date(Date.now() + 60 * 60 * 1000).toISOString() // 1 hour from now
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
console.log(chalk.green('ā
Authentication successful!'));
|
|
87
|
-
console.log(chalk.gray(`Token: ${maskToken(token.trim())}`));
|
|
88
|
-
console.log(chalk.gray('Saved to ~/.kaimon/config.json\n'));
|
|
89
|
-
console.log(chalk.blue('You can now use other CLI commands like:'));
|
|
90
|
-
console.log(chalk.gray(' kaimon sync --pull'));
|
|
91
|
-
console.log(chalk.gray(' kaimon sync --push\n'));
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
} catch (error: any) {
|
|
95
|
-
console.error(chalk.red('ā Login failed:'), error.message);
|
|
96
|
-
process.exit(1);
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
// Logout command
|
|
101
|
-
auth
|
|
102
|
-
.command('logout')
|
|
103
|
-
.description('Logout from Kaimon')
|
|
104
|
-
.action(async () => {
|
|
105
|
-
try {
|
|
106
|
-
if (!isAuthenticated()) {
|
|
107
|
-
console.log(chalk.yellow('ā ļø You are not logged in.'));
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
clearConfig();
|
|
112
|
-
console.log(chalk.green('ā
Logged out successfully!'));
|
|
113
|
-
console.log(chalk.gray('Token removed from ~/.kaimon/config.json\n'));
|
|
114
|
-
} catch (error: any) {
|
|
115
|
-
console.error(chalk.red('ā Logout failed:'), error.message);
|
|
116
|
-
process.exit(1);
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
// Status command
|
|
121
|
-
auth
|
|
122
|
-
.command('status')
|
|
123
|
-
.description('Check authentication status')
|
|
124
|
-
.option('--show-token', 'Show full access token (security risk)')
|
|
125
|
-
.action(async (options) => {
|
|
126
|
-
try {
|
|
127
|
-
const config = loadConfig();
|
|
128
|
-
|
|
129
|
-
if (!config.access_token) {
|
|
130
|
-
console.log(chalk.yellow('ā Not authenticated'));
|
|
131
|
-
console.log(chalk.gray('Run "kaimon auth login" to authenticate\n'));
|
|
132
|
-
return;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
console.log(chalk.green('ā
Authenticated'));
|
|
136
|
-
|
|
137
|
-
if (config.user_email) {
|
|
138
|
-
console.log(chalk.gray(`Email: ${config.user_email}`));
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Show token (masked by default)
|
|
142
|
-
if (options.showToken) {
|
|
143
|
-
console.log(chalk.gray(`Token: ${config.access_token}`));
|
|
144
|
-
console.log(chalk.yellow('ā ļø Full token shown above - keep it secure!'));
|
|
145
|
-
} else {
|
|
146
|
-
console.log(chalk.gray(`Token: ${maskToken(config.access_token)}`));
|
|
147
|
-
console.log(chalk.dim('Use --show-token to reveal full token'));
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (config.expires_at) {
|
|
151
|
-
const expiresAt = new Date(config.expires_at);
|
|
152
|
-
const now = new Date();
|
|
153
|
-
const isExpired = expiresAt < now;
|
|
154
|
-
|
|
155
|
-
if (isExpired) {
|
|
156
|
-
console.log(chalk.red('Status: Expired'));
|
|
157
|
-
console.log(chalk.yellow('Please login again: kaimon auth login\n'));
|
|
158
|
-
} else {
|
|
159
|
-
console.log(chalk.gray(`Expires: ${expiresAt.toLocaleString()}`));
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
console.log(chalk.gray(`Config: ~/.kaimon/config.json\n`));
|
|
164
|
-
} catch (error: any) {
|
|
165
|
-
console.error(chalk.red('ā Status check failed:'), error.message);
|
|
166
|
-
process.exit(1);
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
export { auth };
|
package/src/commands/config.ts
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import { loadConfig, getConfigPath } from '../utils/config.js';
|
|
4
|
-
|
|
5
|
-
const config = new Command('config')
|
|
6
|
-
.description('Manage Kaimon CLI configuration');
|
|
7
|
-
|
|
8
|
-
config
|
|
9
|
-
.command('init')
|
|
10
|
-
.description('Initialize .kaimon config in current directory')
|
|
11
|
-
.action(async () => {
|
|
12
|
-
console.log(chalk.blue('āļø Config init - coming soon!'));
|
|
13
|
-
console.log(chalk.yellow('\nā ļø Project-level config not yet implemented'));
|
|
14
|
-
console.log(chalk.dim('For now, all config is stored globally at:'));
|
|
15
|
-
console.log(chalk.dim(` ${getConfigPath()}\n`));
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
config
|
|
19
|
-
.command('show')
|
|
20
|
-
.description('Show current configuration')
|
|
21
|
-
.action(async () => {
|
|
22
|
-
try {
|
|
23
|
-
const configData = loadConfig();
|
|
24
|
-
const configPath = getConfigPath();
|
|
25
|
-
|
|
26
|
-
console.log(chalk.blue('š Current Configuration\n'));
|
|
27
|
-
console.log(chalk.gray(`Location: ${configPath}\n`));
|
|
28
|
-
|
|
29
|
-
if (Object.keys(configData).length === 0) {
|
|
30
|
-
console.log(chalk.yellow('No configuration found'));
|
|
31
|
-
console.log(chalk.dim('Run "kaimon auth login" to authenticate\n'));
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Show config (mask sensitive data)
|
|
36
|
-
console.log(chalk.cyan('Settings:'));
|
|
37
|
-
|
|
38
|
-
if (configData.access_token) {
|
|
39
|
-
const masked = configData.access_token.substring(0, 10) + '...';
|
|
40
|
-
console.log(chalk.gray(` access_token: ${masked}`));
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (configData.refresh_token) {
|
|
44
|
-
const masked = configData.refresh_token.substring(0, 10) + '...';
|
|
45
|
-
console.log(chalk.gray(` refresh_token: ${masked}`));
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (configData.user_email) {
|
|
49
|
-
console.log(chalk.gray(` user_email: ${configData.user_email}`));
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (configData.expires_at) {
|
|
53
|
-
console.log(chalk.gray(` expires_at: ${configData.expires_at}`));
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
console.log();
|
|
57
|
-
|
|
58
|
-
} catch (error: any) {
|
|
59
|
-
console.error(chalk.red('ā Failed to show config:'), error.message);
|
|
60
|
-
process.exit(1);
|
|
61
|
-
}
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
config
|
|
65
|
-
.command('set <key> <value>')
|
|
66
|
-
.description('Set a configuration value')
|
|
67
|
-
.action(async (key, value) => {
|
|
68
|
-
console.log(chalk.yellow(`ā ļø Manual config editing not yet supported`));
|
|
69
|
-
console.log(chalk.dim('Use "kaimon auth login" to authenticate\n'));
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
export { config };
|
package/src/commands/sync.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import { requireAuth } from '../utils/requireAuth.js';
|
|
4
|
-
|
|
5
|
-
const sync = new Command('sync')
|
|
6
|
-
.description('Sync Kaimon documentation with your codebase')
|
|
7
|
-
.option('--pull', 'Pull docs from Kaimon to local')
|
|
8
|
-
.option('--push', 'Push local docs to Kaimon')
|
|
9
|
-
.option('--diff', 'Show differences without syncing')
|
|
10
|
-
.option('--config <path>', 'Path to sync config file')
|
|
11
|
-
.action(async (options) => {
|
|
12
|
-
try {
|
|
13
|
-
// Validate authentication first
|
|
14
|
-
const token = await requireAuth();
|
|
15
|
-
|
|
16
|
-
console.log(chalk.blue('š Sync command - coming soon!'));
|
|
17
|
-
console.log(chalk.gray(`Authenticated with token: ${token.substring(0, 10)}...`));
|
|
18
|
-
console.log(chalk.gray('Options:'), options);
|
|
19
|
-
|
|
20
|
-
// TODO: Implement sync logic
|
|
21
|
-
console.log(chalk.yellow('\nā ļø Sync functionality not yet implemented'));
|
|
22
|
-
console.log(chalk.dim('This will sync your documents with Kaimon in the future\n'));
|
|
23
|
-
|
|
24
|
-
} catch (error: any) {
|
|
25
|
-
console.error(chalk.red('ā Sync failed:'), error.message);
|
|
26
|
-
process.exit(1);
|
|
27
|
-
}
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
export { sync };
|
package/src/config/constants.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
// Configuration constants for Kaimon CLI
|
|
2
|
-
|
|
3
|
-
// Determine the base URL based on environment
|
|
4
|
-
// In development, use localhost
|
|
5
|
-
// In production, use the actual domain
|
|
6
|
-
export const BASE_URL = process.env.KAIMON_API_URL || 'https://kaimon.ai';
|
|
7
|
-
|
|
8
|
-
// For local development testing
|
|
9
|
-
export const DEV_URL = 'http://localhost:5173';
|
|
10
|
-
|
|
11
|
-
// Determine which URL to use
|
|
12
|
-
export const getAuthUrl = (): string => {
|
|
13
|
-
// Check if we're in development mode
|
|
14
|
-
const isDev = process.env.NODE_ENV === 'development' || process.env.KAIMON_DEV === 'true';
|
|
15
|
-
return isDev ? DEV_URL : BASE_URL;
|
|
16
|
-
};
|
|
17
|
-
|
|
18
|
-
// CLI auth endpoint
|
|
19
|
-
export const CLI_AUTH_PATH = '/cli-auth';
|
|
20
|
-
|
|
21
|
-
// Config file location
|
|
22
|
-
export const CONFIG_DIR = '.kaimon';
|
|
23
|
-
export const CONFIG_FILE = 'config.json';
|
|
24
|
-
|
|
25
|
-
// Supabase config (will be same as web app)
|
|
26
|
-
export const SUPABASE_URL = process.env.KAIMON_SUPABASE_URL || 'https://your-project.supabase.co';
|
|
27
|
-
export const SUPABASE_ANON_KEY = process.env.KAIMON_SUPABASE_ANON_KEY || '';
|
package/src/index.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
import { auth } from './commands/auth.js';
|
|
3
|
-
import { sync } from './commands/sync.js';
|
|
4
|
-
import { config } from './commands/config.js';
|
|
5
|
-
|
|
6
|
-
const program = new Command();
|
|
7
|
-
|
|
8
|
-
program
|
|
9
|
-
.name('kaimon')
|
|
10
|
-
.description('Sync Kaimon documentation with your codebase')
|
|
11
|
-
.version('0.1.0');
|
|
12
|
-
|
|
13
|
-
// Register commands
|
|
14
|
-
program.addCommand(auth);
|
|
15
|
-
program.addCommand(sync);
|
|
16
|
-
program.addCommand(config);
|
|
17
|
-
|
|
18
|
-
export async function main() {
|
|
19
|
-
await program.parseAsync(process.argv);
|
|
20
|
-
}
|
package/src/utils/config.ts
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import os from 'os';
|
|
4
|
-
import { CONFIG_DIR, CONFIG_FILE } from '../config/constants.js';
|
|
5
|
-
|
|
6
|
-
export interface CLIConfig {
|
|
7
|
-
access_token?: string;
|
|
8
|
-
refresh_token?: string;
|
|
9
|
-
expires_at?: string;
|
|
10
|
-
user_email?: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// Get config directory path
|
|
14
|
-
export function getConfigDir(): string {
|
|
15
|
-
return path.join(os.homedir(), CONFIG_DIR);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
// Get config file path
|
|
19
|
-
export function getConfigPath(): string {
|
|
20
|
-
return path.join(getConfigDir(), CONFIG_FILE);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Ensure config directory exists
|
|
24
|
-
export function ensureConfigDir(): void {
|
|
25
|
-
const configDir = getConfigDir();
|
|
26
|
-
if (!fs.existsSync(configDir)) {
|
|
27
|
-
fs.mkdirSync(configDir, { recursive: true, mode: 0o700 }); // Only user can read/write
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Load config from file
|
|
32
|
-
export function loadConfig(): CLIConfig {
|
|
33
|
-
const configPath = getConfigPath();
|
|
34
|
-
|
|
35
|
-
if (!fs.existsSync(configPath)) {
|
|
36
|
-
return {};
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
try {
|
|
40
|
-
const content = fs.readFileSync(configPath, 'utf-8');
|
|
41
|
-
return JSON.parse(content);
|
|
42
|
-
} catch (error) {
|
|
43
|
-
console.error('Failed to load config:', error);
|
|
44
|
-
return {};
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// Save config to file
|
|
49
|
-
export function saveConfig(config: CLIConfig): void {
|
|
50
|
-
ensureConfigDir();
|
|
51
|
-
const configPath = getConfigPath();
|
|
52
|
-
|
|
53
|
-
try {
|
|
54
|
-
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), {
|
|
55
|
-
mode: 0o600 // Only user can read/write
|
|
56
|
-
});
|
|
57
|
-
} catch (error) {
|
|
58
|
-
throw new Error(`Failed to save config: ${error}`);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Clear config (logout)
|
|
63
|
-
export function clearConfig(): void {
|
|
64
|
-
const configPath = getConfigPath();
|
|
65
|
-
|
|
66
|
-
if (fs.existsSync(configPath)) {
|
|
67
|
-
fs.unlinkSync(configPath);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// Check if user is authenticated
|
|
72
|
-
export function isAuthenticated(): boolean {
|
|
73
|
-
const config = loadConfig();
|
|
74
|
-
return !!config.access_token;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Get access token
|
|
78
|
-
export function getAccessToken(): string | null {
|
|
79
|
-
const config = loadConfig();
|
|
80
|
-
return config.access_token || null;
|
|
81
|
-
}
|
package/src/utils/requireAuth.ts
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
|
-
import { loadConfig } from './config.js';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Validates authentication and returns the access token.
|
|
6
|
-
* Exits the process with helpful error message if not authenticated or token expired.
|
|
7
|
-
*/
|
|
8
|
-
export async function requireAuth(): Promise<string> {
|
|
9
|
-
const config = loadConfig();
|
|
10
|
-
|
|
11
|
-
// Check if token exists
|
|
12
|
-
if (!config.access_token) {
|
|
13
|
-
console.log(chalk.red('\nā Not authenticated'));
|
|
14
|
-
console.log(chalk.yellow('Please login first:'));
|
|
15
|
-
console.log(chalk.cyan(' kaimon auth login\n'));
|
|
16
|
-
process.exit(1);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Check if token is expired
|
|
20
|
-
if (config.expires_at) {
|
|
21
|
-
const expiresAt = new Date(config.expires_at);
|
|
22
|
-
const now = new Date();
|
|
23
|
-
|
|
24
|
-
if (expiresAt < now) {
|
|
25
|
-
console.log(chalk.red('\nā Authentication expired'));
|
|
26
|
-
console.log(chalk.yellow('Your session has expired. Please login again:'));
|
|
27
|
-
console.log(chalk.cyan(' kaimon auth login\n'));
|
|
28
|
-
process.exit(1);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Warn if expiring soon (within 5 minutes)
|
|
32
|
-
const fiveMinutes = 5 * 60 * 1000;
|
|
33
|
-
const timeUntilExpiry = expiresAt.getTime() - now.getTime();
|
|
34
|
-
|
|
35
|
-
if (timeUntilExpiry < fiveMinutes && timeUntilExpiry > 0) {
|
|
36
|
-
console.log(chalk.yellow(`ā ļø Token expires in ${Math.floor(timeUntilExpiry / 60000)} minutes`));
|
|
37
|
-
console.log(chalk.dim('Consider re-authenticating soon\n'));
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return config.access_token;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Checks if user is authenticated without exiting.
|
|
46
|
-
* Returns true if authenticated and token is valid.
|
|
47
|
-
*/
|
|
48
|
-
export function checkAuth(): boolean {
|
|
49
|
-
const config = loadConfig();
|
|
50
|
-
|
|
51
|
-
if (!config.access_token) {
|
|
52
|
-
return false;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
if (config.expires_at) {
|
|
56
|
-
const expiresAt = new Date(config.expires_at);
|
|
57
|
-
const now = new Date();
|
|
58
|
-
|
|
59
|
-
if (expiresAt < now) {
|
|
60
|
-
return false;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return true;
|
|
65
|
-
}
|
package/tsconfig.json
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2020",
|
|
4
|
-
"module": "ESNext",
|
|
5
|
-
"lib": ["ES2020"],
|
|
6
|
-
"moduleResolution": "node",
|
|
7
|
-
"outDir": "./dist",
|
|
8
|
-
"rootDir": "./src",
|
|
9
|
-
"strict": true,
|
|
10
|
-
"esModuleInterop": true,
|
|
11
|
-
"skipLibCheck": true,
|
|
12
|
-
"forceConsistentCasingInFileNames": true,
|
|
13
|
-
"resolveJsonModule": true,
|
|
14
|
-
"declaration": true,
|
|
15
|
-
"declarationMap": true,
|
|
16
|
-
"sourceMap": true,
|
|
17
|
-
"allowSyntheticDefaultImports": true
|
|
18
|
-
},
|
|
19
|
-
"include": ["src"],
|
|
20
|
-
"exclude": ["node_modules", "dist", "tests"]
|
|
21
|
-
}
|