amalgm 0.0.0 → 0.0.32
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/README.md +41 -1
- package/bin/amalgm.js +8 -2
- package/lib/auth-store.js +223 -0
- package/lib/cli.js +1132 -0
- package/lib/paths.js +30 -0
- package/lib/supervisor.js +476 -0
- package/lib/tunnel-chat.js +328 -0
- package/lib/tunnel-events.js +499 -0
- package/package.json +30 -3
- package/runtime/README.md +4 -0
- package/runtime/lib/chatInput.js +315 -0
- package/runtime/lib/harnesses.js +988 -0
- package/runtime/lib/local/amalgmStore.js +136 -0
- package/runtime/lib/local/credentialResolver.js +425 -0
- package/runtime/lib/mcpApps/registry.js +619 -0
- package/runtime/package.json +5 -0
- package/runtime/scripts/amalgm-mcp/agents/rest.js +144 -0
- package/runtime/scripts/amalgm-mcp/agents/store.js +684 -0
- package/runtime/scripts/amalgm-mcp/agents/talk.js +1162 -0
- package/runtime/scripts/amalgm-mcp/agents/tools.js +221 -0
- package/runtime/scripts/amalgm-mcp/artifacts/advertise.js +132 -0
- package/runtime/scripts/amalgm-mcp/artifacts/rest.js +103 -0
- package/runtime/scripts/amalgm-mcp/artifacts/store.js +157 -0
- package/runtime/scripts/amalgm-mcp/artifacts/supervisor.js +402 -0
- package/runtime/scripts/amalgm-mcp/artifacts/tools.js +176 -0
- package/runtime/scripts/amalgm-mcp/browser/page.js +637 -0
- package/runtime/scripts/amalgm-mcp/browser/tools.js +688 -0
- package/runtime/scripts/amalgm-mcp/config.js +140 -0
- package/runtime/scripts/amalgm-mcp/credentials/rest.js +45 -0
- package/runtime/scripts/amalgm-mcp/deps.js +40 -0
- package/runtime/scripts/amalgm-mcp/email/inbound.js +215 -0
- package/runtime/scripts/amalgm-mcp/events/executor.js +179 -0
- package/runtime/scripts/amalgm-mcp/events/ingress.js +113 -0
- package/runtime/scripts/amalgm-mcp/events/matcher.js +125 -0
- package/runtime/scripts/amalgm-mcp/events/rest.js +200 -0
- package/runtime/scripts/amalgm-mcp/events/ring-buffer.js +19 -0
- package/runtime/scripts/amalgm-mcp/events/store.js +113 -0
- package/runtime/scripts/amalgm-mcp/events/tools.js +306 -0
- package/runtime/scripts/amalgm-mcp/events/webhook-url.js +28 -0
- package/runtime/scripts/amalgm-mcp/fs/rest.js +293 -0
- package/runtime/scripts/amalgm-mcp/index.js +100 -0
- package/runtime/scripts/amalgm-mcp/lib/chat-runner.js +167 -0
- package/runtime/scripts/amalgm-mcp/lib/email-md.js +288 -0
- package/runtime/scripts/amalgm-mcp/lib/mcp-resolver.js +63 -0
- package/runtime/scripts/amalgm-mcp/lib/prefs.js +441 -0
- package/runtime/scripts/amalgm-mcp/lib/storage.js +92 -0
- package/runtime/scripts/amalgm-mcp/lib/supabase.js +118 -0
- package/runtime/scripts/amalgm-mcp/lib/tool-result.js +177 -0
- package/runtime/scripts/amalgm-mcp/local/rest.js +87 -0
- package/runtime/scripts/amalgm-mcp/mcp-connections/rest.js +234 -0
- package/runtime/scripts/amalgm-mcp/notify/index.js +107 -0
- package/runtime/scripts/amalgm-mcp/server/core-tools.js +20 -0
- package/runtime/scripts/amalgm-mcp/server/http.js +377 -0
- package/runtime/scripts/amalgm-mcp/server/mcp.js +127 -0
- package/runtime/scripts/amalgm-mcp/slack/inbound.js +200 -0
- package/runtime/scripts/amalgm-mcp/state/db.js +194 -0
- package/runtime/scripts/amalgm-mcp/state/events.js +113 -0
- package/runtime/scripts/amalgm-mcp/state/rest.js +64 -0
- package/runtime/scripts/amalgm-mcp/state/snapshot.js +76 -0
- package/runtime/scripts/amalgm-mcp/tasks/executor.js +225 -0
- package/runtime/scripts/amalgm-mcp/tasks/rest.js +110 -0
- package/runtime/scripts/amalgm-mcp/tasks/schedule-normalization.js +85 -0
- package/runtime/scripts/amalgm-mcp/tasks/scheduler.js +105 -0
- package/runtime/scripts/amalgm-mcp/tasks/store.js +154 -0
- package/runtime/scripts/amalgm-mcp/tasks/tools.js +391 -0
- package/runtime/scripts/amalgm-mcp/toolbox/rest.js +75 -0
- package/runtime/scripts/amalgm-mcp/toolbox/runner.js +257 -0
- package/runtime/scripts/amalgm-mcp/toolbox/store.js +933 -0
- package/runtime/scripts/amalgm-mcp/toolbox/tools.js +269 -0
- package/runtime/scripts/amalgm-mcp/user-api-keys/rest.js +105 -0
- package/runtime/scripts/amalgm-mcp/workspace/rest.js +497 -0
- package/runtime/scripts/chat-core/adapters/claude.js +165 -0
- package/runtime/scripts/chat-core/adapters/codex.js +313 -0
- package/runtime/scripts/chat-core/adapters/opencode.js +412 -0
- package/runtime/scripts/chat-core/auth.js +177 -0
- package/runtime/scripts/chat-core/contract.js +328 -0
- package/runtime/scripts/chat-core/credentials/store.js +212 -0
- package/runtime/scripts/chat-core/egress.js +87 -0
- package/runtime/scripts/chat-core/engine.js +253 -0
- package/runtime/scripts/chat-core/event-schema.js +231 -0
- package/runtime/scripts/chat-core/events.js +190 -0
- package/runtime/scripts/chat-core/index.js +11 -0
- package/runtime/scripts/chat-core/input.js +50 -0
- package/runtime/scripts/chat-core/normalizers/claude.js +450 -0
- package/runtime/scripts/chat-core/normalizers/codex.js +380 -0
- package/runtime/scripts/chat-core/normalizers/normalizer_spec.md +259 -0
- package/runtime/scripts/chat-core/normalizers/opencode.js +552 -0
- package/runtime/scripts/chat-core/normalizers/tool_contract.md +123 -0
- package/runtime/scripts/chat-core/normalizers/usage_contract.md +304 -0
- package/runtime/scripts/chat-core/parts.js +253 -0
- package/runtime/scripts/chat-core/recorder.js +65 -0
- package/runtime/scripts/chat-core/runtime.js +86 -0
- package/runtime/scripts/chat-core/server.js +163 -0
- package/runtime/scripts/chat-core/sse.js +196 -0
- package/runtime/scripts/chat-core/stores.js +100 -0
- package/runtime/scripts/chat-core/tool-display.js +149 -0
- package/runtime/scripts/chat-core/tool-shape.js +143 -0
- package/runtime/scripts/chat-core/tooling/mcp-bundle.js +161 -0
- package/runtime/scripts/chat-core/tooling/mcp-relay.js +97 -0
- package/runtime/scripts/chat-core/tooling/native-binaries.js +608 -0
- package/runtime/scripts/chat-core/tooling/system-prompt.js +20 -0
- package/runtime/scripts/chat-core/usage.js +343 -0
- package/runtime/scripts/chat-server/config.js +110 -0
- package/runtime/scripts/chat-server/db.js +529 -0
- package/runtime/scripts/chat-server/index.js +33 -0
- package/runtime/scripts/chat-server/model-catalog.js +327 -0
- package/runtime/scripts/chat-server.js +75 -0
- package/runtime/scripts/credential-adapter.js +131 -0
- package/runtime/scripts/fs-watcher.js +888 -0
- package/runtime/scripts/local-gateway.js +854 -0
- package/runtime/scripts/platform-context.txt +246 -0
- package/runtime/scripts/port-monitor.js +175 -0
- package/runtime/scripts/proxy-token-store.js +162 -0
- package/runtime/scripts/runtime-auth.js +163 -0
- package/runtime/scripts/test-claude-code-models.js +87 -0
- package/runtime/tsconfig.json +15 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { spawn } = require('child_process');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { errorResult, textResult } = require('../lib/tool-result');
|
|
6
|
+
const { readToolbox } = require('./store');
|
|
7
|
+
|
|
8
|
+
const TOOLBOX_TOOL_PREFIX = 'toolbox__';
|
|
9
|
+
const DEFAULT_TIMEOUT_MS = 120_000;
|
|
10
|
+
const MAX_OUTPUT_BYTES = 512 * 1024;
|
|
11
|
+
const MODEL_UNSUPPORTED_TOP_LEVEL_SCHEMA_KEYS = ['anyOf', 'oneOf', 'allOf', 'not', 'enum'];
|
|
12
|
+
|
|
13
|
+
function isObject(value) {
|
|
14
|
+
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function safeMcpName(value) {
|
|
18
|
+
return String(value || '')
|
|
19
|
+
.trim()
|
|
20
|
+
.replace(/[^A-Za-z0-9_.-]+/g, '_')
|
|
21
|
+
.replace(/[.-]+/g, '_')
|
|
22
|
+
.replace(/_+/g, '_')
|
|
23
|
+
.replace(/^_+|_+$/g, '')
|
|
24
|
+
|| 'tool';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function toolboxMcpToolName(action) {
|
|
28
|
+
return `${TOOLBOX_TOOL_PREFIX}${safeMcpName(action.id)}`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function selectedToolIdsForContext(context) {
|
|
32
|
+
const tools = context?.sessionMetadata?.tools;
|
|
33
|
+
if (!tools || tools.mode !== 'selected') return null;
|
|
34
|
+
const selected = Array.isArray(tools.selectedToolIds)
|
|
35
|
+
? tools.selectedToolIds
|
|
36
|
+
: Array.isArray(tools.selected)
|
|
37
|
+
? tools.selected
|
|
38
|
+
: [];
|
|
39
|
+
return new Set(selected.map((value) => String(value || '').trim()).filter(Boolean));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function portableInputSchema(inputSchema) {
|
|
43
|
+
if (!isObject(inputSchema) || inputSchema.type !== 'object') {
|
|
44
|
+
return { type: 'object', properties: {}, additionalProperties: true };
|
|
45
|
+
}
|
|
46
|
+
const schema = { ...inputSchema };
|
|
47
|
+
for (const key of MODEL_UNSUPPORTED_TOP_LEVEL_SCHEMA_KEYS) {
|
|
48
|
+
delete schema[key];
|
|
49
|
+
}
|
|
50
|
+
if (!isObject(schema.properties)) schema.properties = {};
|
|
51
|
+
return schema;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function enabledToolboxActions(context) {
|
|
55
|
+
const toolbox = readToolbox();
|
|
56
|
+
const selectedToolIds = selectedToolIdsForContext(context);
|
|
57
|
+
return Object.values(toolbox.toolActions || {})
|
|
58
|
+
.map((action) => {
|
|
59
|
+
const tool = toolbox.tools?.[action.toolId];
|
|
60
|
+
return { action, tool };
|
|
61
|
+
})
|
|
62
|
+
.filter(({ action, tool }) => {
|
|
63
|
+
if (!tool || tool.status !== 'enabled' || action.status !== 'enabled') return false;
|
|
64
|
+
if (tool.origin === 'system') return false;
|
|
65
|
+
// Remote/local MCP tools are injected as MCP providers. The toolbox MCP
|
|
66
|
+
// runner is for tools Amalgm itself must execute, currently CLI/API.
|
|
67
|
+
if (tool.type !== 'cli' && tool.type !== 'api') return false;
|
|
68
|
+
if (!selectedToolIds) return true;
|
|
69
|
+
return selectedToolIds.has(tool.id) || selectedToolIds.has(action.id);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function listToolboxMcpTools(context) {
|
|
74
|
+
return enabledToolboxActions(context).map(({ action, tool }) => ({
|
|
75
|
+
name: toolboxMcpToolName(action),
|
|
76
|
+
description: [
|
|
77
|
+
action.description || `Run ${tool.name} ${action.name}.`,
|
|
78
|
+
tool.guide?.kind === 'skill' ? `Guidance skill: ${tool.guide.path}` : null,
|
|
79
|
+
action.guide?.kind === 'skill' ? `Action skill: ${action.guide.path}` : null,
|
|
80
|
+
].filter(Boolean).join('\n'),
|
|
81
|
+
inputSchema: portableInputSchema(action.inputSchema),
|
|
82
|
+
}));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function resolveToolboxMcpAction(toolName, context) {
|
|
86
|
+
return enabledToolboxActions(context)
|
|
87
|
+
.find(({ action }) => toolboxMcpToolName(action) === toolName) || null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function collectOutput(child, resolve, reject, timeoutMs) {
|
|
91
|
+
let stdout = Buffer.alloc(0);
|
|
92
|
+
let stderr = Buffer.alloc(0);
|
|
93
|
+
let timedOut = false;
|
|
94
|
+
const append = (current, chunk) => {
|
|
95
|
+
const next = Buffer.concat([current, Buffer.from(chunk)]);
|
|
96
|
+
return next.length > MAX_OUTPUT_BYTES ? next.subarray(next.length - MAX_OUTPUT_BYTES) : next;
|
|
97
|
+
};
|
|
98
|
+
const timer = setTimeout(() => {
|
|
99
|
+
timedOut = true;
|
|
100
|
+
try { child.kill('SIGTERM'); } catch {}
|
|
101
|
+
setTimeout(() => {
|
|
102
|
+
try { child.kill('SIGKILL'); } catch {}
|
|
103
|
+
}, 1500).unref?.();
|
|
104
|
+
}, timeoutMs);
|
|
105
|
+
|
|
106
|
+
child.stdout.on('data', (chunk) => { stdout = append(stdout, chunk); });
|
|
107
|
+
child.stderr.on('data', (chunk) => { stderr = append(stderr, chunk); });
|
|
108
|
+
child.on('error', (err) => {
|
|
109
|
+
clearTimeout(timer);
|
|
110
|
+
reject(err);
|
|
111
|
+
});
|
|
112
|
+
child.on('close', (code, signal) => {
|
|
113
|
+
clearTimeout(timer);
|
|
114
|
+
resolve({
|
|
115
|
+
code,
|
|
116
|
+
signal,
|
|
117
|
+
timedOut,
|
|
118
|
+
stdout: stdout.toString('utf8'),
|
|
119
|
+
stderr: stderr.toString('utf8'),
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function safeEnv(extraEnv) {
|
|
125
|
+
const env = {};
|
|
126
|
+
for (const key of ['PATH', 'HOME', 'USER', 'LOGNAME', 'SHELL', 'LANG', 'LC_ALL', 'TERM', 'TMPDIR']) {
|
|
127
|
+
if (process.env[key]) env[key] = process.env[key];
|
|
128
|
+
}
|
|
129
|
+
if (extraEnv && typeof extraEnv === 'object' && !Array.isArray(extraEnv)) {
|
|
130
|
+
for (const [key, value] of Object.entries(extraEnv)) {
|
|
131
|
+
if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(key) && value != null) env[key] = String(value);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return env;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function runCliTool(tool, action, args) {
|
|
138
|
+
const source = tool.source || {};
|
|
139
|
+
const command = String(source.command || '').trim();
|
|
140
|
+
if (!command) return errorResult(`Tool ${tool.id} has no CLI command configured.`);
|
|
141
|
+
|
|
142
|
+
const inputMode = source.inputMode || 'json-stdin';
|
|
143
|
+
const commandArgs = Array.isArray(source.args) ? [...source.args] : [];
|
|
144
|
+
if (inputMode === 'argv') {
|
|
145
|
+
const providedArgs = Array.isArray(args?.args) ? args.args.map(String) : [];
|
|
146
|
+
commandArgs.push(...providedArgs);
|
|
147
|
+
} else if (inputMode === 'terminal' && typeof args?.command === 'string') {
|
|
148
|
+
commandArgs.push(args.command);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const child = spawn(command, commandArgs, {
|
|
152
|
+
cwd: source.cwd || process.cwd(),
|
|
153
|
+
env: safeEnv(source.env),
|
|
154
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
155
|
+
windowsHide: true,
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
if (inputMode === 'json-stdin') {
|
|
159
|
+
child.stdin.end(`${JSON.stringify(args || {})}\n`);
|
|
160
|
+
} else {
|
|
161
|
+
child.stdin.end();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const result = await new Promise((resolve, reject) => {
|
|
165
|
+
collectOutput(child, resolve, reject, Number(source.timeoutMs) || DEFAULT_TIMEOUT_MS);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const outputMode = source.outputMode || 'text';
|
|
169
|
+
const stdout = String(result.stdout || '').trim();
|
|
170
|
+
const stderr = String(result.stderr || '').trim();
|
|
171
|
+
const statusLine = result.timedOut
|
|
172
|
+
? 'timed out'
|
|
173
|
+
: result.code === 0
|
|
174
|
+
? 'completed'
|
|
175
|
+
: `failed with exit code ${result.code ?? result.signal ?? 'unknown'}`;
|
|
176
|
+
|
|
177
|
+
if (result.timedOut || result.code !== 0) {
|
|
178
|
+
return errorResult([
|
|
179
|
+
`${tool.name}.${action.name} ${statusLine}.`,
|
|
180
|
+
stdout ? `stdout:\n${stdout}` : null,
|
|
181
|
+
stderr ? `stderr:\n${stderr}` : null,
|
|
182
|
+
].filter(Boolean).join('\n\n'));
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (outputMode === 'json' && stdout) {
|
|
186
|
+
try {
|
|
187
|
+
const parsed = JSON.parse(stdout);
|
|
188
|
+
return {
|
|
189
|
+
content: [{ type: 'text', text: JSON.stringify(parsed, null, 2) }],
|
|
190
|
+
structuredContent: parsed,
|
|
191
|
+
};
|
|
192
|
+
} catch {
|
|
193
|
+
// Fall through to text output; many CLIs are aspirationally JSON.
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return textResult(stdout || `${tool.name}.${action.name} ${statusLine}.`);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function fillPathParams(template, params = {}) {
|
|
201
|
+
return String(template || '').replace(/\{([^}]+)\}/g, (_, key) => {
|
|
202
|
+
const value = params?.[key];
|
|
203
|
+
return encodeURIComponent(value == null ? '' : String(value));
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async function runApiTool(tool, action, args) {
|
|
208
|
+
const source = tool.source || {};
|
|
209
|
+
const sourceAction = action.sourceAction || {};
|
|
210
|
+
const baseUrl = String(source.baseUrl || '').replace(/\/$/, '');
|
|
211
|
+
const pathTemplate = sourceAction.path || args?.path || '';
|
|
212
|
+
const method = String(sourceAction.method || args?.method || 'GET').toUpperCase();
|
|
213
|
+
if (!baseUrl || !pathTemplate) {
|
|
214
|
+
return errorResult(`Tool ${tool.id}.${action.name} needs source.baseUrl and sourceAction.path before it can run.`);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const url = new URL(`${baseUrl}${fillPathParams(pathTemplate, args?.pathParams || {})}`);
|
|
218
|
+
for (const [key, value] of Object.entries(args?.query || {})) {
|
|
219
|
+
if (value != null) url.searchParams.set(key, String(value));
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
223
|
+
const response = await fetch(url, {
|
|
224
|
+
method,
|
|
225
|
+
headers,
|
|
226
|
+
body: ['GET', 'HEAD'].includes(method) ? undefined : JSON.stringify(args?.body ?? args ?? {}),
|
|
227
|
+
});
|
|
228
|
+
const text = await response.text();
|
|
229
|
+
if (!response.ok) {
|
|
230
|
+
return errorResult(`HTTP ${response.status} ${response.statusText}\n\n${text.slice(0, 12000)}`);
|
|
231
|
+
}
|
|
232
|
+
try {
|
|
233
|
+
const parsed = JSON.parse(text);
|
|
234
|
+
return {
|
|
235
|
+
content: [{ type: 'text', text: JSON.stringify(parsed, null, 2) }],
|
|
236
|
+
structuredContent: parsed,
|
|
237
|
+
};
|
|
238
|
+
} catch {
|
|
239
|
+
return textResult(text);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
async function callToolboxMcpTool(toolName, args = {}, context) {
|
|
244
|
+
const resolved = resolveToolboxMcpAction(toolName, context);
|
|
245
|
+
if (!resolved) return null;
|
|
246
|
+
const { tool, action } = resolved;
|
|
247
|
+
if (tool.type === 'cli') return runCliTool(tool, action, args);
|
|
248
|
+
if (tool.type === 'api') return runApiTool(tool, action, args);
|
|
249
|
+
return errorResult(`Tool type ${tool.type} cannot be run through the toolbox runner.`);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
module.exports = {
|
|
253
|
+
TOOLBOX_TOOL_PREFIX,
|
|
254
|
+
callToolboxMcpTool,
|
|
255
|
+
listToolboxMcpTools,
|
|
256
|
+
toolboxMcpToolName,
|
|
257
|
+
};
|