mindcache 3.5.3 → 3.7.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/README.md +29 -4
- package/dist/{CloudAdapter-DK4YecbV.d.mts → CloudAdapter-PLGvGjoA.d.mts} +86 -7
- package/dist/{CloudAdapter-DK4YecbV.d.ts → CloudAdapter-PLGvGjoA.d.ts} +86 -7
- package/dist/cloud/index.d.mts +2 -2
- package/dist/cloud/index.d.ts +2 -2
- package/dist/cloud/index.js +379 -67
- package/dist/cloud/index.js.map +1 -1
- package/dist/cloud/index.mjs +379 -67
- package/dist/cloud/index.mjs.map +1 -1
- package/dist/index.d.mts +417 -19
- package/dist/index.d.ts +417 -19
- package/dist/index.js +1444 -105
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1433 -101
- package/dist/index.mjs.map +1 -1
- package/dist/server.d.mts +2 -2
- package/dist/server.d.ts +2 -2
- package/dist/server.js +379 -67
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +379 -67
- package/dist/server.mjs.map +1 -1
- package/package.json +5 -9
package/dist/index.js
CHANGED
|
@@ -6,7 +6,11 @@ var decoding = require('lib0/decoding');
|
|
|
6
6
|
var Y = require('yjs');
|
|
7
7
|
var yIndexeddb = require('y-indexeddb');
|
|
8
8
|
var diff = require('fast-diff');
|
|
9
|
-
var
|
|
9
|
+
var ai = require('ai');
|
|
10
|
+
var zod = require('zod');
|
|
11
|
+
var React2 = require('react');
|
|
12
|
+
var openai = require('@ai-sdk/openai');
|
|
13
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
10
14
|
|
|
11
15
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
12
16
|
|
|
@@ -33,6 +37,7 @@ var encoding__namespace = /*#__PURE__*/_interopNamespace(encoding);
|
|
|
33
37
|
var decoding__namespace = /*#__PURE__*/_interopNamespace(decoding);
|
|
34
38
|
var Y__namespace = /*#__PURE__*/_interopNamespace(Y);
|
|
35
39
|
var diff__default = /*#__PURE__*/_interopDefault(diff);
|
|
40
|
+
var React2__default = /*#__PURE__*/_interopDefault(React2);
|
|
36
41
|
|
|
37
42
|
var __defProp = Object.defineProperty;
|
|
38
43
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -212,7 +217,6 @@ var init_CloudAdapter = __esm({
|
|
|
212
217
|
if (!config.baseUrl) {
|
|
213
218
|
throw new Error("MindCache Cloud: baseUrl is required. Please provide the cloud API URL in your configuration.");
|
|
214
219
|
}
|
|
215
|
-
this.validateConfig(config);
|
|
216
220
|
this.setupNetworkDetection();
|
|
217
221
|
}
|
|
218
222
|
ws = null;
|
|
@@ -228,43 +232,6 @@ var init_CloudAdapter = __esm({
|
|
|
228
232
|
handleOnline = null;
|
|
229
233
|
handleOffline = null;
|
|
230
234
|
_synced = false;
|
|
231
|
-
/**
|
|
232
|
-
* Validate configuration and warn about common mistakes
|
|
233
|
-
*/
|
|
234
|
-
validateConfig(config) {
|
|
235
|
-
const baseUrl = config.baseUrl;
|
|
236
|
-
if (!baseUrl) {
|
|
237
|
-
return;
|
|
238
|
-
}
|
|
239
|
-
console.log("\u2601\uFE0F MindCache Cloud Config:", {
|
|
240
|
-
baseUrl,
|
|
241
|
-
instanceId: config.instanceId,
|
|
242
|
-
hasTokenProvider: !!config.tokenProvider,
|
|
243
|
-
hasApiKey: !!config.apiKey
|
|
244
|
-
});
|
|
245
|
-
try {
|
|
246
|
-
const url = new URL(baseUrl);
|
|
247
|
-
if (url.hostname === "mindcache.dev") {
|
|
248
|
-
console.error(
|
|
249
|
-
'\u26A0\uFE0F MindCache Cloud WARNING: baseUrl is set to "mindcache.dev" but the API is at "api.mindcache.dev".\n Current: ' + baseUrl + "\n Expected: https://api.mindcache.dev\n This will cause WebSocket connection failures (404 errors)."
|
|
250
|
-
);
|
|
251
|
-
}
|
|
252
|
-
if (url.protocol === "ws:" || url.protocol === "wss:") {
|
|
253
|
-
console.warn(
|
|
254
|
-
"\u26A0\uFE0F MindCache Cloud: baseUrl uses WebSocket protocol (" + url.protocol + "). Consider using http:// or https:// - CloudAdapter will handle the WebSocket upgrade automatically."
|
|
255
|
-
);
|
|
256
|
-
}
|
|
257
|
-
if (url.hostname === "localhost" && url.port !== "8787" && url.port !== "3000") {
|
|
258
|
-
console.warn(
|
|
259
|
-
"\u26A0\uFE0F MindCache Cloud: localhost URL detected with non-standard port " + url.port + ". Default MindCache dev server runs on port 8787."
|
|
260
|
-
);
|
|
261
|
-
}
|
|
262
|
-
const wsUrl = baseUrl.replace("https://", "wss://").replace("http://", "ws://");
|
|
263
|
-
console.log("\u2601\uFE0F WebSocket will connect to:", wsUrl + "/sync/" + config.instanceId);
|
|
264
|
-
} catch (e) {
|
|
265
|
-
console.error("\u26A0\uFE0F MindCache Cloud: Invalid baseUrl format:", baseUrl);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
235
|
/** Browser network status - instantly updated via navigator.onLine */
|
|
269
236
|
get isOnline() {
|
|
270
237
|
return this._isOnline;
|
|
@@ -517,6 +484,76 @@ var SystemTagHelpers = {
|
|
|
517
484
|
hasTemplateInjection: (attrs) => attrs.systemTags.includes("ApplyTemplate")
|
|
518
485
|
};
|
|
519
486
|
|
|
487
|
+
// src/core/SchemaParser.ts
|
|
488
|
+
var SchemaParser = class {
|
|
489
|
+
/**
|
|
490
|
+
* Parse a markdown schema string into a CustomTypeDefinition
|
|
491
|
+
* @param schema - Markdown schema string
|
|
492
|
+
* @returns Parsed type definition
|
|
493
|
+
* @throws Error if schema format is invalid
|
|
494
|
+
*/
|
|
495
|
+
static parse(schema) {
|
|
496
|
+
const lines = schema.trim().split("\n").map((line) => line.trim()).filter((line) => line.length > 0);
|
|
497
|
+
if (lines.length === 0) {
|
|
498
|
+
throw new Error("Schema cannot be empty");
|
|
499
|
+
}
|
|
500
|
+
const typeNameMatch = lines[0].match(/^#\s*(\w+)$/);
|
|
501
|
+
if (!typeNameMatch) {
|
|
502
|
+
throw new Error(`Invalid schema: first line must be "#TypeName", got "${lines[0]}"`);
|
|
503
|
+
}
|
|
504
|
+
const typeName = typeNameMatch[1];
|
|
505
|
+
const fields = [];
|
|
506
|
+
for (let i = 1; i < lines.length; i++) {
|
|
507
|
+
const line = lines[i];
|
|
508
|
+
const fieldMatch = line.match(/^\*\s*([^:]+):\s*(.+)$/);
|
|
509
|
+
if (fieldMatch) {
|
|
510
|
+
fields.push({
|
|
511
|
+
name: fieldMatch[1].trim(),
|
|
512
|
+
description: fieldMatch[2].trim()
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
if (fields.length === 0) {
|
|
517
|
+
throw new Error(`Schema "${typeName}" must have at least one field`);
|
|
518
|
+
}
|
|
519
|
+
return {
|
|
520
|
+
name: typeName,
|
|
521
|
+
fields,
|
|
522
|
+
rawSchema: schema
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Generate a markdown representation of a type definition
|
|
527
|
+
* Useful for including in LLM prompts
|
|
528
|
+
*/
|
|
529
|
+
static toMarkdown(typeDef) {
|
|
530
|
+
const lines = [`#${typeDef.name}`];
|
|
531
|
+
for (const field of typeDef.fields) {
|
|
532
|
+
lines.push(`* ${field.name}: ${field.description}`);
|
|
533
|
+
}
|
|
534
|
+
return lines.join("\n");
|
|
535
|
+
}
|
|
536
|
+
/**
|
|
537
|
+
* Generate a prompt-friendly description of the type
|
|
538
|
+
* More verbose than toMarkdown, better for LLM guidance
|
|
539
|
+
*/
|
|
540
|
+
static toPromptDescription(typeDef) {
|
|
541
|
+
const fieldDescs = typeDef.fields.map((f) => ` - ${f.name}: ${f.description}`).join("\n");
|
|
542
|
+
return `Type "${typeDef.name}" with fields:
|
|
543
|
+
${fieldDescs}`;
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* Generate an example value structure based on the type definition
|
|
547
|
+
*/
|
|
548
|
+
static generateExample(typeDef) {
|
|
549
|
+
const lines = [`#${typeDef.name.toLowerCase()}`];
|
|
550
|
+
for (const field of typeDef.fields) {
|
|
551
|
+
lines.push(`* ${field.name}: [${field.description}]`);
|
|
552
|
+
}
|
|
553
|
+
return lines.join("\n");
|
|
554
|
+
}
|
|
555
|
+
};
|
|
556
|
+
|
|
520
557
|
// src/core/MarkdownSerializer.ts
|
|
521
558
|
var MarkdownSerializer = class {
|
|
522
559
|
/**
|
|
@@ -863,18 +900,16 @@ var MarkdownSerializer = class {
|
|
|
863
900
|
return result;
|
|
864
901
|
}
|
|
865
902
|
};
|
|
866
|
-
|
|
867
|
-
// src/core/AIToolBuilder.ts
|
|
868
903
|
var AIToolBuilder = class _AIToolBuilder {
|
|
869
904
|
/**
|
|
870
|
-
|
|
871
|
-
|
|
905
|
+
* Sanitize key name for use in tool names
|
|
906
|
+
*/
|
|
872
907
|
static sanitizeKeyForTool(key) {
|
|
873
908
|
return key.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
874
909
|
}
|
|
875
910
|
/**
|
|
876
|
-
|
|
877
|
-
|
|
911
|
+
* Find original key from sanitized tool name
|
|
912
|
+
*/
|
|
878
913
|
static findKeyFromSanitizedTool(mc, sanitizedKey) {
|
|
879
914
|
for (const key of mc.keys()) {
|
|
880
915
|
if (_AIToolBuilder.sanitizeKeyForTool(key) === sanitizedKey) {
|
|
@@ -884,15 +919,174 @@ var AIToolBuilder = class _AIToolBuilder {
|
|
|
884
919
|
return void 0;
|
|
885
920
|
}
|
|
886
921
|
/**
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
922
|
+
* Generate framework-agnostic tools with raw JSON Schema.
|
|
923
|
+
* Works with: OpenAI SDK, Anthropic SDK, LangChain, etc.
|
|
924
|
+
*
|
|
925
|
+
* Tool format:
|
|
926
|
+
* {
|
|
927
|
+
* description: string,
|
|
928
|
+
* parameters: { type: 'object', properties: {...}, required: [...] },
|
|
929
|
+
* execute: async (args) => result
|
|
930
|
+
* }
|
|
931
|
+
*/
|
|
932
|
+
static createTools(mc) {
|
|
933
|
+
return _AIToolBuilder._buildTools(mc);
|
|
934
|
+
}
|
|
935
|
+
/**
|
|
936
|
+
* Generate Vercel AI SDK v5 compatible tools using Zod schemas.
|
|
937
|
+
* Uses tool() helper with Zod for full AI SDK v5 compatibility.
|
|
938
|
+
*
|
|
939
|
+
* Use this with: generateText(), streamText() from 'ai' package
|
|
940
|
+
*/
|
|
894
941
|
static createVercelAITools(mc) {
|
|
895
942
|
const tools = {};
|
|
943
|
+
const registeredTypes = mc.getRegisteredTypes();
|
|
944
|
+
const typeDesc = registeredTypes.length > 0 ? `Optional type: ${registeredTypes.join(" | ")}` : "No types registered";
|
|
945
|
+
tools["create_key"] = ai.tool({
|
|
946
|
+
description: `Create a new key in MindCache. ${registeredTypes.length > 0 ? `Available types: ${registeredTypes.join(", ")}` : ""}`,
|
|
947
|
+
inputSchema: zod.z.object({
|
|
948
|
+
key: zod.z.string().describe('The key name (e.g., "contact_john_doe")'),
|
|
949
|
+
value: zod.z.string().describe("The value (JSON string for structured data)"),
|
|
950
|
+
type: zod.z.string().optional().describe(typeDesc)
|
|
951
|
+
}),
|
|
952
|
+
execute: async ({ key, value, type }) => {
|
|
953
|
+
if (mc.has(key)) {
|
|
954
|
+
return { result: `Key "${key}" exists. Use write_${_AIToolBuilder.sanitizeKeyForTool(key)}`, error: true };
|
|
955
|
+
}
|
|
956
|
+
if (type && !mc.getTypeSchema(type)) {
|
|
957
|
+
return { result: `Type "${type}" not registered`, error: true };
|
|
958
|
+
}
|
|
959
|
+
mc.set_value(key, value, { systemTags: ["SystemPrompt", "LLMRead", "LLMWrite"] });
|
|
960
|
+
if (type) {
|
|
961
|
+
mc.setType(key, type);
|
|
962
|
+
}
|
|
963
|
+
return { result: `Created "${key}"${type ? ` (${type})` : ""}`, key, value };
|
|
964
|
+
}
|
|
965
|
+
});
|
|
966
|
+
for (const key of mc.keys()) {
|
|
967
|
+
if (key.startsWith("$") || !mc.keyMatchesContext(key)) {
|
|
968
|
+
continue;
|
|
969
|
+
}
|
|
970
|
+
const attrs = mc.get_attributes(key);
|
|
971
|
+
if (!attrs?.systemTags?.includes("LLMWrite")) {
|
|
972
|
+
continue;
|
|
973
|
+
}
|
|
974
|
+
const sanitized = _AIToolBuilder.sanitizeKeyForTool(key);
|
|
975
|
+
const customTypeName = mc.getKeyType(key);
|
|
976
|
+
const customType = customTypeName ? mc.getTypeSchema(customTypeName) : void 0;
|
|
977
|
+
let desc = `Write to "${key}"`;
|
|
978
|
+
if (customType) {
|
|
979
|
+
desc = `Write to "${key}" (${customTypeName}). ${SchemaParser.toPromptDescription(customType)}`;
|
|
980
|
+
}
|
|
981
|
+
tools[`write_${sanitized}`] = ai.tool({
|
|
982
|
+
description: desc,
|
|
983
|
+
inputSchema: zod.z.object({
|
|
984
|
+
value: zod.z.string().describe(customType ? `JSON following ${customTypeName} schema` : "Value to write")
|
|
985
|
+
}),
|
|
986
|
+
execute: async ({ value }) => {
|
|
987
|
+
const success = mc.llm_set_key(key, value);
|
|
988
|
+
return success ? { result: `Wrote to ${key}`, key, value } : { result: `Failed to write to ${key}`, error: true };
|
|
989
|
+
}
|
|
990
|
+
});
|
|
991
|
+
if (attrs?.type === "document") {
|
|
992
|
+
tools[`append_${sanitized}`] = ai.tool({
|
|
993
|
+
description: `Append to "${key}" document`,
|
|
994
|
+
inputSchema: zod.z.object({ text: zod.z.string().describe("Text to append") }),
|
|
995
|
+
execute: async ({ text }) => {
|
|
996
|
+
const yText = mc.get_document(key);
|
|
997
|
+
if (yText) {
|
|
998
|
+
yText.insert(yText.length, text);
|
|
999
|
+
return { result: "Appended", key };
|
|
1000
|
+
}
|
|
1001
|
+
return { result: "Not found", error: true };
|
|
1002
|
+
}
|
|
1003
|
+
});
|
|
1004
|
+
tools[`insert_${sanitized}`] = ai.tool({
|
|
1005
|
+
description: `Insert text at position in "${key}" document`,
|
|
1006
|
+
inputSchema: zod.z.object({
|
|
1007
|
+
index: zod.z.number().describe("Position (0 = start)"),
|
|
1008
|
+
text: zod.z.string().describe("Text to insert")
|
|
1009
|
+
}),
|
|
1010
|
+
execute: async ({ index, text }) => {
|
|
1011
|
+
mc.insert_text(key, index, text);
|
|
1012
|
+
return { result: `Inserted at ${index}`, key };
|
|
1013
|
+
}
|
|
1014
|
+
});
|
|
1015
|
+
tools[`edit_${sanitized}`] = ai.tool({
|
|
1016
|
+
description: `Find and replace in "${key}" document`,
|
|
1017
|
+
inputSchema: zod.z.object({
|
|
1018
|
+
find: zod.z.string().describe("Text to find"),
|
|
1019
|
+
replace: zod.z.string().describe("Replacement")
|
|
1020
|
+
}),
|
|
1021
|
+
execute: async ({ find, replace }) => {
|
|
1022
|
+
const yText = mc.get_document(key);
|
|
1023
|
+
if (yText) {
|
|
1024
|
+
const text = yText.toString();
|
|
1025
|
+
const idx = text.indexOf(find);
|
|
1026
|
+
if (idx !== -1) {
|
|
1027
|
+
yText.delete(idx, find.length);
|
|
1028
|
+
yText.insert(idx, replace);
|
|
1029
|
+
return { result: `Replaced "${find}"`, key };
|
|
1030
|
+
}
|
|
1031
|
+
return { result: `"${find}" not found`, error: true };
|
|
1032
|
+
}
|
|
1033
|
+
return { result: "Document not found", error: true };
|
|
1034
|
+
}
|
|
1035
|
+
});
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
return tools;
|
|
1039
|
+
}
|
|
1040
|
+
/**
|
|
1041
|
+
* Internal: Build tools with raw JSON Schema (framework-agnostic).
|
|
1042
|
+
*/
|
|
1043
|
+
static _buildTools(mc) {
|
|
1044
|
+
const tools = {};
|
|
1045
|
+
const registeredTypes = mc.getRegisteredTypes();
|
|
1046
|
+
const typeInfo = registeredTypes.length > 0 ? `Available types: ${registeredTypes.join(", ")}` : "No custom types registered";
|
|
1047
|
+
tools["create_key"] = {
|
|
1048
|
+
description: `Create a new key in MindCache. ${typeInfo}. The new key will be readable and writable by the LLM.`,
|
|
1049
|
+
parameters: {
|
|
1050
|
+
type: "object",
|
|
1051
|
+
properties: {
|
|
1052
|
+
key: { type: "string", description: 'The key name to create (e.g., "contact_john_doe")' },
|
|
1053
|
+
value: { type: "string", description: "The value to store (use JSON string for structured data)" },
|
|
1054
|
+
type: {
|
|
1055
|
+
type: "string",
|
|
1056
|
+
description: registeredTypes.length > 0 ? `Optional: custom type name (${registeredTypes.join(" | ")})` : "Optional: custom type name (none registered)"
|
|
1057
|
+
}
|
|
1058
|
+
},
|
|
1059
|
+
required: ["key", "value"]
|
|
1060
|
+
},
|
|
1061
|
+
execute: async ({ key, value, type }) => {
|
|
1062
|
+
if (mc.has(key)) {
|
|
1063
|
+
return {
|
|
1064
|
+
result: `Key "${key}" already exists. Use write_${_AIToolBuilder.sanitizeKeyForTool(key)} to update it.`,
|
|
1065
|
+
key,
|
|
1066
|
+
error: true
|
|
1067
|
+
};
|
|
1068
|
+
}
|
|
1069
|
+
if (type && !mc.getTypeSchema(type)) {
|
|
1070
|
+
return {
|
|
1071
|
+
result: `Type "${type}" is not registered. Available types: ${registeredTypes.join(", ") || "none"}`,
|
|
1072
|
+
key,
|
|
1073
|
+
error: true
|
|
1074
|
+
};
|
|
1075
|
+
}
|
|
1076
|
+
mc.set_value(key, value, {
|
|
1077
|
+
systemTags: ["SystemPrompt", "LLMRead", "LLMWrite"]
|
|
1078
|
+
});
|
|
1079
|
+
if (type) {
|
|
1080
|
+
mc.setType(key, type);
|
|
1081
|
+
}
|
|
1082
|
+
return {
|
|
1083
|
+
result: `Successfully created key "${key}"${type ? ` with type "${type}"` : ""}`,
|
|
1084
|
+
key,
|
|
1085
|
+
value,
|
|
1086
|
+
type
|
|
1087
|
+
};
|
|
1088
|
+
}
|
|
1089
|
+
};
|
|
896
1090
|
for (const key of mc.keys()) {
|
|
897
1091
|
if (key.startsWith("$")) {
|
|
898
1092
|
continue;
|
|
@@ -907,12 +1101,28 @@ var AIToolBuilder = class _AIToolBuilder {
|
|
|
907
1101
|
}
|
|
908
1102
|
const sanitizedKey = _AIToolBuilder.sanitizeKeyForTool(key);
|
|
909
1103
|
const isDocument = attributes?.type === "document";
|
|
1104
|
+
const customTypeName = mc.getKeyType(key);
|
|
1105
|
+
const customType = customTypeName ? mc.getTypeSchema(customTypeName) : void 0;
|
|
1106
|
+
let writeDescription;
|
|
1107
|
+
if (customType) {
|
|
1108
|
+
const schemaGuidance = SchemaParser.toPromptDescription(customType);
|
|
1109
|
+
const example = SchemaParser.generateExample(customType);
|
|
1110
|
+
writeDescription = `Write a value to "${key}" that must follow this schema:
|
|
1111
|
+
${schemaGuidance}
|
|
1112
|
+
|
|
1113
|
+
Example format:
|
|
1114
|
+
${example}`;
|
|
1115
|
+
} else if (isDocument) {
|
|
1116
|
+
writeDescription = `Rewrite the entire "${key}" document`;
|
|
1117
|
+
} else {
|
|
1118
|
+
writeDescription = `Write a value to the STM key: ${key}`;
|
|
1119
|
+
}
|
|
910
1120
|
tools[`write_${sanitizedKey}`] = {
|
|
911
|
-
description:
|
|
912
|
-
|
|
1121
|
+
description: writeDescription,
|
|
1122
|
+
parameters: {
|
|
913
1123
|
type: "object",
|
|
914
1124
|
properties: {
|
|
915
|
-
value: { type: "string", description: isDocument ? "New document content" : "The value to write" }
|
|
1125
|
+
value: { type: "string", description: customType ? `Value following ${customTypeName} schema` : isDocument ? "New document content" : "The value to write" }
|
|
916
1126
|
},
|
|
917
1127
|
required: ["value"]
|
|
918
1128
|
},
|
|
@@ -935,7 +1145,7 @@ var AIToolBuilder = class _AIToolBuilder {
|
|
|
935
1145
|
if (isDocument) {
|
|
936
1146
|
tools[`append_${sanitizedKey}`] = {
|
|
937
1147
|
description: `Append text to the end of "${key}" document`,
|
|
938
|
-
|
|
1148
|
+
parameters: {
|
|
939
1149
|
type: "object",
|
|
940
1150
|
properties: {
|
|
941
1151
|
text: { type: "string", description: "Text to append" }
|
|
@@ -960,7 +1170,7 @@ var AIToolBuilder = class _AIToolBuilder {
|
|
|
960
1170
|
};
|
|
961
1171
|
tools[`insert_${sanitizedKey}`] = {
|
|
962
1172
|
description: `Insert text at a position in "${key}" document`,
|
|
963
|
-
|
|
1173
|
+
parameters: {
|
|
964
1174
|
type: "object",
|
|
965
1175
|
properties: {
|
|
966
1176
|
index: { type: "number", description: "Position to insert at (0 = start)" },
|
|
@@ -983,7 +1193,7 @@ var AIToolBuilder = class _AIToolBuilder {
|
|
|
983
1193
|
};
|
|
984
1194
|
tools[`edit_${sanitizedKey}`] = {
|
|
985
1195
|
description: `Find and replace text in "${key}" document`,
|
|
986
|
-
|
|
1196
|
+
parameters: {
|
|
987
1197
|
type: "object",
|
|
988
1198
|
properties: {
|
|
989
1199
|
find: { type: "string", description: "Text to find" },
|
|
@@ -1020,11 +1230,16 @@ var AIToolBuilder = class _AIToolBuilder {
|
|
|
1020
1230
|
return tools;
|
|
1021
1231
|
}
|
|
1022
1232
|
/**
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1233
|
+
* Generate a system prompt containing all visible STM keys and their values.
|
|
1234
|
+
* Indicates which tools can be used to modify writable keys.
|
|
1235
|
+
*/
|
|
1026
1236
|
static getSystemPrompt(mc) {
|
|
1027
1237
|
const lines = [];
|
|
1238
|
+
const registeredTypes = mc.getRegisteredTypes();
|
|
1239
|
+
if (registeredTypes.length > 0) {
|
|
1240
|
+
lines.push(`[create_key tool available - registered types: ${registeredTypes.join(", ")}]`);
|
|
1241
|
+
lines.push("");
|
|
1242
|
+
}
|
|
1028
1243
|
for (const key of mc.keys()) {
|
|
1029
1244
|
if (key.startsWith("$")) {
|
|
1030
1245
|
continue;
|
|
@@ -1042,8 +1257,18 @@ var AIToolBuilder = class _AIToolBuilder {
|
|
|
1042
1257
|
const isWritable = attributes?.systemTags?.includes("LLMWrite");
|
|
1043
1258
|
const isDocument = attributes?.type === "document";
|
|
1044
1259
|
const sanitizedKey = _AIToolBuilder.sanitizeKeyForTool(key);
|
|
1260
|
+
const customTypeName = mc.getKeyType(key);
|
|
1261
|
+
const customType = customTypeName ? mc.getTypeSchema(customTypeName) : void 0;
|
|
1045
1262
|
if (isWritable) {
|
|
1046
|
-
if (
|
|
1263
|
+
if (customType) {
|
|
1264
|
+
const schemaInfo = SchemaParser.toMarkdown(customType);
|
|
1265
|
+
lines.push(
|
|
1266
|
+
`${key} (type: ${customTypeName}): ${displayValue}
|
|
1267
|
+
Schema:
|
|
1268
|
+
${schemaInfo}
|
|
1269
|
+
Tool: write_${sanitizedKey}`
|
|
1270
|
+
);
|
|
1271
|
+
} else if (isDocument) {
|
|
1047
1272
|
lines.push(
|
|
1048
1273
|
`${key}: ${displayValue}. Document tools: write_${sanitizedKey}, append_${sanitizedKey}, edit_${sanitizedKey}`
|
|
1049
1274
|
);
|
|
@@ -1054,15 +1279,19 @@ var AIToolBuilder = class _AIToolBuilder {
|
|
|
1054
1279
|
);
|
|
1055
1280
|
}
|
|
1056
1281
|
} else {
|
|
1057
|
-
|
|
1282
|
+
if (customTypeName) {
|
|
1283
|
+
lines.push(`${key} (type: ${customTypeName}): ${displayValue}`);
|
|
1284
|
+
} else {
|
|
1285
|
+
lines.push(`${key}: ${displayValue}`);
|
|
1286
|
+
}
|
|
1058
1287
|
}
|
|
1059
1288
|
}
|
|
1060
1289
|
return lines.join("\n");
|
|
1061
1290
|
}
|
|
1062
1291
|
/**
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1292
|
+
* Execute a tool call by name with the given value.
|
|
1293
|
+
* Returns the result or null if tool not found.
|
|
1294
|
+
*/
|
|
1066
1295
|
static executeToolCall(mc, toolName, value) {
|
|
1067
1296
|
const match = toolName.match(/^(write|append|insert|edit)_(.+)$/);
|
|
1068
1297
|
if (!match) {
|
|
@@ -1389,7 +1618,7 @@ var MindCache = class {
|
|
|
1389
1618
|
listeners = {};
|
|
1390
1619
|
globalListeners = [];
|
|
1391
1620
|
// Metadata
|
|
1392
|
-
version = "3.
|
|
1621
|
+
version = "3.6.0";
|
|
1393
1622
|
// Internal flag to prevent sync loops when receiving remote updates
|
|
1394
1623
|
// (Less critical with Yjs but kept for API compat)
|
|
1395
1624
|
normalizeSystemTags(tags) {
|
|
@@ -1426,6 +1655,8 @@ var MindCache = class {
|
|
|
1426
1655
|
_history = [];
|
|
1427
1656
|
_historyOptions = { maxEntries: 100, snapshotInterval: 10 };
|
|
1428
1657
|
_historyEnabled = false;
|
|
1658
|
+
// Custom type registry
|
|
1659
|
+
_typeRegistry = /* @__PURE__ */ new Map();
|
|
1429
1660
|
constructor(options) {
|
|
1430
1661
|
this.doc = options?.doc || new Y__namespace.Doc();
|
|
1431
1662
|
this.rootMap = this.doc.getMap("mindcache");
|
|
@@ -2382,6 +2613,71 @@ var MindCache = class {
|
|
|
2382
2613
|
systemGetKeysByTag(tag) {
|
|
2383
2614
|
return TagManager.systemGetKeysByTag(this, tag);
|
|
2384
2615
|
}
|
|
2616
|
+
// ============================================
|
|
2617
|
+
// Custom Type Methods
|
|
2618
|
+
// ============================================
|
|
2619
|
+
/**
|
|
2620
|
+
* Register a custom type with a markdown schema definition.
|
|
2621
|
+
*
|
|
2622
|
+
* Schema format:
|
|
2623
|
+
* ```
|
|
2624
|
+
* #TypeName
|
|
2625
|
+
* * fieldName: description of the field
|
|
2626
|
+
* * anotherField: description
|
|
2627
|
+
* ```
|
|
2628
|
+
*
|
|
2629
|
+
* @param name - Type name (e.g., 'Contact')
|
|
2630
|
+
* @param schema - Markdown schema definition
|
|
2631
|
+
* @throws Error if schema format is invalid
|
|
2632
|
+
*/
|
|
2633
|
+
registerType(name, schema) {
|
|
2634
|
+
const typeDef = SchemaParser.parse(schema);
|
|
2635
|
+
typeDef.name = name;
|
|
2636
|
+
this._typeRegistry.set(name, typeDef);
|
|
2637
|
+
}
|
|
2638
|
+
/**
|
|
2639
|
+
* Assign a custom type to a key.
|
|
2640
|
+
* The key must exist and the type must be registered.
|
|
2641
|
+
* Also sets the underlying type to 'json' since custom types are structured JSON data.
|
|
2642
|
+
*
|
|
2643
|
+
* @param key - Key to assign type to
|
|
2644
|
+
* @param typeName - Registered type name
|
|
2645
|
+
* @throws Error if key doesn't exist or type is not registered
|
|
2646
|
+
*/
|
|
2647
|
+
setType(key, typeName) {
|
|
2648
|
+
if (!this.rootMap.has(key)) {
|
|
2649
|
+
throw new Error(`Key "${key}" does not exist`);
|
|
2650
|
+
}
|
|
2651
|
+
if (!this._typeRegistry.has(typeName)) {
|
|
2652
|
+
throw new Error(`Type "${typeName}" is not registered. Use registerType() first.`);
|
|
2653
|
+
}
|
|
2654
|
+
this.set_attributes(key, { type: "json", customType: typeName });
|
|
2655
|
+
}
|
|
2656
|
+
/**
|
|
2657
|
+
* Get a registered type schema definition.
|
|
2658
|
+
*
|
|
2659
|
+
* @param typeName - Type name to look up
|
|
2660
|
+
* @returns The type definition or undefined if not registered
|
|
2661
|
+
*/
|
|
2662
|
+
getTypeSchema(typeName) {
|
|
2663
|
+
return this._typeRegistry.get(typeName);
|
|
2664
|
+
}
|
|
2665
|
+
/**
|
|
2666
|
+
* Get all registered type names.
|
|
2667
|
+
*/
|
|
2668
|
+
getRegisteredTypes() {
|
|
2669
|
+
return Array.from(this._typeRegistry.keys());
|
|
2670
|
+
}
|
|
2671
|
+
/**
|
|
2672
|
+
* Get the custom type assigned to a key.
|
|
2673
|
+
*
|
|
2674
|
+
* @param key - Key to check
|
|
2675
|
+
* @returns Type name or undefined if no custom type assigned
|
|
2676
|
+
*/
|
|
2677
|
+
getKeyType(key) {
|
|
2678
|
+
const attrs = this.get_attributes(key);
|
|
2679
|
+
return attrs?.customType;
|
|
2680
|
+
}
|
|
2385
2681
|
/**
|
|
2386
2682
|
* Helper to get sorted keys (by zIndex).
|
|
2387
2683
|
* Respects context filtering when set.
|
|
@@ -2643,9 +2939,28 @@ var MindCache = class {
|
|
|
2643
2939
|
findKeyFromSanitizedTool(sanitizedKey) {
|
|
2644
2940
|
return AIToolBuilder.findKeyFromSanitizedTool(this, sanitizedKey);
|
|
2645
2941
|
}
|
|
2942
|
+
/**
|
|
2943
|
+
* Generate framework-agnostic tools with raw JSON Schema.
|
|
2944
|
+
* Works with: OpenAI SDK, Anthropic SDK, LangChain, and other frameworks.
|
|
2945
|
+
*
|
|
2946
|
+
* Tool format:
|
|
2947
|
+
* {
|
|
2948
|
+
* description: string,
|
|
2949
|
+
* parameters: { type: 'object', properties: {...}, required: [...] },
|
|
2950
|
+
* execute: async (args) => result
|
|
2951
|
+
* }
|
|
2952
|
+
*
|
|
2953
|
+
* Security: All tools use llm_set_key internally which:
|
|
2954
|
+
* - Only modifies VALUES, never attributes/systemTags
|
|
2955
|
+
* - Prevents LLMs from escalating privileges
|
|
2956
|
+
*/
|
|
2957
|
+
create_tools() {
|
|
2958
|
+
return AIToolBuilder.createTools(this);
|
|
2959
|
+
}
|
|
2646
2960
|
/**
|
|
2647
2961
|
* Generate Vercel AI SDK compatible tools for writable keys.
|
|
2648
|
-
*
|
|
2962
|
+
* Wraps parameters with jsonSchema() for AI SDK v5 compatibility.
|
|
2963
|
+
* Use this with: generateText(), streamText() from 'ai' package.
|
|
2649
2964
|
*
|
|
2650
2965
|
* Security: All tools use llm_set_key internally which:
|
|
2651
2966
|
* - Only modifies VALUES, never attributes/systemTags
|
|
@@ -2688,8 +3003,6 @@ init_CloudAdapter();
|
|
|
2688
3003
|
init_CloudAdapter();
|
|
2689
3004
|
|
|
2690
3005
|
// src/cloud/OAuthClient.ts
|
|
2691
|
-
var DEFAULT_API_URL = "https://api.mindcache.dev";
|
|
2692
|
-
var DEFAULT_USERINFO_URL = DEFAULT_API_URL + "/oauth/userinfo";
|
|
2693
3006
|
var TOKEN_REFRESH_BUFFER = 5 * 60 * 1e3;
|
|
2694
3007
|
function generateRandomString(length2) {
|
|
2695
3008
|
const array = new Uint8Array(length2);
|
|
@@ -2718,6 +3031,21 @@ var OAuthClient = class {
|
|
|
2718
3031
|
tokens = null;
|
|
2719
3032
|
refreshPromise = null;
|
|
2720
3033
|
constructor(config) {
|
|
3034
|
+
if (!config.baseUrl) {
|
|
3035
|
+
throw new Error(
|
|
3036
|
+
'MindCache OAuth: baseUrl is required!\n For production: baseUrl: "https://api.mindcache.dev"\n For local dev: baseUrl: "http://localhost:8787"'
|
|
3037
|
+
);
|
|
3038
|
+
}
|
|
3039
|
+
try {
|
|
3040
|
+
const url = new URL(config.baseUrl);
|
|
3041
|
+
if (url.hostname === "mindcache.dev") {
|
|
3042
|
+
console.error(
|
|
3043
|
+
'\u274C MindCache OAuth ERROR: baseUrl should be "api.mindcache.dev" not "mindcache.dev"\n Current: ' + config.baseUrl + "\n Correct: https://api.mindcache.dev"
|
|
3044
|
+
);
|
|
3045
|
+
}
|
|
3046
|
+
} catch {
|
|
3047
|
+
throw new Error("MindCache OAuth: Invalid baseUrl format: " + config.baseUrl);
|
|
3048
|
+
}
|
|
2721
3049
|
let redirectUri = config.redirectUri;
|
|
2722
3050
|
if (!redirectUri && typeof window !== "undefined") {
|
|
2723
3051
|
const url = new URL(window.location.href);
|
|
@@ -2725,55 +3053,54 @@ var OAuthClient = class {
|
|
|
2725
3053
|
url.hash = "";
|
|
2726
3054
|
redirectUri = url.toString();
|
|
2727
3055
|
}
|
|
2728
|
-
const baseUrl = config.baseUrl
|
|
2729
|
-
const authUrl = config.authUrl || baseUrl + "/oauth/authorize";
|
|
2730
|
-
const tokenUrl = config.tokenUrl || baseUrl + "/oauth/token";
|
|
2731
|
-
const apiUrl = config.apiUrl || baseUrl;
|
|
3056
|
+
const baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
2732
3057
|
this.config = {
|
|
2733
3058
|
clientId: config.clientId,
|
|
3059
|
+
baseUrl,
|
|
2734
3060
|
redirectUri: redirectUri || "",
|
|
2735
3061
|
scopes: config.scopes || ["read", "write"],
|
|
2736
|
-
baseUrl,
|
|
2737
|
-
authUrl,
|
|
2738
|
-
tokenUrl,
|
|
2739
|
-
apiUrl,
|
|
2740
3062
|
usePKCE: config.usePKCE !== false,
|
|
2741
3063
|
// Default true
|
|
2742
3064
|
storagePrefix: config.storagePrefix || "mindcache_oauth"
|
|
2743
3065
|
};
|
|
2744
|
-
console.log("\u{1F510} MindCache OAuth
|
|
3066
|
+
console.log("\u{1F510} MindCache OAuth:", {
|
|
2745
3067
|
baseUrl: this.config.baseUrl,
|
|
2746
|
-
authUrl: this.
|
|
2747
|
-
tokenUrl: this.
|
|
2748
|
-
apiUrl: this.config.apiUrl,
|
|
3068
|
+
authUrl: this.authUrl,
|
|
3069
|
+
tokenUrl: this.tokenUrl,
|
|
2749
3070
|
clientId: this.config.clientId.substring(0, 20) + "..."
|
|
2750
3071
|
});
|
|
2751
3072
|
this.validateApi();
|
|
2752
3073
|
this.loadTokens();
|
|
2753
3074
|
}
|
|
3075
|
+
/** Derived auth URL */
|
|
3076
|
+
get authUrl() {
|
|
3077
|
+
return this.config.baseUrl + "/oauth/authorize";
|
|
3078
|
+
}
|
|
3079
|
+
/** Derived token URL */
|
|
3080
|
+
get tokenUrl() {
|
|
3081
|
+
return this.config.baseUrl + "/oauth/token";
|
|
3082
|
+
}
|
|
3083
|
+
/** Derived userinfo URL */
|
|
3084
|
+
get userinfoUrl() {
|
|
3085
|
+
return this.config.baseUrl + "/oauth/userinfo";
|
|
3086
|
+
}
|
|
2754
3087
|
/**
|
|
2755
|
-
* Validate the API is reachable
|
|
3088
|
+
* Validate the API is reachable
|
|
2756
3089
|
*/
|
|
2757
3090
|
async validateApi() {
|
|
2758
3091
|
try {
|
|
2759
|
-
const response = await fetch(`${this.config.
|
|
3092
|
+
const response = await fetch(`${this.config.baseUrl}/oauth/apps/info`, {
|
|
2760
3093
|
method: "GET",
|
|
2761
3094
|
headers: { "Accept": "application/json" }
|
|
2762
3095
|
});
|
|
2763
3096
|
if (response.status === 404) {
|
|
2764
3097
|
console.error(
|
|
2765
|
-
"\u274C MindCache OAuth ERROR: API not found at " + this.config.
|
|
3098
|
+
"\u274C MindCache OAuth ERROR: API not found at " + this.config.baseUrl + '\n The server returned 404. Common causes:\n - Wrong domain: Use "api.mindcache.dev" not "mindcache.dev"\n - Wrong port: Local dev server is usually on port 8787\n - Server not running: Make sure the MindCache server is started'
|
|
2766
3099
|
);
|
|
2767
|
-
} else if (!response.ok) {
|
|
2768
|
-
console.warn(
|
|
2769
|
-
"\u26A0\uFE0F MindCache OAuth: API responded with status " + response.status + "\n URL: " + this.config.apiUrl
|
|
2770
|
-
);
|
|
2771
|
-
} else {
|
|
2772
|
-
console.log("\u2705 MindCache OAuth: API is reachable at " + this.config.apiUrl);
|
|
2773
3100
|
}
|
|
2774
3101
|
} catch (error) {
|
|
2775
3102
|
console.error(
|
|
2776
|
-
"\u274C MindCache OAuth ERROR: Cannot reach API at " + this.config.
|
|
3103
|
+
"\u274C MindCache OAuth ERROR: Cannot reach API at " + this.config.baseUrl + "\n Error: " + (error instanceof Error ? error.message : String(error))
|
|
2777
3104
|
);
|
|
2778
3105
|
}
|
|
2779
3106
|
}
|
|
@@ -2802,7 +3129,7 @@ var OAuthClient = class {
|
|
|
2802
3129
|
async authorize(options) {
|
|
2803
3130
|
const state = options?.state || generateRandomString(32);
|
|
2804
3131
|
this.setStorage("state", state);
|
|
2805
|
-
const url = new URL(this.
|
|
3132
|
+
const url = new URL(this.authUrl);
|
|
2806
3133
|
url.searchParams.set("response_type", "code");
|
|
2807
3134
|
url.searchParams.set("client_id", this.config.clientId);
|
|
2808
3135
|
url.searchParams.set("redirect_uri", this.config.redirectUri);
|
|
@@ -2865,7 +3192,7 @@ var OAuthClient = class {
|
|
|
2865
3192
|
}
|
|
2866
3193
|
body.code_verifier = codeVerifier;
|
|
2867
3194
|
}
|
|
2868
|
-
const response = await fetch(this.
|
|
3195
|
+
const response = await fetch(this.tokenUrl, {
|
|
2869
3196
|
method: "POST",
|
|
2870
3197
|
headers: {
|
|
2871
3198
|
"Content-Type": "application/json"
|
|
@@ -2917,7 +3244,7 @@ var OAuthClient = class {
|
|
|
2917
3244
|
throw new Error("No refresh token available");
|
|
2918
3245
|
}
|
|
2919
3246
|
try {
|
|
2920
|
-
const response = await fetch(this.
|
|
3247
|
+
const response = await fetch(this.tokenUrl, {
|
|
2921
3248
|
method: "POST",
|
|
2922
3249
|
headers: {
|
|
2923
3250
|
"Content-Type": "application/json"
|
|
@@ -2951,7 +3278,7 @@ var OAuthClient = class {
|
|
|
2951
3278
|
*/
|
|
2952
3279
|
async getUserInfo() {
|
|
2953
3280
|
const token = await this.getAccessToken();
|
|
2954
|
-
const response = await fetch(
|
|
3281
|
+
const response = await fetch(this.userinfoUrl, {
|
|
2955
3282
|
headers: {
|
|
2956
3283
|
Authorization: `Bearer ${token}`
|
|
2957
3284
|
}
|
|
@@ -2973,7 +3300,7 @@ var OAuthClient = class {
|
|
|
2973
3300
|
async logout() {
|
|
2974
3301
|
if (this.tokens?.accessToken) {
|
|
2975
3302
|
try {
|
|
2976
|
-
await fetch(this.
|
|
3303
|
+
await fetch(this.tokenUrl.replace("/token", "/revoke"), {
|
|
2977
3304
|
method: "POST",
|
|
2978
3305
|
headers: {
|
|
2979
3306
|
"Content-Type": "application/json"
|
|
@@ -2996,7 +3323,7 @@ var OAuthClient = class {
|
|
|
2996
3323
|
}
|
|
2997
3324
|
/**
|
|
2998
3325
|
* Token provider for MindCache cloud config
|
|
2999
|
-
* This fetches a WebSocket token
|
|
3326
|
+
* This fetches a WebSocket token using the OAuth access token
|
|
3000
3327
|
* Use this with MindCacheCloudOptions.tokenProvider
|
|
3001
3328
|
*/
|
|
3002
3329
|
tokenProvider = async () => {
|
|
@@ -3005,7 +3332,7 @@ var OAuthClient = class {
|
|
|
3005
3332
|
if (!instanceId) {
|
|
3006
3333
|
throw new Error("No instance ID available. Complete OAuth flow first.");
|
|
3007
3334
|
}
|
|
3008
|
-
const response = await fetch(`${this.config.
|
|
3335
|
+
const response = await fetch(`${this.config.baseUrl}/api/ws-token`, {
|
|
3009
3336
|
method: "POST",
|
|
3010
3337
|
headers: {
|
|
3011
3338
|
"Content-Type": "application/json",
|
|
@@ -3025,7 +3352,6 @@ var OAuthClient = class {
|
|
|
3025
3352
|
};
|
|
3026
3353
|
/**
|
|
3027
3354
|
* Get raw OAuth access token (for API calls, not WebSocket)
|
|
3028
|
-
* Use getAccessToken() for most cases
|
|
3029
3355
|
*/
|
|
3030
3356
|
accessTokenProvider = async () => {
|
|
3031
3357
|
return this.getAccessToken();
|
|
@@ -3077,11 +3403,11 @@ function createOAuthClient(config) {
|
|
|
3077
3403
|
// src/local/index.ts
|
|
3078
3404
|
init_IndexedDBAdapter();
|
|
3079
3405
|
function useMindCache(options) {
|
|
3080
|
-
const [isLoaded, setIsLoaded] =
|
|
3081
|
-
const [error, setError] =
|
|
3082
|
-
const mindcacheRef =
|
|
3083
|
-
const initializingRef =
|
|
3084
|
-
|
|
3406
|
+
const [isLoaded, setIsLoaded] = React2.useState(false);
|
|
3407
|
+
const [error, setError] = React2.useState(null);
|
|
3408
|
+
const mindcacheRef = React2.useRef(null);
|
|
3409
|
+
const initializingRef = React2.useRef(false);
|
|
3410
|
+
React2.useEffect(() => {
|
|
3085
3411
|
if (initializingRef.current) {
|
|
3086
3412
|
return;
|
|
3087
3413
|
}
|
|
@@ -3110,16 +3436,1029 @@ function useMindCache(options) {
|
|
|
3110
3436
|
error
|
|
3111
3437
|
};
|
|
3112
3438
|
}
|
|
3439
|
+
function createModel(provider, model, apiKey) {
|
|
3440
|
+
switch (provider) {
|
|
3441
|
+
case "openai": {
|
|
3442
|
+
const openai$1 = openai.createOpenAI({ apiKey });
|
|
3443
|
+
return openai$1(model);
|
|
3444
|
+
}
|
|
3445
|
+
case "anthropic":
|
|
3446
|
+
throw new Error("Anthropic provider not yet implemented. Use modelProvider for custom providers.");
|
|
3447
|
+
default:
|
|
3448
|
+
throw new Error(`Unknown provider: ${provider}. Use modelProvider for custom providers.`);
|
|
3449
|
+
}
|
|
3450
|
+
}
|
|
3451
|
+
var MindCacheContext = React2.createContext(null);
|
|
3452
|
+
function useMindCacheContext() {
|
|
3453
|
+
const context = React2.useContext(MindCacheContext);
|
|
3454
|
+
if (!context) {
|
|
3455
|
+
throw new Error("useMindCacheContext must be used within a MindCacheProvider");
|
|
3456
|
+
}
|
|
3457
|
+
return context;
|
|
3458
|
+
}
|
|
3459
|
+
function MindCacheProvider({
|
|
3460
|
+
mindcache: mcOptions,
|
|
3461
|
+
sync: syncConfig,
|
|
3462
|
+
ai: aiConfig = {},
|
|
3463
|
+
children
|
|
3464
|
+
}) {
|
|
3465
|
+
const [mindcache2, setMindcache] = React2.useState(null);
|
|
3466
|
+
const [isLoaded, setIsLoaded] = React2.useState(false);
|
|
3467
|
+
const [error, setError] = React2.useState(null);
|
|
3468
|
+
const [hasApiKey, setHasApiKey] = React2.useState(false);
|
|
3469
|
+
const [lastSyncAt, setLastSyncAt] = React2.useState(null);
|
|
3470
|
+
const [isSyncing, setIsSyncing] = React2.useState(false);
|
|
3471
|
+
const initRef = React2.useRef(false);
|
|
3472
|
+
const resolvedAiConfig = {
|
|
3473
|
+
provider: "openai",
|
|
3474
|
+
model: "gpt-4o",
|
|
3475
|
+
keyStorage: "localStorage",
|
|
3476
|
+
storageKey: "ai_api_key",
|
|
3477
|
+
...aiConfig
|
|
3478
|
+
};
|
|
3479
|
+
React2.useEffect(() => {
|
|
3480
|
+
if (initRef.current) {
|
|
3481
|
+
return;
|
|
3482
|
+
}
|
|
3483
|
+
initRef.current = true;
|
|
3484
|
+
const init = async () => {
|
|
3485
|
+
try {
|
|
3486
|
+
const options = mcOptions || {
|
|
3487
|
+
indexedDB: {
|
|
3488
|
+
dbName: "mindcache_local_first",
|
|
3489
|
+
storeName: "mindcache_store",
|
|
3490
|
+
debounceMs: 1e3
|
|
3491
|
+
}
|
|
3492
|
+
};
|
|
3493
|
+
const mc = new MindCache(options);
|
|
3494
|
+
await mc.waitForSync();
|
|
3495
|
+
setMindcache(mc);
|
|
3496
|
+
setIsLoaded(true);
|
|
3497
|
+
if (resolvedAiConfig.keyStorage === "localStorage" && typeof window !== "undefined") {
|
|
3498
|
+
const stored = localStorage.getItem(resolvedAiConfig.storageKey || "openai_api_key");
|
|
3499
|
+
setHasApiKey(!!stored);
|
|
3500
|
+
} else if (resolvedAiConfig.apiKey) {
|
|
3501
|
+
setHasApiKey(true);
|
|
3502
|
+
}
|
|
3503
|
+
} catch (err) {
|
|
3504
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
3505
|
+
setIsLoaded(true);
|
|
3506
|
+
}
|
|
3507
|
+
};
|
|
3508
|
+
init();
|
|
3509
|
+
return () => {
|
|
3510
|
+
if (mindcache2) {
|
|
3511
|
+
mindcache2.disconnect();
|
|
3512
|
+
}
|
|
3513
|
+
};
|
|
3514
|
+
}, []);
|
|
3515
|
+
const getApiKey = () => {
|
|
3516
|
+
if (resolvedAiConfig.apiKey) {
|
|
3517
|
+
return resolvedAiConfig.apiKey;
|
|
3518
|
+
}
|
|
3519
|
+
if (resolvedAiConfig.keyStorage === "localStorage" && typeof window !== "undefined") {
|
|
3520
|
+
return localStorage.getItem(resolvedAiConfig.storageKey || "openai_api_key");
|
|
3521
|
+
}
|
|
3522
|
+
return null;
|
|
3523
|
+
};
|
|
3524
|
+
const setApiKey = (key) => {
|
|
3525
|
+
if (resolvedAiConfig.keyStorage === "localStorage" && typeof window !== "undefined") {
|
|
3526
|
+
localStorage.setItem(resolvedAiConfig.storageKey || "openai_api_key", key);
|
|
3527
|
+
setHasApiKey(true);
|
|
3528
|
+
}
|
|
3529
|
+
};
|
|
3530
|
+
const getModel = () => {
|
|
3531
|
+
const apiKey = getApiKey();
|
|
3532
|
+
if (!apiKey) {
|
|
3533
|
+
throw new Error("API key not configured. Call setApiKey() first or configure ai.apiKey.");
|
|
3534
|
+
}
|
|
3535
|
+
if (resolvedAiConfig.modelProvider) {
|
|
3536
|
+
return resolvedAiConfig.modelProvider(apiKey);
|
|
3537
|
+
}
|
|
3538
|
+
const provider = resolvedAiConfig.provider || "openai";
|
|
3539
|
+
const model = resolvedAiConfig.model || "gpt-4o";
|
|
3540
|
+
return createModel(provider, model, apiKey);
|
|
3541
|
+
};
|
|
3542
|
+
const syncToGitStore = async () => {
|
|
3543
|
+
if (!mindcache2 || !syncConfig?.gitstore) {
|
|
3544
|
+
return;
|
|
3545
|
+
}
|
|
3546
|
+
setIsSyncing(true);
|
|
3547
|
+
try {
|
|
3548
|
+
let gitStoreModule;
|
|
3549
|
+
try {
|
|
3550
|
+
gitStoreModule = await Function('return import("@mindcache/gitstore")')();
|
|
3551
|
+
} catch {
|
|
3552
|
+
throw new Error("@mindcache/gitstore is not installed. Run: npm install @mindcache/gitstore");
|
|
3553
|
+
}
|
|
3554
|
+
const { GitStore, MindCacheSync } = gitStoreModule;
|
|
3555
|
+
const token = typeof syncConfig.gitstore.token === "function" ? await syncConfig.gitstore.token() : syncConfig.gitstore.token;
|
|
3556
|
+
const gitStore = new GitStore({
|
|
3557
|
+
owner: syncConfig.gitstore.owner,
|
|
3558
|
+
repo: syncConfig.gitstore.repo,
|
|
3559
|
+
tokenProvider: async () => token
|
|
3560
|
+
});
|
|
3561
|
+
const sync = new MindCacheSync(gitStore, mindcache2, {
|
|
3562
|
+
filePath: syncConfig.gitstore.path || "mindcache.md"
|
|
3563
|
+
});
|
|
3564
|
+
await sync.save({ message: "Auto-sync from MindCache" });
|
|
3565
|
+
setLastSyncAt(/* @__PURE__ */ new Date());
|
|
3566
|
+
} catch (err) {
|
|
3567
|
+
console.error("[MindCacheProvider] Sync error:", err);
|
|
3568
|
+
throw err;
|
|
3569
|
+
} finally {
|
|
3570
|
+
setIsSyncing(false);
|
|
3571
|
+
}
|
|
3572
|
+
};
|
|
3573
|
+
const value = {
|
|
3574
|
+
mindcache: mindcache2,
|
|
3575
|
+
isLoaded,
|
|
3576
|
+
error,
|
|
3577
|
+
aiConfig: resolvedAiConfig,
|
|
3578
|
+
syncConfig,
|
|
3579
|
+
getApiKey,
|
|
3580
|
+
setApiKey,
|
|
3581
|
+
hasApiKey,
|
|
3582
|
+
getModel,
|
|
3583
|
+
syncToGitStore,
|
|
3584
|
+
lastSyncAt,
|
|
3585
|
+
isSyncing
|
|
3586
|
+
};
|
|
3587
|
+
return /* @__PURE__ */ jsxRuntime.jsx(MindCacheContext.Provider, { value, children });
|
|
3588
|
+
}
|
|
3589
|
+
function generateId() {
|
|
3590
|
+
return Math.random().toString(36).substring(2, 15) + Date.now().toString(36);
|
|
3591
|
+
}
|
|
3592
|
+
function useClientChat(options = {}) {
|
|
3593
|
+
const context = useMindCacheContext();
|
|
3594
|
+
const mc = options.mindcache || context.mindcache;
|
|
3595
|
+
const [messages, setMessages] = React2.useState(options.initialMessages || []);
|
|
3596
|
+
const [status, setStatus] = React2.useState("idle");
|
|
3597
|
+
const [error, setError] = React2.useState(null);
|
|
3598
|
+
const [streamingContent, setStreamingContent] = React2.useState("");
|
|
3599
|
+
const abortControllerRef = React2.useRef(null);
|
|
3600
|
+
const {
|
|
3601
|
+
systemPrompt,
|
|
3602
|
+
onMindCacheChange,
|
|
3603
|
+
onFinish,
|
|
3604
|
+
onError,
|
|
3605
|
+
maxToolCalls = 5
|
|
3606
|
+
} = options;
|
|
3607
|
+
const apiKey = context.getApiKey();
|
|
3608
|
+
const addMessage = React2.useCallback((msg) => {
|
|
3609
|
+
const newMessage = {
|
|
3610
|
+
...msg,
|
|
3611
|
+
id: generateId(),
|
|
3612
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
3613
|
+
};
|
|
3614
|
+
setMessages((prev) => [...prev, newMessage]);
|
|
3615
|
+
return newMessage;
|
|
3616
|
+
}, []);
|
|
3617
|
+
const clearMessages = React2.useCallback(() => {
|
|
3618
|
+
setMessages([]);
|
|
3619
|
+
setError(null);
|
|
3620
|
+
setStreamingContent("");
|
|
3621
|
+
}, []);
|
|
3622
|
+
const stop = React2.useCallback(() => {
|
|
3623
|
+
abortControllerRef.current?.abort();
|
|
3624
|
+
setStatus("idle");
|
|
3625
|
+
}, []);
|
|
3626
|
+
const sendMessage = React2.useCallback(async (content) => {
|
|
3627
|
+
if (!mc) {
|
|
3628
|
+
const err = new Error("MindCache not initialized");
|
|
3629
|
+
setError(err);
|
|
3630
|
+
onError?.(err);
|
|
3631
|
+
return;
|
|
3632
|
+
}
|
|
3633
|
+
if (!apiKey) {
|
|
3634
|
+
const err = new Error("API key not configured. Please set your API key.");
|
|
3635
|
+
setError(err);
|
|
3636
|
+
onError?.(err);
|
|
3637
|
+
return;
|
|
3638
|
+
}
|
|
3639
|
+
abortControllerRef.current?.abort();
|
|
3640
|
+
abortControllerRef.current = new AbortController();
|
|
3641
|
+
const userMessage = addMessage({ role: "user", content });
|
|
3642
|
+
setStatus("loading");
|
|
3643
|
+
setError(null);
|
|
3644
|
+
setStreamingContent("");
|
|
3645
|
+
try {
|
|
3646
|
+
const model = context.getModel();
|
|
3647
|
+
const finalSystemPrompt = systemPrompt || mc.get_system_prompt();
|
|
3648
|
+
const tools = mc.create_vercel_ai_tools();
|
|
3649
|
+
const apiMessages = messages.concat(userMessage).map((m) => ({
|
|
3650
|
+
role: m.role,
|
|
3651
|
+
content: m.content
|
|
3652
|
+
}));
|
|
3653
|
+
setStatus("streaming");
|
|
3654
|
+
const parts = [];
|
|
3655
|
+
let accumulatedText = "";
|
|
3656
|
+
const result = await ai.streamText({
|
|
3657
|
+
model,
|
|
3658
|
+
system: finalSystemPrompt,
|
|
3659
|
+
messages: apiMessages,
|
|
3660
|
+
tools,
|
|
3661
|
+
stopWhen: ai.stepCountIs(maxToolCalls),
|
|
3662
|
+
abortSignal: abortControllerRef.current.signal,
|
|
3663
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
3664
|
+
onStepFinish: async (step) => {
|
|
3665
|
+
if (step.toolCalls && step.toolCalls.length > 0) {
|
|
3666
|
+
for (const toolCall of step.toolCalls) {
|
|
3667
|
+
const toolName = toolCall.toolName;
|
|
3668
|
+
const args = toolCall.args || toolCall.input || {};
|
|
3669
|
+
parts.push({
|
|
3670
|
+
type: "tool-call",
|
|
3671
|
+
toolCallId: toolCall.toolCallId,
|
|
3672
|
+
toolName,
|
|
3673
|
+
args
|
|
3674
|
+
});
|
|
3675
|
+
if (typeof toolName === "string" && (toolName.startsWith("write_") || toolName === "create_key")) {
|
|
3676
|
+
const value = args.value;
|
|
3677
|
+
const result2 = mc.executeToolCall(toolName, value);
|
|
3678
|
+
parts.push({
|
|
3679
|
+
type: "tool-result",
|
|
3680
|
+
toolCallId: toolCall.toolCallId,
|
|
3681
|
+
toolName,
|
|
3682
|
+
result: result2
|
|
3683
|
+
});
|
|
3684
|
+
onMindCacheChange?.();
|
|
3685
|
+
}
|
|
3686
|
+
}
|
|
3687
|
+
}
|
|
3688
|
+
if (step.text) {
|
|
3689
|
+
accumulatedText += step.text;
|
|
3690
|
+
}
|
|
3691
|
+
}
|
|
3692
|
+
});
|
|
3693
|
+
for await (const chunk of result.textStream) {
|
|
3694
|
+
accumulatedText += chunk;
|
|
3695
|
+
setStreamingContent(accumulatedText);
|
|
3696
|
+
}
|
|
3697
|
+
if (accumulatedText) {
|
|
3698
|
+
parts.unshift({ type: "text", text: accumulatedText });
|
|
3699
|
+
}
|
|
3700
|
+
const assistantMessage = {
|
|
3701
|
+
id: generateId(),
|
|
3702
|
+
role: "assistant",
|
|
3703
|
+
content: accumulatedText,
|
|
3704
|
+
parts: parts.length > 0 ? parts : void 0,
|
|
3705
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
3706
|
+
};
|
|
3707
|
+
setMessages((prev) => [...prev, assistantMessage]);
|
|
3708
|
+
setStreamingContent("");
|
|
3709
|
+
setStatus("idle");
|
|
3710
|
+
onFinish?.(assistantMessage);
|
|
3711
|
+
} catch (err) {
|
|
3712
|
+
if (err.name === "AbortError") {
|
|
3713
|
+
if (streamingContent) {
|
|
3714
|
+
const partialMessage = {
|
|
3715
|
+
id: generateId(),
|
|
3716
|
+
role: "assistant",
|
|
3717
|
+
content: streamingContent + " [stopped]",
|
|
3718
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
3719
|
+
};
|
|
3720
|
+
setMessages((prev) => [...prev, partialMessage]);
|
|
3721
|
+
}
|
|
3722
|
+
setStreamingContent("");
|
|
3723
|
+
setStatus("idle");
|
|
3724
|
+
return;
|
|
3725
|
+
}
|
|
3726
|
+
const error2 = err instanceof Error ? err : new Error(String(err));
|
|
3727
|
+
setError(error2);
|
|
3728
|
+
setStatus("error");
|
|
3729
|
+
setStreamingContent("");
|
|
3730
|
+
onError?.(error2);
|
|
3731
|
+
}
|
|
3732
|
+
}, [
|
|
3733
|
+
mc,
|
|
3734
|
+
apiKey,
|
|
3735
|
+
context,
|
|
3736
|
+
messages,
|
|
3737
|
+
systemPrompt,
|
|
3738
|
+
maxToolCalls,
|
|
3739
|
+
addMessage,
|
|
3740
|
+
onMindCacheChange,
|
|
3741
|
+
onFinish,
|
|
3742
|
+
onError,
|
|
3743
|
+
streamingContent
|
|
3744
|
+
]);
|
|
3745
|
+
React2.useEffect(() => {
|
|
3746
|
+
return () => {
|
|
3747
|
+
abortControllerRef.current?.abort();
|
|
3748
|
+
};
|
|
3749
|
+
}, []);
|
|
3750
|
+
return {
|
|
3751
|
+
messages,
|
|
3752
|
+
status,
|
|
3753
|
+
error,
|
|
3754
|
+
sendMessage,
|
|
3755
|
+
clearMessages,
|
|
3756
|
+
isLoading: status === "loading" || status === "streaming",
|
|
3757
|
+
addMessage,
|
|
3758
|
+
stop,
|
|
3759
|
+
streamingContent
|
|
3760
|
+
};
|
|
3761
|
+
}
|
|
3762
|
+
var defaultTheme = {
|
|
3763
|
+
background: "#000",
|
|
3764
|
+
userBubble: "#1a1a2e",
|
|
3765
|
+
assistantBubble: "#0d0d0d",
|
|
3766
|
+
textColor: "#22c55e",
|
|
3767
|
+
secondaryTextColor: "#6b7280",
|
|
3768
|
+
borderColor: "#333",
|
|
3769
|
+
primaryColor: "#22c55e",
|
|
3770
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif'
|
|
3771
|
+
};
|
|
3772
|
+
function DefaultMessage({
|
|
3773
|
+
message,
|
|
3774
|
+
theme
|
|
3775
|
+
}) {
|
|
3776
|
+
const isUser = message.role === "user";
|
|
3777
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
3778
|
+
"div",
|
|
3779
|
+
{
|
|
3780
|
+
style: {
|
|
3781
|
+
display: "flex",
|
|
3782
|
+
justifyContent: isUser ? "flex-end" : "flex-start",
|
|
3783
|
+
marginBottom: "12px"
|
|
3784
|
+
},
|
|
3785
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3786
|
+
"div",
|
|
3787
|
+
{
|
|
3788
|
+
style: {
|
|
3789
|
+
maxWidth: "80%",
|
|
3790
|
+
padding: "12px 16px",
|
|
3791
|
+
borderRadius: "12px",
|
|
3792
|
+
backgroundColor: isUser ? theme.userBubble : theme.assistantBubble,
|
|
3793
|
+
border: `1px solid ${theme.borderColor}`
|
|
3794
|
+
},
|
|
3795
|
+
children: [
|
|
3796
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3797
|
+
"div",
|
|
3798
|
+
{
|
|
3799
|
+
style: {
|
|
3800
|
+
fontSize: "10px",
|
|
3801
|
+
color: theme.secondaryTextColor,
|
|
3802
|
+
marginBottom: "4px",
|
|
3803
|
+
textTransform: "uppercase"
|
|
3804
|
+
},
|
|
3805
|
+
children: isUser ? "You" : "Assistant"
|
|
3806
|
+
}
|
|
3807
|
+
),
|
|
3808
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3809
|
+
"div",
|
|
3810
|
+
{
|
|
3811
|
+
style: {
|
|
3812
|
+
color: theme.textColor,
|
|
3813
|
+
whiteSpace: "pre-wrap",
|
|
3814
|
+
wordBreak: "break-word",
|
|
3815
|
+
lineHeight: 1.5
|
|
3816
|
+
},
|
|
3817
|
+
children: message.content
|
|
3818
|
+
}
|
|
3819
|
+
)
|
|
3820
|
+
]
|
|
3821
|
+
}
|
|
3822
|
+
)
|
|
3823
|
+
}
|
|
3824
|
+
);
|
|
3825
|
+
}
|
|
3826
|
+
function ApiKeyInput({
|
|
3827
|
+
theme,
|
|
3828
|
+
onSubmit
|
|
3829
|
+
}) {
|
|
3830
|
+
const [key, setKey] = React2.useState("");
|
|
3831
|
+
const handleSubmit = (e) => {
|
|
3832
|
+
e.preventDefault();
|
|
3833
|
+
if (key.trim()) {
|
|
3834
|
+
onSubmit(key.trim());
|
|
3835
|
+
}
|
|
3836
|
+
};
|
|
3837
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3838
|
+
"div",
|
|
3839
|
+
{
|
|
3840
|
+
style: {
|
|
3841
|
+
display: "flex",
|
|
3842
|
+
flexDirection: "column",
|
|
3843
|
+
alignItems: "center",
|
|
3844
|
+
justifyContent: "center",
|
|
3845
|
+
height: "100%",
|
|
3846
|
+
padding: "20px"
|
|
3847
|
+
},
|
|
3848
|
+
children: [
|
|
3849
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3850
|
+
"div",
|
|
3851
|
+
{
|
|
3852
|
+
style: {
|
|
3853
|
+
fontSize: "14px",
|
|
3854
|
+
color: theme.textColor,
|
|
3855
|
+
marginBottom: "16px",
|
|
3856
|
+
textAlign: "center"
|
|
3857
|
+
},
|
|
3858
|
+
children: "Enter your API key to start chatting"
|
|
3859
|
+
}
|
|
3860
|
+
),
|
|
3861
|
+
/* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: handleSubmit, style: { width: "100%", maxWidth: "400px" }, children: [
|
|
3862
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3863
|
+
"input",
|
|
3864
|
+
{
|
|
3865
|
+
type: "password",
|
|
3866
|
+
value: key,
|
|
3867
|
+
onChange: (e) => setKey(e.target.value),
|
|
3868
|
+
placeholder: "sk-...",
|
|
3869
|
+
style: {
|
|
3870
|
+
width: "100%",
|
|
3871
|
+
padding: "12px",
|
|
3872
|
+
backgroundColor: theme.assistantBubble,
|
|
3873
|
+
border: `1px solid ${theme.borderColor}`,
|
|
3874
|
+
borderRadius: "8px",
|
|
3875
|
+
color: theme.textColor,
|
|
3876
|
+
fontFamily: theme.fontFamily,
|
|
3877
|
+
fontSize: "14px",
|
|
3878
|
+
marginBottom: "12px"
|
|
3879
|
+
}
|
|
3880
|
+
}
|
|
3881
|
+
),
|
|
3882
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3883
|
+
"button",
|
|
3884
|
+
{
|
|
3885
|
+
type: "submit",
|
|
3886
|
+
disabled: !key.trim(),
|
|
3887
|
+
style: {
|
|
3888
|
+
width: "100%",
|
|
3889
|
+
padding: "12px",
|
|
3890
|
+
backgroundColor: theme.primaryColor,
|
|
3891
|
+
border: "none",
|
|
3892
|
+
borderRadius: "8px",
|
|
3893
|
+
color: "#000",
|
|
3894
|
+
fontFamily: theme.fontFamily,
|
|
3895
|
+
fontSize: "14px",
|
|
3896
|
+
fontWeight: "bold",
|
|
3897
|
+
cursor: key.trim() ? "pointer" : "not-allowed",
|
|
3898
|
+
opacity: key.trim() ? 1 : 0.5
|
|
3899
|
+
},
|
|
3900
|
+
children: "Save & Start"
|
|
3901
|
+
}
|
|
3902
|
+
)
|
|
3903
|
+
] }),
|
|
3904
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3905
|
+
"div",
|
|
3906
|
+
{
|
|
3907
|
+
style: {
|
|
3908
|
+
fontSize: "11px",
|
|
3909
|
+
color: theme.secondaryTextColor,
|
|
3910
|
+
marginTop: "16px",
|
|
3911
|
+
textAlign: "center"
|
|
3912
|
+
},
|
|
3913
|
+
children: "Your key is stored locally and never sent to our servers."
|
|
3914
|
+
}
|
|
3915
|
+
)
|
|
3916
|
+
]
|
|
3917
|
+
}
|
|
3918
|
+
);
|
|
3919
|
+
}
|
|
3920
|
+
function MindCacheChat({
|
|
3921
|
+
theme: customTheme,
|
|
3922
|
+
placeholder = "Type a message...",
|
|
3923
|
+
welcomeMessage = "Hello! I'm ready to help you.",
|
|
3924
|
+
showApiKeyInput = true,
|
|
3925
|
+
className,
|
|
3926
|
+
style,
|
|
3927
|
+
renderMessage,
|
|
3928
|
+
header,
|
|
3929
|
+
footer,
|
|
3930
|
+
initialMessages,
|
|
3931
|
+
...chatOptions
|
|
3932
|
+
}) {
|
|
3933
|
+
const context = useMindCacheContext();
|
|
3934
|
+
const theme = { ...defaultTheme, ...customTheme };
|
|
3935
|
+
const messagesEndRef = React2.useRef(null);
|
|
3936
|
+
const inputRef = React2.useRef(null);
|
|
3937
|
+
const [inputValue, setInputValue] = React2.useState("");
|
|
3938
|
+
const defaultInitialMessages = welcomeMessage ? [
|
|
3939
|
+
{
|
|
3940
|
+
id: "welcome",
|
|
3941
|
+
role: "assistant",
|
|
3942
|
+
content: welcomeMessage,
|
|
3943
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
3944
|
+
}
|
|
3945
|
+
] : [];
|
|
3946
|
+
const {
|
|
3947
|
+
messages,
|
|
3948
|
+
sendMessage,
|
|
3949
|
+
isLoading,
|
|
3950
|
+
error,
|
|
3951
|
+
streamingContent,
|
|
3952
|
+
stop
|
|
3953
|
+
} = useClientChat({
|
|
3954
|
+
...chatOptions,
|
|
3955
|
+
initialMessages: initialMessages || defaultInitialMessages,
|
|
3956
|
+
mindcache: context.mindcache || void 0
|
|
3957
|
+
});
|
|
3958
|
+
React2.useEffect(() => {
|
|
3959
|
+
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
|
|
3960
|
+
}, [messages]);
|
|
3961
|
+
const handleSubmit = async (e) => {
|
|
3962
|
+
e?.preventDefault();
|
|
3963
|
+
if (!inputValue.trim() || isLoading) {
|
|
3964
|
+
return;
|
|
3965
|
+
}
|
|
3966
|
+
const message = inputValue.trim();
|
|
3967
|
+
setInputValue("");
|
|
3968
|
+
await sendMessage(message);
|
|
3969
|
+
};
|
|
3970
|
+
const handleKeyDown = (e) => {
|
|
3971
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
3972
|
+
e.preventDefault();
|
|
3973
|
+
handleSubmit();
|
|
3974
|
+
}
|
|
3975
|
+
};
|
|
3976
|
+
if (showApiKeyInput && !context.hasApiKey && context.aiConfig.keyStorage !== "memory") {
|
|
3977
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3978
|
+
"div",
|
|
3979
|
+
{
|
|
3980
|
+
className,
|
|
3981
|
+
style: {
|
|
3982
|
+
display: "flex",
|
|
3983
|
+
flexDirection: "column",
|
|
3984
|
+
height: "100%",
|
|
3985
|
+
backgroundColor: theme.background,
|
|
3986
|
+
fontFamily: theme.fontFamily,
|
|
3987
|
+
...style
|
|
3988
|
+
},
|
|
3989
|
+
children: [
|
|
3990
|
+
header,
|
|
3991
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3992
|
+
ApiKeyInput,
|
|
3993
|
+
{
|
|
3994
|
+
theme,
|
|
3995
|
+
onSubmit: (key) => context.setApiKey(key)
|
|
3996
|
+
}
|
|
3997
|
+
),
|
|
3998
|
+
footer
|
|
3999
|
+
]
|
|
4000
|
+
}
|
|
4001
|
+
);
|
|
4002
|
+
}
|
|
4003
|
+
if (!context.isLoaded) {
|
|
4004
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4005
|
+
"div",
|
|
4006
|
+
{
|
|
4007
|
+
className,
|
|
4008
|
+
style: {
|
|
4009
|
+
display: "flex",
|
|
4010
|
+
alignItems: "center",
|
|
4011
|
+
justifyContent: "center",
|
|
4012
|
+
height: "100%",
|
|
4013
|
+
backgroundColor: theme.background,
|
|
4014
|
+
color: theme.textColor,
|
|
4015
|
+
fontFamily: theme.fontFamily,
|
|
4016
|
+
...style
|
|
4017
|
+
},
|
|
4018
|
+
children: "Loading..."
|
|
4019
|
+
}
|
|
4020
|
+
);
|
|
4021
|
+
}
|
|
4022
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4023
|
+
"div",
|
|
4024
|
+
{
|
|
4025
|
+
className,
|
|
4026
|
+
style: {
|
|
4027
|
+
display: "flex",
|
|
4028
|
+
flexDirection: "column",
|
|
4029
|
+
height: "100%",
|
|
4030
|
+
backgroundColor: theme.background,
|
|
4031
|
+
fontFamily: theme.fontFamily,
|
|
4032
|
+
...style
|
|
4033
|
+
},
|
|
4034
|
+
children: [
|
|
4035
|
+
header,
|
|
4036
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4037
|
+
"div",
|
|
4038
|
+
{
|
|
4039
|
+
style: {
|
|
4040
|
+
flex: 1,
|
|
4041
|
+
overflowY: "auto",
|
|
4042
|
+
padding: "16px"
|
|
4043
|
+
},
|
|
4044
|
+
children: [
|
|
4045
|
+
messages.map((message) => renderMessage ? /* @__PURE__ */ jsxRuntime.jsx(React2__default.default.Fragment, { children: renderMessage(message) }, message.id) : /* @__PURE__ */ jsxRuntime.jsx(DefaultMessage, { message, theme }, message.id)),
|
|
4046
|
+
streamingContent && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4047
|
+
"div",
|
|
4048
|
+
{
|
|
4049
|
+
style: {
|
|
4050
|
+
display: "flex",
|
|
4051
|
+
justifyContent: "flex-start",
|
|
4052
|
+
marginBottom: "12px"
|
|
4053
|
+
},
|
|
4054
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4055
|
+
"div",
|
|
4056
|
+
{
|
|
4057
|
+
style: {
|
|
4058
|
+
maxWidth: "80%",
|
|
4059
|
+
padding: "12px 16px",
|
|
4060
|
+
borderRadius: "12px",
|
|
4061
|
+
backgroundColor: theme.assistantBubble,
|
|
4062
|
+
border: `1px solid ${theme.borderColor}`
|
|
4063
|
+
},
|
|
4064
|
+
children: [
|
|
4065
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4066
|
+
"div",
|
|
4067
|
+
{
|
|
4068
|
+
style: {
|
|
4069
|
+
fontSize: "10px",
|
|
4070
|
+
color: theme.secondaryTextColor,
|
|
4071
|
+
marginBottom: "4px",
|
|
4072
|
+
textTransform: "uppercase"
|
|
4073
|
+
},
|
|
4074
|
+
children: "Assistant"
|
|
4075
|
+
}
|
|
4076
|
+
),
|
|
4077
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4078
|
+
"div",
|
|
4079
|
+
{
|
|
4080
|
+
style: {
|
|
4081
|
+
color: theme.textColor,
|
|
4082
|
+
whiteSpace: "pre-wrap",
|
|
4083
|
+
wordBreak: "break-word",
|
|
4084
|
+
lineHeight: 1.5
|
|
4085
|
+
},
|
|
4086
|
+
children: [
|
|
4087
|
+
streamingContent,
|
|
4088
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { opacity: 0.5, animation: "blink 1s infinite" }, children: "\u258A" })
|
|
4089
|
+
]
|
|
4090
|
+
}
|
|
4091
|
+
)
|
|
4092
|
+
]
|
|
4093
|
+
}
|
|
4094
|
+
)
|
|
4095
|
+
}
|
|
4096
|
+
),
|
|
4097
|
+
isLoading && !streamingContent && /* @__PURE__ */ jsxRuntime.jsx(
|
|
4098
|
+
"div",
|
|
4099
|
+
{
|
|
4100
|
+
style: {
|
|
4101
|
+
display: "flex",
|
|
4102
|
+
justifyContent: "flex-start",
|
|
4103
|
+
marginBottom: "12px"
|
|
4104
|
+
},
|
|
4105
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4106
|
+
"div",
|
|
4107
|
+
{
|
|
4108
|
+
style: {
|
|
4109
|
+
padding: "12px 16px",
|
|
4110
|
+
borderRadius: "12px",
|
|
4111
|
+
backgroundColor: theme.assistantBubble,
|
|
4112
|
+
border: `1px solid ${theme.borderColor}`,
|
|
4113
|
+
color: theme.secondaryTextColor
|
|
4114
|
+
},
|
|
4115
|
+
children: "Thinking..."
|
|
4116
|
+
}
|
|
4117
|
+
)
|
|
4118
|
+
}
|
|
4119
|
+
),
|
|
4120
|
+
error && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4121
|
+
"div",
|
|
4122
|
+
{
|
|
4123
|
+
style: {
|
|
4124
|
+
padding: "12px",
|
|
4125
|
+
marginBottom: "12px",
|
|
4126
|
+
borderRadius: "8px",
|
|
4127
|
+
backgroundColor: "rgba(239, 68, 68, 0.1)",
|
|
4128
|
+
border: "1px solid rgba(239, 68, 68, 0.3)",
|
|
4129
|
+
color: "#ef4444",
|
|
4130
|
+
fontSize: "13px"
|
|
4131
|
+
},
|
|
4132
|
+
children: [
|
|
4133
|
+
"Error: ",
|
|
4134
|
+
error.message
|
|
4135
|
+
]
|
|
4136
|
+
}
|
|
4137
|
+
),
|
|
4138
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { ref: messagesEndRef })
|
|
4139
|
+
]
|
|
4140
|
+
}
|
|
4141
|
+
),
|
|
4142
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4143
|
+
"form",
|
|
4144
|
+
{
|
|
4145
|
+
onSubmit: handleSubmit,
|
|
4146
|
+
style: {
|
|
4147
|
+
padding: "12px 16px",
|
|
4148
|
+
borderTop: `1px solid ${theme.borderColor}`
|
|
4149
|
+
},
|
|
4150
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4151
|
+
"div",
|
|
4152
|
+
{
|
|
4153
|
+
style: {
|
|
4154
|
+
display: "flex",
|
|
4155
|
+
gap: "8px",
|
|
4156
|
+
alignItems: "flex-end"
|
|
4157
|
+
},
|
|
4158
|
+
children: [
|
|
4159
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4160
|
+
"textarea",
|
|
4161
|
+
{
|
|
4162
|
+
ref: inputRef,
|
|
4163
|
+
value: inputValue,
|
|
4164
|
+
onChange: (e) => {
|
|
4165
|
+
setInputValue(e.target.value);
|
|
4166
|
+
e.target.style.height = "auto";
|
|
4167
|
+
e.target.style.height = Math.min(e.target.scrollHeight, 120) + "px";
|
|
4168
|
+
},
|
|
4169
|
+
onKeyDown: handleKeyDown,
|
|
4170
|
+
placeholder: isLoading ? "Waiting for response..." : placeholder,
|
|
4171
|
+
disabled: isLoading,
|
|
4172
|
+
rows: 1,
|
|
4173
|
+
style: {
|
|
4174
|
+
flex: 1,
|
|
4175
|
+
padding: "12px",
|
|
4176
|
+
backgroundColor: theme.assistantBubble,
|
|
4177
|
+
border: `1px solid ${theme.borderColor}`,
|
|
4178
|
+
borderRadius: "8px",
|
|
4179
|
+
color: theme.textColor,
|
|
4180
|
+
fontFamily: theme.fontFamily,
|
|
4181
|
+
fontSize: "16px",
|
|
4182
|
+
// Prevents iOS zoom on focus
|
|
4183
|
+
resize: "none",
|
|
4184
|
+
minHeight: "44px",
|
|
4185
|
+
// Apple touch target minimum
|
|
4186
|
+
maxHeight: "120px",
|
|
4187
|
+
outline: "none",
|
|
4188
|
+
WebkitAppearance: "none"
|
|
4189
|
+
// Remove iOS styling
|
|
4190
|
+
}
|
|
4191
|
+
}
|
|
4192
|
+
),
|
|
4193
|
+
isLoading ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
4194
|
+
"button",
|
|
4195
|
+
{
|
|
4196
|
+
type: "button",
|
|
4197
|
+
onClick: stop,
|
|
4198
|
+
style: {
|
|
4199
|
+
padding: "12px 20px",
|
|
4200
|
+
minWidth: "44px",
|
|
4201
|
+
// Touch target
|
|
4202
|
+
minHeight: "44px",
|
|
4203
|
+
// Touch target
|
|
4204
|
+
backgroundColor: "#ef4444",
|
|
4205
|
+
border: "none",
|
|
4206
|
+
borderRadius: "8px",
|
|
4207
|
+
color: "#fff",
|
|
4208
|
+
fontFamily: theme.fontFamily,
|
|
4209
|
+
fontSize: "16px",
|
|
4210
|
+
fontWeight: "bold",
|
|
4211
|
+
cursor: "pointer",
|
|
4212
|
+
transition: "opacity 0.2s",
|
|
4213
|
+
WebkitTapHighlightColor: "transparent",
|
|
4214
|
+
touchAction: "manipulation"
|
|
4215
|
+
// Faster touch response
|
|
4216
|
+
},
|
|
4217
|
+
children: "Stop"
|
|
4218
|
+
}
|
|
4219
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
4220
|
+
"button",
|
|
4221
|
+
{
|
|
4222
|
+
type: "submit",
|
|
4223
|
+
disabled: !inputValue.trim(),
|
|
4224
|
+
style: {
|
|
4225
|
+
padding: "12px 20px",
|
|
4226
|
+
minWidth: "44px",
|
|
4227
|
+
// Touch target
|
|
4228
|
+
minHeight: "44px",
|
|
4229
|
+
// Touch target
|
|
4230
|
+
backgroundColor: theme.primaryColor,
|
|
4231
|
+
border: "none",
|
|
4232
|
+
borderRadius: "8px",
|
|
4233
|
+
color: "#000",
|
|
4234
|
+
fontFamily: theme.fontFamily,
|
|
4235
|
+
fontSize: "16px",
|
|
4236
|
+
fontWeight: "bold",
|
|
4237
|
+
cursor: !inputValue.trim() ? "not-allowed" : "pointer",
|
|
4238
|
+
opacity: !inputValue.trim() ? 0.5 : 1,
|
|
4239
|
+
transition: "opacity 0.2s",
|
|
4240
|
+
WebkitTapHighlightColor: "transparent",
|
|
4241
|
+
touchAction: "manipulation"
|
|
4242
|
+
// Faster touch response
|
|
4243
|
+
},
|
|
4244
|
+
children: "Send"
|
|
4245
|
+
}
|
|
4246
|
+
)
|
|
4247
|
+
]
|
|
4248
|
+
}
|
|
4249
|
+
)
|
|
4250
|
+
}
|
|
4251
|
+
),
|
|
4252
|
+
footer
|
|
4253
|
+
]
|
|
4254
|
+
}
|
|
4255
|
+
);
|
|
4256
|
+
}
|
|
4257
|
+
function useLocalFirstSync(options) {
|
|
4258
|
+
const {
|
|
4259
|
+
mindcache: mindcache2,
|
|
4260
|
+
gitstore,
|
|
4261
|
+
server,
|
|
4262
|
+
autoSyncInterval = 0,
|
|
4263
|
+
saveDebounceMs = 5e3,
|
|
4264
|
+
loadOnMount = true,
|
|
4265
|
+
mergeOnLoad = true
|
|
4266
|
+
} = options;
|
|
4267
|
+
const [status, setStatus] = React2.useState("idle");
|
|
4268
|
+
const [error, setError] = React2.useState(null);
|
|
4269
|
+
const [lastSyncAt, setLastSyncAt] = React2.useState(null);
|
|
4270
|
+
const [hasLocalChanges, setHasLocalChanges] = React2.useState(false);
|
|
4271
|
+
const saveTimeoutRef = React2.useRef(null);
|
|
4272
|
+
const syncIntervalRef = React2.useRef(null);
|
|
4273
|
+
const mountedRef = React2.useRef(true);
|
|
4274
|
+
const getToken = React2.useCallback(async () => {
|
|
4275
|
+
if (!gitstore) {
|
|
4276
|
+
throw new Error("GitStore not configured");
|
|
4277
|
+
}
|
|
4278
|
+
return typeof gitstore.token === "function" ? await gitstore.token() : gitstore.token;
|
|
4279
|
+
}, [gitstore]);
|
|
4280
|
+
const load = React2.useCallback(async () => {
|
|
4281
|
+
if (!mindcache2) {
|
|
4282
|
+
return;
|
|
4283
|
+
}
|
|
4284
|
+
setStatus("loading");
|
|
4285
|
+
setError(null);
|
|
4286
|
+
try {
|
|
4287
|
+
if (gitstore) {
|
|
4288
|
+
let gitStoreModule;
|
|
4289
|
+
try {
|
|
4290
|
+
gitStoreModule = await Function('return import("@mindcache/gitstore")')();
|
|
4291
|
+
} catch {
|
|
4292
|
+
throw new Error("@mindcache/gitstore is not installed. Run: npm install @mindcache/gitstore");
|
|
4293
|
+
}
|
|
4294
|
+
const { GitStore, MindCacheSync } = gitStoreModule;
|
|
4295
|
+
const token = await getToken();
|
|
4296
|
+
const store = new GitStore({
|
|
4297
|
+
owner: gitstore.owner,
|
|
4298
|
+
repo: gitstore.repo,
|
|
4299
|
+
branch: gitstore.branch,
|
|
4300
|
+
tokenProvider: async () => token
|
|
4301
|
+
});
|
|
4302
|
+
const sync2 = new MindCacheSync(store, mindcache2, {
|
|
4303
|
+
filePath: gitstore.path || "mindcache.md"
|
|
4304
|
+
});
|
|
4305
|
+
await sync2.load({ merge: mergeOnLoad });
|
|
4306
|
+
} else if (server) {
|
|
4307
|
+
const response = await fetch(server.url, {
|
|
4308
|
+
headers: server.authToken ? { Authorization: `Bearer ${server.authToken}` } : {}
|
|
4309
|
+
});
|
|
4310
|
+
if (response.ok) {
|
|
4311
|
+
const markdown = await response.text();
|
|
4312
|
+
mindcache2.fromMarkdown(markdown, mergeOnLoad);
|
|
4313
|
+
}
|
|
4314
|
+
}
|
|
4315
|
+
if (mountedRef.current) {
|
|
4316
|
+
setLastSyncAt(/* @__PURE__ */ new Date());
|
|
4317
|
+
setStatus("idle");
|
|
4318
|
+
}
|
|
4319
|
+
} catch (err) {
|
|
4320
|
+
if (mountedRef.current) {
|
|
4321
|
+
const error2 = err instanceof Error ? err : new Error(String(err));
|
|
4322
|
+
setError(error2);
|
|
4323
|
+
setStatus("error");
|
|
4324
|
+
}
|
|
4325
|
+
throw err;
|
|
4326
|
+
}
|
|
4327
|
+
}, [mindcache2, gitstore, server, getToken, mergeOnLoad]);
|
|
4328
|
+
const save = React2.useCallback(async (message) => {
|
|
4329
|
+
if (!mindcache2) {
|
|
4330
|
+
return;
|
|
4331
|
+
}
|
|
4332
|
+
setStatus("saving");
|
|
4333
|
+
setError(null);
|
|
4334
|
+
try {
|
|
4335
|
+
if (gitstore) {
|
|
4336
|
+
let gitStoreModule;
|
|
4337
|
+
try {
|
|
4338
|
+
gitStoreModule = await Function('return import("@mindcache/gitstore")')();
|
|
4339
|
+
} catch {
|
|
4340
|
+
throw new Error("@mindcache/gitstore is not installed. Run: npm install @mindcache/gitstore");
|
|
4341
|
+
}
|
|
4342
|
+
const { GitStore, MindCacheSync } = gitStoreModule;
|
|
4343
|
+
const token = await getToken();
|
|
4344
|
+
const store = new GitStore({
|
|
4345
|
+
owner: gitstore.owner,
|
|
4346
|
+
repo: gitstore.repo,
|
|
4347
|
+
branch: gitstore.branch,
|
|
4348
|
+
tokenProvider: async () => token
|
|
4349
|
+
});
|
|
4350
|
+
const sync2 = new MindCacheSync(store, mindcache2, {
|
|
4351
|
+
filePath: gitstore.path || "mindcache.md"
|
|
4352
|
+
});
|
|
4353
|
+
await sync2.save({ message: message || "MindCache sync" });
|
|
4354
|
+
} else if (server) {
|
|
4355
|
+
await fetch(server.url, {
|
|
4356
|
+
method: "POST",
|
|
4357
|
+
headers: {
|
|
4358
|
+
"Content-Type": "text/plain",
|
|
4359
|
+
...server.authToken ? { Authorization: `Bearer ${server.authToken}` } : {}
|
|
4360
|
+
},
|
|
4361
|
+
body: mindcache2.toMarkdown()
|
|
4362
|
+
});
|
|
4363
|
+
}
|
|
4364
|
+
if (mountedRef.current) {
|
|
4365
|
+
setLastSyncAt(/* @__PURE__ */ new Date());
|
|
4366
|
+
setHasLocalChanges(false);
|
|
4367
|
+
setStatus("idle");
|
|
4368
|
+
}
|
|
4369
|
+
} catch (err) {
|
|
4370
|
+
if (mountedRef.current) {
|
|
4371
|
+
const error2 = err instanceof Error ? err : new Error(String(err));
|
|
4372
|
+
setError(error2);
|
|
4373
|
+
setStatus("error");
|
|
4374
|
+
}
|
|
4375
|
+
throw err;
|
|
4376
|
+
}
|
|
4377
|
+
}, [mindcache2, gitstore, server, getToken]);
|
|
4378
|
+
const sync = React2.useCallback(async () => {
|
|
4379
|
+
setStatus("syncing");
|
|
4380
|
+
try {
|
|
4381
|
+
await load();
|
|
4382
|
+
if (hasLocalChanges) {
|
|
4383
|
+
await save();
|
|
4384
|
+
}
|
|
4385
|
+
} catch (err) {
|
|
4386
|
+
}
|
|
4387
|
+
}, [load, save, hasLocalChanges]);
|
|
4388
|
+
const markSaved = React2.useCallback(() => {
|
|
4389
|
+
setHasLocalChanges(false);
|
|
4390
|
+
}, []);
|
|
4391
|
+
React2.useEffect(() => {
|
|
4392
|
+
if (!mindcache2 || !gitstore || saveDebounceMs <= 0) {
|
|
4393
|
+
return;
|
|
4394
|
+
}
|
|
4395
|
+
const unsubscribe = mindcache2.subscribeToAll(() => {
|
|
4396
|
+
setHasLocalChanges(true);
|
|
4397
|
+
if (saveTimeoutRef.current) {
|
|
4398
|
+
clearTimeout(saveTimeoutRef.current);
|
|
4399
|
+
}
|
|
4400
|
+
saveTimeoutRef.current = setTimeout(() => {
|
|
4401
|
+
save("Auto-save").catch(console.error);
|
|
4402
|
+
}, saveDebounceMs);
|
|
4403
|
+
});
|
|
4404
|
+
return () => {
|
|
4405
|
+
unsubscribe();
|
|
4406
|
+
if (saveTimeoutRef.current) {
|
|
4407
|
+
clearTimeout(saveTimeoutRef.current);
|
|
4408
|
+
}
|
|
4409
|
+
};
|
|
4410
|
+
}, [mindcache2, gitstore, saveDebounceMs, save]);
|
|
4411
|
+
React2.useEffect(() => {
|
|
4412
|
+
if (!mindcache2 || autoSyncInterval <= 0) {
|
|
4413
|
+
return;
|
|
4414
|
+
}
|
|
4415
|
+
syncIntervalRef.current = setInterval(() => {
|
|
4416
|
+
sync().catch(console.error);
|
|
4417
|
+
}, autoSyncInterval);
|
|
4418
|
+
return () => {
|
|
4419
|
+
if (syncIntervalRef.current) {
|
|
4420
|
+
clearInterval(syncIntervalRef.current);
|
|
4421
|
+
}
|
|
4422
|
+
};
|
|
4423
|
+
}, [mindcache2, autoSyncInterval, sync]);
|
|
4424
|
+
React2.useEffect(() => {
|
|
4425
|
+
if (loadOnMount && mindcache2 && (gitstore || server)) {
|
|
4426
|
+
load().catch(console.error);
|
|
4427
|
+
}
|
|
4428
|
+
}, [mindcache2, gitstore, server, loadOnMount]);
|
|
4429
|
+
React2.useEffect(() => {
|
|
4430
|
+
mountedRef.current = true;
|
|
4431
|
+
return () => {
|
|
4432
|
+
mountedRef.current = false;
|
|
4433
|
+
};
|
|
4434
|
+
}, []);
|
|
4435
|
+
return {
|
|
4436
|
+
status,
|
|
4437
|
+
error,
|
|
4438
|
+
lastSyncAt,
|
|
4439
|
+
hasLocalChanges,
|
|
4440
|
+
load,
|
|
4441
|
+
save,
|
|
4442
|
+
sync,
|
|
4443
|
+
markSaved
|
|
4444
|
+
};
|
|
4445
|
+
}
|
|
3113
4446
|
|
|
3114
4447
|
// src/index.ts
|
|
3115
4448
|
var mindcache = new MindCache();
|
|
3116
4449
|
|
|
3117
4450
|
exports.DEFAULT_KEY_ATTRIBUTES = DEFAULT_KEY_ATTRIBUTES;
|
|
3118
4451
|
exports.MindCache = MindCache;
|
|
4452
|
+
exports.MindCacheChat = MindCacheChat;
|
|
4453
|
+
exports.MindCacheProvider = MindCacheProvider;
|
|
3119
4454
|
exports.OAuthClient = OAuthClient;
|
|
4455
|
+
exports.SchemaParser = SchemaParser;
|
|
3120
4456
|
exports.SystemTagHelpers = SystemTagHelpers;
|
|
3121
4457
|
exports.createOAuthClient = createOAuthClient;
|
|
3122
4458
|
exports.mindcache = mindcache;
|
|
4459
|
+
exports.useClientChat = useClientChat;
|
|
4460
|
+
exports.useLocalFirstSync = useLocalFirstSync;
|
|
3123
4461
|
exports.useMindCache = useMindCache;
|
|
4462
|
+
exports.useMindCacheContext = useMindCacheContext;
|
|
3124
4463
|
//# sourceMappingURL=index.js.map
|
|
3125
4464
|
//# sourceMappingURL=index.js.map
|