maesttro 0.4.0 → 0.4.2
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 +6 -2
- package/dist/maesttro.js +82 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -73,8 +73,9 @@ maesttro update # bump the package + rebuild image with
|
|
|
73
73
|
|
|
74
74
|
Run `maesttro` from the root of a repository (git 2.48+ required). Each run is a
|
|
75
75
|
**session** with its own isolated clone, `.worktrees/maesttro-<id>`, detached at
|
|
76
|
-
`origin/<default branch>` and mounted at `/workspace`.
|
|
77
|
-
|
|
76
|
+
`origin/<default branch>` and mounted at `/workspace`. The runner container is
|
|
77
|
+
named `maesttro-<id>` too, so `docker ps` and `docker logs maesttro-<id>` line up
|
|
78
|
+
with the session. That clone is the only part of the repo the container sees:
|
|
78
79
|
|
|
79
80
|
- Your real `.git` is never mounted, and neither is your main checkout or any
|
|
80
81
|
uncommitted work there. The container works in a `git clone --shared` that
|
|
@@ -177,6 +178,9 @@ config, keeping your own keys. So `maesttro update` has two steps:
|
|
|
177
178
|
Your session state (in the `maesttro-home` volume) and your project files are
|
|
178
179
|
untouched by an update.
|
|
179
180
|
|
|
181
|
+
On start, `maesttro` checks for a newer published version and offers to update.
|
|
182
|
+
Declining only skips it for that run; the next start asks again.
|
|
183
|
+
|
|
180
184
|
---
|
|
181
185
|
|
|
182
186
|
## What's mounted, and why
|
package/dist/maesttro.js
CHANGED
|
@@ -1279,7 +1279,7 @@ Usage:
|
|
|
1279
1279
|
`;
|
|
1280
1280
|
|
|
1281
1281
|
// src/launch.ts
|
|
1282
|
-
import { execSync as execSync3, spawn } from "node:child_process";
|
|
1282
|
+
import { execSync as execSync3, spawn, spawnSync as spawnSync2 } from "node:child_process";
|
|
1283
1283
|
import { existsSync as existsSync4, rmSync as rmSync6 } from "node:fs";
|
|
1284
1284
|
import { constants, homedir as homedir4 } from "node:os";
|
|
1285
1285
|
import { join as join11 } from "node:path";
|
|
@@ -1979,7 +1979,8 @@ function dockerRunArgs({
|
|
|
1979
1979
|
command,
|
|
1980
1980
|
mounts,
|
|
1981
1981
|
workdir,
|
|
1982
|
-
extra = []
|
|
1982
|
+
extra = [],
|
|
1983
|
+
name
|
|
1983
1984
|
}) {
|
|
1984
1985
|
const volumes = mounts.flatMap(({ host, container, readOnly }) => [
|
|
1985
1986
|
"-v",
|
|
@@ -1989,6 +1990,7 @@ function dockerRunArgs({
|
|
|
1989
1990
|
"run",
|
|
1990
1991
|
"-it",
|
|
1991
1992
|
"--rm",
|
|
1993
|
+
...name ? ["--name", name] : [],
|
|
1992
1994
|
...volumes,
|
|
1993
1995
|
...extra,
|
|
1994
1996
|
"-w",
|
|
@@ -2016,6 +2018,9 @@ function ensureReady() {
|
|
|
2016
2018
|
throw new LaunchError("Runner isn't set up yet. Run `maesttro setup` first.");
|
|
2017
2019
|
}
|
|
2018
2020
|
}
|
|
2021
|
+
function clearStaleContainer(id) {
|
|
2022
|
+
spawnSync2("docker", ["rm", "-f", id], { stdio: "ignore" });
|
|
2023
|
+
}
|
|
2019
2024
|
function sharedSkillsDir() {
|
|
2020
2025
|
const dir = process.env.AGENT_SHARED_SKILLS ?? join11(homedir4(), "agent-shared", "skills");
|
|
2021
2026
|
return existsSync4(dir) ? dir : undefined;
|
|
@@ -2216,10 +2221,12 @@ async function launch({
|
|
|
2216
2221
|
"MAESTTRO_HOST_UID",
|
|
2217
2222
|
"MAESTTRO_HOST_GID"
|
|
2218
2223
|
].concat(repo ? ["MAESTTRO_REPO"] : []).flatMap((name) => ["-e", name]);
|
|
2224
|
+
clearStaleContainer(saved.id);
|
|
2219
2225
|
const child = spawn("docker", dockerRunArgs({
|
|
2220
2226
|
command: containerCommand(resolveCommand(args)),
|
|
2221
2227
|
mounts,
|
|
2222
2228
|
workdir,
|
|
2229
|
+
name: saved.id,
|
|
2223
2230
|
extra: [...github.dockerArgs, ...envFlags]
|
|
2224
2231
|
}), { stdio: "inherit", env: { ...process.env, ...sessionEnv } });
|
|
2225
2232
|
const forward = (signal) => child.kill(signal);
|
|
@@ -2238,7 +2245,7 @@ async function launch({
|
|
|
2238
2245
|
}
|
|
2239
2246
|
|
|
2240
2247
|
// src/push.ts
|
|
2241
|
-
import { spawnSync as
|
|
2248
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
2242
2249
|
function pushCandidates(sessions) {
|
|
2243
2250
|
const candidates = new Map;
|
|
2244
2251
|
for (const session of sessions) {
|
|
@@ -2349,7 +2356,7 @@ Push? [y/N] `);
|
|
|
2349
2356
|
return;
|
|
2350
2357
|
}
|
|
2351
2358
|
}
|
|
2352
|
-
const pushed =
|
|
2359
|
+
const pushed = spawnSync3("git", ["-C", root, "push", "origin", `${tip}:refs/heads/${branch}`], { stdio: "inherit" });
|
|
2353
2360
|
if (pushed.status === 0) {
|
|
2354
2361
|
console.log(`Pushed ${branch}. Base for comparison was ${baseSha.slice(0, 8)}.`);
|
|
2355
2362
|
}
|
|
@@ -2443,16 +2450,60 @@ async function setup({ args }) {
|
|
|
2443
2450
|
|
|
2444
2451
|
// src/update.ts
|
|
2445
2452
|
import { execSync as execSync5 } from "node:child_process";
|
|
2446
|
-
|
|
2453
|
+
|
|
2454
|
+
// src/version-check.ts
|
|
2455
|
+
import { execFile } from "node:child_process";
|
|
2456
|
+
import { existsSync as existsSync6, readFileSync as readFileSync8 } from "node:fs";
|
|
2447
2457
|
import { join as join12 } from "node:path";
|
|
2448
|
-
|
|
2449
|
-
|
|
2450
|
-
return
|
|
2458
|
+
var LOOKUP_TIMEOUT_MS = 3000;
|
|
2459
|
+
function installedManifest() {
|
|
2460
|
+
return JSON.parse(readFileSync8(join12(PACKAGE_ROOT, "package.json"), "utf8"));
|
|
2461
|
+
}
|
|
2462
|
+
function canOfferUpdate({
|
|
2463
|
+
interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY),
|
|
2464
|
+
inRunner = existsSync6(RUNNER_MARKER)
|
|
2465
|
+
} = {}) {
|
|
2466
|
+
return interactive && !inRunner;
|
|
2467
|
+
}
|
|
2468
|
+
function releaseNumbers(version) {
|
|
2469
|
+
const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(version.trim());
|
|
2470
|
+
return match ? match.slice(1).map(Number) : undefined;
|
|
2471
|
+
}
|
|
2472
|
+
function isNewer({
|
|
2473
|
+
candidate,
|
|
2474
|
+
current
|
|
2475
|
+
}) {
|
|
2476
|
+
const candidateNumbers = releaseNumbers(candidate);
|
|
2477
|
+
const currentNumbers = releaseNumbers(current);
|
|
2478
|
+
if (!candidateNumbers || !currentNumbers)
|
|
2479
|
+
return false;
|
|
2480
|
+
for (let index = 0;index < candidateNumbers.length; index++) {
|
|
2481
|
+
const difference = (candidateNumbers[index] ?? 0) - (currentNumbers[index] ?? 0);
|
|
2482
|
+
if (difference !== 0)
|
|
2483
|
+
return difference > 0;
|
|
2484
|
+
}
|
|
2485
|
+
return false;
|
|
2486
|
+
}
|
|
2487
|
+
function publishedVersion(name) {
|
|
2488
|
+
return new Promise((resolve, reject) => execFile("npm", ["view", name, "version"], { timeout: LOOKUP_TIMEOUT_MS }, (error, stdout) => error ? reject(error) : resolve(stdout.trim())));
|
|
2489
|
+
}
|
|
2490
|
+
async function findNewerVersion({
|
|
2491
|
+
manifest = installedManifest(),
|
|
2492
|
+
lookupLatest = publishedVersion
|
|
2493
|
+
} = {}) {
|
|
2494
|
+
try {
|
|
2495
|
+
const latest = await lookupLatest(manifest.name);
|
|
2496
|
+
return isNewer({ candidate: latest, current: manifest.version }) ? { name: manifest.name, current: manifest.version, latest } : undefined;
|
|
2497
|
+
} catch {
|
|
2498
|
+
return;
|
|
2499
|
+
}
|
|
2451
2500
|
}
|
|
2501
|
+
|
|
2502
|
+
// src/update.ts
|
|
2452
2503
|
function update() {
|
|
2453
2504
|
console.log(`== maesttro update ==
|
|
2454
2505
|
`);
|
|
2455
|
-
const packageName =
|
|
2506
|
+
const packageName = installedManifest().name;
|
|
2456
2507
|
console.log(`-> Updating ${packageName} package...`);
|
|
2457
2508
|
execSync5(`npm i -g ${packageName}@latest`, { stdio: "inherit" });
|
|
2458
2509
|
console.log(`
|
|
@@ -2475,8 +2526,27 @@ var defaultCommands = {
|
|
|
2475
2526
|
login,
|
|
2476
2527
|
logout,
|
|
2477
2528
|
status,
|
|
2478
|
-
print: (text) => console.log(text)
|
|
2529
|
+
print: (text) => console.log(text),
|
|
2530
|
+
findNewerVersion: () => canOfferUpdate() ? findNewerVersion() : Promise.resolve(undefined),
|
|
2531
|
+
ask
|
|
2479
2532
|
};
|
|
2533
|
+
var SKIPS_UPDATE_CHECK = new Set(["update", "list", "help", "--help", "-h"]);
|
|
2534
|
+
async function updatedInstead({
|
|
2535
|
+
command,
|
|
2536
|
+
commands
|
|
2537
|
+
}) {
|
|
2538
|
+
if (command !== undefined && SKIPS_UPDATE_CHECK.has(command))
|
|
2539
|
+
return false;
|
|
2540
|
+
const available = await commands.findNewerVersion();
|
|
2541
|
+
if (!available)
|
|
2542
|
+
return false;
|
|
2543
|
+
const answer = await commands.ask(`${available.name} ${available.latest} is available (you have ${available.current}). Update now? [Y/n] `);
|
|
2544
|
+
if (!/^(y(es)?)?$/i.test(answer))
|
|
2545
|
+
return false;
|
|
2546
|
+
await commands.update();
|
|
2547
|
+
commands.print("Re-run maesttro to continue.");
|
|
2548
|
+
return true;
|
|
2549
|
+
}
|
|
2480
2550
|
var MODE_FLAGS = {
|
|
2481
2551
|
"--strict": true,
|
|
2482
2552
|
"--no-strict": false
|
|
@@ -2516,6 +2586,8 @@ async function main(argv, commands = defaultCommands) {
|
|
|
2516
2586
|
commands.print(`--strict and --no-strict apply to launching a session, not to \`${command}\`.`);
|
|
2517
2587
|
return;
|
|
2518
2588
|
}
|
|
2589
|
+
if (await updatedInstead({ command, commands }))
|
|
2590
|
+
return;
|
|
2519
2591
|
switch (command) {
|
|
2520
2592
|
case undefined:
|
|
2521
2593
|
await commands.launch({ args: [], ...mode });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "maesttro",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
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": {
|