mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-04 06:29:42 +02:00
feat(omp): install omp in the docker agent image, isolate its credentials
OMP had full routing at the Docker layer (default pane command, schema) but
was never actually installed in docker/agent.Dockerfile, and had no
credential-isolation entry in docker-hosts.ts's CRED_STORES - a Docker-mode
OMP session would have failed with "omp: command not found", and even with
the binary present would have had no config/auth seeded, despite the README
already claiming OMP has "seamless auth, isolated credentials" in Docker.
- docker/agent.Dockerfile: install omp via its own installer (standalone
binary, same shape as grok/antigravity - not on npm). Verified against a
real --no-cache build: the installer actually targets ~/.local/bin, not
~/.omp/bin as the resolver's OMP_SEARCH_DIRS ordering would suggest -
confirmed omp/18.0.8 installs and runs correctly inside the image.
- src/docker-hosts.ts: add a .omp/agent CRED_STORES entry. Unlike every
sibling CLI in this family, sessions/ is SHARED (RW), not seeded: Codeman
reads ~/.omp/agent/sessions/**/*.jsonl host-side for history recovery and
--resume pinning (omp-transcript.ts, omp-session-resolver.ts), the same
reason codex's sessions/ is shared rather than seeded. Seeding it instead
would silently break the kill-survival feature for Docker cases. Only the
small config files (config.yml/mcp.json/models.yml/settings.yml) are
seeded; the SQLite caches and terminal-sessions/ stay container-local.
- test/docker-hosts.test.ts: pin the new CRED_STORES entry's behavior.
Found in passing (NOT fixed here, unrelated and pre-existing on master): the
agent image's DeepSeek (dsh) plugin-install step currently fails on a fresh
build ("pnpm not found on PATH"), confirmed via git diff against
origin/master that this line is untouched by this branch. Worth a separate
issue/PR.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
853681f970
commit
d74cde759b
+21
-1
@@ -80,6 +80,22 @@ RUN npm install -g @deepseek-ai/dsh \
|
||||
&& npm cache clean --force \
|
||||
&& dsh --version
|
||||
|
||||
# OMP (Oh My Pi) is NOT on npm: a standalone binary via omp.sh's installer, which
|
||||
# targets $HOME/.local/bin with no --dir override (verified 2026-08-27 — the
|
||||
# resolver's OMP_SEARCH_DIRS lists ~/.omp/bin first, which turned out to be the
|
||||
# WRONG guess for the installer's actual target; build this step for real
|
||||
# rather than trust that ordering). At build time $HOME is root's home and
|
||||
# unreachable by the `agent` user, so copy the binary into /usr/local/bin and
|
||||
# drop root's ~/.local/bin/omp in the same layer so the image does not carry
|
||||
# the download twice.
|
||||
RUN curl -fsSL https://omp.sh/install | sh \
|
||||
&& cp -L /root/.local/bin/omp /usr/local/bin/omp.real \
|
||||
&& rm -f /usr/local/bin/omp \
|
||||
&& mv /usr/local/bin/omp.real /usr/local/bin/omp \
|
||||
&& chmod 755 /usr/local/bin/omp \
|
||||
&& rm -f /root/.local/bin/omp \
|
||||
&& omp --version
|
||||
|
||||
# `agent` user (gid 0) with an arbitrary-uid-writable HOME. The uid is
|
||||
# auto-assigned (node:22-slim already occupies uid 1000 with its `node` user); at
|
||||
# runtime Codeman overrides with `--user <hostUid>:0` on Linux, so the baked uid
|
||||
@@ -106,10 +122,14 @@ ENV HOME=/home/agent
|
||||
# writable by the arbitrary uid the container actually runs as, and a profile
|
||||
# installed after it would miss that fixup. DSH_HOME points the launcher at the
|
||||
# agent's dir while this still runs as root.
|
||||
# `.omp/agent` is pre-created for the same reason `.codex` is: it is a MIXED
|
||||
# store (per-file config seeds PLUS a shared `sessions/` RW bind mount for
|
||||
# Codeman's own host-side history/resume reads), and neither kind of artifact
|
||||
# creates its own parent directory.
|
||||
RUN useradd -g 0 -m -d /home/agent -s /bin/bash agent \
|
||||
&& mkdir -p /home/agent/.npm /home/agent/.cache /home/agent/.config /home/agent/.codeman \
|
||||
/home/agent/.claude/projects /home/agent/.codex/sessions /home/agent/.pi/agent /home/agent/.grok \
|
||||
/home/agent/.dsh \
|
||||
/home/agent/.dsh /home/agent/.omp/agent \
|
||||
&& DSH_HOME=/home/agent/.dsh HOME=/home/agent \
|
||||
dsh plugin --profile dsh-tui add @deepseek-harness-tui/dsh-tui \
|
||||
&& test -f /home/agent/.dsh/profiles/dsh-tui/package.json \
|
||||
|
||||
@@ -640,6 +640,22 @@ const CRED_STORES: CredStorePolicy[] = [
|
||||
},
|
||||
{ rel: '.config/gcloud', seedWhole: true },
|
||||
{ rel: '.config/opencode', seedWhole: true },
|
||||
// OMP keeps its config in `~/.omp/agent` (config.yml/mcp.json/models.yml/
|
||||
// settings.yml — small, no bigger than grok's config.toml/pager.toml), but
|
||||
// that dir ALSO holds agent.db/history.db/models.db (SQLite caches) and
|
||||
// terminal-sessions/blobs/cache (large, regenerable), so seed only the
|
||||
// config files. UNLIKE pi/grok, `sessions/` is SHARED (RW), not
|
||||
// host-invisible: Codeman reads `~/.omp/agent/sessions/**/*.jsonl`
|
||||
// HOST-SIDE for history recovery and --resume pinning
|
||||
// (omp-transcript.ts, omp-session-resolver.ts) — the same reason codex's
|
||||
// `sessions/` is shared rather than seeded. Without this, an in-container
|
||||
// OMP conversation would be invisible to Codeman's own history-scan/resume
|
||||
// logic, silently breaking the kill-survival feature for Docker cases.
|
||||
{
|
||||
rel: '.omp/agent',
|
||||
shareDirs: ['sessions'],
|
||||
seedFiles: ['config.yml', 'mcp.json', 'models.yml', 'settings.yml'],
|
||||
},
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -329,6 +329,32 @@ describe('resolveDockerCredentialArtifacts (isolated codex/gemini/gcloud/opencod
|
||||
expect(mounts).toEqual([]);
|
||||
expect(seedCopies).toEqual([]);
|
||||
});
|
||||
|
||||
it('omp: shares sessions/ RW (host-side history/resume reads), seeds config files only', () => {
|
||||
mkdirSync(join(home, '.omp', 'agent', 'sessions'), { recursive: true });
|
||||
writeFileSync(join(home, '.omp', 'agent', 'config.yml'), '');
|
||||
writeFileSync(join(home, '.omp', 'agent', 'mcp.json'), '{}');
|
||||
writeFileSync(join(home, '.omp', 'agent', 'models.yml'), '');
|
||||
writeFileSync(join(home, '.omp', 'agent', 'settings.yml'), '');
|
||||
// Regenerable local state that must NOT be seeded (mirrors the pi/grok exclusions).
|
||||
writeFileSync(join(home, '.omp', 'agent', 'agent.db'), '');
|
||||
mkdirSync(join(home, '.omp', 'agent', 'terminal-sessions'), { recursive: true });
|
||||
|
||||
const { mounts, seedCopies } = resolveDockerCredentialArtifacts(home);
|
||||
expect(mounts).toContainEqual({
|
||||
src: join(home, '.omp', 'agent', 'sessions'),
|
||||
dst: '/home/agent/.omp/agent/sessions',
|
||||
});
|
||||
const dests = seedCopies.map((s) => s.to);
|
||||
expect(dests).toContain('/home/agent/.omp/agent/config.yml');
|
||||
expect(dests).toContain('/home/agent/.omp/agent/mcp.json');
|
||||
expect(dests).toContain('/home/agent/.omp/agent/models.yml');
|
||||
expect(dests).toContain('/home/agent/.omp/agent/settings.yml');
|
||||
expect(dests).not.toContain('/home/agent/.omp/agent/agent.db');
|
||||
expect(mounts.some((m) => m.dst === '/home/agent/.omp/agent/terminal-sessions')).toBe(false);
|
||||
// seed copies of individual files are NOT recursive
|
||||
expect(seedCopies.filter((s) => s.to.startsWith('/home/agent/.omp')).every((s) => !s.recursive)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('resolveDockerClaudeArtifacts (isolated claude state)', () => {
|
||||
|
||||
Reference in New Issue
Block a user