maesttro 0.3.0 → 0.4.0

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 CHANGED
@@ -155,7 +155,12 @@ thinking blocks shown. The two providers replace yours of the same name; the
155
155
  rest (`mcp`, `plugin`, other providers) is merged. Removed secrets are listed
156
156
  at launch by path. `{env:X}` / `$X` references are kept.
157
157
 
158
- Claude Code gets no Maesttro config and uses its own defaults and login.
158
+ Claude Code keeps its own login and defaults. It is pointed at envoy's
159
+ `$MAESTTRO_URL/claude-code` (`ANTHROPIC_BASE_URL`), with Maesttro added to the
160
+ model picker as a custom option (`maesttro`), with its context window read
161
+ from envoy at launch. Anthropic models pass
162
+ through to Anthropic on your own subscription. These env entries are merged
163
+ into the runner's `~/.claude/settings.json` at every start.
159
164
 
160
165
  ---
161
166
 
package/dist/maesttro.js CHANGED
@@ -461,6 +461,29 @@ var ORCHESTRATOR_MODEL = "maesttro";
461
461
  function maesttroUrl(env = process.env) {
462
462
  return (env.MAESTTRO_URL?.trim() || DEFAULT_MAESTTRO_URL).replace(/\/+$/, "");
463
463
  }
464
+ function contextWindowOf(card) {
465
+ const { max_input_tokens: input, max_tokens: output } = card;
466
+ return typeof input === "number" && typeof output === "number" ? input + output : undefined;
467
+ }
468
+ async function fetchOrchestratorWindow({
469
+ url,
470
+ fetch: fetch2 = globalThis.fetch,
471
+ timeoutMs = 2000
472
+ }) {
473
+ try {
474
+ const response = await fetch2(`${url}/claude-code/v1/models`, {
475
+ signal: AbortSignal.timeout(timeoutMs)
476
+ });
477
+ if (!response.ok)
478
+ return;
479
+ const body = await response.json();
480
+ const cards = Array.isArray(body.data) ? body.data : [];
481
+ const card = cards.find(({ id }) => id === `claude-${ORCHESTRATOR_MODEL}`);
482
+ return card ? contextWindowOf(card) : undefined;
483
+ } catch {
484
+ return;
485
+ }
486
+ }
464
487
 
465
488
  // src/provider-configs.ts
466
489
  var PROVIDER_ID = "maesttro";
@@ -531,6 +554,20 @@ function opencodeConfig(base, url) {
531
554
  }
532
555
  };
533
556
  }
557
+ function claudeSettings({
558
+ url,
559
+ orchestratorWindow
560
+ }) {
561
+ return {
562
+ env: {
563
+ ANTHROPIC_BASE_URL: `${url}/claude-code`,
564
+ ANTHROPIC_CUSTOM_MODEL_OPTION: ORCHESTRATOR_MODEL,
565
+ ANTHROPIC_CUSTOM_MODEL_OPTION_NAME: "Maesttro",
566
+ ANTHROPIC_CUSTOM_MODEL_OPTION_DESCRIPTION: "Maesttro orchestrator",
567
+ ...orchestratorWindow ? { CLAUDE_CODE_MAX_CONTEXT_TOKENS: String(orchestratorWindow) } : {}
568
+ }
569
+ };
570
+ }
534
571
 
535
572
  // src/secrets.ts
536
573
  var SECRET_KEY = /api[_-]?key|token|secret|passw(or)?d|credential|authorization|bearer|private[_-]?key|cookie/i;
@@ -617,6 +654,8 @@ var OPENCODE_HOST_CONFIGS = [
617
654
  ".config/opencode/opencode.jsonc"
618
655
  ];
619
656
  var OPENCODE_CONFIG = ".config/opencode/opencode.json";
657
+ var CLAUDE_SETTINGS = ".claude/settings.json";
658
+ var MERGED_INTO_HOME = [CLAUDE_SETTINGS];
620
659
  var OPENCODE_TUI_STATE = ".local/state/opencode/kv.json";
621
660
  var CURATED_OPENCODE_TUI_STATE = {
622
661
  thinking_mode: "show",
@@ -652,11 +691,15 @@ function json(value) {
652
691
  }
653
692
  function buildClientConfigs({
654
693
  host,
655
- url
694
+ url,
695
+ orchestratorWindow
656
696
  }) {
657
697
  const opencode = redactSecrets(opencodeHostBase(host.opencode));
658
698
  return {
659
- files: { [OPENCODE_CONFIG]: json(opencodeConfig(opencode.value, url)) },
699
+ files: {
700
+ [OPENCODE_CONFIG]: json(opencodeConfig(opencode.value, url)),
701
+ [CLAUDE_SETTINGS]: json(claudeSettings({ url, orchestratorWindow }))
702
+ },
660
703
  removed: opencode.removed.map((path) => `opencode ${path}`)
661
704
  };
662
705
  }
@@ -679,7 +722,19 @@ function installClientConfigs({
679
722
  }) {
680
723
  if (!existsSync(source))
681
724
  return false;
682
- cpSync(source, home, { recursive: true, force: true });
725
+ const merged = MERGED_INTO_HOME.map((file) => join2(source, file));
726
+ cpSync(source, home, {
727
+ recursive: true,
728
+ force: true,
729
+ filter: (path) => !merged.includes(path)
730
+ });
731
+ for (const file of MERGED_INTO_HOME) {
732
+ const staged = join2(source, file);
733
+ if (!existsSync(staged))
734
+ continue;
735
+ const target = join2(home, file);
736
+ writeJson(target, mergeJson(readJson(target), readJson(staged)));
737
+ }
683
738
  return true;
684
739
  }
685
740
  function injectOpencodeTuiState({ home }) {
@@ -2123,9 +2178,15 @@ async function launch({
2123
2178
  if (github.notice)
2124
2179
  console.log(github.notice);
2125
2180
  const hostHome = homedir4();
2181
+ const url = maesttroUrl();
2182
+ const orchestratorWindow = await fetchOrchestratorWindow({ url });
2183
+ if (orchestratorWindow === undefined) {
2184
+ console.log("Couldn't read Maesttro's context window; Claude Code will assume its default.");
2185
+ }
2126
2186
  const clientConfigs = buildClientConfigs({
2127
2187
  host: readHostClientConfigs({ home: hostHome }),
2128
- url: maesttroUrl()
2188
+ url,
2189
+ orchestratorWindow
2129
2190
  });
2130
2191
  if (clientConfigs.removed.length > 0) {
2131
2192
  console.log(`Kept out of the runner as secrets: ${clientConfigs.removed.join(", ")}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "maesttro",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Isolated Docker runner for local coding agents (opencode, claude, ...) under a dedicated bot GitHub identity, with curated configs baked in per client.",
5
5
  "type": "module",
6
6
  "bin": {