agentmemory-cli 1.0.0 → 1.3.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/dist/commands/connect.d.ts +11 -0
- package/dist/commands/connect.d.ts.map +1 -0
- package/dist/commands/connect.js +232 -0
- package/dist/commands/connect.js.map +1 -0
- package/dist/commands/delete.d.ts.map +1 -1
- package/dist/commands/delete.js +3 -0
- package/dist/commands/delete.js.map +1 -1
- package/dist/commands/download.d.ts +5 -0
- package/dist/commands/download.d.ts.map +1 -0
- package/dist/commands/download.js +82 -0
- package/dist/commands/download.js.map +1 -0
- package/dist/commands/export.d.ts.map +1 -1
- package/dist/commands/export.js +3 -0
- package/dist/commands/export.js.map +1 -1
- package/dist/commands/files.d.ts +6 -0
- package/dist/commands/files.d.ts.map +1 -0
- package/dist/commands/files.js +101 -0
- package/dist/commands/files.js.map +1 -0
- package/dist/commands/heartbeat.d.ts +65 -0
- package/dist/commands/heartbeat.d.ts.map +1 -0
- package/dist/commands/heartbeat.js +176 -0
- package/dist/commands/heartbeat.js.map +1 -0
- package/dist/commands/import.d.ts.map +1 -1
- package/dist/commands/import.js +3 -0
- package/dist/commands/import.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +39 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/list.d.ts.map +1 -1
- package/dist/commands/list.js +3 -0
- package/dist/commands/list.js.map +1 -1
- package/dist/commands/search.d.ts.map +1 -1
- package/dist/commands/search.js +3 -0
- package/dist/commands/search.js.map +1 -1
- package/dist/commands/secret.d.ts +25 -0
- package/dist/commands/secret.d.ts.map +1 -0
- package/dist/commands/secret.js +390 -0
- package/dist/commands/secret.js.map +1 -0
- package/dist/commands/store.d.ts.map +1 -1
- package/dist/commands/store.js +3 -0
- package/dist/commands/store.js.map +1 -1
- package/dist/commands/upload.d.ts +4 -0
- package/dist/commands/upload.d.ts.map +1 -0
- package/dist/commands/upload.js +110 -0
- package/dist/commands/upload.js.map +1 -0
- package/dist/index.js +136 -2
- package/dist/index.js.map +1 -1
- package/dist/lib/api.d.ts +23 -1
- package/dist/lib/api.d.ts.map +1 -1
- package/dist/lib/api.js +49 -0
- package/dist/lib/api.js.map +1 -1
- package/dist/lib/autosync.d.ts +14 -0
- package/dist/lib/autosync.d.ts.map +1 -0
- package/dist/lib/autosync.js +165 -0
- package/dist/lib/autosync.js.map +1 -0
- package/dist/types.d.ts +59 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/commands/connect.ts +216 -0
- package/src/commands/delete.ts +4 -0
- package/src/commands/download.ts +105 -0
- package/src/commands/export.ts +4 -0
- package/src/commands/files.ts +119 -0
- package/src/commands/heartbeat.ts +241 -0
- package/src/commands/import.ts +4 -0
- package/src/commands/init.ts +44 -1
- package/src/commands/list.ts +4 -0
- package/src/commands/search.ts +4 -0
- package/src/commands/secret.ts +438 -0
- package/src/commands/store.ts +4 -0
- package/src/commands/upload.ts +117 -0
- package/src/index.ts +158 -2
- package/src/lib/api.ts +86 -1
- package/src/lib/autosync.ts +160 -0
- package/src/types.ts +67 -0
|
@@ -0,0 +1,438 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import { createInterface } from 'readline';
|
|
4
|
+
import { getApiKey } from '../lib/config.js';
|
|
5
|
+
import { listSecrets, getSecret, setSecret, deleteSecret, getAllSecrets } from '../lib/api.js';
|
|
6
|
+
import { autoSync } from '../lib/autosync.js';
|
|
7
|
+
import type { SecretType, SecretListItem } from '../types.js';
|
|
8
|
+
|
|
9
|
+
// Prompt helper
|
|
10
|
+
async function prompt(question: string): Promise<string> {
|
|
11
|
+
const rl = createInterface({
|
|
12
|
+
input: process.stdin,
|
|
13
|
+
output: process.stdout,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
return new Promise((resolve) => {
|
|
17
|
+
rl.question(question, (answer) => {
|
|
18
|
+
rl.close();
|
|
19
|
+
resolve(answer.trim());
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function confirm(question: string): Promise<boolean> {
|
|
25
|
+
const answer = await prompt(question);
|
|
26
|
+
return answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes';
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function promptPassword(question: string): Promise<string> {
|
|
30
|
+
// Simple password prompt (in production, use a library that masks input)
|
|
31
|
+
return prompt(question);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const VALID_TYPES: SecretType[] = ['api_key', 'credential', 'connection_string', 'env_var', 'generic'];
|
|
35
|
+
|
|
36
|
+
// Helper to mask secret values
|
|
37
|
+
function maskValue(value: string): string {
|
|
38
|
+
if (value.length <= 8) {
|
|
39
|
+
return '*'.repeat(value.length);
|
|
40
|
+
}
|
|
41
|
+
return value.slice(0, 4) + '*'.repeat(value.length - 8) + value.slice(-4);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Format secret type with icon
|
|
45
|
+
function formatType(type: SecretType): string {
|
|
46
|
+
const icons: Record<SecretType, string> = {
|
|
47
|
+
api_key: '🔑',
|
|
48
|
+
credential: '🔐',
|
|
49
|
+
connection_string: '🔗',
|
|
50
|
+
env_var: '📦',
|
|
51
|
+
generic: '📄',
|
|
52
|
+
};
|
|
53
|
+
return `${icons[type] || '📄'} ${type}`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Set a secret
|
|
57
|
+
export async function secretSetCommand(
|
|
58
|
+
name: string,
|
|
59
|
+
value: string,
|
|
60
|
+
options: {
|
|
61
|
+
type?: string;
|
|
62
|
+
description?: string;
|
|
63
|
+
json?: boolean;
|
|
64
|
+
}
|
|
65
|
+
): Promise<void> {
|
|
66
|
+
const apiKey = getApiKey();
|
|
67
|
+
|
|
68
|
+
// Auto-sync in background (silent)
|
|
69
|
+
autoSync();
|
|
70
|
+
|
|
71
|
+
if (!apiKey) {
|
|
72
|
+
console.error(chalk.red('Error: Not configured. Run "agentmemory init" first.'));
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Validate type
|
|
77
|
+
const type = (options.type || 'generic') as SecretType;
|
|
78
|
+
if (!VALID_TYPES.includes(type)) {
|
|
79
|
+
console.error(chalk.red(`Error: Invalid type. Must be one of: ${VALID_TYPES.join(', ')}`));
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
const response = await setSecret(name, value, type, options.description);
|
|
85
|
+
|
|
86
|
+
if (options.json) {
|
|
87
|
+
console.log(JSON.stringify(response, null, 2));
|
|
88
|
+
} else {
|
|
89
|
+
const action = response.message?.includes('updated') ? 'updated' : 'created';
|
|
90
|
+
console.log(chalk.green(`✓ Secret "${name}" ${action} successfully`));
|
|
91
|
+
console.log(chalk.gray(` Type: ${formatType(type)}`));
|
|
92
|
+
if (options.description) {
|
|
93
|
+
console.log(chalk.gray(` Description: ${options.description}`));
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
} catch (error) {
|
|
97
|
+
if (options.json) {
|
|
98
|
+
console.log(JSON.stringify({ error: error instanceof Error ? error.message : 'Unknown error' }));
|
|
99
|
+
} else {
|
|
100
|
+
console.error(chalk.red(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
101
|
+
}
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Get a secret value
|
|
107
|
+
export async function secretGetCommand(
|
|
108
|
+
name: string,
|
|
109
|
+
options: {
|
|
110
|
+
show?: boolean;
|
|
111
|
+
json?: boolean;
|
|
112
|
+
}
|
|
113
|
+
): Promise<void> {
|
|
114
|
+
const apiKey = getApiKey();
|
|
115
|
+
|
|
116
|
+
// Auto-sync in background (silent)
|
|
117
|
+
autoSync();
|
|
118
|
+
|
|
119
|
+
if (!apiKey) {
|
|
120
|
+
console.error(chalk.red('Error: Not configured. Run "agentmemory init" first.'));
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
try {
|
|
125
|
+
const secret = await getSecret(name);
|
|
126
|
+
|
|
127
|
+
if (options.json) {
|
|
128
|
+
// In JSON mode, always show full value
|
|
129
|
+
console.log(JSON.stringify(secret, null, 2));
|
|
130
|
+
} else {
|
|
131
|
+
console.log(chalk.cyan(`Secret: ${secret.name}`));
|
|
132
|
+
console.log(chalk.gray(`Type: ${formatType(secret.type)}`));
|
|
133
|
+
if (secret.description) {
|
|
134
|
+
console.log(chalk.gray(`Description: ${secret.description}`));
|
|
135
|
+
}
|
|
136
|
+
console.log();
|
|
137
|
+
|
|
138
|
+
// Show value (masked by default)
|
|
139
|
+
if (options.show) {
|
|
140
|
+
console.log(chalk.yellow('Value:'), secret.value);
|
|
141
|
+
} else {
|
|
142
|
+
console.log(chalk.yellow('Value:'), maskValue(secret.value || ''));
|
|
143
|
+
console.log(chalk.gray(' (use --show to reveal full value)'));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
} catch (error) {
|
|
147
|
+
if (options.json) {
|
|
148
|
+
console.log(JSON.stringify({ error: error instanceof Error ? error.message : 'Unknown error' }));
|
|
149
|
+
} else {
|
|
150
|
+
console.error(chalk.red(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
151
|
+
}
|
|
152
|
+
process.exit(1);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// List all secrets
|
|
157
|
+
export async function secretListCommand(
|
|
158
|
+
options: {
|
|
159
|
+
type?: string;
|
|
160
|
+
json?: boolean;
|
|
161
|
+
}
|
|
162
|
+
): Promise<void> {
|
|
163
|
+
const apiKey = getApiKey();
|
|
164
|
+
|
|
165
|
+
// Auto-sync in background (silent)
|
|
166
|
+
autoSync();
|
|
167
|
+
|
|
168
|
+
if (!apiKey) {
|
|
169
|
+
console.error(chalk.red('Error: Not configured. Run "agentmemory init" first.'));
|
|
170
|
+
process.exit(1);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
try {
|
|
174
|
+
const type = options.type as SecretType | undefined;
|
|
175
|
+
if (type && !VALID_TYPES.includes(type)) {
|
|
176
|
+
console.error(chalk.red(`Error: Invalid type. Must be one of: ${VALID_TYPES.join(', ')}`));
|
|
177
|
+
process.exit(1);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const response = await listSecrets(500, type);
|
|
181
|
+
|
|
182
|
+
if (options.json) {
|
|
183
|
+
console.log(JSON.stringify(response, null, 2));
|
|
184
|
+
} else {
|
|
185
|
+
if (response.secrets.length === 0) {
|
|
186
|
+
console.log(chalk.yellow('No secrets found.'));
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
console.log(chalk.cyan(`\nSecrets (${response.total} total):\n`));
|
|
191
|
+
|
|
192
|
+
for (const secret of response.secrets) {
|
|
193
|
+
console.log(` ${chalk.white(secret.name)}`);
|
|
194
|
+
console.log(` ${formatType(secret.type)}`);
|
|
195
|
+
if (secret.description) {
|
|
196
|
+
console.log(` ${chalk.gray(secret.description)}`);
|
|
197
|
+
}
|
|
198
|
+
console.log();
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
} catch (error) {
|
|
202
|
+
if (options.json) {
|
|
203
|
+
console.log(JSON.stringify({ error: error instanceof Error ? error.message : 'Unknown error' }));
|
|
204
|
+
} else {
|
|
205
|
+
console.error(chalk.red(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
206
|
+
}
|
|
207
|
+
process.exit(1);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Delete a secret
|
|
212
|
+
export async function secretDeleteCommand(
|
|
213
|
+
name: string,
|
|
214
|
+
options: {
|
|
215
|
+
force?: boolean;
|
|
216
|
+
json?: boolean;
|
|
217
|
+
}
|
|
218
|
+
): Promise<void> {
|
|
219
|
+
const apiKey = getApiKey();
|
|
220
|
+
|
|
221
|
+
// Auto-sync in background (silent)
|
|
222
|
+
autoSync();
|
|
223
|
+
|
|
224
|
+
if (!apiKey) {
|
|
225
|
+
console.error(chalk.red('Error: Not configured. Run "agentmemory init" first.'));
|
|
226
|
+
process.exit(1);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Confirmation (unless --force)
|
|
230
|
+
if (!options.force && !options.json) {
|
|
231
|
+
const confirmed = await confirm(`Are you sure you want to delete secret "${name}"? (y/N): `);
|
|
232
|
+
|
|
233
|
+
if (!confirmed) {
|
|
234
|
+
console.log(chalk.yellow('Cancelled.'));
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
try {
|
|
240
|
+
await deleteSecret(name);
|
|
241
|
+
|
|
242
|
+
if (options.json) {
|
|
243
|
+
console.log(JSON.stringify({ success: true, deleted: name }));
|
|
244
|
+
} else {
|
|
245
|
+
console.log(chalk.green(`✓ Secret "${name}" deleted successfully`));
|
|
246
|
+
}
|
|
247
|
+
} catch (error) {
|
|
248
|
+
if (options.json) {
|
|
249
|
+
console.log(JSON.stringify({ error: error instanceof Error ? error.message : 'Unknown error' }));
|
|
250
|
+
} else {
|
|
251
|
+
console.error(chalk.red(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
252
|
+
}
|
|
253
|
+
process.exit(1);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
// Export secrets to file (encrypted with password)
|
|
258
|
+
export async function secretsExportCommand(
|
|
259
|
+
options: {
|
|
260
|
+
output?: string;
|
|
261
|
+
json?: boolean;
|
|
262
|
+
}
|
|
263
|
+
): Promise<void> {
|
|
264
|
+
const apiKey = getApiKey();
|
|
265
|
+
|
|
266
|
+
// Auto-sync in background (silent)
|
|
267
|
+
autoSync();
|
|
268
|
+
|
|
269
|
+
if (!apiKey) {
|
|
270
|
+
console.error(chalk.red('Error: Not configured. Run "agentmemory init" first.'));
|
|
271
|
+
process.exit(1);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
try {
|
|
275
|
+
// Fetch all secrets with values
|
|
276
|
+
const secretsList = await getAllSecrets();
|
|
277
|
+
|
|
278
|
+
// Get full values for each secret
|
|
279
|
+
const secretsWithValues: Array<{
|
|
280
|
+
name: string;
|
|
281
|
+
value: string;
|
|
282
|
+
type: string;
|
|
283
|
+
description?: string;
|
|
284
|
+
}> = [];
|
|
285
|
+
|
|
286
|
+
for (const secretItem of secretsList) {
|
|
287
|
+
const fullSecret = await getSecret(secretItem.name);
|
|
288
|
+
secretsWithValues.push({
|
|
289
|
+
name: fullSecret.name,
|
|
290
|
+
value: fullSecret.value || '',
|
|
291
|
+
type: fullSecret.type,
|
|
292
|
+
description: fullSecret.description,
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Ask for password for encryption
|
|
297
|
+
const password = await promptPassword('Enter password to encrypt secrets (min 8 chars): ');
|
|
298
|
+
|
|
299
|
+
if (!password || password.length < 8) {
|
|
300
|
+
console.error(chalk.red('Error: Password must be at least 8 characters'));
|
|
301
|
+
process.exit(1);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Simple XOR encryption (for demonstration - in production use proper crypto)
|
|
305
|
+
const exportData = {
|
|
306
|
+
version: 1,
|
|
307
|
+
encrypted: true,
|
|
308
|
+
secrets: secretsWithValues.map(s => ({
|
|
309
|
+
...s,
|
|
310
|
+
value: simpleEncrypt(s.value, password),
|
|
311
|
+
})),
|
|
312
|
+
exported_at: new Date().toISOString(),
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
const outputPath = options.output || 'secrets-export.json';
|
|
316
|
+
fs.writeFileSync(outputPath, JSON.stringify(exportData, null, 2));
|
|
317
|
+
|
|
318
|
+
if (options.json) {
|
|
319
|
+
console.log(JSON.stringify({ success: true, file: outputPath, count: secretsWithValues.length }));
|
|
320
|
+
} else {
|
|
321
|
+
console.log(chalk.green(`✓ Exported ${secretsWithValues.length} secrets to ${outputPath}`));
|
|
322
|
+
console.log(chalk.yellow('⚠ Keep this file secure! It contains encrypted secrets.'));
|
|
323
|
+
}
|
|
324
|
+
} catch (error) {
|
|
325
|
+
if (options.json) {
|
|
326
|
+
console.log(JSON.stringify({ error: error instanceof Error ? error.message : 'Unknown error' }));
|
|
327
|
+
} else {
|
|
328
|
+
console.error(chalk.red(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
329
|
+
}
|
|
330
|
+
process.exit(1);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
// Import secrets from file
|
|
335
|
+
export async function secretsImportCommand(
|
|
336
|
+
file: string,
|
|
337
|
+
options: {
|
|
338
|
+
json?: boolean;
|
|
339
|
+
}
|
|
340
|
+
): Promise<void> {
|
|
341
|
+
const apiKey = getApiKey();
|
|
342
|
+
|
|
343
|
+
// Auto-sync in background (silent)
|
|
344
|
+
autoSync();
|
|
345
|
+
|
|
346
|
+
if (!apiKey) {
|
|
347
|
+
console.error(chalk.red('Error: Not configured. Run "agentmemory init" first.'));
|
|
348
|
+
process.exit(1);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
if (!fs.existsSync(file)) {
|
|
352
|
+
console.error(chalk.red(`Error: File not found: ${file}`));
|
|
353
|
+
process.exit(1);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
try {
|
|
357
|
+
const content = fs.readFileSync(file, 'utf-8');
|
|
358
|
+
const data = JSON.parse(content) as {
|
|
359
|
+
version: number;
|
|
360
|
+
encrypted: boolean;
|
|
361
|
+
secrets: Array<{
|
|
362
|
+
name: string;
|
|
363
|
+
value: string;
|
|
364
|
+
type: string;
|
|
365
|
+
description?: string;
|
|
366
|
+
}>;
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
let secrets = data.secrets;
|
|
370
|
+
|
|
371
|
+
// Decrypt if encrypted
|
|
372
|
+
if (data.encrypted) {
|
|
373
|
+
const password = await promptPassword('Enter password to decrypt secrets: ');
|
|
374
|
+
|
|
375
|
+
secrets = secrets.map(s => ({
|
|
376
|
+
...s,
|
|
377
|
+
value: simpleDecrypt(s.value, password),
|
|
378
|
+
}));
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
// Import secrets
|
|
382
|
+
let imported = 0;
|
|
383
|
+
let errors = 0;
|
|
384
|
+
|
|
385
|
+
for (const secret of secrets) {
|
|
386
|
+
try {
|
|
387
|
+
await setSecret(
|
|
388
|
+
secret.name,
|
|
389
|
+
secret.value,
|
|
390
|
+
secret.type as SecretType,
|
|
391
|
+
secret.description
|
|
392
|
+
);
|
|
393
|
+
imported++;
|
|
394
|
+
if (!options.json) {
|
|
395
|
+
console.log(chalk.green(`✓ Imported: ${secret.name}`));
|
|
396
|
+
}
|
|
397
|
+
} catch (err) {
|
|
398
|
+
errors++;
|
|
399
|
+
if (!options.json) {
|
|
400
|
+
console.log(chalk.red(`✗ Failed: ${secret.name} - ${err instanceof Error ? err.message : 'Unknown error'}`));
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (options.json) {
|
|
406
|
+
console.log(JSON.stringify({ success: true, imported, errors, total: secrets.length }));
|
|
407
|
+
} else {
|
|
408
|
+
console.log();
|
|
409
|
+
console.log(chalk.cyan(`Import complete: ${imported} imported, ${errors} errors`));
|
|
410
|
+
}
|
|
411
|
+
} catch (error) {
|
|
412
|
+
if (options.json) {
|
|
413
|
+
console.log(JSON.stringify({ error: error instanceof Error ? error.message : 'Unknown error' }));
|
|
414
|
+
} else {
|
|
415
|
+
console.error(chalk.red(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
416
|
+
}
|
|
417
|
+
process.exit(1);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Simple XOR-based encryption (for demonstration)
|
|
422
|
+
// In production, use Node.js crypto module with AES-256-GCM
|
|
423
|
+
function simpleEncrypt(text: string, password: string): string {
|
|
424
|
+
const result: number[] = [];
|
|
425
|
+
for (let i = 0; i < text.length; i++) {
|
|
426
|
+
result.push(text.charCodeAt(i) ^ password.charCodeAt(i % password.length));
|
|
427
|
+
}
|
|
428
|
+
return Buffer.from(result).toString('base64');
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function simpleDecrypt(encoded: string, password: string): string {
|
|
432
|
+
const data = Buffer.from(encoded, 'base64');
|
|
433
|
+
const result: string[] = [];
|
|
434
|
+
for (let i = 0; i < data.length; i++) {
|
|
435
|
+
result.push(String.fromCharCode(data[i] ^ password.charCodeAt(i % password.length)));
|
|
436
|
+
}
|
|
437
|
+
return result.join('');
|
|
438
|
+
}
|
package/src/commands/store.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { storeMemory } from '../lib/api.js';
|
|
3
3
|
import { isConfigured } from '../lib/config.js';
|
|
4
|
+
import { autoSync } from '../lib/autosync.js';
|
|
4
5
|
|
|
5
6
|
interface StoreOptions {
|
|
6
7
|
category?: string;
|
|
@@ -17,6 +18,9 @@ export async function storeCommand(
|
|
|
17
18
|
process.exit(1);
|
|
18
19
|
}
|
|
19
20
|
|
|
21
|
+
// Auto-sync in background (silent)
|
|
22
|
+
autoSync();
|
|
23
|
+
|
|
20
24
|
try {
|
|
21
25
|
// Build metadata
|
|
22
26
|
let metadata: Record<string, unknown> = {};
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { getApiKey, getApiUrl } from '../lib/config.js';
|
|
5
|
+
import { autoSync } from '../lib/autosync.js';
|
|
6
|
+
|
|
7
|
+
export async function uploadCommand(
|
|
8
|
+
filePath: string,
|
|
9
|
+
options: { description?: string }
|
|
10
|
+
): Promise<void> {
|
|
11
|
+
const apiKey = getApiKey();
|
|
12
|
+
if (!apiKey) {
|
|
13
|
+
console.error(chalk.red('Not configured. Run: agentmemory init'));
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Auto-sync in background (silent)
|
|
18
|
+
autoSync();
|
|
19
|
+
|
|
20
|
+
// Check if file exists
|
|
21
|
+
if (!fs.existsSync(filePath)) {
|
|
22
|
+
console.error(chalk.red(`File not found: ${filePath}`));
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const absolutePath = path.resolve(filePath);
|
|
27
|
+
const fileName = path.basename(absolutePath);
|
|
28
|
+
const fileBuffer = fs.readFileSync(absolutePath);
|
|
29
|
+
const fileSize = fileBuffer.length;
|
|
30
|
+
|
|
31
|
+
// Check file size (100MB limit)
|
|
32
|
+
if (fileSize > 100 * 1024 * 1024) {
|
|
33
|
+
console.error(chalk.red('File too large. Maximum size is 100MB.'));
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
console.log(chalk.blue(`Uploading ${fileName} (${formatSize(fileSize)})...`));
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
// Create FormData
|
|
41
|
+
const FormData = (await import('form-data')).default;
|
|
42
|
+
const form = new FormData();
|
|
43
|
+
form.append('file', fileBuffer, {
|
|
44
|
+
filename: fileName,
|
|
45
|
+
contentType: getMimeType(fileName),
|
|
46
|
+
});
|
|
47
|
+
if (options.description) {
|
|
48
|
+
form.append('description', options.description);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const apiUrl = getApiUrl();
|
|
52
|
+
|
|
53
|
+
// Use node-fetch with form-data
|
|
54
|
+
const nodeFetch = (await import('node-fetch')).default;
|
|
55
|
+
const response = await nodeFetch(`${apiUrl}/files`, {
|
|
56
|
+
method: 'POST',
|
|
57
|
+
headers: {
|
|
58
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
59
|
+
...form.getHeaders(),
|
|
60
|
+
},
|
|
61
|
+
body: form,
|
|
62
|
+
}) as unknown as Response;
|
|
63
|
+
|
|
64
|
+
const data = await response.json() as { success?: boolean; memory?: { id: string; file_name: string }; error?: string };
|
|
65
|
+
|
|
66
|
+
if (!response.ok) {
|
|
67
|
+
console.error(chalk.red(`Error: ${data.error || 'Upload failed'}`));
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
console.log(chalk.green('✓ File uploaded successfully!'));
|
|
72
|
+
console.log(chalk.gray(` ID: ${data.memory?.id}`));
|
|
73
|
+
console.log(chalk.gray(` Name: ${data.memory?.file_name}`));
|
|
74
|
+
} catch (error) {
|
|
75
|
+
console.error(chalk.red(`Upload failed: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function formatSize(bytes: number): string {
|
|
81
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
82
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
83
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function getMimeType(fileName: string): string {
|
|
87
|
+
const ext = path.extname(fileName).toLowerCase();
|
|
88
|
+
const mimeTypes: Record<string, string> = {
|
|
89
|
+
'.txt': 'text/plain',
|
|
90
|
+
'.md': 'text/markdown',
|
|
91
|
+
'.json': 'application/json',
|
|
92
|
+
'.pdf': 'application/pdf',
|
|
93
|
+
'.jpg': 'image/jpeg',
|
|
94
|
+
'.jpeg': 'image/jpeg',
|
|
95
|
+
'.png': 'image/png',
|
|
96
|
+
'.gif': 'image/gif',
|
|
97
|
+
'.webp': 'image/webp',
|
|
98
|
+
'.svg': 'image/svg+xml',
|
|
99
|
+
'.mp3': 'audio/mpeg',
|
|
100
|
+
'.wav': 'audio/wav',
|
|
101
|
+
'.mp4': 'video/mp4',
|
|
102
|
+
'.webm': 'video/webm',
|
|
103
|
+
'.doc': 'application/msword',
|
|
104
|
+
'.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
|
105
|
+
'.xls': 'application/vnd.ms-excel',
|
|
106
|
+
'.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
107
|
+
'.zip': 'application/zip',
|
|
108
|
+
'.js': 'text/javascript',
|
|
109
|
+
'.ts': 'text/typescript',
|
|
110
|
+
'.py': 'text/x-python',
|
|
111
|
+
'.html': 'text/html',
|
|
112
|
+
'.css': 'text/css',
|
|
113
|
+
'.yaml': 'text/yaml',
|
|
114
|
+
'.yml': 'text/yaml',
|
|
115
|
+
};
|
|
116
|
+
return mimeTypes[ext] || 'application/octet-stream';
|
|
117
|
+
}
|