@reactionary/mcp 0.0.0-stage → 0.0.1
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 +461 -2
- package/cli.js +125 -0
- package/index.js +4 -0
- package/inspect.js +158 -0
- package/lib/env-client-builder.js +283 -0
- package/lib/load-project-root-env.js +40 -0
- package/lib/reactionary-mcp-server.js +322 -0
- package/lib/shopping-agent-guide.js +138 -0
- package/lib/tool-discovery.js +21 -0
- package/package.json +35 -4
- package/src/cli.d.ts +8 -0
- package/src/index.d.ts +4 -0
- package/src/inspect.d.ts +2 -0
- package/src/lib/env-client-builder.d.ts +16 -0
- package/src/lib/load-project-root-env.d.ts +2 -0
- package/src/lib/reactionary-mcp-server.d.ts +28 -0
- package/src/lib/shopping-agent-guide.d.ts +3 -0
- package/src/lib/tool-discovery.d.ts +11 -0
package/inspect.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { loadProjectRootEnv } from "./lib/load-project-root-env.js";
|
|
5
|
+
loadProjectRootEnv(import.meta.url);
|
|
6
|
+
const isSourceEntrypoint = import.meta.url.endsWith("/inspect.ts");
|
|
7
|
+
const cliPath = fileURLToPath(
|
|
8
|
+
new URL(isSourceEntrypoint ? "./cli.ts" : "./cli.js", import.meta.url)
|
|
9
|
+
);
|
|
10
|
+
const host = process.env["MCP_HOST"] ?? "127.0.0.1";
|
|
11
|
+
const port = process.env["MCP_PORT"] ?? process.env["PORT"] ?? "3000";
|
|
12
|
+
const path = process.env["MCP_PATH"] ?? "/mcp";
|
|
13
|
+
const serverUrl = `http://${host}:${port}${path}`;
|
|
14
|
+
let serverProcess;
|
|
15
|
+
let inspectorProcess;
|
|
16
|
+
async function main() {
|
|
17
|
+
serverProcess = spawn(process.execPath, getServerArgs(), {
|
|
18
|
+
stdio: "inherit",
|
|
19
|
+
env: process.env
|
|
20
|
+
});
|
|
21
|
+
serverProcess.once("exit", (code, signal) => {
|
|
22
|
+
if (!inspectorProcess || inspectorProcess.exitCode !== null) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
console.error(
|
|
26
|
+
`Reactionary MCP server exited before inspector closed: ${formatExit(
|
|
27
|
+
code,
|
|
28
|
+
signal
|
|
29
|
+
)}`
|
|
30
|
+
);
|
|
31
|
+
inspectorProcess.kill("SIGTERM");
|
|
32
|
+
});
|
|
33
|
+
await waitForServerStartup(serverProcess, serverUrl);
|
|
34
|
+
inspectorProcess = spawn(getPnpmCommand(), [
|
|
35
|
+
"dlx",
|
|
36
|
+
"@modelcontextprotocol/inspector",
|
|
37
|
+
"--web",
|
|
38
|
+
"--transport",
|
|
39
|
+
"http",
|
|
40
|
+
"--server-url",
|
|
41
|
+
serverUrl
|
|
42
|
+
], {
|
|
43
|
+
stdio: "inherit",
|
|
44
|
+
env: process.env
|
|
45
|
+
});
|
|
46
|
+
const exitCode = await waitForExit(inspectorProcess);
|
|
47
|
+
await stopServer();
|
|
48
|
+
process.exit(exitCode);
|
|
49
|
+
}
|
|
50
|
+
function getServerArgs() {
|
|
51
|
+
if (!isSourceEntrypoint) {
|
|
52
|
+
return [cliPath];
|
|
53
|
+
}
|
|
54
|
+
return ["--loader", "@swc-node/register/esm", cliPath];
|
|
55
|
+
}
|
|
56
|
+
function getPnpmCommand() {
|
|
57
|
+
return process.platform === "win32" ? "pnpm.cmd" : "pnpm";
|
|
58
|
+
}
|
|
59
|
+
async function waitForServerStartup(child, url) {
|
|
60
|
+
const timeoutMs = process.env["MCP_INSPECTOR_STARTUP_TIMEOUT_MS"] ? Number.parseInt(process.env["MCP_INSPECTOR_STARTUP_TIMEOUT_MS"], 10) : 1e4;
|
|
61
|
+
if (!Number.isInteger(timeoutMs) || timeoutMs < 1) {
|
|
62
|
+
throw new Error(
|
|
63
|
+
`Invalid MCP_INSPECTOR_STARTUP_TIMEOUT_MS: ${process.env["MCP_INSPECTOR_STARTUP_TIMEOUT_MS"]}`
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
await Promise.race([
|
|
67
|
+
waitForReadyResponse(url, timeoutMs),
|
|
68
|
+
waitForEarlyExit(child)
|
|
69
|
+
]);
|
|
70
|
+
}
|
|
71
|
+
async function waitForReadyResponse(url, timeoutMs) {
|
|
72
|
+
const deadline = Date.now() + timeoutMs;
|
|
73
|
+
let lastError;
|
|
74
|
+
while (Date.now() < deadline) {
|
|
75
|
+
try {
|
|
76
|
+
const response = await fetch(url, {
|
|
77
|
+
method: "POST",
|
|
78
|
+
headers: {
|
|
79
|
+
accept: "application/json, text/event-stream",
|
|
80
|
+
"content-type": "application/json"
|
|
81
|
+
},
|
|
82
|
+
body: JSON.stringify({
|
|
83
|
+
jsonrpc: "2.0",
|
|
84
|
+
id: 0,
|
|
85
|
+
method: "initialize",
|
|
86
|
+
params: {
|
|
87
|
+
protocolVersion: "2025-06-18",
|
|
88
|
+
capabilities: {},
|
|
89
|
+
clientInfo: {
|
|
90
|
+
name: "@reactionary/mcp inspector launcher",
|
|
91
|
+
version: "0.0.0"
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
})
|
|
95
|
+
});
|
|
96
|
+
if (response.ok) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
lastError = new Error(
|
|
100
|
+
`Readiness request failed with HTTP ${response.status}`
|
|
101
|
+
);
|
|
102
|
+
} catch (error) {
|
|
103
|
+
lastError = error;
|
|
104
|
+
}
|
|
105
|
+
await delay(100);
|
|
106
|
+
}
|
|
107
|
+
throw new Error(
|
|
108
|
+
`Timed out waiting for Reactionary MCP server at ${url}: ${formatError(lastError)}`
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
function waitForEarlyExit(child) {
|
|
112
|
+
return new Promise((_, reject) => {
|
|
113
|
+
child.once("error", reject);
|
|
114
|
+
child.once("exit", (code, signal) => {
|
|
115
|
+
reject(
|
|
116
|
+
new Error(
|
|
117
|
+
`Reactionary MCP server exited before it was ready: ${formatExit(
|
|
118
|
+
code,
|
|
119
|
+
signal
|
|
120
|
+
)}`
|
|
121
|
+
)
|
|
122
|
+
);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
function delay(delayMs) {
|
|
127
|
+
return new Promise((resolve) => {
|
|
128
|
+
setTimeout(resolve, delayMs);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
function waitForExit(child) {
|
|
132
|
+
return new Promise((resolve) => {
|
|
133
|
+
child.once("exit", (code) => {
|
|
134
|
+
resolve(code ?? 0);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
async function stopServer() {
|
|
139
|
+
if (!serverProcess || serverProcess.exitCode !== null) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
serverProcess.kill("SIGTERM");
|
|
143
|
+
await waitForExit(serverProcess);
|
|
144
|
+
}
|
|
145
|
+
function formatExit(code, signal) {
|
|
146
|
+
if (signal) {
|
|
147
|
+
return `signal ${signal}`;
|
|
148
|
+
}
|
|
149
|
+
return `code ${code ?? 0}`;
|
|
150
|
+
}
|
|
151
|
+
function formatError(error) {
|
|
152
|
+
return error instanceof Error ? error.message : String(error);
|
|
153
|
+
}
|
|
154
|
+
main().catch(async (error) => {
|
|
155
|
+
console.error(error);
|
|
156
|
+
await stopServer();
|
|
157
|
+
process.exit(1);
|
|
158
|
+
});
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ClientBuilder,
|
|
3
|
+
NoOpCache,
|
|
4
|
+
createInitialRequestContext
|
|
5
|
+
} from "@reactionary/core";
|
|
6
|
+
import { withAlgoliaCapabilities } from "@reactionary/algolia";
|
|
7
|
+
import {
|
|
8
|
+
withCommercetoolsCapabilities
|
|
9
|
+
} from "@reactionary/commercetools";
|
|
10
|
+
import { withFakeCapabilities } from "@reactionary/fake";
|
|
11
|
+
import { withMagentoCapabilities } from "@reactionary/magento";
|
|
12
|
+
import { withMedusaCapabilities } from "@reactionary/medusa";
|
|
13
|
+
import {
|
|
14
|
+
withMeilisearchCapabilities
|
|
15
|
+
} from "@reactionary/meilisearch";
|
|
16
|
+
import { withUnomiCapabilities } from "@reactionary/unomi";
|
|
17
|
+
const providerSystems = [
|
|
18
|
+
"FAKE",
|
|
19
|
+
"MAGENTO",
|
|
20
|
+
"MEDUSA",
|
|
21
|
+
"COMMERCETOOLS",
|
|
22
|
+
"ALGOLIA",
|
|
23
|
+
"MEILISEARCH",
|
|
24
|
+
"UNOMI"
|
|
25
|
+
];
|
|
26
|
+
function createReactionaryClientFromEnv(options = {}) {
|
|
27
|
+
const env = options.env ?? process.env;
|
|
28
|
+
const enabledSystems = getEnabledReactionaryMCPProviderSystems(env);
|
|
29
|
+
if (enabledSystems.length === 0) {
|
|
30
|
+
throw new Error(getNoEnabledProviderSystemsMessage());
|
|
31
|
+
}
|
|
32
|
+
const context = {
|
|
33
|
+
...createInitialRequestContext(),
|
|
34
|
+
...options.contextOverrides
|
|
35
|
+
};
|
|
36
|
+
let builder = new ClientBuilder(context).withCache(
|
|
37
|
+
options.cache ?? new NoOpCache()
|
|
38
|
+
);
|
|
39
|
+
for (const system of enabledSystems) {
|
|
40
|
+
switch (system) {
|
|
41
|
+
case "ALGOLIA":
|
|
42
|
+
builder = builder.withCapability(
|
|
43
|
+
withAlgoliaCapabilities(getAlgoliaConfiguration(env), {
|
|
44
|
+
productSearch: { enabled: true },
|
|
45
|
+
productRecommendations: { enabled: true }
|
|
46
|
+
})
|
|
47
|
+
);
|
|
48
|
+
break;
|
|
49
|
+
case "COMMERCETOOLS":
|
|
50
|
+
builder = builder.withCapability(
|
|
51
|
+
withCommercetoolsCapabilities(getCommercetoolsConfiguration(env), {
|
|
52
|
+
cart: { enabled: true },
|
|
53
|
+
product: { enabled: true },
|
|
54
|
+
category: { enabled: true },
|
|
55
|
+
checkout: { enabled: true },
|
|
56
|
+
identity: { enabled: true },
|
|
57
|
+
inventory: { enabled: true },
|
|
58
|
+
order: { enabled: true },
|
|
59
|
+
price: { enabled: true },
|
|
60
|
+
productSearch: { enabled: true },
|
|
61
|
+
productAssociations: { enabled: true },
|
|
62
|
+
productReviews: { enabled: true },
|
|
63
|
+
productList: { enabled: true },
|
|
64
|
+
orderSearch: { enabled: true },
|
|
65
|
+
companyRegistration: { enabled: true },
|
|
66
|
+
company: { enabled: true },
|
|
67
|
+
employee: { enabled: true },
|
|
68
|
+
employeeInvitation: { enabled: true },
|
|
69
|
+
store: { enabled: true },
|
|
70
|
+
profile: { enabled: true },
|
|
71
|
+
personalizationProfile: { enabled: true }
|
|
72
|
+
})
|
|
73
|
+
);
|
|
74
|
+
break;
|
|
75
|
+
case "FAKE":
|
|
76
|
+
builder = builder.withCapability(
|
|
77
|
+
withFakeCapabilities(getFakeConfiguration(), {
|
|
78
|
+
price: { enabled: true },
|
|
79
|
+
inventory: { enabled: true },
|
|
80
|
+
product: { enabled: true },
|
|
81
|
+
productReviews: { enabled: true },
|
|
82
|
+
productAssociations: { enabled: true },
|
|
83
|
+
featureFlag: { enabled: true }
|
|
84
|
+
})
|
|
85
|
+
);
|
|
86
|
+
break;
|
|
87
|
+
case "MAGENTO":
|
|
88
|
+
builder = builder.withCapability(
|
|
89
|
+
withMagentoCapabilities(getMagentoConfiguration(env), {
|
|
90
|
+
cart: { enabled: true },
|
|
91
|
+
product: { enabled: true },
|
|
92
|
+
category: { enabled: true },
|
|
93
|
+
identity: { enabled: true },
|
|
94
|
+
inventory: { enabled: true },
|
|
95
|
+
price: { enabled: true },
|
|
96
|
+
productSearch: { enabled: true },
|
|
97
|
+
profile: { enabled: true },
|
|
98
|
+
orderSearch: { enabled: true },
|
|
99
|
+
checkout: { enabled: true },
|
|
100
|
+
productAssociations: { enabled: true },
|
|
101
|
+
productRecommendations: { enabled: true },
|
|
102
|
+
productReviews: { enabled: true }
|
|
103
|
+
})
|
|
104
|
+
);
|
|
105
|
+
break;
|
|
106
|
+
case "MEDUSA":
|
|
107
|
+
builder = builder.withCapability(
|
|
108
|
+
withMedusaCapabilities(getMedusaConfiguration(env), {
|
|
109
|
+
cart: { enabled: true },
|
|
110
|
+
product: { enabled: true },
|
|
111
|
+
category: { enabled: true },
|
|
112
|
+
checkout: { enabled: true },
|
|
113
|
+
identity: { enabled: true },
|
|
114
|
+
inventory: { enabled: true },
|
|
115
|
+
order: { enabled: true },
|
|
116
|
+
price: { enabled: true },
|
|
117
|
+
productSearch: { enabled: true },
|
|
118
|
+
productRecommendations: { enabled: true },
|
|
119
|
+
productAssociations: { enabled: true },
|
|
120
|
+
orderSearch: { enabled: true },
|
|
121
|
+
store: { enabled: true },
|
|
122
|
+
profile: { enabled: true },
|
|
123
|
+
personalizationProfile: { enabled: true },
|
|
124
|
+
employee: { enabled: true },
|
|
125
|
+
employeeInvitation: { enabled: true },
|
|
126
|
+
company: { enabled: true },
|
|
127
|
+
companyRegistration: { enabled: true }
|
|
128
|
+
})
|
|
129
|
+
);
|
|
130
|
+
break;
|
|
131
|
+
case "MEILISEARCH":
|
|
132
|
+
builder = builder.withCapability(
|
|
133
|
+
withMeilisearchCapabilities(getMeilisearchConfiguration(env), {
|
|
134
|
+
productSearch: { enabled: true },
|
|
135
|
+
orderSearch: { enabled: true },
|
|
136
|
+
productRecommendations: { enabled: true }
|
|
137
|
+
})
|
|
138
|
+
);
|
|
139
|
+
break;
|
|
140
|
+
case "UNOMI":
|
|
141
|
+
builder = builder.withCapability(
|
|
142
|
+
withUnomiCapabilities(getUnomiConfiguration(env), {
|
|
143
|
+
personalizationProfile: { enabled: true },
|
|
144
|
+
analytics: { enabled: false }
|
|
145
|
+
})
|
|
146
|
+
);
|
|
147
|
+
break;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return {
|
|
151
|
+
client: builder.build(),
|
|
152
|
+
enabledSystems
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
function getEnabledReactionaryMCPProviderSystems(env = process.env) {
|
|
156
|
+
return providerSystems.filter(
|
|
157
|
+
(system) => isEnvEnabled(env[`ENABLED_${system}`])
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
function getNoEnabledProviderSystemsMessage() {
|
|
161
|
+
return `No Reactionary provider system is enabled. Set one of ${providerSystems.map((system) => `ENABLED_${system}=true`).join(", ")}.`;
|
|
162
|
+
}
|
|
163
|
+
function isEnvEnabled(value) {
|
|
164
|
+
return value?.toLowerCase() === "true";
|
|
165
|
+
}
|
|
166
|
+
function getAlgoliaConfiguration(env) {
|
|
167
|
+
return {
|
|
168
|
+
apiKey: env["ALGOLIA_API_KEY"] || "",
|
|
169
|
+
appId: env["ALGOLIA_APP_ID"] || "",
|
|
170
|
+
indexName: env["ALGOLIA_INDEX"] || "",
|
|
171
|
+
useRecommendationsForBots: env["ALGOLIA_USE_RECOMMENDATIONS_FOR_BOTS"] === "true",
|
|
172
|
+
useBaseIndexNameForEnglishLocale: env["ALGOLIA_USE_BASE_INDEX_NAME_FOR_ENGLISH_LOCALE"] === "true"
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function getMeilisearchConfiguration(env) {
|
|
176
|
+
return {
|
|
177
|
+
apiKey: env["MEILISEARCH_API_KEY"] || "",
|
|
178
|
+
apiUrl: env["MEILISEARCH_API_URL"] || "",
|
|
179
|
+
indexName: env["MEILISEARCH_INDEX"] || "",
|
|
180
|
+
useAIEmbedding: env["MEILISEARCH_USE_AI_EMBEDDING"] || void 0,
|
|
181
|
+
semanticRatio: env["MEILISEARCH_SEMANTIC_RATIO"] ? parseFloat(env["MEILISEARCH_SEMANTIC_RATIO"]) : 0.5,
|
|
182
|
+
orderIndexName: env["MEILISEARCH_ORDER_INDEX"] || "order",
|
|
183
|
+
useRecommendationsForBots: env["MEILISEARCH_USE_RECOMMENDATIONS_FOR_BOTS"] === "true"
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
function getUnomiConfiguration(env) {
|
|
187
|
+
return {
|
|
188
|
+
apiUrl: env["UNOMI_API_URL"] || "",
|
|
189
|
+
username: env["UNOMI_USERNAME"] || "",
|
|
190
|
+
password: env["UNOMI_PASSWORD"] || "",
|
|
191
|
+
scope: env["UNOMI_SCOPE"] || "",
|
|
192
|
+
profilePath: env["UNOMI_PROFILE_PATH"] || "/cxs/profiles"
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
function getMedusaConfiguration(env) {
|
|
196
|
+
return {
|
|
197
|
+
publishable_key: env["MEDUSA_PUBLISHABLE_KEY"] || "",
|
|
198
|
+
adminApiKey: env["MEDUSA_ADMIN_KEY"] || "",
|
|
199
|
+
apiUrl: env["MEDUSA_API_URL"] || "",
|
|
200
|
+
defaultCurrency: env["MEDUSA_DEFAULT_CURRENCY"] || "",
|
|
201
|
+
allCurrencies: []
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
function getMagentoConfiguration(env) {
|
|
205
|
+
return {
|
|
206
|
+
adminApiKey: env["MAGENTO_ADMIN_API_KEY"] || "",
|
|
207
|
+
baseUrl: env["MAGENTO_BASE_URL"] || "",
|
|
208
|
+
storeCode: env["MAGENTO_STORE_CODE"] || "",
|
|
209
|
+
authStoreCode: env["MAGENTO_AUTH_STORE_CODE"] || "default",
|
|
210
|
+
mediaUrl: env["MAGENTO_MEDIA_URL"] || void 0,
|
|
211
|
+
mediaSource: env["MAGENTO_MEDIA_SOURCE"] === "EXTERNAL" ? "EXTERNAL" : "DEFAULT",
|
|
212
|
+
defaultCurrency: env["MAGENTO_DEFAULT_CURRENCY"] || "",
|
|
213
|
+
rootCategoryId: env["MAGENTO_ROOT_CATEGORY_ID"] || "2",
|
|
214
|
+
graphqlUrl: env["MAGENTO_GRAPHQL_URL"] || void 0,
|
|
215
|
+
reviewRatingCode: env["MAGENTO_REVIEW_RATING_CODE"] || void 0,
|
|
216
|
+
allCurrencies: []
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
function getFakeConfiguration() {
|
|
220
|
+
return {
|
|
221
|
+
jitter: {
|
|
222
|
+
mean: 0,
|
|
223
|
+
deviation: 0
|
|
224
|
+
},
|
|
225
|
+
seeds: {
|
|
226
|
+
product: 1,
|
|
227
|
+
search: 1,
|
|
228
|
+
category: 1
|
|
229
|
+
},
|
|
230
|
+
featureFlags: {
|
|
231
|
+
flags: [
|
|
232
|
+
{
|
|
233
|
+
key: "true-flag",
|
|
234
|
+
type: "boolean",
|
|
235
|
+
enabled: true
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
key: "string-flag",
|
|
239
|
+
type: "multivariate",
|
|
240
|
+
variants: ["red", "green", "blue"],
|
|
241
|
+
enabledVariant: "blue"
|
|
242
|
+
}
|
|
243
|
+
]
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
function getCommercetoolsConfiguration(env) {
|
|
248
|
+
return {
|
|
249
|
+
apiUrl: env["CTP_API_URL"] || "",
|
|
250
|
+
authUrl: env["CTP_AUTH_URL"] || "",
|
|
251
|
+
clientId: env["CTP_CLIENT_ID"] || "",
|
|
252
|
+
clientSecret: env["CTP_CLIENT_SECRET"] || "",
|
|
253
|
+
projectKey: env["CTP_PROJECT_KEY"] || "",
|
|
254
|
+
scopes: parseCommaSeparatedEnvList(env["CTP_SCOPES"]),
|
|
255
|
+
paymentMethods: [
|
|
256
|
+
{
|
|
257
|
+
identifier: {
|
|
258
|
+
method: "stripe",
|
|
259
|
+
name: "Stripe",
|
|
260
|
+
paymentProcessor: "stripe"
|
|
261
|
+
},
|
|
262
|
+
isPunchOut: false,
|
|
263
|
+
description: "Stripe payment gateway"
|
|
264
|
+
}
|
|
265
|
+
],
|
|
266
|
+
facetFieldsForSearch: parseCommaSeparatedEnvList(
|
|
267
|
+
env["CTP_FACET_FIELDS_FOR_SEARCH"]
|
|
268
|
+
),
|
|
269
|
+
adminClientId: env["CTP_ADMIN_CLIENT_ID"] || void 0,
|
|
270
|
+
adminClientSecret: env["CTP_ADMIN_CLIENT_SECRET"] || void 0,
|
|
271
|
+
listPriceChannelKey: env["CTP_LIST_PRICE_CHANNEL_KEY"] || void 0,
|
|
272
|
+
customerPriceChannelKey: env["CTP_CUSTOMER_PRICE_CHANNEL_KEY"] || void 0
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
function parseCommaSeparatedEnvList(value) {
|
|
276
|
+
return (value || "").split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
277
|
+
}
|
|
278
|
+
export {
|
|
279
|
+
createReactionaryClientFromEnv,
|
|
280
|
+
getEnabledReactionaryMCPProviderSystems,
|
|
281
|
+
getNoEnabledProviderSystemsMessage,
|
|
282
|
+
parseCommaSeparatedEnvList
|
|
283
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { config } from "dotenv";
|
|
5
|
+
const workspaceRootMarkers = ["nx.json", "pnpm-workspace.yaml"];
|
|
6
|
+
function loadProjectRootEnv(from = import.meta.url, env = process.env) {
|
|
7
|
+
const envPath = env["DOTENV_CONFIG_PATH"] ?? resolveProjectRootEnvPath(from);
|
|
8
|
+
if (!envPath) {
|
|
9
|
+
config();
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const result = config({ path: envPath });
|
|
13
|
+
if (!result.error) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const errorCode = result.error.code;
|
|
17
|
+
if (errorCode === "ENOENT" && env["DOTENV_CONFIG_PATH"] === void 0) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
throw result.error;
|
|
21
|
+
}
|
|
22
|
+
function resolveProjectRootEnvPath(from = import.meta.url) {
|
|
23
|
+
let currentDirectory = dirname(fileURLToPath(from));
|
|
24
|
+
while (true) {
|
|
25
|
+
if (workspaceRootMarkers.some(
|
|
26
|
+
(marker) => existsSync(join(currentDirectory, marker))
|
|
27
|
+
)) {
|
|
28
|
+
return join(currentDirectory, ".env");
|
|
29
|
+
}
|
|
30
|
+
const parentDirectory = dirname(currentDirectory);
|
|
31
|
+
if (parentDirectory === currentDirectory) {
|
|
32
|
+
return void 0;
|
|
33
|
+
}
|
|
34
|
+
currentDirectory = parentDirectory;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
export {
|
|
38
|
+
loadProjectRootEnv,
|
|
39
|
+
resolveProjectRootEnvPath
|
|
40
|
+
};
|