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
@@ -0,0 +1,216 @@
1
+ import chalk from 'chalk';
2
+ import * as fs from 'fs';
3
+ import { getApiKey, getMemoryFilePath } from '../lib/config.js';
4
+ import { syncConnect, checkConnection } from '../lib/api.js';
5
+ import { writeMemoryFile } from '../lib/sync.js';
6
+
7
+ // Store sync cache locally
8
+ interface SyncCache {
9
+ last_sync: string;
10
+ memory_count: number;
11
+ secret_count: number;
12
+ }
13
+
14
+ function getSyncCachePath(): string {
15
+ const configDir = require('path').join(require('os').homedir(), '.agentmemory');
16
+ return require('path').join(configDir, 'sync-cache.json');
17
+ }
18
+
19
+ function loadSyncCache(): SyncCache | null {
20
+ const cachePath = getSyncCachePath();
21
+ if (!fs.existsSync(cachePath)) {
22
+ return null;
23
+ }
24
+ try {
25
+ return JSON.parse(fs.readFileSync(cachePath, 'utf-8'));
26
+ } catch {
27
+ return null;
28
+ }
29
+ }
30
+
31
+ function saveSyncCache(cache: SyncCache): void {
32
+ const cachePath = getSyncCachePath();
33
+ fs.writeFileSync(cachePath, JSON.stringify(cache, null, 2));
34
+ }
35
+
36
+ // Connect and auto-sync command
37
+ export async function connectCommand(options: {
38
+ offline?: boolean;
39
+ noMemories?: boolean;
40
+ noSecrets?: boolean;
41
+ json?: boolean;
42
+ }): Promise<void> {
43
+ const apiKey = getApiKey();
44
+
45
+ if (!apiKey) {
46
+ if (options.json) {
47
+ console.log(JSON.stringify({ error: 'Not configured' }));
48
+ } else {
49
+ console.error(chalk.red('Error: Not configured. Run "agentmemory init" first.'));
50
+ }
51
+ process.exit(1);
52
+ }
53
+
54
+ if (options.offline) {
55
+ const cache = loadSyncCache();
56
+ if (cache) {
57
+ if (options.json) {
58
+ console.log(JSON.stringify({ status: 'offline', cached: cache }));
59
+ } else {
60
+ console.log(chalk.yellow('Offline mode - using cached data'));
61
+ console.log(chalk.gray(` Last sync: ${cache.last_sync}`));
62
+ console.log(chalk.gray(` Memories: ${cache.memory_count}`));
63
+ console.log(chalk.gray(` Secrets: ${cache.secret_count}`));
64
+ }
65
+ } else {
66
+ if (options.json) {
67
+ console.log(JSON.stringify({ status: 'offline', cached: null }));
68
+ } else {
69
+ console.log(chalk.yellow('Offline mode - no cached data available'));
70
+ }
71
+ }
72
+ return;
73
+ }
74
+
75
+ try {
76
+ if (!options.json) {
77
+ console.log(chalk.cyan('🔗 Connecting to AgentMemory...'));
78
+ }
79
+
80
+ // Call sync/connect endpoint
81
+ const response = await syncConnect({
82
+ include_files: true,
83
+ memories_limit: 1000,
84
+ secrets_limit: 100,
85
+ });
86
+
87
+ if (options.json) {
88
+ console.log(JSON.stringify(response, null, 2));
89
+ return;
90
+ }
91
+
92
+ // Display connection info
93
+ console.log(chalk.green('✓ Connected successfully!'));
94
+ console.log();
95
+ console.log(chalk.cyan('Agent:'), response.agent.name);
96
+ console.log(chalk.cyan('Plan:'), response.plan.name);
97
+ console.log();
98
+
99
+ // Display sync summary
100
+ console.log(chalk.cyan('Synced data:'));
101
+ console.log(` Memories: ${response.sync.memories.total}${response.sync.memories.has_more ? '+' : ''}`);
102
+ console.log(` Secrets: ${response.sync.secrets.total}${response.sync.secrets.has_more ? '+' : ''}`);
103
+ console.log();
104
+
105
+ // Save to local MEMORY.md if requested
106
+ if (!options.noMemories && response.sync.memories.items.length > 0) {
107
+ const memoryFilePath = getMemoryFilePath();
108
+ writeMemoryFile(memoryFilePath, response.sync.memories.items.map(m => ({
109
+ ...m,
110
+ updated_at: m.updated_at || m.created_at,
111
+ })));
112
+ console.log(chalk.green(`✓ Saved ${response.sync.memories.items.length} memories to ${memoryFilePath}`));
113
+ }
114
+
115
+ // Display secrets list (names only)
116
+ if (!options.noSecrets && response.sync.secrets.items.length > 0) {
117
+ console.log();
118
+ console.log(chalk.cyan('Available secrets:'));
119
+ for (const secret of response.sync.secrets.items) {
120
+ console.log(` - ${secret.name} (${secret.type})`);
121
+ }
122
+ }
123
+
124
+ // Save sync cache
125
+ saveSyncCache({
126
+ last_sync: response.sync.synced_at,
127
+ memory_count: response.sync.memories.total,
128
+ secret_count: response.sync.secrets.total,
129
+ });
130
+
131
+ // Show plan limits
132
+ console.log();
133
+ console.log(chalk.gray(`Limits: ${response.sync.memories.total}/${response.plan.limits.memories === Infinity ? '∞' : response.plan.limits.memories} memories, ${response.sync.secrets.total}/${response.plan.limits.secrets === Infinity ? '∞' : response.plan.limits.secrets} secrets`));
134
+
135
+ } catch (error) {
136
+ if (options.json) {
137
+ console.log(JSON.stringify({ error: error instanceof Error ? error.message : 'Unknown error' }));
138
+ } else {
139
+ console.error(chalk.red(`Error: ${error instanceof Error ? error.message : 'Unknown error'}`));
140
+ }
141
+ process.exit(1);
142
+ }
143
+ }
144
+
145
+ // Quick status check
146
+ export async function statusCommand(options: {
147
+ json?: boolean;
148
+ }): Promise<void> {
149
+ const apiKey = getApiKey();
150
+
151
+ if (!apiKey) {
152
+ if (options.json) {
153
+ console.log(JSON.stringify({ configured: false }));
154
+ } else {
155
+ console.error(chalk.red('Error: Not configured. Run "agentmemory init" first.'));
156
+ }
157
+ process.exit(1);
158
+ }
159
+
160
+ try {
161
+ const status = await checkConnection();
162
+
163
+ if (options.json) {
164
+ console.log(JSON.stringify(status, null, 2));
165
+ } else {
166
+ console.log(chalk.green('✓ Connected'));
167
+ console.log();
168
+ console.log(chalk.cyan('Agent:'), status.agent.name);
169
+ console.log(chalk.cyan('Memories:'), status.counts.memories);
170
+ console.log(chalk.cyan('Secrets:'), status.counts.secrets);
171
+ console.log();
172
+ console.log(chalk.gray(`Server time: ${status.server_time}`));
173
+ }
174
+ } catch (error) {
175
+ if (options.json) {
176
+ console.log(JSON.stringify({ connected: false, error: error instanceof Error ? error.message : 'Unknown error' }));
177
+ } else {
178
+ console.error(chalk.red(`Connection failed: ${error instanceof Error ? error.message : 'Unknown error'}`));
179
+ }
180
+ process.exit(1);
181
+ }
182
+ }
183
+
184
+ // Auto-sync helper (can be called from other commands)
185
+ export async function autoSync(): Promise<boolean> {
186
+ const apiKey = getApiKey();
187
+ if (!apiKey) return false;
188
+
189
+ try {
190
+ const response = await syncConnect({
191
+ include_files: false,
192
+ memories_limit: 1000,
193
+ secrets_limit: 100,
194
+ });
195
+
196
+ // Save to local MEMORY.md
197
+ if (response.sync.memories.items.length > 0) {
198
+ const memoryFilePath = getMemoryFilePath();
199
+ writeMemoryFile(memoryFilePath, response.sync.memories.items.map(m => ({
200
+ ...m,
201
+ updated_at: m.updated_at || m.created_at,
202
+ })));
203
+ }
204
+
205
+ // Save sync cache
206
+ saveSyncCache({
207
+ last_sync: response.sync.synced_at,
208
+ memory_count: response.sync.memories.total,
209
+ secret_count: response.sync.secrets.total,
210
+ });
211
+
212
+ return true;
213
+ } catch {
214
+ return false;
215
+ }
216
+ }
@@ -2,6 +2,7 @@ import chalk from 'chalk';
2
2
  import { createInterface } from 'readline';
3
3
  import { deleteMemory, getMemory } from '../lib/api.js';
4
4
  import { isConfigured } from '../lib/config.js';
5
+ import { autoSync } from '../lib/autosync.js';
5
6
 
6
7
  interface DeleteOptions {
7
8
  force?: boolean;
@@ -31,6 +32,9 @@ export async function deleteCommand(
31
32
  process.exit(1);
32
33
  }
33
34
 
35
+ // Auto-sync in background (silent)
36
+ autoSync();
37
+
34
38
  try {
35
39
  // First, get the memory to show what will be deleted
36
40
  if (!options.force) {
@@ -0,0 +1,105 @@
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
+ interface FileResponse {
8
+ file: {
9
+ id: string;
10
+ name: string;
11
+ type: string;
12
+ size: number;
13
+ url: string;
14
+ download_url: string;
15
+ description: string;
16
+ extracted_text: string;
17
+ metadata: Record<string, unknown>;
18
+ created_at: string;
19
+ };
20
+ error?: string;
21
+ }
22
+
23
+ export async function downloadCommand(
24
+ fileId: string,
25
+ options: { output?: string; info?: boolean }
26
+ ): Promise<void> {
27
+ const apiKey = getApiKey();
28
+ if (!apiKey) {
29
+ console.error(chalk.red('Not configured. Run: agentmemory init'));
30
+ process.exit(1);
31
+ }
32
+
33
+ // Auto-sync in background (silent)
34
+ autoSync();
35
+
36
+ try {
37
+ const apiUrl = getApiUrl();
38
+ const response = await fetch(`${apiUrl}/files/${fileId}`, {
39
+ headers: {
40
+ 'Authorization': `Bearer ${apiKey}`,
41
+ },
42
+ });
43
+
44
+ const data = await response.json() as FileResponse;
45
+
46
+ if (!response.ok) {
47
+ console.error(chalk.red(`Error: ${data.error || 'File not found'}`));
48
+ process.exit(1);
49
+ }
50
+
51
+ const file = data.file;
52
+
53
+ // If --info flag, just show file information
54
+ if (options.info) {
55
+ console.log(chalk.bold(`\nFile Information:\n`));
56
+ console.log(` ${chalk.cyan('Name:')} ${file.name}`);
57
+ console.log(` ${chalk.cyan('Type:')} ${file.type}`);
58
+ console.log(` ${chalk.cyan('Size:')} ${formatSize(file.size)}`);
59
+ console.log(` ${chalk.cyan('Created:')} ${new Date(file.created_at).toLocaleString()}`);
60
+ console.log(` ${chalk.cyan('ID:')} ${file.id}`);
61
+ if (file.description) {
62
+ console.log(` ${chalk.cyan('Description:')} ${file.description}`);
63
+ }
64
+ if (file.extracted_text) {
65
+ console.log(`\n${chalk.cyan('Extracted Content:')}`);
66
+ console.log(chalk.gray(file.extracted_text.slice(0, 500)));
67
+ if (file.extracted_text.length > 500) {
68
+ console.log(chalk.gray('... (truncated)'));
69
+ }
70
+ }
71
+ return;
72
+ }
73
+
74
+ // Download the file
75
+ if (!file.download_url) {
76
+ console.error(chalk.red('No download URL available'));
77
+ process.exit(1);
78
+ }
79
+
80
+ console.log(chalk.blue(`Downloading ${file.name}...`));
81
+
82
+ const downloadResponse = await fetch(file.download_url);
83
+ if (!downloadResponse.ok) {
84
+ console.error(chalk.red('Failed to download file'));
85
+ process.exit(1);
86
+ }
87
+
88
+ const buffer = Buffer.from(await downloadResponse.arrayBuffer());
89
+ const outputPath = options.output || path.join(process.cwd(), file.name);
90
+
91
+ fs.writeFileSync(outputPath, buffer);
92
+
93
+ console.log(chalk.green(`✓ Downloaded to: ${outputPath}`));
94
+ console.log(chalk.gray(` Size: ${formatSize(buffer.length)}`));
95
+ } catch (error) {
96
+ console.error(chalk.red(`Download failed: ${error instanceof Error ? error.message : 'Unknown error'}`));
97
+ process.exit(1);
98
+ }
99
+ }
100
+
101
+ function formatSize(bytes: number): string {
102
+ if (bytes < 1024) return `${bytes} B`;
103
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
104
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
105
+ }
@@ -2,6 +2,7 @@ import chalk from 'chalk';
2
2
  import * as fs from 'fs';
3
3
  import { getAllMemories } from '../lib/api.js';
4
4
  import { isConfigured } from '../lib/config.js';
5
+ import { autoSync } from '../lib/autosync.js';
5
6
 
6
7
  interface ExportOptions {
7
8
  output?: string;
@@ -14,6 +15,9 @@ export async function exportCommand(options: ExportOptions): Promise<void> {
14
15
  process.exit(1);
15
16
  }
16
17
 
18
+ // Auto-sync in background (silent)
19
+ autoSync();
20
+
17
21
  try {
18
22
  console.log(chalk.dim('Fetching all memories...'));
19
23
  const memories = await getAllMemories();
@@ -0,0 +1,119 @@
1
+ import chalk from 'chalk';
2
+ import { getApiKey, getApiUrl } from '../lib/config.js';
3
+ import { autoSync } from '../lib/autosync.js';
4
+
5
+ interface FileMemory {
6
+ id: string;
7
+ content: string;
8
+ file_url: string;
9
+ file_name: string;
10
+ file_type: string;
11
+ file_size: number;
12
+ created_at: string;
13
+ metadata: Record<string, unknown>;
14
+ }
15
+
16
+ interface FilesResponse {
17
+ files: FileMemory[];
18
+ total: number;
19
+ limit: number;
20
+ offset: number;
21
+ error?: string;
22
+ }
23
+
24
+ export async function filesCommand(options: {
25
+ limit?: string;
26
+ type?: string;
27
+ json?: boolean;
28
+ }): Promise<void> {
29
+ const apiKey = getApiKey();
30
+ if (!apiKey) {
31
+ console.error(chalk.red('Not configured. Run: agentmemory init'));
32
+ process.exit(1);
33
+ }
34
+
35
+ // Auto-sync in background (silent)
36
+ autoSync();
37
+
38
+ try {
39
+ const apiUrl = getApiUrl();
40
+ const params = new URLSearchParams();
41
+ if (options.limit) params.set('limit', options.limit);
42
+ if (options.type) params.set('type', options.type);
43
+
44
+ const response = await fetch(`${apiUrl}/files?${params}`, {
45
+ headers: {
46
+ 'Authorization': `Bearer ${apiKey}`,
47
+ },
48
+ });
49
+
50
+ const data = await response.json() as FilesResponse;
51
+
52
+ if (!response.ok) {
53
+ console.error(chalk.red(`Error: ${data.error || 'Failed to list files'}`));
54
+ process.exit(1);
55
+ }
56
+
57
+ if (options.json) {
58
+ console.log(JSON.stringify(data, null, 2));
59
+ return;
60
+ }
61
+
62
+ if (!data.files || data.files.length === 0) {
63
+ console.log(chalk.yellow('No files found.'));
64
+ console.log(chalk.gray('Upload a file with: agentmemory upload <file>'));
65
+ return;
66
+ }
67
+
68
+ console.log(chalk.bold(`\nFiles (${data.total} total):\n`));
69
+
70
+ for (const file of data.files) {
71
+ const category = getFileCategory(file.file_type);
72
+ const icon = getCategoryIcon(category);
73
+ const size = formatSize(file.file_size);
74
+ const date = new Date(file.created_at).toLocaleDateString();
75
+
76
+ console.log(`${icon} ${chalk.cyan(file.file_name)}`);
77
+ console.log(chalk.gray(` ID: ${file.id}`));
78
+ console.log(chalk.gray(` Type: ${file.file_type} | Size: ${size} | Date: ${date}`));
79
+ if (file.content && file.content !== `File: ${file.file_name}`) {
80
+ console.log(chalk.gray(` Description: ${file.content.slice(0, 60)}...`));
81
+ }
82
+ console.log();
83
+ }
84
+ } catch (error) {
85
+ console.error(chalk.red(`Failed to list files: ${error instanceof Error ? error.message : 'Unknown error'}`));
86
+ process.exit(1);
87
+ }
88
+ }
89
+
90
+ function formatSize(bytes: number): string {
91
+ if (bytes < 1024) return `${bytes} B`;
92
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
93
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
94
+ }
95
+
96
+ function getFileCategory(mimeType: string): string {
97
+ if (mimeType.startsWith('image/')) return 'image';
98
+ if (mimeType.startsWith('audio/')) return 'audio';
99
+ if (mimeType.startsWith('video/')) return 'video';
100
+ if (mimeType === 'application/pdf') return 'pdf';
101
+ if (mimeType.includes('word') || mimeType.includes('document')) return 'document';
102
+ if (mimeType.includes('excel') || mimeType.includes('spreadsheet')) return 'spreadsheet';
103
+ if (mimeType.startsWith('text/')) return 'text';
104
+ return 'other';
105
+ }
106
+
107
+ function getCategoryIcon(category: string): string {
108
+ const icons: Record<string, string> = {
109
+ image: '🖼️',
110
+ audio: '🎵',
111
+ video: '🎬',
112
+ pdf: '📄',
113
+ document: '📝',
114
+ spreadsheet: '📊',
115
+ text: '📃',
116
+ other: '📁',
117
+ };
118
+ return icons[category] || '📁';
119
+ }