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
package/lib/index.js
DELETED
|
@@ -1,1030 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.MindCache = exports.mindcache = void 0;
|
|
4
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
5
|
-
const zod_1 = require("zod");
|
|
6
|
-
class MindCache {
|
|
7
|
-
constructor() {
|
|
8
|
-
this.stm = {};
|
|
9
|
-
this.listeners = {};
|
|
10
|
-
this.globalListeners = [];
|
|
11
|
-
}
|
|
12
|
-
// Helper method to encode file to base64
|
|
13
|
-
encodeFileToBase64(file) {
|
|
14
|
-
return new Promise((resolve, reject) => {
|
|
15
|
-
// Check if we're in a browser environment
|
|
16
|
-
if (typeof FileReader !== 'undefined') {
|
|
17
|
-
const reader = new FileReader();
|
|
18
|
-
reader.onload = () => {
|
|
19
|
-
const result = reader.result;
|
|
20
|
-
// Remove data URL prefix to get just the base64 data
|
|
21
|
-
const base64Data = result.split(',')[1];
|
|
22
|
-
resolve(base64Data);
|
|
23
|
-
};
|
|
24
|
-
reader.onerror = reject;
|
|
25
|
-
reader.readAsDataURL(file);
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
28
|
-
// Node.js environment - reject with helpful error
|
|
29
|
-
reject(new Error('FileReader not available in Node.js environment. Use set_base64() method instead.'));
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
// Helper method to create data URL from base64 and content type
|
|
34
|
-
createDataUrl(base64Data, contentType) {
|
|
35
|
-
return `data:${contentType};base64,${base64Data}`;
|
|
36
|
-
}
|
|
37
|
-
// Helper method to validate content type for different STM types
|
|
38
|
-
validateContentType(type, contentType) {
|
|
39
|
-
if (type === 'text' || type === 'json') {
|
|
40
|
-
return true; // No content type validation needed for text/json
|
|
41
|
-
}
|
|
42
|
-
if (!contentType) {
|
|
43
|
-
return false; // Files and images require content type
|
|
44
|
-
}
|
|
45
|
-
if (type === 'image') {
|
|
46
|
-
return contentType.startsWith('image/');
|
|
47
|
-
}
|
|
48
|
-
if (type === 'file') {
|
|
49
|
-
return true; // Any content type is valid for generic files
|
|
50
|
-
}
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
// Get a value from the STM (deprecated - use get_value instead)
|
|
54
|
-
/** @deprecated Use get_value instead */
|
|
55
|
-
get(key) {
|
|
56
|
-
return this.get_value(key);
|
|
57
|
-
}
|
|
58
|
-
// Get a value from the STM with template processing if enabled
|
|
59
|
-
get_value(key, _processingStack) {
|
|
60
|
-
if (key === '$date') {
|
|
61
|
-
const today = new Date();
|
|
62
|
-
return today.toISOString().split('T')[0];
|
|
63
|
-
}
|
|
64
|
-
if (key === '$time') {
|
|
65
|
-
const now = new Date();
|
|
66
|
-
return now.toTimeString().split(' ')[0];
|
|
67
|
-
}
|
|
68
|
-
const entry = this.stm[key];
|
|
69
|
-
if (!entry) {
|
|
70
|
-
return undefined;
|
|
71
|
-
}
|
|
72
|
-
// If template is enabled, process the value through injectSTM
|
|
73
|
-
if (entry.attributes.template) {
|
|
74
|
-
// Prevent circular references
|
|
75
|
-
const processingStack = _processingStack || new Set();
|
|
76
|
-
if (processingStack.has(key)) {
|
|
77
|
-
return entry.value; // Return raw value to break circular reference
|
|
78
|
-
}
|
|
79
|
-
processingStack.add(key);
|
|
80
|
-
const result = this.injectSTM(entry.value, processingStack);
|
|
81
|
-
processingStack.delete(key);
|
|
82
|
-
return result;
|
|
83
|
-
}
|
|
84
|
-
return entry.value;
|
|
85
|
-
}
|
|
86
|
-
// Get attributes for a key
|
|
87
|
-
get_attributes(key) {
|
|
88
|
-
if (key === '$date' || key === '$time') {
|
|
89
|
-
return {
|
|
90
|
-
readonly: true,
|
|
91
|
-
visible: true,
|
|
92
|
-
hardcoded: true,
|
|
93
|
-
template: false,
|
|
94
|
-
type: 'text',
|
|
95
|
-
tags: []
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
const entry = this.stm[key];
|
|
99
|
-
return entry ? entry.attributes : undefined;
|
|
100
|
-
}
|
|
101
|
-
// Set a value in the STM with default attributes
|
|
102
|
-
set_value(key, value, attributes) {
|
|
103
|
-
// Don't allow setting hardcoded system keys
|
|
104
|
-
if (key === '$date' || key === '$time') {
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
const defaultAttributes = {
|
|
108
|
-
readonly: false,
|
|
109
|
-
visible: true,
|
|
110
|
-
hardcoded: false,
|
|
111
|
-
template: false,
|
|
112
|
-
type: 'text',
|
|
113
|
-
tags: []
|
|
114
|
-
};
|
|
115
|
-
// If key exists, preserve existing attributes unless explicitly overridden
|
|
116
|
-
const existingEntry = this.stm[key];
|
|
117
|
-
const baseAttributes = existingEntry ? existingEntry.attributes : defaultAttributes;
|
|
118
|
-
const finalAttributes = attributes ? { ...baseAttributes, ...attributes } : baseAttributes;
|
|
119
|
-
// If hardcoded is true, force readonly to true and template to false
|
|
120
|
-
if (finalAttributes.hardcoded) {
|
|
121
|
-
finalAttributes.readonly = true;
|
|
122
|
-
finalAttributes.template = false;
|
|
123
|
-
}
|
|
124
|
-
this.stm[key] = {
|
|
125
|
-
value,
|
|
126
|
-
attributes: finalAttributes
|
|
127
|
-
};
|
|
128
|
-
if (this.listeners[key]) {
|
|
129
|
-
this.listeners[key].forEach(listener => listener());
|
|
130
|
-
}
|
|
131
|
-
this.notifyGlobalListeners();
|
|
132
|
-
}
|
|
133
|
-
// Set attributes for an existing key
|
|
134
|
-
set_attributes(key, attributes) {
|
|
135
|
-
// Don't allow setting attributes for hardcoded system keys
|
|
136
|
-
if (key === '$date' || key === '$time') {
|
|
137
|
-
return false;
|
|
138
|
-
}
|
|
139
|
-
const entry = this.stm[key];
|
|
140
|
-
if (!entry) {
|
|
141
|
-
return false;
|
|
142
|
-
}
|
|
143
|
-
// Create a copy of attributes, excluding the hardcoded property to prevent modification
|
|
144
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars
|
|
145
|
-
const { hardcoded, ...allowedAttributes } = attributes;
|
|
146
|
-
// Apply the allowed attributes
|
|
147
|
-
entry.attributes = { ...entry.attributes, ...allowedAttributes };
|
|
148
|
-
// If this is a hardcoded key, ensure readonly is always true and template is always false
|
|
149
|
-
if (entry.attributes.hardcoded) {
|
|
150
|
-
entry.attributes.readonly = true;
|
|
151
|
-
entry.attributes.template = false;
|
|
152
|
-
}
|
|
153
|
-
this.notifyGlobalListeners();
|
|
154
|
-
return true;
|
|
155
|
-
}
|
|
156
|
-
// Set a value in the STM (uses default attributes)
|
|
157
|
-
set(key, value) {
|
|
158
|
-
this.set_value(key, value);
|
|
159
|
-
}
|
|
160
|
-
// Set a file value in the STM with base64 encoding
|
|
161
|
-
async set_file(key, file, attributes) {
|
|
162
|
-
const base64Data = await this.encodeFileToBase64(file);
|
|
163
|
-
const contentType = file.type;
|
|
164
|
-
const fileAttributes = {
|
|
165
|
-
type: contentType.startsWith('image/') ? 'image' : 'file',
|
|
166
|
-
contentType,
|
|
167
|
-
...attributes
|
|
168
|
-
};
|
|
169
|
-
this.set_value(key, base64Data, fileAttributes);
|
|
170
|
-
}
|
|
171
|
-
// Set a base64 encoded value with content type
|
|
172
|
-
set_base64(key, base64Data, contentType, type = 'file', attributes) {
|
|
173
|
-
if (!this.validateContentType(type, contentType)) {
|
|
174
|
-
throw new Error(`Invalid content type ${contentType} for type ${type}`);
|
|
175
|
-
}
|
|
176
|
-
const fileAttributes = {
|
|
177
|
-
type,
|
|
178
|
-
contentType,
|
|
179
|
-
...attributes
|
|
180
|
-
};
|
|
181
|
-
this.set_value(key, base64Data, fileAttributes);
|
|
182
|
-
}
|
|
183
|
-
// Convenience method to add an image to STM with proper attributes
|
|
184
|
-
add_image(key, base64Data, contentType = 'image/jpeg', attributes) {
|
|
185
|
-
if (!contentType.startsWith('image/')) {
|
|
186
|
-
throw new Error(`Invalid image content type: ${contentType}. Must start with 'image/'`);
|
|
187
|
-
}
|
|
188
|
-
this.set_base64(key, base64Data, contentType, 'image', attributes);
|
|
189
|
-
// Explicitly ensure the type is set to 'image' after setting the value
|
|
190
|
-
this.set_attributes(key, {
|
|
191
|
-
type: 'image',
|
|
192
|
-
contentType: contentType
|
|
193
|
-
});
|
|
194
|
-
}
|
|
195
|
-
// Get a value as data URL (for files/images)
|
|
196
|
-
get_data_url(key) {
|
|
197
|
-
const entry = this.stm[key];
|
|
198
|
-
if (!entry || (entry.attributes.type !== 'image' && entry.attributes.type !== 'file')) {
|
|
199
|
-
return undefined;
|
|
200
|
-
}
|
|
201
|
-
if (!entry.attributes.contentType) {
|
|
202
|
-
return undefined;
|
|
203
|
-
}
|
|
204
|
-
return this.createDataUrl(entry.value, entry.attributes.contentType);
|
|
205
|
-
}
|
|
206
|
-
// Get raw base64 data
|
|
207
|
-
get_base64(key) {
|
|
208
|
-
const entry = this.stm[key];
|
|
209
|
-
if (!entry || (entry.attributes.type !== 'image' && entry.attributes.type !== 'file')) {
|
|
210
|
-
return undefined;
|
|
211
|
-
}
|
|
212
|
-
return entry.value;
|
|
213
|
-
}
|
|
214
|
-
// Check if a key exists in the STM
|
|
215
|
-
has(key) {
|
|
216
|
-
if (key === '$date' || key === '$time') {
|
|
217
|
-
return true;
|
|
218
|
-
}
|
|
219
|
-
return key in this.stm;
|
|
220
|
-
}
|
|
221
|
-
// Delete a key-value pair from the STM
|
|
222
|
-
delete(key) {
|
|
223
|
-
if (key === '$date' || key === '$time') {
|
|
224
|
-
return false; // Can't delete hardcoded system keys
|
|
225
|
-
}
|
|
226
|
-
if (!(key in this.stm)) {
|
|
227
|
-
return false;
|
|
228
|
-
}
|
|
229
|
-
const deleted = delete this.stm[key];
|
|
230
|
-
if (deleted) {
|
|
231
|
-
this.notifyGlobalListeners();
|
|
232
|
-
if (this.listeners[key]) {
|
|
233
|
-
this.listeners[key].forEach(listener => listener());
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
return deleted;
|
|
237
|
-
}
|
|
238
|
-
// Clear the entire STM
|
|
239
|
-
clear() {
|
|
240
|
-
// Clear the STM
|
|
241
|
-
this.stm = {};
|
|
242
|
-
this.notifyGlobalListeners();
|
|
243
|
-
}
|
|
244
|
-
// Get all keys in the STM
|
|
245
|
-
keys() {
|
|
246
|
-
return [...Object.keys(this.stm), '$date', '$time'];
|
|
247
|
-
}
|
|
248
|
-
// Get all values in the STM
|
|
249
|
-
values() {
|
|
250
|
-
const now = new Date();
|
|
251
|
-
const stmValues = Object.values(this.stm).map(entry => entry.value);
|
|
252
|
-
return [
|
|
253
|
-
...stmValues,
|
|
254
|
-
now.toISOString().split('T')[0],
|
|
255
|
-
now.toTimeString().split(' ')[0]
|
|
256
|
-
];
|
|
257
|
-
}
|
|
258
|
-
// Get all entries (key-value pairs) in the STM
|
|
259
|
-
entries() {
|
|
260
|
-
const now = new Date();
|
|
261
|
-
const stmEntries = Object.entries(this.stm).map(([key, entry]) => [key, entry.value]);
|
|
262
|
-
return [
|
|
263
|
-
...stmEntries,
|
|
264
|
-
['$date', now.toISOString().split('T')[0]],
|
|
265
|
-
['$time', now.toTimeString().split(' ')[0]]
|
|
266
|
-
];
|
|
267
|
-
}
|
|
268
|
-
// Get the size of the STM
|
|
269
|
-
size() {
|
|
270
|
-
return Object.keys(this.stm).length + 2; // +2 for $date and $time
|
|
271
|
-
}
|
|
272
|
-
// Get a copy of the entire STM object (returns values only for backward compatibility)
|
|
273
|
-
getAll() {
|
|
274
|
-
const now = new Date();
|
|
275
|
-
const result = {};
|
|
276
|
-
// Add regular STM values
|
|
277
|
-
Object.entries(this.stm).forEach(([key, entry]) => {
|
|
278
|
-
result[key] = entry.value;
|
|
279
|
-
});
|
|
280
|
-
// Add system values
|
|
281
|
-
result['$date'] = now.toISOString().split('T')[0];
|
|
282
|
-
result['$time'] = now.toTimeString().split(' ')[0];
|
|
283
|
-
return result;
|
|
284
|
-
}
|
|
285
|
-
// Update the STM with multiple key-value pairs (uses default attributes)
|
|
286
|
-
update(newValues) {
|
|
287
|
-
Object.entries(newValues).forEach(([key, value]) => {
|
|
288
|
-
if (key !== '$date' && key !== '$time') {
|
|
289
|
-
// Set value without triggering individual notifications
|
|
290
|
-
const defaultAttributes = {
|
|
291
|
-
readonly: false,
|
|
292
|
-
visible: true,
|
|
293
|
-
hardcoded: false,
|
|
294
|
-
template: false,
|
|
295
|
-
type: 'text',
|
|
296
|
-
tags: []
|
|
297
|
-
};
|
|
298
|
-
this.stm[key] = {
|
|
299
|
-
value,
|
|
300
|
-
attributes: defaultAttributes
|
|
301
|
-
};
|
|
302
|
-
// Trigger key-specific listeners
|
|
303
|
-
if (this.listeners[key]) {
|
|
304
|
-
this.listeners[key].forEach(listener => listener());
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
});
|
|
308
|
-
// Trigger global listeners only once at the end
|
|
309
|
-
this.notifyGlobalListeners();
|
|
310
|
-
}
|
|
311
|
-
// Subscribe to changes for a specific key
|
|
312
|
-
subscribe(key, listener) {
|
|
313
|
-
if (!this.listeners[key]) {
|
|
314
|
-
this.listeners[key] = [];
|
|
315
|
-
}
|
|
316
|
-
this.listeners[key].push(listener);
|
|
317
|
-
}
|
|
318
|
-
// Unsubscribe from changes for a specific key
|
|
319
|
-
unsubscribe(key, listener) {
|
|
320
|
-
if (this.listeners[key]) {
|
|
321
|
-
this.listeners[key] = this.listeners[key].filter(l => l !== listener);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
// Subscribe to changes for all STM keys
|
|
325
|
-
subscribeToAll(listener) {
|
|
326
|
-
this.globalListeners.push(listener);
|
|
327
|
-
}
|
|
328
|
-
// Unsubscribe from all STM changes
|
|
329
|
-
unsubscribeFromAll(listener) {
|
|
330
|
-
this.globalListeners = this.globalListeners.filter(l => l !== listener);
|
|
331
|
-
}
|
|
332
|
-
// Helper method to notify all global listeners
|
|
333
|
-
notifyGlobalListeners() {
|
|
334
|
-
this.globalListeners.forEach(listener => listener());
|
|
335
|
-
}
|
|
336
|
-
// Replace placeholders in a string with STM values (only uses visible keys for public injectSTM calls)
|
|
337
|
-
// Note: Image and file type placeholders are NOT replaced - they remain as {{key}} for tools to process
|
|
338
|
-
injectSTM(template, _processingStack) {
|
|
339
|
-
// Handle null/undefined templates
|
|
340
|
-
if (template === null || template === undefined) {
|
|
341
|
-
return String(template);
|
|
342
|
-
}
|
|
343
|
-
// Convert to string if not already
|
|
344
|
-
const templateStr = String(template);
|
|
345
|
-
// find all the keys in the template using double brackets
|
|
346
|
-
const keys = templateStr.match(/\{\{([$\w]+)\}\}/g);
|
|
347
|
-
if (!keys) {
|
|
348
|
-
return templateStr;
|
|
349
|
-
}
|
|
350
|
-
// Extract the actual key names without the double curly braces
|
|
351
|
-
const cleanKeys = keys.map(key => key.replace(/[{}]/g, ''));
|
|
352
|
-
// Build inputValues with the clean keys
|
|
353
|
-
const inputValues = cleanKeys.reduce((acc, key) => {
|
|
354
|
-
// Always allow system keys
|
|
355
|
-
if (key === '$date' || key === '$time') {
|
|
356
|
-
return {
|
|
357
|
-
...acc,
|
|
358
|
-
[key]: this.get_value(key, _processingStack)
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
// If this is internal processing (from template), allow all keys
|
|
362
|
-
// If this is external call, only allow visible keys
|
|
363
|
-
const attributes = this.get_attributes(key);
|
|
364
|
-
if (_processingStack || (attributes && attributes.visible)) {
|
|
365
|
-
// Skip replacement for image and file types - keep the placeholder
|
|
366
|
-
if (attributes && (attributes.type === 'image' || attributes.type === 'file')) {
|
|
367
|
-
return acc; // Don't add to inputValues, placeholder will remain
|
|
368
|
-
}
|
|
369
|
-
return {
|
|
370
|
-
...acc,
|
|
371
|
-
[key]: this.get_value(key, _processingStack)
|
|
372
|
-
};
|
|
373
|
-
}
|
|
374
|
-
// If key doesn't exist or is not visible (for external calls), don't include it
|
|
375
|
-
return acc;
|
|
376
|
-
}, {});
|
|
377
|
-
// Replace the placeholders with actual values using double brackets
|
|
378
|
-
// Image/file placeholders will remain as {{key}} since they're not in inputValues
|
|
379
|
-
return templateStr.replace(/\{\{([$\w]+)\}\}/g, (match, key) => {
|
|
380
|
-
// If value is in inputValues, use it
|
|
381
|
-
if (inputValues[key] !== undefined) {
|
|
382
|
-
return inputValues[key];
|
|
383
|
-
}
|
|
384
|
-
// Check if this is an image/file placeholder that should be preserved
|
|
385
|
-
const attributes = this.get_attributes(key);
|
|
386
|
-
if (attributes && (attributes.type === 'image' || attributes.type === 'file')) {
|
|
387
|
-
return match; // Keep the placeholder for images/files
|
|
388
|
-
}
|
|
389
|
-
// For missing or invisible keys, replace with empty string (original behavior)
|
|
390
|
-
return '';
|
|
391
|
-
});
|
|
392
|
-
}
|
|
393
|
-
// Get a formatted string of all visible STM key-value pairs
|
|
394
|
-
getSTM() {
|
|
395
|
-
const now = new Date();
|
|
396
|
-
const entries = [];
|
|
397
|
-
// Add visible regular STM entries
|
|
398
|
-
Object.entries(this.stm).forEach(([key, entry]) => {
|
|
399
|
-
if (entry.attributes.visible) {
|
|
400
|
-
// Use get_value to handle template processing
|
|
401
|
-
entries.push([key, this.get_value(key)]);
|
|
402
|
-
}
|
|
403
|
-
});
|
|
404
|
-
// Add system keys (always visible)
|
|
405
|
-
entries.push(['$date', now.toISOString().split('T')[0]]);
|
|
406
|
-
entries.push(['$time', now.toTimeString().split(' ')[0]]);
|
|
407
|
-
return entries
|
|
408
|
-
.map(([key, value]) => `${key}: ${value}`)
|
|
409
|
-
.join(', ');
|
|
410
|
-
}
|
|
411
|
-
// Get STM as a proper object (alias for getAll for clarity)
|
|
412
|
-
getSTMObject() {
|
|
413
|
-
return this.getAll();
|
|
414
|
-
}
|
|
415
|
-
// Get STM data formatted for API calls (multipart form data style)
|
|
416
|
-
getSTMForAPI() {
|
|
417
|
-
const now = new Date();
|
|
418
|
-
const apiData = [];
|
|
419
|
-
// Add visible regular STM entries
|
|
420
|
-
Object.entries(this.stm).forEach(([key, entry]) => {
|
|
421
|
-
if (entry.attributes.visible) {
|
|
422
|
-
const processedValue = entry.attributes.template ? this.get_value(key) : entry.value;
|
|
423
|
-
apiData.push({
|
|
424
|
-
key,
|
|
425
|
-
value: processedValue,
|
|
426
|
-
type: entry.attributes.type,
|
|
427
|
-
contentType: entry.attributes.contentType
|
|
428
|
-
});
|
|
429
|
-
}
|
|
430
|
-
});
|
|
431
|
-
// Add system keys (always visible)
|
|
432
|
-
apiData.push({
|
|
433
|
-
key: '$date',
|
|
434
|
-
value: now.toISOString().split('T')[0],
|
|
435
|
-
type: 'text'
|
|
436
|
-
});
|
|
437
|
-
apiData.push({
|
|
438
|
-
key: '$time',
|
|
439
|
-
value: now.toTimeString().split(' ')[0],
|
|
440
|
-
type: 'text'
|
|
441
|
-
});
|
|
442
|
-
return apiData;
|
|
443
|
-
}
|
|
444
|
-
// Get visible images formatted for AI SDK UIMessage file parts
|
|
445
|
-
getVisibleImages() {
|
|
446
|
-
const imageParts = [];
|
|
447
|
-
Object.entries(this.stm).forEach(([key, entry]) => {
|
|
448
|
-
if (entry.attributes.visible && entry.attributes.type === 'image' && entry.attributes.contentType) {
|
|
449
|
-
// Create data URL from base64 data
|
|
450
|
-
const dataUrl = this.createDataUrl(entry.value, entry.attributes.contentType);
|
|
451
|
-
imageParts.push({
|
|
452
|
-
type: 'file',
|
|
453
|
-
mediaType: entry.attributes.contentType,
|
|
454
|
-
url: dataUrl,
|
|
455
|
-
filename: key // Use the STM key as filename
|
|
456
|
-
});
|
|
457
|
-
}
|
|
458
|
-
});
|
|
459
|
-
return imageParts;
|
|
460
|
-
}
|
|
461
|
-
// Serialize STM to JSON string (complete state)
|
|
462
|
-
toJSON() {
|
|
463
|
-
return JSON.stringify(this.serialize());
|
|
464
|
-
}
|
|
465
|
-
// Deserialize from JSON string and update STM (complete state)
|
|
466
|
-
fromJSON(jsonString) {
|
|
467
|
-
try {
|
|
468
|
-
const data = JSON.parse(jsonString);
|
|
469
|
-
this.deserialize(data);
|
|
470
|
-
}
|
|
471
|
-
catch (error) {
|
|
472
|
-
// eslint-disable-next-line no-console
|
|
473
|
-
console.error('MindCache: Failed to deserialize JSON:', error);
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
// Serialize complete state (values + attributes, excluding hardcoded keys)
|
|
477
|
-
serialize() {
|
|
478
|
-
const result = {};
|
|
479
|
-
Object.entries(this.stm).forEach(([key, entry]) => {
|
|
480
|
-
// Only serialize non-hardcoded entries
|
|
481
|
-
if (!entry.attributes.hardcoded) {
|
|
482
|
-
result[key] = {
|
|
483
|
-
value: entry.value,
|
|
484
|
-
attributes: { ...entry.attributes }
|
|
485
|
-
};
|
|
486
|
-
}
|
|
487
|
-
});
|
|
488
|
-
return result;
|
|
489
|
-
}
|
|
490
|
-
// Deserialize complete state (values + attributes)
|
|
491
|
-
deserialize(data) {
|
|
492
|
-
if (typeof data === 'object' && data !== null) {
|
|
493
|
-
// Clear existing STM (preserves hardcoded keys via clear())
|
|
494
|
-
this.clear();
|
|
495
|
-
// Restore entries with their complete state
|
|
496
|
-
Object.entries(data).forEach(([key, entry]) => {
|
|
497
|
-
if (entry && typeof entry === 'object' && 'value' in entry && 'attributes' in entry) {
|
|
498
|
-
this.stm[key] = {
|
|
499
|
-
value: entry.value,
|
|
500
|
-
attributes: {
|
|
501
|
-
...entry.attributes,
|
|
502
|
-
tags: entry.attributes.tags || [] // Ensure tags array exists
|
|
503
|
-
}
|
|
504
|
-
};
|
|
505
|
-
}
|
|
506
|
-
});
|
|
507
|
-
this.notifyGlobalListeners();
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
// Generate system prompt from all visible STM keys
|
|
511
|
-
get_system_prompt() {
|
|
512
|
-
const now = new Date();
|
|
513
|
-
const promptLines = [];
|
|
514
|
-
// Add visible regular STM entries
|
|
515
|
-
Object.entries(this.stm).forEach(([key, entry]) => {
|
|
516
|
-
if (entry.attributes.visible) {
|
|
517
|
-
// Skip images and large files in system prompt to save context
|
|
518
|
-
if (entry.attributes.type === 'image') {
|
|
519
|
-
promptLines.push(`image ${key} available`);
|
|
520
|
-
return;
|
|
521
|
-
}
|
|
522
|
-
if (entry.attributes.type === 'file') {
|
|
523
|
-
if (entry.attributes.readonly) {
|
|
524
|
-
promptLines.push(`${key}: [${entry.attributes.type.toUpperCase()}] - ${entry.attributes.contentType || 'unknown format'}`);
|
|
525
|
-
}
|
|
526
|
-
else {
|
|
527
|
-
const sanitizedKey = key.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
528
|
-
promptLines.push(`${key}: [${entry.attributes.type.toUpperCase()}] - ${entry.attributes.contentType || 'unknown format'}. You can update this ${entry.attributes.type} using the write_${sanitizedKey} tool.`);
|
|
529
|
-
}
|
|
530
|
-
return;
|
|
531
|
-
}
|
|
532
|
-
const value = this.get_value(key);
|
|
533
|
-
const formattedValue = typeof value === 'object' && value !== null
|
|
534
|
-
? JSON.stringify(value)
|
|
535
|
-
: String(value);
|
|
536
|
-
if (entry.attributes.readonly) {
|
|
537
|
-
// Readonly keys: just show the key-value pair
|
|
538
|
-
promptLines.push(`${key}: ${formattedValue}`);
|
|
539
|
-
}
|
|
540
|
-
else {
|
|
541
|
-
// Writable keys: show value and mention the tool (with sanitized tool name)
|
|
542
|
-
const sanitizedKey = key.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
543
|
-
const toolInstruction = `You can rewrite "${key}" by using the write_${sanitizedKey} tool. ` +
|
|
544
|
-
'This tool DOES NOT append — start your response with the old value ' +
|
|
545
|
-
`(${formattedValue})`;
|
|
546
|
-
promptLines.push(`${key}: ${formattedValue}. ${toolInstruction}`);
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
});
|
|
550
|
-
// Add system keys (always visible and readonly)
|
|
551
|
-
promptLines.push(`$date: ${now.toISOString().split('T')[0]}`);
|
|
552
|
-
promptLines.push(`$time: ${now.toTimeString().split(' ')[0]}`);
|
|
553
|
-
return promptLines.join('\n');
|
|
554
|
-
}
|
|
555
|
-
// Helper method to find original key from sanitized tool name
|
|
556
|
-
findKeyFromToolName(toolName) {
|
|
557
|
-
if (!toolName.startsWith('write_')) {
|
|
558
|
-
return undefined;
|
|
559
|
-
}
|
|
560
|
-
const sanitizedKey = toolName.replace('write_', '');
|
|
561
|
-
// Find the original key by checking all keys and their sanitized versions
|
|
562
|
-
const allKeys = Object.keys(this.stm);
|
|
563
|
-
return allKeys.find(k => k.replace(/[^a-zA-Z0-9_-]/g, '_') === sanitizedKey);
|
|
564
|
-
}
|
|
565
|
-
// Generate tools for Vercel AI SDK to write STM values (excludes readonly keys)
|
|
566
|
-
get_aisdk_tools() {
|
|
567
|
-
const tools = {};
|
|
568
|
-
// Get all current keys (excluding built-in $date and $time and readonly keys)
|
|
569
|
-
const writableKeys = Object.entries(this.stm)
|
|
570
|
-
.filter(([, entry]) => !entry.attributes.readonly)
|
|
571
|
-
.map(([key]) => key);
|
|
572
|
-
// Create a write tool for each writable key
|
|
573
|
-
writableKeys.forEach(key => {
|
|
574
|
-
// Sanitize tool name to match OpenAI's pattern: ^[a-zA-Z0-9_-]+$
|
|
575
|
-
const sanitizedKey = key.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
576
|
-
const toolName = `write_${sanitizedKey}`;
|
|
577
|
-
const entry = this.stm[key];
|
|
578
|
-
const keyType = entry?.attributes.type || 'text';
|
|
579
|
-
// Create appropriate schema based on the key's type
|
|
580
|
-
let inputSchema;
|
|
581
|
-
let description = `Write a value to the STM key: ${key}`;
|
|
582
|
-
if (keyType === 'image' || keyType === 'file') {
|
|
583
|
-
description += ' (expects base64 encoded data)';
|
|
584
|
-
inputSchema = zod_1.z.object({
|
|
585
|
-
value: zod_1.z.string().describe(`Base64 encoded data for ${key}`),
|
|
586
|
-
contentType: zod_1.z.string().optional().describe(`MIME type for the ${keyType}`)
|
|
587
|
-
});
|
|
588
|
-
}
|
|
589
|
-
else if (keyType === 'json') {
|
|
590
|
-
description += ' (expects JSON string)';
|
|
591
|
-
inputSchema = zod_1.z.object({
|
|
592
|
-
value: zod_1.z.string().describe(`JSON string value for ${key}`)
|
|
593
|
-
});
|
|
594
|
-
}
|
|
595
|
-
else {
|
|
596
|
-
inputSchema = zod_1.z.object({
|
|
597
|
-
value: zod_1.z.string().describe(`The text value to write to ${key}`)
|
|
598
|
-
});
|
|
599
|
-
}
|
|
600
|
-
tools[toolName] = {
|
|
601
|
-
description,
|
|
602
|
-
inputSchema,
|
|
603
|
-
execute: async (input) => {
|
|
604
|
-
// Handle different types appropriately
|
|
605
|
-
if (keyType === 'image' || keyType === 'file') {
|
|
606
|
-
if (input.contentType) {
|
|
607
|
-
this.set_base64(key, input.value, input.contentType, keyType);
|
|
608
|
-
}
|
|
609
|
-
else {
|
|
610
|
-
// Use existing content type if available
|
|
611
|
-
const existingContentType = entry?.attributes.contentType;
|
|
612
|
-
if (existingContentType) {
|
|
613
|
-
this.set_base64(key, input.value, existingContentType, keyType);
|
|
614
|
-
}
|
|
615
|
-
else {
|
|
616
|
-
throw new Error(`Content type required for ${keyType} data`);
|
|
617
|
-
}
|
|
618
|
-
}
|
|
619
|
-
}
|
|
620
|
-
else {
|
|
621
|
-
// For text and json, use regular set_value
|
|
622
|
-
this.set_value(key, input.value);
|
|
623
|
-
}
|
|
624
|
-
// Create specialized success message based on type
|
|
625
|
-
let resultMessage;
|
|
626
|
-
if (keyType === 'image') {
|
|
627
|
-
resultMessage = `Successfully saved image to ${key}`;
|
|
628
|
-
}
|
|
629
|
-
else if (keyType === 'file') {
|
|
630
|
-
resultMessage = `Successfully saved file to ${key}`;
|
|
631
|
-
}
|
|
632
|
-
else if (keyType === 'json') {
|
|
633
|
-
resultMessage = `Successfully saved JSON data to ${key}`;
|
|
634
|
-
}
|
|
635
|
-
else {
|
|
636
|
-
// For text type, include the actual value
|
|
637
|
-
resultMessage = `Successfully wrote "${input.value}" to ${key}`;
|
|
638
|
-
}
|
|
639
|
-
return {
|
|
640
|
-
result: resultMessage,
|
|
641
|
-
key: key,
|
|
642
|
-
value: input.value,
|
|
643
|
-
type: keyType,
|
|
644
|
-
contentType: input.contentType,
|
|
645
|
-
sanitizedKey: sanitizedKey
|
|
646
|
-
};
|
|
647
|
-
}
|
|
648
|
-
};
|
|
649
|
-
});
|
|
650
|
-
// If no writable keys exist yet, return an empty object
|
|
651
|
-
if (writableKeys.length === 0) {
|
|
652
|
-
return {};
|
|
653
|
-
}
|
|
654
|
-
return tools;
|
|
655
|
-
}
|
|
656
|
-
// Public method for client-side tool execution
|
|
657
|
-
executeToolCall(toolName, value) {
|
|
658
|
-
const originalKey = this.findKeyFromToolName(toolName);
|
|
659
|
-
if (!originalKey) {
|
|
660
|
-
return null;
|
|
661
|
-
}
|
|
662
|
-
// Check if key is readonly
|
|
663
|
-
const entry = this.stm[originalKey];
|
|
664
|
-
if (entry && entry.attributes.readonly) {
|
|
665
|
-
return null;
|
|
666
|
-
}
|
|
667
|
-
this.set_value(originalKey, value);
|
|
668
|
-
return {
|
|
669
|
-
result: `Successfully wrote "${value}" to ${originalKey}`,
|
|
670
|
-
key: originalKey,
|
|
671
|
-
value: value
|
|
672
|
-
};
|
|
673
|
-
}
|
|
674
|
-
// Add a tag to a key
|
|
675
|
-
addTag(key, tag) {
|
|
676
|
-
// Don't allow tagging hardcoded system keys
|
|
677
|
-
if (key === '$date' || key === '$time') {
|
|
678
|
-
return false;
|
|
679
|
-
}
|
|
680
|
-
const entry = this.stm[key];
|
|
681
|
-
if (!entry) {
|
|
682
|
-
return false;
|
|
683
|
-
}
|
|
684
|
-
// Initialize tags array if it doesn't exist
|
|
685
|
-
if (!entry.attributes.tags) {
|
|
686
|
-
entry.attributes.tags = [];
|
|
687
|
-
}
|
|
688
|
-
// Add tag if it doesn't already exist
|
|
689
|
-
if (!entry.attributes.tags.includes(tag)) {
|
|
690
|
-
entry.attributes.tags.push(tag);
|
|
691
|
-
this.notifyGlobalListeners();
|
|
692
|
-
return true;
|
|
693
|
-
}
|
|
694
|
-
return false; // Tag already exists
|
|
695
|
-
}
|
|
696
|
-
// Remove a tag from a key
|
|
697
|
-
removeTag(key, tag) {
|
|
698
|
-
// Don't allow modifying hardcoded system keys
|
|
699
|
-
if (key === '$date' || key === '$time') {
|
|
700
|
-
return false;
|
|
701
|
-
}
|
|
702
|
-
const entry = this.stm[key];
|
|
703
|
-
if (!entry || !entry.attributes.tags) {
|
|
704
|
-
return false;
|
|
705
|
-
}
|
|
706
|
-
const tagIndex = entry.attributes.tags.indexOf(tag);
|
|
707
|
-
if (tagIndex > -1) {
|
|
708
|
-
entry.attributes.tags.splice(tagIndex, 1);
|
|
709
|
-
this.notifyGlobalListeners();
|
|
710
|
-
return true;
|
|
711
|
-
}
|
|
712
|
-
return false; // Tag not found
|
|
713
|
-
}
|
|
714
|
-
// Get all tags for a key
|
|
715
|
-
getTags(key) {
|
|
716
|
-
if (key === '$date' || key === '$time') {
|
|
717
|
-
return []; // System keys have no tags
|
|
718
|
-
}
|
|
719
|
-
const entry = this.stm[key];
|
|
720
|
-
return entry?.attributes.tags || [];
|
|
721
|
-
}
|
|
722
|
-
// Get all unique tags across all entries
|
|
723
|
-
getAllTags() {
|
|
724
|
-
const allTags = new Set();
|
|
725
|
-
Object.values(this.stm).forEach(entry => {
|
|
726
|
-
if (entry.attributes.tags) {
|
|
727
|
-
entry.attributes.tags.forEach(tag => allTags.add(tag));
|
|
728
|
-
}
|
|
729
|
-
});
|
|
730
|
-
return Array.from(allTags);
|
|
731
|
-
}
|
|
732
|
-
// Check if a key has a specific tag
|
|
733
|
-
hasTag(key, tag) {
|
|
734
|
-
if (key === '$date' || key === '$time') {
|
|
735
|
-
return false; // System keys have no tags
|
|
736
|
-
}
|
|
737
|
-
const entry = this.stm[key];
|
|
738
|
-
return entry?.attributes.tags?.includes(tag) || false;
|
|
739
|
-
}
|
|
740
|
-
// Get a formatted string of all entries with a specific tag (ignores visible attribute)
|
|
741
|
-
getTagged(tag) {
|
|
742
|
-
const entries = [];
|
|
743
|
-
// Add regular STM entries that have the specified tag
|
|
744
|
-
Object.entries(this.stm).forEach(([key, entry]) => {
|
|
745
|
-
if (entry.attributes.tags?.includes(tag)) {
|
|
746
|
-
// Use get_value to handle template processing
|
|
747
|
-
entries.push([key, this.get_value(key)]);
|
|
748
|
-
}
|
|
749
|
-
});
|
|
750
|
-
return entries
|
|
751
|
-
.map(([key, value]) => `${key}: ${value}`)
|
|
752
|
-
.join(', ');
|
|
753
|
-
}
|
|
754
|
-
// Export STM to Markdown format
|
|
755
|
-
toMarkdown() {
|
|
756
|
-
const now = new Date();
|
|
757
|
-
const lines = [];
|
|
758
|
-
const appendixEntries = [];
|
|
759
|
-
let appendixCounter = 0;
|
|
760
|
-
const appendixLabels = {};
|
|
761
|
-
// Header
|
|
762
|
-
lines.push('# MindCache STM Export');
|
|
763
|
-
lines.push('');
|
|
764
|
-
lines.push(`Export Date: ${now.toISOString().split('T')[0]}`);
|
|
765
|
-
lines.push('');
|
|
766
|
-
lines.push('---');
|
|
767
|
-
lines.push('');
|
|
768
|
-
lines.push('## STM Entries');
|
|
769
|
-
lines.push('');
|
|
770
|
-
// Process each entry
|
|
771
|
-
Object.entries(this.stm).forEach(([key, entry]) => {
|
|
772
|
-
// Skip hardcoded keys - they won't be serialized
|
|
773
|
-
if (entry.attributes.hardcoded) {
|
|
774
|
-
return;
|
|
775
|
-
}
|
|
776
|
-
lines.push(`### ${key}`);
|
|
777
|
-
// Bug fix #1: Ensure type is always 'text' if undefined or the string "undefined"
|
|
778
|
-
const entryType = (entry.attributes.type && entry.attributes.type !== 'undefined') ? entry.attributes.type : 'text';
|
|
779
|
-
lines.push(`- **Type**: \`${entryType}\``);
|
|
780
|
-
// Flatten attributes at the same level as Type
|
|
781
|
-
lines.push(`- **Readonly**: \`${entry.attributes.readonly}\``);
|
|
782
|
-
lines.push(`- **Visible**: \`${entry.attributes.visible}\``);
|
|
783
|
-
lines.push(`- **Template**: \`${entry.attributes.template}\``);
|
|
784
|
-
if (entry.attributes.tags && entry.attributes.tags.length > 0) {
|
|
785
|
-
lines.push(`- **Tags**: \`${entry.attributes.tags.join('`, `')}\``);
|
|
786
|
-
}
|
|
787
|
-
// Handle content type for files/images
|
|
788
|
-
if (entry.attributes.contentType) {
|
|
789
|
-
lines.push(`- **Content Type**: \`${entry.attributes.contentType}\``);
|
|
790
|
-
}
|
|
791
|
-
// Handle value based on type - always use code blocks for text
|
|
792
|
-
if (entryType === 'image' || entryType === 'file') {
|
|
793
|
-
// Create appendix reference
|
|
794
|
-
const label = String.fromCharCode(65 + appendixCounter); // A, B, C, etc.
|
|
795
|
-
appendixLabels[key] = label;
|
|
796
|
-
appendixCounter++;
|
|
797
|
-
lines.push(`- **Value**: [See Appendix ${label}]`);
|
|
798
|
-
// Store for appendix section
|
|
799
|
-
appendixEntries.push({
|
|
800
|
-
key,
|
|
801
|
-
type: entryType,
|
|
802
|
-
contentType: entry.attributes.contentType || 'application/octet-stream',
|
|
803
|
-
base64: entry.value,
|
|
804
|
-
label
|
|
805
|
-
});
|
|
806
|
-
}
|
|
807
|
-
else if (entryType === 'json') {
|
|
808
|
-
// Format JSON with proper indentation
|
|
809
|
-
lines.push('- **Value**:');
|
|
810
|
-
lines.push('```json');
|
|
811
|
-
try {
|
|
812
|
-
const jsonValue = typeof entry.value === 'string' ? entry.value : JSON.stringify(entry.value, null, 2);
|
|
813
|
-
lines.push(jsonValue);
|
|
814
|
-
}
|
|
815
|
-
catch {
|
|
816
|
-
lines.push(String(entry.value));
|
|
817
|
-
}
|
|
818
|
-
lines.push('```');
|
|
819
|
-
}
|
|
820
|
-
else {
|
|
821
|
-
// Text type - always use code blocks
|
|
822
|
-
const valueStr = String(entry.value);
|
|
823
|
-
lines.push('- **Value**:');
|
|
824
|
-
lines.push('```');
|
|
825
|
-
lines.push(valueStr);
|
|
826
|
-
lines.push('```');
|
|
827
|
-
}
|
|
828
|
-
lines.push('');
|
|
829
|
-
lines.push('---');
|
|
830
|
-
lines.push('');
|
|
831
|
-
});
|
|
832
|
-
// Add appendix section if there are binary entries
|
|
833
|
-
if (appendixEntries.length > 0) {
|
|
834
|
-
lines.push('## Appendix: Binary Data');
|
|
835
|
-
lines.push('');
|
|
836
|
-
appendixEntries.forEach(({ key, contentType, base64, label }) => {
|
|
837
|
-
lines.push(`### Appendix ${label}: ${key}`);
|
|
838
|
-
lines.push(`**Type**: ${contentType}`);
|
|
839
|
-
lines.push('');
|
|
840
|
-
lines.push('```');
|
|
841
|
-
lines.push(base64);
|
|
842
|
-
lines.push('```');
|
|
843
|
-
lines.push('');
|
|
844
|
-
lines.push('---');
|
|
845
|
-
lines.push('');
|
|
846
|
-
});
|
|
847
|
-
}
|
|
848
|
-
lines.push('*End of MindCache Export*');
|
|
849
|
-
return lines.join('\n');
|
|
850
|
-
}
|
|
851
|
-
// Import STM from Markdown format
|
|
852
|
-
fromMarkdown(markdown) {
|
|
853
|
-
const lines = markdown.split('\n');
|
|
854
|
-
let currentSection = 'header';
|
|
855
|
-
let currentKey = null;
|
|
856
|
-
let currentEntry = null;
|
|
857
|
-
let inCodeBlock = false;
|
|
858
|
-
let codeBlockContent = [];
|
|
859
|
-
let codeBlockType = null;
|
|
860
|
-
const appendixData = {};
|
|
861
|
-
let currentAppendixKey = null;
|
|
862
|
-
const pendingEntries = {};
|
|
863
|
-
// Clear existing STM first
|
|
864
|
-
this.clear();
|
|
865
|
-
for (let i = 0; i < lines.length; i++) {
|
|
866
|
-
const line = lines[i];
|
|
867
|
-
const trimmed = line.trim();
|
|
868
|
-
// Detect sections
|
|
869
|
-
if (trimmed === '## STM Entries') {
|
|
870
|
-
currentSection = 'entries';
|
|
871
|
-
continue;
|
|
872
|
-
}
|
|
873
|
-
if (trimmed === '## Appendix: Binary Data') {
|
|
874
|
-
currentSection = 'appendix';
|
|
875
|
-
continue;
|
|
876
|
-
}
|
|
877
|
-
// Handle code blocks
|
|
878
|
-
if (trimmed === '```' || trimmed === '```json') {
|
|
879
|
-
if (!inCodeBlock) {
|
|
880
|
-
inCodeBlock = true;
|
|
881
|
-
codeBlockContent = [];
|
|
882
|
-
codeBlockType = currentSection === 'appendix' ? 'base64' : (trimmed === '```json' ? 'json' : 'value');
|
|
883
|
-
}
|
|
884
|
-
else {
|
|
885
|
-
inCodeBlock = false;
|
|
886
|
-
const content = codeBlockContent.join('\n');
|
|
887
|
-
if (currentSection === 'appendix' && currentAppendixKey) {
|
|
888
|
-
// Store appendix base64 data
|
|
889
|
-
appendixData[currentAppendixKey].base64 = content;
|
|
890
|
-
}
|
|
891
|
-
else if (currentEntry && codeBlockType === 'json') {
|
|
892
|
-
currentEntry.value = content;
|
|
893
|
-
}
|
|
894
|
-
else if (currentEntry && codeBlockType === 'value') {
|
|
895
|
-
currentEntry.value = content;
|
|
896
|
-
}
|
|
897
|
-
codeBlockContent = [];
|
|
898
|
-
codeBlockType = null;
|
|
899
|
-
}
|
|
900
|
-
continue;
|
|
901
|
-
}
|
|
902
|
-
if (inCodeBlock) {
|
|
903
|
-
codeBlockContent.push(line);
|
|
904
|
-
continue;
|
|
905
|
-
}
|
|
906
|
-
// Parse entries section
|
|
907
|
-
if (currentSection === 'entries') {
|
|
908
|
-
if (trimmed.startsWith('### ')) {
|
|
909
|
-
// Save previous entry if exists
|
|
910
|
-
if (currentKey && currentEntry && currentEntry.attributes) {
|
|
911
|
-
pendingEntries[currentKey] = currentEntry;
|
|
912
|
-
}
|
|
913
|
-
// Start new entry
|
|
914
|
-
currentKey = trimmed.substring(4);
|
|
915
|
-
currentEntry = {
|
|
916
|
-
value: undefined,
|
|
917
|
-
attributes: {
|
|
918
|
-
readonly: false,
|
|
919
|
-
visible: true,
|
|
920
|
-
hardcoded: false,
|
|
921
|
-
template: false,
|
|
922
|
-
type: 'text',
|
|
923
|
-
tags: []
|
|
924
|
-
}
|
|
925
|
-
};
|
|
926
|
-
}
|
|
927
|
-
else if (trimmed.startsWith('- **Type**: `')) {
|
|
928
|
-
const type = trimmed.match(/`([^`]+)`/)?.[1];
|
|
929
|
-
// Don't store "undefined" string - treat it as missing and keep default 'text'
|
|
930
|
-
if (currentEntry && type && type !== 'undefined') {
|
|
931
|
-
currentEntry.attributes.type = type;
|
|
932
|
-
}
|
|
933
|
-
}
|
|
934
|
-
else if (trimmed.startsWith('- **Readonly**: `')) {
|
|
935
|
-
const value = trimmed.match(/`([^`]+)`/)?.[1] === 'true';
|
|
936
|
-
if (currentEntry) {
|
|
937
|
-
currentEntry.attributes.readonly = value;
|
|
938
|
-
}
|
|
939
|
-
}
|
|
940
|
-
else if (trimmed.startsWith('- **Visible**: `')) {
|
|
941
|
-
const value = trimmed.match(/`([^`]+)`/)?.[1] === 'true';
|
|
942
|
-
if (currentEntry) {
|
|
943
|
-
currentEntry.attributes.visible = value;
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
|
-
else if (trimmed.startsWith('- **Template**: `')) {
|
|
947
|
-
const value = trimmed.match(/`([^`]+)`/)?.[1] === 'true';
|
|
948
|
-
if (currentEntry) {
|
|
949
|
-
currentEntry.attributes.template = value;
|
|
950
|
-
}
|
|
951
|
-
}
|
|
952
|
-
else if (trimmed.startsWith('- **Tags**: `')) {
|
|
953
|
-
const tagsStr = trimmed.substring(13, trimmed.length - 1);
|
|
954
|
-
if (currentEntry) {
|
|
955
|
-
currentEntry.attributes.tags = tagsStr.split('`, `');
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
else if (trimmed.startsWith('- **Content Type**: `')) {
|
|
959
|
-
const contentType = trimmed.match(/`([^`]+)`/)?.[1];
|
|
960
|
-
if (currentEntry && contentType) {
|
|
961
|
-
currentEntry.attributes.contentType = contentType;
|
|
962
|
-
}
|
|
963
|
-
}
|
|
964
|
-
else if (trimmed.startsWith('- **Value**: `')) {
|
|
965
|
-
const value = trimmed.substring(14, trimmed.length - 1);
|
|
966
|
-
if (currentEntry) {
|
|
967
|
-
currentEntry.value = value;
|
|
968
|
-
}
|
|
969
|
-
}
|
|
970
|
-
else if (trimmed.startsWith('- **Value**: [See Appendix ')) {
|
|
971
|
-
const labelMatch = trimmed.match(/Appendix ([A-Z])\]/);
|
|
972
|
-
if (currentEntry && labelMatch && currentKey) {
|
|
973
|
-
currentEntry.appendixLabel = labelMatch[1];
|
|
974
|
-
// Set a placeholder value so the entry is saved
|
|
975
|
-
currentEntry.value = '';
|
|
976
|
-
}
|
|
977
|
-
}
|
|
978
|
-
}
|
|
979
|
-
// Parse appendix section
|
|
980
|
-
if (currentSection === 'appendix') {
|
|
981
|
-
if (trimmed.startsWith('### Appendix ')) {
|
|
982
|
-
const match = trimmed.match(/### Appendix ([A-Z]): (.+)/);
|
|
983
|
-
if (match) {
|
|
984
|
-
const label = match[1];
|
|
985
|
-
const key = match[2];
|
|
986
|
-
currentAppendixKey = `${label}:${key}`;
|
|
987
|
-
appendixData[currentAppendixKey] = { contentType: '', base64: '' };
|
|
988
|
-
}
|
|
989
|
-
}
|
|
990
|
-
else if (trimmed.startsWith('**Type**: ')) {
|
|
991
|
-
const contentType = trimmed.substring(10);
|
|
992
|
-
if (currentAppendixKey) {
|
|
993
|
-
appendixData[currentAppendixKey].contentType = contentType;
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
}
|
|
997
|
-
}
|
|
998
|
-
// Save last entry
|
|
999
|
-
if (currentKey && currentEntry && currentEntry.attributes) {
|
|
1000
|
-
pendingEntries[currentKey] = currentEntry;
|
|
1001
|
-
}
|
|
1002
|
-
// Now combine entries with appendix data and populate STM
|
|
1003
|
-
Object.entries(pendingEntries).forEach(([key, entry]) => {
|
|
1004
|
-
const appendixLabel = entry.appendixLabel;
|
|
1005
|
-
if (appendixLabel) {
|
|
1006
|
-
// Find matching appendix data
|
|
1007
|
-
const appendixKey = `${appendixLabel}:${key}`;
|
|
1008
|
-
const appendixInfo = appendixData[appendixKey];
|
|
1009
|
-
if (appendixInfo && appendixInfo.base64) {
|
|
1010
|
-
entry.value = appendixInfo.base64;
|
|
1011
|
-
if (!entry.attributes.contentType && appendixInfo.contentType) {
|
|
1012
|
-
entry.attributes.contentType = appendixInfo.contentType;
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
}
|
|
1016
|
-
// Set the entry in STM (value can be undefined for entries without value line)
|
|
1017
|
-
if (entry.value !== undefined && entry.attributes) {
|
|
1018
|
-
this.stm[key] = {
|
|
1019
|
-
value: entry.value,
|
|
1020
|
-
attributes: entry.attributes
|
|
1021
|
-
};
|
|
1022
|
-
}
|
|
1023
|
-
});
|
|
1024
|
-
this.notifyGlobalListeners();
|
|
1025
|
-
}
|
|
1026
|
-
}
|
|
1027
|
-
exports.MindCache = MindCache;
|
|
1028
|
-
// Create and export a single instance of MindCache
|
|
1029
|
-
exports.mindcache = new MindCache();
|
|
1030
|
-
//# sourceMappingURL=index.js.map
|