maesttro 0.3.0 → 0.4.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 +9 -1
- package/dist/maesttro.js +136 -10
- package/package.json +1 -1
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
|
|
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
|
|
|
@@ -172,6 +177,9 @@ config, keeping your own keys. So `maesttro update` has two steps:
|
|
|
172
177
|
Your session state (in the `maesttro-home` volume) and your project files are
|
|
173
178
|
untouched by an update.
|
|
174
179
|
|
|
180
|
+
On start, `maesttro` checks for a newer published version and offers to update.
|
|
181
|
+
Declining only skips it for that run; the next start asks again.
|
|
182
|
+
|
|
175
183
|
---
|
|
176
184
|
|
|
177
185
|
## What's mounted, and why
|
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: {
|
|
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
|
-
|
|
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
|
|
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(", ")}`);
|
|
@@ -2382,16 +2443,60 @@ async function setup({ args }) {
|
|
|
2382
2443
|
|
|
2383
2444
|
// src/update.ts
|
|
2384
2445
|
import { execSync as execSync5 } from "node:child_process";
|
|
2385
|
-
|
|
2446
|
+
|
|
2447
|
+
// src/version-check.ts
|
|
2448
|
+
import { execFile } from "node:child_process";
|
|
2449
|
+
import { existsSync as existsSync6, readFileSync as readFileSync8 } from "node:fs";
|
|
2386
2450
|
import { join as join12 } from "node:path";
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
return
|
|
2451
|
+
var LOOKUP_TIMEOUT_MS = 3000;
|
|
2452
|
+
function installedManifest() {
|
|
2453
|
+
return JSON.parse(readFileSync8(join12(PACKAGE_ROOT, "package.json"), "utf8"));
|
|
2454
|
+
}
|
|
2455
|
+
function canOfferUpdate({
|
|
2456
|
+
interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY),
|
|
2457
|
+
inRunner = existsSync6(RUNNER_MARKER)
|
|
2458
|
+
} = {}) {
|
|
2459
|
+
return interactive && !inRunner;
|
|
2390
2460
|
}
|
|
2461
|
+
function releaseNumbers(version) {
|
|
2462
|
+
const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(version.trim());
|
|
2463
|
+
return match ? match.slice(1).map(Number) : undefined;
|
|
2464
|
+
}
|
|
2465
|
+
function isNewer({
|
|
2466
|
+
candidate,
|
|
2467
|
+
current
|
|
2468
|
+
}) {
|
|
2469
|
+
const candidateNumbers = releaseNumbers(candidate);
|
|
2470
|
+
const currentNumbers = releaseNumbers(current);
|
|
2471
|
+
if (!candidateNumbers || !currentNumbers)
|
|
2472
|
+
return false;
|
|
2473
|
+
for (let index = 0;index < candidateNumbers.length; index++) {
|
|
2474
|
+
const difference = (candidateNumbers[index] ?? 0) - (currentNumbers[index] ?? 0);
|
|
2475
|
+
if (difference !== 0)
|
|
2476
|
+
return difference > 0;
|
|
2477
|
+
}
|
|
2478
|
+
return false;
|
|
2479
|
+
}
|
|
2480
|
+
function publishedVersion(name) {
|
|
2481
|
+
return new Promise((resolve, reject) => execFile("npm", ["view", name, "version"], { timeout: LOOKUP_TIMEOUT_MS }, (error, stdout) => error ? reject(error) : resolve(stdout.trim())));
|
|
2482
|
+
}
|
|
2483
|
+
async function findNewerVersion({
|
|
2484
|
+
manifest = installedManifest(),
|
|
2485
|
+
lookupLatest = publishedVersion
|
|
2486
|
+
} = {}) {
|
|
2487
|
+
try {
|
|
2488
|
+
const latest = await lookupLatest(manifest.name);
|
|
2489
|
+
return isNewer({ candidate: latest, current: manifest.version }) ? { name: manifest.name, current: manifest.version, latest } : undefined;
|
|
2490
|
+
} catch {
|
|
2491
|
+
return;
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2494
|
+
|
|
2495
|
+
// src/update.ts
|
|
2391
2496
|
function update() {
|
|
2392
2497
|
console.log(`== maesttro update ==
|
|
2393
2498
|
`);
|
|
2394
|
-
const packageName =
|
|
2499
|
+
const packageName = installedManifest().name;
|
|
2395
2500
|
console.log(`-> Updating ${packageName} package...`);
|
|
2396
2501
|
execSync5(`npm i -g ${packageName}@latest`, { stdio: "inherit" });
|
|
2397
2502
|
console.log(`
|
|
@@ -2414,8 +2519,27 @@ var defaultCommands = {
|
|
|
2414
2519
|
login,
|
|
2415
2520
|
logout,
|
|
2416
2521
|
status,
|
|
2417
|
-
print: (text) => console.log(text)
|
|
2522
|
+
print: (text) => console.log(text),
|
|
2523
|
+
findNewerVersion: () => canOfferUpdate() ? findNewerVersion() : Promise.resolve(undefined),
|
|
2524
|
+
ask
|
|
2418
2525
|
};
|
|
2526
|
+
var SKIPS_UPDATE_CHECK = new Set(["update", "list", "help", "--help", "-h"]);
|
|
2527
|
+
async function updatedInstead({
|
|
2528
|
+
command,
|
|
2529
|
+
commands
|
|
2530
|
+
}) {
|
|
2531
|
+
if (command !== undefined && SKIPS_UPDATE_CHECK.has(command))
|
|
2532
|
+
return false;
|
|
2533
|
+
const available = await commands.findNewerVersion();
|
|
2534
|
+
if (!available)
|
|
2535
|
+
return false;
|
|
2536
|
+
const answer = await commands.ask(`${available.name} ${available.latest} is available (you have ${available.current}). Update now? [Y/n] `);
|
|
2537
|
+
if (!/^(y(es)?)?$/i.test(answer))
|
|
2538
|
+
return false;
|
|
2539
|
+
await commands.update();
|
|
2540
|
+
commands.print("Re-run maesttro to continue.");
|
|
2541
|
+
return true;
|
|
2542
|
+
}
|
|
2419
2543
|
var MODE_FLAGS = {
|
|
2420
2544
|
"--strict": true,
|
|
2421
2545
|
"--no-strict": false
|
|
@@ -2455,6 +2579,8 @@ async function main(argv, commands = defaultCommands) {
|
|
|
2455
2579
|
commands.print(`--strict and --no-strict apply to launching a session, not to \`${command}\`.`);
|
|
2456
2580
|
return;
|
|
2457
2581
|
}
|
|
2582
|
+
if (await updatedInstead({ command, commands }))
|
|
2583
|
+
return;
|
|
2458
2584
|
switch (command) {
|
|
2459
2585
|
case undefined:
|
|
2460
2586
|
await commands.launch({ args: [], ...mode });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "maesttro",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
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": {
|