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.
Files changed (75) hide show
  1. package/dist/commands/connect.d.ts +11 -0
  2. package/dist/commands/connect.d.ts.map +1 -0
  3. package/dist/commands/connect.js +232 -0
  4. package/dist/commands/connect.js.map +1 -0
  5. package/dist/commands/delete.d.ts.map +1 -1
  6. package/dist/commands/delete.js +3 -0
  7. package/dist/commands/delete.js.map +1 -1
  8. package/dist/commands/download.d.ts +5 -0
  9. package/dist/commands/download.d.ts.map +1 -0
  10. package/dist/commands/download.js +82 -0
  11. package/dist/commands/download.js.map +1 -0
  12. package/dist/commands/export.d.ts.map +1 -1
  13. package/dist/commands/export.js +3 -0
  14. package/dist/commands/export.js.map +1 -1
  15. package/dist/commands/files.d.ts +6 -0
  16. package/dist/commands/files.d.ts.map +1 -0
  17. package/dist/commands/files.js +101 -0
  18. package/dist/commands/files.js.map +1 -0
  19. package/dist/commands/heartbeat.d.ts +65 -0
  20. package/dist/commands/heartbeat.d.ts.map +1 -0
  21. package/dist/commands/heartbeat.js +176 -0
  22. package/dist/commands/heartbeat.js.map +1 -0
  23. package/dist/commands/import.d.ts.map +1 -1
  24. package/dist/commands/import.js +3 -0
  25. package/dist/commands/import.js.map +1 -1
  26. package/dist/commands/init.d.ts.map +1 -1
  27. package/dist/commands/init.js +39 -1
  28. package/dist/commands/init.js.map +1 -1
  29. package/dist/commands/list.d.ts.map +1 -1
  30. package/dist/commands/list.js +3 -0
  31. package/dist/commands/list.js.map +1 -1
  32. package/dist/commands/search.d.ts.map +1 -1
  33. package/dist/commands/search.js +3 -0
  34. package/dist/commands/search.js.map +1 -1
  35. package/dist/commands/secret.d.ts +25 -0
  36. package/dist/commands/secret.d.ts.map +1 -0
  37. package/dist/commands/secret.js +390 -0
  38. package/dist/commands/secret.js.map +1 -0
  39. package/dist/commands/store.d.ts.map +1 -1
  40. package/dist/commands/store.js +3 -0
  41. package/dist/commands/store.js.map +1 -1
  42. package/dist/commands/upload.d.ts +4 -0
  43. package/dist/commands/upload.d.ts.map +1 -0
  44. package/dist/commands/upload.js +110 -0
  45. package/dist/commands/upload.js.map +1 -0
  46. package/dist/index.js +136 -2
  47. package/dist/index.js.map +1 -1
  48. package/dist/lib/api.d.ts +23 -1
  49. package/dist/lib/api.d.ts.map +1 -1
  50. package/dist/lib/api.js +49 -0
  51. package/dist/lib/api.js.map +1 -1
  52. package/dist/lib/autosync.d.ts +14 -0
  53. package/dist/lib/autosync.d.ts.map +1 -0
  54. package/dist/lib/autosync.js +165 -0
  55. package/dist/lib/autosync.js.map +1 -0
  56. package/dist/types.d.ts +59 -0
  57. package/dist/types.d.ts.map +1 -1
  58. package/package.json +2 -1
  59. package/src/commands/connect.ts +216 -0
  60. package/src/commands/delete.ts +4 -0
  61. package/src/commands/download.ts +105 -0
  62. package/src/commands/export.ts +4 -0
  63. package/src/commands/files.ts +119 -0
  64. package/src/commands/heartbeat.ts +241 -0
  65. package/src/commands/import.ts +4 -0
  66. package/src/commands/init.ts +44 -1
  67. package/src/commands/list.ts +4 -0
  68. package/src/commands/search.ts +4 -0
  69. package/src/commands/secret.ts +438 -0
  70. package/src/commands/store.ts +4 -0
  71. package/src/commands/upload.ts +117 -0
  72. package/src/index.ts +158 -2
  73. package/src/lib/api.ts +86 -1
  74. package/src/lib/autosync.ts +160 -0
  75. package/src/types.ts +67 -0
package/src/index.ts CHANGED
@@ -10,13 +10,26 @@ import { deleteCommand } from './commands/delete.js';
10
10
  import { syncCommand } from './commands/sync.js';
11
11
  import { exportCommand } from './commands/export.js';
12
12
  import { importCommand } from './commands/import.js';
13
+ import { uploadCommand } from './commands/upload.js';
14
+ import { filesCommand } from './commands/files.js';
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';
13
26
 
14
27
  const program = new Command();
15
28
 
16
29
  program
17
30
  .name('agentmemory')
18
31
  .description('CLI tool for AgentMemory - persistent cloud memory for AI agents')
19
- .version('1.0.0');
32
+ .version('1.3.0');
20
33
 
21
34
  // Init command
22
35
  program
@@ -24,6 +37,48 @@ program
24
37
  .description('Initialize AgentMemory CLI with your API key')
25
38
  .action(initCommand);
26
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
+
27
82
  // Store command
28
83
  program
29
84
  .command('store <content>')
@@ -83,6 +138,83 @@ program
83
138
  .option('--json', 'Output as JSON')
84
139
  .action(importCommand);
85
140
 
141
+ // Upload command (files)
142
+ program
143
+ .command('upload <file>')
144
+ .description('Upload a file (images, PDFs, docs, audio, video, etc.)')
145
+ .option('-d, --description <text>', 'Description for the file')
146
+ .action(uploadCommand);
147
+
148
+ // Files command (list files)
149
+ program
150
+ .command('files')
151
+ .description('List uploaded files')
152
+ .option('-l, --limit <number>', 'Maximum number of results', '50')
153
+ .option('-t, --type <type>', 'Filter by type (image, audio, video, pdf, document)')
154
+ .option('--json', 'Output as JSON')
155
+ .action(filesCommand);
156
+
157
+ // Download command
158
+ program
159
+ .command('download <id>')
160
+ .description('Download a file by ID')
161
+ .option('-o, --output <path>', 'Output file path')
162
+ .option('-i, --info', 'Show file info without downloading')
163
+ .action(downloadCommand);
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
+
86
218
  // Custom help
87
219
  program.addHelpText('after', `
88
220
  ${chalk.cyan('Examples:')}
@@ -92,6 +224,30 @@ ${chalk.cyan('Examples:')}
92
224
  $ agentmemory list --limit 10
93
225
  $ agentmemory sync # Sync with MEMORY.md
94
226
  $ agentmemory export > backup.json
227
+
228
+ ${chalk.cyan('File Operations:')}
229
+ $ agentmemory upload photo.jpg # Upload any file
230
+ $ agentmemory upload doc.pdf -d "Meeting notes"
231
+ $ agentmemory files # List uploaded files
232
+ $ agentmemory files --type image # Filter by type
233
+ $ agentmemory download <id> # Download a file
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
95
251
 
96
252
  ${chalk.cyan('Documentation:')}
97
253
  https://agentmemory.cloud/docs
@@ -106,7 +262,7 @@ program.parse();
106
262
  // Show help if no command provided
107
263
  if (!process.argv.slice(2).length) {
108
264
  console.log(chalk.cyan(`
109
- 🧠 AgentMemory CLI v1.0.0
265
+ 🧠 AgentMemory CLI v1.3.0
110
266
 
111
267
  Persistent cloud memory for AI agents.
112
268
  `));
package/src/lib/api.ts CHANGED
@@ -4,7 +4,13 @@ import type {
4
4
  MemoriesListResponse,
5
5
  MemoryCreateResponse,
6
6
  MemorySearchResponse,
7
- MemorySearchResult
7
+ MemorySearchResult,
8
+ Secret,
9
+ SecretListItem,
10
+ SecretsListResponse,
11
+ SecretResponse,
12
+ SecretType,
13
+ SyncConnectResponse
8
14
  } from '../types.js';
9
15
 
10
16
  class ApiError extends Error {
@@ -136,4 +142,83 @@ export async function getAllMemories(): Promise<Memory[]> {
136
142
  return allMemories;
137
143
  }
138
144
 
145
+ // Secrets API functions
146
+
147
+ export async function listSecrets(
148
+ limit: number = 100,
149
+ type?: SecretType
150
+ ): Promise<SecretsListResponse> {
151
+ let endpoint = `/secrets?limit=${limit}`;
152
+ if (type) {
153
+ endpoint += `&type=${type}`;
154
+ }
155
+ return request<SecretsListResponse>(endpoint);
156
+ }
157
+
158
+ export async function getSecret(name: string): Promise<Secret> {
159
+ const response = await request<{ secret: Secret }>(`/secrets/${encodeURIComponent(name)}`);
160
+ return response.secret;
161
+ }
162
+
163
+ export async function setSecret(
164
+ name: string,
165
+ value: string,
166
+ type: SecretType = 'generic',
167
+ description?: string,
168
+ metadata?: Record<string, unknown>
169
+ ): Promise<SecretResponse> {
170
+ const body: {
171
+ name: string;
172
+ value: string;
173
+ type: SecretType;
174
+ description?: string;
175
+ metadata?: Record<string, unknown>;
176
+ } = { name, value, type };
177
+
178
+ if (description) {
179
+ body.description = description;
180
+ }
181
+ if (metadata) {
182
+ body.metadata = metadata;
183
+ }
184
+
185
+ return request<SecretResponse>('/secrets', {
186
+ method: 'POST',
187
+ body: JSON.stringify(body),
188
+ });
189
+ }
190
+
191
+ export async function deleteSecret(name: string): Promise<void> {
192
+ await request<{ message: string; name: string }>(
193
+ `/secrets/${encodeURIComponent(name)}`,
194
+ { method: 'DELETE' }
195
+ );
196
+ }
197
+
198
+ export async function getAllSecrets(): Promise<SecretListItem[]> {
199
+ const response = await listSecrets(500);
200
+ return response.secrets;
201
+ }
202
+
203
+ // Sync connect API
204
+ export async function syncConnect(options?: {
205
+ include_files?: boolean;
206
+ memories_limit?: number;
207
+ secrets_limit?: number;
208
+ }): Promise<SyncConnectResponse> {
209
+ return request<SyncConnectResponse>('/sync/connect', {
210
+ method: 'POST',
211
+ body: JSON.stringify(options || {}),
212
+ });
213
+ }
214
+
215
+ export async function checkConnection(): Promise<{
216
+ connected: boolean;
217
+ agent: { id: string; name: string };
218
+ counts: { memories: number; secrets: number };
219
+ server_time: string;
220
+ }> {
221
+ return request('/sync/connect');
222
+ }
223
+
139
224
  export { ApiError };
@@ -0,0 +1,160 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import * as os from 'os';
4
+ import { getApiKey, getApiUrl, getMemoryFilePath } from './config.js';
5
+
6
+ const CONFIG_DIR = path.join(os.homedir(), '.agentmemory');
7
+ const SYNC_CACHE_FILE = path.join(CONFIG_DIR, 'sync-cache.json');
8
+ const SYNC_INTERVAL_MS = 30000; // Only sync if last sync was 30+ seconds ago
9
+
10
+ interface SyncCache {
11
+ last_sync: string;
12
+ last_sync_ms: number;
13
+ memory_count: number;
14
+ secret_count: number;
15
+ }
16
+
17
+ function loadSyncCache(): SyncCache | null {
18
+ try {
19
+ if (fs.existsSync(SYNC_CACHE_FILE)) {
20
+ return JSON.parse(fs.readFileSync(SYNC_CACHE_FILE, 'utf-8'));
21
+ }
22
+ } catch {
23
+ // Ignore errors
24
+ }
25
+ return null;
26
+ }
27
+
28
+ function saveSyncCache(cache: SyncCache): void {
29
+ try {
30
+ if (!fs.existsSync(CONFIG_DIR)) {
31
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
32
+ }
33
+ fs.writeFileSync(SYNC_CACHE_FILE, JSON.stringify(cache, null, 2));
34
+ } catch {
35
+ // Ignore errors
36
+ }
37
+ }
38
+
39
+ /**
40
+ * Silent auto-sync - runs at the start of every command
41
+ * - Sends heartbeat to mark agent as online
42
+ * - Syncs memories to local MEMORY.md file
43
+ * - Caches results to avoid syncing too frequently
44
+ * - Never throws errors or prints output (silent)
45
+ */
46
+ export async function autoSync(): Promise<void> {
47
+ const apiKey = getApiKey();
48
+ if (!apiKey) return; // Not configured yet
49
+
50
+ // Check if we synced recently (within 30 seconds)
51
+ const cache = loadSyncCache();
52
+ if (cache && Date.now() - cache.last_sync_ms < SYNC_INTERVAL_MS) {
53
+ return; // Skip sync, too recent
54
+ }
55
+
56
+ const apiUrl = getApiUrl();
57
+
58
+ try {
59
+ // Send heartbeat with sync
60
+ const response = await fetch(`${apiUrl}/heartbeat`, {
61
+ method: 'POST',
62
+ headers: {
63
+ 'Authorization': `Bearer ${apiKey}`,
64
+ 'Content-Type': 'application/json',
65
+ },
66
+ body: JSON.stringify({ sync: true }),
67
+ });
68
+
69
+ if (!response.ok) return;
70
+
71
+ const data = await response.json() as {
72
+ sync?: {
73
+ memories: {
74
+ items: Array<{
75
+ id: string;
76
+ content: string;
77
+ metadata?: Record<string, unknown>;
78
+ created_at: string;
79
+ updated_at?: string;
80
+ }>;
81
+ count: number;
82
+ };
83
+ secrets: {
84
+ count: number;
85
+ };
86
+ synced_at: string;
87
+ };
88
+ };
89
+
90
+ if (!data.sync) return;
91
+
92
+ // Save memories to local MEMORY.md file (silently)
93
+ if (data.sync.memories.items.length > 0) {
94
+ try {
95
+ const memoryFilePath = getMemoryFilePath();
96
+ const dir = path.dirname(memoryFilePath);
97
+ if (!fs.existsSync(dir)) {
98
+ fs.mkdirSync(dir, { recursive: true });
99
+ }
100
+
101
+ const lines: string[] = [
102
+ '# Agent Memory',
103
+ '',
104
+ `> Last synced: ${data.sync.synced_at}`,
105
+ `> Total memories: ${data.sync.memories.count}`,
106
+ '',
107
+ '---',
108
+ '',
109
+ ];
110
+
111
+ for (const memory of data.sync.memories.items) {
112
+ lines.push(`<!-- id: ${memory.id} -->`);
113
+ if (memory.metadata && Object.keys(memory.metadata).length > 0) {
114
+ lines.push(`<!-- metadata: ${JSON.stringify(memory.metadata)} -->`);
115
+ }
116
+ lines.push(memory.content);
117
+ lines.push('');
118
+ lines.push('---');
119
+ lines.push('');
120
+ }
121
+
122
+ fs.writeFileSync(memoryFilePath, lines.join('\n'));
123
+ } catch {
124
+ // Silently ignore file write errors
125
+ }
126
+ }
127
+
128
+ // Update cache
129
+ saveSyncCache({
130
+ last_sync: data.sync.synced_at,
131
+ last_sync_ms: Date.now(),
132
+ memory_count: data.sync.memories.count,
133
+ secret_count: data.sync.secrets.count,
134
+ });
135
+ } catch {
136
+ // Silently ignore all errors - auto-sync should never break commands
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Quick heartbeat - just marks agent as online, no sync
142
+ * Use this for commands that don't need fresh data
143
+ */
144
+ export async function quickHeartbeat(): Promise<void> {
145
+ const apiKey = getApiKey();
146
+ if (!apiKey) return;
147
+
148
+ const apiUrl = getApiUrl();
149
+
150
+ try {
151
+ await fetch(`${apiUrl}/heartbeat`, {
152
+ method: 'GET',
153
+ headers: {
154
+ 'Authorization': `Bearer ${apiKey}`,
155
+ },
156
+ });
157
+ } catch {
158
+ // Silently ignore
159
+ }
160
+ }
package/src/types.ts CHANGED
@@ -45,3 +45,70 @@ export interface SyncStatus {
45
45
  cloud: Memory;
46
46
  }>;
47
47
  }
48
+
49
+ // Secrets types
50
+ export type SecretType = 'api_key' | 'credential' | 'connection_string' | 'env_var' | 'generic';
51
+
52
+ export interface Secret {
53
+ id: string;
54
+ name: string;
55
+ value?: string;
56
+ type: SecretType;
57
+ description?: string;
58
+ metadata?: Record<string, unknown>;
59
+ created_at: string;
60
+ updated_at: string;
61
+ }
62
+
63
+ export interface SecretListItem {
64
+ id: string;
65
+ name: string;
66
+ type: SecretType;
67
+ description?: string;
68
+ metadata?: Record<string, unknown>;
69
+ created_at: string;
70
+ updated_at: string;
71
+ }
72
+
73
+ export interface SecretsListResponse {
74
+ secrets: SecretListItem[];
75
+ total: number;
76
+ limit: number;
77
+ offset: number;
78
+ }
79
+
80
+ export interface SecretResponse {
81
+ secret: Secret;
82
+ message?: string;
83
+ }
84
+
85
+ // Sync connect types
86
+ export interface SyncConnectResponse {
87
+ agent: {
88
+ id: string;
89
+ name: string;
90
+ memory_count: number;
91
+ created_at: string;
92
+ };
93
+ plan: {
94
+ name: string;
95
+ activated: boolean;
96
+ limits: {
97
+ memories: number;
98
+ secrets: number;
99
+ };
100
+ };
101
+ sync: {
102
+ memories: {
103
+ items: Memory[];
104
+ total: number;
105
+ has_more: boolean;
106
+ };
107
+ secrets: {
108
+ items: SecretListItem[];
109
+ total: number;
110
+ has_more: boolean;
111
+ };
112
+ synced_at: string;
113
+ };
114
+ }