zalo-agent-cli 1.5.0 → 1.5.1
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/package.json +1 -1
- package/src/commands/mcp.js +10 -0
- package/src/mcp/image-downloader.js +25 -2
- package/src/mcp/mcp-tools.js +20 -15
package/package.json
CHANGED
package/src/commands/mcp.js
CHANGED
|
@@ -14,6 +14,7 @@ import { registerTools } from "../mcp/mcp-tools.js";
|
|
|
14
14
|
import { createHTTPServer } from "../mcp/mcp-http-transport.js";
|
|
15
15
|
import { ZaloNotifier } from "../mcp/notifier.js";
|
|
16
16
|
import { ThreadNameCache } from "../mcp/thread-name-cache.js";
|
|
17
|
+
import { autoDownloadImage } from "../mcp/image-downloader.js";
|
|
17
18
|
|
|
18
19
|
/** Zalo close code for duplicate web session — fatal, do not retry */
|
|
19
20
|
const CLOSE_DUPLICATE = 3000;
|
|
@@ -120,6 +121,15 @@ export function registerMCPCommands(program) {
|
|
|
120
121
|
// Apply noise filter (stickers, system msgs, short emoji)
|
|
121
122
|
if (!filter.shouldKeep(normalized)) return;
|
|
122
123
|
|
|
124
|
+
// Auto-download images in background (non-blocking)
|
|
125
|
+
if (normalized.attachment?.url) {
|
|
126
|
+
const threadName = nameCache?.get(normalized.threadId)?.name || null;
|
|
127
|
+
autoDownloadImage(normalized, {
|
|
128
|
+
downloadDir: config.images?.downloadDir || undefined,
|
|
129
|
+
threadName,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
123
133
|
buffer.push(normalized.threadId, normalized);
|
|
124
134
|
notifier.onMessage(normalized);
|
|
125
135
|
console.error(`[mcp] Buffered ${normalized.threadType} msg from ${normalized.threadId}`);
|
|
@@ -75,9 +75,10 @@ function buildFileName(message, ext) {
|
|
|
75
75
|
|
|
76
76
|
/**
|
|
77
77
|
* Open a file with the system's default viewer (cross-platform).
|
|
78
|
+
* Exported for use by MCP tools.
|
|
78
79
|
* @param {string} filePath
|
|
79
80
|
*/
|
|
80
|
-
function
|
|
81
|
+
export function openFile(filePath) {
|
|
81
82
|
const cmds = { darwin: "open", win32: "start", linux: "xdg-open" };
|
|
82
83
|
const cmd = cmds[platform()] || "xdg-open";
|
|
83
84
|
// Use double quotes for paths with spaces; detach so CLI doesn't block
|
|
@@ -86,6 +87,28 @@ function openWithSystemViewer(filePath) {
|
|
|
86
87
|
});
|
|
87
88
|
}
|
|
88
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Auto-download image in background when a message arrives.
|
|
92
|
+
* Non-blocking — fires and forgets. Mutates message.attachment.localPath on success.
|
|
93
|
+
* @param {object} message - Normalized message (will be mutated with localPath)
|
|
94
|
+
* @param {object} [options]
|
|
95
|
+
* @param {string} [options.downloadDir] - Base download directory
|
|
96
|
+
* @param {string} [options.threadName] - Thread display name from cache
|
|
97
|
+
*/
|
|
98
|
+
export function autoDownloadImage(message, options = {}) {
|
|
99
|
+
if (!message.attachment?.url) return;
|
|
100
|
+
// Fire-and-forget: download in background, don't block message processing
|
|
101
|
+
downloadImage(message, { ...options, autoOpen: false }).then(
|
|
102
|
+
(result) => {
|
|
103
|
+
message.attachment.localPath = result.path;
|
|
104
|
+
console.error(`[image-dl] Saved: ${result.path}`);
|
|
105
|
+
},
|
|
106
|
+
(err) => {
|
|
107
|
+
console.error(`[image-dl] Auto-download failed: ${err.message}`);
|
|
108
|
+
},
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
89
112
|
/**
|
|
90
113
|
* Download an image from URL and save to organized local folder.
|
|
91
114
|
* @param {object} message - Normalized message with attachment.url
|
|
@@ -118,7 +141,7 @@ export async function downloadImage(message, options = {}) {
|
|
|
118
141
|
|
|
119
142
|
// Auto-open with system viewer if configured
|
|
120
143
|
if (options.autoOpen) {
|
|
121
|
-
|
|
144
|
+
openFile(filePath);
|
|
122
145
|
}
|
|
123
146
|
|
|
124
147
|
return { success: true, path: filePath, folder, fileName };
|
package/src/mcp/mcp-tools.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { z } from "zod";
|
|
7
|
-
import { downloadImage } from "./image-downloader.js";
|
|
7
|
+
import { downloadImage, openFile } from "./image-downloader.js";
|
|
8
8
|
|
|
9
9
|
/** Thread type constants matching zca-js ThreadType enum */
|
|
10
10
|
const THREAD_USER = 0;
|
|
@@ -202,19 +202,19 @@ export function registerTools(server, api, buffer, filter, config, nameCache) {
|
|
|
202
202
|
{
|
|
203
203
|
title: "View Zalo Image",
|
|
204
204
|
description:
|
|
205
|
-
"
|
|
206
|
-
"
|
|
207
|
-
"
|
|
205
|
+
"Open a Zalo image with the system viewer. Images are auto-downloaded when received, " +
|
|
206
|
+
"organized by thread folder with date/sender metadata filenames. " +
|
|
207
|
+
"If not yet downloaded, downloads first then opens.",
|
|
208
208
|
inputSchema: z.object({
|
|
209
209
|
messageId: z.string().describe("Message ID from zalo_get_messages that has an image attachment"),
|
|
210
210
|
threadId: z.string().optional().describe("Thread ID to search in. Omit to search all threads."),
|
|
211
|
-
|
|
211
|
+
open: z
|
|
212
212
|
.boolean()
|
|
213
213
|
.default(imgConfig.autoOpen ?? true)
|
|
214
|
-
.describe("Open image with system viewer
|
|
214
|
+
.describe("Open image with system viewer"),
|
|
215
215
|
}),
|
|
216
216
|
},
|
|
217
|
-
async ({ messageId, threadId,
|
|
217
|
+
async ({ messageId, threadId, open }) => {
|
|
218
218
|
try {
|
|
219
219
|
// Find the message in the buffer by ID
|
|
220
220
|
const allMessages = buffer.read(threadId, 0, 9999).messages;
|
|
@@ -222,16 +222,21 @@ export function registerTools(server, api, buffer, filter, config, nameCache) {
|
|
|
222
222
|
if (!message) return err(`Message ${messageId} not found in buffer`);
|
|
223
223
|
if (!message.attachment?.url) return err(`Message ${messageId} has no image attachment`);
|
|
224
224
|
|
|
225
|
-
//
|
|
226
|
-
|
|
225
|
+
// Use local file if already auto-downloaded, otherwise download now
|
|
226
|
+
let localPath = message.attachment.localPath;
|
|
227
|
+
if (!localPath) {
|
|
228
|
+
const threadName = nameCache?.get(message.threadId)?.name || null;
|
|
229
|
+
const result = await downloadImage(message, {
|
|
230
|
+
downloadDir: imgConfig.downloadDir || undefined,
|
|
231
|
+
autoOpen: false,
|
|
232
|
+
threadName,
|
|
233
|
+
});
|
|
234
|
+
localPath = result.path;
|
|
235
|
+
}
|
|
227
236
|
|
|
228
|
-
|
|
229
|
-
downloadDir: imgConfig.downloadDir || undefined,
|
|
230
|
-
autoOpen,
|
|
231
|
-
threadName,
|
|
232
|
-
});
|
|
237
|
+
if (open) openFile(localPath);
|
|
233
238
|
|
|
234
|
-
return ok(
|
|
239
|
+
return ok({ success: true, path: localPath });
|
|
235
240
|
} catch (e) {
|
|
236
241
|
console.error("[mcp-tools] zalo_view_image error:", e.message);
|
|
237
242
|
return err(e.message);
|