mindcache 1.0.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cloud/index.d.mts +45 -0
- package/dist/cloud/index.d.ts +45 -0
- package/dist/cloud/index.js +1313 -0
- package/dist/cloud/index.js.map +1 -0
- package/dist/cloud/index.mjs +1310 -0
- package/dist/cloud/index.mjs.map +1 -0
- package/dist/index-CFJtj3DL.d.mts +279 -0
- package/dist/index-CFJtj3DL.d.ts +279 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1303 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1299 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +36 -27
- package/LICENSE +0 -21
- package/README.md +0 -53
- package/lib/index.d.ts +0 -90
- package/lib/index.d.ts.map +0 -1
- package/lib/index.js +0 -1030
- package/lib/index.js.map +0 -1
|
@@ -0,0 +1,1313 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var zod = require('zod');
|
|
4
|
+
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __esm = (fn, res) => function __init() {
|
|
8
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
9
|
+
};
|
|
10
|
+
var __export = (target, all) => {
|
|
11
|
+
for (var name in all)
|
|
12
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// src/cloud/CloudAdapter.ts
|
|
16
|
+
var CloudAdapter_exports = {};
|
|
17
|
+
__export(CloudAdapter_exports, {
|
|
18
|
+
CloudAdapter: () => exports.CloudAdapter
|
|
19
|
+
});
|
|
20
|
+
var DEFAULT_BASE_URL, RECONNECT_DELAY, MAX_RECONNECT_DELAY; exports.CloudAdapter = void 0;
|
|
21
|
+
var init_CloudAdapter = __esm({
|
|
22
|
+
"src/cloud/CloudAdapter.ts"() {
|
|
23
|
+
DEFAULT_BASE_URL = "wss://api.mindcache.io";
|
|
24
|
+
RECONNECT_DELAY = 1e3;
|
|
25
|
+
MAX_RECONNECT_DELAY = 3e4;
|
|
26
|
+
exports.CloudAdapter = class {
|
|
27
|
+
constructor(config) {
|
|
28
|
+
this.config = config;
|
|
29
|
+
this.config.baseUrl = config.baseUrl || DEFAULT_BASE_URL;
|
|
30
|
+
}
|
|
31
|
+
ws = null;
|
|
32
|
+
queue = [];
|
|
33
|
+
mindcache = null;
|
|
34
|
+
unsubscribe = null;
|
|
35
|
+
reconnectAttempts = 0;
|
|
36
|
+
reconnectTimeout = null;
|
|
37
|
+
_state = "disconnected";
|
|
38
|
+
listeners = {};
|
|
39
|
+
token = null;
|
|
40
|
+
/**
|
|
41
|
+
* Set auth token (short-lived, from /api/ws-token)
|
|
42
|
+
* Call this before connect() or use setTokenProvider for auto-refresh
|
|
43
|
+
*/
|
|
44
|
+
setToken(token) {
|
|
45
|
+
this.token = token;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Set a function that returns a fresh token
|
|
49
|
+
* Used for automatic token refresh on reconnect
|
|
50
|
+
*/
|
|
51
|
+
setTokenProvider(provider) {
|
|
52
|
+
this.config.tokenProvider = provider;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Get current connection state
|
|
56
|
+
*/
|
|
57
|
+
get state() {
|
|
58
|
+
return this._state;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Attach to a MindCache instance and start syncing
|
|
62
|
+
*/
|
|
63
|
+
attach(mc) {
|
|
64
|
+
if (this.mindcache) {
|
|
65
|
+
this.detach();
|
|
66
|
+
}
|
|
67
|
+
this.mindcache = mc;
|
|
68
|
+
const listener = () => {
|
|
69
|
+
if (mc.isRemoteUpdate()) {
|
|
70
|
+
console.log("\u2601\uFE0F CloudAdapter: Skipping remote update");
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
console.log("\u2601\uFE0F CloudAdapter: Local change detected, syncing...");
|
|
74
|
+
this.syncLocalChanges();
|
|
75
|
+
};
|
|
76
|
+
mc.subscribeToAll(listener);
|
|
77
|
+
this.unsubscribe = () => mc.unsubscribeFromAll(listener);
|
|
78
|
+
console.log("\u2601\uFE0F CloudAdapter: Attached to MindCache instance");
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Detach from the MindCache instance
|
|
82
|
+
*/
|
|
83
|
+
detach() {
|
|
84
|
+
if (this.unsubscribe) {
|
|
85
|
+
this.unsubscribe();
|
|
86
|
+
this.unsubscribe = null;
|
|
87
|
+
}
|
|
88
|
+
this.mindcache = null;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Connect to the cloud service
|
|
92
|
+
*/
|
|
93
|
+
async connect() {
|
|
94
|
+
if (this._state === "connecting" || this._state === "connected") {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
this._state = "connecting";
|
|
98
|
+
try {
|
|
99
|
+
if (this.config.tokenProvider && !this.token) {
|
|
100
|
+
this.token = await this.config.tokenProvider();
|
|
101
|
+
}
|
|
102
|
+
let url = `${this.config.baseUrl}/sync/${this.config.instanceId}`;
|
|
103
|
+
if (this.token) {
|
|
104
|
+
url += `?token=${encodeURIComponent(this.token)}`;
|
|
105
|
+
this.token = null;
|
|
106
|
+
}
|
|
107
|
+
this.ws = new WebSocket(url);
|
|
108
|
+
this.setupWebSocket();
|
|
109
|
+
} catch (error) {
|
|
110
|
+
this._state = "error";
|
|
111
|
+
this.emit("error", error);
|
|
112
|
+
this.scheduleReconnect();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Disconnect from the cloud service
|
|
117
|
+
*/
|
|
118
|
+
disconnect() {
|
|
119
|
+
if (this.reconnectTimeout) {
|
|
120
|
+
clearTimeout(this.reconnectTimeout);
|
|
121
|
+
this.reconnectTimeout = null;
|
|
122
|
+
}
|
|
123
|
+
if (this.ws) {
|
|
124
|
+
this.ws.close();
|
|
125
|
+
this.ws = null;
|
|
126
|
+
}
|
|
127
|
+
this._state = "disconnected";
|
|
128
|
+
this.emit("disconnected");
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Push an operation to the cloud
|
|
132
|
+
*/
|
|
133
|
+
push(op) {
|
|
134
|
+
if (this.ws?.readyState === WebSocket.OPEN) {
|
|
135
|
+
this.ws.send(JSON.stringify(op));
|
|
136
|
+
} else {
|
|
137
|
+
this.queue.push(op);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Add event listener
|
|
142
|
+
*/
|
|
143
|
+
on(event, listener) {
|
|
144
|
+
if (!this.listeners[event]) {
|
|
145
|
+
this.listeners[event] = [];
|
|
146
|
+
}
|
|
147
|
+
this.listeners[event].push(listener);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Remove event listener
|
|
151
|
+
*/
|
|
152
|
+
off(event, listener) {
|
|
153
|
+
if (this.listeners[event]) {
|
|
154
|
+
this.listeners[event] = this.listeners[event].filter((l) => l !== listener);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
emit(event, ...args) {
|
|
158
|
+
if (this.listeners[event]) {
|
|
159
|
+
this.listeners[event].forEach((listener) => listener(...args));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
setupWebSocket() {
|
|
163
|
+
if (!this.ws) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
this.ws.onopen = () => {
|
|
167
|
+
if (this.config.apiKey) {
|
|
168
|
+
this.ws.send(JSON.stringify({
|
|
169
|
+
type: "auth",
|
|
170
|
+
apiKey: this.config.apiKey
|
|
171
|
+
}));
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
this.ws.onmessage = (event) => {
|
|
175
|
+
try {
|
|
176
|
+
const msg = JSON.parse(event.data);
|
|
177
|
+
this.handleMessage(msg);
|
|
178
|
+
} catch (error) {
|
|
179
|
+
console.error("MindCache Cloud: Failed to parse message:", error);
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
this.ws.onclose = () => {
|
|
183
|
+
this._state = "disconnected";
|
|
184
|
+
this.emit("disconnected");
|
|
185
|
+
this.scheduleReconnect();
|
|
186
|
+
};
|
|
187
|
+
this.ws.onerror = (error) => {
|
|
188
|
+
this._state = "error";
|
|
189
|
+
this.emit("error", new Error("WebSocket error"));
|
|
190
|
+
console.error("MindCache Cloud: WebSocket error:", error);
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
handleMessage(msg) {
|
|
194
|
+
switch (msg.type) {
|
|
195
|
+
case "auth_success":
|
|
196
|
+
this._state = "connected";
|
|
197
|
+
this.reconnectAttempts = 0;
|
|
198
|
+
this.emit("connected");
|
|
199
|
+
this.flushQueue();
|
|
200
|
+
break;
|
|
201
|
+
case "auth_error":
|
|
202
|
+
this._state = "error";
|
|
203
|
+
this.emit("error", new Error(msg.error));
|
|
204
|
+
this.disconnect();
|
|
205
|
+
break;
|
|
206
|
+
case "sync":
|
|
207
|
+
if (this.mindcache && msg.data) {
|
|
208
|
+
Object.entries(msg.data).forEach(([key, entry]) => {
|
|
209
|
+
const { value, attributes } = entry;
|
|
210
|
+
this.mindcache._setFromRemote(key, value, attributes);
|
|
211
|
+
});
|
|
212
|
+
this.emit("synced");
|
|
213
|
+
}
|
|
214
|
+
break;
|
|
215
|
+
case "set":
|
|
216
|
+
if (this.mindcache) {
|
|
217
|
+
this.mindcache._setFromRemote(msg.key, msg.value, msg.attributes);
|
|
218
|
+
}
|
|
219
|
+
break;
|
|
220
|
+
case "key_updated":
|
|
221
|
+
if (this.mindcache) {
|
|
222
|
+
this.mindcache._setFromRemote(msg.key, msg.value, msg.attributes);
|
|
223
|
+
}
|
|
224
|
+
break;
|
|
225
|
+
case "delete":
|
|
226
|
+
if (this.mindcache) {
|
|
227
|
+
this.mindcache._deleteFromRemote(msg.key);
|
|
228
|
+
}
|
|
229
|
+
break;
|
|
230
|
+
case "key_deleted":
|
|
231
|
+
if (this.mindcache) {
|
|
232
|
+
this.mindcache._deleteFromRemote(msg.key);
|
|
233
|
+
}
|
|
234
|
+
break;
|
|
235
|
+
case "clear":
|
|
236
|
+
if (this.mindcache) {
|
|
237
|
+
this.mindcache._clearFromRemote();
|
|
238
|
+
}
|
|
239
|
+
break;
|
|
240
|
+
case "cleared":
|
|
241
|
+
if (this.mindcache) {
|
|
242
|
+
this.mindcache._clearFromRemote();
|
|
243
|
+
}
|
|
244
|
+
break;
|
|
245
|
+
case "error":
|
|
246
|
+
this.emit("error", new Error(msg.error));
|
|
247
|
+
break;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
flushQueue() {
|
|
251
|
+
while (this.queue.length > 0 && this.ws?.readyState === WebSocket.OPEN) {
|
|
252
|
+
const op = this.queue.shift();
|
|
253
|
+
this.ws.send(JSON.stringify(op));
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
scheduleReconnect() {
|
|
257
|
+
if (this.reconnectTimeout) {
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
const delay = Math.min(
|
|
261
|
+
RECONNECT_DELAY * Math.pow(2, this.reconnectAttempts),
|
|
262
|
+
MAX_RECONNECT_DELAY
|
|
263
|
+
);
|
|
264
|
+
this.reconnectTimeout = setTimeout(async () => {
|
|
265
|
+
this.reconnectTimeout = null;
|
|
266
|
+
this.reconnectAttempts++;
|
|
267
|
+
if (this.config.tokenProvider) {
|
|
268
|
+
try {
|
|
269
|
+
this.token = await this.config.tokenProvider();
|
|
270
|
+
} catch (error) {
|
|
271
|
+
console.error("MindCache Cloud: Failed to get token for reconnect:", error);
|
|
272
|
+
this.emit("error", error);
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
this.connect();
|
|
277
|
+
}, delay);
|
|
278
|
+
}
|
|
279
|
+
syncLocalChanges() {
|
|
280
|
+
if (!this.mindcache) {
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
const entries = this.mindcache.serialize();
|
|
284
|
+
console.log("\u2601\uFE0F CloudAdapter: Syncing local changes:", Object.keys(entries));
|
|
285
|
+
Object.entries(entries).forEach(([key, entry]) => {
|
|
286
|
+
console.log("\u2601\uFE0F CloudAdapter: Pushing key:", key, "=", entry.value);
|
|
287
|
+
this.push({
|
|
288
|
+
type: "set",
|
|
289
|
+
key,
|
|
290
|
+
value: entry.value,
|
|
291
|
+
attributes: entry.attributes,
|
|
292
|
+
timestamp: Date.now()
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
// src/core/types.ts
|
|
301
|
+
var DEFAULT_KEY_ATTRIBUTES = {
|
|
302
|
+
readonly: false,
|
|
303
|
+
visible: true,
|
|
304
|
+
hardcoded: false,
|
|
305
|
+
template: false,
|
|
306
|
+
type: "text",
|
|
307
|
+
tags: []
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
// src/core/MindCache.ts
|
|
311
|
+
var MindCache = class {
|
|
312
|
+
stm = {};
|
|
313
|
+
listeners = {};
|
|
314
|
+
globalListeners = [];
|
|
315
|
+
// Internal flag to prevent sync loops when receiving remote updates
|
|
316
|
+
_isRemoteUpdate = false;
|
|
317
|
+
// Cloud sync state
|
|
318
|
+
_cloudAdapter = null;
|
|
319
|
+
_connectionState = "disconnected";
|
|
320
|
+
_isLoaded = true;
|
|
321
|
+
// Default true for local mode
|
|
322
|
+
_cloudConfig = null;
|
|
323
|
+
constructor(options) {
|
|
324
|
+
if (options?.cloud) {
|
|
325
|
+
this._cloudConfig = options.cloud;
|
|
326
|
+
this._isLoaded = false;
|
|
327
|
+
this._connectionState = "disconnected";
|
|
328
|
+
this._initCloud();
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
async _initCloud() {
|
|
332
|
+
if (!this._cloudConfig) {
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
try {
|
|
336
|
+
const { CloudAdapter: CloudAdapter2 } = await Promise.resolve().then(() => (init_CloudAdapter(), CloudAdapter_exports));
|
|
337
|
+
const configUrl = this._cloudConfig.baseUrl || "https://api.mindcache.io";
|
|
338
|
+
const baseUrl = configUrl.replace("https://", "wss://").replace("http://", "ws://");
|
|
339
|
+
const adapter = new CloudAdapter2({
|
|
340
|
+
instanceId: this._cloudConfig.instanceId,
|
|
341
|
+
projectId: this._cloudConfig.projectId || "default",
|
|
342
|
+
baseUrl,
|
|
343
|
+
apiKey: this._cloudConfig.apiKey
|
|
344
|
+
});
|
|
345
|
+
if (this._cloudConfig.tokenEndpoint) {
|
|
346
|
+
const tokenEndpoint = this._cloudConfig.tokenEndpoint;
|
|
347
|
+
const instanceId = this._cloudConfig.instanceId;
|
|
348
|
+
let resolvedBaseUrl;
|
|
349
|
+
if (tokenEndpoint.startsWith("http://") || tokenEndpoint.startsWith("https://")) {
|
|
350
|
+
resolvedBaseUrl = tokenEndpoint;
|
|
351
|
+
} else if (typeof window !== "undefined" && window.location?.origin) {
|
|
352
|
+
resolvedBaseUrl = `${window.location.origin}${tokenEndpoint.startsWith("/") ? "" : "/"}${tokenEndpoint}`;
|
|
353
|
+
} else {
|
|
354
|
+
console.warn("MindCache: Cannot resolve tokenEndpoint to absolute URL - window.location not available");
|
|
355
|
+
resolvedBaseUrl = tokenEndpoint;
|
|
356
|
+
}
|
|
357
|
+
adapter.setTokenProvider(async () => {
|
|
358
|
+
const url = resolvedBaseUrl.includes("?") ? `${resolvedBaseUrl}&instanceId=${instanceId}` : `${resolvedBaseUrl}?instanceId=${instanceId}`;
|
|
359
|
+
const response = await fetch(url);
|
|
360
|
+
if (!response.ok) {
|
|
361
|
+
const error = await response.json().catch(() => ({ error: "Failed to get token" }));
|
|
362
|
+
throw new Error(error.error || "Failed to get token");
|
|
363
|
+
}
|
|
364
|
+
const data = await response.json();
|
|
365
|
+
return data.token;
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
adapter.on("connected", () => {
|
|
369
|
+
this._connectionState = "connected";
|
|
370
|
+
this.notifyGlobalListeners();
|
|
371
|
+
});
|
|
372
|
+
adapter.on("disconnected", () => {
|
|
373
|
+
this._connectionState = "disconnected";
|
|
374
|
+
this.notifyGlobalListeners();
|
|
375
|
+
});
|
|
376
|
+
adapter.on("error", () => {
|
|
377
|
+
this._connectionState = "error";
|
|
378
|
+
this.notifyGlobalListeners();
|
|
379
|
+
});
|
|
380
|
+
adapter.on("synced", () => {
|
|
381
|
+
this._isLoaded = true;
|
|
382
|
+
this.notifyGlobalListeners();
|
|
383
|
+
});
|
|
384
|
+
adapter.attach(this);
|
|
385
|
+
this._cloudAdapter = adapter;
|
|
386
|
+
this._connectionState = "connecting";
|
|
387
|
+
adapter.connect();
|
|
388
|
+
} catch (error) {
|
|
389
|
+
console.error("MindCache: Failed to initialize cloud connection:", error);
|
|
390
|
+
this._connectionState = "error";
|
|
391
|
+
this._isLoaded = true;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
/**
|
|
395
|
+
* Get the current cloud connection state
|
|
396
|
+
*/
|
|
397
|
+
get connectionState() {
|
|
398
|
+
return this._connectionState;
|
|
399
|
+
}
|
|
400
|
+
/**
|
|
401
|
+
* Check if data is loaded (true for local, true after sync for cloud)
|
|
402
|
+
*/
|
|
403
|
+
get isLoaded() {
|
|
404
|
+
return this._isLoaded;
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Check if this instance is connected to cloud
|
|
408
|
+
*/
|
|
409
|
+
get isCloud() {
|
|
410
|
+
return this._cloudConfig !== null;
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Disconnect from cloud (if connected)
|
|
414
|
+
*/
|
|
415
|
+
disconnect() {
|
|
416
|
+
if (this._cloudAdapter) {
|
|
417
|
+
this._cloudAdapter.disconnect();
|
|
418
|
+
this._cloudAdapter.detach();
|
|
419
|
+
this._cloudAdapter = null;
|
|
420
|
+
this._connectionState = "disconnected";
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
// Helper method to encode file to base64
|
|
424
|
+
encodeFileToBase64(file) {
|
|
425
|
+
return new Promise((resolve, reject) => {
|
|
426
|
+
if (typeof FileReader !== "undefined") {
|
|
427
|
+
const reader = new FileReader();
|
|
428
|
+
reader.onload = () => {
|
|
429
|
+
const result = reader.result;
|
|
430
|
+
const base64Data = result.split(",")[1];
|
|
431
|
+
resolve(base64Data);
|
|
432
|
+
};
|
|
433
|
+
reader.onerror = reject;
|
|
434
|
+
reader.readAsDataURL(file);
|
|
435
|
+
} else {
|
|
436
|
+
reject(new Error("FileReader not available in Node.js environment. Use set_base64() method instead."));
|
|
437
|
+
}
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
// Helper method to create data URL from base64 and content type
|
|
441
|
+
createDataUrl(base64Data, contentType) {
|
|
442
|
+
return `data:${contentType};base64,${base64Data}`;
|
|
443
|
+
}
|
|
444
|
+
// Helper method to validate content type for different STM types
|
|
445
|
+
validateContentType(type, contentType) {
|
|
446
|
+
if (type === "text" || type === "json") {
|
|
447
|
+
return true;
|
|
448
|
+
}
|
|
449
|
+
if (!contentType) {
|
|
450
|
+
return false;
|
|
451
|
+
}
|
|
452
|
+
if (type === "image") {
|
|
453
|
+
return contentType.startsWith("image/");
|
|
454
|
+
}
|
|
455
|
+
if (type === "file") {
|
|
456
|
+
return true;
|
|
457
|
+
}
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
/** @deprecated Use get_value instead */
|
|
461
|
+
get(key) {
|
|
462
|
+
return this.get_value(key);
|
|
463
|
+
}
|
|
464
|
+
// Get a value from the STM with template processing if enabled
|
|
465
|
+
get_value(key, _processingStack) {
|
|
466
|
+
if (key === "$date") {
|
|
467
|
+
const today = /* @__PURE__ */ new Date();
|
|
468
|
+
return today.toISOString().split("T")[0];
|
|
469
|
+
}
|
|
470
|
+
if (key === "$time") {
|
|
471
|
+
const now = /* @__PURE__ */ new Date();
|
|
472
|
+
return now.toTimeString().split(" ")[0];
|
|
473
|
+
}
|
|
474
|
+
const entry = this.stm[key];
|
|
475
|
+
if (!entry) {
|
|
476
|
+
return void 0;
|
|
477
|
+
}
|
|
478
|
+
if (entry.attributes.template) {
|
|
479
|
+
const processingStack = _processingStack || /* @__PURE__ */ new Set();
|
|
480
|
+
if (processingStack.has(key)) {
|
|
481
|
+
return entry.value;
|
|
482
|
+
}
|
|
483
|
+
processingStack.add(key);
|
|
484
|
+
const result = this.injectSTM(entry.value, processingStack);
|
|
485
|
+
processingStack.delete(key);
|
|
486
|
+
return result;
|
|
487
|
+
}
|
|
488
|
+
return entry.value;
|
|
489
|
+
}
|
|
490
|
+
// Get attributes for a key
|
|
491
|
+
get_attributes(key) {
|
|
492
|
+
if (key === "$date" || key === "$time") {
|
|
493
|
+
return {
|
|
494
|
+
readonly: true,
|
|
495
|
+
visible: true,
|
|
496
|
+
hardcoded: true,
|
|
497
|
+
template: false,
|
|
498
|
+
type: "text",
|
|
499
|
+
tags: []
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
const entry = this.stm[key];
|
|
503
|
+
return entry ? entry.attributes : void 0;
|
|
504
|
+
}
|
|
505
|
+
// Set a value in the STM with default attributes
|
|
506
|
+
set_value(key, value, attributes) {
|
|
507
|
+
if (key === "$date" || key === "$time") {
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
const existingEntry = this.stm[key];
|
|
511
|
+
const baseAttributes = existingEntry ? existingEntry.attributes : { ...DEFAULT_KEY_ATTRIBUTES };
|
|
512
|
+
const finalAttributes = attributes ? { ...baseAttributes, ...attributes } : baseAttributes;
|
|
513
|
+
if (finalAttributes.hardcoded) {
|
|
514
|
+
finalAttributes.readonly = true;
|
|
515
|
+
finalAttributes.template = false;
|
|
516
|
+
}
|
|
517
|
+
this.stm[key] = {
|
|
518
|
+
value,
|
|
519
|
+
attributes: finalAttributes
|
|
520
|
+
};
|
|
521
|
+
if (this.listeners[key]) {
|
|
522
|
+
this.listeners[key].forEach((listener) => listener());
|
|
523
|
+
}
|
|
524
|
+
this.notifyGlobalListeners();
|
|
525
|
+
}
|
|
526
|
+
// Internal method for setting values from remote (cloud sync)
|
|
527
|
+
// This doesn't trigger the global listener to prevent sync loops
|
|
528
|
+
_setFromRemote(key, value, attributes) {
|
|
529
|
+
if (key === "$date" || key === "$time") {
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
this._isRemoteUpdate = true;
|
|
533
|
+
this.stm[key] = {
|
|
534
|
+
value,
|
|
535
|
+
attributes
|
|
536
|
+
};
|
|
537
|
+
if (this.listeners[key]) {
|
|
538
|
+
this.listeners[key].forEach((listener) => listener());
|
|
539
|
+
}
|
|
540
|
+
this.notifyGlobalListeners();
|
|
541
|
+
this._isRemoteUpdate = false;
|
|
542
|
+
}
|
|
543
|
+
// Check if current update is from remote
|
|
544
|
+
isRemoteUpdate() {
|
|
545
|
+
return this._isRemoteUpdate;
|
|
546
|
+
}
|
|
547
|
+
// Internal method for deleting from remote (cloud sync)
|
|
548
|
+
_deleteFromRemote(key) {
|
|
549
|
+
if (key === "$date" || key === "$time") {
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
this._isRemoteUpdate = true;
|
|
553
|
+
if (key in this.stm) {
|
|
554
|
+
delete this.stm[key];
|
|
555
|
+
if (this.listeners[key]) {
|
|
556
|
+
this.listeners[key].forEach((listener) => listener());
|
|
557
|
+
}
|
|
558
|
+
this.notifyGlobalListeners();
|
|
559
|
+
}
|
|
560
|
+
this._isRemoteUpdate = false;
|
|
561
|
+
}
|
|
562
|
+
// Internal method for clearing from remote (cloud sync)
|
|
563
|
+
_clearFromRemote() {
|
|
564
|
+
this._isRemoteUpdate = true;
|
|
565
|
+
this.stm = {};
|
|
566
|
+
this.notifyGlobalListeners();
|
|
567
|
+
this._isRemoteUpdate = false;
|
|
568
|
+
}
|
|
569
|
+
// Set attributes for an existing key
|
|
570
|
+
set_attributes(key, attributes) {
|
|
571
|
+
if (key === "$date" || key === "$time") {
|
|
572
|
+
return false;
|
|
573
|
+
}
|
|
574
|
+
const entry = this.stm[key];
|
|
575
|
+
if (!entry) {
|
|
576
|
+
return false;
|
|
577
|
+
}
|
|
578
|
+
const { hardcoded: _hardcoded, ...allowedAttributes } = attributes;
|
|
579
|
+
entry.attributes = { ...entry.attributes, ...allowedAttributes };
|
|
580
|
+
if (entry.attributes.hardcoded) {
|
|
581
|
+
entry.attributes.readonly = true;
|
|
582
|
+
entry.attributes.template = false;
|
|
583
|
+
}
|
|
584
|
+
this.notifyGlobalListeners();
|
|
585
|
+
return true;
|
|
586
|
+
}
|
|
587
|
+
set(key, value) {
|
|
588
|
+
this.set_value(key, value);
|
|
589
|
+
}
|
|
590
|
+
async set_file(key, file, attributes) {
|
|
591
|
+
const base64Data = await this.encodeFileToBase64(file);
|
|
592
|
+
const contentType = file.type;
|
|
593
|
+
const fileAttributes = {
|
|
594
|
+
type: contentType.startsWith("image/") ? "image" : "file",
|
|
595
|
+
contentType,
|
|
596
|
+
...attributes
|
|
597
|
+
};
|
|
598
|
+
this.set_value(key, base64Data, fileAttributes);
|
|
599
|
+
}
|
|
600
|
+
set_base64(key, base64Data, contentType, type = "file", attributes) {
|
|
601
|
+
if (!this.validateContentType(type, contentType)) {
|
|
602
|
+
throw new Error(`Invalid content type ${contentType} for type ${type}`);
|
|
603
|
+
}
|
|
604
|
+
const fileAttributes = {
|
|
605
|
+
type,
|
|
606
|
+
contentType,
|
|
607
|
+
...attributes
|
|
608
|
+
};
|
|
609
|
+
this.set_value(key, base64Data, fileAttributes);
|
|
610
|
+
}
|
|
611
|
+
add_image(key, base64Data, contentType = "image/jpeg", attributes) {
|
|
612
|
+
if (!contentType.startsWith("image/")) {
|
|
613
|
+
throw new Error(`Invalid image content type: ${contentType}. Must start with 'image/'`);
|
|
614
|
+
}
|
|
615
|
+
this.set_base64(key, base64Data, contentType, "image", attributes);
|
|
616
|
+
this.set_attributes(key, {
|
|
617
|
+
type: "image",
|
|
618
|
+
contentType
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
get_data_url(key) {
|
|
622
|
+
const entry = this.stm[key];
|
|
623
|
+
if (!entry || entry.attributes.type !== "image" && entry.attributes.type !== "file") {
|
|
624
|
+
return void 0;
|
|
625
|
+
}
|
|
626
|
+
if (!entry.attributes.contentType) {
|
|
627
|
+
return void 0;
|
|
628
|
+
}
|
|
629
|
+
return this.createDataUrl(entry.value, entry.attributes.contentType);
|
|
630
|
+
}
|
|
631
|
+
get_base64(key) {
|
|
632
|
+
const entry = this.stm[key];
|
|
633
|
+
if (!entry || entry.attributes.type !== "image" && entry.attributes.type !== "file") {
|
|
634
|
+
return void 0;
|
|
635
|
+
}
|
|
636
|
+
return entry.value;
|
|
637
|
+
}
|
|
638
|
+
has(key) {
|
|
639
|
+
if (key === "$date" || key === "$time") {
|
|
640
|
+
return true;
|
|
641
|
+
}
|
|
642
|
+
return key in this.stm;
|
|
643
|
+
}
|
|
644
|
+
delete(key) {
|
|
645
|
+
if (key === "$date" || key === "$time") {
|
|
646
|
+
return false;
|
|
647
|
+
}
|
|
648
|
+
if (!(key in this.stm)) {
|
|
649
|
+
return false;
|
|
650
|
+
}
|
|
651
|
+
const deleted = delete this.stm[key];
|
|
652
|
+
if (deleted) {
|
|
653
|
+
this.notifyGlobalListeners();
|
|
654
|
+
if (this.listeners[key]) {
|
|
655
|
+
this.listeners[key].forEach((listener) => listener());
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
return deleted;
|
|
659
|
+
}
|
|
660
|
+
clear() {
|
|
661
|
+
this.stm = {};
|
|
662
|
+
this.notifyGlobalListeners();
|
|
663
|
+
}
|
|
664
|
+
keys() {
|
|
665
|
+
return [...Object.keys(this.stm), "$date", "$time"];
|
|
666
|
+
}
|
|
667
|
+
values() {
|
|
668
|
+
const now = /* @__PURE__ */ new Date();
|
|
669
|
+
const stmValues = Object.values(this.stm).map((entry) => entry.value);
|
|
670
|
+
return [
|
|
671
|
+
...stmValues,
|
|
672
|
+
now.toISOString().split("T")[0],
|
|
673
|
+
now.toTimeString().split(" ")[0]
|
|
674
|
+
];
|
|
675
|
+
}
|
|
676
|
+
entries() {
|
|
677
|
+
const now = /* @__PURE__ */ new Date();
|
|
678
|
+
const stmEntries = Object.entries(this.stm).map(
|
|
679
|
+
([key, entry]) => [key, entry.value]
|
|
680
|
+
);
|
|
681
|
+
return [
|
|
682
|
+
...stmEntries,
|
|
683
|
+
["$date", now.toISOString().split("T")[0]],
|
|
684
|
+
["$time", now.toTimeString().split(" ")[0]]
|
|
685
|
+
];
|
|
686
|
+
}
|
|
687
|
+
size() {
|
|
688
|
+
return Object.keys(this.stm).length + 2;
|
|
689
|
+
}
|
|
690
|
+
getAll() {
|
|
691
|
+
const now = /* @__PURE__ */ new Date();
|
|
692
|
+
const result = {};
|
|
693
|
+
Object.entries(this.stm).forEach(([key, entry]) => {
|
|
694
|
+
result[key] = entry.value;
|
|
695
|
+
});
|
|
696
|
+
result["$date"] = now.toISOString().split("T")[0];
|
|
697
|
+
result["$time"] = now.toTimeString().split(" ")[0];
|
|
698
|
+
return result;
|
|
699
|
+
}
|
|
700
|
+
update(newValues) {
|
|
701
|
+
Object.entries(newValues).forEach(([key, value]) => {
|
|
702
|
+
if (key !== "$date" && key !== "$time") {
|
|
703
|
+
this.stm[key] = {
|
|
704
|
+
value,
|
|
705
|
+
attributes: { ...DEFAULT_KEY_ATTRIBUTES }
|
|
706
|
+
};
|
|
707
|
+
if (this.listeners[key]) {
|
|
708
|
+
this.listeners[key].forEach((listener) => listener());
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
});
|
|
712
|
+
this.notifyGlobalListeners();
|
|
713
|
+
}
|
|
714
|
+
subscribe(key, listener) {
|
|
715
|
+
if (!this.listeners[key]) {
|
|
716
|
+
this.listeners[key] = [];
|
|
717
|
+
}
|
|
718
|
+
this.listeners[key].push(listener);
|
|
719
|
+
}
|
|
720
|
+
unsubscribe(key, listener) {
|
|
721
|
+
if (this.listeners[key]) {
|
|
722
|
+
this.listeners[key] = this.listeners[key].filter((l) => l !== listener);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
subscribeToAll(listener) {
|
|
726
|
+
this.globalListeners.push(listener);
|
|
727
|
+
}
|
|
728
|
+
unsubscribeFromAll(listener) {
|
|
729
|
+
this.globalListeners = this.globalListeners.filter((l) => l !== listener);
|
|
730
|
+
}
|
|
731
|
+
notifyGlobalListeners() {
|
|
732
|
+
this.globalListeners.forEach((listener) => listener());
|
|
733
|
+
}
|
|
734
|
+
injectSTM(template, _processingStack) {
|
|
735
|
+
if (template === null || template === void 0) {
|
|
736
|
+
return String(template);
|
|
737
|
+
}
|
|
738
|
+
const templateStr = String(template);
|
|
739
|
+
const keys = templateStr.match(/\{\{([$\w]+)\}\}/g);
|
|
740
|
+
if (!keys) {
|
|
741
|
+
return templateStr;
|
|
742
|
+
}
|
|
743
|
+
const cleanKeys = keys.map((key) => key.replace(/[{}]/g, ""));
|
|
744
|
+
const inputValues = cleanKeys.reduce((acc, key) => {
|
|
745
|
+
if (key === "$date" || key === "$time") {
|
|
746
|
+
return {
|
|
747
|
+
...acc,
|
|
748
|
+
[key]: this.get_value(key, _processingStack)
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
const attributes = this.get_attributes(key);
|
|
752
|
+
if (_processingStack || attributes && attributes.visible) {
|
|
753
|
+
if (attributes && (attributes.type === "image" || attributes.type === "file")) {
|
|
754
|
+
return acc;
|
|
755
|
+
}
|
|
756
|
+
return {
|
|
757
|
+
...acc,
|
|
758
|
+
[key]: this.get_value(key, _processingStack)
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
return acc;
|
|
762
|
+
}, {});
|
|
763
|
+
return templateStr.replace(/\{\{([$\w]+)\}\}/g, (match, key) => {
|
|
764
|
+
if (inputValues[key] !== void 0) {
|
|
765
|
+
return inputValues[key];
|
|
766
|
+
}
|
|
767
|
+
const attributes = this.get_attributes(key);
|
|
768
|
+
if (attributes && (attributes.type === "image" || attributes.type === "file")) {
|
|
769
|
+
return match;
|
|
770
|
+
}
|
|
771
|
+
return "";
|
|
772
|
+
});
|
|
773
|
+
}
|
|
774
|
+
getSTM() {
|
|
775
|
+
const now = /* @__PURE__ */ new Date();
|
|
776
|
+
const entries = [];
|
|
777
|
+
Object.entries(this.stm).forEach(([key, entry]) => {
|
|
778
|
+
if (entry.attributes.visible) {
|
|
779
|
+
entries.push([key, this.get_value(key)]);
|
|
780
|
+
}
|
|
781
|
+
});
|
|
782
|
+
entries.push(["$date", now.toISOString().split("T")[0]]);
|
|
783
|
+
entries.push(["$time", now.toTimeString().split(" ")[0]]);
|
|
784
|
+
return entries.map(([key, value]) => `${key}: ${value}`).join(", ");
|
|
785
|
+
}
|
|
786
|
+
getSTMObject() {
|
|
787
|
+
return this.getAll();
|
|
788
|
+
}
|
|
789
|
+
getSTMForAPI() {
|
|
790
|
+
const now = /* @__PURE__ */ new Date();
|
|
791
|
+
const apiData = [];
|
|
792
|
+
Object.entries(this.stm).forEach(([key, entry]) => {
|
|
793
|
+
if (entry.attributes.visible) {
|
|
794
|
+
const processedValue = entry.attributes.template ? this.get_value(key) : entry.value;
|
|
795
|
+
apiData.push({
|
|
796
|
+
key,
|
|
797
|
+
value: processedValue,
|
|
798
|
+
type: entry.attributes.type,
|
|
799
|
+
contentType: entry.attributes.contentType
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
});
|
|
803
|
+
apiData.push({
|
|
804
|
+
key: "$date",
|
|
805
|
+
value: now.toISOString().split("T")[0],
|
|
806
|
+
type: "text"
|
|
807
|
+
});
|
|
808
|
+
apiData.push({
|
|
809
|
+
key: "$time",
|
|
810
|
+
value: now.toTimeString().split(" ")[0],
|
|
811
|
+
type: "text"
|
|
812
|
+
});
|
|
813
|
+
return apiData;
|
|
814
|
+
}
|
|
815
|
+
getVisibleImages() {
|
|
816
|
+
const imageParts = [];
|
|
817
|
+
Object.entries(this.stm).forEach(([key, entry]) => {
|
|
818
|
+
if (entry.attributes.visible && entry.attributes.type === "image" && entry.attributes.contentType) {
|
|
819
|
+
const dataUrl = this.createDataUrl(entry.value, entry.attributes.contentType);
|
|
820
|
+
imageParts.push({
|
|
821
|
+
type: "file",
|
|
822
|
+
mediaType: entry.attributes.contentType,
|
|
823
|
+
url: dataUrl,
|
|
824
|
+
filename: key
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
});
|
|
828
|
+
return imageParts;
|
|
829
|
+
}
|
|
830
|
+
toJSON() {
|
|
831
|
+
return JSON.stringify(this.serialize());
|
|
832
|
+
}
|
|
833
|
+
fromJSON(jsonString) {
|
|
834
|
+
try {
|
|
835
|
+
const data = JSON.parse(jsonString);
|
|
836
|
+
this.deserialize(data);
|
|
837
|
+
} catch (error) {
|
|
838
|
+
console.error("MindCache: Failed to deserialize JSON:", error);
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
serialize() {
|
|
842
|
+
const result = {};
|
|
843
|
+
Object.entries(this.stm).forEach(([key, entry]) => {
|
|
844
|
+
if (!entry.attributes.hardcoded) {
|
|
845
|
+
result[key] = {
|
|
846
|
+
value: entry.value,
|
|
847
|
+
attributes: { ...entry.attributes }
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
});
|
|
851
|
+
return result;
|
|
852
|
+
}
|
|
853
|
+
deserialize(data) {
|
|
854
|
+
if (typeof data === "object" && data !== null) {
|
|
855
|
+
this.clear();
|
|
856
|
+
Object.entries(data).forEach(([key, entry]) => {
|
|
857
|
+
if (entry && typeof entry === "object" && "value" in entry && "attributes" in entry) {
|
|
858
|
+
this.stm[key] = {
|
|
859
|
+
value: entry.value,
|
|
860
|
+
attributes: {
|
|
861
|
+
...entry.attributes,
|
|
862
|
+
tags: entry.attributes.tags || []
|
|
863
|
+
}
|
|
864
|
+
};
|
|
865
|
+
}
|
|
866
|
+
});
|
|
867
|
+
this.notifyGlobalListeners();
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
get_system_prompt() {
|
|
871
|
+
const now = /* @__PURE__ */ new Date();
|
|
872
|
+
const promptLines = [];
|
|
873
|
+
Object.entries(this.stm).forEach(([key, entry]) => {
|
|
874
|
+
if (entry.attributes.visible) {
|
|
875
|
+
if (entry.attributes.type === "image") {
|
|
876
|
+
promptLines.push(`image ${key} available`);
|
|
877
|
+
return;
|
|
878
|
+
}
|
|
879
|
+
if (entry.attributes.type === "file") {
|
|
880
|
+
if (entry.attributes.readonly) {
|
|
881
|
+
promptLines.push(`${key}: [${entry.attributes.type.toUpperCase()}] - ${entry.attributes.contentType || "unknown format"}`);
|
|
882
|
+
} else {
|
|
883
|
+
const sanitizedKey = key.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
884
|
+
promptLines.push(`${key}: [${entry.attributes.type.toUpperCase()}] - ${entry.attributes.contentType || "unknown format"}. You can update this ${entry.attributes.type} using the write_${sanitizedKey} tool.`);
|
|
885
|
+
}
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
const value = this.get_value(key);
|
|
889
|
+
const formattedValue = typeof value === "object" && value !== null ? JSON.stringify(value) : String(value);
|
|
890
|
+
if (entry.attributes.readonly) {
|
|
891
|
+
promptLines.push(`${key}: ${formattedValue}`);
|
|
892
|
+
} else {
|
|
893
|
+
const sanitizedKey = key.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
894
|
+
const toolInstruction = `You can rewrite "${key}" by using the write_${sanitizedKey} tool. This tool DOES NOT append \u2014 start your response with the old value (${formattedValue})`;
|
|
895
|
+
promptLines.push(`${key}: ${formattedValue}. ${toolInstruction}`);
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
});
|
|
899
|
+
promptLines.push(`$date: ${now.toISOString().split("T")[0]}`);
|
|
900
|
+
promptLines.push(`$time: ${now.toTimeString().split(" ")[0]}`);
|
|
901
|
+
return promptLines.join("\n");
|
|
902
|
+
}
|
|
903
|
+
findKeyFromToolName(toolName) {
|
|
904
|
+
if (!toolName.startsWith("write_")) {
|
|
905
|
+
return void 0;
|
|
906
|
+
}
|
|
907
|
+
const sanitizedKey = toolName.replace("write_", "");
|
|
908
|
+
const allKeys = Object.keys(this.stm);
|
|
909
|
+
return allKeys.find(
|
|
910
|
+
(k) => k.replace(/[^a-zA-Z0-9_-]/g, "_") === sanitizedKey
|
|
911
|
+
);
|
|
912
|
+
}
|
|
913
|
+
get_aisdk_tools() {
|
|
914
|
+
const tools = {};
|
|
915
|
+
const writableKeys = Object.entries(this.stm).filter(([, entry]) => !entry.attributes.readonly).map(([key]) => key);
|
|
916
|
+
writableKeys.forEach((key) => {
|
|
917
|
+
const sanitizedKey = key.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
918
|
+
const toolName = `write_${sanitizedKey}`;
|
|
919
|
+
const entry = this.stm[key];
|
|
920
|
+
const keyType = entry?.attributes.type || "text";
|
|
921
|
+
let inputSchema;
|
|
922
|
+
let description = `Write a value to the STM key: ${key}`;
|
|
923
|
+
if (keyType === "image" || keyType === "file") {
|
|
924
|
+
description += " (expects base64 encoded data)";
|
|
925
|
+
inputSchema = zod.z.object({
|
|
926
|
+
value: zod.z.string().describe(`Base64 encoded data for ${key}`),
|
|
927
|
+
contentType: zod.z.string().optional().describe(`MIME type for the ${keyType}`)
|
|
928
|
+
});
|
|
929
|
+
} else if (keyType === "json") {
|
|
930
|
+
description += " (expects JSON string)";
|
|
931
|
+
inputSchema = zod.z.object({
|
|
932
|
+
value: zod.z.string().describe(`JSON string value for ${key}`)
|
|
933
|
+
});
|
|
934
|
+
} else {
|
|
935
|
+
inputSchema = zod.z.object({
|
|
936
|
+
value: zod.z.string().describe(`The text value to write to ${key}`)
|
|
937
|
+
});
|
|
938
|
+
}
|
|
939
|
+
tools[toolName] = {
|
|
940
|
+
description,
|
|
941
|
+
inputSchema,
|
|
942
|
+
execute: async (input) => {
|
|
943
|
+
if (keyType === "image" || keyType === "file") {
|
|
944
|
+
if (input.contentType) {
|
|
945
|
+
this.set_base64(key, input.value, input.contentType, keyType);
|
|
946
|
+
} else {
|
|
947
|
+
const existingContentType = entry?.attributes.contentType;
|
|
948
|
+
if (existingContentType) {
|
|
949
|
+
this.set_base64(key, input.value, existingContentType, keyType);
|
|
950
|
+
} else {
|
|
951
|
+
throw new Error(`Content type required for ${keyType} data`);
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
} else {
|
|
955
|
+
this.set_value(key, input.value);
|
|
956
|
+
}
|
|
957
|
+
let resultMessage;
|
|
958
|
+
if (keyType === "image") {
|
|
959
|
+
resultMessage = `Successfully saved image to ${key}`;
|
|
960
|
+
} else if (keyType === "file") {
|
|
961
|
+
resultMessage = `Successfully saved file to ${key}`;
|
|
962
|
+
} else if (keyType === "json") {
|
|
963
|
+
resultMessage = `Successfully saved JSON data to ${key}`;
|
|
964
|
+
} else {
|
|
965
|
+
resultMessage = `Successfully wrote "${input.value}" to ${key}`;
|
|
966
|
+
}
|
|
967
|
+
return {
|
|
968
|
+
result: resultMessage,
|
|
969
|
+
key,
|
|
970
|
+
value: input.value,
|
|
971
|
+
type: keyType,
|
|
972
|
+
contentType: input.contentType,
|
|
973
|
+
sanitizedKey
|
|
974
|
+
};
|
|
975
|
+
}
|
|
976
|
+
};
|
|
977
|
+
});
|
|
978
|
+
if (writableKeys.length === 0) {
|
|
979
|
+
return {};
|
|
980
|
+
}
|
|
981
|
+
return tools;
|
|
982
|
+
}
|
|
983
|
+
executeToolCall(toolName, value) {
|
|
984
|
+
const originalKey = this.findKeyFromToolName(toolName);
|
|
985
|
+
if (!originalKey) {
|
|
986
|
+
return null;
|
|
987
|
+
}
|
|
988
|
+
const entry = this.stm[originalKey];
|
|
989
|
+
if (entry && entry.attributes.readonly) {
|
|
990
|
+
return null;
|
|
991
|
+
}
|
|
992
|
+
this.set_value(originalKey, value);
|
|
993
|
+
return {
|
|
994
|
+
result: `Successfully wrote "${value}" to ${originalKey}`,
|
|
995
|
+
key: originalKey,
|
|
996
|
+
value
|
|
997
|
+
};
|
|
998
|
+
}
|
|
999
|
+
addTag(key, tag) {
|
|
1000
|
+
if (key === "$date" || key === "$time") {
|
|
1001
|
+
return false;
|
|
1002
|
+
}
|
|
1003
|
+
const entry = this.stm[key];
|
|
1004
|
+
if (!entry) {
|
|
1005
|
+
return false;
|
|
1006
|
+
}
|
|
1007
|
+
if (!entry.attributes.tags) {
|
|
1008
|
+
entry.attributes.tags = [];
|
|
1009
|
+
}
|
|
1010
|
+
if (!entry.attributes.tags.includes(tag)) {
|
|
1011
|
+
entry.attributes.tags.push(tag);
|
|
1012
|
+
this.notifyGlobalListeners();
|
|
1013
|
+
return true;
|
|
1014
|
+
}
|
|
1015
|
+
return false;
|
|
1016
|
+
}
|
|
1017
|
+
removeTag(key, tag) {
|
|
1018
|
+
if (key === "$date" || key === "$time") {
|
|
1019
|
+
return false;
|
|
1020
|
+
}
|
|
1021
|
+
const entry = this.stm[key];
|
|
1022
|
+
if (!entry || !entry.attributes.tags) {
|
|
1023
|
+
return false;
|
|
1024
|
+
}
|
|
1025
|
+
const tagIndex = entry.attributes.tags.indexOf(tag);
|
|
1026
|
+
if (tagIndex > -1) {
|
|
1027
|
+
entry.attributes.tags.splice(tagIndex, 1);
|
|
1028
|
+
this.notifyGlobalListeners();
|
|
1029
|
+
return true;
|
|
1030
|
+
}
|
|
1031
|
+
return false;
|
|
1032
|
+
}
|
|
1033
|
+
getTags(key) {
|
|
1034
|
+
if (key === "$date" || key === "$time") {
|
|
1035
|
+
return [];
|
|
1036
|
+
}
|
|
1037
|
+
const entry = this.stm[key];
|
|
1038
|
+
return entry?.attributes.tags || [];
|
|
1039
|
+
}
|
|
1040
|
+
getAllTags() {
|
|
1041
|
+
const allTags = /* @__PURE__ */ new Set();
|
|
1042
|
+
Object.values(this.stm).forEach((entry) => {
|
|
1043
|
+
if (entry.attributes.tags) {
|
|
1044
|
+
entry.attributes.tags.forEach((tag) => allTags.add(tag));
|
|
1045
|
+
}
|
|
1046
|
+
});
|
|
1047
|
+
return Array.from(allTags);
|
|
1048
|
+
}
|
|
1049
|
+
hasTag(key, tag) {
|
|
1050
|
+
if (key === "$date" || key === "$time") {
|
|
1051
|
+
return false;
|
|
1052
|
+
}
|
|
1053
|
+
const entry = this.stm[key];
|
|
1054
|
+
return entry?.attributes.tags?.includes(tag) || false;
|
|
1055
|
+
}
|
|
1056
|
+
getTagged(tag) {
|
|
1057
|
+
const entries = [];
|
|
1058
|
+
Object.entries(this.stm).forEach(([key, entry]) => {
|
|
1059
|
+
if (entry.attributes.tags?.includes(tag)) {
|
|
1060
|
+
entries.push([key, this.get_value(key)]);
|
|
1061
|
+
}
|
|
1062
|
+
});
|
|
1063
|
+
return entries.map(([key, value]) => `${key}: ${value}`).join(", ");
|
|
1064
|
+
}
|
|
1065
|
+
toMarkdown() {
|
|
1066
|
+
const now = /* @__PURE__ */ new Date();
|
|
1067
|
+
const lines = [];
|
|
1068
|
+
const appendixEntries = [];
|
|
1069
|
+
let appendixCounter = 0;
|
|
1070
|
+
lines.push("# MindCache STM Export");
|
|
1071
|
+
lines.push("");
|
|
1072
|
+
lines.push(`Export Date: ${now.toISOString().split("T")[0]}`);
|
|
1073
|
+
lines.push("");
|
|
1074
|
+
lines.push("---");
|
|
1075
|
+
lines.push("");
|
|
1076
|
+
lines.push("## STM Entries");
|
|
1077
|
+
lines.push("");
|
|
1078
|
+
Object.entries(this.stm).forEach(([key, entry]) => {
|
|
1079
|
+
if (entry.attributes.hardcoded) {
|
|
1080
|
+
return;
|
|
1081
|
+
}
|
|
1082
|
+
lines.push(`### ${key}`);
|
|
1083
|
+
const entryType = entry.attributes.type && entry.attributes.type !== "undefined" ? entry.attributes.type : "text";
|
|
1084
|
+
lines.push(`- **Type**: \`${entryType}\``);
|
|
1085
|
+
lines.push(`- **Readonly**: \`${entry.attributes.readonly}\``);
|
|
1086
|
+
lines.push(`- **Visible**: \`${entry.attributes.visible}\``);
|
|
1087
|
+
lines.push(`- **Template**: \`${entry.attributes.template}\``);
|
|
1088
|
+
if (entry.attributes.tags && entry.attributes.tags.length > 0) {
|
|
1089
|
+
lines.push(`- **Tags**: \`${entry.attributes.tags.join("`, `")}\``);
|
|
1090
|
+
}
|
|
1091
|
+
if (entry.attributes.contentType) {
|
|
1092
|
+
lines.push(`- **Content Type**: \`${entry.attributes.contentType}\``);
|
|
1093
|
+
}
|
|
1094
|
+
if (entryType === "image" || entryType === "file") {
|
|
1095
|
+
const label = String.fromCharCode(65 + appendixCounter);
|
|
1096
|
+
appendixCounter++;
|
|
1097
|
+
lines.push(`- **Value**: [See Appendix ${label}]`);
|
|
1098
|
+
appendixEntries.push({
|
|
1099
|
+
key,
|
|
1100
|
+
type: entryType,
|
|
1101
|
+
contentType: entry.attributes.contentType || "application/octet-stream",
|
|
1102
|
+
base64: entry.value,
|
|
1103
|
+
label
|
|
1104
|
+
});
|
|
1105
|
+
} else if (entryType === "json") {
|
|
1106
|
+
lines.push("- **Value**:");
|
|
1107
|
+
lines.push("```json");
|
|
1108
|
+
try {
|
|
1109
|
+
const jsonValue = typeof entry.value === "string" ? entry.value : JSON.stringify(entry.value, null, 2);
|
|
1110
|
+
lines.push(jsonValue);
|
|
1111
|
+
} catch {
|
|
1112
|
+
lines.push(String(entry.value));
|
|
1113
|
+
}
|
|
1114
|
+
lines.push("```");
|
|
1115
|
+
} else {
|
|
1116
|
+
const valueStr = String(entry.value);
|
|
1117
|
+
lines.push("- **Value**:");
|
|
1118
|
+
lines.push("```");
|
|
1119
|
+
lines.push(valueStr);
|
|
1120
|
+
lines.push("```");
|
|
1121
|
+
}
|
|
1122
|
+
lines.push("");
|
|
1123
|
+
lines.push("---");
|
|
1124
|
+
lines.push("");
|
|
1125
|
+
});
|
|
1126
|
+
if (appendixEntries.length > 0) {
|
|
1127
|
+
lines.push("## Appendix: Binary Data");
|
|
1128
|
+
lines.push("");
|
|
1129
|
+
appendixEntries.forEach(({ key, contentType, base64, label }) => {
|
|
1130
|
+
lines.push(`### Appendix ${label}: ${key}`);
|
|
1131
|
+
lines.push(`**Type**: ${contentType}`);
|
|
1132
|
+
lines.push("");
|
|
1133
|
+
lines.push("```");
|
|
1134
|
+
lines.push(base64);
|
|
1135
|
+
lines.push("```");
|
|
1136
|
+
lines.push("");
|
|
1137
|
+
lines.push("---");
|
|
1138
|
+
lines.push("");
|
|
1139
|
+
});
|
|
1140
|
+
}
|
|
1141
|
+
lines.push("*End of MindCache Export*");
|
|
1142
|
+
return lines.join("\n");
|
|
1143
|
+
}
|
|
1144
|
+
fromMarkdown(markdown) {
|
|
1145
|
+
const lines = markdown.split("\n");
|
|
1146
|
+
let currentSection = "header";
|
|
1147
|
+
let currentKey = null;
|
|
1148
|
+
let currentEntry = null;
|
|
1149
|
+
let inCodeBlock = false;
|
|
1150
|
+
let codeBlockContent = [];
|
|
1151
|
+
let codeBlockType = null;
|
|
1152
|
+
const appendixData = {};
|
|
1153
|
+
let currentAppendixKey = null;
|
|
1154
|
+
const pendingEntries = {};
|
|
1155
|
+
this.clear();
|
|
1156
|
+
for (let i = 0; i < lines.length; i++) {
|
|
1157
|
+
const line = lines[i];
|
|
1158
|
+
const trimmed = line.trim();
|
|
1159
|
+
if (trimmed === "## STM Entries") {
|
|
1160
|
+
currentSection = "entries";
|
|
1161
|
+
continue;
|
|
1162
|
+
}
|
|
1163
|
+
if (trimmed === "## Appendix: Binary Data") {
|
|
1164
|
+
currentSection = "appendix";
|
|
1165
|
+
continue;
|
|
1166
|
+
}
|
|
1167
|
+
if (trimmed === "```" || trimmed === "```json") {
|
|
1168
|
+
if (!inCodeBlock) {
|
|
1169
|
+
inCodeBlock = true;
|
|
1170
|
+
codeBlockContent = [];
|
|
1171
|
+
codeBlockType = currentSection === "appendix" ? "base64" : trimmed === "```json" ? "json" : "value";
|
|
1172
|
+
} else {
|
|
1173
|
+
inCodeBlock = false;
|
|
1174
|
+
const content = codeBlockContent.join("\n");
|
|
1175
|
+
if (currentSection === "appendix" && currentAppendixKey) {
|
|
1176
|
+
appendixData[currentAppendixKey].base64 = content;
|
|
1177
|
+
} else if (currentEntry && codeBlockType === "json") {
|
|
1178
|
+
currentEntry.value = content;
|
|
1179
|
+
} else if (currentEntry && codeBlockType === "value") {
|
|
1180
|
+
currentEntry.value = content;
|
|
1181
|
+
}
|
|
1182
|
+
codeBlockContent = [];
|
|
1183
|
+
codeBlockType = null;
|
|
1184
|
+
}
|
|
1185
|
+
continue;
|
|
1186
|
+
}
|
|
1187
|
+
if (inCodeBlock) {
|
|
1188
|
+
codeBlockContent.push(line);
|
|
1189
|
+
continue;
|
|
1190
|
+
}
|
|
1191
|
+
if (currentSection === "entries") {
|
|
1192
|
+
if (trimmed.startsWith("### ")) {
|
|
1193
|
+
if (currentKey && currentEntry && currentEntry.attributes) {
|
|
1194
|
+
pendingEntries[currentKey] = currentEntry;
|
|
1195
|
+
}
|
|
1196
|
+
currentKey = trimmed.substring(4);
|
|
1197
|
+
currentEntry = {
|
|
1198
|
+
value: void 0,
|
|
1199
|
+
attributes: {
|
|
1200
|
+
readonly: false,
|
|
1201
|
+
visible: true,
|
|
1202
|
+
hardcoded: false,
|
|
1203
|
+
template: false,
|
|
1204
|
+
type: "text",
|
|
1205
|
+
tags: []
|
|
1206
|
+
}
|
|
1207
|
+
};
|
|
1208
|
+
} else if (trimmed.startsWith("- **Type**: `")) {
|
|
1209
|
+
const type = trimmed.match(/`([^`]+)`/)?.[1];
|
|
1210
|
+
if (currentEntry && type && type !== "undefined") {
|
|
1211
|
+
currentEntry.attributes.type = type;
|
|
1212
|
+
}
|
|
1213
|
+
} else if (trimmed.startsWith("- **Readonly**: `")) {
|
|
1214
|
+
const value = trimmed.match(/`([^`]+)`/)?.[1] === "true";
|
|
1215
|
+
if (currentEntry) {
|
|
1216
|
+
currentEntry.attributes.readonly = value;
|
|
1217
|
+
}
|
|
1218
|
+
} else if (trimmed.startsWith("- **Visible**: `")) {
|
|
1219
|
+
const value = trimmed.match(/`([^`]+)`/)?.[1] === "true";
|
|
1220
|
+
if (currentEntry) {
|
|
1221
|
+
currentEntry.attributes.visible = value;
|
|
1222
|
+
}
|
|
1223
|
+
} else if (trimmed.startsWith("- **Template**: `")) {
|
|
1224
|
+
const value = trimmed.match(/`([^`]+)`/)?.[1] === "true";
|
|
1225
|
+
if (currentEntry) {
|
|
1226
|
+
currentEntry.attributes.template = value;
|
|
1227
|
+
}
|
|
1228
|
+
} else if (trimmed.startsWith("- **Tags**: `")) {
|
|
1229
|
+
const tagsStr = trimmed.substring(13, trimmed.length - 1);
|
|
1230
|
+
if (currentEntry) {
|
|
1231
|
+
currentEntry.attributes.tags = tagsStr.split("`, `");
|
|
1232
|
+
}
|
|
1233
|
+
} else if (trimmed.startsWith("- **Content Type**: `")) {
|
|
1234
|
+
const contentType = trimmed.match(/`([^`]+)`/)?.[1];
|
|
1235
|
+
if (currentEntry && contentType) {
|
|
1236
|
+
currentEntry.attributes.contentType = contentType;
|
|
1237
|
+
}
|
|
1238
|
+
} else if (trimmed.startsWith("- **Value**: `")) {
|
|
1239
|
+
const value = trimmed.substring(14, trimmed.length - 1);
|
|
1240
|
+
if (currentEntry) {
|
|
1241
|
+
currentEntry.value = value;
|
|
1242
|
+
}
|
|
1243
|
+
} else if (trimmed.startsWith("- **Value**: [See Appendix ")) {
|
|
1244
|
+
const labelMatch = trimmed.match(/Appendix ([A-Z])\]/);
|
|
1245
|
+
if (currentEntry && labelMatch && currentKey) {
|
|
1246
|
+
currentEntry.appendixLabel = labelMatch[1];
|
|
1247
|
+
currentEntry.value = "";
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
if (currentSection === "appendix") {
|
|
1252
|
+
if (trimmed.startsWith("### Appendix ")) {
|
|
1253
|
+
const match = trimmed.match(/### Appendix ([A-Z]): (.+)/);
|
|
1254
|
+
if (match) {
|
|
1255
|
+
const label = match[1];
|
|
1256
|
+
const key = match[2];
|
|
1257
|
+
currentAppendixKey = `${label}:${key}`;
|
|
1258
|
+
appendixData[currentAppendixKey] = { contentType: "", base64: "" };
|
|
1259
|
+
}
|
|
1260
|
+
} else if (trimmed.startsWith("**Type**: ")) {
|
|
1261
|
+
const contentType = trimmed.substring(10);
|
|
1262
|
+
if (currentAppendixKey) {
|
|
1263
|
+
appendixData[currentAppendixKey].contentType = contentType;
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
if (currentKey && currentEntry && currentEntry.attributes) {
|
|
1269
|
+
pendingEntries[currentKey] = currentEntry;
|
|
1270
|
+
}
|
|
1271
|
+
Object.entries(pendingEntries).forEach(([key, entry]) => {
|
|
1272
|
+
const appendixLabel = entry.appendixLabel;
|
|
1273
|
+
if (appendixLabel) {
|
|
1274
|
+
const appendixKey = `${appendixLabel}:${key}`;
|
|
1275
|
+
const appendixInfo = appendixData[appendixKey];
|
|
1276
|
+
if (appendixInfo && appendixInfo.base64) {
|
|
1277
|
+
entry.value = appendixInfo.base64;
|
|
1278
|
+
if (!entry.attributes.contentType && appendixInfo.contentType) {
|
|
1279
|
+
entry.attributes.contentType = appendixInfo.contentType;
|
|
1280
|
+
}
|
|
1281
|
+
}
|
|
1282
|
+
}
|
|
1283
|
+
if (entry.value !== void 0 && entry.attributes) {
|
|
1284
|
+
this.stm[key] = {
|
|
1285
|
+
value: entry.value,
|
|
1286
|
+
attributes: entry.attributes
|
|
1287
|
+
};
|
|
1288
|
+
}
|
|
1289
|
+
});
|
|
1290
|
+
this.notifyGlobalListeners();
|
|
1291
|
+
}
|
|
1292
|
+
};
|
|
1293
|
+
new MindCache();
|
|
1294
|
+
|
|
1295
|
+
// src/cloud/index.ts
|
|
1296
|
+
init_CloudAdapter();
|
|
1297
|
+
init_CloudAdapter();
|
|
1298
|
+
function connectCloud(mc, config) {
|
|
1299
|
+
const adapter = new exports.CloudAdapter(config);
|
|
1300
|
+
adapter.attach(mc);
|
|
1301
|
+
adapter.connect();
|
|
1302
|
+
return adapter;
|
|
1303
|
+
}
|
|
1304
|
+
function createCloudMindCache(config) {
|
|
1305
|
+
const mc = new MindCache();
|
|
1306
|
+
const adapter = connectCloud(mc, config);
|
|
1307
|
+
return Object.assign(mc, { adapter });
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
exports.connectCloud = connectCloud;
|
|
1311
|
+
exports.createCloudMindCache = createCloudMindCache;
|
|
1312
|
+
//# sourceMappingURL=index.js.map
|
|
1313
|
+
//# sourceMappingURL=index.js.map
|