maesttro 0.6.0 → 0.6.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 +2 -2
- package/dist/maesttro.js +165 -123
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -153,8 +153,8 @@ config on top (`$MAESTTRO_URL`, default `http://100.64.0.2`). Only the result
|
|
|
153
153
|
is mounted into the runner, read-only, and copied into place at start. Your
|
|
154
154
|
raw config never enters the container, and your host's config is never written.
|
|
155
155
|
|
|
156
|
-
Injected: a `
|
|
157
|
-
(lorem direct on the DGX), compaction, default model `
|
|
156
|
+
Injected: a `esatto` provider (Maesttro) at `/v1`, a `dgx-spark` provider
|
|
157
|
+
(lorem direct on the DGX), compaction, default model `esatto/maesttro`, and
|
|
158
158
|
thinking blocks shown. The two providers replace yours of the same name; the
|
|
159
159
|
rest (`mcp`, `plugin`, other providers) is merged. Removed secrets are listed
|
|
160
160
|
at launch by path. `{env:X}` / `$X` references are kept.
|
package/dist/maesttro.js
CHANGED
|
@@ -1871,15 +1871,14 @@ function isClientKey(value) {
|
|
|
1871
1871
|
|
|
1872
1872
|
// src/configure.ts
|
|
1873
1873
|
import {
|
|
1874
|
-
cpSync as cpSync2,
|
|
1875
1874
|
existsSync as existsSync3,
|
|
1876
|
-
mkdirSync as
|
|
1877
|
-
readdirSync as
|
|
1875
|
+
mkdirSync as mkdirSync10,
|
|
1876
|
+
readdirSync as readdirSync4,
|
|
1878
1877
|
readFileSync as readFileSync10,
|
|
1879
1878
|
writeFileSync as writeFileSync9
|
|
1880
1879
|
} from "node:fs";
|
|
1881
1880
|
import { homedir as homedir2 } from "node:os";
|
|
1882
|
-
import { dirname as
|
|
1881
|
+
import { dirname as dirname7, join as join11 } from "node:path";
|
|
1883
1882
|
|
|
1884
1883
|
// src/claude-state.ts
|
|
1885
1884
|
import { readFileSync as readFileSync8, renameSync as renameSync6, writeFileSync as writeFileSync7 } from "node:fs";
|
|
@@ -1924,17 +1923,52 @@ function seedClaudeState({
|
|
|
1924
1923
|
|
|
1925
1924
|
// src/client-configs.ts
|
|
1926
1925
|
import {
|
|
1927
|
-
chmodSync as
|
|
1928
|
-
cpSync,
|
|
1926
|
+
chmodSync as chmodSync5,
|
|
1929
1927
|
existsSync as existsSync2,
|
|
1930
|
-
mkdirSync as
|
|
1928
|
+
mkdirSync as mkdirSync9,
|
|
1931
1929
|
mkdtempSync as mkdtempSync2,
|
|
1932
1930
|
readFileSync as readFileSync9,
|
|
1933
|
-
rmSync as
|
|
1931
|
+
rmSync as rmSync7,
|
|
1934
1932
|
writeFileSync as writeFileSync8
|
|
1935
1933
|
} from "node:fs";
|
|
1936
1934
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
1937
|
-
import { dirname as
|
|
1935
|
+
import { dirname as dirname6, join as join10 } from "node:path";
|
|
1936
|
+
|
|
1937
|
+
// src/copy-tree.ts
|
|
1938
|
+
import {
|
|
1939
|
+
chmodSync as chmodSync4,
|
|
1940
|
+
copyFileSync,
|
|
1941
|
+
lstatSync as lstatSync4,
|
|
1942
|
+
mkdirSync as mkdirSync8,
|
|
1943
|
+
readdirSync as readdirSync3,
|
|
1944
|
+
readlinkSync,
|
|
1945
|
+
rmSync as rmSync6,
|
|
1946
|
+
symlinkSync
|
|
1947
|
+
} from "node:fs";
|
|
1948
|
+
import { dirname as dirname5, isAbsolute, join as join9, resolve as resolve2 } from "node:path";
|
|
1949
|
+
function copyTree(source, target, filter = () => true) {
|
|
1950
|
+
mkdirSync8(target, { recursive: true });
|
|
1951
|
+
for (const name of readdirSync3(source)) {
|
|
1952
|
+
const from = join9(source, name);
|
|
1953
|
+
if (!filter(from))
|
|
1954
|
+
continue;
|
|
1955
|
+
const to = join9(target, name);
|
|
1956
|
+
const stat = lstatSync4(from);
|
|
1957
|
+
if (stat.isSymbolicLink()) {
|
|
1958
|
+
const link = readlinkSync(from);
|
|
1959
|
+
rmSync6(to, { force: true });
|
|
1960
|
+
symlinkSync(isAbsolute(link) ? link : resolve2(dirname5(from), link), to);
|
|
1961
|
+
continue;
|
|
1962
|
+
}
|
|
1963
|
+
if (stat.isDirectory()) {
|
|
1964
|
+
copyTree(from, to, filter);
|
|
1965
|
+
continue;
|
|
1966
|
+
}
|
|
1967
|
+
rmSync6(to, { force: true });
|
|
1968
|
+
copyFileSync(from, to);
|
|
1969
|
+
chmodSync4(to, stat.mode & 511);
|
|
1970
|
+
}
|
|
1971
|
+
}
|
|
1938
1972
|
|
|
1939
1973
|
// src/maesttro-endpoint.ts
|
|
1940
1974
|
var DEFAULT_MAESTTRO_URL = "http://100.64.0.2";
|
|
@@ -1967,7 +2001,7 @@ async function fetchOrchestratorWindow({
|
|
|
1967
2001
|
}
|
|
1968
2002
|
|
|
1969
2003
|
// src/provider-configs.ts
|
|
1970
|
-
var PROVIDER_ID = "
|
|
2004
|
+
var PROVIDER_ID = "esatto";
|
|
1971
2005
|
var MAESTTRO_API_KEY = "maesttro";
|
|
1972
2006
|
var DGX_SPARK_PROVIDER_ID = "dgx-spark";
|
|
1973
2007
|
var DGX_SPARK_URL = "http://100.64.0.1:8001";
|
|
@@ -1994,7 +2028,7 @@ function opencodeProviders(url) {
|
|
|
1994
2028
|
name: "Maesttro",
|
|
1995
2029
|
options: { baseURL: `${url}/v1`, apiKey: MAESTTRO_API_KEY },
|
|
1996
2030
|
models: {
|
|
1997
|
-
[ORCHESTRATOR_MODEL]: { name: "
|
|
2031
|
+
[ORCHESTRATOR_MODEL]: { name: "Maesttro", ...IMAGE_INPUT }
|
|
1998
2032
|
}
|
|
1999
2033
|
},
|
|
2000
2034
|
[DGX_SPARK_PROVIDER_ID]: {
|
|
@@ -2011,10 +2045,11 @@ function opencodeProviders(url) {
|
|
|
2011
2045
|
}
|
|
2012
2046
|
};
|
|
2013
2047
|
}
|
|
2014
|
-
function opencodeConfig(base, url) {
|
|
2048
|
+
function opencodeConfig(base, url, isStrict = false) {
|
|
2015
2049
|
const merged = mergeJson({ $schema: "https://opencode.ai/config.json", ...base }, {
|
|
2016
2050
|
model: `${PROVIDER_ID}/${ORCHESTRATOR_MODEL}`,
|
|
2017
|
-
compaction: { auto: true, keep: { tokens: 24000 }, buffer: 32000 }
|
|
2051
|
+
compaction: { auto: true, keep: { tokens: 24000 }, buffer: 32000 },
|
|
2052
|
+
...isStrict ? {} : { permission: "allow" }
|
|
2018
2053
|
});
|
|
2019
2054
|
const enabled = stringList(base.enabled_providers);
|
|
2020
2055
|
const disabled = stringList(base.disabled_providers);
|
|
@@ -2037,7 +2072,8 @@ function opencodeConfig(base, url) {
|
|
|
2037
2072
|
}
|
|
2038
2073
|
function claudeSettings({
|
|
2039
2074
|
url,
|
|
2040
|
-
orchestratorWindow
|
|
2075
|
+
orchestratorWindow,
|
|
2076
|
+
isStrict
|
|
2041
2077
|
}) {
|
|
2042
2078
|
return {
|
|
2043
2079
|
env: {
|
|
@@ -2046,7 +2082,8 @@ function claudeSettings({
|
|
|
2046
2082
|
ANTHROPIC_CUSTOM_MODEL_OPTION_NAME: "Maesttro",
|
|
2047
2083
|
ANTHROPIC_CUSTOM_MODEL_OPTION_DESCRIPTION: "Maesttro orchestrator",
|
|
2048
2084
|
...orchestratorWindow ? { CLAUDE_CODE_MAX_CONTEXT_TOKENS: String(orchestratorWindow) } : {}
|
|
2049
|
-
}
|
|
2085
|
+
},
|
|
2086
|
+
...isStrict ? {} : { permissions: { defaultMode: "bypassPermissions" } }
|
|
2050
2087
|
};
|
|
2051
2088
|
}
|
|
2052
2089
|
|
|
@@ -2148,7 +2185,7 @@ function readHostFile({
|
|
|
2148
2185
|
parse,
|
|
2149
2186
|
warn
|
|
2150
2187
|
}) {
|
|
2151
|
-
const path =
|
|
2188
|
+
const path = join10(home, file);
|
|
2152
2189
|
if (!existsSync2(path))
|
|
2153
2190
|
return {};
|
|
2154
2191
|
try {
|
|
@@ -2173,29 +2210,30 @@ function json(value) {
|
|
|
2173
2210
|
function buildClientConfigs({
|
|
2174
2211
|
host,
|
|
2175
2212
|
url,
|
|
2176
|
-
orchestratorWindow
|
|
2213
|
+
orchestratorWindow,
|
|
2214
|
+
isStrict
|
|
2177
2215
|
}) {
|
|
2178
2216
|
const opencode = redactSecrets(opencodeHostBase(host.opencode));
|
|
2179
2217
|
return {
|
|
2180
2218
|
files: {
|
|
2181
|
-
[OPENCODE_CONFIG]: json(opencodeConfig(opencode.value, url)),
|
|
2182
|
-
[CLAUDE_SETTINGS]: json(claudeSettings({ url, orchestratorWindow }))
|
|
2219
|
+
[OPENCODE_CONFIG]: json(opencodeConfig(opencode.value, url, isStrict)),
|
|
2220
|
+
[CLAUDE_SETTINGS]: json(claudeSettings({ url, orchestratorWindow, isStrict }))
|
|
2183
2221
|
},
|
|
2184
2222
|
removed: opencode.removed.map((path) => `opencode ${path}`)
|
|
2185
2223
|
};
|
|
2186
2224
|
}
|
|
2187
2225
|
function writeClientConfigDir(files, parent = tmpdir2()) {
|
|
2188
|
-
const root = mkdtempSync2(
|
|
2189
|
-
|
|
2190
|
-
const dir =
|
|
2226
|
+
const root = mkdtempSync2(join10(parent, "maesttro-clients-"));
|
|
2227
|
+
chmodSync5(root, 448);
|
|
2228
|
+
const dir = join10(root, "home");
|
|
2191
2229
|
for (const [file, content] of Object.entries(files)) {
|
|
2192
|
-
const path =
|
|
2193
|
-
|
|
2230
|
+
const path = join10(dir, file);
|
|
2231
|
+
mkdirSync9(dirname6(path), { recursive: true, mode: 493 });
|
|
2194
2232
|
writeFileSync8(path, content);
|
|
2195
|
-
|
|
2233
|
+
chmodSync5(path, 420);
|
|
2196
2234
|
}
|
|
2197
|
-
|
|
2198
|
-
return { dir, remove: () =>
|
|
2235
|
+
chmodSync5(dir, 493);
|
|
2236
|
+
return { dir, remove: () => rmSync7(root, { recursive: true, force: true }) };
|
|
2199
2237
|
}
|
|
2200
2238
|
function installClientConfigs({
|
|
2201
2239
|
home,
|
|
@@ -2203,23 +2241,19 @@ function installClientConfigs({
|
|
|
2203
2241
|
}) {
|
|
2204
2242
|
if (!existsSync2(source))
|
|
2205
2243
|
return false;
|
|
2206
|
-
const merged = MERGED_INTO_HOME.map((file) =>
|
|
2207
|
-
|
|
2208
|
-
recursive: true,
|
|
2209
|
-
force: true,
|
|
2210
|
-
filter: (path) => !merged.includes(path)
|
|
2211
|
-
});
|
|
2244
|
+
const merged = MERGED_INTO_HOME.map((file) => join10(source, file));
|
|
2245
|
+
copyTree(source, home, (path) => !merged.includes(path));
|
|
2212
2246
|
for (const file of MERGED_INTO_HOME) {
|
|
2213
|
-
const staged =
|
|
2247
|
+
const staged = join10(source, file);
|
|
2214
2248
|
if (!existsSync2(staged))
|
|
2215
2249
|
continue;
|
|
2216
|
-
const target =
|
|
2250
|
+
const target = join10(home, file);
|
|
2217
2251
|
writeJson(target, mergeJson(readJson(target), readJson(staged)));
|
|
2218
2252
|
}
|
|
2219
2253
|
return true;
|
|
2220
2254
|
}
|
|
2221
2255
|
function injectOpencodeTuiState({ home }) {
|
|
2222
|
-
const path =
|
|
2256
|
+
const path = join10(home, OPENCODE_TUI_STATE);
|
|
2223
2257
|
writeJson(path, mergeJson(readJson(path), CURATED_OPENCODE_TUI_STATE));
|
|
2224
2258
|
return path;
|
|
2225
2259
|
}
|
|
@@ -2316,22 +2350,22 @@ function withoutComments(value) {
|
|
|
2316
2350
|
}
|
|
2317
2351
|
function applyProviderConfigs({
|
|
2318
2352
|
home,
|
|
2319
|
-
configsDir =
|
|
2353
|
+
configsDir = join11(DOCKERFILE_DIR, "configs")
|
|
2320
2354
|
}) {
|
|
2321
2355
|
const written = [];
|
|
2322
2356
|
for (const [key, client] of Object.entries(CLIENTS)) {
|
|
2323
|
-
const source =
|
|
2357
|
+
const source = join11(configsDir, key);
|
|
2324
2358
|
if (!existsSync3(source))
|
|
2325
2359
|
continue;
|
|
2326
|
-
const targetDir =
|
|
2327
|
-
for (const name of
|
|
2360
|
+
const targetDir = join11(home, client.configDir.replace(/^\/home\/agent\/?/, ""));
|
|
2361
|
+
for (const name of readdirSync4(source)) {
|
|
2328
2362
|
if (!name.endsWith(".json"))
|
|
2329
2363
|
continue;
|
|
2330
|
-
const curated = withoutComments(readJson(
|
|
2364
|
+
const curated = withoutComments(readJson(join11(source, name)));
|
|
2331
2365
|
if (Object.keys(curated).length === 0)
|
|
2332
2366
|
continue;
|
|
2333
|
-
const target =
|
|
2334
|
-
|
|
2367
|
+
const target = join11(targetDir, name);
|
|
2368
|
+
mkdirSync10(targetDir, { recursive: true });
|
|
2335
2369
|
const merged = mergeJson(readJson(target), curated);
|
|
2336
2370
|
writeFileSync9(target, `${JSON.stringify(merged, null, 2)}
|
|
2337
2371
|
`);
|
|
@@ -2346,19 +2380,19 @@ function stageHostInstructions({
|
|
|
2346
2380
|
context
|
|
2347
2381
|
}) {
|
|
2348
2382
|
for (const directory of STAGED_DIRECTORIES) {
|
|
2349
|
-
const source =
|
|
2383
|
+
const source = join11(staging, directory);
|
|
2350
2384
|
if (!existsSync3(source))
|
|
2351
2385
|
continue;
|
|
2352
2386
|
try {
|
|
2353
|
-
|
|
2387
|
+
copyTree(source, join11(home, directory));
|
|
2354
2388
|
} catch {}
|
|
2355
2389
|
}
|
|
2356
2390
|
const instructions = runnerInstructions(context);
|
|
2357
2391
|
for (const file of INSTRUCTION_FILES) {
|
|
2358
|
-
const source =
|
|
2392
|
+
const source = join11(staging, file);
|
|
2359
2393
|
const hostCopy = existsSync3(source) ? readFileSync10(source, "utf8") : "";
|
|
2360
|
-
const target =
|
|
2361
|
-
|
|
2394
|
+
const target = join11(home, file);
|
|
2395
|
+
mkdirSync10(dirname7(target), { recursive: true });
|
|
2362
2396
|
writeFileSync9(target, hostCopy ? `${hostCopy.trimEnd()}
|
|
2363
2397
|
|
|
2364
2398
|
${instructions}` : instructions);
|
|
@@ -2483,8 +2517,8 @@ async function reachable(host, port, {
|
|
|
2483
2517
|
}
|
|
2484
2518
|
|
|
2485
2519
|
// src/repo.ts
|
|
2486
|
-
import { appendFileSync as appendFileSync2, mkdirSync as
|
|
2487
|
-
import { join as
|
|
2520
|
+
import { appendFileSync as appendFileSync2, mkdirSync as mkdirSync11, realpathSync } from "node:fs";
|
|
2521
|
+
import { join as join12 } from "node:path";
|
|
2488
2522
|
var MINIMUM_GIT = [2, 48];
|
|
2489
2523
|
var WORKTREES_DIR = ".worktrees";
|
|
2490
2524
|
|
|
@@ -2527,7 +2561,7 @@ function repoRoot(run, cwd) {
|
|
|
2527
2561
|
"--git-common-dir"
|
|
2528
2562
|
]).out;
|
|
2529
2563
|
const root = realpathSync(top.out);
|
|
2530
|
-
if (realpathSync(commonDir) !==
|
|
2564
|
+
if (realpathSync(commonDir) !== join12(root, ".git")) {
|
|
2531
2565
|
throw new LaunchError("This is a git worktree. Run maesttro from the main checkout; each session gets its own isolated clone.");
|
|
2532
2566
|
}
|
|
2533
2567
|
if (realpathSync(cwd) !== root) {
|
|
@@ -2554,12 +2588,12 @@ function defaultBranch(run, root) {
|
|
|
2554
2588
|
throw new LaunchError("Can't find the default branch of origin. Run `git remote set-head origin --auto`.");
|
|
2555
2589
|
}
|
|
2556
2590
|
function ensureWorktreesDir(run, root) {
|
|
2557
|
-
|
|
2591
|
+
mkdirSync11(join12(root, WORKTREES_DIR), { recursive: true });
|
|
2558
2592
|
if (git(run, root, ["check-ignore", "-q", `${WORKTREES_DIR}/`]).ok)
|
|
2559
2593
|
return;
|
|
2560
|
-
const info =
|
|
2561
|
-
|
|
2562
|
-
appendFileSync2(
|
|
2594
|
+
const info = join12(root, ".git", "info");
|
|
2595
|
+
mkdirSync11(info, { recursive: true });
|
|
2596
|
+
appendFileSync2(join12(info, "exclude"), `
|
|
2563
2597
|
${WORKTREES_DIR}/
|
|
2564
2598
|
`);
|
|
2565
2599
|
}
|
|
@@ -2837,20 +2871,20 @@ Usage:
|
|
|
2837
2871
|
|
|
2838
2872
|
// src/launch.ts
|
|
2839
2873
|
import { execSync as execSync3, spawn as spawn2, spawnSync as spawnSync3 } from "node:child_process";
|
|
2840
|
-
import { existsSync as existsSync5, rmSync as
|
|
2874
|
+
import { existsSync as existsSync5, rmSync as rmSync10 } from "node:fs";
|
|
2841
2875
|
import { constants as constants2, homedir as homedir3 } from "node:os";
|
|
2842
|
-
import { join as
|
|
2876
|
+
import { join as join16 } from "node:path";
|
|
2843
2877
|
|
|
2844
2878
|
// src/backup.ts
|
|
2845
2879
|
import { createHash } from "node:crypto";
|
|
2846
|
-
import { mkdirSync as
|
|
2847
|
-
import { basename, join as
|
|
2880
|
+
import { mkdirSync as mkdirSync12, readdirSync as readdirSync5, rmSync as rmSync8 } from "node:fs";
|
|
2881
|
+
import { basename, join as join13 } from "node:path";
|
|
2848
2882
|
var BACKUPS_KEPT = 5;
|
|
2849
2883
|
function shortHash(value) {
|
|
2850
2884
|
return createHash("sha256").update(value).digest("hex").slice(0, 8);
|
|
2851
2885
|
}
|
|
2852
2886
|
function backupDir(repoRoot, home = maesttroHome()) {
|
|
2853
|
-
return
|
|
2887
|
+
return join13(home, "backups", `${basename(repoRoot)}-${shortHash(repoRoot)}`);
|
|
2854
2888
|
}
|
|
2855
2889
|
function refsFingerprint(refs) {
|
|
2856
2890
|
const sorted = Object.keys(refs).sort().map((ref) => `${ref} ${refs[ref]}`);
|
|
@@ -2868,40 +2902,40 @@ function backupRepo({
|
|
|
2868
2902
|
}) {
|
|
2869
2903
|
if (Object.keys(refs).length === 0)
|
|
2870
2904
|
return;
|
|
2871
|
-
|
|
2905
|
+
mkdirSync12(dir, { recursive: true, mode: 448 });
|
|
2872
2906
|
const fingerprint = refsFingerprint(refs);
|
|
2873
|
-
const existing =
|
|
2907
|
+
const existing = readdirSync5(dir).filter((name) => name.endsWith(".bundle")).sort();
|
|
2874
2908
|
const latest = existing.at(-1);
|
|
2875
2909
|
if (latest?.endsWith(`-${fingerprint}.bundle`)) {
|
|
2876
|
-
return { path:
|
|
2910
|
+
return { path: join13(dir, latest), reused: true };
|
|
2877
2911
|
}
|
|
2878
2912
|
const stamp = now.toISOString().replace(/[:.]/g, "-");
|
|
2879
|
-
const path =
|
|
2913
|
+
const path = join13(dir, `${stamp}-${sessionId}-${fingerprint}.bundle`);
|
|
2880
2914
|
if (!git(run, repoRoot, ["bundle", "create", "--quiet", path, "--all"]).ok) {
|
|
2881
2915
|
return;
|
|
2882
2916
|
}
|
|
2883
2917
|
for (const stale of [...existing, basename(path)].sort().slice(0, -keep)) {
|
|
2884
|
-
|
|
2918
|
+
rmSync8(join13(dir, stale), { force: true });
|
|
2885
2919
|
}
|
|
2886
2920
|
return { path, reused: false };
|
|
2887
2921
|
}
|
|
2888
2922
|
|
|
2889
2923
|
// src/session-dir.ts
|
|
2890
2924
|
import {
|
|
2891
|
-
chmodSync as
|
|
2892
|
-
mkdirSync as
|
|
2925
|
+
chmodSync as chmodSync6,
|
|
2926
|
+
mkdirSync as mkdirSync13,
|
|
2893
2927
|
mkdtempSync as mkdtempSync3,
|
|
2894
2928
|
renameSync as renameSync7,
|
|
2895
|
-
rmSync as
|
|
2929
|
+
rmSync as rmSync9,
|
|
2896
2930
|
writeFileSync as writeFileSync10
|
|
2897
2931
|
} from "node:fs";
|
|
2898
2932
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
2899
|
-
import { join as
|
|
2933
|
+
import { join as join14 } from "node:path";
|
|
2900
2934
|
var CONTAINER_GH_CONFIG_DIR = "/home/agent/.config/gh-maesttro";
|
|
2901
2935
|
function writeAtomically(path, content) {
|
|
2902
2936
|
const temporary = `${path}.tmp`;
|
|
2903
2937
|
writeFileSync10(temporary, content, { mode: 420 });
|
|
2904
|
-
|
|
2938
|
+
chmodSync6(temporary, 420);
|
|
2905
2939
|
renameSync7(temporary, path);
|
|
2906
2940
|
}
|
|
2907
2941
|
function hostsYaml(token, user) {
|
|
@@ -2915,19 +2949,19 @@ function hostsYaml(token, user) {
|
|
|
2915
2949
|
`);
|
|
2916
2950
|
}
|
|
2917
2951
|
function createSessionDir(parent = tmpdir3()) {
|
|
2918
|
-
const root = mkdtempSync3(
|
|
2919
|
-
|
|
2920
|
-
const ghDir =
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
writeAtomically(
|
|
2952
|
+
const root = mkdtempSync3(join14(parent, "maesttro-"));
|
|
2953
|
+
chmodSync6(root, 448);
|
|
2954
|
+
const ghDir = join14(root, "gh");
|
|
2955
|
+
mkdirSync13(ghDir);
|
|
2956
|
+
chmodSync6(ghDir, 493);
|
|
2957
|
+
writeAtomically(join14(ghDir, "config.yml"), `version: "1"
|
|
2924
2958
|
`);
|
|
2925
2959
|
return {
|
|
2926
2960
|
ghDir,
|
|
2927
|
-
writeToken: (token, user) => writeAtomically(
|
|
2928
|
-
writeStatus: (status) => writeAtomically(
|
|
2961
|
+
writeToken: (token, user) => writeAtomically(join14(ghDir, "hosts.yml"), hostsYaml(token, user)),
|
|
2962
|
+
writeStatus: (status) => writeAtomically(join14(ghDir, "status"), `${status}
|
|
2929
2963
|
`),
|
|
2930
|
-
remove: () =>
|
|
2964
|
+
remove: () => rmSync9(root, { recursive: true, force: true })
|
|
2931
2965
|
};
|
|
2932
2966
|
}
|
|
2933
2967
|
|
|
@@ -3078,15 +3112,15 @@ function ask(question) {
|
|
|
3078
3112
|
|
|
3079
3113
|
// src/session-clone.ts
|
|
3080
3114
|
import {
|
|
3081
|
-
copyFileSync,
|
|
3115
|
+
copyFileSync as copyFileSync2,
|
|
3082
3116
|
existsSync as existsSync4,
|
|
3083
|
-
lstatSync as
|
|
3084
|
-
mkdirSync as
|
|
3085
|
-
readdirSync as
|
|
3117
|
+
lstatSync as lstatSync5,
|
|
3118
|
+
mkdirSync as mkdirSync14,
|
|
3119
|
+
readdirSync as readdirSync6,
|
|
3086
3120
|
readFileSync as readFileSync11,
|
|
3087
3121
|
writeFileSync as writeFileSync11
|
|
3088
3122
|
} from "node:fs";
|
|
3089
|
-
import { join as
|
|
3123
|
+
import { join as join15 } from "node:path";
|
|
3090
3124
|
|
|
3091
3125
|
// src/sanitize.ts
|
|
3092
3126
|
function stripControl(text) {
|
|
@@ -3173,25 +3207,25 @@ function createSessionClone(run, root, clonePath, baseSha) {
|
|
|
3173
3207
|
}
|
|
3174
3208
|
function realDir(path) {
|
|
3175
3209
|
try {
|
|
3176
|
-
return
|
|
3210
|
+
return lstatSync5(path).isDirectory();
|
|
3177
3211
|
} catch {
|
|
3178
3212
|
return false;
|
|
3179
3213
|
}
|
|
3180
3214
|
}
|
|
3181
3215
|
function dirents(dir) {
|
|
3182
3216
|
try {
|
|
3183
|
-
return
|
|
3217
|
+
return readdirSync6(dir, { withFileTypes: true });
|
|
3184
3218
|
} catch {
|
|
3185
3219
|
return [];
|
|
3186
3220
|
}
|
|
3187
3221
|
}
|
|
3188
3222
|
function cloneGitDir(clonePath) {
|
|
3189
|
-
const gitDir =
|
|
3223
|
+
const gitDir = join15(clonePath, ".git");
|
|
3190
3224
|
return realDir(gitDir) ? gitDir : undefined;
|
|
3191
3225
|
}
|
|
3192
3226
|
function readSha(path) {
|
|
3193
3227
|
try {
|
|
3194
|
-
if (!
|
|
3228
|
+
if (!lstatSync5(path).isFile())
|
|
3195
3229
|
return;
|
|
3196
3230
|
const text = readFileSync11(path, "utf8").trim();
|
|
3197
3231
|
return SHA.test(text) ? text : undefined;
|
|
@@ -3203,7 +3237,7 @@ function packedRefs(gitDir) {
|
|
|
3203
3237
|
const refs = new Map;
|
|
3204
3238
|
let text;
|
|
3205
3239
|
try {
|
|
3206
|
-
text = readFileSync11(
|
|
3240
|
+
text = readFileSync11(join15(gitDir, "packed-refs"), "utf8");
|
|
3207
3241
|
} catch {
|
|
3208
3242
|
return refs;
|
|
3209
3243
|
}
|
|
@@ -3228,12 +3262,12 @@ function cloneBranches(clonePath) {
|
|
|
3228
3262
|
continue;
|
|
3229
3263
|
const name = prefix ? `${prefix}/${entry.name}` : entry.name;
|
|
3230
3264
|
if (entry.isDirectory())
|
|
3231
|
-
walk(
|
|
3265
|
+
walk(join15(dir, entry.name), name);
|
|
3232
3266
|
else if (entry.isFile())
|
|
3233
3267
|
names.add(name);
|
|
3234
3268
|
}
|
|
3235
3269
|
};
|
|
3236
|
-
walk(
|
|
3270
|
+
walk(join15(gitDir, "refs", "heads"), "");
|
|
3237
3271
|
for (const ref of packedRefs(gitDir).keys()) {
|
|
3238
3272
|
if (ref.startsWith(HEADS_PREFIX))
|
|
3239
3273
|
names.add(ref.slice(HEADS_PREFIX.length));
|
|
@@ -3246,7 +3280,7 @@ function branchTip(clonePath, branch) {
|
|
|
3246
3280
|
const gitDir = cloneGitDir(clonePath);
|
|
3247
3281
|
if (!gitDir)
|
|
3248
3282
|
return;
|
|
3249
|
-
return readSha(
|
|
3283
|
+
return readSha(join15(gitDir, "refs", "heads", branch)) ?? packedRefs(gitDir).get(`${HEADS_PREFIX}${branch}`);
|
|
3250
3284
|
}
|
|
3251
3285
|
function cloneHead(clonePath) {
|
|
3252
3286
|
const gitDir = cloneGitDir(clonePath);
|
|
@@ -3254,9 +3288,9 @@ function cloneHead(clonePath) {
|
|
|
3254
3288
|
return;
|
|
3255
3289
|
let text;
|
|
3256
3290
|
try {
|
|
3257
|
-
if (!
|
|
3291
|
+
if (!lstatSync5(join15(gitDir, "HEAD")).isFile())
|
|
3258
3292
|
return;
|
|
3259
|
-
text = readFileSync11(
|
|
3293
|
+
text = readFileSync11(join15(gitDir, "HEAD"), "utf8").trim();
|
|
3260
3294
|
} catch {
|
|
3261
3295
|
return;
|
|
3262
3296
|
}
|
|
@@ -3277,29 +3311,29 @@ function copyObjectFiles(fromDir, intoDir) {
|
|
|
3277
3311
|
for (const entry of dirents(fromDir)) {
|
|
3278
3312
|
if (!entry.isFile())
|
|
3279
3313
|
continue;
|
|
3280
|
-
const dest =
|
|
3314
|
+
const dest = join15(intoDir, entry.name);
|
|
3281
3315
|
if (existsSync4(dest))
|
|
3282
3316
|
continue;
|
|
3283
3317
|
if (!created) {
|
|
3284
|
-
|
|
3318
|
+
mkdirSync14(intoDir, { recursive: true });
|
|
3285
3319
|
created = true;
|
|
3286
3320
|
}
|
|
3287
|
-
|
|
3321
|
+
copyFileSync2(join15(fromDir, entry.name), dest);
|
|
3288
3322
|
}
|
|
3289
3323
|
}
|
|
3290
3324
|
function pinAlternates(clonePath, containerObjects) {
|
|
3291
3325
|
const gitDir = cloneGitDir(clonePath);
|
|
3292
3326
|
if (!gitDir)
|
|
3293
3327
|
return;
|
|
3294
|
-
const objects =
|
|
3295
|
-
const info =
|
|
3328
|
+
const objects = join15(gitDir, "objects");
|
|
3329
|
+
const info = join15(objects, "info");
|
|
3296
3330
|
if (!realDir(objects) || !realDir(info)) {
|
|
3297
3331
|
throw new LaunchError(`Unsafe objects tree in the session clone: ${clonePath}`);
|
|
3298
3332
|
}
|
|
3299
|
-
const alternates =
|
|
3333
|
+
const alternates = join15(info, "alternates");
|
|
3300
3334
|
let stat;
|
|
3301
3335
|
try {
|
|
3302
|
-
stat =
|
|
3336
|
+
stat = lstatSync5(alternates);
|
|
3303
3337
|
} catch {
|
|
3304
3338
|
return;
|
|
3305
3339
|
}
|
|
@@ -3316,17 +3350,17 @@ function importObjects(run, root, clonePath, tip) {
|
|
|
3316
3350
|
const gitDir = cloneGitDir(clonePath);
|
|
3317
3351
|
if (!gitDir)
|
|
3318
3352
|
return false;
|
|
3319
|
-
const objects =
|
|
3353
|
+
const objects = join15(gitDir, "objects");
|
|
3320
3354
|
if (!realDir(objects))
|
|
3321
3355
|
return false;
|
|
3322
|
-
const into =
|
|
3356
|
+
const into = join15(root, ".git", "objects");
|
|
3323
3357
|
for (const entry of dirents(objects)) {
|
|
3324
3358
|
if (entry.name === "info" || entry.isSymbolicLink() || !entry.isDirectory()) {
|
|
3325
3359
|
continue;
|
|
3326
3360
|
}
|
|
3327
3361
|
if (!OBJECT_SUBDIR.test(entry.name) && entry.name !== "pack")
|
|
3328
3362
|
continue;
|
|
3329
|
-
copyObjectFiles(
|
|
3363
|
+
copyObjectFiles(join15(objects, entry.name), join15(into, entry.name));
|
|
3330
3364
|
}
|
|
3331
3365
|
return git(run, root, ["cat-file", "-e", `${tip}^{commit}`]).ok;
|
|
3332
3366
|
}
|
|
@@ -3381,6 +3415,20 @@ function resolveCommand(args) {
|
|
|
3381
3415
|
function containerCommand(command) {
|
|
3382
3416
|
return `node ${CONTAINER_PACKAGE}/dist/maesttro.js configure || echo "maesttro: in-container setup failed" >&2; ${command}`;
|
|
3383
3417
|
}
|
|
3418
|
+
function sessionEnvFlags({ repo }) {
|
|
3419
|
+
const names = [
|
|
3420
|
+
"MAESTTRO_SESSION_ID",
|
|
3421
|
+
"MAESTTRO_BASE",
|
|
3422
|
+
"MAESTTRO_GITHUB",
|
|
3423
|
+
"MAESTTRO_HOST_UID",
|
|
3424
|
+
"MAESTTRO_HOST_GID",
|
|
3425
|
+
...repo ? ["MAESTTRO_REPO"] : [],
|
|
3426
|
+
"COLORTERM",
|
|
3427
|
+
"TERM_PROGRAM",
|
|
3428
|
+
"TERM=xterm-256color"
|
|
3429
|
+
];
|
|
3430
|
+
return names.flatMap((name) => ["-e", name]);
|
|
3431
|
+
}
|
|
3384
3432
|
function sessionMounts({
|
|
3385
3433
|
repoRoot,
|
|
3386
3434
|
sessionDir,
|
|
@@ -3392,7 +3440,7 @@ function sessionMounts({
|
|
|
3392
3440
|
home,
|
|
3393
3441
|
claudeDir
|
|
3394
3442
|
}) {
|
|
3395
|
-
const objects =
|
|
3443
|
+
const objects = join16(repoRoot, ".git", "objects");
|
|
3396
3444
|
const mounts = [
|
|
3397
3445
|
{ host: home, container: "/home/agent" },
|
|
3398
3446
|
{ host: claudeDir, container: "/home/agent/.claude" },
|
|
@@ -3400,7 +3448,7 @@ function sessionMounts({
|
|
|
3400
3448
|
{ host: sessionDir, container: CONTAINER_REPO },
|
|
3401
3449
|
{ host: packageRoot, container: CONTAINER_PACKAGE, readOnly: true },
|
|
3402
3450
|
...stagedPaths.map((path) => ({
|
|
3403
|
-
host:
|
|
3451
|
+
host: join16(hostHome, path),
|
|
3404
3452
|
container: `${HOST_STAGING_DIR}/${path}`,
|
|
3405
3453
|
readOnly: true
|
|
3406
3454
|
})),
|
|
@@ -3503,7 +3551,7 @@ async function establishClaudeCredentials({
|
|
|
3503
3551
|
}
|
|
3504
3552
|
}
|
|
3505
3553
|
function sharedSkillsDir() {
|
|
3506
|
-
const dir = process.env.AGENT_SHARED_SKILLS ??
|
|
3554
|
+
const dir = process.env.AGENT_SHARED_SKILLS ?? join16(homedir3(), "agent-shared", "skills");
|
|
3507
3555
|
return existsSync5(dir) ? dir : undefined;
|
|
3508
3556
|
}
|
|
3509
3557
|
async function resolveAuthor(workspace) {
|
|
@@ -3533,7 +3581,7 @@ function startSession(run, root) {
|
|
|
3533
3581
|
if (!baseSha)
|
|
3534
3582
|
throw new LaunchError(`Can't resolve ${base}.`);
|
|
3535
3583
|
const id = newSessionId();
|
|
3536
|
-
const sessionDir =
|
|
3584
|
+
const sessionDir = join16(root, WORKTREES_DIR, id);
|
|
3537
3585
|
createSessionClone(run, root, sessionDir, baseSha);
|
|
3538
3586
|
return {
|
|
3539
3587
|
id,
|
|
@@ -3579,7 +3627,7 @@ function finishSession(run, session) {
|
|
|
3579
3627
|
}
|
|
3580
3628
|
const preserved = preserveBranches(run, session);
|
|
3581
3629
|
if (!cloneHasWork(session.worktree, session.baseSha)) {
|
|
3582
|
-
|
|
3630
|
+
rmSync10(session.worktree, { recursive: true, force: true });
|
|
3583
3631
|
deleteSession(session.id);
|
|
3584
3632
|
return;
|
|
3585
3633
|
}
|
|
@@ -3751,7 +3799,8 @@ Interrupted; cleaning up session ${saved.id}…`);
|
|
|
3751
3799
|
const clientConfigs = buildClientConfigs({
|
|
3752
3800
|
host: readHostClientConfigs({ home: hostHome }),
|
|
3753
3801
|
url,
|
|
3754
|
-
orchestratorWindow
|
|
3802
|
+
orchestratorWindow,
|
|
3803
|
+
isStrict: strict
|
|
3755
3804
|
});
|
|
3756
3805
|
if (clientConfigs.removed.length > 0) {
|
|
3757
3806
|
console.log(`Kept out of the runner as secrets: ${clientConfigs.removed.join(", ")}`);
|
|
@@ -3762,7 +3811,7 @@ Interrupted; cleaning up session ${saved.id}…`);
|
|
|
3762
3811
|
sessionDir: saved.worktree,
|
|
3763
3812
|
packageRoot: PACKAGE_ROOT,
|
|
3764
3813
|
hostHome,
|
|
3765
|
-
stagedPaths: STAGED_PATHS.filter((path) => existsSync5(
|
|
3814
|
+
stagedPaths: STAGED_PATHS.filter((path) => existsSync5(join16(hostHome, path))),
|
|
3766
3815
|
clientConfigs: clientConfigDir.dir,
|
|
3767
3816
|
sharedSkills: sharedSkillsDir(),
|
|
3768
3817
|
home,
|
|
@@ -3776,13 +3825,6 @@ Interrupted; cleaning up session ${saved.id}…`);
|
|
|
3776
3825
|
...hostIdentityEnv(),
|
|
3777
3826
|
...repo ? { MAESTTRO_REPO: repo } : {}
|
|
3778
3827
|
};
|
|
3779
|
-
const envFlags = [
|
|
3780
|
-
"MAESTTRO_SESSION_ID",
|
|
3781
|
-
"MAESTTRO_BASE",
|
|
3782
|
-
"MAESTTRO_GITHUB",
|
|
3783
|
-
"MAESTTRO_HOST_UID",
|
|
3784
|
-
"MAESTTRO_HOST_GID"
|
|
3785
|
-
].concat(repo ? ["MAESTTRO_REPO"] : []).flatMap((name) => ["-e", name]);
|
|
3786
3828
|
clearStaleContainer(saved.id);
|
|
3787
3829
|
pinAlternates(saved.worktree, CONTAINER_GIT_OBJECTS);
|
|
3788
3830
|
const child = spawn2("docker", dockerRunArgs({
|
|
@@ -3790,7 +3832,7 @@ Interrupted; cleaning up session ${saved.id}…`);
|
|
|
3790
3832
|
mounts,
|
|
3791
3833
|
workdir,
|
|
3792
3834
|
name: saved.id,
|
|
3793
|
-
extra: [...github.dockerArgs, ...
|
|
3835
|
+
extra: [...github.dockerArgs, ...sessionEnvFlags({ repo })]
|
|
3794
3836
|
}), { stdio: "inherit", env: { ...process.env, ...sessionEnv } });
|
|
3795
3837
|
interrupts.dispose();
|
|
3796
3838
|
claudeNotices.quiet();
|
|
@@ -5019,7 +5061,7 @@ async function push({ args }) {
|
|
|
5019
5061
|
}
|
|
5020
5062
|
|
|
5021
5063
|
// src/sessions-command.ts
|
|
5022
|
-
import { existsSync as existsSync6, rmSync as
|
|
5064
|
+
import { existsSync as existsSync6, rmSync as rmSync11 } from "node:fs";
|
|
5023
5065
|
function parseRemoveArgs(args) {
|
|
5024
5066
|
let all = false;
|
|
5025
5067
|
let force = false;
|
|
@@ -5053,7 +5095,7 @@ async function removeSession(run, session, force, ask, interactive) {
|
|
|
5053
5095
|
return "kept";
|
|
5054
5096
|
}
|
|
5055
5097
|
}
|
|
5056
|
-
|
|
5098
|
+
rmSync11(session.worktree, { recursive: true, force: true });
|
|
5057
5099
|
const kept = preserved.length > 0 ? ` Its branches are kept as refs/maesttro/${session.id}/*.` : "";
|
|
5058
5100
|
console.log(`Removed session ${session.id}.${kept}`);
|
|
5059
5101
|
} else {
|
|
@@ -5245,10 +5287,10 @@ function removeStaleRunnerImages({
|
|
|
5245
5287
|
// src/version-check.ts
|
|
5246
5288
|
import { execFile } from "node:child_process";
|
|
5247
5289
|
import { existsSync as existsSync7, readFileSync as readFileSync12 } from "node:fs";
|
|
5248
|
-
import { join as
|
|
5290
|
+
import { join as join17 } from "node:path";
|
|
5249
5291
|
var LOOKUP_TIMEOUT_MS = 3000;
|
|
5250
5292
|
function installedManifest() {
|
|
5251
|
-
return JSON.parse(readFileSync12(
|
|
5293
|
+
return JSON.parse(readFileSync12(join17(PACKAGE_ROOT, "package.json"), "utf8"));
|
|
5252
5294
|
}
|
|
5253
5295
|
function canOfferUpdate({
|
|
5254
5296
|
interactive = Boolean(process.stdin.isTTY && process.stdout.isTTY),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "maesttro",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.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": {
|