toon-memory 1.6.6 → 1.6.8
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/install.ps1 +13 -0
- package/mcp/server.js +35 -15
- package/package.json +1 -1
- package/src/mcp/server.ts +52 -15
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,11 +27716,13 @@ 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 "";
|
|
27704
27722
|
try {
|
|
27705
|
-
return decrypt(data,
|
|
27723
|
+
return decrypt(data, key);
|
|
27706
27724
|
} catch {
|
|
27707
|
-
return
|
|
27725
|
+
return "";
|
|
27708
27726
|
}
|
|
27709
27727
|
}
|
|
27710
27728
|
return data;
|
|
@@ -27712,12 +27730,14 @@ function readMemory() {
|
|
|
27712
27730
|
function writeMemory(content) {
|
|
27713
27731
|
ensureMemoryFile();
|
|
27714
27732
|
const config2 = loadConfig();
|
|
27715
|
-
if (config2.encrypted
|
|
27716
|
-
const
|
|
27733
|
+
if (config2.encrypted) {
|
|
27734
|
+
const key = readEnvKey();
|
|
27735
|
+
if (!key) return;
|
|
27736
|
+
const encrypted = encrypt(content, key);
|
|
27717
27737
|
writeFileSync(MEMORY_FILE, encrypted);
|
|
27718
|
-
|
|
27719
|
-
writeFileSync(MEMORY_FILE, content);
|
|
27738
|
+
return;
|
|
27720
27739
|
}
|
|
27740
|
+
writeFileSync(MEMORY_FILE, content);
|
|
27721
27741
|
}
|
|
27722
27742
|
function generateId() {
|
|
27723
27743
|
return randomBytes(4).toString("hex");
|
|
@@ -28022,14 +28042,10 @@ server.registerTool(
|
|
|
28022
28042
|
const data = readFileSync(MEMORY_FILE, "utf-8");
|
|
28023
28043
|
const encrypted = encrypt(data, key);
|
|
28024
28044
|
writeFileSync(MEMORY_FILE, encrypted);
|
|
28025
|
-
saveConfig({ encrypted: true
|
|
28045
|
+
saveConfig({ encrypted: true });
|
|
28046
|
+
writeEnvKey(key);
|
|
28026
28047
|
return {
|
|
28027
|
-
content: [{
|
|
28028
|
-
type: "text",
|
|
28029
|
-
text: `\u{1F510} Encriptaci\xF3n habilitada
|
|
28030
|
-
\u26A0\uFE0F Guarda esta clave (no se puede recuperar):
|
|
28031
|
-
${key}`
|
|
28032
|
-
}]
|
|
28048
|
+
content: [{ type: "text", text: "\u{1F510} Encriptaci\xF3n habilitada" }]
|
|
28033
28049
|
};
|
|
28034
28050
|
}
|
|
28035
28051
|
);
|
|
@@ -28047,9 +28063,13 @@ server.registerTool(
|
|
|
28047
28063
|
if (!config2.encrypted) {
|
|
28048
28064
|
return { content: [{ type: "text", text: "La encriptaci\xF3n no est\xE1 habilitada" }] };
|
|
28049
28065
|
}
|
|
28066
|
+
const resolvedKey = key || readEnvKey() || "";
|
|
28067
|
+
if (!resolvedKey) {
|
|
28068
|
+
return { content: [{ type: "text", text: "\u274C No hay clave. P\xE1sala como argumento o la del archivo .env" }] };
|
|
28069
|
+
}
|
|
28050
28070
|
try {
|
|
28051
28071
|
const data = readFileSync(MEMORY_FILE, "utf-8");
|
|
28052
|
-
const decrypted = decrypt(data,
|
|
28072
|
+
const decrypted = decrypt(data, resolvedKey);
|
|
28053
28073
|
writeFileSync(MEMORY_FILE, decrypted);
|
|
28054
28074
|
saveConfig({ encrypted: false });
|
|
28055
28075
|
return {
|
package/package.json
CHANGED
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,11 +184,13 @@ 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 ""
|
|
160
190
|
try {
|
|
161
|
-
return decrypt(data,
|
|
191
|
+
return decrypt(data, key)
|
|
162
192
|
} catch {
|
|
163
|
-
return
|
|
193
|
+
return ""
|
|
164
194
|
}
|
|
165
195
|
}
|
|
166
196
|
|
|
@@ -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,15 @@ function writeMemory(content: string): void {
|
|
|
176
207
|
ensureMemoryFile()
|
|
177
208
|
const config = loadConfig()
|
|
178
209
|
|
|
179
|
-
if (config.encrypted
|
|
180
|
-
const
|
|
210
|
+
if (config.encrypted) {
|
|
211
|
+
const key = readEnvKey()
|
|
212
|
+
if (!key) return
|
|
213
|
+
const encrypted = encrypt(content, key)
|
|
181
214
|
writeFileSync(MEMORY_FILE, encrypted)
|
|
182
|
-
|
|
183
|
-
writeFileSync(MEMORY_FILE, content)
|
|
215
|
+
return
|
|
184
216
|
}
|
|
217
|
+
|
|
218
|
+
writeFileSync(MEMORY_FILE, content)
|
|
185
219
|
}
|
|
186
220
|
|
|
187
221
|
/**
|
|
@@ -648,13 +682,11 @@ server.registerTool(
|
|
|
648
682
|
const encrypted = encrypt(data, key)
|
|
649
683
|
writeFileSync(MEMORY_FILE, encrypted)
|
|
650
684
|
|
|
651
|
-
saveConfig({ encrypted: true
|
|
685
|
+
saveConfig({ encrypted: true })
|
|
686
|
+
writeEnvKey(key)
|
|
652
687
|
|
|
653
688
|
return {
|
|
654
|
-
content: [{
|
|
655
|
-
type: "text" as const,
|
|
656
|
-
text: `🔐 Encriptación habilitada\n⚠️ Guarda esta clave (no se puede recuperar):\n${key}`
|
|
657
|
-
}],
|
|
689
|
+
content: [{ type: "text" as const, text: "🔐 Encriptación habilitada" }],
|
|
658
690
|
}
|
|
659
691
|
}
|
|
660
692
|
)
|
|
@@ -685,9 +717,14 @@ server.registerTool(
|
|
|
685
717
|
return { content: [{ type: "text" as const, text: "La encriptación no está habilitada" }] }
|
|
686
718
|
}
|
|
687
719
|
|
|
720
|
+
const resolvedKey = key || readEnvKey() || ""
|
|
721
|
+
if (!resolvedKey) {
|
|
722
|
+
return { content: [{ type: "text" as const, text: "❌ No hay clave. Pásala como argumento o la del archivo .env" }] }
|
|
723
|
+
}
|
|
724
|
+
|
|
688
725
|
try {
|
|
689
726
|
const data = readFileSync(MEMORY_FILE, "utf-8")
|
|
690
|
-
const decrypted = decrypt(data,
|
|
727
|
+
const decrypted = decrypt(data, resolvedKey)
|
|
691
728
|
|
|
692
729
|
writeFileSync(MEMORY_FILE, decrypted)
|
|
693
730
|
saveConfig({ encrypted: false })
|