@lightcone-ai/daemon 0.9.67 → 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.
- package/package.json +1 -1
- package/src/chat-bridge.js +56 -0
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.',
|