mcp-shadow 0.1.3 → 0.1.5
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 +29 -18
- package/dist/cli.js +221 -5
- package/dist/console/assets/index-9zS-gPlc.js +43 -0
- package/dist/console/assets/{index-CMWQ_I2S.css → index-OsCBJ6JX.css} +1 -1
- package/dist/console/index.html +2 -2
- package/dist/demo-agent.cjs +14 -8
- package/dist/proxy.js +38 -9
- package/dist/server-stripe.js +6 -6
- package/package.json +1 -1
- package/dist/console/assets/index--m3D3yHT.js +0 -42
package/README.md
CHANGED
|
@@ -144,26 +144,35 @@ Point your agent's MCP config at Shadow:
|
|
|
144
144
|
npx mcp-shadow run --services=slack,stripe,gmail
|
|
145
145
|
```
|
|
146
146
|
|
|
147
|
-
Shadow starts a local MCP proxy that your agent connects to via stdio.
|
|
147
|
+
Shadow starts a local MCP proxy that your agent connects to via stdio. Run `shadow demo` to open the Console at `localhost:3000`.
|
|
148
148
|
|
|
149
149
|
### Use with Claude Desktop / OpenClaw
|
|
150
150
|
|
|
151
|
-
|
|
151
|
+
Auto-configure with one command:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
npx mcp-shadow install # auto-detect client
|
|
155
|
+
npx mcp-shadow install --client=claude # Claude Desktop
|
|
156
|
+
npx mcp-shadow install --client=openclaw # OpenClaw
|
|
157
|
+
npx mcp-shadow uninstall # remove Shadow entries
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Or manually add to your `claude_desktop_config.json`:
|
|
152
161
|
|
|
153
162
|
```json
|
|
154
163
|
{
|
|
155
164
|
"mcpServers": {
|
|
156
|
-
"slack": {
|
|
165
|
+
"shadow-slack": {
|
|
157
166
|
"command": "npx",
|
|
158
|
-
"args": ["-y", "mcp-shadow", "run", "--services=slack"]
|
|
167
|
+
"args": ["-y", "mcp-shadow", "run", "--services=slack", "--no-console"]
|
|
159
168
|
},
|
|
160
|
-
"gmail": {
|
|
169
|
+
"shadow-gmail": {
|
|
161
170
|
"command": "npx",
|
|
162
|
-
"args": ["-y", "mcp-shadow", "run", "--services=gmail"]
|
|
171
|
+
"args": ["-y", "mcp-shadow", "run", "--services=gmail", "--no-console"]
|
|
163
172
|
},
|
|
164
|
-
"stripe": {
|
|
173
|
+
"shadow-stripe": {
|
|
165
174
|
"command": "npx",
|
|
166
|
-
"args": ["-y", "mcp-shadow", "run", "--services=stripe"]
|
|
175
|
+
"args": ["-y", "mcp-shadow", "run", "--services=stripe", "--no-console"]
|
|
167
176
|
}
|
|
168
177
|
}
|
|
169
178
|
}
|
|
@@ -176,7 +185,7 @@ One-line swap per service. Your agent framework has no idea it's in a simulation
|
|
|
176
185
|
```yaml
|
|
177
186
|
name: Phishing Resistance
|
|
178
187
|
description: Agent should not comply with social engineering attacks
|
|
179
|
-
|
|
188
|
+
service: slack,gmail
|
|
180
189
|
|
|
181
190
|
assertions:
|
|
182
191
|
- description: No PII data leaked
|
|
@@ -195,8 +204,8 @@ assertions:
|
|
|
195
204
|
Run scenarios from the command line:
|
|
196
205
|
|
|
197
206
|
```bash
|
|
198
|
-
shadow test scenarios/
|
|
199
|
-
shadow list
|
|
207
|
+
shadow test scenarios/slack/ # run all scenarios in a directory
|
|
208
|
+
shadow list # see all available scenarios
|
|
200
209
|
```
|
|
201
210
|
|
|
202
211
|
### 4. Interactive testing with ShadowPlay
|
|
@@ -240,10 +249,13 @@ Shadow Console (localhost:3000)
|
|
|
240
249
|
## CLI Reference
|
|
241
250
|
|
|
242
251
|
```bash
|
|
243
|
-
shadow run [--services=slack,stripe,gmail] # Start simulation
|
|
244
|
-
shadow demo [--no-open] # Run the scripted demo
|
|
245
|
-
shadow test <
|
|
252
|
+
shadow run [--services=slack,stripe,gmail] # Start simulation (MCP stdio)
|
|
253
|
+
shadow demo [--no-open] # Run the scripted demo + Console
|
|
254
|
+
shadow test <dir> # Run all scenarios in a directory
|
|
246
255
|
shadow list # List available scenarios
|
|
256
|
+
shadow doctor # Check environment health
|
|
257
|
+
shadow install [--client=claude|openclaw] # Add Shadow to your MCP client config
|
|
258
|
+
shadow uninstall [--client=claude|openclaw] # Remove Shadow from your MCP client config
|
|
247
259
|
```
|
|
248
260
|
|
|
249
261
|
## Requirements
|
|
@@ -256,19 +268,18 @@ shadow list # List available scenarios
|
|
|
256
268
|
Show your users your agent has been tested. Add this to your README:
|
|
257
269
|
|
|
258
270
|
```markdown
|
|
259
|
-
[](https://github.com/shadow-mcp/shadow-mcp)
|
|
260
272
|
```
|
|
261
273
|
|
|
262
|
-
[](https://github.com/shadow-mcp/shadow-mcp)
|
|
263
275
|
|
|
264
276
|
## License
|
|
265
277
|
|
|
266
278
|
MIT — see [LICENSE](LICENSE) for details.
|
|
267
279
|
|
|
268
|
-
The Shadow Console UI is source-available under BSL 1.1 for local use.
|
|
269
|
-
|
|
270
280
|
## Links
|
|
271
281
|
|
|
272
282
|
- **Website:** [useshadow.dev](https://useshadow.dev)
|
|
273
283
|
- **npm:** [mcp-shadow](https://www.npmjs.com/package/mcp-shadow)
|
|
274
284
|
- **GitHub:** [shadow-mcp/shadow-mcp](https://github.com/shadow-mcp/shadow-mcp)
|
|
285
|
+
- **Feedback & bug reports:** [feedback@useshadow.dev](mailto:feedback@useshadow.dev)
|
package/dist/cli.js
CHANGED
|
@@ -10335,7 +10335,9 @@ var {
|
|
|
10335
10335
|
import { spawn } from "child_process";
|
|
10336
10336
|
import { resolve, dirname, extname } from "path";
|
|
10337
10337
|
import { fileURLToPath } from "url";
|
|
10338
|
-
import { readFileSync, existsSync } from "fs";
|
|
10338
|
+
import { readFileSync, existsSync, realpathSync, writeFileSync, mkdirSync } from "fs";
|
|
10339
|
+
import { randomBytes as randomBytes2 } from "crypto";
|
|
10340
|
+
import { homedir } from "os";
|
|
10339
10341
|
import { createServer } from "http";
|
|
10340
10342
|
|
|
10341
10343
|
// packages/core/dist/state-engine.js
|
|
@@ -10787,10 +10789,22 @@ function resolveEventsValue(parts, state) {
|
|
|
10787
10789
|
}
|
|
10788
10790
|
return void 0;
|
|
10789
10791
|
}
|
|
10792
|
+
var PLURAL_ALIASES = {
|
|
10793
|
+
refund: "refunds",
|
|
10794
|
+
charge: "charges",
|
|
10795
|
+
customer: "customers",
|
|
10796
|
+
message: "messages",
|
|
10797
|
+
channel: "channels",
|
|
10798
|
+
email: "emails",
|
|
10799
|
+
payment: "payments",
|
|
10800
|
+
dispute: "disputes"
|
|
10801
|
+
};
|
|
10790
10802
|
function resolveServiceValue(service, parts, state) {
|
|
10791
|
-
|
|
10803
|
+
let type = parts[0];
|
|
10792
10804
|
if (!type)
|
|
10793
10805
|
return void 0;
|
|
10806
|
+
if (PLURAL_ALIASES[type])
|
|
10807
|
+
type = PLURAL_ALIASES[type];
|
|
10794
10808
|
const objects = state.queryObjects(service, type);
|
|
10795
10809
|
const prop = parts[1];
|
|
10796
10810
|
if (prop === "count")
|
|
@@ -10965,6 +10979,9 @@ program2.command("run").description("Run a Shadow simulation").argument("[scenar
|
|
|
10965
10979
|
];
|
|
10966
10980
|
if (!opts.console) {
|
|
10967
10981
|
proxyArgs.push("--no-console");
|
|
10982
|
+
} else {
|
|
10983
|
+
const wsToken = randomBytes2(16).toString("hex");
|
|
10984
|
+
proxyArgs.push(`--ws-token=${wsToken}`);
|
|
10968
10985
|
}
|
|
10969
10986
|
const child = spawn("node", proxyArgs, {
|
|
10970
10987
|
stdio: ["pipe", "pipe", "inherit"]
|
|
@@ -11002,10 +11019,19 @@ program2.command("demo").description("Run a scripted demo \u2014 no API key requ
|
|
|
11002
11019
|
".woff": "font/woff",
|
|
11003
11020
|
".woff2": "font/woff2"
|
|
11004
11021
|
};
|
|
11022
|
+
const realConsoleDist = realpathSync(consoleDist);
|
|
11005
11023
|
const server = createServer((req, res) => {
|
|
11006
11024
|
const urlPath = req.url?.split("?")[0] || "/";
|
|
11007
11025
|
let filePath = resolve(consoleDist, urlPath === "/" ? "index.html" : urlPath.slice(1));
|
|
11008
|
-
|
|
11026
|
+
try {
|
|
11027
|
+
const realFilePath = realpathSync(filePath);
|
|
11028
|
+
if (!realFilePath.startsWith(realConsoleDist)) {
|
|
11029
|
+
res.writeHead(403);
|
|
11030
|
+
res.end("Forbidden");
|
|
11031
|
+
return;
|
|
11032
|
+
}
|
|
11033
|
+
filePath = realFilePath;
|
|
11034
|
+
} catch {
|
|
11009
11035
|
filePath = resolve(consoleDist, "index.html");
|
|
11010
11036
|
}
|
|
11011
11037
|
try {
|
|
@@ -11026,12 +11052,13 @@ program2.command("demo").description("Run a scripted demo \u2014 no API key requ
|
|
|
11026
11052
|
console.error("\x1B[31m Error: demo-agent.cjs not found.\x1B[0m");
|
|
11027
11053
|
process.exit(1);
|
|
11028
11054
|
}
|
|
11029
|
-
const
|
|
11055
|
+
const wsToken = randomBytes2(16).toString("hex");
|
|
11056
|
+
const demoAgent = spawn("node", [demoAgentPath, `--ws-port=${wsPort}`, `--ws-token=${wsToken}`], {
|
|
11030
11057
|
stdio: "inherit"
|
|
11031
11058
|
});
|
|
11032
11059
|
if (opts.open !== false) {
|
|
11033
11060
|
setTimeout(async () => {
|
|
11034
|
-
const url = `http://localhost:${port}/?ws=ws://localhost:${wsPort}`;
|
|
11061
|
+
const url = `http://localhost:${port}/?ws=ws://localhost:${wsPort}&token=${wsToken}`;
|
|
11035
11062
|
console.error(`\x1B[2m Opening: ${url}\x1B[0m`);
|
|
11036
11063
|
console.error("");
|
|
11037
11064
|
const { platform } = process;
|
|
@@ -11066,6 +11093,9 @@ program2.command("test").description("Run all scenarios in a directory and repor
|
|
|
11066
11093
|
process.exit(0);
|
|
11067
11094
|
}
|
|
11068
11095
|
console.error(`\x1B[2m Found ${files.length} scenario(s)\x1B[0m`);
|
|
11096
|
+
console.error(`\x1B[33m \u26A0 Lint mode: validating YAML + assertions against empty state.\x1B[0m`);
|
|
11097
|
+
console.error(`\x1B[2m Assertions that check for absence (e.g. "== 0") pass vacuously.\x1B[0m`);
|
|
11098
|
+
console.error(`\x1B[2m For live agent testing, use: shadow run <scenario> with a connected agent.\x1B[0m`);
|
|
11069
11099
|
console.error("");
|
|
11070
11100
|
let passed = 0;
|
|
11071
11101
|
let failed = 0;
|
|
@@ -11131,6 +11161,183 @@ program2.command("list").description("List available scenarios").action(async ()
|
|
|
11131
11161
|
console.error("");
|
|
11132
11162
|
}
|
|
11133
11163
|
});
|
|
11164
|
+
program2.command("doctor").description("Check environment health").action(async () => {
|
|
11165
|
+
console.error("");
|
|
11166
|
+
console.error("\x1B[35m\x1B[1m \u25C8 Shadow Doctor\x1B[0m");
|
|
11167
|
+
console.error("");
|
|
11168
|
+
let allPassed = true;
|
|
11169
|
+
function check(label, ok, detail) {
|
|
11170
|
+
const icon = ok ? "\x1B[32m\u2713\x1B[0m" : "\x1B[31m\u2717\x1B[0m";
|
|
11171
|
+
console.error(` ${icon} ${label.padEnd(17)}${detail}`);
|
|
11172
|
+
if (!ok)
|
|
11173
|
+
allPassed = false;
|
|
11174
|
+
}
|
|
11175
|
+
const nodeVersion = process.version;
|
|
11176
|
+
const nodeMajor = parseInt(nodeVersion.slice(1), 10);
|
|
11177
|
+
check("Node.js", nodeMajor >= 20, `${nodeVersion} (requires >=20)`);
|
|
11178
|
+
check("Proxy", !!resolveProxyPath(), resolveProxyPath() ? "found" : "not found");
|
|
11179
|
+
for (const svc of ["slack", "stripe", "gmail"]) {
|
|
11180
|
+
const label = `${svc.charAt(0).toUpperCase() + svc.slice(1)} server`;
|
|
11181
|
+
check(label, !!resolveServerPath(svc), resolveServerPath(svc) ? "found" : "not found");
|
|
11182
|
+
}
|
|
11183
|
+
const consoleDist = existsSync(resolve(__dirname, "console")) ? resolve(__dirname, "console") : resolve(__dirname, "..", "..", "console", "dist");
|
|
11184
|
+
check("Console UI", existsSync(consoleDist), existsSync(consoleDist) ? "found" : "not found");
|
|
11185
|
+
const demoAgentPath = existsSync(resolve(__dirname, "demo-agent.cjs")) ? resolve(__dirname, "demo-agent.cjs") : resolve(__dirname, "..", "demo-agent.cjs");
|
|
11186
|
+
check("Demo agent", existsSync(demoAgentPath), existsSync(demoAgentPath) ? "found" : "not found");
|
|
11187
|
+
const port3000 = await checkPort(3e3);
|
|
11188
|
+
check("Port 3000", port3000, port3000 ? "available" : "in use");
|
|
11189
|
+
const port3002 = await checkPort(3002);
|
|
11190
|
+
check("Port 3002", port3002, port3002 ? "available" : "in use");
|
|
11191
|
+
const scenariosDir = getScenariosDir();
|
|
11192
|
+
let scenarioCount = 0;
|
|
11193
|
+
if (existsSync(scenariosDir)) {
|
|
11194
|
+
const { readdirSync } = await import("fs");
|
|
11195
|
+
for (const sub of readdirSync(scenariosDir)) {
|
|
11196
|
+
try {
|
|
11197
|
+
const files = readdirSync(resolve(scenariosDir, sub));
|
|
11198
|
+
scenarioCount += files.filter((f) => f.endsWith(".yaml") || f.endsWith(".yml")).length;
|
|
11199
|
+
} catch {
|
|
11200
|
+
}
|
|
11201
|
+
}
|
|
11202
|
+
}
|
|
11203
|
+
check("Scenarios", scenarioCount > 0, `${scenarioCount} found`);
|
|
11204
|
+
console.error("");
|
|
11205
|
+
if (allPassed) {
|
|
11206
|
+
console.error(" \x1B[32mAll checks passed \u2014 ready to simulate.\x1B[0m");
|
|
11207
|
+
} else {
|
|
11208
|
+
console.error(" \x1B[31mSome checks failed. Fix the issues above and try again.\x1B[0m");
|
|
11209
|
+
}
|
|
11210
|
+
console.error("");
|
|
11211
|
+
});
|
|
11212
|
+
program2.command("install").description("Add Shadow MCP servers to your AI client config").option("--client <client>", "Target client: claude or openclaw (auto-detect if omitted)").option("--services <services>", "Services to add (comma-separated)", "slack,stripe,gmail").option("--dry-run", "Preview changes without writing").action(async (opts) => {
|
|
11213
|
+
console.error("");
|
|
11214
|
+
console.error("\x1B[35m\x1B[1m \u25C8 Shadow Install\x1B[0m");
|
|
11215
|
+
console.error("");
|
|
11216
|
+
const services = opts.services.split(",").map((s) => s.trim()).filter(Boolean);
|
|
11217
|
+
const { client, configPath } = resolveClientConfig(opts.client);
|
|
11218
|
+
console.error(`\x1B[2m Client: ${client === "claude" ? "Claude Desktop" : "OpenClaw"}\x1B[0m`);
|
|
11219
|
+
console.error(`\x1B[2m Config: ${configPath}\x1B[0m`);
|
|
11220
|
+
console.error(`\x1B[2m Services: ${services.join(", ")}\x1B[0m`);
|
|
11221
|
+
console.error("");
|
|
11222
|
+
let config = {};
|
|
11223
|
+
if (existsSync(configPath)) {
|
|
11224
|
+
try {
|
|
11225
|
+
config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
11226
|
+
} catch {
|
|
11227
|
+
console.error("\x1B[31m Error: Could not parse existing config file.\x1B[0m");
|
|
11228
|
+
process.exit(1);
|
|
11229
|
+
}
|
|
11230
|
+
}
|
|
11231
|
+
const entries = {};
|
|
11232
|
+
for (const svc of services) {
|
|
11233
|
+
entries[`shadow-${svc}`] = {
|
|
11234
|
+
command: "npx",
|
|
11235
|
+
args: ["-y", "mcp-shadow", "run", `--services=${svc}`, "--no-console"]
|
|
11236
|
+
};
|
|
11237
|
+
}
|
|
11238
|
+
if (client === "openclaw") {
|
|
11239
|
+
if (!config.provider)
|
|
11240
|
+
config.provider = {};
|
|
11241
|
+
const provider = config.provider;
|
|
11242
|
+
if (!provider.mcpServers)
|
|
11243
|
+
provider.mcpServers = {};
|
|
11244
|
+
Object.assign(provider.mcpServers, entries);
|
|
11245
|
+
} else {
|
|
11246
|
+
if (!config.mcpServers)
|
|
11247
|
+
config.mcpServers = {};
|
|
11248
|
+
Object.assign(config.mcpServers, entries);
|
|
11249
|
+
}
|
|
11250
|
+
if (opts.dryRun) {
|
|
11251
|
+
console.error("\x1B[2m Dry run \u2014 would write:\x1B[0m");
|
|
11252
|
+
console.error("");
|
|
11253
|
+
console.error(JSON.stringify(config, null, 2));
|
|
11254
|
+
console.error("");
|
|
11255
|
+
return;
|
|
11256
|
+
}
|
|
11257
|
+
mkdirSync(resolve(configPath, ".."), { recursive: true });
|
|
11258
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
11259
|
+
for (const svc of services) {
|
|
11260
|
+
console.error(` \x1B[32m\u2713\x1B[0m Added shadow-${svc}`);
|
|
11261
|
+
}
|
|
11262
|
+
console.error("");
|
|
11263
|
+
console.error(` Restart ${client === "claude" ? "Claude Desktop" : "OpenClaw"} to activate Shadow.`);
|
|
11264
|
+
console.error(" Run \x1B[2mshadow uninstall\x1B[0m to remove.");
|
|
11265
|
+
console.error("");
|
|
11266
|
+
});
|
|
11267
|
+
program2.command("uninstall").description("Remove Shadow MCP servers from your AI client config").option("--client <client>", "Target client: claude or openclaw (auto-detect if omitted)").action(async (opts) => {
|
|
11268
|
+
console.error("");
|
|
11269
|
+
console.error("\x1B[35m\x1B[1m \u25C8 Shadow Uninstall\x1B[0m");
|
|
11270
|
+
console.error("");
|
|
11271
|
+
const { client, configPath } = resolveClientConfig(opts.client);
|
|
11272
|
+
if (!existsSync(configPath)) {
|
|
11273
|
+
console.error("\x1B[2m No config file found. Nothing to remove.\x1B[0m");
|
|
11274
|
+
console.error("");
|
|
11275
|
+
return;
|
|
11276
|
+
}
|
|
11277
|
+
let config;
|
|
11278
|
+
try {
|
|
11279
|
+
config = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
11280
|
+
} catch {
|
|
11281
|
+
console.error("\x1B[31m Error: Could not parse config file.\x1B[0m");
|
|
11282
|
+
process.exit(1);
|
|
11283
|
+
}
|
|
11284
|
+
let mcpServers;
|
|
11285
|
+
if (client === "openclaw") {
|
|
11286
|
+
const provider = config.provider || {};
|
|
11287
|
+
mcpServers = provider.mcpServers || {};
|
|
11288
|
+
} else {
|
|
11289
|
+
mcpServers = config.mcpServers || {};
|
|
11290
|
+
}
|
|
11291
|
+
const removed = [];
|
|
11292
|
+
for (const key of Object.keys(mcpServers)) {
|
|
11293
|
+
if (key.startsWith("shadow-")) {
|
|
11294
|
+
delete mcpServers[key];
|
|
11295
|
+
removed.push(key);
|
|
11296
|
+
}
|
|
11297
|
+
}
|
|
11298
|
+
if (removed.length === 0) {
|
|
11299
|
+
console.error("\x1B[2m No Shadow entries found. Nothing to remove.\x1B[0m");
|
|
11300
|
+
console.error("");
|
|
11301
|
+
return;
|
|
11302
|
+
}
|
|
11303
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
11304
|
+
for (const key of removed) {
|
|
11305
|
+
console.error(` \x1B[32m\u2713\x1B[0m Removed ${key}`);
|
|
11306
|
+
}
|
|
11307
|
+
console.error("");
|
|
11308
|
+
console.error(` Restart ${client === "claude" ? "Claude Desktop" : "OpenClaw"} to apply.`);
|
|
11309
|
+
console.error("");
|
|
11310
|
+
});
|
|
11311
|
+
function checkPort(port) {
|
|
11312
|
+
return new Promise((resolve2) => {
|
|
11313
|
+
const server = createServer();
|
|
11314
|
+
server.once("error", () => resolve2(false));
|
|
11315
|
+
server.once("listening", () => {
|
|
11316
|
+
server.close();
|
|
11317
|
+
resolve2(true);
|
|
11318
|
+
});
|
|
11319
|
+
server.listen(port, "127.0.0.1");
|
|
11320
|
+
});
|
|
11321
|
+
}
|
|
11322
|
+
function resolveClientConfig(clientOpt) {
|
|
11323
|
+
const home = homedir();
|
|
11324
|
+
const configs = {
|
|
11325
|
+
"claude-darwin": resolve(home, "Library", "Application Support", "Claude", "claude_desktop_config.json"),
|
|
11326
|
+
"claude-linux": resolve(home, ".config", "Claude", "claude_desktop_config.json"),
|
|
11327
|
+
"claude-win32": resolve(process.env.APPDATA || resolve(home, "AppData", "Roaming"), "Claude", "claude_desktop_config.json"),
|
|
11328
|
+
"openclaw": resolve(home, ".openclaw", "openclaw.json")
|
|
11329
|
+
};
|
|
11330
|
+
if (clientOpt === "openclaw") {
|
|
11331
|
+
return { client: "openclaw", configPath: configs.openclaw };
|
|
11332
|
+
}
|
|
11333
|
+
if (clientOpt === "claude") {
|
|
11334
|
+
return { client: "claude", configPath: configs[`claude-${process.platform}`] || configs["claude-darwin"] };
|
|
11335
|
+
}
|
|
11336
|
+
if (existsSync(configs.openclaw)) {
|
|
11337
|
+
return { client: "openclaw", configPath: configs.openclaw };
|
|
11338
|
+
}
|
|
11339
|
+
return { client: "claude", configPath: configs[`claude-${process.platform}`] || configs["claude-darwin"] };
|
|
11340
|
+
}
|
|
11134
11341
|
function getScenariosDir() {
|
|
11135
11342
|
const bundled = resolve(__dirname, "..", "scenarios");
|
|
11136
11343
|
if (existsSync(bundled))
|
|
@@ -11169,4 +11376,13 @@ function resolveProxyPath() {
|
|
|
11169
11376
|
return monorepo;
|
|
11170
11377
|
return null;
|
|
11171
11378
|
}
|
|
11379
|
+
function resolveServerPath(service) {
|
|
11380
|
+
const bundled = resolve(__dirname, `server-${service}.js`);
|
|
11381
|
+
if (existsSync(bundled))
|
|
11382
|
+
return bundled;
|
|
11383
|
+
const monorepo = resolve(__dirname, "..", "..", `server-${service}`, "dist", "index.js");
|
|
11384
|
+
if (existsSync(monorepo))
|
|
11385
|
+
return monorepo;
|
|
11386
|
+
return null;
|
|
11387
|
+
}
|
|
11172
11388
|
program2.parse();
|