@agimon-ai/po-config 0.0.0-alpha.2 → 0.0.0-alpha.4
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 +9 -9
- package/dist/agentPluginMcp.cjs +313 -134
- package/dist/agentPluginMcp.cjs.map +1 -1
- package/dist/agentPluginMcp.d.mts +22 -4
- package/dist/agentPluginMcp.d.mts.map +1 -1
- package/dist/agentPluginMcp.mjs +312 -134
- package/dist/agentPluginMcp.mjs.map +1 -1
- package/dist/apiContracts.cjs.map +1 -1
- package/dist/apiContracts.mjs.map +1 -1
- package/dist/atomicJson-D3MHqnLW.cjs +59 -0
- package/dist/atomicJson-D3MHqnLW.cjs.map +1 -0
- package/dist/atomicJson-Dj3rlZbD.mjs +40 -0
- package/dist/atomicJson-Dj3rlZbD.mjs.map +1 -0
- package/dist/atomicJson.cjs +4 -2
- package/dist/atomicJson.d.mts +2 -2
- package/dist/atomicJson.mjs +3 -2
- package/dist/{index-BT29GZx5.d.mts → completion-Ba0TiBHa.d.mts} +58 -27
- package/dist/completion-Ba0TiBHa.d.mts.map +1 -0
- package/dist/config-BBvn_ZLP.mjs +1 -0
- package/dist/config-Cdybp9rT.cjs +155 -0
- package/dist/config-Cdybp9rT.cjs.map +1 -0
- package/dist/config-Cxc2xw54.mjs +112 -0
- package/dist/config-Cxc2xw54.mjs.map +1 -0
- package/dist/{config-D2HbBLzo.d.mts → config-pxC7FyUJ.d.mts} +33 -33
- package/dist/config-pxC7FyUJ.d.mts.map +1 -0
- package/dist/config.cjs +21 -17
- package/dist/config.d.mts +3 -3
- package/dist/config.mjs +4 -3
- package/dist/{configPolicy-Ce1QKDxa.cjs → configPolicy-CowlLhK1.cjs} +99 -99
- package/dist/configPolicy-CowlLhK1.cjs.map +1 -0
- package/dist/{configPolicy-DCQgF5-f.mjs → configPolicy-DueX0k6i.mjs} +84 -84
- package/dist/configPolicy-DueX0k6i.mjs.map +1 -0
- package/dist/configSchema.d.mts +2 -2
- package/dist/configWriter-DCASRzr-.mjs +180 -0
- package/dist/configWriter-DCASRzr-.mjs.map +1 -0
- package/dist/configWriter-DGO0gmXo.cjs +218 -0
- package/dist/configWriter-DGO0gmXo.cjs.map +1 -0
- package/dist/configWriter.cjs +7 -4
- package/dist/configWriter.d.mts +2 -2
- package/dist/configWriter.mjs +1 -2
- package/dist/core-B0Qg0Qq2.cjs +224 -0
- package/dist/core-B0Qg0Qq2.cjs.map +1 -0
- package/dist/core-DKWW9gkh.mjs +139 -0
- package/dist/core-DKWW9gkh.mjs.map +1 -0
- package/dist/{domains-DGCb7uMC.cjs → domains-CiDUsRiX.cjs} +473 -171
- package/dist/domains-CiDUsRiX.cjs.map +1 -0
- package/dist/{domains-BroCVHrS.mjs → domains-IlieGHaY.mjs} +437 -170
- package/dist/domains-IlieGHaY.mjs.map +1 -0
- package/dist/domains.cjs +9 -3
- package/dist/domains.d.mts +3 -3
- package/dist/domains.mjs +3 -3
- package/dist/extensions/mcp-ui.mjs +1 -1
- package/dist/extensions/mcp-ui.mjs.map +1 -1
- package/dist/extensions/mcp.cjs +1 -1
- package/dist/extensions/mcp.cjs.map +1 -1
- package/dist/extensions/mcp.d.mts +1 -1
- package/dist/extensions/mcp.mjs +1 -1
- package/dist/extensions/mcp.mjs.map +1 -1
- package/dist/extensions/server.cjs +46 -39
- package/dist/extensions/server.cjs.map +1 -1
- package/dist/extensions/server.d.mts +1 -1
- package/dist/extensions/server.mjs +50 -43
- package/dist/extensions/server.mjs.map +1 -1
- package/dist/{harnessState-Dn_S7fB9.mjs → harnessState-8lZda_8r.mjs} +2 -2
- package/dist/{harnessState-Dn_S7fB9.mjs.map → harnessState-8lZda_8r.mjs.map} +1 -1
- package/dist/{harnessState-CCyBeutB.cjs → harnessState-BrmH0lG5.cjs} +2 -2
- package/dist/{harnessState-CCyBeutB.cjs.map → harnessState-BrmH0lG5.cjs.map} +1 -1
- package/dist/harnessState.cjs +1 -1
- package/dist/harnessState.d.mts +1 -1
- package/dist/harnessState.mjs +1 -2
- package/dist/{harnessStore-CJSxdSkV.cjs → harnessStore-BinxGITh.cjs} +189 -81
- package/dist/harnessStore-BinxGITh.cjs.map +1 -0
- package/dist/{harnessStore-CltwLB7M.mjs → harnessStore-w8zHmJ_H.mjs} +160 -82
- package/dist/harnessStore-w8zHmJ_H.mjs.map +1 -0
- package/dist/harnessStore.cjs +6 -1
- package/dist/harnessStore.d.mts +2 -2
- package/dist/harnessStore.mjs +1 -2
- package/dist/index-7RuIAWX1.d.mts +63 -0
- package/dist/index-7RuIAWX1.d.mts.map +1 -0
- package/dist/{index-Dy_-m7gx.d.mts → index-BTOU4XiI.d.mts} +3 -3
- package/dist/{index-Dy_-m7gx.d.mts.map → index-BTOU4XiI.d.mts.map} +1 -1
- package/dist/{index-CkDDvVQt.d.mts → index-CE5g27J6.d.mts} +52 -16
- package/dist/index-CE5g27J6.d.mts.map +1 -0
- package/dist/index-COokUToB.d.mts +83 -0
- package/dist/index-COokUToB.d.mts.map +1 -0
- package/dist/index-D3vGEYup.d.mts +135 -0
- package/dist/index-D3vGEYup.d.mts.map +1 -0
- package/dist/{index-Ch7ptOkW.d.mts → index-DNEK9W6n.d.mts} +26 -16
- package/dist/index-DNEK9W6n.d.mts.map +1 -0
- package/dist/index-D_OtYtsl.d.mts +37 -0
- package/dist/index-D_OtYtsl.d.mts.map +1 -0
- package/dist/{index-DNk4q0Qm.d.mts → index-Nbj2iQWw.d.mts} +19 -25
- package/dist/index-Nbj2iQWw.d.mts.map +1 -0
- package/dist/{index-CEUc7m16.d.mts → index-dYgF03lE.d.mts} +82 -32
- package/dist/index-dYgF03lE.d.mts.map +1 -0
- package/dist/index-fTuf8tVH.d.mts +34 -0
- package/dist/index-fTuf8tVH.d.mts.map +1 -0
- package/dist/index-nVcEC1jO.d.mts +62 -0
- package/dist/index-nVcEC1jO.d.mts.map +1 -0
- package/dist/index.cjs +80 -59
- package/dist/index.d.mts +16 -15
- package/dist/index.mjs +17 -15
- package/dist/{init-Cd3Ki6Ub.cjs → init-BTb1iBES.cjs} +148 -54
- package/dist/init-BTb1iBES.cjs.map +1 -0
- package/dist/{init-DVzhiCUE.mjs → init-CuxfAN8g.mjs} +126 -43
- package/dist/init-CuxfAN8g.mjs.map +1 -0
- package/dist/init.cjs +8 -6
- package/dist/init.d.mts +2 -2
- package/dist/init.mjs +1 -2
- package/dist/layeredConfig-BbkoIEfM.mjs +95 -0
- package/dist/layeredConfig-BbkoIEfM.mjs.map +1 -0
- package/dist/layeredConfig-Bs18dXQG.cjs +127 -0
- package/dist/layeredConfig-Bs18dXQG.cjs.map +1 -0
- package/dist/layeredConfig.cjs +5 -4
- package/dist/layeredConfig.d.mts +7 -2
- package/dist/layeredConfig.d.mts.map +1 -0
- package/dist/layeredConfig.mjs +1 -2
- package/dist/{majorModes-DnNSH9ZK.cjs → majorModes-Ctsdi8QW.cjs} +116 -19
- package/dist/majorModes-Ctsdi8QW.cjs.map +1 -0
- package/dist/{majorModes-D1Itd8SN.mjs → majorModes-Ddx975MG.mjs} +105 -20
- package/dist/majorModes-Ddx975MG.mjs.map +1 -0
- package/dist/majorModes.cjs +3 -1
- package/dist/majorModes.d.mts +2 -2
- package/dist/majorModes.mjs +1 -2
- package/dist/{piConfig-qGyCbcVj.mjs → piConfig-Bq36PQFF.mjs} +104 -69
- package/dist/piConfig-Bq36PQFF.mjs.map +1 -0
- package/dist/{piConfig-Bx26HMb_.d.mts → piConfig-C9rGUqPe.d.mts} +3 -3
- package/dist/{piConfig-Bx26HMb_.d.mts.map → piConfig-C9rGUqPe.d.mts.map} +1 -1
- package/dist/{piConfig-CCBdZXQr.cjs → piConfig-cluCie5i.cjs} +122 -70
- package/dist/piConfig-cluCie5i.cjs.map +1 -0
- package/dist/piConfig.cjs +4 -1
- package/dist/piConfig.d.mts +2 -2
- package/dist/piConfig.mjs +1 -2
- package/dist/piContext.cjs +27 -16
- package/dist/piContext.d.mts +3 -3
- package/dist/piContext.mjs +1 -2
- package/dist/{profiles-DBcYg5nA.mjs → profiles-CHict0x-.mjs} +322 -101
- package/dist/profiles-CHict0x-.mjs.map +1 -0
- package/dist/{profiles-tpplXY4r.cjs → profiles-t2EUj0aB.cjs} +359 -102
- package/dist/profiles-t2EUj0aB.cjs.map +1 -0
- package/dist/profiles.cjs +7 -1
- package/dist/profiles.d.mts +27 -3
- package/dist/profiles.d.mts.map +1 -1
- package/dist/profiles.mjs +1 -2
- package/dist/selectionSwitch.cjs +88 -22
- package/dist/selectionSwitch.cjs.map +1 -1
- package/dist/selectionSwitch.d.mts +40 -18
- package/dist/selectionSwitch.d.mts.map +1 -1
- package/dist/selectionSwitch.mjs +85 -22
- package/dist/selectionSwitch.mjs.map +1 -1
- package/dist/{sessionConfig-DV5xTRRw.mjs → sessionConfig-DFQF618G.mjs} +48 -47
- package/dist/sessionConfig-DFQF618G.mjs.map +1 -0
- package/dist/{sessionConfig-Bf6xHWLd.cjs → sessionConfig-DeP6pplT.cjs} +74 -85
- package/dist/sessionConfig-DeP6pplT.cjs.map +1 -0
- package/dist/sync-BJ8gCP9s.mjs +29 -0
- package/dist/sync-BJ8gCP9s.mjs.map +1 -0
- package/dist/sync-CC_15kkB.d.mts +46 -0
- package/dist/sync-CC_15kkB.d.mts.map +1 -0
- package/dist/sync-Dlxz2msM.cjs +37 -0
- package/dist/sync-Dlxz2msM.cjs.map +1 -0
- package/dist/types.cjs +2 -2
- package/dist/types.d.mts +2 -2
- package/dist/types.mjs +1 -2
- package/llms.txt +1 -1
- package/package.json +12 -12
- package/src/prompts/po-author-config/SKILL.md +4 -4
- package/src/prompts/po-author-config/agents/openai.yaml +1 -1
- package/src/prompts/po-author-config/references/config-contract.md +5 -5
- package/dist/atomicJson-1W0WhuFF.mjs +0 -44
- package/dist/atomicJson-1W0WhuFF.mjs.map +0 -1
- package/dist/atomicJson-Dt2z_DXl.cjs +0 -52
- package/dist/atomicJson-Dt2z_DXl.cjs.map +0 -1
- package/dist/config-CfTb0_9G.cjs +0 -219
- package/dist/config-CfTb0_9G.cjs.map +0 -1
- package/dist/config-D2HbBLzo.d.mts.map +0 -1
- package/dist/config-DoQWSt1V.mjs +0 -162
- package/dist/config-DoQWSt1V.mjs.map +0 -1
- package/dist/config-NSHRd6na.mjs +0 -2
- package/dist/configPolicy-Ce1QKDxa.cjs.map +0 -1
- package/dist/configPolicy-DCQgF5-f.mjs.map +0 -1
- package/dist/configWriter-B7uO8MuN.cjs +0 -150
- package/dist/configWriter-B7uO8MuN.cjs.map +0 -1
- package/dist/configWriter-DUtLgOlD.mjs +0 -130
- package/dist/configWriter-DUtLgOlD.mjs.map +0 -1
- package/dist/domains-BroCVHrS.mjs.map +0 -1
- package/dist/domains-DGCb7uMC.cjs.map +0 -1
- package/dist/harnessStore-CJSxdSkV.cjs.map +0 -1
- package/dist/harnessStore-CltwLB7M.mjs.map +0 -1
- package/dist/index-78zvoS9s.d.mts +0 -35
- package/dist/index-78zvoS9s.d.mts.map +0 -1
- package/dist/index-BT29GZx5.d.mts.map +0 -1
- package/dist/index-C6GaYjq-.d.mts +0 -6
- package/dist/index-C6GaYjq-.d.mts.map +0 -1
- package/dist/index-CEUc7m16.d.mts.map +0 -1
- package/dist/index-CRcoVYXz.d.mts +0 -39
- package/dist/index-CRcoVYXz.d.mts.map +0 -1
- package/dist/index-CVlciCJF.d.mts +0 -40
- package/dist/index-CVlciCJF.d.mts.map +0 -1
- package/dist/index-Ch7ptOkW.d.mts.map +0 -1
- package/dist/index-CkDDvVQt.d.mts.map +0 -1
- package/dist/index-Cvdt-Y-i.d.mts +0 -66
- package/dist/index-Cvdt-Y-i.d.mts.map +0 -1
- package/dist/index-DNk4q0Qm.d.mts.map +0 -1
- package/dist/index-DdH0w56b.d.mts +0 -116
- package/dist/index-DdH0w56b.d.mts.map +0 -1
- package/dist/init-Cd3Ki6Ub.cjs.map +0 -1
- package/dist/init-DVzhiCUE.mjs.map +0 -1
- package/dist/layeredConfig-fukqN2EA.mjs +0 -67
- package/dist/layeredConfig-fukqN2EA.mjs.map +0 -1
- package/dist/layeredConfig-ol0274TN.cjs +0 -94
- package/dist/layeredConfig-ol0274TN.cjs.map +0 -1
- package/dist/majorModes-D1Itd8SN.mjs.map +0 -1
- package/dist/majorModes-DnNSH9ZK.cjs.map +0 -1
- package/dist/piConfig-CCBdZXQr.cjs.map +0 -1
- package/dist/piConfig-qGyCbcVj.mjs.map +0 -1
- package/dist/profiles-DBcYg5nA.mjs.map +0 -1
- package/dist/profiles-tpplXY4r.cjs.map +0 -1
- package/dist/sessionConfig-Bf6xHWLd.cjs.map +0 -1
- package/dist/sessionConfig-DV5xTRRw.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ Typed runtime configuration and immutable session context for
|
|
|
4
4
|
[Po](https://www.npmjs.com/package/@agimon-ai/po).
|
|
5
5
|
|
|
6
6
|
This package is fixed core in the Po distribution. It loads `config.yaml`, validates supported
|
|
7
|
-
runtime settings, publishes the `
|
|
7
|
+
runtime settings, publishes the `po/config` Cordis service, and maintains selection state shared
|
|
8
8
|
by the major-mode, domain, and profile transition flows.
|
|
9
9
|
|
|
10
10
|
> **Alpha:** configuration contracts may change between releases.
|
|
@@ -66,15 +66,15 @@ the complete runtime schema and additional configuration details.
|
|
|
66
66
|
## Typed session context
|
|
67
67
|
|
|
68
68
|
The native session server root provides
|
|
69
|
-
`
|
|
69
|
+
`PO_CONFIG_SERVICE`. Consumers must access it under an owning service injection:
|
|
70
70
|
|
|
71
71
|
```ts
|
|
72
|
-
import {
|
|
72
|
+
import { PO_CONFIG_SERVICE, type PoConfigContext, requirePoConfigContext } from '@agimon-ai/po-config';
|
|
73
73
|
import type { Context } from '@deepseek-ai/cordis';
|
|
74
74
|
|
|
75
|
-
export function
|
|
76
|
-
context.inject([
|
|
77
|
-
consume(
|
|
75
|
+
export function consumePoConfig(context: Context, consume: (snapshot: PoConfigContext) => void): void {
|
|
76
|
+
context.inject([PO_CONFIG_SERVICE], (configContext) => {
|
|
77
|
+
consume(requirePoConfigContext(configContext));
|
|
78
78
|
});
|
|
79
79
|
}
|
|
80
80
|
```
|
|
@@ -108,9 +108,9 @@ The root and focused `/config` and `/piContext` exports provide runtime parsing,
|
|
|
108
108
|
session integration:
|
|
109
109
|
|
|
110
110
|
```ts
|
|
111
|
-
import {
|
|
111
|
+
import { loadPoConfig } from '@agimon-ai/po-config';
|
|
112
112
|
|
|
113
|
-
const settings =
|
|
113
|
+
const settings = loadPoConfig(process.cwd());
|
|
114
114
|
```
|
|
115
115
|
|
|
116
116
|
Pi-specific JSON settings are exposed through `/pi-config`; their types are exposed through `/config-schema`. The removed `/config/piConfig` and `/config/schema` paths have no aliases.
|
|
@@ -122,7 +122,7 @@ runtime Config authoring skill.
|
|
|
122
122
|
## Help
|
|
123
123
|
|
|
124
124
|
The package publishes `src/prompts/po-author-config`, links it from `llms.txt`, and registers
|
|
125
|
-
`po-author-config` with the `
|
|
125
|
+
`po-author-config` with the `po/help` Cordis service. The guidance is visible to the AI only
|
|
126
126
|
while the parent Help minor mode is active.
|
|
127
127
|
|
|
128
128
|
## Remote session app
|
package/dist/agentPluginMcp.cjs
CHANGED
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_rolldown_runtime = require("./rolldown-runtime-VH7oDXx4.cjs");
|
|
3
|
-
let node_crypto = require("node:crypto");
|
|
4
|
-
let node_fs = require("node:fs");
|
|
5
|
-
node_fs = require_rolldown_runtime.__toESM(node_fs, 1);
|
|
6
|
-
let node_http = require("node:http");
|
|
7
|
-
let node_net = require("node:net");
|
|
8
3
|
let node_path = require("node:path");
|
|
9
4
|
node_path = require_rolldown_runtime.__toESM(node_path, 1);
|
|
5
|
+
let _agimon_ai_po_core_platform = require("@agimon-ai/po-core/platform");
|
|
6
|
+
let node_crypto = require("node:crypto");
|
|
10
7
|
let _agimon_ai_po_core_mcpProjection = require("@agimon-ai/po-core/mcpProjection");
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
8
|
+
let node_fs = require("node:fs");
|
|
9
|
+
node_fs = require_rolldown_runtime.__toESM(node_fs, 1);
|
|
10
|
+
//#region src/services/agentPluginMcp/normalize.ts
|
|
11
|
+
/** Pure Agent Plugin MCP normalisation shared by the async API and the deprecated Node-only sync shim. */
|
|
12
|
+
/** Same token grammar as node:http `validateHeaderName` (RFC 9110 tchar). */
|
|
13
|
+
const HTTP_TOKEN = /^[\^_`a-zA-Z\-0-9!#$%&'*+.|~]+$/u;
|
|
14
|
+
/** Same rejected characters as node:http `validateHeaderValue`. */
|
|
15
|
+
const INVALID_HEADER_VALUE_CHARACTER = /[^\t\x20-\x7e\x80-\xff]/u;
|
|
16
|
+
const IPV4_OCTET = "(?:25[0-5]|2[0-4]\\d|1\\d\\d|[1-9]?\\d)";
|
|
17
|
+
const IPV4_ADDRESS = new RegExp(`^${IPV4_OCTET}(?:\\.${IPV4_OCTET}){3}$`, "u");
|
|
18
|
+
function isIPv4(value) {
|
|
19
|
+
return IPV4_ADDRESS.test(value);
|
|
20
|
+
}
|
|
14
21
|
const RESERVED_ENVIRONMENT_KEYS = /* @__PURE__ */ new Set(["PLUGIN_ROOT", "PLUGIN_DATA"]);
|
|
15
22
|
const PROXY_SERVER_ALIASES = /* @__PURE__ */ new Set(["mcp-proxy", "agiflow-proxy"]);
|
|
16
23
|
function isObject(value) {
|
|
@@ -47,49 +54,6 @@ function isInside(root, candidate) {
|
|
|
47
54
|
const relative = node_path.default.relative(root, candidate);
|
|
48
55
|
return relative === "" || !relative.startsWith(`..${node_path.default.sep}`) && relative !== ".." && !node_path.default.isAbsolute(relative);
|
|
49
56
|
}
|
|
50
|
-
function realContainedPath(root, candidate, label) {
|
|
51
|
-
let realCandidate;
|
|
52
|
-
try {
|
|
53
|
-
realCandidate = node_fs.default.realpathSync(candidate);
|
|
54
|
-
} catch {
|
|
55
|
-
throw new Error(`${label} does not exist`);
|
|
56
|
-
}
|
|
57
|
-
if (!isInside(root, realCandidate)) throw new Error(`${label} escapes its allowed plugin directory`);
|
|
58
|
-
return realCandidate;
|
|
59
|
-
}
|
|
60
|
-
function normalizeCommand(command, pluginRoot) {
|
|
61
|
-
if (command.startsWith("./")) {
|
|
62
|
-
const resolved = realContainedPath(pluginRoot, node_path.default.resolve(pluginRoot, command), "command");
|
|
63
|
-
if (!node_fs.default.statSync(resolved).isFile()) throw new Error("command must resolve to a regular file");
|
|
64
|
-
try {
|
|
65
|
-
node_fs.default.accessSync(resolved, node_fs.default.constants.X_OK);
|
|
66
|
-
} catch {
|
|
67
|
-
throw new Error("command must resolve to an executable file");
|
|
68
|
-
}
|
|
69
|
-
return resolved;
|
|
70
|
-
}
|
|
71
|
-
if (command === "." || command === ".." || /[\s/\\]/u.test(command)) throw new Error("command must be a bare executable name or a ./ path inside the plugin");
|
|
72
|
-
return command;
|
|
73
|
-
}
|
|
74
|
-
function normalizeWorkingDirectory(declared, pluginRoot, pluginDataDirectory) {
|
|
75
|
-
if (declared === void 0) return pluginRoot;
|
|
76
|
-
const cwd = nonEmptyString(declared, "cwd");
|
|
77
|
-
let base;
|
|
78
|
-
let expanded;
|
|
79
|
-
if (cwd.startsWith("./")) {
|
|
80
|
-
base = pluginRoot;
|
|
81
|
-
expanded = node_path.default.resolve(pluginRoot, cwd);
|
|
82
|
-
} else if (cwd === "${PLUGIN_ROOT}" || cwd.startsWith("${PLUGIN_ROOT}/")) {
|
|
83
|
-
base = pluginRoot;
|
|
84
|
-
expanded = expandPluginPlaceholders(cwd, pluginRoot, pluginDataDirectory);
|
|
85
|
-
} else if (cwd === "${PLUGIN_DATA}" || cwd.startsWith("${PLUGIN_DATA}/")) {
|
|
86
|
-
base = pluginDataDirectory;
|
|
87
|
-
expanded = expandPluginPlaceholders(cwd, pluginRoot, pluginDataDirectory);
|
|
88
|
-
} else throw new Error("cwd must start with ./, ${PLUGIN_ROOT}, or ${PLUGIN_DATA}");
|
|
89
|
-
const resolved = realContainedPath(base, node_path.default.resolve(expanded), "cwd");
|
|
90
|
-
if (!node_fs.default.statSync(resolved).isDirectory()) throw new Error("cwd must resolve to a directory");
|
|
91
|
-
return resolved;
|
|
92
|
-
}
|
|
93
57
|
function normalizeEnvironment(value, pluginRoot, pluginDataDirectory) {
|
|
94
58
|
const declared = optionalStringRecord(value, "env") ?? {};
|
|
95
59
|
for (const key of Object.keys(declared)) {
|
|
@@ -105,9 +69,8 @@ function normalizeEnvironment(value, pluginRoot, pluginDataDirectory) {
|
|
|
105
69
|
function isLoopbackHost(hostname) {
|
|
106
70
|
const bareHostname = hostname.startsWith("[") && hostname.endsWith("]") ? hostname.slice(1, -1) : hostname;
|
|
107
71
|
if (bareHostname.toLowerCase() === "localhost") return true;
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
return family === 6 && bareHostname === "::1";
|
|
72
|
+
if (isIPv4(bareHostname)) return bareHostname.split(".")[0] === "127";
|
|
73
|
+
return bareHostname === "::1";
|
|
111
74
|
}
|
|
112
75
|
function normalizeUrl(value) {
|
|
113
76
|
const raw = nonEmptyString(value, "url");
|
|
@@ -131,48 +94,89 @@ function normalizeHeaders(value) {
|
|
|
131
94
|
const canonical = name.toLowerCase();
|
|
132
95
|
if (seen.has(canonical)) throw new Error(`headers contain duplicate name "${name}"`);
|
|
133
96
|
seen.add(canonical);
|
|
134
|
-
|
|
135
|
-
(0, node_http.validateHeaderName)(name);
|
|
136
|
-
(0, node_http.validateHeaderValue)(name, item);
|
|
137
|
-
} catch {
|
|
138
|
-
throw new Error(`header "${name}" is invalid`);
|
|
139
|
-
}
|
|
97
|
+
if (!HTTP_TOKEN.test(name) || INVALID_HEADER_VALUE_CHARACTER.test(item)) throw new Error(`header "${name}" is invalid`);
|
|
140
98
|
}
|
|
141
99
|
return headers;
|
|
142
100
|
}
|
|
143
|
-
function
|
|
101
|
+
function stagedInternalPath(source, contents, stagingDirectory) {
|
|
102
|
+
const digest = (0, node_crypto.createHash)("sha256").update(source.sourceId).update("\0").update(source.contentDigest).update("\0").update(contents).digest("hex").slice(0, 20);
|
|
103
|
+
return node_path.default.join(stagingDirectory, `agent-plugin-mcp-${digest}.internal.json`);
|
|
104
|
+
}
|
|
105
|
+
function emptyResult(diagnostic) {
|
|
106
|
+
return {
|
|
107
|
+
claudeConfig: { mcpServers: {} },
|
|
108
|
+
serverNames: [],
|
|
109
|
+
droppedServers: [],
|
|
110
|
+
diagnostics: [diagnostic]
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
function sourceLabel(source) {
|
|
114
|
+
return `Agent Plugin MCP source "${source.sourceId}"`;
|
|
115
|
+
}
|
|
116
|
+
/** Checks the projection identity before any file is touched. */
|
|
117
|
+
function assertProjectionSource(source) {
|
|
118
|
+
if (source.mcpSchemaUrl !== _agimon_ai_po_core_mcpProjection.AGENT_PLUGIN_MCP_SCHEMA_URL) throw new Error(`unsupported schema "${String(source.mcpSchemaUrl)}"`);
|
|
119
|
+
if (!node_path.default.isAbsolute(source.pluginRoot) || !node_path.default.isAbsolute(source.pluginDataDirectory)) throw new Error("pluginRoot and pluginDataDirectory must be absolute");
|
|
120
|
+
}
|
|
121
|
+
function assertRootConfigPath(configPath, pluginRoot) {
|
|
122
|
+
if (node_path.default.dirname(configPath) !== pluginRoot || node_path.default.basename(configPath) !== "mcp.json") throw new Error("portable plugin config must be root mcp.json");
|
|
123
|
+
}
|
|
124
|
+
/** Verifies the digest and document shape of the raw mcp.json bytes; returns its server map. */
|
|
125
|
+
function parseMcpDocument(source, bytes, label) {
|
|
126
|
+
if ((0, node_crypto.createHash)("sha256").update(bytes).digest("hex") !== source.contentDigest) throw new Error("content digest does not match the projection");
|
|
127
|
+
const parsed = JSON.parse(new TextDecoder("utf-8", { ignoreBOM: true }).decode(bytes));
|
|
128
|
+
if (!isObject(parsed)) throw new Error("document must be an object");
|
|
129
|
+
onlyKeys(parsed, ["$schema", "mcpServers"], label);
|
|
130
|
+
if (parsed.$schema !== source.mcpSchemaUrl) throw new Error("$schema does not match the projected schema");
|
|
131
|
+
if (!isObject(parsed.mcpServers)) throw new Error("mcpServers must be an object");
|
|
132
|
+
return parsed.mcpServers;
|
|
133
|
+
}
|
|
134
|
+
/** Whether a declared server is skipped (empty name), dropped by the allowlist or wrapper rule, or normalised. */
|
|
135
|
+
function serverDisposition(name, options) {
|
|
136
|
+
if (!name) return "empty";
|
|
137
|
+
const allowed = options.allowedServers;
|
|
138
|
+
const keepAll = !allowed || allowed.length === 0;
|
|
139
|
+
const stripProxyWrappers = options.stripProxyWrappers ?? true;
|
|
140
|
+
if (!keepAll && !allowed.includes(name) || stripProxyWrappers && PROXY_SERVER_ALIASES.has(name)) return "dropped";
|
|
141
|
+
return "keep";
|
|
142
|
+
}
|
|
143
|
+
function emptyServerDiagnostic(label) {
|
|
144
|
+
return `${label} contains an empty server name; that entry was skipped.`;
|
|
145
|
+
}
|
|
146
|
+
function skippedServerDiagnostic(label, name, error) {
|
|
147
|
+
return `${label} skipped server "${name}": ${error instanceof Error ? error.message : String(error)}`;
|
|
148
|
+
}
|
|
149
|
+
function disabledResult(label, error) {
|
|
150
|
+
return emptyResult(`${label} was disabled: ${error instanceof Error ? error.message : String(error)}`);
|
|
151
|
+
}
|
|
152
|
+
/** The declared transport of one server; stdio declarations are also checked for unsupported fields. */
|
|
153
|
+
function serverType(raw, name) {
|
|
144
154
|
if (!isObject(raw)) throw new Error("definition must be an object");
|
|
145
155
|
const type = nonEmptyString(raw.type, "type");
|
|
146
|
-
if (type === "stdio")
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
claude: {
|
|
171
|
-
type: "stdio",
|
|
172
|
-
...config
|
|
173
|
-
}
|
|
174
|
-
};
|
|
175
|
-
}
|
|
156
|
+
if (type === "stdio") onlyKeys(raw, [
|
|
157
|
+
"type",
|
|
158
|
+
"command",
|
|
159
|
+
"args",
|
|
160
|
+
"env",
|
|
161
|
+
"cwd"
|
|
162
|
+
], `server "${name}"`);
|
|
163
|
+
return type;
|
|
164
|
+
}
|
|
165
|
+
function stdioServer(name, config) {
|
|
166
|
+
return {
|
|
167
|
+
internal: {
|
|
168
|
+
name,
|
|
169
|
+
transport: "stdio",
|
|
170
|
+
config
|
|
171
|
+
},
|
|
172
|
+
claude: {
|
|
173
|
+
type: "stdio",
|
|
174
|
+
...config
|
|
175
|
+
}
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
/** A streamable-http or sse server; any other type is unsupported. */
|
|
179
|
+
function remoteServer(raw, name, type) {
|
|
176
180
|
if (type === "streamable-http" || type === "sse") {
|
|
177
181
|
onlyKeys(raw, [
|
|
178
182
|
"type",
|
|
@@ -200,21 +204,93 @@ function normalizeServer(raw, pluginRoot, pluginDataDirectory, name) {
|
|
|
200
204
|
}
|
|
201
205
|
throw new Error(`unsupported transport type "${type}"`);
|
|
202
206
|
}
|
|
203
|
-
function
|
|
204
|
-
|
|
205
|
-
|
|
207
|
+
function stagedContents(internalServers) {
|
|
208
|
+
return `${JSON.stringify({ mcpServers: internalServers }, null, 2)}\n`;
|
|
209
|
+
}
|
|
210
|
+
function stagedCacheKey(source, configPath, contents) {
|
|
211
|
+
return (0, node_crypto.createHash)("sha256").update("agent-plugin-v1\0").update(source.sourceId).update("\0").update(source.contentDigest).update("\0").update(configPath).update("\0").update(contents).digest("hex");
|
|
212
|
+
}
|
|
213
|
+
/** The containment base and expanded path of a declared cwd, or undefined when the plugin root is the default. */
|
|
214
|
+
function workingDirectoryCandidate(declared, pluginRoot, pluginDataDirectory) {
|
|
215
|
+
if (declared === void 0) return void 0;
|
|
216
|
+
const cwd = nonEmptyString(declared, "cwd");
|
|
217
|
+
if (cwd.startsWith("./")) return {
|
|
218
|
+
base: pluginRoot,
|
|
219
|
+
candidate: node_path.default.resolve(pluginRoot, cwd)
|
|
220
|
+
};
|
|
221
|
+
const expanded = expandPluginPlaceholders(cwd, pluginRoot, pluginDataDirectory);
|
|
222
|
+
if (cwd === "${PLUGIN_ROOT}" || cwd.startsWith("${PLUGIN_ROOT}/")) return {
|
|
223
|
+
base: pluginRoot,
|
|
224
|
+
candidate: node_path.default.resolve(expanded)
|
|
225
|
+
};
|
|
226
|
+
if (cwd === "${PLUGIN_DATA}" || cwd.startsWith("${PLUGIN_DATA}/")) return {
|
|
227
|
+
base: pluginDataDirectory,
|
|
228
|
+
candidate: node_path.default.resolve(expanded)
|
|
229
|
+
};
|
|
230
|
+
throw new Error("cwd must start with ./, ${PLUGIN_ROOT}, or ${PLUGIN_DATA}");
|
|
206
231
|
}
|
|
207
|
-
|
|
232
|
+
/** Rejects a command that is neither a bare executable name nor a ./ path; true when it must be resolved. */
|
|
233
|
+
function isPluginRelativeCommand(command) {
|
|
234
|
+
if (command.startsWith("./")) return true;
|
|
235
|
+
if (command === "." || command === ".." || /[\s/\\]/u.test(command)) throw new Error("command must be a bare executable name or a ./ path inside the plugin");
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
//#endregion
|
|
239
|
+
//#region src/services/agentPluginMcp/sync.ts
|
|
240
|
+
function realContainedPath$1(root, candidate, label) {
|
|
241
|
+
let realCandidate;
|
|
242
|
+
try {
|
|
243
|
+
realCandidate = node_fs.default.realpathSync(candidate);
|
|
244
|
+
} catch {
|
|
245
|
+
throw new Error(`${label} does not exist`);
|
|
246
|
+
}
|
|
247
|
+
if (!isInside(root, realCandidate)) throw new Error(`${label} escapes its allowed plugin directory`);
|
|
248
|
+
return realCandidate;
|
|
249
|
+
}
|
|
250
|
+
function normalizeCommand$1(command, pluginRoot) {
|
|
251
|
+
if (!isPluginRelativeCommand(command)) return command;
|
|
252
|
+
const resolved = realContainedPath$1(pluginRoot, node_path.default.resolve(pluginRoot, command), "command");
|
|
253
|
+
if (!node_fs.default.statSync(resolved).isFile()) throw new Error("command must resolve to a regular file");
|
|
254
|
+
try {
|
|
255
|
+
node_fs.default.accessSync(resolved, node_fs.default.constants.X_OK);
|
|
256
|
+
} catch {
|
|
257
|
+
throw new Error("command must resolve to an executable file");
|
|
258
|
+
}
|
|
259
|
+
return resolved;
|
|
260
|
+
}
|
|
261
|
+
function normalizeWorkingDirectory$1(declared, pluginRoot, pluginDataDirectory) {
|
|
262
|
+
const declaredCwd = workingDirectoryCandidate(declared, pluginRoot, pluginDataDirectory);
|
|
263
|
+
if (!declaredCwd) return pluginRoot;
|
|
264
|
+
const resolved = realContainedPath$1(declaredCwd.base, declaredCwd.candidate, "cwd");
|
|
265
|
+
if (!node_fs.default.statSync(resolved).isDirectory()) throw new Error("cwd must resolve to a directory");
|
|
266
|
+
return resolved;
|
|
267
|
+
}
|
|
268
|
+
function normalizeServer$1(raw, pluginRoot, pluginDataDirectory, name) {
|
|
269
|
+
const type = serverType(raw, name);
|
|
270
|
+
const declaration = raw;
|
|
271
|
+
if (type !== "stdio") return remoteServer(declaration, name, type);
|
|
272
|
+
const command = normalizeCommand$1(nonEmptyString(declaration.command, "command"), pluginRoot);
|
|
273
|
+
const args = optionalStringArray(declaration.args, "args")?.map((item) => expandPluginPlaceholders(item, pluginRoot, pluginDataDirectory));
|
|
274
|
+
const env = normalizeEnvironment(declaration.env, pluginRoot, pluginDataDirectory);
|
|
275
|
+
const cwd = normalizeWorkingDirectory$1(declaration.cwd, pluginRoot, pluginDataDirectory);
|
|
276
|
+
return stdioServer(name, {
|
|
277
|
+
command,
|
|
278
|
+
...args ? { args } : {},
|
|
279
|
+
env,
|
|
280
|
+
cwd
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
function writeStagedInternalConfig$1(target, contents, stagingDirectory) {
|
|
208
284
|
node_fs.default.mkdirSync(stagingDirectory, {
|
|
209
|
-
mode:
|
|
285
|
+
mode: 448,
|
|
210
286
|
recursive: true
|
|
211
287
|
});
|
|
212
|
-
node_fs.default.chmodSync(stagingDirectory,
|
|
288
|
+
node_fs.default.chmodSync(stagingDirectory, 448);
|
|
213
289
|
const temporaryPath = `${target}.${process.pid}-${(0, node_crypto.randomUUID)()}.tmp`;
|
|
214
290
|
try {
|
|
215
291
|
node_fs.default.writeFileSync(temporaryPath, contents, {
|
|
216
292
|
flag: "wx",
|
|
217
|
-
mode:
|
|
293
|
+
mode: 384
|
|
218
294
|
});
|
|
219
295
|
try {
|
|
220
296
|
node_fs.default.renameSync(temporaryPath, target);
|
|
@@ -224,19 +300,11 @@ function writeStagedInternalConfig(target, contents, stagingDirectory) {
|
|
|
224
300
|
const stat = node_fs.default.lstatSync(target);
|
|
225
301
|
if (stat.isSymbolicLink() || !stat.isFile() || node_fs.default.readFileSync(target, "utf8") !== contents) throw new Error("content-addressed staging path is occupied by different content");
|
|
226
302
|
}
|
|
227
|
-
node_fs.default.chmodSync(target,
|
|
303
|
+
node_fs.default.chmodSync(target, 384);
|
|
228
304
|
} finally {
|
|
229
305
|
node_fs.default.rmSync(temporaryPath, { force: true });
|
|
230
306
|
}
|
|
231
307
|
}
|
|
232
|
-
function emptyResult(diagnostic) {
|
|
233
|
-
return {
|
|
234
|
-
claudeConfig: { mcpServers: {} },
|
|
235
|
-
serverNames: [],
|
|
236
|
-
droppedServers: [],
|
|
237
|
-
diagnostics: [diagnostic]
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
308
|
/**
|
|
241
309
|
* Validates one Agent Plugins v1 `mcp.json` and stages a native mcp-proxy layer.
|
|
242
310
|
*
|
|
@@ -244,12 +312,13 @@ function emptyResult(diagnostic) {
|
|
|
244
312
|
* Document/schema, source identity, or plugin-boundary failures reject the whole
|
|
245
313
|
* source. Only the two portable plugin placeholders are expanded; the staged
|
|
246
314
|
* layer is tagged `internal`, so legacy `${ENV}` interpolation cannot run again.
|
|
315
|
+
*
|
|
316
|
+
* @deprecated Node-only sync shim; use normalizeAgentPluginMcpSourceAsync. Removed in M2.
|
|
247
317
|
*/
|
|
248
318
|
function normalizeAgentPluginMcpSource(source, options) {
|
|
249
|
-
const label =
|
|
319
|
+
const label = sourceLabel(source);
|
|
250
320
|
try {
|
|
251
|
-
|
|
252
|
-
if (!node_path.default.isAbsolute(source.pluginRoot) || !node_path.default.isAbsolute(source.pluginDataDirectory)) throw new Error("pluginRoot and pluginDataDirectory must be absolute");
|
|
321
|
+
assertProjectionSource(source);
|
|
253
322
|
const pluginRoot = node_fs.default.realpathSync(source.pluginRoot);
|
|
254
323
|
try {
|
|
255
324
|
const dataStat = node_fs.default.lstatSync(source.pluginDataDirectory);
|
|
@@ -257,55 +326,164 @@ function normalizeAgentPluginMcpSource(source, options) {
|
|
|
257
326
|
} catch (error) {
|
|
258
327
|
if (error.code !== "ENOENT") throw error;
|
|
259
328
|
node_fs.default.mkdirSync(source.pluginDataDirectory, {
|
|
260
|
-
mode:
|
|
329
|
+
mode: 448,
|
|
261
330
|
recursive: true
|
|
262
331
|
});
|
|
263
332
|
}
|
|
264
|
-
node_fs.default.chmodSync(source.pluginDataDirectory,
|
|
333
|
+
node_fs.default.chmodSync(source.pluginDataDirectory, 448);
|
|
265
334
|
node_fs.default.accessSync(source.pluginDataDirectory, node_fs.default.constants.W_OK);
|
|
266
335
|
const pluginDataDirectory = node_fs.default.realpathSync(source.pluginDataDirectory);
|
|
267
|
-
const configPath = realContainedPath(pluginRoot, source.configPath, "mcp.json");
|
|
268
|
-
|
|
269
|
-
const
|
|
270
|
-
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
336
|
+
const configPath = realContainedPath$1(pluginRoot, source.configPath, "mcp.json");
|
|
337
|
+
assertRootConfigPath(configPath, pluginRoot);
|
|
338
|
+
const servers = parseMcpDocument(source, node_fs.default.readFileSync(configPath), label);
|
|
339
|
+
const internalServers = Object.create(null);
|
|
340
|
+
const claudeServers = Object.create(null);
|
|
341
|
+
const droppedServers = [];
|
|
342
|
+
const diagnostics = [];
|
|
343
|
+
for (const [name, rawServer] of Object.entries(servers)) {
|
|
344
|
+
const disposition = serverDisposition(name, options);
|
|
345
|
+
if (disposition === "empty") {
|
|
346
|
+
diagnostics.push(emptyServerDiagnostic(label));
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
if (disposition === "dropped") {
|
|
350
|
+
droppedServers.push(name);
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
try {
|
|
354
|
+
const normalized = normalizeServer$1(rawServer, pluginRoot, pluginDataDirectory, name);
|
|
355
|
+
internalServers[name] = normalized.internal;
|
|
356
|
+
claudeServers[name] = normalized.claude;
|
|
357
|
+
} catch (error) {
|
|
358
|
+
droppedServers.push(name);
|
|
359
|
+
diagnostics.push(skippedServerDiagnostic(label, name, error));
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
const contents = stagedContents(internalServers);
|
|
363
|
+
const target = stagedInternalPath(source, contents, options.stagingDirectory);
|
|
364
|
+
writeStagedInternalConfig$1(target, contents, options.stagingDirectory);
|
|
365
|
+
return {
|
|
366
|
+
configSource: {
|
|
367
|
+
path: target,
|
|
368
|
+
format: "internal",
|
|
369
|
+
cacheKey: stagedCacheKey(source, configPath, contents)
|
|
370
|
+
},
|
|
371
|
+
claudeConfig: { mcpServers: claudeServers },
|
|
372
|
+
serverNames: Object.keys(internalServers),
|
|
373
|
+
droppedServers,
|
|
374
|
+
diagnostics
|
|
375
|
+
};
|
|
376
|
+
} catch (error) {
|
|
377
|
+
return disabledResult(label, error);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
//#endregion
|
|
381
|
+
//#region src/services/agentPluginMcp/index.ts
|
|
382
|
+
async function realContainedPath(fs, root, candidate, label, context) {
|
|
383
|
+
const real = await fs.canonicalPath(candidate, context);
|
|
384
|
+
if (!real.ok) throw new Error(`${label} does not exist`);
|
|
385
|
+
if (!isInside(root, real.value)) throw new Error(`${label} escapes its allowed plugin directory`);
|
|
386
|
+
return real.value;
|
|
387
|
+
}
|
|
388
|
+
async function normalizeCommand(fs, command, pluginRoot, context) {
|
|
389
|
+
if (!isPluginRelativeCommand(command)) return command;
|
|
390
|
+
const resolved = await realContainedPath(fs, pluginRoot, node_path.default.resolve(pluginRoot, command), "command", context);
|
|
391
|
+
if ((0, _agimon_ai_po_core_platform.getOrThrow)(await fs.fileInfo(resolved, context)).kind !== "file") throw new Error("command must resolve to a regular file");
|
|
392
|
+
const mode = await _agimon_ai_po_core_platform.nodeHost.fileMode(resolved);
|
|
393
|
+
if (mode !== void 0 && (mode & 73) === 0) throw new Error("command must resolve to an executable file");
|
|
394
|
+
return resolved;
|
|
395
|
+
}
|
|
396
|
+
async function normalizeWorkingDirectory(fs, declared, pluginRoot, pluginDataDirectory, context) {
|
|
397
|
+
const declaredCwd = workingDirectoryCandidate(declared, pluginRoot, pluginDataDirectory);
|
|
398
|
+
if (!declaredCwd) return pluginRoot;
|
|
399
|
+
const resolved = await realContainedPath(fs, declaredCwd.base, declaredCwd.candidate, "cwd", context);
|
|
400
|
+
if ((0, _agimon_ai_po_core_platform.getOrThrow)(await fs.fileInfo(resolved, context)).kind !== "directory") throw new Error("cwd must resolve to a directory");
|
|
401
|
+
return resolved;
|
|
402
|
+
}
|
|
403
|
+
async function normalizeServer(fs, raw, pluginRoot, pluginDataDirectory, name, context) {
|
|
404
|
+
const type = serverType(raw, name);
|
|
405
|
+
const declaration = raw;
|
|
406
|
+
if (type !== "stdio") return remoteServer(declaration, name, type);
|
|
407
|
+
const command = await normalizeCommand(fs, nonEmptyString(declaration.command, "command"), pluginRoot, context);
|
|
408
|
+
const args = optionalStringArray(declaration.args, "args")?.map((item) => expandPluginPlaceholders(item, pluginRoot, pluginDataDirectory));
|
|
409
|
+
const env = normalizeEnvironment(declaration.env, pluginRoot, pluginDataDirectory);
|
|
410
|
+
const cwd = await normalizeWorkingDirectory(fs, declaration.cwd, pluginRoot, pluginDataDirectory, context);
|
|
411
|
+
return stdioServer(name, {
|
|
412
|
+
command,
|
|
413
|
+
...args ? { args } : {},
|
|
414
|
+
env,
|
|
415
|
+
cwd
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
/** Owner-only plugin data directory; a symbolic link or non-directory rejects the source. */
|
|
419
|
+
async function preparePluginDataDirectory(fs, directory, context) {
|
|
420
|
+
const info = await fs.fileInfo(directory, context);
|
|
421
|
+
if (info.ok) {
|
|
422
|
+
if (info.value.kind !== "directory") throw new Error("pluginDataDirectory must be a real directory");
|
|
423
|
+
} else if (info.error.code === "not_found") (0, _agimon_ai_po_core_platform.getOrThrow)(await fs.createDir(directory, { recursive: true }, context));
|
|
424
|
+
else throw info.error;
|
|
425
|
+
await _agimon_ai_po_core_platform.nodeHost.setMode(directory, 448);
|
|
426
|
+
return (0, _agimon_ai_po_core_platform.getOrThrow)(await fs.canonicalPath(directory, context));
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* Content-addressed and owner-only. The name hashes the contents, so replacing an
|
|
430
|
+
* existing staged file through a uniquely named temporary file never changes what it holds.
|
|
431
|
+
*/
|
|
432
|
+
async function writeStagedInternalConfig(fs, target, contents, stagingDirectory, context) {
|
|
433
|
+
(0, _agimon_ai_po_core_platform.getOrThrow)(await fs.createDir(stagingDirectory, { recursive: true }, context));
|
|
434
|
+
await _agimon_ai_po_core_platform.nodeHost.setMode(stagingDirectory, 448);
|
|
435
|
+
await (0, _agimon_ai_po_core_platform.writeFileAtomic)(fs, target, contents, context);
|
|
436
|
+
await _agimon_ai_po_core_platform.nodeHost.setMode(target, 384);
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Validates one Agent Plugins v1 `mcp.json` and stages a native mcp-proxy layer,
|
|
440
|
+
* reading and writing through the injected file system.
|
|
441
|
+
*
|
|
442
|
+
* Server validation is isolated: one malformed server is diagnosed and skipped.
|
|
443
|
+
* Document/schema, source identity, or plugin-boundary failures reject the whole
|
|
444
|
+
* source. Only the two portable plugin placeholders are expanded; the staged
|
|
445
|
+
* layer is tagged `internal`, so legacy `${ENV}` interpolation cannot run again.
|
|
446
|
+
*/
|
|
447
|
+
async function normalizeAgentPluginMcpSourceAsync(fs, source, options, context) {
|
|
448
|
+
const label = sourceLabel(source);
|
|
449
|
+
try {
|
|
450
|
+
assertProjectionSource(source);
|
|
451
|
+
const pluginRoot = (0, _agimon_ai_po_core_platform.getOrThrow)(await fs.canonicalPath(source.pluginRoot, context));
|
|
452
|
+
const pluginDataDirectory = await preparePluginDataDirectory(fs, source.pluginDataDirectory, context);
|
|
453
|
+
const configPath = await realContainedPath(fs, pluginRoot, source.configPath, "mcp.json", context);
|
|
454
|
+
assertRootConfigPath(configPath, pluginRoot);
|
|
455
|
+
const servers = parseMcpDocument(source, (0, _agimon_ai_po_core_platform.getOrThrow)(await fs.readBinaryFile(configPath, context)), label);
|
|
279
456
|
const internalServers = Object.create(null);
|
|
280
457
|
const claudeServers = Object.create(null);
|
|
281
458
|
const droppedServers = [];
|
|
282
459
|
const diagnostics = [];
|
|
283
|
-
for (const [name, rawServer] of Object.entries(
|
|
284
|
-
|
|
285
|
-
|
|
460
|
+
for (const [name, rawServer] of Object.entries(servers)) {
|
|
461
|
+
const disposition = serverDisposition(name, options);
|
|
462
|
+
if (disposition === "empty") {
|
|
463
|
+
diagnostics.push(emptyServerDiagnostic(label));
|
|
286
464
|
continue;
|
|
287
465
|
}
|
|
288
|
-
if (
|
|
466
|
+
if (disposition === "dropped") {
|
|
289
467
|
droppedServers.push(name);
|
|
290
468
|
continue;
|
|
291
469
|
}
|
|
292
470
|
try {
|
|
293
|
-
const normalized = normalizeServer(rawServer, pluginRoot, pluginDataDirectory, name);
|
|
471
|
+
const normalized = await normalizeServer(fs, rawServer, pluginRoot, pluginDataDirectory, name, context);
|
|
294
472
|
internalServers[name] = normalized.internal;
|
|
295
473
|
claudeServers[name] = normalized.claude;
|
|
296
474
|
} catch (error) {
|
|
297
475
|
droppedServers.push(name);
|
|
298
|
-
diagnostics.push(
|
|
476
|
+
diagnostics.push(skippedServerDiagnostic(label, name, error));
|
|
299
477
|
}
|
|
300
478
|
}
|
|
301
|
-
const contents =
|
|
479
|
+
const contents = stagedContents(internalServers);
|
|
302
480
|
const target = stagedInternalPath(source, contents, options.stagingDirectory);
|
|
303
|
-
writeStagedInternalConfig(target, contents, options.stagingDirectory);
|
|
481
|
+
await writeStagedInternalConfig(fs, target, contents, options.stagingDirectory, context);
|
|
304
482
|
return {
|
|
305
483
|
configSource: {
|
|
306
484
|
path: target,
|
|
307
485
|
format: "internal",
|
|
308
|
-
cacheKey: (
|
|
486
|
+
cacheKey: stagedCacheKey(source, configPath, contents)
|
|
309
487
|
},
|
|
310
488
|
claudeConfig: { mcpServers: claudeServers },
|
|
311
489
|
serverNames: Object.keys(internalServers),
|
|
@@ -313,10 +491,11 @@ function normalizeAgentPluginMcpSource(source, options) {
|
|
|
313
491
|
diagnostics
|
|
314
492
|
};
|
|
315
493
|
} catch (error) {
|
|
316
|
-
return
|
|
494
|
+
return disabledResult(label, error);
|
|
317
495
|
}
|
|
318
496
|
}
|
|
319
497
|
//#endregion
|
|
320
498
|
exports.normalizeAgentPluginMcpSource = normalizeAgentPluginMcpSource;
|
|
499
|
+
exports.normalizeAgentPluginMcpSourceAsync = normalizeAgentPluginMcpSourceAsync;
|
|
321
500
|
|
|
322
501
|
//# sourceMappingURL=agentPluginMcp.cjs.map
|