@clawdreyhepburn/carapace 0.3.0 → 0.3.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/src/index.ts CHANGED
@@ -130,6 +130,46 @@ export default function register(api: OpenClawPluginApi) {
130
130
  return { patched: toAdd, alreadyDenied };
131
131
  }
132
132
 
133
+ function patchConfigProxyBaseUrl(): { patched: string[]; alreadySet: string[] } {
134
+ const { readFileSync, writeFileSync, existsSync } = require("node:fs");
135
+ const { join } = require("node:path");
136
+ const { homedir } = require("node:os");
137
+ const configPath = join(homedir(), ".openclaw", "openclaw.json");
138
+
139
+ if (!existsSync(configPath)) return { patched: [], alreadySet: [] };
140
+ const cfg = JSON.parse(readFileSync(configPath, "utf-8"));
141
+
142
+ const port = config.proxy?.port ?? 19821;
143
+ const proxyUrl = `http://127.0.0.1:${port}`;
144
+
145
+ // Figure out which providers have upstream keys configured
146
+ const providers: string[] = [];
147
+ if (config.proxy?.upstream?.anthropic) providers.push("anthropic");
148
+ if (config.proxy?.upstream?.openai) providers.push("openai");
149
+
150
+ const patched: string[] = [];
151
+ const alreadySet: string[] = [];
152
+
153
+ if (!cfg.models) cfg.models = {};
154
+ if (!cfg.models.providers) cfg.models.providers = {};
155
+
156
+ for (const provider of providers) {
157
+ if (!cfg.models.providers[provider]) cfg.models.providers[provider] = {};
158
+ if (cfg.models.providers[provider].baseUrl === proxyUrl) {
159
+ alreadySet.push(provider);
160
+ } else {
161
+ cfg.models.providers[provider].baseUrl = proxyUrl;
162
+ patched.push(provider);
163
+ }
164
+ }
165
+
166
+ if (patched.length > 0) {
167
+ writeFileSync(configPath, JSON.stringify(cfg, null, 2) + "\n", "utf-8");
168
+ }
169
+
170
+ return { patched, alreadySet };
171
+ }
172
+
133
173
  // --- Background service: connect to MCP servers and serve GUI ---
134
174
  api.registerService({
135
175
  id: "carapace",
@@ -469,37 +509,126 @@ export default function register(api: OpenClawPluginApi) {
469
509
  });
470
510
 
471
511
  cmd.command("setup")
472
- .description("Configure OpenClaw to route exec/fetch through Carapace (denies built-in bypass tools)")
512
+ .description("Configure OpenClaw to route all traffic through Carapace")
473
513
  .action(async () => {
474
514
  console.log("\nšŸ¦ž Carapace Setup\n");
515
+ let anyChanges = false;
475
516
 
517
+ // 1. Deny built-in bypass tools
476
518
  const bypasses = checkForBypasses();
477
- if (bypasses.length === 0) {
478
- console.log(" āœ… All bypass tools are already denied. No changes needed.\n");
479
- return;
519
+ if (bypasses.length > 0) {
520
+ console.log(" Denying built-in tools that bypass Cedar:");
521
+ const { patched, alreadyDenied } = patchConfigDenyTools();
522
+ if (alreadyDenied.length > 0) {
523
+ console.log(` Already denied: ${alreadyDenied.join(", ")}`);
524
+ }
525
+ if (patched.length > 0) {
526
+ console.log(` āœ… Added to tools.deny: ${patched.join(", ")}`);
527
+ anyChanges = true;
528
+ }
529
+ } else {
530
+ console.log(" āœ… Built-in bypass tools already denied.");
480
531
  }
481
532
 
482
- console.log(" Built-in tools that bypass Carapace Cedar policies:");
483
- for (const tool of bypasses) {
484
- console.log(` āš ļø ${tool} — agents can use this to skip Cedar authorization`);
533
+ // 2. Set up LLM proxy baseUrl if proxy is configured
534
+ if (config.proxy?.enabled) {
535
+ console.log("\n Configuring LLM proxy baseUrl:");
536
+ const { patched, alreadySet } = patchConfigProxyBaseUrl();
537
+ if (alreadySet.length > 0) {
538
+ console.log(` Already set: ${alreadySet.join(", ")}`);
539
+ }
540
+ if (patched.length > 0) {
541
+ console.log(` āœ… Set models.providers baseUrl for: ${patched.join(", ")}`);
542
+ anyChanges = true;
543
+ }
544
+ if (patched.length === 0 && alreadySet.length === 0) {
545
+ console.log(" āš ļø No upstream providers configured in proxy config.");
546
+ console.log(" Add proxy.upstream.anthropic or proxy.upstream.openai to your plugin config.");
547
+ }
548
+ } else {
549
+ console.log("\n LLM proxy not enabled — skipping baseUrl setup.");
550
+ console.log(" To enable, add proxy.enabled: true to your Carapace plugin config.");
485
551
  }
486
552
 
487
- console.log("\n This will add the following to your OpenClaw config:");
488
- console.log(` tools.deny: [${bypasses.map(t => `"${t}"`).join(", ")}]`);
489
- console.log("\n After setup, agents must use carapace_exec and carapace_fetch");
490
- console.log(" instead, which enforce Cedar policies on every call.\n");
491
-
492
- const { patched, alreadyDenied } = patchConfigDenyTools();
493
-
494
- if (alreadyDenied.length > 0) {
495
- console.log(` Already denied: ${alreadyDenied.join(", ")}`);
496
- }
497
- if (patched.length > 0) {
498
- console.log(` āœ… Added to tools.deny: ${patched.join(", ")}`);
553
+ if (anyChanges) {
499
554
  console.log("\n Restart the gateway for changes to take effect:");
500
555
  console.log(" openclaw gateway restart\n");
501
556
  } else {
502
- console.log(" āœ… No changes needed.\n");
557
+ console.log("\n āœ… Everything already configured. No changes needed.\n");
558
+ }
559
+ });
560
+
561
+ cmd.command("uninstall")
562
+ .description("Reverse all config changes made by Carapace (restores built-in tools)")
563
+ .action(async () => {
564
+ console.log("\nšŸ¦ž Carapace Uninstall\n");
565
+ console.log(" This reverses changes made by 'openclaw carapace setup'.\n");
566
+
567
+ try {
568
+ const { readFileSync, writeFileSync, existsSync } = require("node:fs");
569
+ const { join } = require("node:path");
570
+ const { homedir } = require("node:os");
571
+ const configPath = join(homedir(), ".openclaw", "openclaw.json");
572
+
573
+ if (!existsSync(configPath)) {
574
+ console.log(" No config file found. Nothing to undo.\n");
575
+ return;
576
+ }
577
+
578
+ const cfg = JSON.parse(readFileSync(configPath, "utf-8"));
579
+ let changed = false;
580
+
581
+ // Remove Carapace-added entries from tools.deny
582
+ if (cfg.tools?.deny) {
583
+ const before = cfg.tools.deny.length;
584
+ cfg.tools.deny = cfg.tools.deny.filter((t: string) => !BYPASS_TOOLS.includes(t));
585
+ if (cfg.tools.deny.length === 0) delete cfg.tools.deny;
586
+ if (cfg.tools && Object.keys(cfg.tools).length === 0) delete cfg.tools;
587
+ if (cfg.tools?.deny?.length !== before) {
588
+ changed = true;
589
+ console.log(` āœ… Removed [${BYPASS_TOOLS.join(", ")}] from tools.deny`);
590
+ console.log(" Built-in exec, web_fetch, and web_search are restored.");
591
+ }
592
+ }
593
+
594
+ // Remove models.providers baseUrl override if it points at the proxy
595
+ const proxyPort = cfg.plugins?.entries?.carapace?.config?.proxy?.port ?? 19821;
596
+ const proxyUrl = `http://127.0.0.1:${proxyPort}`;
597
+ if (cfg.models?.providers) {
598
+ for (const [name, provCfg] of Object.entries(cfg.models.providers)) {
599
+ if ((provCfg as any)?.baseUrl === proxyUrl) {
600
+ delete (provCfg as any).baseUrl;
601
+ // Clean up empty objects
602
+ if (Object.keys(provCfg as any).length === 0) delete cfg.models.providers[name];
603
+ changed = true;
604
+ console.log(` āœ… Removed baseUrl proxy override for ${name}`);
605
+ console.log(` ${name} will connect directly to its API again.`);
606
+ }
607
+ }
608
+ if (Object.keys(cfg.models.providers).length === 0) delete cfg.models.providers;
609
+ if (cfg.models && Object.keys(cfg.models).length === 0) delete cfg.models;
610
+ }
611
+
612
+ // Disable the plugin entry (don't delete — user might want to re-enable)
613
+ if (cfg.plugins?.entries?.carapace?.enabled) {
614
+ cfg.plugins.entries.carapace.enabled = false;
615
+ changed = true;
616
+ console.log(" āœ… Disabled carapace plugin in config");
617
+ }
618
+
619
+ if (changed) {
620
+ writeFileSync(configPath, JSON.stringify(cfg, null, 2) + "\n", "utf-8");
621
+ console.log("\n Config updated. Restart the gateway for changes to take effect:");
622
+ console.log(" openclaw gateway restart\n");
623
+ console.log(" To fully remove the plugin files:");
624
+ console.log(" rm -rf ~/.openclaw/extensions/carapace\n");
625
+ } else {
626
+ console.log(" No Carapace changes found in config. Nothing to undo.\n");
627
+ }
628
+
629
+
630
+ } catch (err: any) {
631
+ console.log(` āŒ Error: ${err.message}\n`);
503
632
  }
504
633
  });
505
634