kaimon-cli 0.1.2

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 ADDED
@@ -0,0 +1,73 @@
1
+ # Kaimon CLI
2
+
3
+ > Sync your Kaimon documentation with your codebase from the command line.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install -g kaimon-cli
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### Authenticate
14
+
15
+ ```bash
16
+ # Login to your Kaimon account
17
+ kaimon auth login
18
+
19
+ # Check your auth status
20
+ kaimon auth status
21
+
22
+ # Logout
23
+ kaimon auth logout
24
+ ```
25
+
26
+ ### Sync Documentation
27
+
28
+ ```bash
29
+ # Pull documentation from Kaimon
30
+ kaimon sync --pull
31
+
32
+ # Push local documentation to Kaimon
33
+ kaimon sync --push
34
+
35
+ # See what would change
36
+ kaimon sync --diff
37
+ ```
38
+
39
+ ### Configuration
40
+
41
+ ```bash
42
+ # Initialize CLI in your project
43
+ kaimon config init
44
+
45
+ # View configuration
46
+ kaimon config show
47
+
48
+ # Set a configuration value
49
+ kaimon config set key value
50
+ ```
51
+
52
+ ## Features
53
+
54
+ - šŸ” Secure authentication with Kaimon
55
+ - šŸ“ Bidirectional sync (pull/push)
56
+ - šŸ” Diff preview before sync
57
+ - āš™ļø Project-level configuration
58
+ - šŸš€ Fast and lightweight
59
+
60
+ ## Getting Started
61
+
62
+ 1. Install the CLI: `npm install -g kaimon-cli`
63
+ 2. Login: `kaimon auth login`
64
+ 3. Initialize your project: `kaimon config init`
65
+ 4. Sync docs: `kaimon sync --pull` or `kaimon sync --push`
66
+
67
+ ## Documentation
68
+
69
+ For more details, visit [kaimon.ai/cli](https://kaimon.ai)
70
+
71
+ ## License
72
+
73
+ MIT
package/bin/index.js ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { fileURLToPath } from 'url';
4
+ import { dirname } from 'path';
5
+ import { main } from '../dist/index.js';
6
+
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = dirname(__filename);
9
+
10
+ main().catch(error => {
11
+ console.error('Fatal error:', error.message);
12
+ process.exit(1);
13
+ });
@@ -0,0 +1,4 @@
1
+ import { Command } from 'commander';
2
+ declare const auth: Command;
3
+ export { auth };
4
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAgBpC,QAAA,MAAM,IAAI,SACgC,CAAC;AAwJ3C,OAAO,EAAE,IAAI,EAAE,CAAC"}
@@ -0,0 +1,150 @@
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
+ // Mask token for display (show first 8 and last 8 characters)
7
+ function maskToken(token) {
8
+ if (!token)
9
+ return '';
10
+ if (token.length <= 20)
11
+ return token; // Don't mask if too short
12
+ const start = token.substring(0, 8);
13
+ const end = token.substring(token.length - 8);
14
+ const maskLength = Math.min(token.length - 16, 40); // Max 40 dots
15
+ return `${start}${'•'.repeat(maskLength)}${end}`;
16
+ }
17
+ const auth = new Command('auth')
18
+ .description('Authenticate with Kaimon');
19
+ // Login command
20
+ auth
21
+ .command('login')
22
+ .description('Login to your Kaimon account')
23
+ .option('--dev', 'Use development URL (localhost)')
24
+ .action(async (options) => {
25
+ try {
26
+ console.log(chalk.blue('šŸ” Opening browser for authentication...\n'));
27
+ // Construct auth URL
28
+ const baseUrl = options.dev ? 'http://localhost:5173' : getAuthUrl();
29
+ const authUrl = `${baseUrl}${CLI_AUTH_PATH}`;
30
+ console.log(chalk.gray(`URL: ${authUrl}\n`));
31
+ // Open browser
32
+ await open(authUrl);
33
+ console.log(chalk.yellow('Please complete authentication in your browser.'));
34
+ console.log(chalk.gray('After logging in, copy the access token and paste it here.\n'));
35
+ // Wait for user to paste token with hidden input
36
+ const readline = await import('readline');
37
+ const rl = readline.createInterface({
38
+ input: process.stdin,
39
+ output: process.stdout
40
+ });
41
+ // Mute output to hide token input
42
+ const mutableStdout = process.stdout;
43
+ console.log(chalk.cyan('Paste your access token below:'));
44
+ console.log(chalk.dim('(Your paste will be invisible for security - press Enter when done)'));
45
+ mutableStdout.write(chalk.cyan('> '));
46
+ let token = '';
47
+ rl.on('line', (input) => {
48
+ token = input;
49
+ rl.close();
50
+ });
51
+ // Mute the output while typing
52
+ const oldWrite = mutableStdout.write;
53
+ mutableStdout.write = (chunk) => {
54
+ // Only show the prompt, hide everything else
55
+ return true;
56
+ };
57
+ rl.on('close', () => {
58
+ // Restore normal output
59
+ mutableStdout.write = oldWrite;
60
+ if (!token || token.trim() === '') {
61
+ console.log(chalk.red('\nāŒ No token provided. Authentication cancelled.'));
62
+ process.exit(1);
63
+ }
64
+ // Show confirmation that token was received
65
+ console.log(chalk.green(`\nāœ“ Token received (${token.trim().length} characters)`));
66
+ console.log(chalk.gray('Verifying and saving...\n'));
67
+ // Save token to config
68
+ saveConfig({
69
+ access_token: token.trim(),
70
+ expires_at: new Date(Date.now() + 60 * 60 * 1000).toISOString() // 1 hour from now
71
+ });
72
+ console.log(chalk.green('āœ… Authentication successful!'));
73
+ console.log(chalk.gray(`Token: ${maskToken(token.trim())}`));
74
+ console.log(chalk.gray('Saved to ~/.kaimon/config.json\n'));
75
+ console.log(chalk.blue('You can now use other CLI commands like:'));
76
+ console.log(chalk.gray(' kaimon sync --pull'));
77
+ console.log(chalk.gray(' kaimon sync --push\n'));
78
+ });
79
+ }
80
+ catch (error) {
81
+ console.error(chalk.red('āŒ Login failed:'), error.message);
82
+ process.exit(1);
83
+ }
84
+ });
85
+ // Logout command
86
+ auth
87
+ .command('logout')
88
+ .description('Logout from Kaimon')
89
+ .action(async () => {
90
+ try {
91
+ if (!isAuthenticated()) {
92
+ console.log(chalk.yellow('āš ļø You are not logged in.'));
93
+ return;
94
+ }
95
+ clearConfig();
96
+ console.log(chalk.green('āœ… Logged out successfully!'));
97
+ console.log(chalk.gray('Token removed from ~/.kaimon/config.json\n'));
98
+ }
99
+ catch (error) {
100
+ console.error(chalk.red('āŒ Logout failed:'), error.message);
101
+ process.exit(1);
102
+ }
103
+ });
104
+ // Status command
105
+ auth
106
+ .command('status')
107
+ .description('Check authentication status')
108
+ .option('--show-token', 'Show full access token (security risk)')
109
+ .action(async (options) => {
110
+ try {
111
+ const config = loadConfig();
112
+ if (!config.access_token) {
113
+ console.log(chalk.yellow('āŒ Not authenticated'));
114
+ console.log(chalk.gray('Run "kaimon auth login" to authenticate\n'));
115
+ return;
116
+ }
117
+ console.log(chalk.green('āœ… Authenticated'));
118
+ if (config.user_email) {
119
+ console.log(chalk.gray(`Email: ${config.user_email}`));
120
+ }
121
+ // Show token (masked by default)
122
+ if (options.showToken) {
123
+ console.log(chalk.gray(`Token: ${config.access_token}`));
124
+ console.log(chalk.yellow('āš ļø Full token shown above - keep it secure!'));
125
+ }
126
+ else {
127
+ console.log(chalk.gray(`Token: ${maskToken(config.access_token)}`));
128
+ console.log(chalk.dim('Use --show-token to reveal full token'));
129
+ }
130
+ if (config.expires_at) {
131
+ const expiresAt = new Date(config.expires_at);
132
+ const now = new Date();
133
+ const isExpired = expiresAt < now;
134
+ if (isExpired) {
135
+ console.log(chalk.red('Status: Expired'));
136
+ console.log(chalk.yellow('Please login again: kaimon auth login\n'));
137
+ }
138
+ else {
139
+ console.log(chalk.gray(`Expires: ${expiresAt.toLocaleString()}`));
140
+ }
141
+ }
142
+ console.log(chalk.gray(`Config: ~/.kaimon/config.json\n`));
143
+ }
144
+ catch (error) {
145
+ console.error(chalk.red('āŒ Status check failed:'), error.message);
146
+ process.exit(1);
147
+ }
148
+ });
149
+ export { auth };
150
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/commands/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { Command } from 'commander';
2
+ declare const config: Command;
3
+ export { config };
4
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,QAAA,MAAM,MAAM,SACqC,CAAC;AAkElD,OAAO,EAAE,MAAM,EAAE,CAAC"}
@@ -0,0 +1,60 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import { loadConfig, getConfigPath } from '../utils/config.js';
4
+ const config = new Command('config')
5
+ .description('Manage Kaimon CLI configuration');
6
+ config
7
+ .command('init')
8
+ .description('Initialize .kaimon config in current directory')
9
+ .action(async () => {
10
+ console.log(chalk.blue('āš™ļø Config init - coming soon!'));
11
+ console.log(chalk.yellow('\nāš ļø Project-level config not yet implemented'));
12
+ console.log(chalk.dim('For now, all config is stored globally at:'));
13
+ console.log(chalk.dim(` ${getConfigPath()}\n`));
14
+ });
15
+ config
16
+ .command('show')
17
+ .description('Show current configuration')
18
+ .action(async () => {
19
+ try {
20
+ const configData = loadConfig();
21
+ const configPath = getConfigPath();
22
+ console.log(chalk.blue('šŸ” Current Configuration\n'));
23
+ console.log(chalk.gray(`Location: ${configPath}\n`));
24
+ if (Object.keys(configData).length === 0) {
25
+ console.log(chalk.yellow('No configuration found'));
26
+ console.log(chalk.dim('Run "kaimon auth login" to authenticate\n'));
27
+ return;
28
+ }
29
+ // Show config (mask sensitive data)
30
+ console.log(chalk.cyan('Settings:'));
31
+ if (configData.access_token) {
32
+ const masked = configData.access_token.substring(0, 10) + '...';
33
+ console.log(chalk.gray(` access_token: ${masked}`));
34
+ }
35
+ if (configData.refresh_token) {
36
+ const masked = configData.refresh_token.substring(0, 10) + '...';
37
+ console.log(chalk.gray(` refresh_token: ${masked}`));
38
+ }
39
+ if (configData.user_email) {
40
+ console.log(chalk.gray(` user_email: ${configData.user_email}`));
41
+ }
42
+ if (configData.expires_at) {
43
+ console.log(chalk.gray(` expires_at: ${configData.expires_at}`));
44
+ }
45
+ console.log();
46
+ }
47
+ catch (error) {
48
+ console.error(chalk.red('āŒ Failed to show config:'), error.message);
49
+ process.exit(1);
50
+ }
51
+ });
52
+ config
53
+ .command('set <key> <value>')
54
+ .description('Set a configuration value')
55
+ .action(async (key, value) => {
56
+ console.log(chalk.yellow(`āš ļø Manual config editing not yet supported`));
57
+ console.log(chalk.dim('Use "kaimon auth login" to authenticate\n'));
58
+ });
59
+ export { config };
60
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import { Command } from 'commander';
2
+ declare const sync: Command;
3
+ export { sync };
4
+ //# sourceMappingURL=sync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,QAAA,MAAM,IAAI,SAuBN,CAAC;AAEL,OAAO,EAAE,IAAI,EAAE,CAAC"}
@@ -0,0 +1,27 @@
1
+ import { Command } from 'commander';
2
+ import chalk from 'chalk';
3
+ import { requireAuth } from '../utils/requireAuth.js';
4
+ const sync = new Command('sync')
5
+ .description('Sync Kaimon documentation with your codebase')
6
+ .option('--pull', 'Pull docs from Kaimon to local')
7
+ .option('--push', 'Push local docs to Kaimon')
8
+ .option('--diff', 'Show differences without syncing')
9
+ .option('--config <path>', 'Path to sync config file')
10
+ .action(async (options) => {
11
+ try {
12
+ // Validate authentication first
13
+ const token = await requireAuth();
14
+ console.log(chalk.blue('šŸ“ Sync command - coming soon!'));
15
+ console.log(chalk.gray(`Authenticated with token: ${token.substring(0, 10)}...`));
16
+ console.log(chalk.gray('Options:'), options);
17
+ // TODO: Implement sync logic
18
+ console.log(chalk.yellow('\nāš ļø Sync functionality not yet implemented'));
19
+ console.log(chalk.dim('This will sync your documents with Kaimon in the future\n'));
20
+ }
21
+ catch (error) {
22
+ console.error(chalk.red('āŒ Sync failed:'), error.message);
23
+ process.exit(1);
24
+ }
25
+ });
26
+ export { sync };
27
+ //# sourceMappingURL=sync.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.js","sourceRoot":"","sources":["../../src/commands/sync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAEtD,MAAM,IAAI,GAAG,IAAI,OAAO,CAAC,MAAM,CAAC;KAC7B,WAAW,CAAC,8CAA8C,CAAC;KAC3D,MAAM,CAAC,QAAQ,EAAE,gCAAgC,CAAC;KAClD,MAAM,CAAC,QAAQ,EAAE,2BAA2B,CAAC;KAC7C,MAAM,CAAC,QAAQ,EAAE,kCAAkC,CAAC;KACpD,MAAM,CAAC,iBAAiB,EAAE,0BAA0B,CAAC;KACrD,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;IACxB,IAAI,CAAC;QACH,gCAAgC;QAChC,MAAM,KAAK,GAAG,MAAM,WAAW,EAAE,CAAC;QAElC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAC,CAAC;QAC1D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,6BAA6B,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;QAClF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC,CAAC;QAE7C,6BAA6B;QAC7B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,8CAA8C,CAAC,CAAC,CAAC;QAC1E,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,2DAA2D,CAAC,CAAC,CAAC;IAEtF,CAAC;IAAC,OAAO,KAAU,EAAE,CAAC;QACpB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;QAC1D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC,CAAC,CAAC;AAEL,OAAO,EAAE,IAAI,EAAE,CAAC"}
@@ -0,0 +1,9 @@
1
+ export declare const BASE_URL: string;
2
+ export declare const DEV_URL = "http://localhost:5173";
3
+ export declare const getAuthUrl: () => string;
4
+ export declare const CLI_AUTH_PATH = "/cli-auth";
5
+ export declare const CONFIG_DIR = ".kaimon";
6
+ export declare const CONFIG_FILE = "config.json";
7
+ export declare const SUPABASE_URL: string;
8
+ export declare const SUPABASE_ANON_KEY: string;
9
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/config/constants.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,QAAQ,QAAoD,CAAC;AAG1E,eAAO,MAAM,OAAO,0BAA0B,CAAC;AAG/C,eAAO,MAAM,UAAU,QAAO,MAI7B,CAAC;AAGF,eAAO,MAAM,aAAa,cAAc,CAAC;AAGzC,eAAO,MAAM,UAAU,YAAY,CAAC;AACpC,eAAO,MAAM,WAAW,gBAAgB,CAAC;AAGzC,eAAO,MAAM,YAAY,QAAwE,CAAC;AAClG,eAAO,MAAM,iBAAiB,QAA6C,CAAC"}
@@ -0,0 +1,22 @@
1
+ // Configuration constants for Kaimon CLI
2
+ // Determine the base URL based on environment
3
+ // In development, use localhost
4
+ // In production, use the actual domain
5
+ export const BASE_URL = process.env.KAIMON_API_URL || 'https://kaimon.ai';
6
+ // For local development testing
7
+ export const DEV_URL = 'http://localhost:5173';
8
+ // Determine which URL to use
9
+ export const getAuthUrl = () => {
10
+ // Check if we're in development mode
11
+ const isDev = process.env.NODE_ENV === 'development' || process.env.KAIMON_DEV === 'true';
12
+ return isDev ? DEV_URL : BASE_URL;
13
+ };
14
+ // CLI auth endpoint
15
+ export const CLI_AUTH_PATH = '/cli-auth';
16
+ // Config file location
17
+ export const CONFIG_DIR = '.kaimon';
18
+ export const CONFIG_FILE = 'config.json';
19
+ // Supabase config (will be same as web app)
20
+ export const SUPABASE_URL = process.env.KAIMON_SUPABASE_URL || 'https://your-project.supabase.co';
21
+ export const SUPABASE_ANON_KEY = process.env.KAIMON_SUPABASE_ANON_KEY || '';
22
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/config/constants.ts"],"names":[],"mappings":"AAAA,yCAAyC;AAEzC,8CAA8C;AAC9C,gCAAgC;AAChC,uCAAuC;AACvC,MAAM,CAAC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,mBAAmB,CAAC;AAE1E,gCAAgC;AAChC,MAAM,CAAC,MAAM,OAAO,GAAG,uBAAuB,CAAC;AAE/C,6BAA6B;AAC7B,MAAM,CAAC,MAAM,UAAU,GAAG,GAAW,EAAE;IACrC,qCAAqC;IACrC,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,aAAa,IAAI,OAAO,CAAC,GAAG,CAAC,UAAU,KAAK,MAAM,CAAC;IAC1F,OAAO,KAAK,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC;AACpC,CAAC,CAAC;AAEF,oBAAoB;AACpB,MAAM,CAAC,MAAM,aAAa,GAAG,WAAW,CAAC;AAEzC,uBAAuB;AACvB,MAAM,CAAC,MAAM,UAAU,GAAG,SAAS,CAAC;AACpC,MAAM,CAAC,MAAM,WAAW,GAAG,aAAa,CAAC;AAEzC,4CAA4C;AAC5C,MAAM,CAAC,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,kCAAkC,CAAC;AAClG,MAAM,CAAC,MAAM,iBAAiB,GAAG,OAAO,CAAC,GAAG,CAAC,wBAAwB,IAAI,EAAE,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function main(): Promise<void>;
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAiBA,wBAAsB,IAAI,kBAEzB"}
package/dist/index.js ADDED
@@ -0,0 +1,17 @@
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
+ const program = new Command();
6
+ program
7
+ .name('kaimon')
8
+ .description('Sync Kaimon documentation with your codebase')
9
+ .version('0.1.0');
10
+ // Register commands
11
+ program.addCommand(auth);
12
+ program.addCommand(sync);
13
+ program.addCommand(config);
14
+ export async function main() {
15
+ await program.parseAsync(process.argv);
16
+ }
17
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAE9C,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,QAAQ,CAAC;KACd,WAAW,CAAC,8CAA8C,CAAC;KAC3D,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,oBAAoB;AACpB,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;AACzB,OAAO,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;AACzB,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;AAE3B,MAAM,CAAC,KAAK,UAAU,IAAI;IACxB,MAAM,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AACzC,CAAC"}
@@ -0,0 +1,15 @@
1
+ export interface CLIConfig {
2
+ access_token?: string;
3
+ refresh_token?: string;
4
+ expires_at?: string;
5
+ user_email?: string;
6
+ }
7
+ export declare function getConfigDir(): string;
8
+ export declare function getConfigPath(): string;
9
+ export declare function ensureConfigDir(): void;
10
+ export declare function loadConfig(): CLIConfig;
11
+ export declare function saveConfig(config: CLIConfig): void;
12
+ export declare function clearConfig(): void;
13
+ export declare function isAuthenticated(): boolean;
14
+ export declare function getAccessToken(): string | null;
15
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,SAAS;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAGD,wBAAgB,YAAY,IAAI,MAAM,CAErC;AAGD,wBAAgB,aAAa,IAAI,MAAM,CAEtC;AAGD,wBAAgB,eAAe,IAAI,IAAI,CAKtC;AAGD,wBAAgB,UAAU,IAAI,SAAS,CActC;AAGD,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,GAAG,IAAI,CAWlD;AAGD,wBAAgB,WAAW,IAAI,IAAI,CAMlC;AAGD,wBAAgB,eAAe,IAAI,OAAO,CAGzC;AAGD,wBAAgB,cAAc,IAAI,MAAM,GAAG,IAAI,CAG9C"}
@@ -0,0 +1,65 @@
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
+ // Get config directory path
6
+ export function getConfigDir() {
7
+ return path.join(os.homedir(), CONFIG_DIR);
8
+ }
9
+ // Get config file path
10
+ export function getConfigPath() {
11
+ return path.join(getConfigDir(), CONFIG_FILE);
12
+ }
13
+ // Ensure config directory exists
14
+ export function ensureConfigDir() {
15
+ const configDir = getConfigDir();
16
+ if (!fs.existsSync(configDir)) {
17
+ fs.mkdirSync(configDir, { recursive: true, mode: 0o700 }); // Only user can read/write
18
+ }
19
+ }
20
+ // Load config from file
21
+ export function loadConfig() {
22
+ const configPath = getConfigPath();
23
+ if (!fs.existsSync(configPath)) {
24
+ return {};
25
+ }
26
+ try {
27
+ const content = fs.readFileSync(configPath, 'utf-8');
28
+ return JSON.parse(content);
29
+ }
30
+ catch (error) {
31
+ console.error('Failed to load config:', error);
32
+ return {};
33
+ }
34
+ }
35
+ // Save config to file
36
+ export function saveConfig(config) {
37
+ ensureConfigDir();
38
+ const configPath = getConfigPath();
39
+ try {
40
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2), {
41
+ mode: 0o600 // Only user can read/write
42
+ });
43
+ }
44
+ catch (error) {
45
+ throw new Error(`Failed to save config: ${error}`);
46
+ }
47
+ }
48
+ // Clear config (logout)
49
+ export function clearConfig() {
50
+ const configPath = getConfigPath();
51
+ if (fs.existsSync(configPath)) {
52
+ fs.unlinkSync(configPath);
53
+ }
54
+ }
55
+ // Check if user is authenticated
56
+ export function isAuthenticated() {
57
+ const config = loadConfig();
58
+ return !!config.access_token;
59
+ }
60
+ // Get access token
61
+ export function getAccessToken() {
62
+ const config = loadConfig();
63
+ return config.access_token || null;
64
+ }
65
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/utils/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AASjE,4BAA4B;AAC5B,MAAM,UAAU,YAAY;IAC1B,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,UAAU,CAAC,CAAC;AAC7C,CAAC;AAED,uBAAuB;AACvB,MAAM,UAAU,aAAa;IAC3B,OAAO,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,WAAW,CAAC,CAAC;AAChD,CAAC;AAED,iCAAiC;AACjC,MAAM,UAAU,eAAe;IAC7B,MAAM,SAAS,GAAG,YAAY,EAAE,CAAC;IACjC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QAC9B,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,2BAA2B;IACxF,CAAC;AACH,CAAC;AAED,wBAAwB;AACxB,MAAM,UAAU,UAAU;IACxB,MAAM,UAAU,GAAG,aAAa,EAAE,CAAC;IAEnC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC/B,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,EAAE,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QACrD,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC7B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,wBAAwB,EAAE,KAAK,CAAC,CAAC;QAC/C,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC;AAED,sBAAsB;AACtB,MAAM,UAAU,UAAU,CAAC,MAAiB;IAC1C,eAAe,EAAE,CAAC;IAClB,MAAM,UAAU,GAAG,aAAa,EAAE,CAAC;IAEnC,IAAI,CAAC;QACH,EAAE,CAAC,aAAa,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE;YAC5D,IAAI,EAAE,KAAK,CAAC,2BAA2B;SACxC,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,0BAA0B,KAAK,EAAE,CAAC,CAAC;IACrD,CAAC;AACH,CAAC;AAED,wBAAwB;AACxB,MAAM,UAAU,WAAW;IACzB,MAAM,UAAU,GAAG,aAAa,EAAE,CAAC;IAEnC,IAAI,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC9B,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;IAC5B,CAAC;AACH,CAAC;AAED,iCAAiC;AACjC,MAAM,UAAU,eAAe;IAC7B,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,OAAO,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC;AAC/B,CAAC;AAED,mBAAmB;AACnB,MAAM,UAAU,cAAc;IAC5B,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,OAAO,MAAM,CAAC,YAAY,IAAI,IAAI,CAAC;AACrC,CAAC"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Validates authentication and returns the access token.
3
+ * Exits the process with helpful error message if not authenticated or token expired.
4
+ */
5
+ export declare function requireAuth(): Promise<string>;
6
+ /**
7
+ * Checks if user is authenticated without exiting.
8
+ * Returns true if authenticated and token is valid.
9
+ */
10
+ export declare function checkAuth(): boolean;
11
+ //# sourceMappingURL=requireAuth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requireAuth.d.ts","sourceRoot":"","sources":["../../src/utils/requireAuth.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC,CAkCnD;AAED;;;GAGG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAiBnC"}
@@ -0,0 +1,54 @@
1
+ import chalk from 'chalk';
2
+ import { loadConfig } from './config.js';
3
+ /**
4
+ * Validates authentication and returns the access token.
5
+ * Exits the process with helpful error message if not authenticated or token expired.
6
+ */
7
+ export async function requireAuth() {
8
+ const config = loadConfig();
9
+ // Check if token exists
10
+ if (!config.access_token) {
11
+ console.log(chalk.red('\nāŒ Not authenticated'));
12
+ console.log(chalk.yellow('Please login first:'));
13
+ console.log(chalk.cyan(' kaimon auth login\n'));
14
+ process.exit(1);
15
+ }
16
+ // Check if token is expired
17
+ if (config.expires_at) {
18
+ const expiresAt = new Date(config.expires_at);
19
+ const now = new Date();
20
+ if (expiresAt < now) {
21
+ console.log(chalk.red('\nāŒ Authentication expired'));
22
+ console.log(chalk.yellow('Your session has expired. Please login again:'));
23
+ console.log(chalk.cyan(' kaimon auth login\n'));
24
+ process.exit(1);
25
+ }
26
+ // Warn if expiring soon (within 5 minutes)
27
+ const fiveMinutes = 5 * 60 * 1000;
28
+ const timeUntilExpiry = expiresAt.getTime() - now.getTime();
29
+ if (timeUntilExpiry < fiveMinutes && timeUntilExpiry > 0) {
30
+ console.log(chalk.yellow(`āš ļø Token expires in ${Math.floor(timeUntilExpiry / 60000)} minutes`));
31
+ console.log(chalk.dim('Consider re-authenticating soon\n'));
32
+ }
33
+ }
34
+ return config.access_token;
35
+ }
36
+ /**
37
+ * Checks if user is authenticated without exiting.
38
+ * Returns true if authenticated and token is valid.
39
+ */
40
+ export function checkAuth() {
41
+ const config = loadConfig();
42
+ if (!config.access_token) {
43
+ return false;
44
+ }
45
+ if (config.expires_at) {
46
+ const expiresAt = new Date(config.expires_at);
47
+ const now = new Date();
48
+ if (expiresAt < now) {
49
+ return false;
50
+ }
51
+ }
52
+ return true;
53
+ }
54
+ //# sourceMappingURL=requireAuth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requireAuth.js","sourceRoot":"","sources":["../../src/utils/requireAuth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW;IAC/B,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAE5B,wBAAwB;IACxB,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC,CAAC;QAChD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC,CAAC;QACjD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC,CAAC;QACjD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,4BAA4B;IAC5B,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QACtB,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAC9C,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QAEvB,IAAI,SAAS,GAAG,GAAG,EAAE,CAAC;YACpB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC,CAAC;YACrD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,+CAA+C,CAAC,CAAC,CAAC;YAC3E,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC,CAAC;YACjD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,2CAA2C;QAC3C,MAAM,WAAW,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;QAClC,MAAM,eAAe,GAAG,SAAS,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC;QAE5D,IAAI,eAAe,GAAG,WAAW,IAAI,eAAe,GAAG,CAAC,EAAE,CAAC;YACzD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,wBAAwB,IAAI,CAAC,KAAK,CAAC,eAAe,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;YACjG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,mCAAmC,CAAC,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC,YAAY,CAAC;AAC7B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS;IACvB,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAE5B,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;QACtB,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QAC9C,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;QAEvB,IAAI,SAAS,GAAG,GAAG,EAAE,CAAC;YACpB,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "kaimon-cli",
3
+ "version": "0.1.2",
4
+ "description": "CLI tool to sync Kaimon documentation with your codebase",
5
+ "type": "module",
6
+ "bin": {
7
+ "kaimon": "./bin/index.js"
8
+ },
9
+ "main": "./dist/index.js",
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "dev": "ts-node src/index.ts",
13
+ "test": "vitest",
14
+ "lint": "eslint src --ext .ts",
15
+ "format": "prettier --write src",
16
+ "prepublishOnly": "npm run build"
17
+ },
18
+ "keywords": [
19
+ "kaimon",
20
+ "documentation",
21
+ "sync",
22
+ "cli"
23
+ ],
24
+ "author": "Kaimon",
25
+ "license": "MIT",
26
+ "dependencies": {
27
+ "commander": "^14.0.0",
28
+ "axios": "^1.7.0",
29
+ "chalk": "^5.3.0",
30
+ "ora": "^9.0.0",
31
+ "dotenv": "^17.0.0",
32
+ "conf": "^15.0.0",
33
+ "open": "^10.0.0"
34
+ },
35
+ "devDependencies": {
36
+ "@types/node": "^25.0.0",
37
+ "typescript": "^5.4.0",
38
+ "ts-node": "^10.9.2",
39
+ "eslint": "^10.0.0",
40
+ "@typescript-eslint/eslint-plugin": "^8.0.0",
41
+ "@typescript-eslint/parser": "^8.0.0",
42
+ "prettier": "^3.2.0",
43
+ "vitest": "^4.0.0"
44
+ },
45
+ "engines": {
46
+ "node": ">=18.0.0"
47
+ }
48
+ }
@@ -0,0 +1,170 @@
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 };
@@ -0,0 +1,72 @@
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 };
@@ -0,0 +1,30 @@
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 };
@@ -0,0 +1,27 @@
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 ADDED
@@ -0,0 +1,20 @@
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
+ }
@@ -0,0 +1,81 @@
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
+ }
@@ -0,0 +1,65 @@
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 ADDED
@@ -0,0 +1,21 @@
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
+ }