@lanonasis/cli 1.0.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 +217 -0
- package/dist/commands/auth.d.ts +1 -0
- package/dist/commands/auth.js +130 -0
- package/dist/commands/config.d.ts +2 -0
- package/dist/commands/config.js +122 -0
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.js +52 -0
- package/dist/commands/memory.d.ts +2 -0
- package/dist/commands/memory.js +401 -0
- package/dist/commands/organization.d.ts +2 -0
- package/dist/commands/organization.js +41 -0
- package/dist/commands/topics.d.ts +2 -0
- package/dist/commands/topics.js +297 -0
- package/dist/index-simple.d.ts +2 -0
- package/dist/index-simple.js +215 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +228 -0
- package/dist/utils/api.d.ts +24 -0
- package/dist/utils/api.js +141 -0
- package/dist/utils/config.d.ts +26 -0
- package/dist/utils/config.js +104 -0
- package/dist/utils/formatting.d.ts +4 -0
- package/dist/utils/formatting.js +34 -0
- package/package.json +51 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
import { config } from 'dotenv';
|
|
5
|
+
import { initCommand } from './commands/init.js';
|
|
6
|
+
import { loginCommand } from './commands/auth.js';
|
|
7
|
+
import { memoryCommands } from './commands/memory.js';
|
|
8
|
+
import { topicCommands } from './commands/topics.js';
|
|
9
|
+
import { configCommands } from './commands/config.js';
|
|
10
|
+
import { orgCommands } from './commands/organization.js';
|
|
11
|
+
import { CLIConfig } from './utils/config.js';
|
|
12
|
+
// Load environment variables
|
|
13
|
+
config();
|
|
14
|
+
const program = new Command();
|
|
15
|
+
// CLI Configuration
|
|
16
|
+
const cliConfig = new CLIConfig();
|
|
17
|
+
program
|
|
18
|
+
.name('memory')
|
|
19
|
+
.alias('maas')
|
|
20
|
+
.description('Enterprise Memory as a Service (MaaS) CLI')
|
|
21
|
+
.version('1.0.0')
|
|
22
|
+
.option('-v, --verbose', 'enable verbose logging')
|
|
23
|
+
.option('--api-url <url>', 'override API URL')
|
|
24
|
+
.option('--output <format>', 'output format (json, table, yaml)', 'table')
|
|
25
|
+
.hook('preAction', (thisCommand, actionCommand) => {
|
|
26
|
+
const opts = thisCommand.opts();
|
|
27
|
+
if (opts.verbose) {
|
|
28
|
+
process.env.CLI_VERBOSE = 'true';
|
|
29
|
+
}
|
|
30
|
+
if (opts.apiUrl) {
|
|
31
|
+
process.env.MEMORY_API_URL = opts.apiUrl;
|
|
32
|
+
}
|
|
33
|
+
process.env.CLI_OUTPUT_FORMAT = opts.output;
|
|
34
|
+
});
|
|
35
|
+
// Global error handler
|
|
36
|
+
process.on('uncaughtException', (error) => {
|
|
37
|
+
console.error(chalk.red('✖ Unexpected error:'), error.message);
|
|
38
|
+
if (process.env.CLI_VERBOSE === 'true') {
|
|
39
|
+
console.error(error.stack);
|
|
40
|
+
}
|
|
41
|
+
process.exit(1);
|
|
42
|
+
});
|
|
43
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
44
|
+
console.error(chalk.red('✖ Unhandled promise rejection:'), reason);
|
|
45
|
+
if (process.env.CLI_VERBOSE === 'true') {
|
|
46
|
+
console.error(promise);
|
|
47
|
+
}
|
|
48
|
+
process.exit(1);
|
|
49
|
+
});
|
|
50
|
+
// Welcome message for first-time users
|
|
51
|
+
const showWelcome = () => {
|
|
52
|
+
console.log(chalk.blue.bold('🧠 Memory as a Service (MaaS) CLI'));
|
|
53
|
+
console.log(chalk.gray('Enterprise-grade memory management for AI applications'));
|
|
54
|
+
console.log();
|
|
55
|
+
console.log(chalk.yellow('Get started:'));
|
|
56
|
+
console.log(chalk.white(' memory init # Initialize CLI configuration'));
|
|
57
|
+
console.log(chalk.white(' memory login # Authenticate with Supabase account'));
|
|
58
|
+
console.log(chalk.white(' memory --help # Show all available commands'));
|
|
59
|
+
console.log();
|
|
60
|
+
};
|
|
61
|
+
// Check if user is authenticated for protected commands
|
|
62
|
+
const requireAuth = (command) => {
|
|
63
|
+
command.hook('preAction', async () => {
|
|
64
|
+
const isAuthenticated = await cliConfig.isAuthenticated();
|
|
65
|
+
if (!isAuthenticated) {
|
|
66
|
+
console.error(chalk.red('✖ Authentication required'));
|
|
67
|
+
console.log(chalk.yellow('Please run:'), chalk.white('memory login'));
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
// Initialize command (no auth required)
|
|
73
|
+
program
|
|
74
|
+
.command('init')
|
|
75
|
+
.description('Initialize CLI configuration')
|
|
76
|
+
.option('-f, --force', 'overwrite existing configuration')
|
|
77
|
+
.action(initCommand);
|
|
78
|
+
// Authentication commands (no auth required)
|
|
79
|
+
const authCmd = program
|
|
80
|
+
.command('auth')
|
|
81
|
+
.alias('login')
|
|
82
|
+
.description('Authentication commands');
|
|
83
|
+
authCmd
|
|
84
|
+
.command('login')
|
|
85
|
+
.description('Login to your MaaS account')
|
|
86
|
+
.option('-e, --email <email>', 'email address')
|
|
87
|
+
.option('-p, --password <password>', 'password')
|
|
88
|
+
.action(loginCommand);
|
|
89
|
+
authCmd
|
|
90
|
+
.command('logout')
|
|
91
|
+
.description('Logout from your account')
|
|
92
|
+
.action(async () => {
|
|
93
|
+
await cliConfig.logout();
|
|
94
|
+
console.log(chalk.green('✓ Logged out successfully'));
|
|
95
|
+
});
|
|
96
|
+
authCmd
|
|
97
|
+
.command('status')
|
|
98
|
+
.description('Show authentication status')
|
|
99
|
+
.action(async () => {
|
|
100
|
+
const isAuth = await cliConfig.isAuthenticated();
|
|
101
|
+
const user = await cliConfig.getCurrentUser();
|
|
102
|
+
if (isAuth && user) {
|
|
103
|
+
console.log(chalk.green('✓ Authenticated'));
|
|
104
|
+
console.log(`Email: ${user.email}`);
|
|
105
|
+
console.log(`Organization: ${user.organization_id}`);
|
|
106
|
+
console.log(`Plan: ${user.plan}`);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
console.log(chalk.red('✖ Not authenticated'));
|
|
110
|
+
console.log(chalk.yellow('Run:'), chalk.white('memory login'));
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
// Memory commands (require auth)
|
|
114
|
+
const memoryCmd = program
|
|
115
|
+
.command('memory')
|
|
116
|
+
.alias('mem')
|
|
117
|
+
.description('Memory management commands');
|
|
118
|
+
requireAuth(memoryCmd);
|
|
119
|
+
memoryCommands(memoryCmd);
|
|
120
|
+
// Topic commands (require auth)
|
|
121
|
+
const topicCmd = program
|
|
122
|
+
.command('topic')
|
|
123
|
+
.alias('topics')
|
|
124
|
+
.description('Topic management commands');
|
|
125
|
+
requireAuth(topicCmd);
|
|
126
|
+
topicCommands(topicCmd);
|
|
127
|
+
// Configuration commands (require auth)
|
|
128
|
+
const configCmd = program
|
|
129
|
+
.command('config')
|
|
130
|
+
.description('Configuration management');
|
|
131
|
+
requireAuth(configCmd);
|
|
132
|
+
configCommands(configCmd);
|
|
133
|
+
// Organization commands (require auth)
|
|
134
|
+
const orgCmd = program
|
|
135
|
+
.command('org')
|
|
136
|
+
.alias('organization')
|
|
137
|
+
.description('Organization management');
|
|
138
|
+
requireAuth(orgCmd);
|
|
139
|
+
orgCommands(orgCmd);
|
|
140
|
+
// Global commands that don't require auth
|
|
141
|
+
program
|
|
142
|
+
.command('status')
|
|
143
|
+
.description('Show overall system status')
|
|
144
|
+
.action(async () => {
|
|
145
|
+
const isAuth = await cliConfig.isAuthenticated();
|
|
146
|
+
const apiUrl = cliConfig.getApiUrl();
|
|
147
|
+
console.log(chalk.blue.bold('MaaS CLI Status'));
|
|
148
|
+
console.log(`API URL: ${apiUrl}`);
|
|
149
|
+
console.log(`Authenticated: ${isAuth ? chalk.green('Yes') : chalk.red('No')}`);
|
|
150
|
+
if (isAuth) {
|
|
151
|
+
const user = await cliConfig.getCurrentUser();
|
|
152
|
+
if (user) {
|
|
153
|
+
console.log(`User: ${user.email}`);
|
|
154
|
+
console.log(`Plan: ${user.plan}`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
program
|
|
159
|
+
.command('docs')
|
|
160
|
+
.description('Open documentation in browser')
|
|
161
|
+
.action(() => {
|
|
162
|
+
const url = 'https://docs.seyederick.com/memory-service';
|
|
163
|
+
console.log(chalk.blue(`Opening documentation: ${url}`));
|
|
164
|
+
// Try to open in browser
|
|
165
|
+
import('open').then(open => {
|
|
166
|
+
open.default(url).catch(() => {
|
|
167
|
+
console.log(chalk.yellow('Could not open browser automatically.'));
|
|
168
|
+
console.log(chalk.white(`Please visit: ${url}`));
|
|
169
|
+
});
|
|
170
|
+
}).catch(() => {
|
|
171
|
+
console.log(chalk.white(`Please visit: ${url}`));
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
// Help customization
|
|
175
|
+
program.configureHelp({
|
|
176
|
+
formatHelp: (cmd, helper) => {
|
|
177
|
+
const term = helper.termWidth || 80;
|
|
178
|
+
const helpWidth = Math.min(term - 2, 80);
|
|
179
|
+
let help = chalk.blue.bold('🧠 Memory as a Service CLI\n\n');
|
|
180
|
+
help += helper.commandUsage(cmd) + '\n\n';
|
|
181
|
+
if (cmd.description()) {
|
|
182
|
+
help += chalk.yellow('Description:\n');
|
|
183
|
+
help += ` ${cmd.description()}\n\n`;
|
|
184
|
+
}
|
|
185
|
+
const commands = helper.visibleCommands(cmd);
|
|
186
|
+
if (commands.length > 0) {
|
|
187
|
+
help += chalk.yellow('Commands:\n');
|
|
188
|
+
const maxNameLength = Math.max(...commands.map(c => c.name().length));
|
|
189
|
+
commands.forEach(c => {
|
|
190
|
+
const name = c.name().padEnd(maxNameLength);
|
|
191
|
+
help += ` ${chalk.white(name)} ${c.description()}\n`;
|
|
192
|
+
});
|
|
193
|
+
help += '\n';
|
|
194
|
+
}
|
|
195
|
+
const options = helper.visibleOptions(cmd);
|
|
196
|
+
if (options.length > 0) {
|
|
197
|
+
help += chalk.yellow('Options:\n');
|
|
198
|
+
options.forEach(option => {
|
|
199
|
+
help += ` ${option.flags.padEnd(20)} ${option.description}\n`;
|
|
200
|
+
});
|
|
201
|
+
help += '\n';
|
|
202
|
+
}
|
|
203
|
+
help += chalk.gray('For more help on a specific command, run: memory <command> --help\n');
|
|
204
|
+
help += chalk.gray('Documentation: https://docs.seyederick.com/memory-service\n');
|
|
205
|
+
return help;
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
// Parse CLI arguments
|
|
209
|
+
async function main() {
|
|
210
|
+
// Show welcome message if no arguments provided
|
|
211
|
+
if (process.argv.length <= 2) {
|
|
212
|
+
showWelcome();
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
try {
|
|
216
|
+
await program.parseAsync(process.argv);
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
if (error instanceof Error) {
|
|
220
|
+
console.error(chalk.red('✖ Error:'), error.message);
|
|
221
|
+
if (process.env.CLI_VERBOSE === 'true') {
|
|
222
|
+
console.error(error.stack);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
process.exit(1);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
main();
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { AxiosRequestConfig } from 'axios';
|
|
2
|
+
export declare class APIClient {
|
|
3
|
+
private client;
|
|
4
|
+
private config;
|
|
5
|
+
constructor();
|
|
6
|
+
login(email: string, password: string): Promise<any>;
|
|
7
|
+
register(email: string, password: string, organizationName?: string): Promise<any>;
|
|
8
|
+
createMemory(data: any): Promise<any>;
|
|
9
|
+
getMemories(params?: any): Promise<any>;
|
|
10
|
+
getMemory(id: string): Promise<any>;
|
|
11
|
+
updateMemory(id: string, data: any): Promise<any>;
|
|
12
|
+
deleteMemory(id: string): Promise<void>;
|
|
13
|
+
searchMemories(query: string, options?: any): Promise<any>;
|
|
14
|
+
getMemoryStats(): Promise<any>;
|
|
15
|
+
bulkDeleteMemories(memoryIds: string[]): Promise<any>;
|
|
16
|
+
createTopic(data: any): Promise<any>;
|
|
17
|
+
getTopics(): Promise<any>;
|
|
18
|
+
getTopic(id: string): Promise<any>;
|
|
19
|
+
updateTopic(id: string, data: any): Promise<any>;
|
|
20
|
+
deleteTopic(id: string): Promise<void>;
|
|
21
|
+
getHealth(): Promise<any>;
|
|
22
|
+
request<T = any>(config: AxiosRequestConfig): Promise<T>;
|
|
23
|
+
}
|
|
24
|
+
export declare const apiClient: APIClient;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { CLIConfig } from './config.js';
|
|
4
|
+
export class APIClient {
|
|
5
|
+
client;
|
|
6
|
+
config;
|
|
7
|
+
constructor() {
|
|
8
|
+
this.config = new CLIConfig();
|
|
9
|
+
this.client = axios.create();
|
|
10
|
+
// Setup request interceptor to add auth token
|
|
11
|
+
this.client.interceptors.request.use(async (config) => {
|
|
12
|
+
await this.config.init();
|
|
13
|
+
const token = this.config.getToken();
|
|
14
|
+
if (token) {
|
|
15
|
+
config.headers.Authorization = `Bearer ${token}`;
|
|
16
|
+
}
|
|
17
|
+
config.baseURL = this.config.getApiUrl();
|
|
18
|
+
if (process.env.CLI_VERBOSE === 'true') {
|
|
19
|
+
console.log(chalk.dim(`→ ${config.method?.toUpperCase()} ${config.url}`));
|
|
20
|
+
}
|
|
21
|
+
return config;
|
|
22
|
+
});
|
|
23
|
+
// Setup response interceptor for error handling
|
|
24
|
+
this.client.interceptors.response.use((response) => {
|
|
25
|
+
if (process.env.CLI_VERBOSE === 'true') {
|
|
26
|
+
console.log(chalk.dim(`← ${response.status} ${response.statusText}`));
|
|
27
|
+
}
|
|
28
|
+
return response;
|
|
29
|
+
}, (error) => {
|
|
30
|
+
if (error.response) {
|
|
31
|
+
const { status, data } = error.response;
|
|
32
|
+
if (status === 401) {
|
|
33
|
+
console.error(chalk.red('✖ Authentication failed'));
|
|
34
|
+
console.log(chalk.yellow('Please run:'), chalk.white('memory login'));
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
if (status === 403) {
|
|
38
|
+
console.error(chalk.red('✖ Permission denied'));
|
|
39
|
+
if (data.message) {
|
|
40
|
+
console.error(chalk.gray(data.message));
|
|
41
|
+
}
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
if (status === 429) {
|
|
45
|
+
console.error(chalk.red('✖ Rate limit exceeded'));
|
|
46
|
+
console.error(chalk.gray('Please wait a moment before trying again'));
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
if (process.env.CLI_VERBOSE === 'true') {
|
|
50
|
+
console.error(chalk.dim(`← ${status} ${error.response.statusText}`));
|
|
51
|
+
console.error(chalk.dim(JSON.stringify(data, null, 2)));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return Promise.reject(error);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
// Authentication - aligned with Supabase auth
|
|
58
|
+
async login(email, password) {
|
|
59
|
+
const response = await this.client.post('/api/v1/auth/login', {
|
|
60
|
+
email,
|
|
61
|
+
password
|
|
62
|
+
});
|
|
63
|
+
return response.data;
|
|
64
|
+
}
|
|
65
|
+
async register(email, password, organizationName) {
|
|
66
|
+
const response = await this.client.post('/api/v1/auth/register', {
|
|
67
|
+
email,
|
|
68
|
+
password,
|
|
69
|
+
organization_name: organizationName
|
|
70
|
+
});
|
|
71
|
+
return response.data;
|
|
72
|
+
}
|
|
73
|
+
// Memory operations - aligned with existing schema
|
|
74
|
+
async createMemory(data) {
|
|
75
|
+
const response = await this.client.post('/api/v1/memory', data);
|
|
76
|
+
return response.data;
|
|
77
|
+
}
|
|
78
|
+
async getMemories(params = {}) {
|
|
79
|
+
const response = await this.client.get('/api/v1/memory', { params });
|
|
80
|
+
return response.data;
|
|
81
|
+
}
|
|
82
|
+
async getMemory(id) {
|
|
83
|
+
const response = await this.client.get(`/api/v1/memory/${id}`);
|
|
84
|
+
return response.data;
|
|
85
|
+
}
|
|
86
|
+
async updateMemory(id, data) {
|
|
87
|
+
const response = await this.client.put(`/api/v1/memory/${id}`, data);
|
|
88
|
+
return response.data;
|
|
89
|
+
}
|
|
90
|
+
async deleteMemory(id) {
|
|
91
|
+
await this.client.delete(`/api/v1/memory/${id}`);
|
|
92
|
+
}
|
|
93
|
+
async searchMemories(query, options = {}) {
|
|
94
|
+
const response = await this.client.post('/api/v1/memory/search', {
|
|
95
|
+
query,
|
|
96
|
+
...options
|
|
97
|
+
});
|
|
98
|
+
return response.data;
|
|
99
|
+
}
|
|
100
|
+
async getMemoryStats() {
|
|
101
|
+
const response = await this.client.get('/api/v1/memory/stats');
|
|
102
|
+
return response.data;
|
|
103
|
+
}
|
|
104
|
+
async bulkDeleteMemories(memoryIds) {
|
|
105
|
+
const response = await this.client.post('/api/v1/memory/bulk/delete', {
|
|
106
|
+
memory_ids: memoryIds
|
|
107
|
+
});
|
|
108
|
+
return response.data;
|
|
109
|
+
}
|
|
110
|
+
// Topic operations - working with existing memory_topics table
|
|
111
|
+
async createTopic(data) {
|
|
112
|
+
const response = await this.client.post('/api/v1/topics', data);
|
|
113
|
+
return response.data;
|
|
114
|
+
}
|
|
115
|
+
async getTopics() {
|
|
116
|
+
const response = await this.client.get('/api/v1/topics');
|
|
117
|
+
return response.data;
|
|
118
|
+
}
|
|
119
|
+
async getTopic(id) {
|
|
120
|
+
const response = await this.client.get(`/api/v1/topics/${id}`);
|
|
121
|
+
return response.data;
|
|
122
|
+
}
|
|
123
|
+
async updateTopic(id, data) {
|
|
124
|
+
const response = await this.client.put(`/api/v1/topics/${id}`, data);
|
|
125
|
+
return response.data;
|
|
126
|
+
}
|
|
127
|
+
async deleteTopic(id) {
|
|
128
|
+
await this.client.delete(`/api/v1/topics/${id}`);
|
|
129
|
+
}
|
|
130
|
+
// Health check
|
|
131
|
+
async getHealth() {
|
|
132
|
+
const response = await this.client.get('/api/v1/health');
|
|
133
|
+
return response.data;
|
|
134
|
+
}
|
|
135
|
+
// Generic request method
|
|
136
|
+
async request(config) {
|
|
137
|
+
const response = await this.client.request(config);
|
|
138
|
+
return response.data;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
export const apiClient = new APIClient();
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
interface UserProfile {
|
|
2
|
+
email: string;
|
|
3
|
+
organization_id: string;
|
|
4
|
+
role: string;
|
|
5
|
+
plan: string;
|
|
6
|
+
}
|
|
7
|
+
export declare class CLIConfig {
|
|
8
|
+
private configDir;
|
|
9
|
+
private configPath;
|
|
10
|
+
private config;
|
|
11
|
+
constructor();
|
|
12
|
+
init(): Promise<void>;
|
|
13
|
+
load(): Promise<void>;
|
|
14
|
+
save(): Promise<void>;
|
|
15
|
+
getApiUrl(): string;
|
|
16
|
+
setApiUrl(url: string): Promise<void>;
|
|
17
|
+
setToken(token: string): Promise<void>;
|
|
18
|
+
getToken(): string | undefined;
|
|
19
|
+
getCurrentUser(): Promise<UserProfile | undefined>;
|
|
20
|
+
isAuthenticated(): Promise<boolean>;
|
|
21
|
+
logout(): Promise<void>;
|
|
22
|
+
clear(): Promise<void>;
|
|
23
|
+
getConfigPath(): string;
|
|
24
|
+
exists(): Promise<boolean>;
|
|
25
|
+
}
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import fs from 'fs/promises';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import os from 'os';
|
|
4
|
+
import { jwtDecode } from 'jwt-decode';
|
|
5
|
+
export class CLIConfig {
|
|
6
|
+
configDir;
|
|
7
|
+
configPath;
|
|
8
|
+
config = {};
|
|
9
|
+
constructor() {
|
|
10
|
+
this.configDir = path.join(os.homedir(), '.maas');
|
|
11
|
+
this.configPath = path.join(this.configDir, 'config.json');
|
|
12
|
+
}
|
|
13
|
+
async init() {
|
|
14
|
+
try {
|
|
15
|
+
await fs.mkdir(this.configDir, { recursive: true });
|
|
16
|
+
await this.load();
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
// Config doesn't exist yet, that's ok
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
async load() {
|
|
23
|
+
try {
|
|
24
|
+
const data = await fs.readFile(this.configPath, 'utf-8');
|
|
25
|
+
this.config = JSON.parse(data);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
this.config = {};
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
async save() {
|
|
32
|
+
await fs.mkdir(this.configDir, { recursive: true });
|
|
33
|
+
this.config.lastUpdated = new Date().toISOString();
|
|
34
|
+
await fs.writeFile(this.configPath, JSON.stringify(this.config, null, 2));
|
|
35
|
+
}
|
|
36
|
+
getApiUrl() {
|
|
37
|
+
return process.env.MEMORY_API_URL ||
|
|
38
|
+
this.config.apiUrl ||
|
|
39
|
+
'http://localhost:3000/api/v1';
|
|
40
|
+
}
|
|
41
|
+
async setApiUrl(url) {
|
|
42
|
+
this.config.apiUrl = url;
|
|
43
|
+
await this.save();
|
|
44
|
+
}
|
|
45
|
+
async setToken(token) {
|
|
46
|
+
this.config.token = token;
|
|
47
|
+
// Decode token to get user info
|
|
48
|
+
try {
|
|
49
|
+
const decoded = jwtDecode(token);
|
|
50
|
+
// We'll need to fetch full user details from the API
|
|
51
|
+
// For now, store what we can decode
|
|
52
|
+
this.config.user = {
|
|
53
|
+
email: decoded.email || '',
|
|
54
|
+
organization_id: decoded.organizationId || '',
|
|
55
|
+
role: decoded.role || '',
|
|
56
|
+
plan: decoded.plan || ''
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
catch (error) {
|
|
60
|
+
// Invalid token, don't store user info
|
|
61
|
+
}
|
|
62
|
+
await this.save();
|
|
63
|
+
}
|
|
64
|
+
getToken() {
|
|
65
|
+
return this.config.token;
|
|
66
|
+
}
|
|
67
|
+
async getCurrentUser() {
|
|
68
|
+
return this.config.user;
|
|
69
|
+
}
|
|
70
|
+
async isAuthenticated() {
|
|
71
|
+
const token = this.getToken();
|
|
72
|
+
if (!token)
|
|
73
|
+
return false;
|
|
74
|
+
try {
|
|
75
|
+
const decoded = jwtDecode(token);
|
|
76
|
+
const now = Date.now() / 1000;
|
|
77
|
+
return decoded.exp > now;
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async logout() {
|
|
84
|
+
this.config.token = undefined;
|
|
85
|
+
this.config.user = undefined;
|
|
86
|
+
await this.save();
|
|
87
|
+
}
|
|
88
|
+
async clear() {
|
|
89
|
+
this.config = {};
|
|
90
|
+
await this.save();
|
|
91
|
+
}
|
|
92
|
+
getConfigPath() {
|
|
93
|
+
return this.configPath;
|
|
94
|
+
}
|
|
95
|
+
async exists() {
|
|
96
|
+
try {
|
|
97
|
+
await fs.access(this.configPath);
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare function formatOutput(data: any, format?: string): void;
|
|
2
|
+
export declare function formatBytes(bytes: number): string;
|
|
3
|
+
export declare function truncateText(text: string, maxLength: number): string;
|
|
4
|
+
export declare function formatDuration(ms: number): string;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export function formatOutput(data, format = 'table') {
|
|
2
|
+
switch (format) {
|
|
3
|
+
case 'json':
|
|
4
|
+
console.log(JSON.stringify(data, null, 2));
|
|
5
|
+
break;
|
|
6
|
+
case 'yaml':
|
|
7
|
+
// Would need to import yaml library
|
|
8
|
+
console.log('YAML output not implemented yet');
|
|
9
|
+
break;
|
|
10
|
+
default:
|
|
11
|
+
// Table format is handled by calling code
|
|
12
|
+
break;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export function formatBytes(bytes) {
|
|
16
|
+
if (bytes === 0)
|
|
17
|
+
return '0 Bytes';
|
|
18
|
+
const k = 1024;
|
|
19
|
+
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
|
20
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
21
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
22
|
+
}
|
|
23
|
+
export function truncateText(text, maxLength) {
|
|
24
|
+
if (text.length <= maxLength)
|
|
25
|
+
return text;
|
|
26
|
+
return text.substring(0, maxLength - 3) + '...';
|
|
27
|
+
}
|
|
28
|
+
export function formatDuration(ms) {
|
|
29
|
+
if (ms < 1000)
|
|
30
|
+
return `${ms}ms`;
|
|
31
|
+
if (ms < 60000)
|
|
32
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
33
|
+
return `${(ms / 60000).toFixed(1)}m`;
|
|
34
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@lanonasis/cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Lanonasis Enterprise CLI - Memory as a Service and Infrastructure Management",
|
|
5
|
+
"main": "dist/index-simple.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"lanonasis": "dist/index-simple.js",
|
|
8
|
+
"memory": "dist/index-simple.js",
|
|
9
|
+
"maas": "dist/index-simple.js"
|
|
10
|
+
},
|
|
11
|
+
"type": "module",
|
|
12
|
+
"scripts": {
|
|
13
|
+
"dev": "tsx src/index.ts",
|
|
14
|
+
"build": "tsc && chmod +x dist/index-simple.js",
|
|
15
|
+
"start": "node dist/index.js",
|
|
16
|
+
"test": "jest",
|
|
17
|
+
"lint": "eslint src/**/*.ts",
|
|
18
|
+
"type-check": "tsc --noEmit"
|
|
19
|
+
},
|
|
20
|
+
"keywords": [
|
|
21
|
+
"lanonasis",
|
|
22
|
+
"cli",
|
|
23
|
+
"memory",
|
|
24
|
+
"ai",
|
|
25
|
+
"maas",
|
|
26
|
+
"enterprise",
|
|
27
|
+
"infrastructure",
|
|
28
|
+
"orchestrator"
|
|
29
|
+
],
|
|
30
|
+
"author": "Lanonasis (Seye Derick)",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"files": [
|
|
33
|
+
"dist",
|
|
34
|
+
"README.md"
|
|
35
|
+
],
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"commander": "^12.1.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/node": "^22.10.2",
|
|
41
|
+
"@types/inquirer": "^9.0.7",
|
|
42
|
+
"@typescript-eslint/eslint-plugin": "^8.18.1",
|
|
43
|
+
"@typescript-eslint/parser": "^8.18.1",
|
|
44
|
+
"eslint": "^9.17.0",
|
|
45
|
+
"tsx": "^4.19.2",
|
|
46
|
+
"typescript": "^5.7.2"
|
|
47
|
+
},
|
|
48
|
+
"engines": {
|
|
49
|
+
"node": ">=18.0.0"
|
|
50
|
+
}
|
|
51
|
+
}
|