amalgm 0.1.257 → 0.1.258
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/lib/cli.js +72 -3
- package/lib/layout.js +30 -9
- package/lib/shared-realtime-tunnel.js +12 -14
- package/package.json +2 -2
- package/runtime/scripts/amalgm-mcp/lib/layout.js +32 -9
- package/runtime/scripts/amalgm-mcp/registration/entity-cloud.js +186 -32
- package/runtime/scripts/amalgm-mcp/registration/entity-content.js +33 -6
- package/runtime/scripts/amalgm-mcp/registration/index.js +21 -0
- package/runtime/scripts/amalgm-mcp/registration/service.js +102 -11
- package/runtime/scripts/amalgm-mcp/registry/index.js +1 -1
- package/runtime/scripts/amalgm-mcp/server/routes/entities.js +6 -0
- package/runtime/scripts/amalgm-mcp/tests/entity-cloud.test.js +67 -8
- package/runtime/scripts/amalgm-mcp/tests/entity-materialization.test.js +3 -3
- package/runtime/scripts/amalgm-mcp/tests/registration.service.test.js +45 -3
- package/runtime/scripts/amalgm-mcp/tests/system-catalog.test.js +3 -6
- package/runtime/scripts/amalgm-mcp/tests/toolbox-artifacts.test.js +88 -0
- package/runtime/scripts/amalgm-mcp/tests/toolbox-cloud-registration.test.js +91 -0
- package/runtime/scripts/amalgm-mcp/toolbox/artifacts.js +225 -0
- package/runtime/scripts/amalgm-mcp/toolbox/store.js +59 -10
- package/runtime/scripts/amalgm-mcp/toolbox/system-catalog.js +14 -5
|
@@ -12,14 +12,25 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
const path = require('path');
|
|
15
|
-
const {
|
|
16
|
-
const { ensureDir, readJson, writeJsonAtomic } = require('../lib/storage');
|
|
15
|
+
const { readJson } = require('../lib/storage');
|
|
17
16
|
const { openLocalDb } = require('../state/db');
|
|
18
17
|
const { insertStateEvent, publishStateEvent } = require('../state/events');
|
|
18
|
+
const {
|
|
19
|
+
TOOLBOX_DIR,
|
|
20
|
+
TOOLBOX_INDEX_FILE,
|
|
21
|
+
isSystemToolId,
|
|
22
|
+
readToolArtifacts,
|
|
23
|
+
removeUserToolArtifact,
|
|
24
|
+
writeToolArtifact,
|
|
25
|
+
writeToolboxIndex,
|
|
26
|
+
} = require('./artifacts');
|
|
19
27
|
|
|
20
28
|
const TOOLBOX_VERSION = 1;
|
|
21
|
-
|
|
22
|
-
|
|
29
|
+
// `toolbox.index.json` is a local cache, deliberately outside the registered
|
|
30
|
+
// toolbox folder. Individual `toolbox/*.json` artifacts are the portable
|
|
31
|
+
// source of truth; `toolbox/toolbox.json` remains only a legacy import path.
|
|
32
|
+
const TOOLBOX_FILE = TOOLBOX_INDEX_FILE;
|
|
33
|
+
const LEGACY_TOOLBOX_FILE = path.join(TOOLBOX_DIR, 'toolbox.json');
|
|
23
34
|
|
|
24
35
|
const TOOL_TYPES = new Set(['mcp', 'api', 'cli']);
|
|
25
36
|
const TOOL_ORIGINS = new Set(['system', 'user', 'catalog']);
|
|
@@ -528,19 +539,32 @@ function writeToolboxToDb(toolbox, options = {}) {
|
|
|
528
539
|
}
|
|
529
540
|
|
|
530
541
|
function exportToolboxJson(toolbox) {
|
|
531
|
-
|
|
532
|
-
writeJsonAtomic(TOOLBOX_FILE, normalizeToolbox(toolbox));
|
|
542
|
+
writeToolboxIndex(normalizeToolbox(toolbox), { toolboxDir: TOOLBOX_DIR });
|
|
533
543
|
}
|
|
534
544
|
|
|
535
545
|
function migrateLegacyToolboxFileIfNeeded() {
|
|
536
546
|
const db = openLocalDb();
|
|
537
547
|
if (toolboxRowCount(db) > 0) return;
|
|
538
548
|
|
|
539
|
-
const legacyToolbox = dropInheritedHarnessTools(normalizeToolbox(readJson(
|
|
549
|
+
const legacyToolbox = dropInheritedHarnessTools(normalizeToolbox(readJson(LEGACY_TOOLBOX_FILE, emptyToolbox())));
|
|
540
550
|
if (Object.keys(legacyToolbox.tools).length === 0 && Object.keys(legacyToolbox.toolActions).length === 0) return;
|
|
541
551
|
writeToolboxToDb(legacyToolbox, { replace: true });
|
|
542
552
|
}
|
|
543
553
|
|
|
554
|
+
function writeUserToolArtifacts(toolbox) {
|
|
555
|
+
for (const tool of Object.values(toolbox.tools || {})) {
|
|
556
|
+
if (!tool || isSystemToolId(tool.id) || tool.origin === 'system') continue;
|
|
557
|
+
writeToolArtifact({ tool, actions: toolActionsForTool(toolbox, tool.id) }, { toolboxDir: TOOLBOX_DIR });
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
function cacheToolboxFromArtifacts(toolbox) {
|
|
562
|
+
const normalized = normalizeToolbox(toolbox);
|
|
563
|
+
writeToolboxToDb(normalized, { replace: true });
|
|
564
|
+
exportToolboxJson(normalized);
|
|
565
|
+
return normalized;
|
|
566
|
+
}
|
|
567
|
+
|
|
544
568
|
function publishCommittedEvents(events) {
|
|
545
569
|
for (const event of events) publishStateEvent(event);
|
|
546
570
|
}
|
|
@@ -569,14 +593,18 @@ function insertToolActionEvent(db, op, action, options = {}) {
|
|
|
569
593
|
|
|
570
594
|
function readToolbox() {
|
|
571
595
|
migrateLegacyToolboxFileIfNeeded();
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
596
|
+
// An existing device may still have user records only in its local cache.
|
|
597
|
+
// Give each one a portable artifact before treating artifacts as canonical.
|
|
598
|
+
const cached = readToolboxFromDb();
|
|
599
|
+
dropLegacyAggregateAmalgmTool(cached);
|
|
600
|
+
writeUserToolArtifacts(cached);
|
|
601
|
+
const toolbox = cacheToolboxFromArtifacts(readToolArtifacts({ toolboxDir: TOOLBOX_DIR }));
|
|
575
602
|
return toolbox;
|
|
576
603
|
}
|
|
577
604
|
|
|
578
605
|
function writeToolbox(toolbox) {
|
|
579
606
|
const normalized = writeToolboxToDb(toolbox, { replace: true });
|
|
607
|
+
writeUserToolArtifacts(normalized);
|
|
580
608
|
exportToolboxJson(normalized);
|
|
581
609
|
return normalized;
|
|
582
610
|
}
|
|
@@ -614,6 +642,9 @@ function upsertTool(input) {
|
|
|
614
642
|
const existing = input?.id ? toolbox.tools[sanitizeId(input.id)] : null;
|
|
615
643
|
const existingActionIds = new Set(Object.keys(toolbox.toolActions));
|
|
616
644
|
const tool = normalizeTool(input, existing);
|
|
645
|
+
if (isSystemToolId(tool.id) || tool.origin === 'system') {
|
|
646
|
+
throw new Error('System tools are managed by Amalgm and cannot be created or replaced here');
|
|
647
|
+
}
|
|
617
648
|
toolbox.tools[tool.id] = tool;
|
|
618
649
|
|
|
619
650
|
const actions = [];
|
|
@@ -659,6 +690,8 @@ function upsertTool(input) {
|
|
|
659
690
|
}
|
|
660
691
|
return inserted;
|
|
661
692
|
})();
|
|
693
|
+
writeToolArtifact({ tool, actions: toolActionsForTool(toolbox, tool.id) }, { toolboxDir: TOOLBOX_DIR });
|
|
694
|
+
exportToolboxJson(toolbox);
|
|
662
695
|
publishCommittedEvents(events);
|
|
663
696
|
return { tool, actions };
|
|
664
697
|
}
|
|
@@ -735,6 +768,8 @@ function updateTool(input) {
|
|
|
735
768
|
source: 'toolbox:update-tool',
|
|
736
769
|
})];
|
|
737
770
|
})();
|
|
771
|
+
writeToolArtifact({ tool, actions: toolActionsForTool(toolbox, tool.id) }, { toolboxDir: TOOLBOX_DIR });
|
|
772
|
+
exportToolboxJson(toolbox);
|
|
738
773
|
publishCommittedEvents(events);
|
|
739
774
|
return { tool, actions: toolActionsForTool(toolbox, tool.id) };
|
|
740
775
|
}
|
|
@@ -744,6 +779,7 @@ function upsertToolAction(input) {
|
|
|
744
779
|
const toolId = sanitizeId(input?.toolId || input?.providerId || input?.tool);
|
|
745
780
|
const tool = toolbox.tools[toolId];
|
|
746
781
|
if (!tool) throw new Error(`Unknown tool: ${toolId}`);
|
|
782
|
+
if (tool.origin === 'system' || isSystemToolId(tool.id)) throw new Error('System tool actions cannot be modified');
|
|
747
783
|
const existing = input?.id ? toolbox.toolActions[sanitizeId(input.id)] : null;
|
|
748
784
|
const action = normalizeAction(input, tool, existing);
|
|
749
785
|
toolbox.toolActions[action.id] = action;
|
|
@@ -755,6 +791,8 @@ function upsertToolAction(input) {
|
|
|
755
791
|
source: 'toolbox:upsert-action',
|
|
756
792
|
})];
|
|
757
793
|
})();
|
|
794
|
+
writeToolArtifact({ tool, actions: toolActionsForTool(toolbox, tool.id) }, { toolboxDir: TOOLBOX_DIR });
|
|
795
|
+
exportToolboxJson(toolbox);
|
|
758
796
|
publishCommittedEvents(events);
|
|
759
797
|
return action;
|
|
760
798
|
}
|
|
@@ -805,6 +843,8 @@ function updateToolAction(input) {
|
|
|
805
843
|
source: 'toolbox:update-action',
|
|
806
844
|
})];
|
|
807
845
|
})();
|
|
846
|
+
writeToolArtifact({ tool, actions: toolActionsForTool(toolbox, tool.id) }, { toolboxDir: TOOLBOX_DIR });
|
|
847
|
+
exportToolboxJson(toolbox);
|
|
808
848
|
publishCommittedEvents(events);
|
|
809
849
|
return action;
|
|
810
850
|
}
|
|
@@ -830,6 +870,12 @@ function deleteTool(id, options = {}) {
|
|
|
830
870
|
}));
|
|
831
871
|
return inserted;
|
|
832
872
|
})();
|
|
873
|
+
removeUserToolArtifact(toolId, { toolboxDir: TOOLBOX_DIR });
|
|
874
|
+
exportToolboxJson({
|
|
875
|
+
...toolbox,
|
|
876
|
+
tools: Object.fromEntries(Object.entries(toolbox.tools).filter(([id]) => id !== toolId)),
|
|
877
|
+
toolActions: Object.fromEntries(Object.entries(toolbox.toolActions).filter(([, action]) => action.toolId !== toolId)),
|
|
878
|
+
});
|
|
833
879
|
publishCommittedEvents(events);
|
|
834
880
|
scrubToolFromApps(toolId);
|
|
835
881
|
return { ok: true, deleted: true };
|
|
@@ -867,6 +913,9 @@ function deleteToolAction(id, options = {}) {
|
|
|
867
913
|
source: 'toolbox:delete-action',
|
|
868
914
|
})];
|
|
869
915
|
})();
|
|
916
|
+
delete toolbox.toolActions[actionId];
|
|
917
|
+
writeToolArtifact({ tool, actions: toolActionsForTool(toolbox, tool.id) }, { toolboxDir: TOOLBOX_DIR });
|
|
918
|
+
exportToolboxJson(toolbox);
|
|
870
919
|
publishCommittedEvents(events);
|
|
871
920
|
return { ok: true, deleted: true };
|
|
872
921
|
}
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* Virtual first-party Toolbox catalog entries.
|
|
5
5
|
*
|
|
6
|
-
* These records
|
|
7
|
-
*
|
|
8
|
-
* MCP
|
|
6
|
+
* These records are the canonical definitions materialized as one exact
|
|
7
|
+
* `toolbox/amalgm-*.json` artifact per built-in tool. The overlay keeps the
|
|
8
|
+
* legacy MCP surface stable and refuses a stale local cache from overriding a
|
|
9
|
+
* product-managed definition.
|
|
9
10
|
*/
|
|
10
11
|
|
|
11
12
|
const { PORT } = require('../config');
|
|
@@ -29,6 +30,10 @@ function coreToolGroups() {
|
|
|
29
30
|
return require('../server/core-tools').CORE_TOOL_GROUPS || [];
|
|
30
31
|
}
|
|
31
32
|
|
|
33
|
+
function systemToolIds() {
|
|
34
|
+
return new Set(coreToolGroups().map((group) => group.id));
|
|
35
|
+
}
|
|
36
|
+
|
|
32
37
|
function systemToolForGroup(group) {
|
|
33
38
|
return {
|
|
34
39
|
id: group.id,
|
|
@@ -95,12 +100,15 @@ function buildSystemToolboxCatalog() {
|
|
|
95
100
|
function persistedCatalogWithoutLegacyAggregate(toolbox) {
|
|
96
101
|
const tools = { ...(toolbox?.tools || {}) };
|
|
97
102
|
const toolActions = { ...(toolbox?.toolActions || {}) };
|
|
103
|
+
const systemIds = systemToolIds();
|
|
98
104
|
delete tools.amalgm;
|
|
105
|
+
for (const systemId of systemIds) delete tools[systemId];
|
|
99
106
|
for (const toolId of LEGACY_FIRST_PARTY_TOOL_IDS) delete tools[toolId];
|
|
100
107
|
for (const [actionId, action] of Object.entries(toolActions)) {
|
|
101
108
|
const legacyGroupId = actionId.split('.').slice(0, 2).join('.');
|
|
102
109
|
if (
|
|
103
110
|
action?.toolId === 'amalgm'
|
|
111
|
+
|| systemIds.has(action?.toolId)
|
|
104
112
|
|| LEGACY_FIRST_PARTY_TOOL_IDS.has(action?.toolId)
|
|
105
113
|
|| LEGACY_FIRST_PARTY_TOOL_IDS.has(legacyGroupId)
|
|
106
114
|
) {
|
|
@@ -116,18 +124,19 @@ function mergeSystemToolboxCatalog(toolbox) {
|
|
|
116
124
|
return {
|
|
117
125
|
...toolbox,
|
|
118
126
|
tools: {
|
|
119
|
-
...catalog.tools,
|
|
120
127
|
...persisted.tools,
|
|
128
|
+
...catalog.tools,
|
|
121
129
|
},
|
|
122
130
|
toolActions: {
|
|
123
|
-
...catalog.toolActions,
|
|
124
131
|
...persisted.toolActions,
|
|
132
|
+
...catalog.toolActions,
|
|
125
133
|
},
|
|
126
134
|
};
|
|
127
135
|
}
|
|
128
136
|
|
|
129
137
|
module.exports = {
|
|
130
138
|
buildSystemToolboxCatalog,
|
|
139
|
+
systemToolIds,
|
|
131
140
|
mergeSystemToolboxCatalog,
|
|
132
141
|
persistedCatalogWithoutLegacyAggregate,
|
|
133
142
|
};
|