opencode-supermemory 0.1.6 → 2.0.1

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/README.md CHANGED
@@ -219,7 +219,10 @@ Create `~/.config/opencode/supermemory.jsonc`:
219
219
  "containerTagPrefix": "opencode",
220
220
 
221
221
  // Extra keyword patterns for memory detection (regex)
222
- "keywordPatterns": ["log\\s+this", "write\\s+down"]
222
+ "keywordPatterns": ["log\\s+this", "write\\s+down"],
223
+
224
+ // Context usage ratio that triggers compaction (0-1)
225
+ "compactionThreshold": 0.80
223
226
  }
224
227
  ```
225
228
 
package/dist/cli.js CHANGED
@@ -1,9 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
- import { mkdirSync, writeFileSync, readFileSync, existsSync } from "node:fs";
5
- import { join } from "node:path";
6
- import { homedir } from "node:os";
4
+ import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2, readFileSync as readFileSync2, existsSync as existsSync2 } from "node:fs";
5
+ import { join as join2 } from "node:path";
6
+ import { homedir as homedir2 } from "node:os";
7
7
  import * as readline from "node:readline";
8
8
 
9
9
  // src/services/jsonc.ts
@@ -77,10 +77,132 @@ function stripJsoncComments(content) {
77
77
  return result.replace(/,\s*([}\]])/g, "$1");
78
78
  }
79
79
 
80
+ // src/services/auth.ts
81
+ import { createServer } from "node:http";
82
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
83
+ import { exec } from "node:child_process";
84
+ import { join } from "node:path";
85
+ import { homedir } from "node:os";
86
+ var CREDENTIALS_DIR = join(homedir(), ".supermemory-opencode");
87
+ var CREDENTIALS_FILE = join(CREDENTIALS_DIR, "credentials.json");
88
+ var AUTH_PORT = 19877;
89
+ var AUTH_BASE_URL = "https://console.supermemory.ai/auth/connect";
90
+ var CLIENT_NAME = "opencode";
91
+ function loadCredentials() {
92
+ if (!existsSync(CREDENTIALS_FILE))
93
+ return null;
94
+ try {
95
+ const content = readFileSync(CREDENTIALS_FILE, "utf-8");
96
+ return JSON.parse(content);
97
+ } catch {
98
+ return null;
99
+ }
100
+ }
101
+ function saveCredentials(apiKey) {
102
+ mkdirSync(CREDENTIALS_DIR, { recursive: true, mode: 448 });
103
+ const credentials = {
104
+ apiKey,
105
+ createdAt: new Date().toISOString()
106
+ };
107
+ writeFileSync(CREDENTIALS_FILE, JSON.stringify(credentials, null, 2), { mode: 384 });
108
+ }
109
+ function clearCredentials() {
110
+ if (!existsSync(CREDENTIALS_FILE))
111
+ return false;
112
+ rmSync(CREDENTIALS_FILE);
113
+ return true;
114
+ }
115
+ function openBrowser(url) {
116
+ const platform = process.platform;
117
+ const commands = {
118
+ darwin: `open "${url}"`,
119
+ win32: `start "" "${url}"`,
120
+ linux: `xdg-open "${url}"`
121
+ };
122
+ const cmd = commands[platform] ?? `xdg-open "${url}"`;
123
+ exec(cmd, (err) => {
124
+ if (err)
125
+ console.error("Failed to open browser:", err.message);
126
+ });
127
+ }
128
+ function startAuthFlow(timeoutMs = 120000) {
129
+ return new Promise((resolve) => {
130
+ let resolved = false;
131
+ const server = createServer((req, res) => {
132
+ if (resolved)
133
+ return;
134
+ const url = new URL(req.url || "/", `http://localhost:${AUTH_PORT}`);
135
+ if (url.pathname === "/callback") {
136
+ const apiKey = url.searchParams.get("apikey");
137
+ if (apiKey) {
138
+ saveCredentials(apiKey);
139
+ res.writeHead(200, { "Content-Type": "text/html" });
140
+ res.end(`
141
+ <!DOCTYPE html>
142
+ <html>
143
+ <head><title>Success</title></head>
144
+ <body style="font-family: system-ui; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; background: #0a0a0a; color: #fafafa;">
145
+ <div style="text-align: center;">
146
+ <h1 style="color: #22c55e;">✓ Connected!</h1>
147
+ <p>You can close this window and return to your terminal.</p>
148
+ </div>
149
+ </body>
150
+ </html>
151
+ `);
152
+ resolved = true;
153
+ server.close();
154
+ resolve({ success: true, apiKey });
155
+ } else {
156
+ res.writeHead(400, { "Content-Type": "text/html" });
157
+ res.end(`
158
+ <!DOCTYPE html>
159
+ <html>
160
+ <head><title>Error</title></head>
161
+ <body style="font-family: system-ui; display: flex; align-items: center; justify-content: center; height: 100vh; margin: 0; background: #0a0a0a; color: #fafafa;">
162
+ <div style="text-align: center;">
163
+ <h1 style="color: #ef4444;">✗ Connection Failed</h1>
164
+ <p>No API key received. Please try again.</p>
165
+ </div>
166
+ </body>
167
+ </html>
168
+ `);
169
+ resolved = true;
170
+ server.close();
171
+ resolve({ success: false, error: "No API key received" });
172
+ }
173
+ } else {
174
+ res.writeHead(404);
175
+ res.end("Not Found");
176
+ }
177
+ });
178
+ server.on("error", (err) => {
179
+ if (err.code === "EADDRINUSE") {
180
+ resolve({ success: false, error: `Port ${AUTH_PORT} is already in use` });
181
+ } else {
182
+ resolve({ success: false, error: err.message });
183
+ }
184
+ });
185
+ server.listen(AUTH_PORT, () => {
186
+ const callbackUrl = `http://localhost:${AUTH_PORT}/callback`;
187
+ const authUrl = `${AUTH_BASE_URL}?callback=${encodeURIComponent(callbackUrl)}&client=${CLIENT_NAME}`;
188
+ console.log("Opening browser for authentication...");
189
+ console.log(`If it doesn't open, visit: ${authUrl}`);
190
+ openBrowser(authUrl);
191
+ });
192
+ setTimeout(() => {
193
+ if (!resolved) {
194
+ resolved = true;
195
+ server.close();
196
+ resolve({ success: false, error: "Authentication timed out" });
197
+ }
198
+ }, timeoutMs);
199
+ });
200
+ }
201
+
80
202
  // src/cli.ts
81
- var OPENCODE_CONFIG_DIR = join(homedir(), ".config", "opencode");
82
- var OPENCODE_COMMAND_DIR = join(OPENCODE_CONFIG_DIR, "command");
83
- var OH_MY_OPENCODE_CONFIG = join(OPENCODE_CONFIG_DIR, "oh-my-opencode.json");
203
+ var OPENCODE_CONFIG_DIR = join2(homedir2(), ".config", "opencode");
204
+ var OPENCODE_COMMAND_DIR = join2(OPENCODE_CONFIG_DIR, "command");
205
+ var OH_MY_OPENCODE_CONFIG = join2(OPENCODE_CONFIG_DIR, "oh-my-opencode.json");
84
206
  var PLUGIN_NAME = "opencode-supermemory@latest";
85
207
  var SUPERMEMORY_INIT_COMMAND = `---
86
208
  description: Initialize Supermemory with comprehensive codebase knowledge
@@ -233,6 +355,30 @@ Then ask: "I've initialized memory with X insights. Want me to continue refining
233
355
  5. Reflect and verify completeness
234
356
  6. Summarize what was learned and ask if user wants refinement
235
357
  `;
358
+ var SUPERMEMORY_LOGIN_COMMAND = `---
359
+ description: Authenticate with Supermemory via browser
360
+ ---
361
+
362
+ # Supermemory Login
363
+
364
+ Run this command to authenticate the user with Supermemory:
365
+
366
+ \`\`\`bash
367
+ bunx opencode-supermemory@latest login
368
+ \`\`\`
369
+
370
+ This will:
371
+ 1. Start a local server on port 19877
372
+ 2. Open the browser to Supermemory's authentication page
373
+ 3. After the user logs in, save credentials to ~/.supermemory-opencode/credentials.json
374
+
375
+ Wait for the command to complete, then inform the user whether authentication succeeded or failed.
376
+
377
+ If the user wants to log out instead, run:
378
+ \`\`\`bash
379
+ bunx opencode-supermemory@latest logout
380
+ \`\`\`
381
+ `;
236
382
  function createReadline() {
237
383
  return readline.createInterface({
238
384
  input: process.stdin,
@@ -248,11 +394,11 @@ async function confirm(rl, question) {
248
394
  }
249
395
  function findOpencodeConfig() {
250
396
  const candidates = [
251
- join(OPENCODE_CONFIG_DIR, "opencode.jsonc"),
252
- join(OPENCODE_CONFIG_DIR, "opencode.json")
397
+ join2(OPENCODE_CONFIG_DIR, "opencode.jsonc"),
398
+ join2(OPENCODE_CONFIG_DIR, "opencode.json")
253
399
  ];
254
400
  for (const path of candidates) {
255
- if (existsSync(path)) {
401
+ if (existsSync2(path)) {
256
402
  return path;
257
403
  }
258
404
  }
@@ -260,7 +406,7 @@ function findOpencodeConfig() {
260
406
  }
261
407
  function addPluginToConfig(configPath) {
262
408
  try {
263
- const content = readFileSync(configPath, "utf-8");
409
+ const content = readFileSync2(configPath, "utf-8");
264
410
  if (content.includes("opencode-supermemory")) {
265
411
  console.log("✓ Plugin already registered in config");
266
412
  return true;
@@ -289,14 +435,14 @@ function addPluginToConfig(configPath) {
289
435
  "${PLUGIN_NAME}"
290
436
  ${end}`;
291
437
  });
292
- writeFileSync(configPath, newContent);
438
+ writeFileSync2(configPath, newContent);
293
439
  } else {
294
440
  const newContent = content.replace(/^(\s*\{)/, `$1
295
441
  "plugin": ["${PLUGIN_NAME}"],`);
296
- writeFileSync(configPath, newContent);
442
+ writeFileSync2(configPath, newContent);
297
443
  }
298
444
  } else {
299
- writeFileSync(configPath, JSON.stringify(config, null, 2));
445
+ writeFileSync2(configPath, JSON.stringify(config, null, 2));
300
446
  }
301
447
  console.log(`✓ Added plugin to ${configPath}`);
302
448
  return true;
@@ -306,21 +452,24 @@ function addPluginToConfig(configPath) {
306
452
  }
307
453
  }
308
454
  function createNewConfig() {
309
- const configPath = join(OPENCODE_CONFIG_DIR, "opencode.jsonc");
310
- mkdirSync(OPENCODE_CONFIG_DIR, { recursive: true });
455
+ const configPath = join2(OPENCODE_CONFIG_DIR, "opencode.jsonc");
456
+ mkdirSync2(OPENCODE_CONFIG_DIR, { recursive: true });
311
457
  const config = `{
312
458
  "plugin": ["${PLUGIN_NAME}"]
313
459
  }
314
460
  `;
315
- writeFileSync(configPath, config);
461
+ writeFileSync2(configPath, config);
316
462
  console.log(`✓ Created ${configPath}`);
317
463
  return true;
318
464
  }
319
- function createCommand() {
320
- mkdirSync(OPENCODE_COMMAND_DIR, { recursive: true });
321
- const commandPath = join(OPENCODE_COMMAND_DIR, "supermemory-init.md");
322
- writeFileSync(commandPath, SUPERMEMORY_INIT_COMMAND);
465
+ function createCommands() {
466
+ mkdirSync2(OPENCODE_COMMAND_DIR, { recursive: true });
467
+ const initPath = join2(OPENCODE_COMMAND_DIR, "supermemory-init.md");
468
+ writeFileSync2(initPath, SUPERMEMORY_INIT_COMMAND);
323
469
  console.log(`✓ Created /supermemory-init command`);
470
+ const loginPath = join2(OPENCODE_COMMAND_DIR, "supermemory-login.md");
471
+ writeFileSync2(loginPath, SUPERMEMORY_LOGIN_COMMAND);
472
+ console.log(`✓ Created /supermemory-login command`);
324
473
  return true;
325
474
  }
326
475
  function isOhMyOpencodeInstalled() {
@@ -328,17 +477,17 @@ function isOhMyOpencodeInstalled() {
328
477
  if (!configPath)
329
478
  return false;
330
479
  try {
331
- const content = readFileSync(configPath, "utf-8");
480
+ const content = readFileSync2(configPath, "utf-8");
332
481
  return content.includes("oh-my-opencode");
333
482
  } catch {
334
483
  return false;
335
484
  }
336
485
  }
337
486
  function isAutoCompactAlreadyDisabled() {
338
- if (!existsSync(OH_MY_OPENCODE_CONFIG))
487
+ if (!existsSync2(OH_MY_OPENCODE_CONFIG))
339
488
  return false;
340
489
  try {
341
- const content = readFileSync(OH_MY_OPENCODE_CONFIG, "utf-8");
490
+ const content = readFileSync2(OH_MY_OPENCODE_CONFIG, "utf-8");
342
491
  const config = JSON.parse(content);
343
492
  const disabledHooks = config.disabled_hooks;
344
493
  return disabledHooks?.includes("anthropic-context-window-limit-recovery") ?? false;
@@ -349,8 +498,8 @@ function isAutoCompactAlreadyDisabled() {
349
498
  function disableAutoCompactHook() {
350
499
  try {
351
500
  let config = {};
352
- if (existsSync(OH_MY_OPENCODE_CONFIG)) {
353
- const content = readFileSync(OH_MY_OPENCODE_CONFIG, "utf-8");
501
+ if (existsSync2(OH_MY_OPENCODE_CONFIG)) {
502
+ const content = readFileSync2(OH_MY_OPENCODE_CONFIG, "utf-8");
354
503
  config = JSON.parse(content);
355
504
  }
356
505
  const disabledHooks = config.disabled_hooks || [];
@@ -358,7 +507,7 @@ function disableAutoCompactHook() {
358
507
  disabledHooks.push("anthropic-context-window-limit-recovery");
359
508
  }
360
509
  config.disabled_hooks = disabledHooks;
361
- writeFileSync(OH_MY_OPENCODE_CONFIG, JSON.stringify(config, null, 2));
510
+ writeFileSync2(OH_MY_OPENCODE_CONFIG, JSON.stringify(config, null, 2));
362
511
  console.log(`✓ Disabled anthropic-context-window-limit-recovery hook in oh-my-opencode.json`);
363
512
  return true;
364
513
  } catch (err) {
@@ -397,16 +546,16 @@ async function install(options) {
397
546
  }
398
547
  }
399
548
  console.log(`
400
- Step 2: Create /supermemory-init command`);
549
+ Step 2: Create /supermemory-init and /supermemory-login commands`);
401
550
  if (options.tui) {
402
- const shouldCreate = await confirm(rl, "Add /supermemory-init command?");
551
+ const shouldCreate = await confirm(rl, "Add supermemory commands?");
403
552
  if (!shouldCreate) {
404
553
  console.log("Skipped.");
405
554
  } else {
406
- createCommand();
555
+ createCommands();
407
556
  }
408
557
  } else {
409
- createCommand();
558
+ createCommands();
410
559
  }
411
560
  if (isOhMyOpencodeInstalled()) {
412
561
  console.log(`
@@ -430,42 +579,71 @@ Step 3: Configure Oh My OpenCode`);
430
579
  }
431
580
  }
432
581
  }
582
+ if (rl)
583
+ rl.close();
433
584
  console.log(`
434
585
  ` + "─".repeat(50));
435
586
  console.log(`
436
- \uD83D\uDD11 Final step: Set your API key
587
+ \uD83D\uDD11 Final step: Authenticate with Supermemory
437
588
  `);
438
- console.log("Get your API key from: https://console.supermemory.ai");
589
+ if (options.tui) {
590
+ return login();
591
+ }
592
+ console.log("Run this command to authenticate:");
593
+ console.log(" bunx opencode-supermemory@latest login");
439
594
  console.log(`
440
- Then add to your shell profile:
441
- `);
595
+ Or set your API key manually:`);
442
596
  console.log(' export SUPERMEMORY_API_KEY="sm_..."');
443
597
  console.log(`
444
- Or create ~/.config/opencode/supermemory.jsonc:
445
- `);
446
- console.log(' { "apiKey": "sm_..." }');
447
- console.log(`
448
598
  ` + "─".repeat(50));
449
599
  console.log(`
450
600
  ✓ Setup complete! Restart OpenCode to activate.
451
601
  `);
452
- if (rl)
453
- rl.close();
454
602
  return 0;
455
603
  }
604
+ async function login() {
605
+ const existing = loadCredentials();
606
+ if (existing) {
607
+ console.log("Already authenticated. Use 'logout' first to re-authenticate.");
608
+ return 0;
609
+ }
610
+ const result = await startAuthFlow();
611
+ if (result.success) {
612
+ console.log(`
613
+ ✓ Successfully authenticated with Supermemory!`);
614
+ console.log(`Restart OpenCode to activate.
615
+ `);
616
+ return 0;
617
+ } else {
618
+ console.error(`
619
+ ✗ Authentication failed: ${result.error}`);
620
+ return 1;
621
+ }
622
+ }
623
+ function logout() {
624
+ if (clearCredentials()) {
625
+ console.log("✓ Logged out. Credentials cleared.");
626
+ return 0;
627
+ } else {
628
+ console.log("No credentials found.");
629
+ return 0;
630
+ }
631
+ }
456
632
  function printHelp() {
457
633
  console.log(`
458
634
  opencode-supermemory - Persistent memory for OpenCode agents
459
635
 
460
636
  Commands:
461
- install Install and configure the plugin
462
- --no-tui Run in non-interactive mode (for LLM agents)
463
- --disable-context-recovery Disable Oh My OpenCode's context-window-limit-recovery hook (use with --no-tui)
637
+ install Install and configure the plugin
638
+ --no-tui Non-interactive mode (for LLM agents)
639
+ --disable-context-recovery Disable Oh My OpenCode's context hook
640
+ login Authenticate with Supermemory (opens browser)
641
+ logout Clear stored credentials
464
642
 
465
643
  Examples:
466
644
  bunx opencode-supermemory@latest install
467
- bunx opencode-supermemory@latest install --no-tui
468
- bunx opencode-supermemory@latest install --no-tui --disable-context-recovery
645
+ bunx opencode-supermemory@latest login
646
+ bunx opencode-supermemory@latest logout
469
647
  `);
470
648
  }
471
649
  var args = process.argv.slice(2);
@@ -483,6 +661,10 @@ if (args[0] === "install") {
483
661
  const noTui = args.includes("--no-tui");
484
662
  const disableAutoCompact = args.includes("--disable-context-recovery");
485
663
  install({ tui: !noTui, disableAutoCompact }).then((code) => process.exit(code));
664
+ } else if (args[0] === "login") {
665
+ login().then((code) => process.exit(code));
666
+ } else if (args[0] === "logout") {
667
+ process.exit(logout());
486
668
  } else {
487
669
  console.error(`Unknown command: ${args[0]}`);
488
670
  printHelp();
package/dist/config.d.ts CHANGED
@@ -8,6 +8,7 @@ export declare const CONFIG: {
8
8
  containerTagPrefix: string;
9
9
  filterPrompt: string;
10
10
  keywordPatterns: string[];
11
+ compactionThreshold: number;
11
12
  };
12
13
  export declare function isConfigured(): boolean;
13
14
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AA+EA,eAAO,MAAM,mBAAmB,oBAAuD,CAAC;AAExF,eAAO,MAAM,MAAM;;;;;;;;;CAYlB,CAAC;AAEF,wBAAgB,YAAY,IAAI,OAAO,CAEtC"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAiGA,eAAO,MAAM,mBAAmB,oBAAc,CAAC;AAE/C,eAAO,MAAM,MAAM;;;;;;;;;;CAalB,CAAC;AAEF,wBAAgB,YAAY,IAAI,OAAO,CAEtC"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAC;AAsC/D,eAAO,MAAM,iBAAiB,EAAE,MAwa/B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,qBAAqB,CAAC;AAsC/D,eAAO,MAAM,iBAAiB,EAAE,MAsc/B,CAAC"}
package/dist/index.js CHANGED
@@ -13602,9 +13602,9 @@ Supermemory.Search = Search;
13602
13602
  Supermemory.Settings = Settings;
13603
13603
  Supermemory.Connections = Connections;
13604
13604
  // src/config.ts
13605
- import { existsSync, readFileSync } from "node:fs";
13606
- import { join } from "node:path";
13607
- import { homedir } from "node:os";
13605
+ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
13606
+ import { join as join2 } from "node:path";
13607
+ import { homedir as homedir2 } from "node:os";
13608
13608
 
13609
13609
  // src/services/jsonc.ts
13610
13610
  function stripJsoncComments(content) {
@@ -13677,11 +13677,28 @@ function stripJsoncComments(content) {
13677
13677
  return result.replace(/,\s*([}\]])/g, "$1");
13678
13678
  }
13679
13679
 
13680
+ // src/services/auth.ts
13681
+ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
13682
+ import { join } from "node:path";
13683
+ import { homedir } from "node:os";
13684
+ var CREDENTIALS_DIR = join(homedir(), ".supermemory-opencode");
13685
+ var CREDENTIALS_FILE = join(CREDENTIALS_DIR, "credentials.json");
13686
+ function loadCredentials() {
13687
+ if (!existsSync(CREDENTIALS_FILE))
13688
+ return null;
13689
+ try {
13690
+ const content = readFileSync(CREDENTIALS_FILE, "utf-8");
13691
+ return JSON.parse(content);
13692
+ } catch {
13693
+ return null;
13694
+ }
13695
+ }
13696
+
13680
13697
  // src/config.ts
13681
- var CONFIG_DIR = join(homedir(), ".config", "opencode");
13698
+ var CONFIG_DIR = join2(homedir2(), ".config", "opencode");
13682
13699
  var CONFIG_FILES = [
13683
- join(CONFIG_DIR, "supermemory.jsonc"),
13684
- join(CONFIG_DIR, "supermemory.json")
13700
+ join2(CONFIG_DIR, "supermemory.jsonc"),
13701
+ join2(CONFIG_DIR, "supermemory.json")
13685
13702
  ];
13686
13703
  var DEFAULT_KEYWORD_PATTERNS = [
13687
13704
  "remember",
@@ -13709,7 +13726,8 @@ var DEFAULTS = {
13709
13726
  injectProfile: true,
13710
13727
  containerTagPrefix: "opencode",
13711
13728
  filterPrompt: "You are a stateful coding agent. Remember all the information, including but not limited to user's coding preferences, tech stack, behaviours, workflows, and any other relevant details.",
13712
- keywordPatterns: []
13729
+ keywordPatterns: [],
13730
+ compactionThreshold: 0.8
13713
13731
  };
13714
13732
  function isValidRegex(pattern) {
13715
13733
  try {
@@ -13719,11 +13737,19 @@ function isValidRegex(pattern) {
13719
13737
  return false;
13720
13738
  }
13721
13739
  }
13740
+ function validateCompactionThreshold(value) {
13741
+ if (value === undefined || typeof value !== "number" || isNaN(value)) {
13742
+ return DEFAULTS.compactionThreshold;
13743
+ }
13744
+ if (value <= 0 || value > 1)
13745
+ return DEFAULTS.compactionThreshold;
13746
+ return value;
13747
+ }
13722
13748
  function loadConfig() {
13723
13749
  for (const path2 of CONFIG_FILES) {
13724
- if (existsSync(path2)) {
13750
+ if (existsSync2(path2)) {
13725
13751
  try {
13726
- const content = readFileSync(path2, "utf-8");
13752
+ const content = readFileSync2(path2, "utf-8");
13727
13753
  const json2 = stripJsoncComments(content);
13728
13754
  return JSON.parse(json2);
13729
13755
  } catch {}
@@ -13732,7 +13758,14 @@ function loadConfig() {
13732
13758
  return {};
13733
13759
  }
13734
13760
  var fileConfig = loadConfig();
13735
- var SUPERMEMORY_API_KEY = fileConfig.apiKey ?? process.env.SUPERMEMORY_API_KEY;
13761
+ function getApiKey() {
13762
+ if (process.env.SUPERMEMORY_API_KEY)
13763
+ return process.env.SUPERMEMORY_API_KEY;
13764
+ if (fileConfig.apiKey)
13765
+ return fileConfig.apiKey;
13766
+ return loadCredentials()?.apiKey;
13767
+ }
13768
+ var SUPERMEMORY_API_KEY = getApiKey();
13736
13769
  var CONFIG = {
13737
13770
  similarityThreshold: fileConfig.similarityThreshold ?? DEFAULTS.similarityThreshold,
13738
13771
  maxMemories: fileConfig.maxMemories ?? DEFAULTS.maxMemories,
@@ -13744,18 +13777,19 @@ var CONFIG = {
13744
13777
  keywordPatterns: [
13745
13778
  ...DEFAULT_KEYWORD_PATTERNS,
13746
13779
  ...(fileConfig.keywordPatterns ?? []).filter(isValidRegex)
13747
- ]
13780
+ ],
13781
+ compactionThreshold: validateCompactionThreshold(fileConfig.compactionThreshold)
13748
13782
  };
13749
13783
  function isConfigured() {
13750
13784
  return !!SUPERMEMORY_API_KEY;
13751
13785
  }
13752
13786
 
13753
13787
  // src/services/logger.ts
13754
- import { appendFileSync, writeFileSync } from "fs";
13755
- import { homedir as homedir2 } from "os";
13756
- import { join as join2 } from "path";
13757
- var LOG_FILE = join2(homedir2(), ".opencode-supermemory.log");
13758
- writeFileSync(LOG_FILE, `
13788
+ import { appendFileSync, writeFileSync as writeFileSync2 } from "fs";
13789
+ import { homedir as homedir3 } from "os";
13790
+ import { join as join3 } from "path";
13791
+ var LOG_FILE = join3(homedir3(), ".opencode-supermemory.log");
13792
+ writeFileSync2(LOG_FILE, `
13759
13793
  --- Session started: ${new Date().toISOString()} ---
13760
13794
  `, { flag: "a" });
13761
13795
  function log(message, data) {
@@ -13991,16 +14025,15 @@ function isFullyPrivate(content) {
13991
14025
  }
13992
14026
 
13993
14027
  // src/services/compaction.ts
13994
- import { existsSync as existsSync2, mkdirSync, readdirSync, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
13995
- import { join as join3 } from "node:path";
13996
- import { homedir as homedir3 } from "node:os";
13997
- var MESSAGE_STORAGE = join3(homedir3(), ".opencode", "messages");
13998
- var PART_STORAGE = join3(homedir3(), ".opencode", "parts");
14028
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "node:fs";
14029
+ import { join as join4 } from "node:path";
14030
+ import { homedir as homedir4 } from "node:os";
14031
+ var MESSAGE_STORAGE = join4(homedir4(), ".opencode", "messages");
14032
+ var PART_STORAGE = join4(homedir4(), ".opencode", "parts");
13999
14033
  var DEFAULT_THRESHOLD = 0.8;
14000
14034
  var MIN_TOKENS_FOR_COMPACTION = 50000;
14001
14035
  var COMPACTION_COOLDOWN_MS = 30000;
14002
- var CLAUDE_DEFAULT_CONTEXT_LIMIT = 200000;
14003
- var CLAUDE_MODEL_PATTERN = /claude-(opus|sonnet|haiku)/i;
14036
+ var DEFAULT_CONTEXT_LIMIT = 200000;
14004
14037
  function createCompactionPrompt(projectMemories) {
14005
14038
  const memoriesSection = projectMemories.length > 0 ? `
14006
14039
  ## Project Knowledge (from Supermemory)
@@ -14040,35 +14073,32 @@ ${memoriesSection}
14040
14073
  This context is critical for maintaining continuity after compaction.
14041
14074
  `;
14042
14075
  }
14043
- function isSupportedModel(modelID) {
14044
- return CLAUDE_MODEL_PATTERN.test(modelID);
14045
- }
14046
14076
  function getMessageDir(sessionID) {
14047
- if (!existsSync2(MESSAGE_STORAGE))
14077
+ if (!existsSync3(MESSAGE_STORAGE))
14048
14078
  return null;
14049
- const directPath = join3(MESSAGE_STORAGE, sessionID);
14050
- if (existsSync2(directPath))
14079
+ const directPath = join4(MESSAGE_STORAGE, sessionID);
14080
+ if (existsSync3(directPath))
14051
14081
  return directPath;
14052
14082
  for (const dir of readdirSync(MESSAGE_STORAGE)) {
14053
- const sessionPath = join3(MESSAGE_STORAGE, dir, sessionID);
14054
- if (existsSync2(sessionPath))
14083
+ const sessionPath = join4(MESSAGE_STORAGE, dir, sessionID);
14084
+ if (existsSync3(sessionPath))
14055
14085
  return sessionPath;
14056
14086
  }
14057
14087
  return null;
14058
14088
  }
14059
14089
  function getOrCreateMessageDir(sessionID) {
14060
- if (!existsSync2(MESSAGE_STORAGE)) {
14061
- mkdirSync(MESSAGE_STORAGE, { recursive: true });
14090
+ if (!existsSync3(MESSAGE_STORAGE)) {
14091
+ mkdirSync2(MESSAGE_STORAGE, { recursive: true });
14062
14092
  }
14063
- const directPath = join3(MESSAGE_STORAGE, sessionID);
14064
- if (existsSync2(directPath))
14093
+ const directPath = join4(MESSAGE_STORAGE, sessionID);
14094
+ if (existsSync3(directPath))
14065
14095
  return directPath;
14066
14096
  for (const dir of readdirSync(MESSAGE_STORAGE)) {
14067
- const sessionPath = join3(MESSAGE_STORAGE, dir, sessionID);
14068
- if (existsSync2(sessionPath))
14097
+ const sessionPath = join4(MESSAGE_STORAGE, dir, sessionID);
14098
+ if (existsSync3(sessionPath))
14069
14099
  return sessionPath;
14070
14100
  }
14071
- mkdirSync(directPath, { recursive: true });
14101
+ mkdirSync2(directPath, { recursive: true });
14072
14102
  return directPath;
14073
14103
  }
14074
14104
  function findNearestMessageWithFields(messageDir) {
@@ -14076,7 +14106,7 @@ function findNearestMessageWithFields(messageDir) {
14076
14106
  const files = readdirSync(messageDir).filter((f) => f.endsWith(".json")).sort().reverse();
14077
14107
  for (const file2 of files) {
14078
14108
  try {
14079
- const content = readFileSync2(join3(messageDir, file2), "utf-8");
14109
+ const content = readFileSync3(join4(messageDir, file2), "utf-8");
14080
14110
  const msg = JSON.parse(content);
14081
14111
  if (msg.agent && msg.model?.providerID && msg.model?.modelID) {
14082
14112
  return msg;
@@ -14131,12 +14161,12 @@ function injectHookMessage(sessionID, hookContent, originalMessage) {
14131
14161
  sessionID
14132
14162
  };
14133
14163
  try {
14134
- writeFileSync2(join3(messageDir, `${messageID}.json`), JSON.stringify(messageMeta, null, 2));
14135
- const partDir = join3(PART_STORAGE, messageID);
14136
- if (!existsSync2(partDir)) {
14137
- mkdirSync(partDir, { recursive: true });
14164
+ writeFileSync3(join4(messageDir, `${messageID}.json`), JSON.stringify(messageMeta, null, 2));
14165
+ const partDir = join4(PART_STORAGE, messageID);
14166
+ if (!existsSync3(partDir)) {
14167
+ mkdirSync2(partDir, { recursive: true });
14138
14168
  }
14139
- writeFileSync2(join3(partDir, `${partID}.json`), JSON.stringify(textPart, null, 2));
14169
+ writeFileSync3(join4(partDir, `${partID}.json`), JSON.stringify(textPart, null, 2));
14140
14170
  log("[compaction] hook message injected", { sessionID, messageID });
14141
14171
  return true;
14142
14172
  } catch (err) {
@@ -14218,12 +14248,8 @@ ${summaryContent}`, tags.project, { type: "conversation" });
14218
14248
  modelID = storedMessage.model.modelID;
14219
14249
  }
14220
14250
  agent = storedMessage?.agent;
14221
- if (!isSupportedModel(modelID)) {
14222
- log("[compaction] skipping unsupported model", { modelID });
14223
- return;
14224
- }
14225
14251
  const configLimit = getModelLimit?.(providerID, modelID);
14226
- const contextLimit = configLimit ?? CLAUDE_DEFAULT_CONTEXT_LIMIT;
14252
+ const contextLimit = configLimit ?? DEFAULT_CONTEXT_LIMIT;
14227
14253
  const totalUsed = tokens.input + tokens.cache.read + tokens.output;
14228
14254
  if (totalUsed < MIN_TOKENS_FOR_COMPACTION)
14229
14255
  return;
@@ -14415,7 +14441,33 @@ var SupermemoryPlugin = async (ctx) => {
14415
14441
  if (!isConfigured()) {
14416
14442
  log("Plugin disabled - SUPERMEMORY_API_KEY not set");
14417
14443
  }
14418
- const compactionHook = isConfigured() && ctx.client ? createCompactionHook(ctx, tags) : null;
14444
+ const modelLimits = new Map;
14445
+ (async () => {
14446
+ try {
14447
+ const response = await ctx.client.provider.list();
14448
+ if (response.data?.all) {
14449
+ for (const provider of response.data.all) {
14450
+ if (provider.models) {
14451
+ for (const [modelId, model] of Object.entries(provider.models)) {
14452
+ if (model.limit?.context) {
14453
+ modelLimits.set(`${provider.id}/${modelId}`, model.limit.context);
14454
+ }
14455
+ }
14456
+ }
14457
+ }
14458
+ }
14459
+ log("Model limits loaded", { count: modelLimits.size });
14460
+ } catch (error45) {
14461
+ log("Failed to fetch model limits", { error: String(error45) });
14462
+ }
14463
+ })();
14464
+ const getModelLimit = (providerID, modelID) => {
14465
+ return modelLimits.get(`${providerID}/${modelID}`);
14466
+ };
14467
+ const compactionHook = isConfigured() && ctx.client ? createCompactionHook(ctx, tags, {
14468
+ threshold: CONFIG.compactionThreshold,
14469
+ getModelLimit
14470
+ }) : null;
14419
14471
  return {
14420
14472
  "chat.message": async (input, output) => {
14421
14473
  if (!isConfigured())
@@ -0,0 +1,15 @@
1
+ interface Credentials {
2
+ apiKey: string;
3
+ createdAt: string;
4
+ }
5
+ export declare function loadCredentials(): Credentials | null;
6
+ export declare function saveCredentials(apiKey: string): void;
7
+ export declare function clearCredentials(): boolean;
8
+ export interface AuthResult {
9
+ success: boolean;
10
+ apiKey?: string;
11
+ error?: string;
12
+ }
13
+ export declare function startAuthFlow(timeoutMs?: number): Promise<AuthResult>;
14
+ export {};
15
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/services/auth.ts"],"names":[],"mappings":"AAYA,UAAU,WAAW;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,eAAe,IAAI,WAAW,GAAG,IAAI,CAQpD;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAOpD;AAED,wBAAgB,gBAAgB,IAAI,OAAO,CAI1C;AAiBD,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,aAAa,CAAC,SAAS,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CA+ErE"}
@@ -1 +1 @@
1
- {"version":3,"file":"compaction.d.ts","sourceRoot":"","sources":["../../src/services/compaction.ts"],"names":[],"mappings":"AAsBA,UAAU,SAAS;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CACxC;AAED,UAAU,WAAW;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAgBD,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;CAC7E;AAuLD,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE;QACN,OAAO,EAAE;YACP,SAAS,EAAE,CAAC,MAAM,EAAE;gBAAE,IAAI,EAAE;oBAAE,EAAE,EAAE,MAAM,CAAA;iBAAE,CAAC;gBAAC,IAAI,EAAE;oBAAE,UAAU,EAAE,MAAM,CAAC;oBAAC,OAAO,EAAE,MAAM,CAAA;iBAAE,CAAC;gBAAC,KAAK,EAAE;oBAAE,SAAS,EAAE,MAAM,CAAA;iBAAE,CAAA;aAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;YAC/I,QAAQ,EAAE,CAAC,MAAM,EAAE;gBAAE,IAAI,EAAE;oBAAE,EAAE,EAAE,MAAM,CAAA;iBAAE,CAAC;gBAAC,KAAK,EAAE;oBAAE,SAAS,EAAE,MAAM,CAAA;iBAAE,CAAA;aAAE,KAAK,OAAO,CAAC;gBAAE,IAAI,CAAC,EAAE,KAAK,CAAC;oBAAE,IAAI,EAAE,WAAW,CAAA;iBAAE,CAAC,CAAA;aAAE,CAAC,CAAC;YAC/H,WAAW,EAAE,CAAC,MAAM,EAAE;gBAAE,IAAI,EAAE;oBAAE,EAAE,EAAE,MAAM,CAAA;iBAAE,CAAC;gBAAC,IAAI,EAAE;oBAAE,KAAK,CAAC,EAAE,MAAM,CAAC;oBAAC,KAAK,EAAE,KAAK,CAAC;wBAAE,IAAI,EAAE,MAAM,CAAC;wBAAC,IAAI,EAAE,MAAM,CAAA;qBAAE,CAAC,CAAA;iBAAE,CAAC;gBAAC,KAAK,EAAE;oBAAE,SAAS,EAAE,MAAM,CAAA;iBAAE,CAAA;aAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;SAC3K,CAAC;QACF,GAAG,EAAE;YACH,SAAS,EAAE,CAAC,MAAM,EAAE;gBAAE,IAAI,EAAE;oBAAE,KAAK,EAAE,MAAM,CAAC;oBAAC,OAAO,EAAE,MAAM,CAAC;oBAAC,OAAO,EAAE,MAAM,CAAC;oBAAC,QAAQ,EAAE,MAAM,CAAA;iBAAE,CAAA;aAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;SAC1H,CAAC;KACH,CAAC;CACH;AAED,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,iBAAiB,EACtB,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EACvC,OAAO,CAAC,EAAE,iBAAiB;qBAqOF;QAAE,KAAK,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,UAAU,CAAC,EAAE,OAAO,CAAA;SAAE,CAAA;KAAE;EAgE3E"}
1
+ {"version":3,"file":"compaction.d.ts","sourceRoot":"","sources":["../../src/services/compaction.ts"],"names":[],"mappings":"AAqBA,UAAU,SAAS;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CACxC;AAED,UAAU,WAAW;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAgBD,MAAM,WAAW,iBAAiB;IAChC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;CAC7E;AAmLD,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE;QACN,OAAO,EAAE;YACP,SAAS,EAAE,CAAC,MAAM,EAAE;gBAAE,IAAI,EAAE;oBAAE,EAAE,EAAE,MAAM,CAAA;iBAAE,CAAC;gBAAC,IAAI,EAAE;oBAAE,UAAU,EAAE,MAAM,CAAC;oBAAC,OAAO,EAAE,MAAM,CAAA;iBAAE,CAAC;gBAAC,KAAK,EAAE;oBAAE,SAAS,EAAE,MAAM,CAAA;iBAAE,CAAA;aAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;YAC/I,QAAQ,EAAE,CAAC,MAAM,EAAE;gBAAE,IAAI,EAAE;oBAAE,EAAE,EAAE,MAAM,CAAA;iBAAE,CAAC;gBAAC,KAAK,EAAE;oBAAE,SAAS,EAAE,MAAM,CAAA;iBAAE,CAAA;aAAE,KAAK,OAAO,CAAC;gBAAE,IAAI,CAAC,EAAE,KAAK,CAAC;oBAAE,IAAI,EAAE,WAAW,CAAA;iBAAE,CAAC,CAAA;aAAE,CAAC,CAAC;YAC/H,WAAW,EAAE,CAAC,MAAM,EAAE;gBAAE,IAAI,EAAE;oBAAE,EAAE,EAAE,MAAM,CAAA;iBAAE,CAAC;gBAAC,IAAI,EAAE;oBAAE,KAAK,CAAC,EAAE,MAAM,CAAC;oBAAC,KAAK,EAAE,KAAK,CAAC;wBAAE,IAAI,EAAE,MAAM,CAAC;wBAAC,IAAI,EAAE,MAAM,CAAA;qBAAE,CAAC,CAAA;iBAAE,CAAC;gBAAC,KAAK,EAAE;oBAAE,SAAS,EAAE,MAAM,CAAA;iBAAE,CAAA;aAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;SAC3K,CAAC;QACF,GAAG,EAAE;YACH,SAAS,EAAE,CAAC,MAAM,EAAE;gBAAE,IAAI,EAAE;oBAAE,KAAK,EAAE,MAAM,CAAC;oBAAC,OAAO,EAAE,MAAM,CAAC;oBAAC,OAAO,EAAE,MAAM,CAAC;oBAAC,QAAQ,EAAE,MAAM,CAAA;iBAAE,CAAA;aAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;SAC1H,CAAC;KACH,CAAC;CACH;AAED,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,iBAAiB,EACtB,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,EACvC,OAAO,CAAC,EAAE,iBAAiB;qBAgOF;QAAE,KAAK,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,UAAU,CAAC,EAAE,OAAO,CAAA;SAAE,CAAA;KAAE;EAgE3E"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-supermemory",
3
- "version": "0.1.6",
3
+ "version": "2.0.1",
4
4
  "description": "OpenCode plugin that gives coding agents persistent memory using Supermemory",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",