amalgm 0.1.103 → 0.1.104
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/runtime/scripts/amalgm-mcp/lib/email-embeds.js +169 -81
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-letter-c-00599c-ffffff.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-letter-c-5c6bc0-ffffff.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-letter-c-68217a-ffffff.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-letter-go-00add8-ffffff.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-letter-j-e76f00-ffffff.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-letter-js-f7df1e-1c1917.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-letter-kt-7f52ff-ffffff.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-letter-php-777bb4-ffffff.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-letter-rb-cc342d-ffffff.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-letter-rs-ce4a1f-ffffff.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-letter-s-cd6799-ffffff.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-letter-sw-f05138-ffffff.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-letter-ts-3178c6-ffffff.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-letter-v-42b883-ffffff.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-lucide-box-ffffff.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-lucide-braces-facc15.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-lucide-braces-fb923c.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-lucide-calendarclock-d6d3d1.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-lucide-database-60a5fa.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-lucide-externallink-ffffff.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-lucide-file-a8a29e.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-lucide-filearchive-facc15.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-lucide-fileimage-2dd4bf.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-lucide-fileimage-c084fc.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-lucide-filespreadsheet-4ade80.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-lucide-filetext-60a5fa.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-lucide-filetext-a8a29e.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-lucide-filetext-f87171.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-lucide-folder-d6d3d1.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-lucide-globe-d6d3d1.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-lucide-lock-facc15.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-lucide-messagesquare-ffffff.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-lucide-music-f472b6.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-lucide-package-38bdf8.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-lucide-package-facc15.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-lucide-settings-a8a29e.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-lucide-settings-d6d3d1.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-lucide-settings-f87171.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-lucide-terminal-4ade80.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-lucide-video-f472b6.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-lucide-zap-fbbf24.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-path-css3-1572b6.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-path-html5-e34f26.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-path-markdown-a8a29e.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-path-python-4b8bbe.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-react.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-skill.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icons.js +53 -0
- package/runtime/scripts/amalgm-mcp/lib/email-md.js +18 -3
- package/runtime/scripts/amalgm-mcp/lib/file-icon-registry.json +995 -0
- package/runtime/scripts/amalgm-mcp/notify/email-media.js +16 -4
- package/runtime/scripts/amalgm-mcp/notify/index.js +6 -1
- package/runtime/scripts/amalgm-mcp/tests/email-embeds.test.js +97 -0
- package/runtime/scripts/amalgm-mcp/tests/email-media.test.js +19 -0
|
@@ -31,7 +31,19 @@ const MAX_VIDEO_BYTES = 20 * 1024 * 1024;
|
|
|
31
31
|
const MAX_TOTAL_BYTES = 22 * 1024 * 1024; // proxy caps at 25MB; leave margin
|
|
32
32
|
const MAX_ATTACHMENTS = 8;
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
function parseMediaLine(line) {
|
|
35
|
+
const match = line.trim().match(/^!\[([^\]]*)\]\((.*)\)$/);
|
|
36
|
+
if (!match) return null;
|
|
37
|
+
let src = match[2].trim();
|
|
38
|
+
let title = null;
|
|
39
|
+
const titleMatch = src.match(/^(.*)\s+"([^"]*)"$/);
|
|
40
|
+
if (titleMatch) {
|
|
41
|
+
src = titleMatch[1].trim();
|
|
42
|
+
title = titleMatch[2];
|
|
43
|
+
}
|
|
44
|
+
if (!src) return null;
|
|
45
|
+
return { alt: match[1], src, title };
|
|
46
|
+
}
|
|
35
47
|
|
|
36
48
|
/** Resolve a media src to an absolute path on this computer, or null. */
|
|
37
49
|
function resolveLocalPath(src) {
|
|
@@ -133,9 +145,9 @@ function prepareEmailMedia(message) {
|
|
|
133
145
|
};
|
|
134
146
|
|
|
135
147
|
const lines = message.split('\n').map((line) => {
|
|
136
|
-
const
|
|
137
|
-
if (!
|
|
138
|
-
const
|
|
148
|
+
const media = parseMediaLine(line);
|
|
149
|
+
if (!media) return line;
|
|
150
|
+
const { alt, src, title } = media;
|
|
139
151
|
|
|
140
152
|
if (/^(https?:|cid:|attached:|data:)/i.test(src)) return line;
|
|
141
153
|
const filePath = resolveLocalPath(src);
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
const { PROXY_BASE_URL, PROXY_TOKEN } = require('../config');
|
|
10
10
|
const { ensureFreshProxyToken, readProxyToken } = require('../../proxy-token-store');
|
|
11
11
|
const { formatNotificationEmail } = require('../lib/email-md');
|
|
12
|
+
const { prepareEmailIconAttachments } = require('../lib/email-icons');
|
|
12
13
|
const { prepareEmailMedia } = require('./email-media');
|
|
13
14
|
const { textResult, errorResult } = require('../lib/tool-result');
|
|
14
15
|
|
|
@@ -32,6 +33,10 @@ async function notifyUserViaProxy({ subject: customSubject, message, link, sessi
|
|
|
32
33
|
// inline via cid:, videos as regular attachments with a poster card.
|
|
33
34
|
const media = prepareEmailMedia(message);
|
|
34
35
|
const html = formatNotificationEmail(media.message, link || null);
|
|
36
|
+
const attachments = [
|
|
37
|
+
...media.attachments,
|
|
38
|
+
...prepareEmailIconAttachments(html, media.attachments),
|
|
39
|
+
];
|
|
35
40
|
|
|
36
41
|
try {
|
|
37
42
|
const res = await fetch(notifyUrl, {
|
|
@@ -44,7 +49,7 @@ async function notifyUserViaProxy({ subject: customSubject, message, link, sessi
|
|
|
44
49
|
subject,
|
|
45
50
|
html,
|
|
46
51
|
text: message,
|
|
47
|
-
...(
|
|
52
|
+
...(attachments.length ? { attachments } : {}),
|
|
48
53
|
...(sessionId ? { sessionId } : {}),
|
|
49
54
|
}),
|
|
50
55
|
});
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const assert = require('node:assert/strict');
|
|
4
|
+
const test = require('node:test');
|
|
5
|
+
|
|
6
|
+
const {
|
|
7
|
+
fileBasenameOf,
|
|
8
|
+
renderEntityEmbed,
|
|
9
|
+
renderMediaEmbed,
|
|
10
|
+
resolveFileEmbedIdentity,
|
|
11
|
+
} = require('../lib/email-embeds');
|
|
12
|
+
const { prepareEmailIconAttachments } = require('../lib/email-icons');
|
|
13
|
+
const { markdownToEmailHtml } = require('../lib/email-md');
|
|
14
|
+
|
|
15
|
+
test('file identity decodes local, amalgm, url, and attached sources', () => {
|
|
16
|
+
assert.equal(fileBasenameOf('/Users/me/Recordings/flower.webm'), 'flower.webm');
|
|
17
|
+
assert.equal(fileBasenameOf('amalgm://file/%2FUsers%2Fme%2Fsample.pdf'), 'sample.pdf');
|
|
18
|
+
assert.equal(fileBasenameOf('https://example.com/files/report%20final.pdf?token=abc'), 'report final.pdf');
|
|
19
|
+
assert.equal(fileBasenameOf('attached:qa-pass.webm'), 'qa-pass.webm');
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test('attached video embed uses filename for the floating tab, not markdown alt', () => {
|
|
23
|
+
const html = renderMediaEmbed('QA recording', 'attached:qa-pass.webm', 'cid:poster-1');
|
|
24
|
+
|
|
25
|
+
assert.match(html, /qa-pass\.webm/);
|
|
26
|
+
assert.doesNotMatch(html, /QA recording/);
|
|
27
|
+
assert.match(html, /role="presentation" width="100%"/);
|
|
28
|
+
assert.match(html, /display:inline-table;height:32px/);
|
|
29
|
+
assert.match(html, /cid:amalgm-icon-lucide-video-f472b6/);
|
|
30
|
+
assert.match(html, /cid:amalgm-icon-lucide-externallink-ffffff/);
|
|
31
|
+
assert.doesNotMatch(html, /<svg/);
|
|
32
|
+
assert.doesNotMatch(html, /position:absolute/);
|
|
33
|
+
assert.match(html, /font-weight:400/);
|
|
34
|
+
assert.doesNotMatch(html, /\.\.\./);
|
|
35
|
+
assert.doesNotMatch(html, /▸/);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('email icon cid references become inline png attachments', () => {
|
|
39
|
+
const html = renderMediaEmbed('QA recording', 'attached:qa-pass.webm', 'cid:poster-1');
|
|
40
|
+
const icons = prepareEmailIconAttachments(html, []);
|
|
41
|
+
|
|
42
|
+
assert.equal(icons.length, 2);
|
|
43
|
+
assert.equal(icons[0].contentType, 'image/png');
|
|
44
|
+
assert.deepEqual(
|
|
45
|
+
icons.map((attachment) => attachment.contentId).sort(),
|
|
46
|
+
['amalgm-icon-lucide-externallink-ffffff', 'amalgm-icon-lucide-video-f472b6'],
|
|
47
|
+
);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('long email embed tabs use faded title text instead of hard ellipsis', () => {
|
|
51
|
+
const html = renderMediaEmbed(
|
|
52
|
+
'QA recording',
|
|
53
|
+
'attached:amalgm-new-chat-flow-2026-06-12T05-57-32-525Z.webm',
|
|
54
|
+
'cid:poster-1',
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
assert.match(html, /amalgm-new-c/);
|
|
58
|
+
assert.match(html, /rgba\(255,255,255,0\.75\)/);
|
|
59
|
+
assert.match(html, /font-weight:400/);
|
|
60
|
+
assert.doesNotMatch(html, /\.\.\./);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test('local file fallback uses chrome identity only, not email-specific metadata rows', () => {
|
|
64
|
+
const html = renderMediaEmbed('Context label', 'amalgm://file/%2Ftmp%2Fdocx-experiment-sandbox.docx', null);
|
|
65
|
+
|
|
66
|
+
assert.match(html, /docx-experiment-sandbox\.docx/);
|
|
67
|
+
assert.doesNotMatch(html, /Context label/);
|
|
68
|
+
assert.doesNotMatch(html, /On your computer/);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('standalone media lines accept local file paths with spaces', () => {
|
|
72
|
+
const html = markdownToEmailHtml('');
|
|
73
|
+
|
|
74
|
+
assert.match(html, /sample file\.pdf/);
|
|
75
|
+
assert.match(html, /display:inline-table;height:32px/);
|
|
76
|
+
assert.doesNotMatch(html, /Context label/);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('full entity embed uses chrome identity only, not centered email-only body text', () => {
|
|
80
|
+
const html = renderEntityEmbed('full', 'app', 'scout');
|
|
81
|
+
|
|
82
|
+
assert.match(html, /scout/);
|
|
83
|
+
assert.doesNotMatch(html, /text-transform:uppercase/);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test('file identity prefers explicit filename over path and url', () => {
|
|
87
|
+
const identity = resolveFileEmbedIdentity({
|
|
88
|
+
name: 'renamed.mov',
|
|
89
|
+
path: '/tmp/original.webm',
|
|
90
|
+
url: 'https://example.com/original.webm',
|
|
91
|
+
mimeType: 'video/quicktime',
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
assert.equal(identity.label, 'renamed.mov');
|
|
95
|
+
assert.equal(identity.kind, 'video');
|
|
96
|
+
assert.equal(identity.mimeType, 'video/quicktime');
|
|
97
|
+
});
|
|
@@ -32,3 +32,22 @@ test('prepareEmailMedia attaches local video and prefers sibling animated GIF pr
|
|
|
32
32
|
fs.rmSync(dir, { recursive: true, force: true });
|
|
33
33
|
}
|
|
34
34
|
});
|
|
35
|
+
|
|
36
|
+
test('prepareEmailMedia accepts absolute media paths with spaces', () => {
|
|
37
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'amalgm email media-'));
|
|
38
|
+
try {
|
|
39
|
+
const videoPath = path.join(dir, 'qa pass.webm');
|
|
40
|
+
const gifPath = path.join(dir, 'qa pass.preview.gif');
|
|
41
|
+
fs.writeFileSync(videoPath, Buffer.from('not really a webm but enough for extension detection'));
|
|
42
|
+
fs.writeFileSync(gifPath, Buffer.from('GIF89a'));
|
|
43
|
+
|
|
44
|
+
const result = prepareEmailMedia(``);
|
|
45
|
+
|
|
46
|
+
assert.match(result.message, /!\[QA recording\]\(attached:qa%20pass\.webm "cid:poster-1"\)/);
|
|
47
|
+
assert.equal(result.attachments.length, 2);
|
|
48
|
+
assert.equal(result.attachments[0].filename, 'qa pass.webm');
|
|
49
|
+
assert.equal(result.attachments[1].filename, 'qa pass.preview.gif');
|
|
50
|
+
} finally {
|
|
51
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
52
|
+
}
|
|
53
|
+
});
|