toon-memory 1.6.5 → 1.6.7
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/bin/toon-memory.js +6 -5
- package/dist/cli/toon-memory.js +6 -5
- package/install.ps1 +13 -0
- package/mcp/server.js +35 -9
- package/package.json +1 -1
- package/src/bin/toon-memory.ts +6 -8
- package/src/cli/toon-memory.ts +6 -8
- package/src/mcp/server.ts +52 -11
package/bin/toon-memory.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { dirname, join } from "path";
|
|
3
3
|
import { fileURLToPath } from "url";
|
|
4
|
-
import { spawn } from "child_process";
|
|
5
4
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
5
|
const args = process.argv.slice(2);
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
if (args[0] === "mcp") {
|
|
7
|
+
await import(join(__dirname, "..", "mcp", "server.js"));
|
|
8
|
+
} else {
|
|
9
|
+
process.argv = ["node", "toon-memory", ...args];
|
|
10
|
+
await import(join(__dirname, "..", "dist", "cli", "setup.js"));
|
|
11
|
+
}
|
package/dist/cli/toon-memory.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { dirname, join } from "path";
|
|
2
2
|
import { fileURLToPath } from "url";
|
|
3
|
-
import { spawn } from "child_process";
|
|
4
3
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
5
4
|
const args = process.argv.slice(2);
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
if (args[0] === "mcp") {
|
|
6
|
+
await import(join(__dirname, "..", "mcp", "server.js"));
|
|
7
|
+
} else {
|
|
8
|
+
process.argv = ["node", "toon-memory", ...args];
|
|
9
|
+
await import(join(__dirname, "..", "dist", "cli", "setup.js"));
|
|
10
|
+
}
|
package/install.ps1
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
# Usage: irm https://raw.githubusercontent.com/LuiggiVal08/toon-memory/main/install.ps1 | iex
|
|
3
3
|
|
|
4
4
|
$TOON_VERSION = "1.0.9"
|
|
5
|
+
# SHA-256 checksum of the tarball. Update on each release.
|
|
6
|
+
# Generate with: certutil -hashfile toon-memory-$TOON_VERSION.tgz SHA256
|
|
7
|
+
$TOON_CHECKSUM = "d5b2a8cbe0f3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9"
|
|
5
8
|
|
|
6
9
|
Write-Host "🧠 toon-memory installer" -ForegroundColor Cyan
|
|
7
10
|
Write-Host ""
|
|
@@ -32,6 +35,16 @@ if (Get-Command npm -ErrorAction SilentlyContinue) {
|
|
|
32
35
|
$tgzPath = "$INSTALL_DIR\toon-memory.tgz"
|
|
33
36
|
Invoke-WebRequest -Uri $url -OutFile $tgzPath
|
|
34
37
|
|
|
38
|
+
# Verify integrity
|
|
39
|
+
$actualHash = (Get-FileHash -Path $tgzPath -Algorithm SHA256).Hash.ToLower()
|
|
40
|
+
if ($actualHash -ne $TOON_CHECKSUM) {
|
|
41
|
+
Write-Host "❌ Checksum mismatch! Expected $TOON_CHECKSUM, got $actualHash" -ForegroundColor Red
|
|
42
|
+
Write-Host "The downloaded file may be corrupted or tampered with." -ForegroundColor Red
|
|
43
|
+
Remove-Item -Path $tgzPath -Force
|
|
44
|
+
exit 1
|
|
45
|
+
}
|
|
46
|
+
Write-Host "✅ Integrity verified" -ForegroundColor Green
|
|
47
|
+
|
|
35
48
|
# Extract
|
|
36
49
|
cd $INSTALL_DIR
|
|
37
50
|
tar xzf toon-memory.tgz
|
package/mcp/server.js
CHANGED
|
@@ -27648,6 +27648,7 @@ var MEMORY_DIR = join(process.cwd(), ".opencode", "memory");
|
|
|
27648
27648
|
var MEMORY_FILE = join(MEMORY_DIR, "data.toon");
|
|
27649
27649
|
var ARCHIVE_FILE = join(MEMORY_DIR, "archive.toon");
|
|
27650
27650
|
var CONFIG_FILE = join(MEMORY_DIR, "config.json");
|
|
27651
|
+
var ENV_FILE = join(MEMORY_DIR, ".env");
|
|
27651
27652
|
var ARCHIVE_DAYS = 30;
|
|
27652
27653
|
var ALGORITHM = "aes-256-gcm";
|
|
27653
27654
|
function loadConfig() {
|
|
@@ -27664,6 +27665,21 @@ function saveConfig(config2) {
|
|
|
27664
27665
|
ensureMemoryDir();
|
|
27665
27666
|
writeFileSync(CONFIG_FILE, JSON.stringify(config2, null, 2));
|
|
27666
27667
|
}
|
|
27668
|
+
function readEnvKey() {
|
|
27669
|
+
try {
|
|
27670
|
+
if (!existsSync(ENV_FILE)) return void 0;
|
|
27671
|
+
const content = readFileSync(ENV_FILE, "utf-8").trim();
|
|
27672
|
+
const match = content.match(/^TOON_MEMORY_KEY=(.+)$/m);
|
|
27673
|
+
return match ? match[1] : void 0;
|
|
27674
|
+
} catch {
|
|
27675
|
+
return void 0;
|
|
27676
|
+
}
|
|
27677
|
+
}
|
|
27678
|
+
function writeEnvKey(key) {
|
|
27679
|
+
ensureMemoryDir();
|
|
27680
|
+
writeFileSync(ENV_FILE, `TOON_MEMORY_KEY=${key}
|
|
27681
|
+
`);
|
|
27682
|
+
}
|
|
27667
27683
|
function ensureMemoryDir() {
|
|
27668
27684
|
if (!existsSync(MEMORY_DIR)) mkdirSync(MEMORY_DIR, { recursive: true });
|
|
27669
27685
|
}
|
|
@@ -27700,9 +27716,11 @@ function readMemory() {
|
|
|
27700
27716
|
ensureMemoryFile();
|
|
27701
27717
|
const config2 = loadConfig();
|
|
27702
27718
|
const data = readFileSync(MEMORY_FILE, "utf-8");
|
|
27703
|
-
if (config2.encrypted
|
|
27719
|
+
if (config2.encrypted) {
|
|
27720
|
+
const key = readEnvKey();
|
|
27721
|
+
if (!key) return data;
|
|
27704
27722
|
try {
|
|
27705
|
-
return decrypt(data,
|
|
27723
|
+
return decrypt(data, key);
|
|
27706
27724
|
} catch {
|
|
27707
27725
|
return data;
|
|
27708
27726
|
}
|
|
@@ -27712,12 +27730,15 @@ function readMemory() {
|
|
|
27712
27730
|
function writeMemory(content) {
|
|
27713
27731
|
ensureMemoryFile();
|
|
27714
27732
|
const config2 = loadConfig();
|
|
27715
|
-
if (config2.encrypted
|
|
27716
|
-
const
|
|
27717
|
-
|
|
27718
|
-
|
|
27719
|
-
|
|
27733
|
+
if (config2.encrypted) {
|
|
27734
|
+
const key = readEnvKey();
|
|
27735
|
+
if (key) {
|
|
27736
|
+
const encrypted = encrypt(content, key);
|
|
27737
|
+
writeFileSync(MEMORY_FILE, encrypted);
|
|
27738
|
+
return;
|
|
27739
|
+
}
|
|
27720
27740
|
}
|
|
27741
|
+
writeFileSync(MEMORY_FILE, content);
|
|
27721
27742
|
}
|
|
27722
27743
|
function generateId() {
|
|
27723
27744
|
return randomBytes(4).toString("hex");
|
|
@@ -28022,7 +28043,8 @@ server.registerTool(
|
|
|
28022
28043
|
const data = readFileSync(MEMORY_FILE, "utf-8");
|
|
28023
28044
|
const encrypted = encrypt(data, key);
|
|
28024
28045
|
writeFileSync(MEMORY_FILE, encrypted);
|
|
28025
|
-
saveConfig({ encrypted: true
|
|
28046
|
+
saveConfig({ encrypted: true });
|
|
28047
|
+
writeEnvKey(key);
|
|
28026
28048
|
return {
|
|
28027
28049
|
content: [{
|
|
28028
28050
|
type: "text",
|
|
@@ -28047,9 +28069,13 @@ server.registerTool(
|
|
|
28047
28069
|
if (!config2.encrypted) {
|
|
28048
28070
|
return { content: [{ type: "text", text: "La encriptaci\xF3n no est\xE1 habilitada" }] };
|
|
28049
28071
|
}
|
|
28072
|
+
const resolvedKey = key || readEnvKey() || "";
|
|
28073
|
+
if (!resolvedKey) {
|
|
28074
|
+
return { content: [{ type: "text", text: "\u274C No hay clave. P\xE1sala como argumento o la del archivo .env" }] };
|
|
28075
|
+
}
|
|
28050
28076
|
try {
|
|
28051
28077
|
const data = readFileSync(MEMORY_FILE, "utf-8");
|
|
28052
|
-
const decrypted = decrypt(data,
|
|
28078
|
+
const decrypted = decrypt(data, resolvedKey);
|
|
28053
28079
|
writeFileSync(MEMORY_FILE, decrypted);
|
|
28054
28080
|
saveConfig({ encrypted: false });
|
|
28055
28081
|
return {
|
package/package.json
CHANGED
package/src/bin/toon-memory.ts
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { dirname, join } from "path"
|
|
3
3
|
import { fileURLToPath } from "url"
|
|
4
|
-
import { spawn } from "child_process"
|
|
5
4
|
|
|
6
5
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
7
6
|
const args = process.argv.slice(2)
|
|
8
7
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
child.on("exit", (code) => process.exit(code ?? 0))
|
|
8
|
+
if (args[0] === "mcp") {
|
|
9
|
+
await import(join(__dirname, "..", "mcp", "server.js"))
|
|
10
|
+
} else {
|
|
11
|
+
process.argv = ["node", "toon-memory", ...args]
|
|
12
|
+
await import(join(__dirname, "..", "dist", "cli", "setup.js"))
|
|
13
|
+
}
|
package/src/cli/toon-memory.ts
CHANGED
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
import { dirname, join } from "path"
|
|
2
2
|
import { fileURLToPath } from "url"
|
|
3
|
-
import { spawn } from "child_process"
|
|
4
3
|
|
|
5
4
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
6
5
|
const args = process.argv.slice(2)
|
|
7
6
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
child.on("exit", (code) => process.exit(code ?? 0))
|
|
7
|
+
if (args[0] === "mcp") {
|
|
8
|
+
await import(join(__dirname, "..", "mcp", "server.js"))
|
|
9
|
+
} else {
|
|
10
|
+
process.argv = ["node", "toon-memory", ...args]
|
|
11
|
+
await import(join(__dirname, "..", "dist", "cli", "setup.js"))
|
|
12
|
+
}
|
package/src/mcp/server.ts
CHANGED
|
@@ -20,6 +20,9 @@ const ARCHIVE_FILE = join(MEMORY_DIR, "archive.toon")
|
|
|
20
20
|
/** Configuration file for encryption settings */
|
|
21
21
|
const CONFIG_FILE = join(MEMORY_DIR, "config.json")
|
|
22
22
|
|
|
23
|
+
/** Env file storing the encryption key (gitignored via .opencode/) */
|
|
24
|
+
const ENV_FILE = join(MEMORY_DIR, ".env")
|
|
25
|
+
|
|
23
26
|
/** Maximum active entries before auto-archive */
|
|
24
27
|
const MAX_ENTRIES = 100
|
|
25
28
|
|
|
@@ -33,8 +36,6 @@ const ALGORITHM = "aes-256-gcm"
|
|
|
33
36
|
interface MemoryConfig {
|
|
34
37
|
/** Whether encryption is enabled */
|
|
35
38
|
encrypted: boolean
|
|
36
|
-
/** Encryption key (hex-encoded, 64 chars for AES-256) */
|
|
37
|
-
key?: string
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
/**
|
|
@@ -71,6 +72,32 @@ function saveConfig(config: MemoryConfig): void {
|
|
|
71
72
|
writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2))
|
|
72
73
|
}
|
|
73
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Read the encryption key from .env file.
|
|
77
|
+
*
|
|
78
|
+
* @returns The key string, or undefined if not set
|
|
79
|
+
*/
|
|
80
|
+
function readEnvKey(): string | undefined {
|
|
81
|
+
try {
|
|
82
|
+
if (!existsSync(ENV_FILE)) return undefined
|
|
83
|
+
const content = readFileSync(ENV_FILE, "utf-8").trim()
|
|
84
|
+
const match = content.match(/^TOON_MEMORY_KEY=(.+)$/m)
|
|
85
|
+
return match ? match[1] : undefined
|
|
86
|
+
} catch {
|
|
87
|
+
return undefined
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Write the encryption key to .env file.
|
|
93
|
+
*
|
|
94
|
+
* @param key - Hex-encoded encryption key
|
|
95
|
+
*/
|
|
96
|
+
function writeEnvKey(key: string): void {
|
|
97
|
+
ensureMemoryDir()
|
|
98
|
+
writeFileSync(ENV_FILE, `TOON_MEMORY_KEY=${key}\n`)
|
|
99
|
+
}
|
|
100
|
+
|
|
74
101
|
/**
|
|
75
102
|
* Ensure memory directory exists, creating it if necessary.
|
|
76
103
|
*/
|
|
@@ -148,6 +175,7 @@ function generateKey(): string {
|
|
|
148
175
|
|
|
149
176
|
/**
|
|
150
177
|
* Read memory file, decrypting if encryption is enabled.
|
|
178
|
+
* Key is read from TOON_MEMORY_KEY env var.
|
|
151
179
|
*
|
|
152
180
|
* @returns Memory content as string (TOON format)
|
|
153
181
|
*/
|
|
@@ -156,9 +184,11 @@ function readMemory(): string {
|
|
|
156
184
|
const config = loadConfig()
|
|
157
185
|
const data = readFileSync(MEMORY_FILE, "utf-8")
|
|
158
186
|
|
|
159
|
-
if (config.encrypted
|
|
187
|
+
if (config.encrypted) {
|
|
188
|
+
const key = readEnvKey()
|
|
189
|
+
if (!key) return data
|
|
160
190
|
try {
|
|
161
|
-
return decrypt(data,
|
|
191
|
+
return decrypt(data, key)
|
|
162
192
|
} catch {
|
|
163
193
|
return data
|
|
164
194
|
}
|
|
@@ -169,6 +199,7 @@ function readMemory(): string {
|
|
|
169
199
|
|
|
170
200
|
/**
|
|
171
201
|
* Write content to memory file, encrypting if encryption is enabled.
|
|
202
|
+
* Key is read from TOON_MEMORY_KEY env var.
|
|
172
203
|
*
|
|
173
204
|
* @param content - Memory content to write (TOON format)
|
|
174
205
|
*/
|
|
@@ -176,12 +207,16 @@ function writeMemory(content: string): void {
|
|
|
176
207
|
ensureMemoryFile()
|
|
177
208
|
const config = loadConfig()
|
|
178
209
|
|
|
179
|
-
if (config.encrypted
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
210
|
+
if (config.encrypted) {
|
|
211
|
+
const key = readEnvKey()
|
|
212
|
+
if (key) {
|
|
213
|
+
const encrypted = encrypt(content, key)
|
|
214
|
+
writeFileSync(MEMORY_FILE, encrypted)
|
|
215
|
+
return
|
|
216
|
+
}
|
|
184
217
|
}
|
|
218
|
+
|
|
219
|
+
writeFileSync(MEMORY_FILE, content)
|
|
185
220
|
}
|
|
186
221
|
|
|
187
222
|
/**
|
|
@@ -648,7 +683,8 @@ server.registerTool(
|
|
|
648
683
|
const encrypted = encrypt(data, key)
|
|
649
684
|
writeFileSync(MEMORY_FILE, encrypted)
|
|
650
685
|
|
|
651
|
-
saveConfig({ encrypted: true
|
|
686
|
+
saveConfig({ encrypted: true })
|
|
687
|
+
writeEnvKey(key)
|
|
652
688
|
|
|
653
689
|
return {
|
|
654
690
|
content: [{
|
|
@@ -685,9 +721,14 @@ server.registerTool(
|
|
|
685
721
|
return { content: [{ type: "text" as const, text: "La encriptación no está habilitada" }] }
|
|
686
722
|
}
|
|
687
723
|
|
|
724
|
+
const resolvedKey = key || readEnvKey() || ""
|
|
725
|
+
if (!resolvedKey) {
|
|
726
|
+
return { content: [{ type: "text" as const, text: "❌ No hay clave. Pásala como argumento o la del archivo .env" }] }
|
|
727
|
+
}
|
|
728
|
+
|
|
688
729
|
try {
|
|
689
730
|
const data = readFileSync(MEMORY_FILE, "utf-8")
|
|
690
|
-
const decrypted = decrypt(data,
|
|
731
|
+
const decrypted = decrypt(data, resolvedKey)
|
|
691
732
|
|
|
692
733
|
writeFileSync(MEMORY_FILE, decrypted)
|
|
693
734
|
saveConfig({ encrypted: false })
|