maesttro 0.4.2 → 0.4.3

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
@@ -189,7 +189,7 @@ Declining only skips it for that run; the next start asks again.
189
189
  |---|---|---|
190
190
  | `/home/agent` | `maesttro-home` named volume | Per-client session state. Survives across runs, lives only in Docker. Holds no GitHub credential. |
191
191
  | `/home/agent/.config/gh-maesttro` (read-only) | per-session temp dir on host | The session's GitHub token (`hosts.yml`). The CLI refreshes it before its 1 h expiry and revokes it on exit. Not mounted in `--strict` sessions, or when GitHub access is unavailable. |
192
- | `<repo>/.git/objects` (read-only, same absolute path) | your repo's object store | Base history, borrowed by the clone through its `alternates`. The real `.git` is never mounted. |
192
+ | `/opt/maesttro-objects` (read-only) | your repo's `.git/objects` | Base history, borrowed by the clone through its `alternates`, which is pinned to this fixed path so it resolves the same on every host OS. The real `.git` is never mounted. |
193
193
  | `/workspace` | the session clone (`.worktrees/<id>`) | The working directory: a `git clone --shared` detached at the default branch. |
194
194
  | `/opt/maesttro`, `/opt/maesttro-host` (read-only) | this package; your instruction files | Used by `maesttro configure` at container start. |
195
195
  | `/home/agent/.claude/skills` (read-only) | `~/agent-shared/skills` on host, if present | Reusable skills/house-style files shared across sessions. Read-only. |
package/dist/maesttro.js CHANGED
@@ -1611,7 +1611,10 @@ async function openGitHubSession({
1611
1611
  ...identityEnv(author, initial.committer)
1612
1612
  };
1613
1613
  return {
1614
- dockerArgs: withEnvFlags(["-v", `${dir.ghDir}:${CONTAINER_GH_CONFIG_DIR}:ro`], env),
1614
+ dockerArgs: withEnvFlags([
1615
+ "--mount",
1616
+ `type=bind,source=${dir.ghDir},target=${CONTAINER_GH_CONFIG_DIR},readonly`
1617
+ ], env),
1615
1618
  env,
1616
1619
  close: async () => {
1617
1620
  refresher.stop();
@@ -1642,7 +1645,8 @@ import {
1642
1645
  lstatSync,
1643
1646
  mkdirSync as mkdirSync9,
1644
1647
  readdirSync as readdirSync3,
1645
- readFileSync as readFileSync6
1648
+ readFileSync as readFileSync6,
1649
+ writeFileSync as writeFileSync7
1646
1650
  } from "node:fs";
1647
1651
  import { join as join9 } from "node:path";
1648
1652
 
@@ -1837,6 +1841,31 @@ function copyObjectFiles(fromDir, intoDir) {
1837
1841
  copyFileSync(join9(fromDir, entry.name), dest);
1838
1842
  }
1839
1843
  }
1844
+ function pinAlternates(clonePath, containerObjects) {
1845
+ const gitDir = cloneGitDir(clonePath);
1846
+ if (!gitDir)
1847
+ return;
1848
+ const objects = join9(gitDir, "objects");
1849
+ const info = join9(objects, "info");
1850
+ if (!realDir(objects) || !realDir(info)) {
1851
+ throw new LaunchError(`Unsafe objects tree in the session clone: ${clonePath}`);
1852
+ }
1853
+ const alternates = join9(info, "alternates");
1854
+ let stat;
1855
+ try {
1856
+ stat = lstatSync(alternates);
1857
+ } catch {
1858
+ return;
1859
+ }
1860
+ if (!stat.isFile()) {
1861
+ throw new LaunchError(`Unsafe alternates file in the session clone: ${clonePath}`);
1862
+ }
1863
+ const desired = `${containerObjects}
1864
+ `;
1865
+ if (readFileSync6(alternates, "utf8") !== desired) {
1866
+ writeFileSync7(alternates, desired);
1867
+ }
1868
+ }
1840
1869
  function importObjects(run, root, clonePath, tip) {
1841
1870
  const gitDir = cloneGitDir(clonePath);
1842
1871
  if (!gitDir)
@@ -1864,7 +1893,7 @@ import {
1864
1893
  readFileSync as readFileSync7,
1865
1894
  renameSync as renameSync4,
1866
1895
  rmSync as rmSync5,
1867
- writeFileSync as writeFileSync7
1896
+ writeFileSync as writeFileSync8
1868
1897
  } from "node:fs";
1869
1898
  import { join as join10 } from "node:path";
1870
1899
  function newSessionId() {
@@ -1876,7 +1905,7 @@ function sessionsDir(home = maesttroHome()) {
1876
1905
  function saveSession(record, dir = sessionsDir()) {
1877
1906
  mkdirSync10(dir, { recursive: true, mode: 448 });
1878
1907
  const path = join10(dir, `${record.id}.json`);
1879
- writeFileSync7(`${path}.tmp`, `${JSON.stringify(record, null, 2)}
1908
+ writeFileSync8(`${path}.tmp`, `${JSON.stringify(record, null, 2)}
1880
1909
  `);
1881
1910
  renameSync4(`${path}.tmp`, path);
1882
1911
  }
@@ -1929,6 +1958,7 @@ function workspaceAuthor(run, workspace) {
1929
1958
  // src/launch.ts
1930
1959
  var CONTAINER_REPO = "/workspace";
1931
1960
  var CONTAINER_PACKAGE = "/opt/maesttro";
1961
+ var CONTAINER_GIT_OBJECTS = "/opt/maesttro-objects";
1932
1962
  function resolveCommand(args) {
1933
1963
  const [first, ...rest] = args;
1934
1964
  if (first === undefined)
@@ -1951,8 +1981,8 @@ function sessionMounts({
1951
1981
  }) {
1952
1982
  const objects = join11(repoRoot, ".git", "objects");
1953
1983
  const mounts = [
1954
- { host: VOLUME, container: "/home/agent" },
1955
- { host: objects, container: objects, readOnly: true },
1984
+ { host: VOLUME, container: "/home/agent", type: "volume" },
1985
+ { host: objects, container: CONTAINER_GIT_OBJECTS, readOnly: true },
1956
1986
  { host: sessionDir, container: CONTAINER_REPO },
1957
1987
  { host: packageRoot, container: CONTAINER_PACKAGE, readOnly: true },
1958
1988
  ...stagedPaths.map((path) => ({
@@ -1982,9 +2012,9 @@ function dockerRunArgs({
1982
2012
  extra = [],
1983
2013
  name
1984
2014
  }) {
1985
- const volumes = mounts.flatMap(({ host, container, readOnly }) => [
1986
- "-v",
1987
- `${host}:${container}${readOnly ? ":ro" : ""}`
2015
+ const volumes = mounts.flatMap(({ host, container, readOnly, type = "bind" }) => [
2016
+ "--mount",
2017
+ `type=${type},source=${host},target=${container}${readOnly ? ",readonly" : ""}`
1988
2018
  ]);
1989
2019
  return [
1990
2020
  "run",
@@ -2222,6 +2252,7 @@ async function launch({
2222
2252
  "MAESTTRO_HOST_GID"
2223
2253
  ].concat(repo ? ["MAESTTRO_REPO"] : []).flatMap((name) => ["-e", name]);
2224
2254
  clearStaleContainer(saved.id);
2255
+ pinAlternates(saved.worktree, CONTAINER_GIT_OBJECTS);
2225
2256
  const child = spawn("docker", dockerRunArgs({
2226
2257
  command: containerCommand(resolveCommand(args)),
2227
2258
  mounts,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "maesttro",
3
- "version": "0.4.2",
3
+ "version": "0.4.3",
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": {