molymemo 1.0.10

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 ADDED
@@ -0,0 +1,121 @@
1
+ # MolyMemo OpenClaw Plugin
2
+
3
+ OpenClaw 插件,通过 WebSocket 连接到服务器,接收推送的内容并写入 memory 文件夹。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ npm install
9
+ npm run build
10
+ ```
11
+
12
+ ## 配置
13
+
14
+ 在 OpenClaw 配置文件中添加插件配置:
15
+
16
+ ```json5
17
+ {
18
+ plugins: {
19
+ entries: {
20
+ "molymemo": {
21
+ enabled: true,
22
+ config: {
23
+ serverUrl: "ws://localhost:8080", // 必填,WebSocket 服务器地址
24
+ apiKey: "your-api-key", // 可选,认证密钥
25
+ reconnectInterval: 5000 // 可选,重连间隔(ms),默认 5000
26
+ }
27
+ }
28
+ }
29
+ }
30
+ }
31
+ ```
32
+
33
+ ### 配置项
34
+
35
+ | 参数 | 类型 | 必填 | 说明 |
36
+ |------|------|------|------|
37
+ | `serverUrl` | string | 是 | WebSocket 服务器地址 |
38
+ | `apiKey` | string | 否 | API 认证密钥 |
39
+ | `reconnectInterval` | number | 否 | 重连间隔(ms),默认 5000 |
40
+
41
+ ## 工作原理
42
+
43
+ 1. 插件启动时建立 WebSocket 连接到配置的服务器
44
+ 2. 监听服务器推送的消息
45
+ 3. 收到消息后将内容 append 到 `{workspaceDir}/memory/molymemo/{filepath}`
46
+ 4. 连接断开后自动重连
47
+
48
+ ## 服务器消息格式
49
+
50
+ 服务器推送的消息需要符合以下 JSON 格式:
51
+
52
+ ```json
53
+ {
54
+ "type": "memo",
55
+ "content": "要追加的内容",
56
+ "filepath": "notes/daily.md"
57
+ }
58
+ ```
59
+
60
+ ## 接口
61
+
62
+ ### Gateway RPC 方法
63
+
64
+ #### `molymemo.append`
65
+
66
+ 手动追加内容到文件。
67
+
68
+ 参数:
69
+ ```json
70
+ {
71
+ "content": "要追加的内容",
72
+ "filepath": "相对路径,如 notes/2024/01/15.md"
73
+ }
74
+ ```
75
+
76
+ #### `molymemo.status`
77
+
78
+ 获取插件状态。
79
+
80
+ 响应:
81
+ ```json
82
+ {
83
+ "serverUrl": "ws://localhost:8080",
84
+ "connected": true,
85
+ "workspaceDir": "~/.openclaw/workspace",
86
+ "molyMemosDir": "~/.openclaw/workspace/memory/molymemo"
87
+ }
88
+ ```
89
+
90
+ ### HTTP 路由
91
+
92
+ #### `POST /molymemo/append`
93
+
94
+ 手动追加内容到文件。
95
+
96
+ ```bash
97
+ curl -X POST http://localhost:port/molymemo/append \
98
+ -H "Content-Type: application/json" \
99
+ -d '{"content": "Hello World", "filepath": "test.md"}'
100
+ ```
101
+
102
+ #### `GET /molymemo/status`
103
+
104
+ 获取插件状态。
105
+
106
+ ### Agent 工具
107
+
108
+ #### `append_memo`
109
+
110
+ Agent 可调用的工具,用于追加内容到记忆文件夹。
111
+
112
+ 参数:
113
+ - `content`: 要追加的内容
114
+ - `filepath`: 相对文件路径
115
+
116
+ ## 文件结构
117
+
118
+ 内容会被追加到 `{workspaceDir}/memory/molymemo/{filepath}` 路径下。
119
+
120
+ 例如,如果 `workspaceDir` 是 `~/.openclaw/workspace`,`filepath` 是 `notes/daily.md`,
121
+ 那么内容会追加到 `~/.openclaw/workspace/memory/molymemo/notes/daily.md`。
@@ -0,0 +1,20 @@
1
+ import { z } from "zod";
2
+ import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
3
+ declare const molyMemosPlugin: {
4
+ id: string;
5
+ name: string;
6
+ description: string;
7
+ configSchema: z.ZodObject<{
8
+ apiKey: z.ZodDefault<z.ZodString>;
9
+ reconnectInterval: z.ZodOptional<z.ZodNumber>;
10
+ }, "strip", z.ZodTypeAny, {
11
+ apiKey: string;
12
+ reconnectInterval?: number | undefined;
13
+ }, {
14
+ apiKey?: string | undefined;
15
+ reconnectInterval?: number | undefined;
16
+ }>;
17
+ register(api: OpenClawPluginApi): void;
18
+ };
19
+ export default molyMemosPlugin;
20
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAsB7D,QAAA,MAAM,eAAe;;;;;;;;;;;;;;kBAML,iBAAiB;CAgKhC,CAAC;AAEF,eAAe,eAAe,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,157 @@
1
+ import { z } from "zod";
2
+ import * as fs from "node:fs";
3
+ import * as path from "node:path";
4
+ import { MolyMemoServerClient } from "./server-client.js";
5
+ import { SyncWorker } from "./sync-worker.js";
6
+ const PLUGIN_ID = "molymemo";
7
+ const SERVER_URL = "wss://idwmmi5m07.execute-api.us-east-1.amazonaws.com/online/";
8
+ const DEFAULT_API_KEY = "yf-test";
9
+ const configSchema = z.object({
10
+ apiKey: z.string().default(DEFAULT_API_KEY),
11
+ reconnectInterval: z.number().optional(),
12
+ });
13
+ const molyMemosPlugin = {
14
+ id: PLUGIN_ID,
15
+ name: "MolyMemo",
16
+ description: "Sync AI chat sessions from MolyMemo server",
17
+ configSchema,
18
+ register(api) {
19
+ const config = configSchema.parse(api.pluginConfig ?? {});
20
+ const logger = api.logger;
21
+ const getPluginDir = () => {
22
+ const stateDir = api.runtime.state.resolveStateDir();
23
+ return path.join(stateDir, PLUGIN_ID);
24
+ };
25
+ const getMemoryDir = () => {
26
+ const stateDir = api.runtime.state.resolveStateDir();
27
+ return path.join(stateDir, "memory", PLUGIN_ID);
28
+ };
29
+ const getSyncMetaDir = () => path.join(getPluginDir(), "sync-meta");
30
+ const getDataPath = (relativePath) => path.join(getMemoryDir(), relativePath);
31
+ const getMetaPath = (relativePath) => path.join(getSyncMetaDir(), relativePath.replace(/\.[^.]+$/, ".meta.json"));
32
+ const ensureDir = (filePath) => {
33
+ const dir = path.dirname(filePath);
34
+ if (!fs.existsSync(dir))
35
+ fs.mkdirSync(dir, { recursive: true });
36
+ };
37
+ const readMeta = (relativePath) => {
38
+ try {
39
+ const raw = fs.readFileSync(getMetaPath(relativePath), "utf-8");
40
+ return JSON.parse(raw);
41
+ }
42
+ catch {
43
+ return null;
44
+ }
45
+ };
46
+ const writeMeta = (relativePath, meta) => {
47
+ const metaPath = getMetaPath(relativePath);
48
+ ensureDir(metaPath);
49
+ fs.writeFileSync(metaPath, JSON.stringify(meta) + "\n", "utf-8");
50
+ };
51
+ const scanLocalFiles = () => {
52
+ const metaDir = getSyncMetaDir();
53
+ if (!fs.existsSync(metaDir))
54
+ return [];
55
+ const files = [];
56
+ const platforms = fs.readdirSync(metaDir, { withFileTypes: true });
57
+ for (const platform of platforms) {
58
+ if (!platform.isDirectory())
59
+ continue;
60
+ const platformDir = path.join(metaDir, platform.name);
61
+ const entries = fs.readdirSync(platformDir, { withFileTypes: true });
62
+ for (const entry of entries) {
63
+ if (!entry.isFile() || !entry.name.endsWith(".meta.json"))
64
+ continue;
65
+ const sessionId = entry.name.replace(/\.meta\.json$/, "");
66
+ const relativePath = `${platform.name}/${sessionId}.json`;
67
+ const meta = readMeta(relativePath);
68
+ files.push({ path: relativePath, last_sync_at: meta?.latest_at ?? 0 });
69
+ }
70
+ }
71
+ return files;
72
+ };
73
+ const writeFileData = (msg) => {
74
+ const dataPath = getDataPath(msg.path);
75
+ ensureDir(dataPath);
76
+ const cutoff = syncWorker.getLocalTimestamp(msg.path);
77
+ const newMessages = msg.messages.filter(m => m.created_at > cutoff);
78
+ if (newMessages.length > 0) {
79
+ const lines = newMessages.map(m => JSON.stringify(m)).join("\n") + "\n";
80
+ fs.appendFileSync(dataPath, lines, "utf-8");
81
+ }
82
+ const existingMeta = readMeta(msg.path);
83
+ const prevLines = existingMeta?.lines ?? 0;
84
+ const meta = {
85
+ latest_at: msg.latest_at,
86
+ lines: prevLines + newMessages.length,
87
+ };
88
+ if (msg.title)
89
+ meta.title = msg.title;
90
+ else if (existingMeta?.title)
91
+ meta.title = existingMeta.title;
92
+ if (msg.url)
93
+ meta.url = msg.url;
94
+ else if (existingMeta?.url)
95
+ meta.url = existingMeta.url;
96
+ writeMeta(msg.path, meta);
97
+ logger.info(`Synced ${msg.path}: +${newMessages.length} messages (total ${meta.lines} lines)`);
98
+ };
99
+ const serverClient = new MolyMemoServerClient({
100
+ serverUrl: SERVER_URL,
101
+ apiKey: config.apiKey,
102
+ reconnectInterval: config.reconnectInterval,
103
+ });
104
+ serverClient.setLogger(logger);
105
+ const syncWorker = new SyncWorker((filePath, since) => serverClient.sendPullFile(filePath, since));
106
+ syncWorker.setLogger(logger);
107
+ serverClient.setHandlers({
108
+ onConnected: () => {
109
+ const files = scanLocalFiles();
110
+ for (const f of files)
111
+ syncWorker.setLocalTimestamp(f.path, f.last_sync_at);
112
+ logger.info(`Sending check_updates with ${files.length} local files`);
113
+ serverClient.sendCheckUpdates(files);
114
+ },
115
+ onUpdates: (msg) => {
116
+ logger.info(`${msg.updates.length} files need updating`);
117
+ for (const u of msg.updates)
118
+ syncWorker.queueSync(u.path, u.latest_at);
119
+ },
120
+ onFileData: (msg) => {
121
+ writeFileData(msg);
122
+ syncWorker.onPullComplete(msg.path, msg.latest_at);
123
+ },
124
+ onFileUpdated: (msg) => {
125
+ logger.info(`File updated: ${msg.path}`);
126
+ syncWorker.queueSync(msg.path, msg.latest_at);
127
+ },
128
+ });
129
+ api.registerGatewayMethod("molymemo.status", async ({ respond }) => {
130
+ respond(true, {
131
+ serverUrl: SERVER_URL,
132
+ connected: serverClient.isConnected(),
133
+ dataDir: getMemoryDir(),
134
+ });
135
+ });
136
+ api.registerService({
137
+ id: PLUGIN_ID,
138
+ start: async () => {
139
+ logger.info("MolyMemo service starting...");
140
+ const connected = await serverClient.connect();
141
+ if (connected) {
142
+ logger.info("Connected to MolyMemo server");
143
+ }
144
+ else {
145
+ logger.warn("Failed to connect, will retry...");
146
+ }
147
+ },
148
+ stop: async () => {
149
+ logger.info("MolyMemo service stopping...");
150
+ await serverClient.disconnect();
151
+ },
152
+ });
153
+ logger.info(`MolyMemo plugin registered, dir: ${getPluginDir()}`);
154
+ },
155
+ };
156
+ export default molyMemosPlugin;
157
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ {
2
+ "id": "molymemo",
3
+ "name": "MolyMemo",
4
+ "description": "Sync AI chat sessions from MolyMemo server",
5
+ "version": "1.0.0",
6
+ "configSchema": {
7
+ "type": "object",
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "apiKey": {
11
+ "type": "string",
12
+ "description": "API key for authentication",
13
+ "default": "yf-test"
14
+ },
15
+ "reconnectInterval": {
16
+ "type": "number",
17
+ "description": "Reconnect interval in milliseconds",
18
+ "default": 5000
19
+ }
20
+ },
21
+ "required": []
22
+ }
23
+ }
@@ -0,0 +1,139 @@
1
+ import { z } from "zod";
2
+ export interface PluginLogger {
3
+ debug?: (message: string) => void;
4
+ info: (message: string) => void;
5
+ warn: (message: string) => void;
6
+ error: (message: string) => void;
7
+ }
8
+ export interface ServerConfig {
9
+ serverUrl: string;
10
+ apiKey?: string;
11
+ reconnectInterval?: number;
12
+ }
13
+ export interface FileInfo {
14
+ path: string;
15
+ last_sync_at: number;
16
+ }
17
+ export interface FileUpdate {
18
+ path: string;
19
+ latest_at: number;
20
+ }
21
+ export interface MessageItem {
22
+ role: string;
23
+ content: string;
24
+ created_at: number;
25
+ }
26
+ declare const updatesMessageSchema: z.ZodObject<{
27
+ action: z.ZodLiteral<"updates">;
28
+ updates: z.ZodArray<z.ZodObject<{
29
+ path: z.ZodString;
30
+ latest_at: z.ZodNumber;
31
+ }, "strip", z.ZodTypeAny, {
32
+ path: string;
33
+ latest_at: number;
34
+ }, {
35
+ path: string;
36
+ latest_at: number;
37
+ }>, "many">;
38
+ }, "strip", z.ZodTypeAny, {
39
+ action: "updates";
40
+ updates: {
41
+ path: string;
42
+ latest_at: number;
43
+ }[];
44
+ }, {
45
+ action: "updates";
46
+ updates: {
47
+ path: string;
48
+ latest_at: number;
49
+ }[];
50
+ }>;
51
+ declare const fileDataMessageSchema: z.ZodObject<{
52
+ action: z.ZodLiteral<"file_data">;
53
+ path: z.ZodString;
54
+ messages: z.ZodArray<z.ZodObject<{
55
+ role: z.ZodString;
56
+ content: z.ZodString;
57
+ created_at: z.ZodNumber;
58
+ }, "strip", z.ZodTypeAny, {
59
+ role: string;
60
+ content: string;
61
+ created_at: number;
62
+ }, {
63
+ role: string;
64
+ content: string;
65
+ created_at: number;
66
+ }>, "many">;
67
+ latest_at: z.ZodNumber;
68
+ title: z.ZodOptional<z.ZodString>;
69
+ url: z.ZodOptional<z.ZodString>;
70
+ }, "strip", z.ZodTypeAny, {
71
+ action: "file_data";
72
+ path: string;
73
+ latest_at: number;
74
+ messages: {
75
+ role: string;
76
+ content: string;
77
+ created_at: number;
78
+ }[];
79
+ title?: string | undefined;
80
+ url?: string | undefined;
81
+ }, {
82
+ action: "file_data";
83
+ path: string;
84
+ latest_at: number;
85
+ messages: {
86
+ role: string;
87
+ content: string;
88
+ created_at: number;
89
+ }[];
90
+ title?: string | undefined;
91
+ url?: string | undefined;
92
+ }>;
93
+ declare const fileUpdatedMessageSchema: z.ZodObject<{
94
+ action: z.ZodLiteral<"file_updated">;
95
+ path: z.ZodString;
96
+ latest_at: z.ZodNumber;
97
+ }, "strip", z.ZodTypeAny, {
98
+ action: "file_updated";
99
+ path: string;
100
+ latest_at: number;
101
+ }, {
102
+ action: "file_updated";
103
+ path: string;
104
+ latest_at: number;
105
+ }>;
106
+ export type UpdatesMessage = z.infer<typeof updatesMessageSchema>;
107
+ export type FileDataMessage = z.infer<typeof fileDataMessageSchema>;
108
+ export type FileUpdatedMessage = z.infer<typeof fileUpdatedMessageSchema>;
109
+ export interface ServerMessageHandlers {
110
+ onConnected: () => void;
111
+ onUpdates: (msg: UpdatesMessage) => void;
112
+ onFileData: (msg: FileDataMessage) => void;
113
+ onFileUpdated: (msg: FileUpdatedMessage) => void;
114
+ }
115
+ export declare class MolyMemoServerClient {
116
+ private serverUrl;
117
+ private apiKey?;
118
+ private reconnectInterval;
119
+ private ws;
120
+ private connected;
121
+ private shouldReconnect;
122
+ private reconnectTimer;
123
+ private handlers;
124
+ private logger;
125
+ constructor(config: ServerConfig);
126
+ setLogger(logger: PluginLogger): void;
127
+ setHandlers(handlers: ServerMessageHandlers): void;
128
+ sendCheckUpdates(files: FileInfo[]): void;
129
+ sendPullFile(path: string, since: number): void;
130
+ connect(): Promise<boolean>;
131
+ disconnect(): Promise<void>;
132
+ isConnected(): boolean;
133
+ private sendJSON;
134
+ private doConnect;
135
+ private handleMessage;
136
+ private scheduleReconnect;
137
+ }
138
+ export {};
139
+ //# sourceMappingURL=server-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-client.d.ts","sourceRoot":"","sources":["../src/server-client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,MAAM,WAAW,YAAY;IAC3B,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CAClC;AAED,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,QAAA,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;EAGxB,CAAC;AAEH,QAAA,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAWzB,CAAC;AAEH,QAAA,MAAM,wBAAwB;;;;;;;;;;;;EAI5B,CAAC;AAEH,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAClE,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACpE,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAE1E,MAAM,WAAW,qBAAqB;IACpC,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,SAAS,EAAE,CAAC,GAAG,EAAE,cAAc,KAAK,IAAI,CAAC;IACzC,UAAU,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,IAAI,CAAC;IAC3C,aAAa,EAAE,CAAC,GAAG,EAAE,kBAAkB,KAAK,IAAI,CAAC;CAClD;AAED,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,MAAM,CAAC,CAAS;IACxB,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,EAAE,CAA0B;IACpC,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,cAAc,CAA8C;IACpE,OAAO,CAAC,QAAQ,CAAsC;IACtD,OAAO,CAAC,MAAM,CAA6B;gBAE/B,MAAM,EAAE,YAAY;IAMhC,SAAS,CAAC,MAAM,EAAE,YAAY;IAI9B,WAAW,CAAC,QAAQ,EAAE,qBAAqB;IAI3C,gBAAgB,CAAC,KAAK,EAAE,QAAQ,EAAE;IAIlC,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM;IAIlC,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;IAM3B,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAcjC,WAAW,IAAI,OAAO;IAItB,OAAO,CAAC,QAAQ;IAQhB,OAAO,CAAC,SAAS;IA6CjB,OAAO,CAAC,aAAa;IAgCrB,OAAO,CAAC,iBAAiB;CAS1B"}
@@ -0,0 +1,168 @@
1
+ import { z } from "zod";
2
+ import WebSocket from "ws";
3
+ const updatesMessageSchema = z.object({
4
+ action: z.literal("updates"),
5
+ updates: z.array(z.object({ path: z.string(), latest_at: z.number() })),
6
+ });
7
+ const fileDataMessageSchema = z.object({
8
+ action: z.literal("file_data"),
9
+ path: z.string(),
10
+ messages: z.array(z.object({
11
+ role: z.string(),
12
+ content: z.string(),
13
+ created_at: z.number(),
14
+ })),
15
+ latest_at: z.number(),
16
+ title: z.string().optional(),
17
+ url: z.string().optional(),
18
+ });
19
+ const fileUpdatedMessageSchema = z.object({
20
+ action: z.literal("file_updated"),
21
+ path: z.string(),
22
+ latest_at: z.number(),
23
+ });
24
+ export class MolyMemoServerClient {
25
+ serverUrl;
26
+ apiKey;
27
+ reconnectInterval;
28
+ ws = null;
29
+ connected = false;
30
+ shouldReconnect = false;
31
+ reconnectTimer = null;
32
+ handlers = null;
33
+ logger = null;
34
+ constructor(config) {
35
+ this.serverUrl = config.serverUrl.replace(/^http/, "ws").replace(/\/$/, "");
36
+ this.apiKey = config.apiKey;
37
+ this.reconnectInterval = config.reconnectInterval ?? 5000;
38
+ }
39
+ setLogger(logger) {
40
+ this.logger = logger;
41
+ }
42
+ setHandlers(handlers) {
43
+ this.handlers = handlers;
44
+ }
45
+ sendCheckUpdates(files) {
46
+ this.sendJSON({ action: "check_updates", files });
47
+ }
48
+ sendPullFile(path, since) {
49
+ this.sendJSON({ action: "pull_file", path, since });
50
+ }
51
+ async connect() {
52
+ if (this.ws && this.connected)
53
+ return true;
54
+ this.shouldReconnect = true;
55
+ return this.doConnect();
56
+ }
57
+ async disconnect() {
58
+ this.shouldReconnect = false;
59
+ if (this.reconnectTimer) {
60
+ clearTimeout(this.reconnectTimer);
61
+ this.reconnectTimer = null;
62
+ }
63
+ if (this.ws) {
64
+ this.ws.close();
65
+ this.ws = null;
66
+ }
67
+ this.connected = false;
68
+ this.logger?.info("WebSocket disconnected");
69
+ }
70
+ isConnected() {
71
+ return this.connected;
72
+ }
73
+ sendJSON(data) {
74
+ if (!this.ws || !this.connected) {
75
+ this.logger?.warn("Cannot send: not connected");
76
+ return;
77
+ }
78
+ this.ws.send(JSON.stringify(data));
79
+ }
80
+ doConnect() {
81
+ return new Promise((resolve) => {
82
+ if (!this.shouldReconnect) {
83
+ resolve(false);
84
+ return;
85
+ }
86
+ try {
87
+ const url = this.apiKey
88
+ ? `${this.serverUrl}?token=${encodeURIComponent(this.apiKey)}`
89
+ : this.serverUrl;
90
+ this.logger?.info(`Connecting to ${this.serverUrl}`);
91
+ this.ws = new WebSocket(url);
92
+ this.ws.on("open", () => {
93
+ this.connected = true;
94
+ this.logger?.info("WebSocket connected");
95
+ this.handlers?.onConnected();
96
+ resolve(true);
97
+ });
98
+ this.ws.on("message", (data) => {
99
+ this.handleMessage(data);
100
+ });
101
+ this.ws.on("close", (code, reason) => {
102
+ this.connected = false;
103
+ this.logger?.info(`WebSocket closed: ${code} ${reason?.toString() || ""}`);
104
+ this.scheduleReconnect();
105
+ });
106
+ this.ws.on("error", (err) => {
107
+ this.logger?.error(`WebSocket error: ${err.message}`);
108
+ this.connected = false;
109
+ resolve(false);
110
+ });
111
+ }
112
+ catch (error) {
113
+ this.logger?.error(`Failed to create WebSocket: ${error}`);
114
+ this.connected = false;
115
+ resolve(false);
116
+ }
117
+ });
118
+ }
119
+ handleMessage(data) {
120
+ try {
121
+ const raw = typeof data === "string" ? data : data.toString();
122
+ const json = JSON.parse(raw);
123
+ switch (json.action) {
124
+ case "updates": {
125
+ const r = updatesMessageSchema.safeParse(json);
126
+ if (r.success)
127
+ this.handlers?.onUpdates(r.data);
128
+ else
129
+ this.logger?.warn(`Invalid updates message: ${r.error.message}`);
130
+ break;
131
+ }
132
+ case "file_data": {
133
+ const r = fileDataMessageSchema.safeParse(json);
134
+ if (r.success)
135
+ this.handlers?.onFileData(r.data);
136
+ else
137
+ this.logger?.warn(`Invalid file_data message: ${r.error.message}`);
138
+ break;
139
+ }
140
+ case "file_updated": {
141
+ const r = fileUpdatedMessageSchema.safeParse(json);
142
+ if (r.success)
143
+ this.handlers?.onFileUpdated(r.data);
144
+ else
145
+ this.logger?.warn(`Invalid file_updated message: ${r.error.message}`);
146
+ break;
147
+ }
148
+ default:
149
+ this.logger?.debug?.(`Unknown action: ${json.action}`);
150
+ }
151
+ }
152
+ catch (error) {
153
+ this.logger?.error(`Failed to parse message: ${error}`);
154
+ }
155
+ }
156
+ scheduleReconnect() {
157
+ if (!this.shouldReconnect)
158
+ return;
159
+ if (this.reconnectTimer)
160
+ clearTimeout(this.reconnectTimer);
161
+ this.logger?.info(`Reconnecting in ${this.reconnectInterval}ms...`);
162
+ this.reconnectTimer = setTimeout(() => {
163
+ if (this.shouldReconnect)
164
+ this.doConnect().catch(() => { });
165
+ }, this.reconnectInterval);
166
+ }
167
+ }
168
+ //# sourceMappingURL=server-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-client.js","sourceRoot":"","sources":["../src/server-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { PluginLogger } from "./server-client.js";
2
+ export type PullFileFn = (path: string, since: number) => void;
3
+ export declare class SyncWorker {
4
+ private pending;
5
+ private inProgress;
6
+ private localTimestamps;
7
+ private maxConcurrent;
8
+ private pullFile;
9
+ private logger;
10
+ constructor(pullFile: PullFileFn, maxConcurrent?: number);
11
+ setLogger(logger: PluginLogger): void;
12
+ setLocalTimestamp(path: string, timestamp: number): void;
13
+ getLocalTimestamp(path: string): number;
14
+ queueSync(path: string, latestAt: number): void;
15
+ onPullComplete(path: string, latestAt: number): void;
16
+ onPullFailed(path: string): void;
17
+ private drain;
18
+ }
19
+ //# sourceMappingURL=sync-worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-worker.d.ts","sourceRoot":"","sources":["../src/sync-worker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,MAAM,MAAM,UAAU,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;AAE/D,qBAAa,UAAU;IACrB,OAAO,CAAC,OAAO,CAAkC;IACjD,OAAO,CAAC,UAAU,CAAkC;IACpD,OAAO,CAAC,eAAe,CAAkC;IACzD,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,QAAQ,CAAa;IAC7B,OAAO,CAAC,MAAM,CAA6B;gBAE/B,QAAQ,EAAE,UAAU,EAAE,aAAa,SAAI;IAKnD,SAAS,CAAC,MAAM,EAAE,YAAY;IAI9B,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM;IAIjD,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAIvC,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;IAexC,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;IAM7C,YAAY,CAAC,IAAI,EAAE,MAAM;IAKzB,OAAO,CAAC,KAAK;CAgBd"}
@@ -0,0 +1,58 @@
1
+ export class SyncWorker {
2
+ pending = new Map();
3
+ inProgress = new Map();
4
+ localTimestamps = new Map();
5
+ maxConcurrent;
6
+ pullFile;
7
+ logger = null;
8
+ constructor(pullFile, maxConcurrent = 3) {
9
+ this.pullFile = pullFile;
10
+ this.maxConcurrent = maxConcurrent;
11
+ }
12
+ setLogger(logger) {
13
+ this.logger = logger;
14
+ }
15
+ setLocalTimestamp(path, timestamp) {
16
+ this.localTimestamps.set(path, timestamp);
17
+ }
18
+ getLocalTimestamp(path) {
19
+ return this.localTimestamps.get(path) ?? 0;
20
+ }
21
+ queueSync(path, latestAt) {
22
+ const inProgressTs = this.inProgress.get(path);
23
+ if (inProgressTs !== undefined && inProgressTs >= latestAt) {
24
+ return;
25
+ }
26
+ const pendingTs = this.pending.get(path);
27
+ if (pendingTs !== undefined && pendingTs >= latestAt) {
28
+ return;
29
+ }
30
+ this.pending.set(path, latestAt);
31
+ this.drain();
32
+ }
33
+ onPullComplete(path, latestAt) {
34
+ this.inProgress.delete(path);
35
+ this.localTimestamps.set(path, latestAt);
36
+ this.drain();
37
+ }
38
+ onPullFailed(path) {
39
+ this.inProgress.delete(path);
40
+ this.drain();
41
+ }
42
+ drain() {
43
+ if (this.inProgress.size >= this.maxConcurrent)
44
+ return;
45
+ for (const [path, latestAt] of this.pending) {
46
+ if (this.inProgress.has(path))
47
+ continue;
48
+ this.pending.delete(path);
49
+ this.inProgress.set(path, latestAt);
50
+ const since = this.localTimestamps.get(path) ?? 0;
51
+ this.logger?.info(`Pulling ${path} since=${since}`);
52
+ this.pullFile(path, since);
53
+ if (this.inProgress.size >= this.maxConcurrent)
54
+ break;
55
+ }
56
+ }
57
+ }
58
+ //# sourceMappingURL=sync-worker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync-worker.js","sourceRoot":"","sources":["../src/sync-worker.ts"],"names":[],"mappings":"AAIA,MAAM,OAAO,UAAU;IACb,OAAO,GAAwB,IAAI,GAAG,EAAE,CAAC;IACzC,UAAU,GAAwB,IAAI,GAAG,EAAE,CAAC;IAC5C,eAAe,GAAwB,IAAI,GAAG,EAAE,CAAC;IACjD,aAAa,CAAS;IACtB,QAAQ,CAAa;IACrB,MAAM,GAAwB,IAAI,CAAC;IAE3C,YAAY,QAAoB,EAAE,aAAa,GAAG,CAAC;QACjD,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;IACrC,CAAC;IAED,SAAS,CAAC,MAAoB;QAC5B,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;IAED,iBAAiB,CAAC,IAAY,EAAE,SAAiB;QAC/C,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAC5C,CAAC;IAED,iBAAiB,CAAC,IAAY;QAC5B,OAAO,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7C,CAAC;IAED,SAAS,CAAC,IAAY,EAAE,QAAgB;QACtC,MAAM,YAAY,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC/C,IAAI,YAAY,KAAK,SAAS,IAAI,YAAY,IAAI,QAAQ,EAAE,CAAC;YAC3D,OAAO;QACT,CAAC;QAED,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACzC,IAAI,SAAS,KAAK,SAAS,IAAI,SAAS,IAAI,QAAQ,EAAE,CAAC;YACrD,OAAO;QACT,CAAC;QAED,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QACjC,IAAI,CAAC,KAAK,EAAE,CAAC;IACf,CAAC;IAED,cAAc,CAAC,IAAY,EAAE,QAAgB;QAC3C,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC7B,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QACzC,IAAI,CAAC,KAAK,EAAE,CAAC;IACf,CAAC;IAED,YAAY,CAAC,IAAY;QACvB,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAC7B,IAAI,CAAC,KAAK,EAAE,CAAC;IACf,CAAC;IAEO,KAAK;QACX,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,IAAI,IAAI,CAAC,aAAa;YAAE,OAAO;QAEvD,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC5C,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC;gBAAE,SAAS;YAExC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;YAC1B,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;YAEpC,MAAM,KAAK,GAAG,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAClD,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,WAAW,IAAI,UAAU,KAAK,EAAE,CAAC,CAAC;YACpD,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YAE3B,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,IAAI,IAAI,CAAC,aAAa;gBAAE,MAAM;QACxD,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,23 @@
1
+ {
2
+ "id": "molymemo",
3
+ "name": "MolyMemo",
4
+ "description": "Sync AI chat sessions from MolyMemo server",
5
+ "version": "1.0.0",
6
+ "configSchema": {
7
+ "type": "object",
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "apiKey": {
11
+ "type": "string",
12
+ "description": "API key for authentication",
13
+ "default": "yf-test"
14
+ },
15
+ "reconnectInterval": {
16
+ "type": "number",
17
+ "description": "Reconnect interval in milliseconds",
18
+ "default": 5000
19
+ }
20
+ },
21
+ "required": []
22
+ }
23
+ }
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "molymemo",
3
+ "version": "1.0.10",
4
+ "description": "OpenClaw plugin for MolyMemo - sync AI chat sessions",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "type": "module",
8
+ "scripts": {
9
+ "build": "tsc && cp openclaw.plugin.json dist/",
10
+ "dev": "tsc --watch",
11
+ "clean": "rm -rf dist",
12
+ "prepublishOnly": "npm run build"
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "openclaw.plugin.json"
17
+ ],
18
+ "keywords": [
19
+ "openclaw",
20
+ "plugin",
21
+ "molymemo",
22
+ "memory"
23
+ ],
24
+ "dependencies": {
25
+ "ws": "^8.16.0",
26
+ "zod": "^3.22.0"
27
+ },
28
+ "devDependencies": {
29
+ "@types/node": "^20.10.0",
30
+ "@types/ws": "^8.5.10",
31
+ "typescript": "^5.3.0"
32
+ },
33
+ "openclaw": {
34
+ "extensions": ["./dist/index.js"]
35
+ },
36
+ "peerDependencies": {
37
+ "openclaw": "*"
38
+ }
39
+ }