@satori-sh/cli 0.0.2

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 (69) hide show
  1. package/README.md +88 -0
  2. package/bun.lock +37 -0
  3. package/dist/add.d.ts +2 -0
  4. package/dist/add.d.ts.map +1 -0
  5. package/dist/add.js +27 -0
  6. package/dist/add.js.map +1 -0
  7. package/dist/config.d.ts +3 -0
  8. package/dist/config.d.ts.map +1 -0
  9. package/dist/config.js +15 -0
  10. package/dist/config.js.map +1 -0
  11. package/dist/index.d.ts +4 -0
  12. package/dist/index.d.ts.map +1 -0
  13. package/dist/index.js +49 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/memory.d.ts +12 -0
  16. package/dist/memory.d.ts.map +1 -0
  17. package/dist/memory.js +42 -0
  18. package/dist/memory.js.map +1 -0
  19. package/dist/search.d.ts +2 -0
  20. package/dist/search.d.ts.map +1 -0
  21. package/dist/search.js +27 -0
  22. package/dist/search.js.map +1 -0
  23. package/dist/src/add.d.ts +23 -0
  24. package/dist/src/add.d.ts.map +1 -0
  25. package/dist/src/add.js +46 -0
  26. package/dist/src/add.js.map +1 -0
  27. package/dist/src/config.d.ts +39 -0
  28. package/dist/src/config.d.ts.map +1 -0
  29. package/dist/src/config.js +165 -0
  30. package/dist/src/config.js.map +1 -0
  31. package/dist/src/index.d.ts +14 -0
  32. package/dist/src/index.d.ts.map +1 -0
  33. package/dist/src/index.js +141 -0
  34. package/dist/src/index.js.map +1 -0
  35. package/dist/src/memory.d.ts +52 -0
  36. package/dist/src/memory.d.ts.map +1 -0
  37. package/dist/src/memory.js +98 -0
  38. package/dist/src/memory.js.map +1 -0
  39. package/dist/src/providers.d.ts +34 -0
  40. package/dist/src/providers.d.ts.map +1 -0
  41. package/dist/src/providers.js +107 -0
  42. package/dist/src/providers.js.map +1 -0
  43. package/dist/src/search.d.ts +14 -0
  44. package/dist/src/search.d.ts.map +1 -0
  45. package/dist/src/search.js +39 -0
  46. package/dist/src/search.js.map +1 -0
  47. package/dist/src/types.d.ts +51 -0
  48. package/dist/src/types.d.ts.map +1 -0
  49. package/dist/src/types.js +2 -0
  50. package/dist/src/types.js.map +1 -0
  51. package/dist/tests/index.test.d.ts +2 -0
  52. package/dist/tests/index.test.d.ts.map +1 -0
  53. package/dist/tests/index.test.js +257 -0
  54. package/dist/tests/index.test.js.map +1 -0
  55. package/dist/types.d.ts +19 -0
  56. package/dist/types.d.ts.map +1 -0
  57. package/dist/types.js +2 -0
  58. package/dist/types.js.map +1 -0
  59. package/logo.txt +2 -0
  60. package/package.json +25 -0
  61. package/src/add.ts +49 -0
  62. package/src/config.ts +170 -0
  63. package/src/index.ts +163 -0
  64. package/src/memory.ts +118 -0
  65. package/src/providers.ts +133 -0
  66. package/src/search.ts +42 -0
  67. package/src/types.ts +45 -0
  68. package/tests/index.test.ts +322 -0
  69. package/tsconfig.json +33 -0
package/README.md ADDED
@@ -0,0 +1,88 @@
1
+ # satori
2
+
3
+ CLI tool for Satori memory server.
4
+
5
+ ## Installation
6
+
7
+ Using Bun (recommended):
8
+ ```bash
9
+ bun add -g @satori-sh/cli
10
+ ```
11
+
12
+ Using npm:
13
+ ```bash
14
+ npm install -g @satori-sh/cli
15
+ ```
16
+
17
+ ## Usage
18
+
19
+ ### Chat (Default)
20
+
21
+ Start memory-augmented chat sessions:
22
+ ```bash
23
+ satori "What's the best pizza topping?"
24
+ satori "hello" --provider openai --model gpt-4o
25
+ ```
26
+
27
+ ### Search Memories
28
+
29
+ Query the memory database:
30
+ ```bash
31
+ satori search "what is my favorite food?"
32
+ ```
33
+
34
+ ### Add Memory
35
+
36
+ Add new memories:
37
+ ```bash
38
+ satori add "I like pizza"
39
+ ```
40
+
41
+ **Options & Memory:**
42
+ - `--provider <openai|anthropic>` (default: openai)
43
+ - `--model <model>` (default: gpt-4o)
44
+ - `--memory-id <id>` (scopes conversations)
45
+ - `--no-stream` (disables streaming)
46
+
47
+ **Memory Sessions:** If no `--memory-id` is provided, a random ID is generated. Set `SATORI_MEMORY_ID=generated-id` to continue sessions:
48
+ ```bash
49
+ export SATORI_MEMORY_ID=example-words-joined
50
+ satori chat "Follow up question"
51
+ ```
52
+
53
+ ## Configuration
54
+
55
+ **Required:**
56
+ - `SATORI_API_KEY` - Satori authentication key
57
+
58
+ **For Chat:**
59
+ - `OPENAI_API_KEY` / `ANTHROPIC_API_KEY` - LLM provider keys
60
+
61
+ **Optional:**
62
+ - `SATORI_BASE_URL` (default: http://localhost:8000)
63
+ - `SATORI_PROVIDER` (default: openai)
64
+ - `SATORI_MODEL` (default: gpt-4o)
65
+ - `SATORI_MEMORY_ID` - Session scoping
66
+ - `SATORI_MOCK` - Enable mock mode
67
+
68
+ ## Troubleshooting
69
+
70
+ - **API Key Errors**: Ensure provider keys are set (`OPENAI_API_KEY` or `ANTHROPIC_API_KEY`)
71
+ - **Memory Server Down**: Chat falls back to raw LLM responses with warning logs
72
+ - **Memory ID Issues**: Invalid IDs scope searches but don't break functionality
73
+ - **Streaming Problems**: Use `--no-stream` for terminal compatibility
74
+
75
+ ## Contributing
76
+
77
+ ### Publishing
78
+
79
+ 1. Update version: `npm version patch|minor|major`
80
+ 2. Publish beta: `npm publish --tag beta`
81
+ 3. Test: `npm install @satori-sh/cli@beta`
82
+ 4. Promote: `npm dist-tag add @satori-sh/cli@<version> latest`
83
+
84
+ ### Notes
85
+ - Uses ES modules for modern compatibility
86
+ - `prepublishOnly` builds automatically
87
+ - Test beta versions before promoting
88
+ - OTP required for publishing
package/bun.lock ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "lockfileVersion": 1,
3
+ "workspaces": {
4
+ "": {
5
+ "name": "@satori-sh/cli",
6
+ "dependencies": {
7
+ "chalk": "^5.6.2",
8
+ "commander": "^12.1.0",
9
+ "random-words": "^2.0.1",
10
+ },
11
+ "devDependencies": {
12
+ "@types/bun": "^1.3.3",
13
+ "@types/node": "^24.10.1",
14
+ "typescript": "^5.9.3",
15
+ },
16
+ },
17
+ },
18
+ "packages": {
19
+ "@types/bun": ["@types/bun@1.3.5", "", { "dependencies": { "bun-types": "1.3.5" } }, "sha512-RnygCqNrd3srIPEWBd5LFeUYG7plCoH2Yw9WaZGyNmdTEei+gWaHqydbaIRkIkcbXwhBT94q78QljxN0Sk838w=="],
20
+
21
+ "@types/node": ["@types/node@24.10.4", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-vnDVpYPMzs4wunl27jHrfmwojOGKya0xyM3sH+UE5iv5uPS6vX7UIoh6m+vQc5LGBq52HBKPIn/zcSZVzeDEZg=="],
22
+
23
+ "bun-types": ["bun-types@1.3.5", "", { "dependencies": { "@types/node": "*" } }, "sha512-inmAYe2PFLs0SUbFOWSVD24sg1jFlMPxOjOSSCYqUgn4Hsc3rDc7dFvfVYjFPNHtov6kgUeulV4SxbuIV/stPw=="],
24
+
25
+ "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
26
+
27
+ "commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="],
28
+
29
+ "random-words": ["random-words@2.0.1", "", { "dependencies": { "seedrandom": "^3.0.5" } }, "sha512-nZNJAmgcFmtJMTDDIUCm/iK4R6RydC6NvALvWhYItXQrgYGk1F7Gww416LpVROFQtfVd5TaLEf4WuSsko03N7w=="],
30
+
31
+ "seedrandom": ["seedrandom@3.0.5", "", {}, "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg=="],
32
+
33
+ "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
34
+
35
+ "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
36
+ }
37
+ }
package/dist/add.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ export declare function addMemories(text: string): Promise<void>;
2
+ //# sourceMappingURL=add.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../src/add.ts"],"names":[],"mappings":"AAGA,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA0B7D"}
package/dist/add.js ADDED
@@ -0,0 +1,27 @@
1
+ import { getConfig } from './config.js';
2
+ export async function addMemories(text) {
3
+ if (!text || !text.trim()) {
4
+ console.error('Text cannot be empty');
5
+ return;
6
+ }
7
+ try {
8
+ const config = getConfig();
9
+ const response = await fetch(`${config.baseUrl}/memories`, {
10
+ method: 'POST',
11
+ headers: {
12
+ 'Content-Type': 'application/json',
13
+ 'Authorization': `Bearer ${config.apiKey}`
14
+ },
15
+ body: JSON.stringify({ messages: [{ role: "user", content: text }] })
16
+ });
17
+ if (!response.ok) {
18
+ console.error(`HTTP error: ${response.status} ${response.statusText}`);
19
+ return;
20
+ }
21
+ await response.json();
22
+ }
23
+ catch (error) {
24
+ console.error(`Error adding memory: ${error instanceof Error ? error.message : error}`);
25
+ }
26
+ }
27
+ //# sourceMappingURL=add.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add.js","sourceRoot":"","sources":["../src/add.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGxC,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAY;IAC5C,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;QAC1B,OAAO,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;QACtC,OAAO;IACT,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,MAAM,CAAC,OAAO,WAAW,EAAE;YACzD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,eAAe,EAAE,UAAU,MAAM,CAAC,MAAM,EAAE;aAC3C;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;SACtE,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,OAAO,CAAC,KAAK,CAAC,eAAe,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;YACvE,OAAO;QACT,CAAC;QAED,MAAM,QAAQ,CAAC,IAAI,EAAiB,CAAC;IACvC,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,OAAO,CAAC,KAAK,CAAC,wBAAwB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;IAC1F,CAAC;AACH,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Config } from './types.js';
2
+ export declare function getConfig(): Config;
3
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEpC,wBAAgB,SAAS,IAAI,MAAM,CAelC"}
package/dist/config.js ADDED
@@ -0,0 +1,15 @@
1
+ export function getConfig() {
2
+ const apiKey = process.env.SATORI_API_KEY;
3
+ const baseUrl = process.env.SATORI_BASE_URL || 'http://localhost:8000';
4
+ if (!apiKey) {
5
+ throw new Error('Missing SATORI_API_KEY environment variable');
6
+ }
7
+ try {
8
+ new URL(baseUrl);
9
+ }
10
+ catch {
11
+ throw new Error('Invalid SATORI_BASE_URL format');
12
+ }
13
+ return { apiKey, baseUrl };
14
+ }
15
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,SAAS;IACvB,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IAC1C,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,uBAAuB,CAAC;IAEvE,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;IACjE,CAAC;IAED,IAAI,CAAC;QACH,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;IACnB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,gCAAgC,CAAC,CAAC;IACpD,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { searchMemories } from './search.js';
2
+ export { addMemories } from './add.js';
3
+ export declare function main(): Promise<void>;
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEvC,wBAAsB,IAAI,kBA0CzB"}
package/dist/index.js ADDED
@@ -0,0 +1,49 @@
1
+ import { Command } from 'commander';
2
+ import { getConfig } from './config.js';
3
+ import { searchMemories } from './search.js';
4
+ import { addMemories } from './add.js';
5
+ // Re-export for external use
6
+ export { searchMemories } from './search.js';
7
+ export { addMemories } from './add.js';
8
+ export async function main() {
9
+ try {
10
+ getConfig(); // Validate early
11
+ }
12
+ catch (error) {
13
+ console.error(error instanceof Error ? error.message : 'Configuration error');
14
+ process.exit(1);
15
+ }
16
+ const program = new Command();
17
+ program
18
+ .name('satori')
19
+ .description('CLI tool for Satori memory server')
20
+ .version('0.0.1');
21
+ program
22
+ .argument('<query>', 'search query for memories')
23
+ .action(async (query) => {
24
+ await searchMemories(query);
25
+ });
26
+ program
27
+ .command('add')
28
+ .description('add a new memory')
29
+ .argument('<text>', 'text to add as memory')
30
+ .action(async (text) => {
31
+ await addMemories(text);
32
+ });
33
+ program
34
+ .command('chat')
35
+ .description('Start a chat session')
36
+ .option('--provider <provider>', 'Provider to use (openai or anthropic)', 'openai')
37
+ .option('--model <model>', 'Model to use', 'gpt-4o')
38
+ .option('--memory-id <id>', 'Memory ID for scoping')
39
+ .option('--no-stream', 'Disable streaming')
40
+ .action(async (options) => {
41
+ // TODO: Implement chat logic
42
+ console.log('Chat command invoked with options:', options);
43
+ });
44
+ program.parse();
45
+ }
46
+ if (import.meta.main) {
47
+ main();
48
+ }
49
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEvC,6BAA6B;AAC7B,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAEvC,MAAM,CAAC,KAAK,UAAU,IAAI;IACxB,IAAI,CAAC;QACH,SAAS,EAAE,CAAC,CAAC,iBAAiB;IAChC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC;QAC9E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IAE9B,OAAO;SACJ,IAAI,CAAC,QAAQ,CAAC;SACd,WAAW,CAAC,mCAAmC,CAAC;SAChD,OAAO,CAAC,OAAO,CAAC,CAAC;IAEpB,OAAO;SACJ,QAAQ,CAAC,SAAS,EAAE,2BAA2B,CAAC;SAChD,MAAM,CAAC,KAAK,EAAE,KAAa,EAAE,EAAE;QAC9B,MAAM,cAAc,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC,CAAC,CAAC;IAEL,OAAO;SACJ,OAAO,CAAC,KAAK,CAAC;SACd,WAAW,CAAC,kBAAkB,CAAC;SAC/B,QAAQ,CAAC,QAAQ,EAAE,uBAAuB,CAAC;SAC3C,MAAM,CAAC,KAAK,EAAE,IAAY,EAAE,EAAE;QAC7B,MAAM,WAAW,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC,CAAC,CAAC;IAEL,OAAO;SACJ,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,sBAAsB,CAAC;SACnC,MAAM,CAAC,uBAAuB,EAAE,uCAAuC,EAAE,QAAQ,CAAC;SAClF,MAAM,CAAC,iBAAiB,EAAE,cAAc,EAAE,QAAQ,CAAC;SACnD,MAAM,CAAC,kBAAkB,EAAE,uBAAuB,CAAC;SACnD,MAAM,CAAC,aAAa,EAAE,mBAAmB,CAAC;SAC1C,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;QACxB,6BAA6B;QAC7B,OAAO,CAAC,GAAG,CAAC,oCAAoC,EAAE,OAAO,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;IAEL,OAAO,CAAC,KAAK,EAAE,CAAC;AAClB,CAAC;AAED,IAAI,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;IACrB,IAAI,EAAE,CAAC;AACT,CAAC"}
@@ -0,0 +1,12 @@
1
+ import { SearchResponse } from './types.js';
2
+ interface MemoryOptions {
3
+ memoryId?: string;
4
+ topK?: number;
5
+ }
6
+ export declare function buildMemoryContext(prompt: string, options?: MemoryOptions): Promise<{
7
+ results: SearchResponse['results'];
8
+ memoryId: string;
9
+ instruction?: string;
10
+ }>;
11
+ export {};
12
+ //# sourceMappingURL=memory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../src/memory.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAG5C,UAAU,aAAa;IACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,cAAc,CAAC,SAAS,CAAC,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA2C7K"}
package/dist/memory.js ADDED
@@ -0,0 +1,42 @@
1
+ import { getConfig } from './config.js';
2
+ import { generate } from 'random-words';
3
+ export async function buildMemoryContext(prompt, options = {}) {
4
+ let memoryId;
5
+ let generated = false;
6
+ if (options.memoryId) {
7
+ memoryId = options.memoryId;
8
+ }
9
+ else if (process.env.SATORI_MEMORY_ID) {
10
+ memoryId = process.env.SATORI_MEMORY_ID;
11
+ }
12
+ else {
13
+ const words = generate({ exactly: 3 });
14
+ memoryId = words.join('-');
15
+ generated = true;
16
+ }
17
+ const topK = options.topK || 5;
18
+ const config = getConfig();
19
+ const response = await fetch(`${config.baseUrl}/search`, {
20
+ method: 'POST',
21
+ headers: {
22
+ 'Content-Type': 'application/json',
23
+ 'Authorization': `Bearer ${config.apiKey}`
24
+ },
25
+ body: JSON.stringify({
26
+ query: prompt,
27
+ memory_id: memoryId,
28
+ top_k: topK
29
+ })
30
+ });
31
+ if (!response.ok) {
32
+ throw new Error(`HTTP error: ${response.status} ${response.statusText}`);
33
+ }
34
+ const data = await response.json();
35
+ const instruction = generated ? `Set SATORI_MEMORY_ID=${memoryId} to reuse this session.` : undefined;
36
+ return {
37
+ results: data.results,
38
+ memoryId,
39
+ instruction
40
+ };
41
+ }
42
+ //# sourceMappingURL=memory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"memory.js","sourceRoot":"","sources":["../src/memory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAOxC,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,MAAc,EAAE,UAAyB,EAAE;IAClF,IAAI,QAAgB,CAAC;IACrB,IAAI,SAAS,GAAG,KAAK,CAAC;IAEtB,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;QACrB,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;IAC9B,CAAC;SAAM,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QACxC,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;IAC1C,CAAC;SAAM,CAAC;QACN,MAAM,KAAK,GAAG,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAa,CAAC;QACnD,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC3B,SAAS,GAAG,IAAI,CAAC;IACnB,CAAC;IAED,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC;IAE/B,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,MAAM,CAAC,OAAO,SAAS,EAAE;QACvD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE;YACP,cAAc,EAAE,kBAAkB;YAClC,eAAe,EAAE,UAAU,MAAM,CAAC,MAAM,EAAE;SAC3C;QACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;YACnB,KAAK,EAAE,MAAM;YACb,SAAS,EAAE,QAAQ;YACnB,KAAK,EAAE,IAAI;SACZ,CAAC;KACH,CAAC,CAAC;IAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,eAAe,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;IAC3E,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAoB,CAAC;IAErD,MAAM,WAAW,GAAG,SAAS,CAAC,CAAC,CAAC,wBAAwB,QAAQ,yBAAyB,CAAC,CAAC,CAAC,SAAS,CAAC;IAEtG,OAAO;QACL,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,QAAQ;QACR,WAAW;KACZ,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export declare function searchMemories(query: string): Promise<void>;
2
+ //# sourceMappingURL=search.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../src/search.ts"],"names":[],"mappings":"AAGA,wBAAsB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA0BjE"}
package/dist/search.js ADDED
@@ -0,0 +1,27 @@
1
+ import { getConfig } from './config.js';
2
+ export async function searchMemories(query) {
3
+ if (!query || !query.trim()) {
4
+ console.error('Query cannot be empty');
5
+ return;
6
+ }
7
+ try {
8
+ const config = getConfig();
9
+ const response = await fetch(`${config.baseUrl}/search`, {
10
+ method: 'POST',
11
+ headers: {
12
+ 'Content-Type': 'application/json',
13
+ 'Authorization': `Bearer ${config.apiKey}`
14
+ },
15
+ body: JSON.stringify({ query })
16
+ });
17
+ if (!response.ok) {
18
+ console.error(`HTTP error: ${response.status} ${response.statusText}`);
19
+ return;
20
+ }
21
+ await response.json();
22
+ }
23
+ catch (error) {
24
+ console.error(`Error searching memories: ${error instanceof Error ? error.message : error}`);
25
+ }
26
+ }
27
+ //# sourceMappingURL=search.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search.js","sourceRoot":"","sources":["../src/search.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGxC,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,KAAa;IAChD,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;QAC5B,OAAO,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;QACvC,OAAO;IACT,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,MAAM,CAAC,OAAO,SAAS,EAAE;YACvD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,eAAe,EAAE,UAAU,MAAM,CAAC,MAAM,EAAE;aAC3C;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,CAAC;SAChC,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,OAAO,CAAC,KAAK,CAAC,eAAe,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;YACvE,OAAO;QACT,CAAC;QAED,MAAM,QAAQ,CAAC,IAAI,EAAoB,CAAC;IAC1C,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,OAAO,CAAC,KAAK,CAAC,6BAA6B,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;IAC/F,CAAC;AACH,CAAC"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Adds a new memory to the Satori server.
3
+ *
4
+ * @param {string} text - The text content to add as a memory
5
+ * @param {object} [options] - Additional options for the memory
6
+ * @param {string} [options.memoryId] - Optional memory ID for scoping
7
+ * @returns {Promise<void>} Logs success or error messages to console
8
+ * @throws {Error} If the text is empty
9
+ *
10
+ * @example
11
+ * ```bash
12
+ * satori add "I like pizza"
13
+ * ```
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * await addMemories("User prefers dark mode", { memoryId: "session-123" });
18
+ * ```
19
+ */
20
+ export declare function addMemories(text: string, options?: {
21
+ memoryId?: string;
22
+ }): Promise<void>;
23
+ //# sourceMappingURL=add.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../src/add.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CA0BlG"}
@@ -0,0 +1,46 @@
1
+ import { getConfig } from './config.js';
2
+ /**
3
+ * Adds a new memory to the Satori server.
4
+ *
5
+ * @param {string} text - The text content to add as a memory
6
+ * @param {object} [options] - Additional options for the memory
7
+ * @param {string} [options.memoryId] - Optional memory ID for scoping
8
+ * @returns {Promise<void>} Logs success or error messages to console
9
+ * @throws {Error} If the text is empty
10
+ *
11
+ * @example
12
+ * ```bash
13
+ * satori add "I like pizza"
14
+ * ```
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * await addMemories("User prefers dark mode", { memoryId: "session-123" });
19
+ * ```
20
+ */
21
+ export async function addMemories(text, options = {}) {
22
+ if (!text || !text.trim()) {
23
+ console.error('Text cannot be empty');
24
+ return;
25
+ }
26
+ try {
27
+ const config = await getConfig();
28
+ const response = await fetch(`${config.baseUrl}/memories`, {
29
+ method: 'POST',
30
+ headers: {
31
+ 'Content-Type': 'application/json',
32
+ 'Authorization': `Bearer ${config.apiKey}`
33
+ },
34
+ body: JSON.stringify({ messages: [{ role: "user", content: text }], ...(options.memoryId && { memory_id: options.memoryId }) })
35
+ });
36
+ if (!response.ok) {
37
+ console.error(`HTTP error: ${response.status} ${response.statusText}`);
38
+ return;
39
+ }
40
+ await response.json();
41
+ }
42
+ catch (error) {
43
+ console.error(`Error adding memory: ${error instanceof Error ? error.message : error}`);
44
+ }
45
+ }
46
+ //# sourceMappingURL=add.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"add.js","sourceRoot":"","sources":["../../src/add.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAGxC;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,IAAY,EAAE,UAAiC,EAAE;IACjF,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;QAC1B,OAAO,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAC;QACtC,OAAO;IACT,CAAC;IAED,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,SAAS,EAAE,CAAC;QACjC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,MAAM,CAAC,OAAO,WAAW,EAAE;YACzD,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,eAAe,EAAE,UAAU,MAAM,CAAC,MAAM,EAAE;aAC3C;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,QAAQ,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,EAAE,CAAC;SAChI,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,OAAO,CAAC,KAAK,CAAC,eAAe,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;YACvE,OAAO;QACT,CAAC;QAED,MAAM,QAAQ,CAAC,IAAI,EAAiB,CAAC;IACvC,CAAC;IAAC,OAAO,KAAc,EAAE,CAAC;QACxB,OAAO,CAAC,KAAK,CAAC,wBAAwB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC;IAC1F,CAAC;AACH,CAAC"}
@@ -0,0 +1,39 @@
1
+ import { Config } from './types.js';
2
+ /**
3
+ * Checks write access to the config directory by attempting to create, write, and delete a temporary file.
4
+ *
5
+ * @throws {Error} If write access is denied or any operation fails
6
+ */
7
+ export declare function checkWriteAccess(): Promise<void>;
8
+ /**
9
+ * Saves the API key to the config file.
10
+ *
11
+ * @param {string} apiKey - The API key to save
12
+ * @throws {Error} If write access fails or saving fails
13
+ */
14
+ export declare function saveApiKey(apiKey: string): Promise<void>;
15
+ /**
16
+ * Saves the memory ID to the config file.
17
+ *
18
+ * @param {string} memoryId - The memory ID to save
19
+ * @throws {Error} If write access fails or saving fails
20
+ */
21
+ export declare function saveMemoryId(memoryId: string): Promise<void>;
22
+ /**
23
+ * Retrieves and validates configuration from file and environment variables.
24
+ * Fetches the memory_id and api_key from the config file in ~/.config/satori/satori.json.
25
+ *
26
+ * If the API key isn't in the config, it looks in SATORI_API_KEY.
27
+ * If there is no API key in either place it will call the API to get a new key.
28
+ *
29
+ * @returns {Promise<Config>} The validated configuration object
30
+ * @throws {Error} If required environment variables are missing or invalid
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * const config = await getConfig();
35
+ * console.log(config.apiKey); // API key from ~/.config/satori/satori.json or SATORI_API_KEY env var, or null
36
+ * ```
37
+ */
38
+ export declare function getConfig(): Promise<Config>;
39
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAKpC;;;;GAIG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAatD;AAED;;;;;GAKG;AACH,wBAAsB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAa9D;AAED;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAalE;AAoBD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,CAoEjD"}
@@ -0,0 +1,165 @@
1
+ import { homedir } from 'node:os';
2
+ import { join } from 'node:path';
3
+ import { randomUUID } from 'node:crypto';
4
+ /**
5
+ * Checks write access to the config directory by attempting to create, write, and delete a temporary file.
6
+ *
7
+ * @throws {Error} If write access is denied or any operation fails
8
+ */
9
+ export async function checkWriteAccess() {
10
+ // Dynamic import used instead of static import at top-level to enable mocking with Bun's mock.module() in tests.
11
+ // Static imports cannot be overridden by module mocks in Bun, so this allows fs operations to be properly mocked.
12
+ const { promises: fs } = await import('node:fs');
13
+ const dir = join(homedir(), '.config', 'satori');
14
+ try {
15
+ await fs.mkdir(dir, { recursive: true });
16
+ const tempFile = join(dir, `temp-${randomUUID()}.txt`);
17
+ await fs.writeFile(tempFile, 'test');
18
+ await fs.unlink(tempFile);
19
+ }
20
+ catch (error) {
21
+ throw new Error(`Cannot write to config directory: ${error instanceof Error ? error.message : error}`);
22
+ }
23
+ }
24
+ /**
25
+ * Saves the API key to the config file.
26
+ *
27
+ * @param {string} apiKey - The API key to save
28
+ * @throws {Error} If write access fails or saving fails
29
+ */
30
+ export async function saveApiKey(apiKey) {
31
+ // Dynamic import used instead of static import at top-level to enable mocking with Bun's mock.module() in tests.
32
+ // Static imports cannot be overridden by module mocks in Bun, so this allows fs operations to be properly mocked.
33
+ const { promises: fs } = await import('node:fs');
34
+ await checkWriteAccess();
35
+ const configPath = join(homedir(), '.config', 'satori', 'satori.json');
36
+ try {
37
+ const existing = await loadConfigFile();
38
+ const config = { ...existing, api_key: apiKey };
39
+ await fs.writeFile(configPath, JSON.stringify(config, null, 2));
40
+ }
41
+ catch (error) {
42
+ throw new Error(`Failed to save API key: ${error instanceof Error ? error.message : error}`);
43
+ }
44
+ }
45
+ /**
46
+ * Saves the memory ID to the config file.
47
+ *
48
+ * @param {string} memoryId - The memory ID to save
49
+ * @throws {Error} If write access fails or saving fails
50
+ */
51
+ export async function saveMemoryId(memoryId) {
52
+ // Dynamic import used instead of static import at top-level to enable mocking with Bun's mock.module() in tests.
53
+ // Static imports cannot be overridden by module mocks in Bun, so this allows fs operations to be properly mocked.
54
+ const { promises: fs } = await import('node:fs');
55
+ await checkWriteAccess();
56
+ const configPath = join(homedir(), '.config', 'satori', 'satori.json');
57
+ try {
58
+ const existing = await loadConfigFile();
59
+ const config = { ...existing, memory_id: memoryId };
60
+ await fs.writeFile(configPath, JSON.stringify(config, null, 2));
61
+ }
62
+ catch (error) {
63
+ throw new Error(`Failed to save memory ID: ${error instanceof Error ? error.message : error}`);
64
+ }
65
+ }
66
+ /**
67
+ * Loads the config file data.
68
+ *
69
+ * @returns {Promise<Record<string, any>>} The config data
70
+ */
71
+ async function loadConfigFile() {
72
+ try {
73
+ const configPath = join(homedir(), '.config', 'satori', 'satori.json');
74
+ const configFile = Bun.file(configPath);
75
+ if (await configFile.exists()) {
76
+ return await configFile.json();
77
+ }
78
+ }
79
+ catch {
80
+ // Ignore errors
81
+ }
82
+ return {};
83
+ }
84
+ /**
85
+ * Retrieves and validates configuration from file and environment variables.
86
+ * Fetches the memory_id and api_key from the config file in ~/.config/satori/satori.json.
87
+ *
88
+ * If the API key isn't in the config, it looks in SATORI_API_KEY.
89
+ * If there is no API key in either place it will call the API to get a new key.
90
+ *
91
+ * @returns {Promise<Config>} The validated configuration object
92
+ * @throws {Error} If required environment variables are missing or invalid
93
+ *
94
+ * @example
95
+ * ```typescript
96
+ * const config = await getConfig();
97
+ * console.log(config.apiKey); // API key from ~/.config/satori/satori.json or SATORI_API_KEY env var, or null
98
+ * ```
99
+ */
100
+ export async function getConfig() {
101
+ if (process.platform !== 'darwin') {
102
+ throw new Error('We do not currently support Windows yet, email support@satori.sh to request Windows support');
103
+ }
104
+ let apiKey = null;
105
+ let memoryId = undefined;
106
+ try {
107
+ const configPath = join(homedir(), '.config', 'satori', 'satori.json');
108
+ const configFile = Bun.file(configPath);
109
+ if (await configFile.exists()) {
110
+ const data = await configFile.json();
111
+ if (data && typeof data.api_key === 'string') {
112
+ apiKey = data.api_key;
113
+ }
114
+ if (data && typeof data.memory_id === 'string') {
115
+ memoryId = data.memory_id;
116
+ }
117
+ }
118
+ }
119
+ catch {
120
+ // If file reading or parsing fails, apiKey and memoryId remain default
121
+ }
122
+ // Fallback to environment variable if not set from file
123
+ if (!apiKey) {
124
+ apiKey = process.env.SATORI_API_KEY || null;
125
+ }
126
+ const baseUrl = process.env.SATORI_BASE_URL || 'https://api.satori.sh';
127
+ try {
128
+ new URL(baseUrl);
129
+ }
130
+ catch {
131
+ throw new Error('Invalid SATORI_BASE_URL format');
132
+ }
133
+ // Generate new API key if still null
134
+ if (!apiKey) {
135
+ try {
136
+ const response = await fetch(`${baseUrl}/orgs`, {
137
+ method: 'POST',
138
+ });
139
+ if (!response.ok) {
140
+ throw new Error(`HTTP error: ${response.status} ${response.statusText}`);
141
+ }
142
+ const data = await response.json();
143
+ if (data && typeof data.api_key === 'string') {
144
+ apiKey = data.api_key;
145
+ await saveApiKey(apiKey);
146
+ }
147
+ else {
148
+ throw new Error('Invalid response: missing api_key');
149
+ }
150
+ }
151
+ catch (error) {
152
+ throw new Error(`Failed to generate API key: ${error instanceof Error ? error.message : error}`);
153
+ }
154
+ }
155
+ const provider = process.env.SATORI_PROVIDER || 'openai';
156
+ const model = process.env.SATORI_MODEL || 'gpt-4o';
157
+ const openaiKey = process.env.OPENAI_API_KEY;
158
+ const anthropicKey = process.env.ANTHROPIC_API_KEY;
159
+ // Fallback to config file memoryId if not set via env
160
+ if (!memoryId) {
161
+ memoryId = process.env.SATORI_MEMORY_ID;
162
+ }
163
+ return { apiKey, baseUrl, provider, model, openaiKey, anthropicKey, memoryId };
164
+ }
165
+ //# sourceMappingURL=config.js.map