maesttro 0.1.0 → 0.2.0
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 +109 -44
- package/dist/maesttro.js +2106 -0
- package/docker/Dockerfile +47 -10
- package/docker/entrypoint.sh +18 -0
- package/docker/gitconfig +8 -0
- package/docker/sandbox-marker +1 -0
- package/package.json +11 -4
- package/bin/maesttro.js +0 -49
- package/src/clients.js +0 -26
- package/src/doctor.js +0 -78
- package/src/launch.js +0 -68
- package/src/paths.js +0 -12
- package/src/setup.js +0 -101
- package/src/update.js +0 -22
package/docker/Dockerfile
CHANGED
|
@@ -1,10 +1,43 @@
|
|
|
1
|
+
# git is built from source: Debian ships 2.47 and the sandbox wants a recent
|
|
2
|
+
# git. Builder matches node:22-slim's Debian so the runtime libs line up.
|
|
3
|
+
FROM debian:bookworm-slim AS git-build
|
|
4
|
+
ARG GIT_VERSION=2.55.0
|
|
5
|
+
ARG GIT_SHA256=457fdb04dc8728e007d4688695e6912e6f680727920f2a40bf11eacc17505357
|
|
6
|
+
RUN apt-get update \
|
|
7
|
+
&& apt-get install -y --no-install-recommends ca-certificates curl xz-utils \
|
|
8
|
+
build-essential libcurl4-openssl-dev libexpat1-dev libssl-dev zlib1g-dev \
|
|
9
|
+
&& curl -fsSL -o /tmp/git.tar.xz \
|
|
10
|
+
"https://mirrors.edge.kernel.org/pub/software/scm/git/git-${GIT_VERSION}.tar.xz" \
|
|
11
|
+
&& echo "${GIT_SHA256} /tmp/git.tar.xz" | sha256sum -c - \
|
|
12
|
+
&& tar -xJf /tmp/git.tar.xz -C /tmp \
|
|
13
|
+
&& cd "/tmp/git-${GIT_VERSION}" \
|
|
14
|
+
&& make -j"$(nproc)" prefix=/usr/local sysconfdir=/etc NO_TCLTK=1 NO_PERL=1 NO_PYTHON=1 NO_GETTEXT=1 NO_RUST=1 \
|
|
15
|
+
DESTDIR=/out all install
|
|
16
|
+
|
|
1
17
|
FROM node:22-slim
|
|
2
18
|
|
|
3
|
-
|
|
4
|
-
|
|
19
|
+
COPY --from=git-build /out/usr/local /usr/local
|
|
20
|
+
|
|
21
|
+
# gh comes from GitHub's apt repo: Debian's lags by years and predates the
|
|
22
|
+
# hosts.yml / config.yml behaviour the CLI's read-only session mount relies on
|
|
23
|
+
RUN apt-get update \
|
|
24
|
+
&& apt-get install -y --no-install-recommends curl ca-certificates less libcurl4 libexpat1 \
|
|
25
|
+
&& install -m 0755 -d /etc/apt/keyrings \
|
|
26
|
+
&& curl -fsSL https://cli.github.com/packages/githubcli-archive-keyring.gpg \
|
|
27
|
+
-o /etc/apt/keyrings/githubcli-archive-keyring.gpg \
|
|
28
|
+
&& chmod go+r /etc/apt/keyrings/githubcli-archive-keyring.gpg \
|
|
29
|
+
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/githubcli-archive-keyring.gpg] https://cli.github.com/packages stable main" \
|
|
30
|
+
> /etc/apt/sources.list.d/github-cli.list \
|
|
31
|
+
&& apt-get update \
|
|
32
|
+
&& apt-get install -y --no-install-recommends gh \
|
|
5
33
|
&& rm -rf /var/lib/apt/lists/*
|
|
6
34
|
|
|
7
|
-
#
|
|
35
|
+
# System-level, not ~/.gitconfig: /home/agent is the persistent volume and
|
|
36
|
+
# shadows anything baked into home for existing installs
|
|
37
|
+
COPY gitconfig /etc/gitconfig
|
|
38
|
+
COPY sandbox-marker /etc/maesttro/sandbox
|
|
39
|
+
|
|
40
|
+
# Curated client CLIs — keep in sync with src/clients.ts
|
|
8
41
|
RUN npm install -g \
|
|
9
42
|
@anthropic-ai/claude-code \
|
|
10
43
|
opencode-ai \
|
|
@@ -12,14 +45,18 @@ RUN npm install -g \
|
|
|
12
45
|
|
|
13
46
|
RUN useradd -m -s /bin/bash agent
|
|
14
47
|
USER agent
|
|
48
|
+
# Created as agent so a fresh volume inherits them; otherwise Docker creates
|
|
49
|
+
# ~/.config and ~/.claude as root when it sets up the nested read-only mounts
|
|
50
|
+
RUN mkdir -p /home/agent/.config/opencode /home/agent/.claude /home/agent/.codex
|
|
15
51
|
WORKDIR /workspace
|
|
16
52
|
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
COPY --chown=agent:agent configs/claude/ /home/agent/.claude/
|
|
21
|
-
COPY --chown=agent:agent configs/opencode/ /home/agent/.config/opencode/
|
|
22
|
-
COPY --chown=agent:agent configs/codex/ /home/agent/.codex/
|
|
53
|
+
# Client configs are applied at container start by `maesttro configure`
|
|
54
|
+
# (mounted from the host's install): /home/agent is a persistent volume, so
|
|
55
|
+
# anything baked here would only ever reach brand-new volumes.
|
|
23
56
|
|
|
24
|
-
|
|
57
|
+
# Starts as root only to remap agent to the host user's uid/gid (bind mounts
|
|
58
|
+
# keep host ownership on Linux), then drops to agent before running anything
|
|
59
|
+
USER root
|
|
60
|
+
COPY --chmod=755 entrypoint.sh /usr/local/bin/maesttro-entrypoint
|
|
61
|
+
ENTRYPOINT ["/usr/local/bin/maesttro-entrypoint"]
|
|
25
62
|
CMD ["claude"]
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
set -e
|
|
3
|
+
|
|
4
|
+
uid="${MAESTTRO_HOST_UID:-}"
|
|
5
|
+
gid="${MAESTTRO_HOST_GID:-$uid}"
|
|
6
|
+
|
|
7
|
+
if [ -n "$uid" ] && [ "$uid" != "$(id -u agent)" ]; then
|
|
8
|
+
groupmod --non-unique --gid "$gid" agent
|
|
9
|
+
usermod --non-unique --uid "$uid" --gid "$gid" agent
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
owner="$(id -u agent):$(id -g agent)"
|
|
13
|
+
if [ -n "$uid" ] && [ "$(stat -c %u:%g /home/agent)" != "$owner" ]; then
|
|
14
|
+
find /home/agent -xdev -exec chown -h "$owner" {} + 2>/dev/null || true
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
export HOME=/home/agent USER=agent LOGNAME=agent
|
|
18
|
+
exec setpriv --reuid=agent --regid=agent --init-groups /bin/bash -c "$*"
|
package/docker/gitconfig
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
maesttro sandbox
|
package/package.json
CHANGED
|
@@ -1,15 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "maesttro",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Isolated Docker sandbox for running local coding agents (claude, opencode, codex, ...) under a dedicated bot GitHub identity, with curated configs baked in per client.",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"bin": {
|
|
6
|
-
"maesttro": "
|
|
7
|
+
"maesttro": "dist/maesttro.js"
|
|
7
8
|
},
|
|
8
9
|
"files": [
|
|
9
|
-
"
|
|
10
|
-
"src",
|
|
10
|
+
"dist",
|
|
11
11
|
"docker"
|
|
12
12
|
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "bun build src/cli.ts --target node --format esm --outfile dist/maesttro.js",
|
|
15
|
+
"prepack": "bun run build",
|
|
16
|
+
"test": "bun test ./tests",
|
|
17
|
+
"typecheck": "tsc --noEmit -p tsconfig.json && tsc --noEmit -p tsconfig.test.json",
|
|
18
|
+
"clean": "rm -rf dist node_modules"
|
|
19
|
+
},
|
|
13
20
|
"repository": {
|
|
14
21
|
"type": "git",
|
|
15
22
|
"url": "git+https://github.com/esatto/maesttro.git",
|
package/bin/maesttro.js
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const { setup } = require("../src/setup")
|
|
4
|
-
const { doctor } = require("../src/doctor")
|
|
5
|
-
const { launch, list } = require("../src/launch")
|
|
6
|
-
const { update } = require("../src/update")
|
|
7
|
-
|
|
8
|
-
const [, , cmd, ...rest] = process.argv
|
|
9
|
-
|
|
10
|
-
const HELP = `maesttro — isolated sandbox for local coding agents
|
|
11
|
-
|
|
12
|
-
Usage:
|
|
13
|
-
maesttro Launch the default client (claude) in the sandbox
|
|
14
|
-
maesttro <client> Launch a specific client — see \`maesttro list\`
|
|
15
|
-
maesttro list Show available clients
|
|
16
|
-
maesttro setup [token] One-time: build image, create volume, authenticate bot identity
|
|
17
|
-
maesttro update Update the package and rebuild the image with refreshed client configs
|
|
18
|
-
maesttro doctor Health check — Docker, image, volume, bot auth, isolation, clients
|
|
19
|
-
maesttro help Show this message
|
|
20
|
-
`
|
|
21
|
-
|
|
22
|
-
async function main() {
|
|
23
|
-
switch (cmd) {
|
|
24
|
-
case undefined:
|
|
25
|
-
launch([])
|
|
26
|
-
break
|
|
27
|
-
case "setup":
|
|
28
|
-
await setup(rest)
|
|
29
|
-
break
|
|
30
|
-
case "update":
|
|
31
|
-
update()
|
|
32
|
-
break
|
|
33
|
-
case "doctor":
|
|
34
|
-
doctor()
|
|
35
|
-
break
|
|
36
|
-
case "list":
|
|
37
|
-
list()
|
|
38
|
-
break
|
|
39
|
-
case "help":
|
|
40
|
-
case "--help":
|
|
41
|
-
case "-h":
|
|
42
|
-
console.log(HELP)
|
|
43
|
-
break
|
|
44
|
-
default:
|
|
45
|
-
launch([cmd, ...rest])
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
main()
|
package/src/clients.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
// Curated list of supported agent clients. Adding a new one here:
|
|
2
|
-
// 1. add the entry below
|
|
3
|
-
// 2. add `RUN npm install -g <npmPackage>` to docker/Dockerfile
|
|
4
|
-
// 3. create docker/configs/<key>/ with any default config to bake in
|
|
5
|
-
// then `maesttro update` (or `agent setup` during dev) picks it up.
|
|
6
|
-
|
|
7
|
-
module.exports = {
|
|
8
|
-
claude: {
|
|
9
|
-
label: "Claude Code",
|
|
10
|
-
npmPackage: "@anthropic-ai/claude-code",
|
|
11
|
-
binary: "claude",
|
|
12
|
-
configDir: "/home/agent/.claude",
|
|
13
|
-
},
|
|
14
|
-
opencode: {
|
|
15
|
-
label: "opencode",
|
|
16
|
-
npmPackage: "opencode-ai",
|
|
17
|
-
binary: "opencode",
|
|
18
|
-
configDir: "/home/agent/.config/opencode",
|
|
19
|
-
},
|
|
20
|
-
codex: {
|
|
21
|
-
label: "OpenAI Codex CLI",
|
|
22
|
-
npmPackage: "@openai/codex",
|
|
23
|
-
binary: "codex",
|
|
24
|
-
configDir: "/home/agent/.codex",
|
|
25
|
-
},
|
|
26
|
-
}
|
package/src/doctor.js
DELETED
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
const { execSync } = require("child_process")
|
|
2
|
-
const { IMAGE, VOLUME } = require("./paths")
|
|
3
|
-
const clients = require("./clients")
|
|
4
|
-
|
|
5
|
-
function check(label, fn) {
|
|
6
|
-
try {
|
|
7
|
-
const detail = fn()
|
|
8
|
-
console.log(` [ok] ${label}${detail ? " — " + detail : ""}`)
|
|
9
|
-
return true
|
|
10
|
-
} catch (e) {
|
|
11
|
-
console.log(` [fail] ${label}`)
|
|
12
|
-
return false
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function doctor() {
|
|
17
|
-
console.log("== maesttro doctor ==\n")
|
|
18
|
-
let allOk = true
|
|
19
|
-
|
|
20
|
-
allOk &= check("Docker daemon reachable", () => {
|
|
21
|
-
execSync("docker info", { stdio: "ignore" })
|
|
22
|
-
})
|
|
23
|
-
|
|
24
|
-
allOk &= check("Sandbox image built", () => {
|
|
25
|
-
const out = execSync(
|
|
26
|
-
`docker image inspect ${IMAGE} --format "{{.Created}}"`,
|
|
27
|
-
)
|
|
28
|
-
.toString()
|
|
29
|
-
.trim()
|
|
30
|
-
return `built ${out}`
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
allOk &= check("Persistent volume exists", () => {
|
|
34
|
-
execSync(`docker volume inspect ${VOLUME}`, { stdio: "ignore" })
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
allOk &= check("Bot identity authenticated", () => {
|
|
38
|
-
const out = execSync(
|
|
39
|
-
`docker run --rm -v ${VOLUME}:/home/agent ${IMAGE} "gh auth status 2>&1"`,
|
|
40
|
-
).toString()
|
|
41
|
-
const match = out.match(/Logged in to github\.com account (\S+)/)
|
|
42
|
-
return match ? match[1] : "authenticated"
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
allOk &= check(
|
|
46
|
-
"Personal SSH key NOT visible inside sandbox (isolation intact)",
|
|
47
|
-
() => {
|
|
48
|
-
const out = execSync(
|
|
49
|
-
`docker run --rm -v ${VOLUME}:/home/agent ${IMAGE} "cat ~/.ssh/id_rsa 2>&1 || echo MISSING"`,
|
|
50
|
-
).toString()
|
|
51
|
-
if (!out.includes("MISSING") && !out.includes("No such file")) {
|
|
52
|
-
throw new Error(
|
|
53
|
-
"SSH key is reachable inside the sandbox — isolation broken",
|
|
54
|
-
)
|
|
55
|
-
}
|
|
56
|
-
},
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
for (const [key, c] of Object.entries(clients)) {
|
|
60
|
-
allOk &= check(`Client available: ${c.label}`, () => {
|
|
61
|
-
execSync(`docker run --rm ${IMAGE} "which ${c.binary}"`, {
|
|
62
|
-
stdio: "ignore",
|
|
63
|
-
})
|
|
64
|
-
})
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
console.log()
|
|
68
|
-
if (allOk) {
|
|
69
|
-
console.log("All checks passed.")
|
|
70
|
-
} else {
|
|
71
|
-
console.log(
|
|
72
|
-
"Some checks failed — run `maesttro setup` or `maesttro update` to fix, or check the failing item above.",
|
|
73
|
-
)
|
|
74
|
-
process.exit(1)
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
module.exports = { doctor }
|
package/src/launch.js
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
const { spawnSync, execSync } = require("child_process")
|
|
2
|
-
const fs = require("fs")
|
|
3
|
-
const path = require("path")
|
|
4
|
-
const os = require("os")
|
|
5
|
-
const { IMAGE, VOLUME } = require("./paths")
|
|
6
|
-
const clients = require("./clients")
|
|
7
|
-
|
|
8
|
-
const DEFAULT_CLIENT = "claude"
|
|
9
|
-
|
|
10
|
-
function ensureReady() {
|
|
11
|
-
try {
|
|
12
|
-
execSync(`docker image inspect ${IMAGE}`, { stdio: "ignore" })
|
|
13
|
-
execSync(`docker volume inspect ${VOLUME}`, { stdio: "ignore" })
|
|
14
|
-
} catch {
|
|
15
|
-
console.error("Sandbox isn't set up yet. Run `maesttro setup` first.")
|
|
16
|
-
process.exit(1)
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function list() {
|
|
21
|
-
console.log("Available clients:\n")
|
|
22
|
-
for (const [key, c] of Object.entries(clients)) {
|
|
23
|
-
console.log(` ${key.padEnd(10)} ${c.label} (${c.npmPackage})`)
|
|
24
|
-
}
|
|
25
|
-
console.log(
|
|
26
|
-
`\nDefault: ${DEFAULT_CLIENT}. Run \`maesttro <client>\` to pick one, or \`maesttro <client> -- <args>\` to pass args through.`,
|
|
27
|
-
)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function launch(cmdArgs) {
|
|
31
|
-
ensureReady()
|
|
32
|
-
|
|
33
|
-
const cwd = process.cwd()
|
|
34
|
-
const sharedSkills =
|
|
35
|
-
process.env.AGENT_SHARED_SKILLS ||
|
|
36
|
-
path.join(os.homedir(), "agent-shared", "skills")
|
|
37
|
-
|
|
38
|
-
let clientKey = DEFAULT_CLIENT
|
|
39
|
-
let extraArgs = []
|
|
40
|
-
|
|
41
|
-
if (cmdArgs.length > 0 && clients[cmdArgs[0]]) {
|
|
42
|
-
clientKey = cmdArgs[0]
|
|
43
|
-
extraArgs = cmdArgs.slice(1)
|
|
44
|
-
} else if (cmdArgs.length > 0) {
|
|
45
|
-
// not a known client — treat the whole thing as a raw command, e.g. `maesttro bash`
|
|
46
|
-
return runRaw(cmdArgs.join(" "), cwd, sharedSkills)
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const client = clients[clientKey]
|
|
50
|
-
const command = [client.binary, ...extraArgs].join(" ")
|
|
51
|
-
runRaw(command, cwd, sharedSkills)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function runRaw(command, cwd, sharedSkills) {
|
|
55
|
-
const mounts = ["-v", `${VOLUME}:/home/agent`, "-v", `${cwd}:/workspace`]
|
|
56
|
-
if (fs.existsSync(sharedSkills)) {
|
|
57
|
-
mounts.push("-v", `${sharedSkills}:/home/agent/.claude/skills:ro`)
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const result = spawnSync(
|
|
61
|
-
"docker",
|
|
62
|
-
["run", "-it", "--rm", ...mounts, "-w", "/workspace", IMAGE, command],
|
|
63
|
-
{ stdio: "inherit" },
|
|
64
|
-
)
|
|
65
|
-
process.exit(result.status ?? 0)
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
module.exports = { launch, list }
|
package/src/paths.js
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
const path = require("path")
|
|
2
|
-
|
|
3
|
-
// __dirname here is <install>/src — package root is one level up.
|
|
4
|
-
// This works identically whether installed via `npm link`, `npm i -g .`,
|
|
5
|
-
// or from the registry — npm always keeps the package's own files together.
|
|
6
|
-
const PKG_ROOT = path.resolve(__dirname, "..")
|
|
7
|
-
const DOCKERFILE_DIR = path.join(PKG_ROOT, "docker")
|
|
8
|
-
|
|
9
|
-
const IMAGE = "maesttro-runner:latest"
|
|
10
|
-
const VOLUME = "maesttro-home"
|
|
11
|
-
|
|
12
|
-
module.exports = { PKG_ROOT, DOCKERFILE_DIR, IMAGE, VOLUME }
|
package/src/setup.js
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
const { execSync, spawnSync } = require("child_process")
|
|
2
|
-
const readline = require("readline")
|
|
3
|
-
const fs = require("fs")
|
|
4
|
-
const { DOCKERFILE_DIR, IMAGE, VOLUME } = require("./paths")
|
|
5
|
-
|
|
6
|
-
function run(cmd, opts = {}) {
|
|
7
|
-
return execSync(cmd, { stdio: "inherit", ...opts })
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
function ask(question) {
|
|
11
|
-
const rl = readline.createInterface({
|
|
12
|
-
input: process.stdin,
|
|
13
|
-
output: process.stdout,
|
|
14
|
-
})
|
|
15
|
-
return new Promise((resolve) =>
|
|
16
|
-
rl.question(question, (answer) => {
|
|
17
|
-
rl.close()
|
|
18
|
-
resolve(answer.trim())
|
|
19
|
-
}),
|
|
20
|
-
)
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
async function setup(args) {
|
|
24
|
-
console.log("== maesttro setup ==\n")
|
|
25
|
-
|
|
26
|
-
// 1. Docker present?
|
|
27
|
-
try {
|
|
28
|
-
execSync("docker info", { stdio: "ignore" })
|
|
29
|
-
} catch {
|
|
30
|
-
console.error(
|
|
31
|
-
"Docker doesn't seem to be running. Start Docker Desktop (or the docker daemon) and try again.",
|
|
32
|
-
)
|
|
33
|
-
process.exit(1)
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
// 2. Build image from the Dockerfile bundled inside this package
|
|
37
|
-
console.log(
|
|
38
|
-
"-> Building sandbox image (this pulls node:22-slim + installs CLIs, first run only)...",
|
|
39
|
-
)
|
|
40
|
-
run(`docker build -t ${IMAGE} "${DOCKERFILE_DIR}"`)
|
|
41
|
-
|
|
42
|
-
// 3. Create persistent volume
|
|
43
|
-
console.log("\n-> Ensuring persistent volume exists...")
|
|
44
|
-
execSync(`docker volume create ${VOLUME}`, { stdio: "ignore" })
|
|
45
|
-
|
|
46
|
-
// 4. Get bot token — file path arg, or interactive paste
|
|
47
|
-
const tokenPath = args[0]
|
|
48
|
-
let token
|
|
49
|
-
if (tokenPath && fs.existsSync(tokenPath)) {
|
|
50
|
-
token = fs.readFileSync(tokenPath, "utf8").trim()
|
|
51
|
-
} else {
|
|
52
|
-
console.log(
|
|
53
|
-
"\nPaste the bot account's fine-grained GitHub token (input hidden not supported in plain readline — clear your scrollback after):",
|
|
54
|
-
)
|
|
55
|
-
token = await ask("Token: ")
|
|
56
|
-
}
|
|
57
|
-
if (!token) {
|
|
58
|
-
console.error(
|
|
59
|
-
"No token provided. Run `maesttro setup /path/to/token.txt` or paste one when prompted.",
|
|
60
|
-
)
|
|
61
|
-
process.exit(1)
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// 5. Authenticate the bot identity into the volume
|
|
65
|
-
console.log("\n-> Authenticating bot identity into the sandbox volume...")
|
|
66
|
-
const auth = spawnSync(
|
|
67
|
-
"docker",
|
|
68
|
-
[
|
|
69
|
-
"run",
|
|
70
|
-
"-i",
|
|
71
|
-
"--rm",
|
|
72
|
-
"-v",
|
|
73
|
-
`${VOLUME}:/home/agent`,
|
|
74
|
-
IMAGE,
|
|
75
|
-
"gh auth login --with-token && gh auth setup-git",
|
|
76
|
-
],
|
|
77
|
-
{ input: token, stdio: ["pipe", "inherit", "inherit"] },
|
|
78
|
-
)
|
|
79
|
-
if (auth.status !== 0) {
|
|
80
|
-
console.error(
|
|
81
|
-
"\nToken authentication failed — check the token is valid and has repo access.",
|
|
82
|
-
)
|
|
83
|
-
process.exit(1)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// 6. Verify isolation
|
|
87
|
-
console.log("\n-> Verifying isolation...")
|
|
88
|
-
console.log(" gh identity inside sandbox:")
|
|
89
|
-
run(`docker run --rm -v ${VOLUME}:/home/agent ${IMAGE} "gh auth status"`)
|
|
90
|
-
console.log(" personal SSH key should NOT be visible:")
|
|
91
|
-
run(
|
|
92
|
-
`docker run --rm -v ${VOLUME}:/home/agent ${IMAGE} "cat ~/.ssh/id_rsa 2>&1 || true"`,
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
console.log(
|
|
96
|
-
"\nSetup complete. Run `maesttro` from any project directory to start a session,",
|
|
97
|
-
)
|
|
98
|
-
console.log("or `maesttro list` to see available clients.")
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
module.exports = { setup }
|
package/src/update.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
const { execSync } = require("child_process")
|
|
2
|
-
const { DOCKERFILE_DIR, IMAGE, PKG_ROOT } = require("./paths")
|
|
3
|
-
const { name: packageName } = require(`${PKG_ROOT}/package.json`)
|
|
4
|
-
|
|
5
|
-
function update() {
|
|
6
|
-
console.log("== maesttro update ==\n")
|
|
7
|
-
|
|
8
|
-
console.log(`-> Updating ${packageName} package...`)
|
|
9
|
-
execSync(`npm i -g ${packageName}@latest`, { stdio: "inherit" })
|
|
10
|
-
|
|
11
|
-
console.log("\n-> Rebuilding sandbox image with refreshed client configs...")
|
|
12
|
-
execSync(`docker build -t ${IMAGE} "${DOCKERFILE_DIR}"`, { stdio: "inherit" })
|
|
13
|
-
|
|
14
|
-
console.log(
|
|
15
|
-
"\nUpdate complete. Bot identity and your project files are untouched —",
|
|
16
|
-
)
|
|
17
|
-
console.log(
|
|
18
|
-
"only the image (client versions + curated configs) was refreshed.",
|
|
19
|
-
)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
module.exports = { update }
|