@lightcone-ai/daemon 0.9.66 → 0.9.68
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.
|
@@ -10,8 +10,8 @@ const REQUIREMENTS = {
|
|
|
10
10
|
max_text_length: 1000,
|
|
11
11
|
max_images: 9,
|
|
12
12
|
image_formats: ['jpg', 'jpeg', 'png', 'webp'],
|
|
13
|
-
required_fields: ['text'],
|
|
14
|
-
notes: '
|
|
13
|
+
required_fields: ['text', 'images'],
|
|
14
|
+
notes: '当前自动化支持上传图文,必须提供至少 1 张本地图片;标题建议 2-20 字;图片建议 3:4 竖版;话题标签写在正文末尾如 #话题名',
|
|
15
15
|
},
|
|
16
16
|
short_video: {
|
|
17
17
|
max_text_length: 1000,
|
|
@@ -146,6 +146,9 @@ function validateLocalFile(filePath, { kind, required = false, allowedExts = []
|
|
|
146
146
|
function validateMedia({ platform, contentType, images = [], video, cover }) {
|
|
147
147
|
const limits = MEDIA_LIMITS[platform] ?? MEDIA_LIMITS.xhs;
|
|
148
148
|
if (contentType === 'image_text') {
|
|
149
|
+
if (images.length === 0) {
|
|
150
|
+
throw new Error(`image_text requires at least 1 image on ${platform}. Provide absolute image paths inside the agent workspace artifacts directory.`);
|
|
151
|
+
}
|
|
149
152
|
if (images.length > limits.maxImages) throw new Error(`image_text supports at most ${limits.maxImages} images on ${platform}`);
|
|
150
153
|
return {
|
|
151
154
|
images: images.map(filePath => validateLocalFile(filePath, { kind: 'image', required: true, allowedExts: limits.imageExts })),
|
package/package.json
CHANGED
package/src/chat-bridge.js
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
3
3
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
4
|
import { z } from 'zod';
|
|
5
|
+
import { readFileSync } from 'fs';
|
|
6
|
+
import { extname } from 'path';
|
|
5
7
|
|
|
6
8
|
const cliArgs = process.argv.slice(2);
|
|
7
9
|
function getArg(name) {
|
|
@@ -17,6 +19,38 @@ const TEAM_ID = process.env.TEAM_ID || getArg('--team-id') || ''; // injec
|
|
|
17
19
|
// Current active teamId for memory isolation (defaults to spawn-time TEAM_ID)
|
|
18
20
|
let currentTeamId = TEAM_ID;
|
|
19
21
|
|
|
22
|
+
const WORKSPACE_BINARY_MIME = {
|
|
23
|
+
'.png': 'image/png',
|
|
24
|
+
'.jpg': 'image/jpeg',
|
|
25
|
+
'.jpeg': 'image/jpeg',
|
|
26
|
+
'.webp': 'image/webp',
|
|
27
|
+
'.gif': 'image/gif',
|
|
28
|
+
'.pdf': 'application/pdf',
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
function dataUrlSummary(content) {
|
|
32
|
+
if (typeof content !== 'string' || !content.startsWith('data:')) return null;
|
|
33
|
+
const commaIdx = content.indexOf(',');
|
|
34
|
+
if (commaIdx === -1) return null;
|
|
35
|
+
const header = content.slice(5, commaIdx);
|
|
36
|
+
const mime = header.split(';')[0] || 'application/octet-stream';
|
|
37
|
+
const isBase64 = header.split(';').includes('base64');
|
|
38
|
+
let bytes = null;
|
|
39
|
+
if (isBase64) {
|
|
40
|
+
const encoded = content.slice(commaIdx + 1).replace(/\s/g, '');
|
|
41
|
+
const padding = encoded.endsWith('==') ? 2 : encoded.endsWith('=') ? 1 : 0;
|
|
42
|
+
bytes = Math.floor(encoded.length * 3 / 4) - padding;
|
|
43
|
+
}
|
|
44
|
+
return { mime, bytes };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function formatBytes(bytes) {
|
|
48
|
+
if (!Number.isFinite(bytes)) return 'unknown size';
|
|
49
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
50
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
51
|
+
return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
52
|
+
}
|
|
53
|
+
|
|
20
54
|
async function api(method, path, body) {
|
|
21
55
|
const url = `${SERVER_URL}/internal/agent/${AGENT_ID}${path}`;
|
|
22
56
|
const res = await fetch(url, {
|
|
@@ -277,6 +311,15 @@ server.tool('read_workspace', 'Read a file from the shared team workspace (e.g.
|
|
|
277
311
|
if (!currentTeamId) return { content: [{ type: 'text', text: 'No team context.' }] };
|
|
278
312
|
try {
|
|
279
313
|
const data = await api('GET', `/team-memory?path=${encodeURIComponent(path)}&teamId=${encodeURIComponent(currentTeamId)}`);
|
|
314
|
+
const summary = dataUrlSummary(data.content);
|
|
315
|
+
if (summary) {
|
|
316
|
+
return {
|
|
317
|
+
content: [{
|
|
318
|
+
type: 'text',
|
|
319
|
+
text: `${path} is a binary workspace file (${summary.mime}, ${formatBytes(summary.bytes)}). Do not read it as text. Use it by path in the Files tab, or use upload_image/read_file_base64 if you need a public URL or local base64.`,
|
|
320
|
+
}],
|
|
321
|
+
};
|
|
322
|
+
}
|
|
280
323
|
return { content: [{ type: 'text', text: data.content }] };
|
|
281
324
|
} catch (err) {
|
|
282
325
|
if (err.message.includes('404')) return { content: [{ type: 'text', text: `(empty — ${path} has no content yet)` }] };
|
|
@@ -294,6 +337,19 @@ server.tool('write_workspace', 'Write a file to the shared team workspace. Use t
|
|
|
294
337
|
return { content: [{ type: 'text', text: `Saved to team workspace: ${path}` }] };
|
|
295
338
|
});
|
|
296
339
|
|
|
340
|
+
server.tool('write_workspace_file', 'Write a local file directly to the shared team workspace. Prefer this over write_workspace for images/PDFs/binary files so large base64 content never enters the model context.', {
|
|
341
|
+
file_path: z.string().describe('Absolute path to the local file, e.g. "/home/ubuntu/lightcone/public/cover.png"'),
|
|
342
|
+
path: z.string().describe('Destination path relative to team workspace root, e.g. "artifacts/cover.png"'),
|
|
343
|
+
}, async ({ file_path, path }) => {
|
|
344
|
+
if (!currentTeamId) return { content: [{ type: 'text', text: 'No team context.' }] };
|
|
345
|
+
const ext = extname(path || file_path).toLowerCase();
|
|
346
|
+
const mime = WORKSPACE_BINARY_MIME[ext] ?? 'application/octet-stream';
|
|
347
|
+
const buf = readFileSync(file_path);
|
|
348
|
+
const content = `data:${mime};base64,${buf.toString('base64')}`;
|
|
349
|
+
await api('PUT', `/team-memory?path=${encodeURIComponent(path)}&teamId=${encodeURIComponent(currentTeamId)}`, { content });
|
|
350
|
+
return { content: [{ type: 'text', text: `Saved local file to team workspace: ${path} (${mime}, ${formatBytes(buf.length)})` }] };
|
|
351
|
+
});
|
|
352
|
+
|
|
297
353
|
// ── get_credential ───────────────────────────────────────────────────────────
|
|
298
354
|
server.tool('get_credential',
|
|
299
355
|
'Retrieve decrypted credential fields for a platform granted to this agent (e.g. XHS_COOKIE for "xhs"). Use when you need to inject credentials into a browser session or external call.',
|