agentmemory-cli 1.1.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.map +1 -1
- package/dist/commands/download.js +3 -0
- package/dist/commands/download.js.map +1 -1
- 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.map +1 -1
- package/dist/commands/files.js +3 -0
- package/dist/commands/files.js.map +1 -1
- 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.map +1 -1
- package/dist/commands/upload.js +3 -0
- package/dist/commands/upload.js.map +1 -1
- package/dist/index.js +105 -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 +1 -1
- package/src/commands/connect.ts +216 -0
- package/src/commands/delete.ts +4 -0
- package/src/commands/download.ts +4 -0
- package/src/commands/export.ts +4 -0
- package/src/commands/files.ts +4 -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 +4 -0
- package/src/index.ts +124 -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> = {};
|
package/src/commands/upload.ts
CHANGED
|
@@ -2,6 +2,7 @@ import chalk from 'chalk';
|
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { getApiKey, getApiUrl } from '../lib/config.js';
|
|
5
|
+
import { autoSync } from '../lib/autosync.js';
|
|
5
6
|
|
|
6
7
|
export async function uploadCommand(
|
|
7
8
|
filePath: string,
|
|
@@ -13,6 +14,9 @@ export async function uploadCommand(
|
|
|
13
14
|
process.exit(1);
|
|
14
15
|
}
|
|
15
16
|
|
|
17
|
+
// Auto-sync in background (silent)
|
|
18
|
+
autoSync();
|
|
19
|
+
|
|
16
20
|
// Check if file exists
|
|
17
21
|
if (!fs.existsSync(filePath)) {
|
|
18
22
|
console.error(chalk.red(`File not found: ${filePath}`));
|
package/src/index.ts
CHANGED
|
@@ -13,13 +13,23 @@ import { importCommand } from './commands/import.js';
|
|
|
13
13
|
import { uploadCommand } from './commands/upload.js';
|
|
14
14
|
import { filesCommand } from './commands/files.js';
|
|
15
15
|
import { downloadCommand } from './commands/download.js';
|
|
16
|
+
import {
|
|
17
|
+
secretSetCommand,
|
|
18
|
+
secretGetCommand,
|
|
19
|
+
secretListCommand,
|
|
20
|
+
secretDeleteCommand,
|
|
21
|
+
secretsExportCommand,
|
|
22
|
+
secretsImportCommand
|
|
23
|
+
} from './commands/secret.js';
|
|
24
|
+
import { connectCommand, statusCommand } from './commands/connect.js';
|
|
25
|
+
import { heartbeatCommand, watchCommand, disconnectCommand } from './commands/heartbeat.js';
|
|
16
26
|
|
|
17
27
|
const program = new Command();
|
|
18
28
|
|
|
19
29
|
program
|
|
20
30
|
.name('agentmemory')
|
|
21
31
|
.description('CLI tool for AgentMemory - persistent cloud memory for AI agents')
|
|
22
|
-
.version('1.
|
|
32
|
+
.version('1.3.0');
|
|
23
33
|
|
|
24
34
|
// Init command
|
|
25
35
|
program
|
|
@@ -27,6 +37,48 @@ program
|
|
|
27
37
|
.description('Initialize AgentMemory CLI with your API key')
|
|
28
38
|
.action(initCommand);
|
|
29
39
|
|
|
40
|
+
// Connect command (auto-sync)
|
|
41
|
+
program
|
|
42
|
+
.command('connect')
|
|
43
|
+
.description('Connect and sync all data from cloud')
|
|
44
|
+
.option('--offline', 'Use cached data (no network)')
|
|
45
|
+
.option('--no-memories', 'Skip syncing memories to MEMORY.md')
|
|
46
|
+
.option('--no-secrets', 'Skip showing secrets list')
|
|
47
|
+
.option('--json', 'Output as JSON')
|
|
48
|
+
.action(connectCommand);
|
|
49
|
+
|
|
50
|
+
// Status command
|
|
51
|
+
program
|
|
52
|
+
.command('status')
|
|
53
|
+
.description('Check connection status and data counts')
|
|
54
|
+
.option('--json', 'Output as JSON')
|
|
55
|
+
.action(statusCommand);
|
|
56
|
+
|
|
57
|
+
// Heartbeat command
|
|
58
|
+
program
|
|
59
|
+
.command('heartbeat')
|
|
60
|
+
.description('Send heartbeat signal (marks agent as online)')
|
|
61
|
+
.option('--no-sync', 'Skip syncing data')
|
|
62
|
+
.option('-c, --continuous', 'Run continuous heartbeat')
|
|
63
|
+
.option('-i, --interval <seconds>', 'Heartbeat interval in seconds', '60')
|
|
64
|
+
.option('--json', 'Output as JSON')
|
|
65
|
+
.action(heartbeatCommand);
|
|
66
|
+
|
|
67
|
+
// Watch command (continuous heartbeat with auto-sync)
|
|
68
|
+
program
|
|
69
|
+
.command('watch')
|
|
70
|
+
.description('Keep agent online with continuous heartbeat and auto-sync')
|
|
71
|
+
.option('-i, --interval <seconds>', 'Heartbeat interval in seconds', '60')
|
|
72
|
+
.option('--json', 'Output as JSON')
|
|
73
|
+
.action(watchCommand);
|
|
74
|
+
|
|
75
|
+
// Disconnect command
|
|
76
|
+
program
|
|
77
|
+
.command('disconnect')
|
|
78
|
+
.description('Mark agent as offline')
|
|
79
|
+
.option('--json', 'Output as JSON')
|
|
80
|
+
.action(disconnectCommand);
|
|
81
|
+
|
|
30
82
|
// Store command
|
|
31
83
|
program
|
|
32
84
|
.command('store <content>')
|
|
@@ -110,6 +162,59 @@ program
|
|
|
110
162
|
.option('-i, --info', 'Show file info without downloading')
|
|
111
163
|
.action(downloadCommand);
|
|
112
164
|
|
|
165
|
+
// Secret commands (grouped)
|
|
166
|
+
const secretCmd = program
|
|
167
|
+
.command('secret')
|
|
168
|
+
.description('Manage secrets vault');
|
|
169
|
+
|
|
170
|
+
// secret set
|
|
171
|
+
secretCmd
|
|
172
|
+
.command('set <name> <value>')
|
|
173
|
+
.description('Store a secret')
|
|
174
|
+
.option('-t, --type <type>', 'Secret type (api_key, credential, connection_string, env_var, generic)', 'generic')
|
|
175
|
+
.option('-d, --description <text>', 'Description for the secret')
|
|
176
|
+
.option('--json', 'Output as JSON')
|
|
177
|
+
.action(secretSetCommand);
|
|
178
|
+
|
|
179
|
+
// secret get
|
|
180
|
+
secretCmd
|
|
181
|
+
.command('get <name>')
|
|
182
|
+
.description('Retrieve a secret')
|
|
183
|
+
.option('-s, --show', 'Show full value (not masked)')
|
|
184
|
+
.option('--json', 'Output as JSON')
|
|
185
|
+
.action(secretGetCommand);
|
|
186
|
+
|
|
187
|
+
// secret list
|
|
188
|
+
secretCmd
|
|
189
|
+
.command('list')
|
|
190
|
+
.description('List all secret names')
|
|
191
|
+
.option('-t, --type <type>', 'Filter by type')
|
|
192
|
+
.option('--json', 'Output as JSON')
|
|
193
|
+
.action(secretListCommand);
|
|
194
|
+
|
|
195
|
+
// secret delete
|
|
196
|
+
secretCmd
|
|
197
|
+
.command('delete <name>')
|
|
198
|
+
.description('Delete a secret')
|
|
199
|
+
.option('-f, --force', 'Skip confirmation')
|
|
200
|
+
.option('--json', 'Output as JSON')
|
|
201
|
+
.action(secretDeleteCommand);
|
|
202
|
+
|
|
203
|
+
// secrets export (note: "secrets" plural for bulk operations)
|
|
204
|
+
program
|
|
205
|
+
.command('secrets-export')
|
|
206
|
+
.description('Export all secrets (encrypted with password)')
|
|
207
|
+
.option('-o, --output <file>', 'Output file path', 'secrets-export.json')
|
|
208
|
+
.option('--json', 'Output as JSON')
|
|
209
|
+
.action(secretsExportCommand);
|
|
210
|
+
|
|
211
|
+
// secrets import
|
|
212
|
+
program
|
|
213
|
+
.command('secrets-import <file>')
|
|
214
|
+
.description('Import secrets from encrypted file')
|
|
215
|
+
.option('--json', 'Output as JSON')
|
|
216
|
+
.action(secretsImportCommand);
|
|
217
|
+
|
|
113
218
|
// Custom help
|
|
114
219
|
program.addHelpText('after', `
|
|
115
220
|
${chalk.cyan('Examples:')}
|
|
@@ -127,6 +232,23 @@ ${chalk.cyan('Examples:')}
|
|
|
127
232
|
$ agentmemory files --type image # Filter by type
|
|
128
233
|
$ agentmemory download <id> # Download a file
|
|
129
234
|
|
|
235
|
+
${chalk.cyan('Secrets Vault:')}
|
|
236
|
+
$ agentmemory secret set OPENAI_KEY sk-xxx --type api_key
|
|
237
|
+
$ agentmemory secret get OPENAI_KEY # Shows masked value
|
|
238
|
+
$ agentmemory secret get OPENAI_KEY --show # Shows full value
|
|
239
|
+
$ agentmemory secret list # List all secrets
|
|
240
|
+
$ agentmemory secret delete OPENAI_KEY # Delete a secret
|
|
241
|
+
$ agentmemory secrets-export # Export encrypted backup
|
|
242
|
+
$ agentmemory secrets-import backup.json # Import from backup
|
|
243
|
+
|
|
244
|
+
${chalk.cyan('Auto-Sync & Heartbeat:')}
|
|
245
|
+
$ agentmemory connect # Sync all data from cloud
|
|
246
|
+
$ agentmemory status # Check connection status
|
|
247
|
+
$ agentmemory heartbeat # Send single heartbeat with sync
|
|
248
|
+
$ agentmemory watch # Continuous heartbeat (keeps online)
|
|
249
|
+
$ agentmemory watch -i 30 # Heartbeat every 30 seconds
|
|
250
|
+
$ agentmemory disconnect # Mark agent as offline
|
|
251
|
+
|
|
130
252
|
${chalk.cyan('Documentation:')}
|
|
131
253
|
https://agentmemory.cloud/docs
|
|
132
254
|
|
|
@@ -140,7 +262,7 @@ program.parse();
|
|
|
140
262
|
// Show help if no command provided
|
|
141
263
|
if (!process.argv.slice(2).length) {
|
|
142
264
|
console.log(chalk.cyan(`
|
|
143
|
-
🧠 AgentMemory CLI v1.
|
|
265
|
+
🧠 AgentMemory CLI v1.3.0
|
|
144
266
|
|
|
145
267
|
Persistent cloud memory for AI agents.
|
|
146
268
|
`));
|