maesttro 0.6.0 → 0.6.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.
Files changed (3) hide show
  1. package/README.md +2 -2
  2. package/dist/maesttro.js +167 -125
  3. 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 `maesttro` provider (Orchestrate) at `/v1`, a `dgx-spark` provider
157
- (lorem direct on the DGX), compaction, default model `maesttro/maesttro`, and
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 mkdirSync9,
1877
- readdirSync as readdirSync3,
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 dirname6, join as join10 } from "node:path";
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 chmodSync4,
1928
- cpSync,
1926
+ chmodSync as chmodSync5,
1929
1927
  existsSync as existsSync2,
1930
- mkdirSync as mkdirSync8,
1928
+ mkdirSync as mkdirSync9,
1931
1929
  mkdtempSync as mkdtempSync2,
1932
1930
  readFileSync as readFileSync9,
1933
- rmSync as rmSync6,
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 dirname5, join as join9 } from "node:path";
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 = "maesttro";
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";
@@ -1991,10 +2025,10 @@ function opencodeProviders(url) {
1991
2025
  return {
1992
2026
  [PROVIDER_ID]: {
1993
2027
  npm: "@ai-sdk/openai-compatible",
1994
- name: "Maesttro",
2028
+ name: "Esatto",
1995
2029
  options: { baseURL: `${url}/v1`, apiKey: MAESTTRO_API_KEY },
1996
2030
  models: {
1997
- [ORCHESTRATOR_MODEL]: { name: "Orchestrate", ...IMAGE_INPUT }
2031
+ [ORCHESTRATOR_MODEL]: { name: "Maesttro", ...IMAGE_INPUT }
1998
2032
  }
1999
2033
  },
2000
2034
  [DGX_SPARK_PROVIDER_ID]: {
@@ -2003,7 +2037,7 @@ function opencodeProviders(url) {
2003
2037
  options: { baseURL: `${DGX_SPARK_URL}/v1`, apiKey: "" },
2004
2038
  models: {
2005
2039
  "maesttro/lorem": {
2006
- name: "Qwen3.8 27B",
2040
+ name: "Lorem",
2007
2041
  ...IMAGE_INPUT,
2008
2042
  limit: { context: 131072, output: 32768 }
2009
2043
  }
@@ -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 = join9(home, file);
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(join9(parent, "maesttro-clients-"));
2189
- chmodSync4(root, 448);
2190
- const dir = join9(root, "home");
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 = join9(dir, file);
2193
- mkdirSync8(dirname5(path), { recursive: true, mode: 493 });
2230
+ const path = join10(dir, file);
2231
+ mkdirSync9(dirname6(path), { recursive: true, mode: 493 });
2194
2232
  writeFileSync8(path, content);
2195
- chmodSync4(path, 420);
2233
+ chmodSync5(path, 420);
2196
2234
  }
2197
- chmodSync4(dir, 493);
2198
- return { dir, remove: () => rmSync6(root, { recursive: true, force: true }) };
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) => join9(source, file));
2207
- cpSync(source, home, {
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 = join9(source, file);
2247
+ const staged = join10(source, file);
2214
2248
  if (!existsSync2(staged))
2215
2249
  continue;
2216
- const target = join9(home, file);
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 = join9(home, OPENCODE_TUI_STATE);
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 = join10(DOCKERFILE_DIR, "configs")
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 = join10(configsDir, key);
2357
+ const source = join11(configsDir, key);
2324
2358
  if (!existsSync3(source))
2325
2359
  continue;
2326
- const targetDir = join10(home, client.configDir.replace(/^\/home\/agent\/?/, ""));
2327
- for (const name of readdirSync3(source)) {
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(join10(source, name)));
2364
+ const curated = withoutComments(readJson(join11(source, name)));
2331
2365
  if (Object.keys(curated).length === 0)
2332
2366
  continue;
2333
- const target = join10(targetDir, name);
2334
- mkdirSync9(targetDir, { recursive: true });
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 = join10(staging, directory);
2383
+ const source = join11(staging, directory);
2350
2384
  if (!existsSync3(source))
2351
2385
  continue;
2352
2386
  try {
2353
- cpSync2(source, join10(home, directory), { recursive: true, force: true });
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 = join10(staging, file);
2392
+ const source = join11(staging, file);
2359
2393
  const hostCopy = existsSync3(source) ? readFileSync10(source, "utf8") : "";
2360
- const target = join10(home, file);
2361
- mkdirSync9(dirname6(target), { recursive: true });
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 mkdirSync10, realpathSync } from "node:fs";
2487
- import { join as join11 } from "node:path";
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) !== join11(root, ".git")) {
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
- mkdirSync10(join11(root, WORKTREES_DIR), { recursive: true });
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 = join11(root, ".git", "info");
2561
- mkdirSync10(info, { recursive: true });
2562
- appendFileSync2(join11(info, "exclude"), `
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 rmSync9 } from "node:fs";
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 join15 } from "node:path";
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 mkdirSync11, readdirSync as readdirSync4, rmSync as rmSync7 } from "node:fs";
2847
- import { basename, join as join12 } from "node:path";
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 join12(home, "backups", `${basename(repoRoot)}-${shortHash(repoRoot)}`);
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
- mkdirSync11(dir, { recursive: true, mode: 448 });
2905
+ mkdirSync12(dir, { recursive: true, mode: 448 });
2872
2906
  const fingerprint = refsFingerprint(refs);
2873
- const existing = readdirSync4(dir).filter((name) => name.endsWith(".bundle")).sort();
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: join12(dir, latest), reused: true };
2910
+ return { path: join13(dir, latest), reused: true };
2877
2911
  }
2878
2912
  const stamp = now.toISOString().replace(/[:.]/g, "-");
2879
- const path = join12(dir, `${stamp}-${sessionId}-${fingerprint}.bundle`);
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
- rmSync7(join12(dir, stale), { force: true });
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 chmodSync5,
2892
- mkdirSync as mkdirSync12,
2925
+ chmodSync as chmodSync6,
2926
+ mkdirSync as mkdirSync13,
2893
2927
  mkdtempSync as mkdtempSync3,
2894
2928
  renameSync as renameSync7,
2895
- rmSync as rmSync8,
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 join13 } from "node:path";
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
- chmodSync5(temporary, 420);
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(join13(parent, "maesttro-"));
2919
- chmodSync5(root, 448);
2920
- const ghDir = join13(root, "gh");
2921
- mkdirSync12(ghDir);
2922
- chmodSync5(ghDir, 493);
2923
- writeAtomically(join13(ghDir, "config.yml"), `version: "1"
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(join13(ghDir, "hosts.yml"), hostsYaml(token, user)),
2928
- writeStatus: (status) => writeAtomically(join13(ghDir, "status"), `${status}
2961
+ writeToken: (token, user) => writeAtomically(join14(ghDir, "hosts.yml"), hostsYaml(token, user)),
2962
+ writeStatus: (status) => writeAtomically(join14(ghDir, "status"), `${status}
2929
2963
  `),
2930
- remove: () => rmSync8(root, { recursive: true, force: true })
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 lstatSync4,
3084
- mkdirSync as mkdirSync13,
3085
- readdirSync as readdirSync5,
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 join14 } from "node:path";
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 lstatSync4(path).isDirectory();
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 readdirSync5(dir, { withFileTypes: true });
3217
+ return readdirSync6(dir, { withFileTypes: true });
3184
3218
  } catch {
3185
3219
  return [];
3186
3220
  }
3187
3221
  }
3188
3222
  function cloneGitDir(clonePath) {
3189
- const gitDir = join14(clonePath, ".git");
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 (!lstatSync4(path).isFile())
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(join14(gitDir, "packed-refs"), "utf8");
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(join14(dir, entry.name), name);
3265
+ walk(join15(dir, entry.name), name);
3232
3266
  else if (entry.isFile())
3233
3267
  names.add(name);
3234
3268
  }
3235
3269
  };
3236
- walk(join14(gitDir, "refs", "heads"), "");
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(join14(gitDir, "refs", "heads", branch)) ?? packedRefs(gitDir).get(`${HEADS_PREFIX}${branch}`);
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 (!lstatSync4(join14(gitDir, "HEAD")).isFile())
3291
+ if (!lstatSync5(join15(gitDir, "HEAD")).isFile())
3258
3292
  return;
3259
- text = readFileSync11(join14(gitDir, "HEAD"), "utf8").trim();
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 = join14(intoDir, entry.name);
3314
+ const dest = join15(intoDir, entry.name);
3281
3315
  if (existsSync4(dest))
3282
3316
  continue;
3283
3317
  if (!created) {
3284
- mkdirSync13(intoDir, { recursive: true });
3318
+ mkdirSync14(intoDir, { recursive: true });
3285
3319
  created = true;
3286
3320
  }
3287
- copyFileSync(join14(fromDir, entry.name), dest);
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 = join14(gitDir, "objects");
3295
- const info = join14(objects, "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 = join14(info, "alternates");
3333
+ const alternates = join15(info, "alternates");
3300
3334
  let stat;
3301
3335
  try {
3302
- stat = lstatSync4(alternates);
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 = join14(gitDir, "objects");
3353
+ const objects = join15(gitDir, "objects");
3320
3354
  if (!realDir(objects))
3321
3355
  return false;
3322
- const into = join14(root, ".git", "objects");
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(join14(objects, entry.name), join14(into, entry.name));
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 = join15(repoRoot, ".git", "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: join15(hostHome, path),
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 ?? join15(homedir3(), "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 = join15(root, WORKTREES_DIR, id);
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
- rmSync9(session.worktree, { recursive: true, force: true });
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(join15(hostHome, path))),
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, ...envFlags]
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 rmSync10 } from "node:fs";
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
- rmSync10(session.worktree, { recursive: true, force: true });
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 join16 } from "node:path";
5290
+ import { join as join17 } from "node:path";
5249
5291
  var LOOKUP_TIMEOUT_MS = 3000;
5250
5292
  function installedManifest() {
5251
- return JSON.parse(readFileSync12(join16(PACKAGE_ROOT, "package.json"), "utf8"));
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.0",
3
+ "version": "0.6.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": {