amalgm 0.1.143 → 0.1.144
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/lib/local/amalgmStore.js +0 -7
- package/runtime/scripts/amalgm-mcp/apps/rest.js +24 -1
- package/runtime/scripts/amalgm-mcp/apps/supervisor.js +16 -0
- package/runtime/scripts/amalgm-mcp/browser/cli.js +1 -5
- package/runtime/scripts/amalgm-mcp/browser/engine.js +18 -3
- package/runtime/scripts/amalgm-mcp/browser/paths.js +6 -1
- package/runtime/scripts/amalgm-mcp/browser/rest.js +12 -5
- package/runtime/scripts/amalgm-mcp/computer-use/index.js +1038 -0
- package/runtime/scripts/amalgm-mcp/lib/email-embeds.js +53 -23
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-letter-aa-57534e-ffffff.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-letter-nb-f37626-ffffff.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-letter-p-d24726-ffffff.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-lucide-atsign-ffffff.png +0 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icon-assets/amalgm-icon-lucide-box-d6d3d1.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-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-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/manifest.json +56 -0
- package/runtime/scripts/amalgm-mcp/lib/email-icons.js +34 -1
- package/runtime/scripts/amalgm-mcp/lib/email-md.js +95 -51
- package/runtime/scripts/amalgm-mcp/lib/file-icon-registry.json +146 -3
- package/runtime/scripts/amalgm-mcp/lib/generated/email-render.json +153 -0
- package/runtime/scripts/amalgm-mcp/notify/index.js +5 -2
- package/runtime/scripts/amalgm-mcp/server/core-tools.js +6 -0
- package/runtime/scripts/amalgm-mcp/server/routes/apps.js +4 -0
- package/runtime/scripts/amalgm-mcp/tests/browser-cookie-push.test.js +60 -0
- package/runtime/scripts/amalgm-mcp/tests/bundle-entries.test.js +54 -0
- package/runtime/scripts/amalgm-mcp/tests/computer-use.test.js +151 -0
- package/runtime/scripts/amalgm-mcp/tests/core-tools.test.js +4 -0
- package/runtime/scripts/amalgm-mcp/tests/email-render-fixtures/citations.md +9 -0
- package/runtime/scripts/amalgm-mcp/tests/email-render-fixtures/entities.md +15 -0
- package/runtime/scripts/amalgm-mcp/tests/email-render-fixtures/links.md +5 -0
- package/runtime/scripts/amalgm-mcp/tests/email-render-fixtures/media.md +21 -0
- package/runtime/scripts/amalgm-mcp/tests/email-render-fixtures/text.md +30 -0
- package/runtime/scripts/amalgm-mcp/tests/email-render-goldens/citations.html +5 -0
- package/runtime/scripts/amalgm-mcp/tests/email-render-goldens/entities.html +8 -0
- package/runtime/scripts/amalgm-mcp/tests/email-render-goldens/links.html +3 -0
- package/runtime/scripts/amalgm-mcp/tests/email-render-goldens/media.html +11 -0
- package/runtime/scripts/amalgm-mcp/tests/email-render-goldens/notification-shell.html +11 -0
- package/runtime/scripts/amalgm-mcp/tests/email-render-goldens/text.html +12 -0
- package/runtime/scripts/amalgm-mcp/tests/email-render.test.js +82 -0
- package/runtime/scripts/amalgm-mcp/tests/mcp-surface.test.js +2 -0
- package/runtime/scripts/amalgm-mcp/tests/system-catalog.test.js +5 -0
- package/runtime/scripts/amalgm-mcp/toolbox/loadout-context.js +1 -0
- package/runtime/scripts/amalgm-mcp/toolbox/mcp-surface.js +1 -0
- package/runtime/scripts/amalgm-mcp/toolbox/store.js +18 -0
|
@@ -20,30 +20,16 @@
|
|
|
20
20
|
const embeds = require('./email-embeds');
|
|
21
21
|
const { buildEmailRenderContext, normalizeEmailLink } = require('./email-deeplinks');
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
table: 14,
|
|
34
|
-
},
|
|
35
|
-
color: {
|
|
36
|
-
text: '#ffffff',
|
|
37
|
-
textSecondary: '#d6d3d1',
|
|
38
|
-
border: '#1c1917', // stone-900 — container borders
|
|
39
|
-
tableBorder: '#292524', // stone-800 — table grid (Notion editor standard)
|
|
40
|
-
tableHeaderBg: '#141210', // stone-925 — Notion editor header row
|
|
41
|
-
bg: '#000000',
|
|
42
|
-
bgInset: '#0a0a0a', // inline-code fill
|
|
43
|
-
link: '#60a5fa', // blue-400 — hyperlinks + citations everywhere
|
|
44
|
-
},
|
|
45
|
-
radius: { inline: 6, block: 12 },
|
|
46
|
-
};
|
|
23
|
+
// GENERATED from lib/rendering/tokens.ts by
|
|
24
|
+
// scripts/amalgm-mcp/generate-email-render-assets.mts — the deterministic
|
|
25
|
+
// adapter. There is no hand-mirrored token copy anymore; change tokens.ts
|
|
26
|
+
// (or Citation.tsx for the templates below) and regenerate.
|
|
27
|
+
const RENDER_ASSETS = require('./generated/email-render.json');
|
|
28
|
+
const EMAIL_TOKENS = RENDER_ASSETS.tokens;
|
|
29
|
+
const TEMPLATES = RENDER_ASSETS.templates;
|
|
30
|
+
// Markdown block metrics — the same values app/generated/markdown-surface.css
|
|
31
|
+
// is generated from, so blocks land at identical positions in both surfaces.
|
|
32
|
+
const BLOCKS = RENDER_ASSETS.blocks;
|
|
47
33
|
|
|
48
34
|
function escapeHtmlAttr(value) {
|
|
49
35
|
return String(value)
|
|
@@ -74,14 +60,57 @@ function parseStandaloneMediaLine(line) {
|
|
|
74
60
|
return { alt: match[1], src, title };
|
|
75
61
|
}
|
|
76
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Fill an SSR'd citation template (the real <Citation> component, rendered at
|
|
65
|
+
* asset-generation time with __PLACEHOLDER__ values). label/href/title are
|
|
66
|
+
* escaped here; icon is trusted HTML (a cid: <img> or favicon <img>).
|
|
67
|
+
*/
|
|
68
|
+
function fillCitationTemplate(template, { label, href, title, iconHtml }) {
|
|
69
|
+
let html = template;
|
|
70
|
+
html = title
|
|
71
|
+
? html.replace('__TITLE__', escapeHtmlAttr(title))
|
|
72
|
+
: html.replace(' title="__TITLE__"', '');
|
|
73
|
+
if (href !== undefined) html = html.replace('__HREF__', escapeHtmlAttr(href));
|
|
74
|
+
if (iconHtml !== undefined) html = html.replace('__ICON__', iconHtml);
|
|
75
|
+
return html.replace('__LABEL__', escapeHtmlText(label));
|
|
76
|
+
}
|
|
77
|
+
|
|
77
78
|
// Inline citation for amalgm:// references (files, folders, skills, agents).
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const
|
|
84
|
-
|
|
79
|
+
// Same visual as the app: blue inline text led by the file-type icon (CID
|
|
80
|
+
// PNG raster of the same registry glyph the app shows). Non-clickable in
|
|
81
|
+
// email (local resources can't open from a mail client); the optional
|
|
82
|
+
// description survives as a title tooltip.
|
|
83
|
+
function renderAmalgmCitation(label, kindAndPath, description) {
|
|
84
|
+
const kind = String(kindAndPath || '').split('/')[0].toLowerCase();
|
|
85
|
+
let descriptor = null;
|
|
86
|
+
if (kind === 'file') descriptor = embeds.resolveFileIconDescriptor(label);
|
|
87
|
+
else if (kind === 'folder') descriptor = embeds.resolveFileIconDescriptor('', { isDirectory: true });
|
|
88
|
+
else if (kind === 'skill') descriptor = embeds.resolveFileIconDescriptor('', { isSkill: true });
|
|
89
|
+
else if (kind === 'agent') descriptor = embeds.resolveEntityIconDescriptor('agent');
|
|
90
|
+
if (!descriptor) {
|
|
91
|
+
return fillCitationTemplate(TEMPLATES.citationText, { label, title: description });
|
|
92
|
+
}
|
|
93
|
+
return fillCitationTemplate(TEMPLATES.citationTextWithIcon, {
|
|
94
|
+
label,
|
|
95
|
+
title: description,
|
|
96
|
+
iconHtml: embeds.citationIconHtml(descriptor),
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Web hyperlink as the app renders it: favicon + blue label (Citation + WebFavicon). */
|
|
101
|
+
function renderWebCitation(label, href) {
|
|
102
|
+
let domain = '';
|
|
103
|
+
try {
|
|
104
|
+
domain = new URL(href).hostname;
|
|
105
|
+
} catch {
|
|
106
|
+
domain = '';
|
|
107
|
+
}
|
|
108
|
+
if (!domain) {
|
|
109
|
+
return fillCitationTemplate(TEMPLATES.citationLink, { label, href });
|
|
110
|
+
}
|
|
111
|
+
const size = RENDER_ASSETS.chrome.citationIconSize;
|
|
112
|
+
const favicon = `<img src="https://www.google.com/s2/favicons?domain=${escapeHtmlAttr(domain)}&sz=32" alt="" width="${size}" height="${size}" style="display:inline-block;width:${size}px;height:${size}px;border:0;border-radius:3px;vertical-align:-1px">`;
|
|
113
|
+
return fillCitationTemplate(TEMPLATES.citationLinkWithIcon, { label, href, iconHtml: favicon });
|
|
85
114
|
}
|
|
86
115
|
|
|
87
116
|
/**
|
|
@@ -124,12 +153,12 @@ function inlineFormat(text, renderContext = {}) {
|
|
|
124
153
|
(_, label, kindAndPath, description) =>
|
|
125
154
|
renderAmalgmCitation(label, kindAndPath, description),
|
|
126
155
|
)
|
|
127
|
-
// Links: [text](url) —
|
|
156
|
+
// Links: [text](url) — the app's web citation: favicon + blue label.
|
|
128
157
|
.replace(
|
|
129
158
|
/\[([^\]]+)\]\(([^)]+)\)/g,
|
|
130
159
|
(_, label, url) => {
|
|
131
160
|
const href = normalizeEmailLink(url, renderContext.appOrigin) || url;
|
|
132
|
-
return
|
|
161
|
+
return renderWebCitation(label, href);
|
|
133
162
|
},
|
|
134
163
|
)
|
|
135
164
|
// Bold + italic: ***text***
|
|
@@ -145,7 +174,7 @@ function inlineFormat(text, renderContext = {}) {
|
|
|
145
174
|
// Inline code: `text`
|
|
146
175
|
.replace(
|
|
147
176
|
/`([^`]+)`/g,
|
|
148
|
-
`<code style="background:${
|
|
177
|
+
`<code style="background:${BLOCKS.codeInline.bg};border:1px solid ${BLOCKS.codeInline.border};padding:${BLOCKS.codeInline.paddingY}px ${BLOCKS.codeInline.paddingX}px;border-radius:${BLOCKS.codeInline.radius}px;font-size:${BLOCKS.codeInline.size}px;line-height:${BLOCKS.codeInline.lineHeight};font-family:${EMAIL_TOKENS.font.mono};color:${BLOCKS.codeInline.color}">$1</code>`,
|
|
149
178
|
)
|
|
150
179
|
);
|
|
151
180
|
}
|
|
@@ -157,12 +186,18 @@ function markdownToEmailHtml(md, renderContextInput = {}) {
|
|
|
157
186
|
text = text.replace(/\\n/g, '\n');
|
|
158
187
|
|
|
159
188
|
// Extract fenced code blocks first so line parser doesn't touch them.
|
|
189
|
+
// Chrome mirrors the chat CodeBlock: rounded card with a language header
|
|
190
|
+
// bar (the copy/download controls are app-only interactivity).
|
|
160
191
|
const codeBlocks = [];
|
|
161
|
-
text = text.replace(/```(\w*)\n([\s\S]*?)```/g, (_,
|
|
192
|
+
text = text.replace(/```(\w*)\n([\s\S]*?)```/g, (_, lang, code) => {
|
|
162
193
|
const escaped = code.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
163
194
|
const placeholder = `%%CODEBLOCK_${codeBlocks.length}%%`;
|
|
195
|
+
const B = BLOCKS.codeBlock;
|
|
164
196
|
codeBlocks.push(
|
|
165
|
-
`<
|
|
197
|
+
`<div style="background:${B.bg};border:1px solid ${B.border};border-radius:${B.radius}px;overflow:hidden;margin:0">` +
|
|
198
|
+
`<div style="padding:${B.header.paddingY}px ${B.header.paddingX}px;border-bottom:1px solid ${B.border};font-size:${B.header.size}px;color:${B.header.color};font-family:${EMAIL_TOKENS.font.mono};text-transform:lowercase">${escapeHtmlText(lang || 'text')}</div>` +
|
|
199
|
+
`<pre style="background:${B.bg};padding:${B.padding}px;font-size:${B.size}px;font-family:${EMAIL_TOKENS.font.mono};color:${EMAIL_TOKENS.color.text};overflow-x:auto;margin:0;line-height:${B.lineHeight}"><code>${escaped.trimEnd()}</code></pre>` +
|
|
200
|
+
`</div>`,
|
|
166
201
|
);
|
|
167
202
|
return placeholder;
|
|
168
203
|
});
|
|
@@ -178,18 +213,21 @@ function markdownToEmailHtml(md, renderContextInput = {}) {
|
|
|
178
213
|
|
|
179
214
|
function flushPara() {
|
|
180
215
|
if (!paraLines.length) return;
|
|
181
|
-
|
|
182
|
-
|
|
216
|
+
// Soft line breaks collapse to spaces, like the app's markdown pipeline.
|
|
217
|
+
const content = inlineFormat(paraLines.join(' '), renderContext);
|
|
218
|
+
out.push(
|
|
219
|
+
`<p style="margin:0;line-height:${BLOCKS.paragraph.lineHeight};color:${EMAIL_TOKENS.color.text}">${content}</p>`,
|
|
220
|
+
);
|
|
183
221
|
paraLines = [];
|
|
184
222
|
}
|
|
185
223
|
function flushList() {
|
|
186
224
|
if (!listItems.length) return;
|
|
187
225
|
const tag = listType;
|
|
188
226
|
const items = listItems
|
|
189
|
-
.map((t) => `<li style="margin
|
|
227
|
+
.map((t) => `<li style="margin:${BLOCKS.list.itemMarginY}px 0;padding:${BLOCKS.list.itemPaddingY}px 0;line-height:${BLOCKS.list.itemLineHeight};color:${EMAIL_TOKENS.color.text}">${inlineFormat(t, renderContext)}</li>`)
|
|
190
228
|
.join('');
|
|
191
229
|
out.push(
|
|
192
|
-
`<${tag} style="margin:
|
|
230
|
+
`<${tag} style="margin:0;padding-left:${BLOCKS.list.paddingLeft}px;color:${EMAIL_TOKENS.color.text}">${items}</${tag}>`,
|
|
193
231
|
);
|
|
194
232
|
listItems = [];
|
|
195
233
|
listType = '';
|
|
@@ -204,8 +242,8 @@ function markdownToEmailHtml(md, renderContextInput = {}) {
|
|
|
204
242
|
// stone-925 header row, px-3 py-1.5 cells, 14px text, wrapped in the
|
|
205
243
|
// same rounded-xl (12px) chrome as code blocks. The wrapper draws the
|
|
206
244
|
// outer border, so cells only draw their inner right/bottom edges.
|
|
207
|
-
const cellBase = `padding
|
|
208
|
-
const edge = `1px solid ${
|
|
245
|
+
const cellBase = `padding:${BLOCKS.table.cellPaddingY}px ${BLOCKS.table.cellPaddingX}px;text-align:left;vertical-align:top;font-size:${BLOCKS.table.size}px;line-height:${BLOCKS.table.cellLineHeight}`;
|
|
246
|
+
const edge = `1px solid ${BLOCKS.table.gridColor}`;
|
|
209
247
|
const cellBorders = (isLastCol, isLastRow) =>
|
|
210
248
|
`${isLastCol ? '' : `border-right:${edge};`}${isLastRow ? '' : `border-bottom:${edge};`}`;
|
|
211
249
|
const headerCells = tableRows[0].cells;
|
|
@@ -213,7 +251,7 @@ function markdownToEmailHtml(md, renderContextInput = {}) {
|
|
|
213
251
|
const hdr = headerCells
|
|
214
252
|
.map(
|
|
215
253
|
(c, i) =>
|
|
216
|
-
`<th style="${cellBase};${cellBorders(i === headerCells.length - 1, false)}background:${
|
|
254
|
+
`<th style="${cellBase};${cellBorders(i === headerCells.length - 1, false)}background:${BLOCKS.table.headerBgEmail};font-weight:${BLOCKS.table.headerWeight};color:${EMAIL_TOKENS.color.text}">${inlineFormat(c, renderContext)}</th>`,
|
|
217
255
|
)
|
|
218
256
|
.join('');
|
|
219
257
|
const body = bodyRows
|
|
@@ -230,15 +268,16 @@ function markdownToEmailHtml(md, renderContextInput = {}) {
|
|
|
230
268
|
)
|
|
231
269
|
.join('');
|
|
232
270
|
out.push(
|
|
233
|
-
`<div style="border:${edge};border-radius
|
|
271
|
+
`<div style="border:${edge};border-radius:${BLOCKS.table.radius}px;overflow:hidden;margin:0"><table style="width:100%;border-collapse:collapse"><thead><tr>${hdr}</tr></thead><tbody>${body}</tbody></table></div>`,
|
|
234
272
|
);
|
|
235
273
|
tableRows = [];
|
|
236
274
|
}
|
|
237
275
|
function flushQuote() {
|
|
238
276
|
if (!quoteLines.length) return;
|
|
239
|
-
|
|
277
|
+
// Soft line breaks collapse to spaces, like the app's markdown pipeline.
|
|
278
|
+
const content = inlineFormat(quoteLines.join(' '), renderContext);
|
|
240
279
|
out.push(
|
|
241
|
-
`<blockquote style="margin:
|
|
280
|
+
`<blockquote style="margin:0;line-height:${BLOCKS.blockquote.lineHeight};${BLOCKS.blockquote.italic ? 'font-style:italic;' : ''}padding:0 0 0 ${BLOCKS.blockquote.paddingLeft}px;border-left:${BLOCKS.blockquote.borderWidth}px solid ${BLOCKS.blockquote.borderColor};color:${BLOCKS.blockquote.color}">${content}</blockquote>`,
|
|
242
281
|
);
|
|
243
282
|
quoteLines = [];
|
|
244
283
|
}
|
|
@@ -272,7 +311,7 @@ function markdownToEmailHtml(md, renderContextInput = {}) {
|
|
|
272
311
|
}
|
|
273
312
|
if (/^(-{3,}|_{3,}|\*{3,})$/.test(trimmed)) {
|
|
274
313
|
flushAll();
|
|
275
|
-
out.push(`<hr style="border:none;border-top:1px solid ${
|
|
314
|
+
out.push(`<hr style="border:none;border-top:1px solid ${BLOCKS.hr.color};margin:0">`);
|
|
276
315
|
continue;
|
|
277
316
|
}
|
|
278
317
|
// Standalone media line → block embed (image bare, video in the
|
|
@@ -295,9 +334,9 @@ function markdownToEmailHtml(md, renderContextInput = {}) {
|
|
|
295
334
|
if (headingMatch) {
|
|
296
335
|
flushAll();
|
|
297
336
|
const level = headingMatch[1].length;
|
|
298
|
-
const
|
|
337
|
+
const spec = BLOCKS.heading[`h${Math.min(level, 4)}`];
|
|
299
338
|
out.push(
|
|
300
|
-
`<h${level} style="margin:
|
|
339
|
+
`<h${level} style="margin:0;font-size:${spec.size}px;font-weight:${BLOCKS.heading.weight};color:${EMAIL_TOKENS.color.text};line-height:${BLOCKS.heading.lineHeight}">${inlineFormat(headingMatch[2], renderContext)}</h${level}>`,
|
|
301
340
|
);
|
|
302
341
|
continue;
|
|
303
342
|
}
|
|
@@ -349,7 +388,12 @@ function markdownToEmailHtml(md, renderContextInput = {}) {
|
|
|
349
388
|
}
|
|
350
389
|
|
|
351
390
|
flushAll();
|
|
352
|
-
|
|
391
|
+
// Uniform gap between top-level blocks — the chat surface's Streamdown
|
|
392
|
+
// container applies space-y (15px at the app root font-size) with the
|
|
393
|
+
// first child's top margin removed; individual blocks carry margin:0.
|
|
394
|
+
return out
|
|
395
|
+
.map((html, index) => (index === 0 ? html : `<div style="margin:${BLOCKS.blockGap}px 0 0">${html}</div>`))
|
|
396
|
+
.join('\n');
|
|
353
397
|
}
|
|
354
398
|
|
|
355
399
|
/**
|
|
@@ -363,7 +407,7 @@ function formatNotificationEmail(message, link, renderContextInput = {}) {
|
|
|
363
407
|
: '';
|
|
364
408
|
return `<!DOCTYPE html>
|
|
365
409
|
<html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"></head>
|
|
366
|
-
<body style="margin:0;padding:0;background:#000000;font-family:${EMAIL_TOKENS.font.text}
|
|
410
|
+
<body style="margin:0;padding:0;background:#000000;font-family:${EMAIL_TOKENS.font.text}">
|
|
367
411
|
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:#000000"><tr><td align="center" style="padding:0">
|
|
368
412
|
<table role="presentation" width="520" cellpadding="0" cellspacing="0" style="max-width:520px;width:100%"><tr><td style="padding:40px 28px 48px">
|
|
369
413
|
<div style="font-size:${EMAIL_TOKENS.type.body}px;color:#ffffff">${htmlMessage}</div>
|
|
@@ -14,9 +14,10 @@
|
|
|
14
14
|
"color": "#d6d3d1"
|
|
15
15
|
},
|
|
16
16
|
"skill": {
|
|
17
|
-
"kind": "
|
|
18
|
-
"
|
|
19
|
-
"
|
|
17
|
+
"kind": "lucide",
|
|
18
|
+
"name": "Box",
|
|
19
|
+
"colorClass": "text-stone-300",
|
|
20
|
+
"color": "#d6d3d1"
|
|
20
21
|
}
|
|
21
22
|
},
|
|
22
23
|
"extensionIcons": {
|
|
@@ -418,6 +419,126 @@
|
|
|
418
419
|
"name": "FileArchive",
|
|
419
420
|
"colorClass": "text-yellow-400",
|
|
420
421
|
"color": "#facc15"
|
|
422
|
+
},
|
|
423
|
+
"pptx": {
|
|
424
|
+
"kind": "letter",
|
|
425
|
+
"letters": "P",
|
|
426
|
+
"bg": "#D24726",
|
|
427
|
+
"fg": "#ffffff"
|
|
428
|
+
},
|
|
429
|
+
"ppt": {
|
|
430
|
+
"kind": "letter",
|
|
431
|
+
"letters": "P",
|
|
432
|
+
"bg": "#D24726",
|
|
433
|
+
"fg": "#ffffff"
|
|
434
|
+
},
|
|
435
|
+
"ipynb": {
|
|
436
|
+
"kind": "letter",
|
|
437
|
+
"letters": "Nb",
|
|
438
|
+
"bg": "#F37626",
|
|
439
|
+
"fg": "#ffffff"
|
|
440
|
+
},
|
|
441
|
+
"ttf": {
|
|
442
|
+
"kind": "letter",
|
|
443
|
+
"letters": "Aa",
|
|
444
|
+
"bg": "#57534E",
|
|
445
|
+
"fg": "#ffffff"
|
|
446
|
+
},
|
|
447
|
+
"otf": {
|
|
448
|
+
"kind": "letter",
|
|
449
|
+
"letters": "Aa",
|
|
450
|
+
"bg": "#57534E",
|
|
451
|
+
"fg": "#ffffff"
|
|
452
|
+
},
|
|
453
|
+
"woff": {
|
|
454
|
+
"kind": "letter",
|
|
455
|
+
"letters": "Aa",
|
|
456
|
+
"bg": "#57534E",
|
|
457
|
+
"fg": "#ffffff"
|
|
458
|
+
},
|
|
459
|
+
"woff2": {
|
|
460
|
+
"kind": "letter",
|
|
461
|
+
"letters": "Aa",
|
|
462
|
+
"bg": "#57534E",
|
|
463
|
+
"fg": "#ffffff"
|
|
464
|
+
},
|
|
465
|
+
"m4a": {
|
|
466
|
+
"kind": "lucide",
|
|
467
|
+
"name": "Music",
|
|
468
|
+
"colorClass": "text-pink-400",
|
|
469
|
+
"color": "#f472b6"
|
|
470
|
+
},
|
|
471
|
+
"flac": {
|
|
472
|
+
"kind": "lucide",
|
|
473
|
+
"name": "Music",
|
|
474
|
+
"colorClass": "text-pink-400",
|
|
475
|
+
"color": "#f472b6"
|
|
476
|
+
},
|
|
477
|
+
"ogg": {
|
|
478
|
+
"kind": "lucide",
|
|
479
|
+
"name": "Music",
|
|
480
|
+
"colorClass": "text-pink-400",
|
|
481
|
+
"color": "#f472b6"
|
|
482
|
+
},
|
|
483
|
+
"aac": {
|
|
484
|
+
"kind": "lucide",
|
|
485
|
+
"name": "Music",
|
|
486
|
+
"colorClass": "text-pink-400",
|
|
487
|
+
"color": "#f472b6"
|
|
488
|
+
},
|
|
489
|
+
"opus": {
|
|
490
|
+
"kind": "lucide",
|
|
491
|
+
"name": "Music",
|
|
492
|
+
"colorClass": "text-pink-400",
|
|
493
|
+
"color": "#f472b6"
|
|
494
|
+
},
|
|
495
|
+
"avif": {
|
|
496
|
+
"kind": "lucide",
|
|
497
|
+
"name": "FileImage",
|
|
498
|
+
"colorClass": "text-purple-400",
|
|
499
|
+
"color": "#c084fc"
|
|
500
|
+
},
|
|
501
|
+
"bmp": {
|
|
502
|
+
"kind": "lucide",
|
|
503
|
+
"name": "FileImage",
|
|
504
|
+
"colorClass": "text-purple-400",
|
|
505
|
+
"color": "#c084fc"
|
|
506
|
+
},
|
|
507
|
+
"avi": {
|
|
508
|
+
"kind": "lucide",
|
|
509
|
+
"name": "Video",
|
|
510
|
+
"colorClass": "text-pink-400",
|
|
511
|
+
"color": "#f472b6"
|
|
512
|
+
},
|
|
513
|
+
"ogv": {
|
|
514
|
+
"kind": "lucide",
|
|
515
|
+
"name": "Video",
|
|
516
|
+
"colorClass": "text-pink-400",
|
|
517
|
+
"color": "#f472b6"
|
|
518
|
+
},
|
|
519
|
+
"jsonl": {
|
|
520
|
+
"kind": "lucide",
|
|
521
|
+
"name": "Braces",
|
|
522
|
+
"colorClass": "text-yellow-400",
|
|
523
|
+
"color": "#facc15"
|
|
524
|
+
},
|
|
525
|
+
"ndjson": {
|
|
526
|
+
"kind": "lucide",
|
|
527
|
+
"name": "Braces",
|
|
528
|
+
"colorClass": "text-yellow-400",
|
|
529
|
+
"color": "#facc15"
|
|
530
|
+
},
|
|
531
|
+
"diff": {
|
|
532
|
+
"kind": "lucide",
|
|
533
|
+
"name": "FileText",
|
|
534
|
+
"colorClass": "text-stone-400",
|
|
535
|
+
"color": "#a8a29e"
|
|
536
|
+
},
|
|
537
|
+
"patch": {
|
|
538
|
+
"kind": "lucide",
|
|
539
|
+
"name": "FileText",
|
|
540
|
+
"colorClass": "text-stone-400",
|
|
541
|
+
"color": "#a8a29e"
|
|
421
542
|
}
|
|
422
543
|
},
|
|
423
544
|
"specialFilenameIcons": [
|
|
@@ -553,6 +674,12 @@
|
|
|
553
674
|
"name": "Box",
|
|
554
675
|
"colorClass": "text-white",
|
|
555
676
|
"color": "#ffffff"
|
|
677
|
+
},
|
|
678
|
+
"agent": {
|
|
679
|
+
"kind": "lucide",
|
|
680
|
+
"name": "AtSign",
|
|
681
|
+
"colorClass": "text-white",
|
|
682
|
+
"color": "#ffffff"
|
|
556
683
|
}
|
|
557
684
|
},
|
|
558
685
|
"lucideNodes": {
|
|
@@ -990,6 +1117,22 @@
|
|
|
990
1117
|
"d": "M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z"
|
|
991
1118
|
}
|
|
992
1119
|
]
|
|
1120
|
+
],
|
|
1121
|
+
"AtSign": [
|
|
1122
|
+
[
|
|
1123
|
+
"circle",
|
|
1124
|
+
{
|
|
1125
|
+
"cx": "12",
|
|
1126
|
+
"cy": "12",
|
|
1127
|
+
"r": "4"
|
|
1128
|
+
}
|
|
1129
|
+
],
|
|
1130
|
+
[
|
|
1131
|
+
"path",
|
|
1132
|
+
{
|
|
1133
|
+
"d": "M16 8v5a3 3 0 0 0 6 0v-1a10 10 0 1 0-4 8"
|
|
1134
|
+
}
|
|
1135
|
+
]
|
|
993
1136
|
]
|
|
994
1137
|
}
|
|
995
1138
|
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
{
|
|
2
|
+
"//": "GENERATED by scripts/amalgm-mcp/generate-email-render-assets.mts — do not edit.",
|
|
3
|
+
"version": 1,
|
|
4
|
+
"tokens": {
|
|
5
|
+
"font": {
|
|
6
|
+
"text": "\"SF Pro Text\", \"SF Pro Display\", -apple-system, BlinkMacSystemFont, system-ui, sans-serif",
|
|
7
|
+
"mono": "'JetBrains Mono', 'SFMono-Regular', Menlo, monospace"
|
|
8
|
+
},
|
|
9
|
+
"type": {
|
|
10
|
+
"body": 14,
|
|
11
|
+
"headings": {
|
|
12
|
+
"1": 18,
|
|
13
|
+
"2": 16,
|
|
14
|
+
"3": 15,
|
|
15
|
+
"4": 14,
|
|
16
|
+
"5": 14,
|
|
17
|
+
"6": 14
|
|
18
|
+
},
|
|
19
|
+
"code": 12,
|
|
20
|
+
"table": 14
|
|
21
|
+
},
|
|
22
|
+
"color": {
|
|
23
|
+
"text": "#ffffff",
|
|
24
|
+
"textSecondary": "#d6d3d1",
|
|
25
|
+
"textMuted": "#a8a29e",
|
|
26
|
+
"faint": "#57534e",
|
|
27
|
+
"border": "#1c1917",
|
|
28
|
+
"tableBorder": "#292524",
|
|
29
|
+
"tableHeaderBg": "#141210",
|
|
30
|
+
"bg": "#000000",
|
|
31
|
+
"bgInset": "#0a0a0a",
|
|
32
|
+
"bgChip": "#0c0a09",
|
|
33
|
+
"link": "#60a5fa",
|
|
34
|
+
"linkHover": "#93c5fd"
|
|
35
|
+
},
|
|
36
|
+
"radius": {
|
|
37
|
+
"inline": 6,
|
|
38
|
+
"block": 12,
|
|
39
|
+
"embed": 16
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"chrome": {
|
|
43
|
+
"pillTableCss": "display:inline-table;height:32px;background:#0c0a09;border:1px solid #292524;border-radius:12px;border-collapse:separate;box-shadow:0 25px 50px -12px rgba(0,0,0,0.25)",
|
|
44
|
+
"pillHeight": 32,
|
|
45
|
+
"pillFontSize": 13,
|
|
46
|
+
"pillIconSize": 14,
|
|
47
|
+
"pillPaddingX": 12,
|
|
48
|
+
"pillGap": 8,
|
|
49
|
+
"pillText": "#ffffff",
|
|
50
|
+
"frameCss": "border:1px solid #292524;border-radius:16px;background:#000000;margin:0;overflow:hidden",
|
|
51
|
+
"frameBg": "#000000",
|
|
52
|
+
"entityTileCss": "text-decoration:none;display:inline-block;background:#000000;border:1px solid #1c1917;border-radius:12px;padding:8px 12px;margin:2px 0;vertical-align:middle",
|
|
53
|
+
"entityTileFontSize": 14,
|
|
54
|
+
"entityTileFontWeight": 650,
|
|
55
|
+
"entityTileIconSize": 14,
|
|
56
|
+
"entityTileText": "#ffffff",
|
|
57
|
+
"citationIconSize": 12
|
|
58
|
+
},
|
|
59
|
+
"blocks": {
|
|
60
|
+
"blockGap": 15,
|
|
61
|
+
"heading": {
|
|
62
|
+
"lineHeight": 1.35,
|
|
63
|
+
"weight": 650,
|
|
64
|
+
"h1": {
|
|
65
|
+
"size": 18,
|
|
66
|
+
"marginTop": 20,
|
|
67
|
+
"marginBottom": 8
|
|
68
|
+
},
|
|
69
|
+
"h2": {
|
|
70
|
+
"size": 16,
|
|
71
|
+
"marginTop": 18,
|
|
72
|
+
"marginBottom": 6
|
|
73
|
+
},
|
|
74
|
+
"h3": {
|
|
75
|
+
"size": 15,
|
|
76
|
+
"marginTop": 16,
|
|
77
|
+
"marginBottom": 6
|
|
78
|
+
},
|
|
79
|
+
"h4": {
|
|
80
|
+
"size": 14,
|
|
81
|
+
"marginTop": 14,
|
|
82
|
+
"marginBottom": 4
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
"paragraph": {
|
|
86
|
+
"lineHeight": 1.75,
|
|
87
|
+
"marginY": 4
|
|
88
|
+
},
|
|
89
|
+
"list": {
|
|
90
|
+
"marginY": 8,
|
|
91
|
+
"paddingLeft": 24,
|
|
92
|
+
"itemMarginY": 4,
|
|
93
|
+
"itemLineHeight": 1.5,
|
|
94
|
+
"itemPaddingY": 3.75
|
|
95
|
+
},
|
|
96
|
+
"blockquote": {
|
|
97
|
+
"marginY": 14,
|
|
98
|
+
"borderWidth": 3,
|
|
99
|
+
"borderColor": "#292524",
|
|
100
|
+
"paddingLeft": 16,
|
|
101
|
+
"color": "#d6d3d1",
|
|
102
|
+
"italic": true,
|
|
103
|
+
"lineHeight": 1.75
|
|
104
|
+
},
|
|
105
|
+
"codeInline": {
|
|
106
|
+
"size": 12,
|
|
107
|
+
"lineHeight": 1.2,
|
|
108
|
+
"radius": 5.625,
|
|
109
|
+
"border": "#1c1917",
|
|
110
|
+
"bg": "#0a0a0a",
|
|
111
|
+
"color": "#f5f5f4",
|
|
112
|
+
"paddingX": 7.5,
|
|
113
|
+
"paddingY": 1.875
|
|
114
|
+
},
|
|
115
|
+
"codeBlock": {
|
|
116
|
+
"size": 12,
|
|
117
|
+
"lineHeight": 1.6,
|
|
118
|
+
"marginY": 16,
|
|
119
|
+
"radius": 12,
|
|
120
|
+
"border": "#1c1917",
|
|
121
|
+
"bg": "#000000",
|
|
122
|
+
"padding": 15,
|
|
123
|
+
"header": {
|
|
124
|
+
"paddingX": 15,
|
|
125
|
+
"paddingY": 7.5,
|
|
126
|
+
"size": 11.25,
|
|
127
|
+
"color": "#a8a29e"
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
"table": {
|
|
131
|
+
"size": 14,
|
|
132
|
+
"marginY": 16,
|
|
133
|
+
"radius": 12,
|
|
134
|
+
"gridColor": "#292524",
|
|
135
|
+
"headerBg": "rgb(20 18 16 / 0.6)",
|
|
136
|
+
"headerWeight": 500,
|
|
137
|
+
"cellPaddingX": 11.25,
|
|
138
|
+
"cellPaddingY": 5.625,
|
|
139
|
+
"cellLineHeight": 1.5,
|
|
140
|
+
"headerBgEmail": "#0c0b0a"
|
|
141
|
+
},
|
|
142
|
+
"hr": {
|
|
143
|
+
"color": "#1c1917",
|
|
144
|
+
"marginY": 24
|
|
145
|
+
}
|
|
146
|
+
},
|
|
147
|
+
"templates": {
|
|
148
|
+
"citationLink": "<a href=\"__HREF__\" target=\"_blank\" rel=\"noreferrer\" title=\"__TITLE__\" class=\"render-citation\" style=\"display:inline;vertical-align:baseline;font:inherit;font-size:inherit;line-height:inherit;color:#60a5fa;text-decoration:none;white-space:nowrap;padding:0;background:transparent;border:none;\">__LABEL__</a>",
|
|
149
|
+
"citationLinkWithIcon": "<a href=\"__HREF__\" target=\"_blank\" rel=\"noreferrer\" title=\"__TITLE__\" class=\"render-citation\" style=\"display:inline;vertical-align:baseline;font:inherit;font-size:inherit;line-height:inherit;color:#60a5fa;text-decoration:none;white-space:nowrap;padding:0;background:transparent;border:none;\"><span style=\"display:inline-block;width:12px;height:12px;font-size:12px;line-height:12px;margin-right:0.3em;vertical-align:middle\">__ICON__</span>__LABEL__</a>",
|
|
150
|
+
"citationText": "<span title=\"__TITLE__\" class=\"render-citation\" style=\"display:inline;vertical-align:baseline;font:inherit;font-size:inherit;line-height:inherit;color:#60a5fa;text-decoration:none;white-space:nowrap;padding:0;background:transparent;border:none;\">__LABEL__</span>",
|
|
151
|
+
"citationTextWithIcon": "<span title=\"__TITLE__\" class=\"render-citation\" style=\"display:inline;vertical-align:baseline;font:inherit;font-size:inherit;line-height:inherit;color:#60a5fa;text-decoration:none;white-space:nowrap;padding:0;background:transparent;border:none;\"><span style=\"display:inline-block;width:12px;height:12px;font-size:12px;line-height:12px;margin-right:0.3em;vertical-align:middle\">__ICON__</span>__LABEL__</span>"
|
|
152
|
+
}
|
|
153
|
+
}
|
|
@@ -9,7 +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
|
+
const { prepareEmailIconAttachments, stripUnattachedIconImages } = require('../lib/email-icons');
|
|
13
13
|
const { prepareEmailMedia } = require('./email-media');
|
|
14
14
|
const { textResult, errorResult } = require('../lib/tool-result');
|
|
15
15
|
|
|
@@ -32,11 +32,14 @@ async function notifyUserViaProxy({ subject: customSubject, message, link, sessi
|
|
|
32
32
|
// Local media embeds become attachments riding inside the email: images
|
|
33
33
|
// inline via cid:, videos as regular attachments with a poster card.
|
|
34
34
|
const media = prepareEmailMedia(message);
|
|
35
|
-
|
|
35
|
+
let html = formatNotificationEmail(media.message, link || null, { sessionId });
|
|
36
36
|
const attachments = [
|
|
37
37
|
...media.attachments,
|
|
38
38
|
...prepareEmailIconAttachments(html, media.attachments),
|
|
39
39
|
];
|
|
40
|
+
// Icons that didn't fit the attachment budget degrade to plain text
|
|
41
|
+
// instead of broken-image boxes.
|
|
42
|
+
html = stripUnattachedIconImages(html, attachments);
|
|
40
43
|
|
|
41
44
|
try {
|
|
42
45
|
const res = await fetch(notifyUrl, {
|
|
@@ -58,6 +58,12 @@ const CORE_TOOL_GROUPS = [
|
|
|
58
58
|
'Register, run, and route local-first Amalgm apps.',
|
|
59
59
|
require('../apps/tools'),
|
|
60
60
|
),
|
|
61
|
+
group(
|
|
62
|
+
'computer-use',
|
|
63
|
+
'Computer Use',
|
|
64
|
+
require('../computer-use').DESCRIPTION,
|
|
65
|
+
require('../computer-use'),
|
|
66
|
+
),
|
|
61
67
|
// Browser is ONE toolbox entry. Users enable/disable it per agent as a
|
|
62
68
|
// single tool; fine-grained control stays available because loadouts can
|
|
63
69
|
// hold individual action ids (`browser.cua_click`). Action list, capability
|
|
@@ -45,6 +45,10 @@ async function handleAppRoutes(ctx) {
|
|
|
45
45
|
await appsRest.handleDelete(await ctx.readJsonBody(), ctx.sendJson, options);
|
|
46
46
|
return true;
|
|
47
47
|
}
|
|
48
|
+
if (route.path === '/update' && ctx.method === 'POST') {
|
|
49
|
+
await appsRest.handleUpdate(await ctx.readJsonBody(), ctx.sendJson, options);
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
48
52
|
if (route.path === '/connect-dns' && ctx.method === 'POST') {
|
|
49
53
|
await appsRest.handleConnectDns(await ctx.readJsonBody(), ctx.sendJson, options);
|
|
50
54
|
return true;
|