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
package/package.json
CHANGED
|
@@ -48,8 +48,6 @@ var DIRECTORIES = [
|
|
|
48
48
|
// ~/.amalgm/
|
|
49
49
|
"browser",
|
|
50
50
|
// ~/.amalgm/browser/
|
|
51
|
-
"browser/local-storage",
|
|
52
|
-
// ~/.amalgm/browser/local-storage/
|
|
53
51
|
"agents",
|
|
54
52
|
// ~/.amalgm/agents/
|
|
55
53
|
"instructions",
|
|
@@ -70,11 +68,6 @@ var DEFAULT_FILES = {
|
|
|
70
68
|
version: 1,
|
|
71
69
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
72
70
|
},
|
|
73
|
-
"browser/cookies.json": {
|
|
74
|
-
version: 1,
|
|
75
|
-
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
76
|
-
cookies: []
|
|
77
|
-
},
|
|
78
71
|
"agents/discovery.json": {
|
|
79
72
|
version: 1,
|
|
80
73
|
lastScan: null,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* /apps/* REST routes for the Next.js API and local tunnel runtime.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
const { getConnectedRoutes, loadApps } = require('./store');
|
|
5
|
+
const { getApp, getConnectedRoutes, loadApps, updateAppMeta } = require('./store');
|
|
6
6
|
const {
|
|
7
7
|
deleteApp,
|
|
8
8
|
connectDns,
|
|
@@ -109,6 +109,28 @@ async function handleDelete(body, sendJson, options = {}) {
|
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
async function handleUpdate(body, sendJson, options = {}) {
|
|
113
|
+
try {
|
|
114
|
+
const appId = appIdFromBody(body);
|
|
115
|
+
if (!appId) return sendJson(400, { error: 'app_id is required' });
|
|
116
|
+
if (!getApp(appId)) return sendJson(404, { error: 'App not found' });
|
|
117
|
+
|
|
118
|
+
const updates = {};
|
|
119
|
+
if (typeof body?.name === 'string' && body.name.trim()) updates.name = body.name.trim();
|
|
120
|
+
if (typeof body?.description === 'string') updates.description = body.description;
|
|
121
|
+
const toolIds = body?.tool_ids ?? body?.toolIds;
|
|
122
|
+
if (Array.isArray(toolIds)) updates.toolIds = toolIds;
|
|
123
|
+
if (Object.keys(updates).length === 0) {
|
|
124
|
+
return sendJson(400, { error: 'No updatable fields provided (name, description, tool_ids)' });
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const app = updateAppMeta(appId, updates);
|
|
128
|
+
sendJson(200, appPayload(app, options));
|
|
129
|
+
} catch (err) {
|
|
130
|
+
sendJson(400, { error: err instanceof Error ? err.message : String(err) });
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
112
134
|
async function handleConnectDns(body, sendJson, options = {}) {
|
|
113
135
|
try {
|
|
114
136
|
const appId = appIdFromBody(body);
|
|
@@ -141,4 +163,5 @@ module.exports = {
|
|
|
141
163
|
handleRoutes,
|
|
142
164
|
handleStart,
|
|
143
165
|
handleStop,
|
|
166
|
+
handleUpdate,
|
|
144
167
|
};
|
|
@@ -390,10 +390,26 @@ async function deleteApp(appId) {
|
|
|
390
390
|
stopping.delete(appId);
|
|
391
391
|
running.delete(appId);
|
|
392
392
|
await syncRoutesBestEffort(`delete:${appId}`);
|
|
393
|
+
clearAppLinkFromTools(appId);
|
|
393
394
|
|
|
394
395
|
return app;
|
|
395
396
|
}
|
|
396
397
|
|
|
398
|
+
// Keep the app↔tool relation from drifting: tools pointing at a deleted app
|
|
399
|
+
// must lose their appId, or sharing them would chase a dead dependency.
|
|
400
|
+
function clearAppLinkFromTools(appId) {
|
|
401
|
+
try {
|
|
402
|
+
const { readToolbox, updateTool } = require('../toolbox/store');
|
|
403
|
+
const owned = Object.values(readToolbox().tools)
|
|
404
|
+
.filter((tool) => tool.appId === appId && tool.origin !== 'system');
|
|
405
|
+
for (const tool of owned) {
|
|
406
|
+
updateTool({ id: tool.id, appId: null });
|
|
407
|
+
}
|
|
408
|
+
} catch (error) {
|
|
409
|
+
console.warn('[Apps] Failed to clear app link from tools:', error.message);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
397
413
|
async function registerApp(input) {
|
|
398
414
|
const name = String(input.name || '').trim() || 'App';
|
|
399
415
|
const cwd = path.resolve(input.cwd || input.root_dir || DEFAULT_CWD);
|
|
@@ -21,6 +21,7 @@ const os = require('os');
|
|
|
21
21
|
const path = require('path');
|
|
22
22
|
const { spawn } = require('child_process');
|
|
23
23
|
const { amalgmDir: resolveAmalgmDir } = require('../../lib/runtime-paths');
|
|
24
|
+
const { safeId } = require('./paths');
|
|
24
25
|
|
|
25
26
|
const DEFAULT_TIMEOUT_MS = 120_000;
|
|
26
27
|
const DEFAULT_SESSION = 'default';
|
|
@@ -35,11 +36,6 @@ function ensureDir(dir) {
|
|
|
35
36
|
return dir;
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
function safeId(value, fallback = DEFAULT_SESSION) {
|
|
39
|
-
const raw = String(value || fallback).replace(/^agent-browser:/, '');
|
|
40
|
-
return raw.replace(/[^a-zA-Z0-9._-]/g, '_').slice(0, 64) || fallback;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
39
|
function browserRoot() {
|
|
44
40
|
return ensureDir(path.join(amalgmDir(), 'browser'));
|
|
45
41
|
}
|
|
@@ -79,13 +79,28 @@ async function loadStateIntoSession(session, statePayload, context) {
|
|
|
79
79
|
|
|
80
80
|
async function ensureCookieSourceLoaded(session, context) {
|
|
81
81
|
if (cookieSourceDisabled()) return;
|
|
82
|
+
// Attached sessions drive the desktop app's own jar — the jar the cookie
|
|
83
|
+
// source is exported FROM (electron/browserCookieSource.ts). Importing the
|
|
84
|
+
// bundle back into it is at best an echo and at worst overwrites the user's
|
|
85
|
+
// live cookies with older ones. Only headless sessions import.
|
|
86
|
+
if (sessions.backendFor(session) === 'attached') return;
|
|
82
87
|
const bundle = getBrowserAuthBundle(COOKIE_SOURCE_BUNDLE_ID);
|
|
83
88
|
if (!bundle?.blobPath) return;
|
|
84
89
|
const version = cookieSourceVersion(bundle);
|
|
85
90
|
if (version && loadedCookieSources.get(session) === version) return;
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
91
|
+
try {
|
|
92
|
+
const { payload } = readEncryptedStateBundle(COOKIE_SOURCE_BUNDLE_ID);
|
|
93
|
+
if (payload?.state && typeof payload.state === 'object') {
|
|
94
|
+
await loadStateIntoSession(session, payload.state, context);
|
|
95
|
+
console.error(`[browser] cookie source ${String(version).slice(0, 12)} imported into session ${session}`);
|
|
96
|
+
}
|
|
97
|
+
} catch (err) {
|
|
98
|
+
// A bad bundle (key mismatch, corrupt blob, load failure) must degrade to
|
|
99
|
+
// a logged-out session, never break every browser verb.
|
|
100
|
+
console.error(`[browser] cookie source import failed for session ${session}: ${err.message}`);
|
|
101
|
+
}
|
|
102
|
+
// Recorded even on failure: the failure is a property of this bundle
|
|
103
|
+
// version, so retrying on every verb would only repeat the error.
|
|
89
104
|
if (version) loadedCookieSources.set(session, version);
|
|
90
105
|
}
|
|
91
106
|
|
|
@@ -10,9 +10,14 @@ function ensurePrivateDir(dir) {
|
|
|
10
10
|
return dir;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
// The one id sanitizer for the browser module — cli.js re-exports it. The
|
|
14
|
+
// 64-char cap matches the profile directories cli.js actually creates;
|
|
15
|
+
// history note: cli.js and this file once disagreed (64 vs 96), which would
|
|
16
|
+
// have put the chromium profile and its registry record at different paths
|
|
17
|
+
// for long ids.
|
|
13
18
|
function safeId(value, fallback = 'default') {
|
|
14
19
|
const raw = String(value || fallback).replace(/^agent-browser:/, '');
|
|
15
|
-
return raw.replace(/[^a-zA-Z0-9._-]/g, '_').slice(0,
|
|
20
|
+
return raw.replace(/[^a-zA-Z0-9._-]/g, '_').slice(0, 64) || fallback;
|
|
16
21
|
}
|
|
17
22
|
|
|
18
23
|
function browserRoot() {
|
|
@@ -53,11 +53,18 @@ function handleAuthBundlesList(sendJson) {
|
|
|
53
53
|
|
|
54
54
|
async function exportAuthBundleFromProfile(body = {}) {
|
|
55
55
|
const profileId = profileIdFrom(body);
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
56
|
+
// A caller that already holds the jar (the desktop app pushing its Electron
|
|
57
|
+
// session, see electron/browserCookieSource.ts) sends `state` directly.
|
|
58
|
+
// Capturing via a browser session here would read whichever backend the
|
|
59
|
+
// in-process session registry happens to name — after a runtime restart
|
|
60
|
+
// that is an empty headless profile, not the jar the user logged into.
|
|
61
|
+
const saved = body.state && typeof body.state === 'object'
|
|
62
|
+
? { state: body.state, backend: 'state-push' }
|
|
63
|
+
: await saveStateBrowser({
|
|
64
|
+
...body,
|
|
65
|
+
browserProfileId: profileId,
|
|
66
|
+
browserSessionId: profileId,
|
|
67
|
+
}, {});
|
|
61
68
|
|
|
62
69
|
const bundle = writeEncryptedStateBundle({
|
|
63
70
|
id: body.id || (profileId === DEFAULT_COOKIE_SOURCE_PROFILE_ID ? COOKIE_SOURCE_BUNDLE_ID : undefined),
|