@madarco/agentbox 0.6.0 → 0.7.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/dist/_cloud-attach-DMVH6GWO.js +12 -0
- package/dist/chunk-7KOEFGN2.js +1162 -0
- package/dist/chunk-7KOEFGN2.js.map +1 -0
- package/dist/chunk-I24B6AXR.js +600 -0
- package/dist/chunk-I24B6AXR.js.map +1 -0
- package/dist/chunk-NAVL4R34.js +7546 -0
- package/dist/chunk-NAVL4R34.js.map +1 -0
- package/dist/chunk-NW5NYTQM.js +1366 -0
- package/dist/chunk-NW5NYTQM.js.map +1 -0
- package/dist/chunk-UK72UQ5U.js +237 -0
- package/dist/chunk-UK72UQ5U.js.map +1 -0
- package/dist/chunk-V5KZGB5V.js +722 -0
- package/dist/chunk-V5KZGB5V.js.map +1 -0
- package/dist/cloud-poller-ZIWSADJB-JXFRJUEM.js +10 -0
- package/dist/dist-ETCFRVPA.js +423 -0
- package/dist/dist-QZGJIBT5.js +1339 -0
- package/dist/dist-QZGJIBT5.js.map +1 -0
- package/dist/dist-R67WMLCF.js +183 -0
- package/dist/dist-R67WMLCF.js.map +1 -0
- package/dist/index.js +3998 -1569
- package/dist/index.js.map +1 -1
- package/package.json +8 -3
- package/runtime/docker/Dockerfile.box +98 -14
- package/runtime/docker/apps/cli/share/agentbox-setup/SKILL.md +15 -8
- package/runtime/docker/packages/ctl/dist/bin.cjs +10220 -773
- package/runtime/docker/packages/sandbox-docker/scripts/agentbox-codex-hooks.json +37 -0
- package/runtime/docker/packages/sandbox-docker/scripts/agentbox-open +9 -9
- package/runtime/hetzner/agentbox-checkpoint-cleanup +52 -0
- package/runtime/hetzner/agentbox-codex-hooks.json +37 -0
- package/runtime/hetzner/agentbox-dockerd-start +132 -0
- package/runtime/hetzner/agentbox-open +28 -0
- package/runtime/hetzner/agentbox-setup-skill.md +196 -0
- package/runtime/hetzner/agentbox-vnc-start +77 -0
- package/runtime/hetzner/claude-managed-settings.json +54 -0
- package/runtime/hetzner/ctl.cjs +22350 -0
- package/runtime/hetzner/custom-system-CLAUDE.md +27 -0
- package/runtime/hetzner/scripts/install-box.sh +365 -0
- package/runtime/relay/bin.cjs +9118 -809
- package/share/agentbox-setup/SKILL.md +15 -8
- package/dist/chunk-BBZMA2K6.js +0 -238
- package/dist/chunk-BBZMA2K6.js.map +0 -1
- package/dist/chunk-HHMWQNLF.js +0 -1709
- package/dist/chunk-HHMWQNLF.js.map +0 -1
- package/dist/chunk-HPZMD5DE.js +0 -106
- package/dist/chunk-HPZMD5DE.js.map +0 -1
- package/dist/chunk-HTTKML3C.js +0 -2655
- package/dist/chunk-HTTKML3C.js.map +0 -1
- package/dist/chunk-KJNZP6I3.js +0 -586
- package/dist/chunk-KJNZP6I3.js.map +0 -1
- package/dist/chunk-M7I247BK.js +0 -525
- package/dist/chunk-M7I247BK.js.map +0 -1
- package/dist/create-6PWXI6HO-OWAMHBAK.js +0 -15
- package/dist/lifecycle-EMXR46DI-DUVBXNTV.js +0 -38
- package/dist/state-KD7M46ZP-KHFTHFUS.js +0 -26
- package/dist/stats-SZXOJE3D-N7OODCHW.js +0 -19
- package/dist/stats-SZXOJE3D-N7OODCHW.js.map +0 -1
- /package/dist/{create-6PWXI6HO-OWAMHBAK.js.map → _cloud-attach-DMVH6GWO.js.map} +0 -0
- /package/dist/{lifecycle-EMXR46DI-DUVBXNTV.js.map → cloud-poller-ZIWSADJB-JXFRJUEM.js.map} +0 -0
- /package/dist/{state-KD7M46ZP-KHFTHFUS.js.map → dist-ETCFRVPA.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../packages/sandbox-docker/src/create.ts","../../../packages/sandbox-docker/src/claude-credentials.ts","../../../packages/sandbox-docker/src/box-env.ts"],"sourcesContent":["import { randomBytes } from 'node:crypto';\nimport { mkdir, stat } from 'node:fs/promises';\nimport { homedir } from 'node:os';\nimport { basename, join, resolve } from 'node:path';\nimport { execa } from 'execa';\nimport { ConfigError, loadConfig } from '@agentbox/ctl';\nimport {\n buildClaudeMounts,\n ensureClaudeVolume,\n resolveClaudeVolume,\n seedSetupSkillIntoVolume,\n} from './claude.js';\nimport { syncClaudeCredentials } from './claude-credentials.js';\nimport {\n type BoxLimitSpec,\n containerExists,\n dockerInfo,\n dockerStorageDriver,\n ensureVolume,\n publishedHostPort,\n runBox,\n} from './docker.js';\nimport { dockerVolumeName, launchDockerdDaemon } from './dockerd.js';\nimport { generateVncPassword, launchVncDaemon, VNC_CONTAINER_PORT } from './vnc.js';\nimport { WEB_CONTAINER_PORT } from './web.js';\nimport { detectGitRepos, pickFreshBranch } from './git-worktree.js';\nimport {\n bindWorktrees,\n chownGitBindParents,\n collectRepoCarryOver,\n gitWorktreePathFor,\n seedWorkspace,\n seedWorkspaceFromDir,\n type RepoCarryOver,\n} from './in-box-git.js';\nimport {\n CONTAINER_EXPORT_MERGED,\n DEFAULT_ENV_PATTERNS,\n boxRunDirFor,\n copyHostEnvFilesToBox,\n copyHostFilesToBox,\n} from './host-export.js';\nimport { DEFAULT_BOX_IMAGE, ensureImage } from './image.js';\nimport {\n allocateProjectIndex,\n readState,\n recordBox,\n type BoxRecord,\n type GitWorktreeRecord,\n} from './state.js';\nimport { createSnapshot, snapshotPathFor } from './snapshot.js';\nimport { resolveCheckpoint } from './checkpoint.js';\nimport { launchCtlDaemon } from './ctl.js';\nimport { writeBoxEnvFile } from './box-env.js';\nimport { ensureHomeOwnedByVscode } from './home-ownership.js';\nimport {\n ensureRelay,\n generateRelayToken,\n registerBoxWithRelay,\n rehydrateRelayRegistry,\n} from './relay.js';\nimport {\n buildIdeMounts,\n cursorServerVolumeName,\n ensureIdeVolumes,\n repairIdeOwnership,\n vscodeServerVolumeName,\n} from './vscode.js';\n\nexport interface CreateBoxOptions {\n workspacePath: string;\n name?: string;\n /**\n * Take a `cp -c` APFS clone of the host workspace into\n * `~/.agentbox/snapshots/<id>/` before seeding `/workspace`. Stabilizes the\n * source of the tar pipe in the non-git case (and the untracked-file\n * pipe in the git case) against host edits during create. Effectively a\n * no-op when a git worktree is detected — the worktree's tracked content\n * comes from `.git`, not from a workspace copy.\n */\n useSnapshot: boolean;\n /**\n * Start the box from a project checkpoint (the `--snapshot <ref>` path).\n * Resolved against `projectRoot` (or `workspacePath` when unset). The\n * checkpoint is a local Docker *image* tag now; the box is created with\n * `docker run <ckpt-image>` and inherits a populated `/workspace`. No\n * `seedWorkspace` runs in this path.\n */\n checkpointRef?: string;\n image?: string;\n onLog?: (line: string) => void;\n /**\n * Claude Code config volume. When omitted, defaults to `{ isolate: false }` —\n * every box mounts the shared `agentbox-claude-config` volume at\n * /home/vscode/.claude so auth / skills / plugins persist across boxes.\n */\n claudeConfig?: { isolate: boolean };\n /** Extra env vars forwarded to the container (merged on top of claude env forwarding). */\n claudeEnv?: Record<string, string>;\n /**\n * When true, run `npm install -g @playwright/cli@latest` inside the box after\n * `/workspace` is seeded. agent-browser is always installed in the image;\n * this flag adds the Playwright CLI on top for boxes that need it.\n */\n withPlaywright?: boolean;\n /**\n * When true, copy the host's env/config files (DEFAULT_ENV_PATTERNS basename\n * globs — `.env*`, `secrets.toml`, `agentbox.yaml`, ...) into the box's\n * /workspace after seeding, bypassing gitignore. The reverse of `pull env`.\n * One-shot at create time; the files persist in the container's writable\n * layer across pause/stop/start.\n */\n withEnv?: boolean;\n /**\n * Explicit relative-path file list to copy from `workspacePath` into the\n * box's /workspace after seeding (no glob expansion, no scan — the list is\n * pre-vetted, e.g. picked by the wizard's multiselect). Independent of\n * `withEnv`: if both are set, both run (idempotent on overlapping files).\n * One-shot at create time; persists across pause/stop/start.\n */\n envFilesToImport?: string[];\n /**\n * VNC stack (Xvnc on :1 + websockify serving noVNC on container :6080).\n * Defaults to enabled. The CLI exposes `--no-vnc` for opt-out. Disabling\n * skips port mapping + password generation + the in-container supervisor\n * launch; the apt-installed binaries stay in the image but are unused.\n */\n vnc?: { enabled: boolean };\n /**\n * Docker-in-Docker. Always-on (the in-box dockerd is part of the box\n * surface). When `sharedCache` is true the per-box `agentbox-docker-<id>`\n * volume is replaced with the shared `agentbox-docker-cache` volume — image\n * layers persist across boxes (and `destroy`/`prune` won't remove it).\n */\n docker?: { sharedCache: boolean };\n /**\n * Absolute host path of the cwd's project at create time. When provided,\n * `createBox` stamps `projectRoot` + an allocated `projectIndex` on the\n * BoxRecord so the CLI can auto-pick / resolve by index. The CLI computes\n * this via `findProjectRoot(workspacePath)` from `@agentbox/config`; this\n * package stays free of the config dep. Omit for unowned boxes created\n * directly via the programmatic API.\n */\n projectRoot?: string;\n /**\n * Container resource ceilings (engine-agnostic: bytes / fractional cpus /\n * pid count / raw disk size string). Absent fields = unlimited. `disk` is\n * best-effort: dropped (with a warning via `onLog`) when the engine's\n * storage driver can't enforce `--storage-opt size=` (overlay2 / macOS).\n */\n limits?: BoxLimitSpec;\n}\n\nexport interface CreatedBox {\n record: BoxRecord;\n imageBuilt: boolean;\n}\n\n/**\n * Compact the engine-applied limits into the BoxRecord shape: only fields that\n * actually constrain the box (>0 / non-empty). Returns undefined when nothing\n * was applied so legacy/unlimited boxes stay free of the field.\n */\nfunction persistableLimits(\n lim: BoxLimitSpec | undefined,\n): BoxRecord['resourceLimits'] | undefined {\n if (!lim) return undefined;\n const out: NonNullable<BoxRecord['resourceLimits']> = {};\n if (lim.memoryBytes && lim.memoryBytes > 0) out.memoryBytes = Math.floor(lim.memoryBytes);\n if (lim.cpus && lim.cpus > 0) out.cpus = lim.cpus;\n if (lim.pidsLimit && lim.pidsLimit > 0) out.pidsLimit = Math.floor(lim.pidsLimit);\n if (lim.disk) out.disk = lim.disk;\n return Object.keys(out).length > 0 ? out : undefined;\n}\n\nfunction generateBoxId(): string {\n return randomBytes(4).toString('hex');\n}\n\nexport function sanitizeBasename(workspacePath: string): string {\n const raw = basename(resolve(workspacePath));\n return raw\n .toLowerCase()\n .replace(/[^a-z0-9._-]+/g, '-')\n .replace(/-+/g, '-')\n .replace(/^[-._]+|[-._]+$/g, '')\n .slice(0, 30)\n .replace(/[-._]+$/, '');\n}\n\nexport function defaultBoxName(workspacePath: string, id: string): string {\n const base = sanitizeBasename(workspacePath);\n return base.length > 0 ? `${base}-${id}` : id;\n}\n\nasync function pathExists(p: string): Promise<boolean> {\n try {\n await stat(p);\n return true;\n } catch {\n return false;\n }\n}\n\n// ~/.claude is intentionally NOT in this list: it lives in the named volume\n// `agentbox-claude-config` (see resolveClaudeVolume / ensureClaudeVolume) so\n// auth persists inside the container without leaking host state. Only\n// non-claude identity files are bind-mounted from the host.\nasync function buildIdentityMounts(): Promise<string[]> {\n const home = homedir();\n const candidates: Array<{ src: string; dst: string; readOnly: boolean }> = [\n { src: join(home, '.codex'), dst: '/home/vscode/.codex', readOnly: false },\n { src: join(home, '.gitconfig'), dst: '/home/vscode/.gitconfig', readOnly: true },\n ];\n const out: string[] = [];\n for (const c of candidates) {\n if (await pathExists(c.src)) {\n out.push(`${c.src}:${c.dst}${c.readOnly ? ':ro' : ''}`);\n }\n }\n return out;\n}\n\nexport async function createBox(opts: CreateBoxOptions): Promise<CreatedBox> {\n const log = opts.onLog ?? (() => {});\n const workspace = resolve(opts.workspacePath);\n if (!(await pathExists(workspace))) {\n throw new Error(`workspace does not exist: ${workspace}`);\n }\n\n // Pre-flight agentbox.yaml validation on the host so the user sees the real\n // ConfigError instead of an opaque \"socket did not appear\" timeout from the\n // detached daemon exec later. The daemon re-validates inside the box anyway\n // — defence in depth, and necessary because the file lives in the\n // container's writable layer and can be edited after create.\n const cfgPath = join(workspace, 'agentbox.yaml');\n if (await pathExists(cfgPath)) {\n try {\n const cfg = await loadConfig(cfgPath);\n log(`agentbox.yaml validated (${String(cfg.services.length)} service(s))`);\n } catch (err) {\n if (err instanceof ConfigError) {\n throw new Error(`agentbox.yaml validation failed:\\n ${err.message}`);\n }\n throw err;\n }\n }\n\n await dockerInfo();\n log('docker daemon reachable');\n\n // Checkpoint resolution happens *before* image ensure because a checkpoint\n // image replaces the base image as the docker-run base. resolveCheckpoint\n // returns null on miss; we error with the ref so the user can fix it.\n let checkpointImage: string | undefined;\n let checkpointSource: BoxRecord['checkpointSource'];\n let restoredWorktrees: GitWorktreeRecord[] | undefined;\n if (opts.checkpointRef) {\n const projectRootForCkpt = opts.projectRoot ?? workspace;\n const head = await resolveCheckpoint(projectRootForCkpt, opts.checkpointRef);\n if (!head) {\n throw new Error(`checkpoint not found: ${opts.checkpointRef}`);\n }\n checkpointImage = head.manifest.image;\n // Chain: head first then its parents (base-most last). For a flattened\n // checkpoint this collapses to a single-entry chain.\n const chain = [head.name, ...head.manifest.parents];\n checkpointSource = { ref: opts.checkpointRef, type: head.manifest.type, chain };\n // The source's per-worktree paths persisted on the manifest so we can\n // re-establish the /workspace bind mount(s) after `docker run` (docker\n // commit doesn't capture bind-mount content, so the image's /workspace\n // is empty until we re-bind).\n restoredWorktrees = head.manifest.worktrees;\n log(\n `starting from checkpoint ${opts.checkpointRef} (${head.manifest.type}, ${String(chain.length)} layer(s), image ${head.manifest.image})`,\n );\n }\n\n const imageRef = checkpointImage ?? opts.image ?? DEFAULT_BOX_IMAGE;\n // ensureImage only acts on the base image; checkpoint images are local-only\n // and must already exist (they were created by `agentbox checkpoint`).\n const ensureRef = checkpointImage ? (opts.image ?? DEFAULT_BOX_IMAGE) : imageRef;\n const { built } = await ensureImage(ensureRef, {\n onProgress: (line) => log(`[image] ${line}`),\n });\n log(built ? `built image ${ensureRef}` : `using cached image ${imageRef}`);\n\n // Bring up the host relay before the box so the box can post events\n // immediately on boot. Best-effort — a relay outage shouldn't block create.\n // Always re-push known box tokens after ensure: the relay's registry is\n // in-memory, so a daemon restart or `docker restart agentbox-relay` between\n // CLI invocations leaves it empty. Repushing is idempotent and cheap.\n let relayUp = false;\n try {\n await ensureRelay({ onLog: log });\n const existing = await readState();\n await rehydrateRelayRegistry(existing.boxes);\n relayUp = true;\n } catch (err) {\n log(`relay unavailable: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n const id = generateBoxId();\n const name = opts.name ?? defaultBoxName(workspace, id);\n const containerName = `agentbox-${name}`;\n const createdAt = new Date().toISOString();\n if (await containerExists(containerName)) {\n throw new Error(`container ${containerName} already exists; remove it first`);\n }\n\n // Per-project monotonic index. Allocated *here* so it can flow into the\n // box / snapshot dir segments (`<id>-<n>-<mnemonic>`) and the\n // `AGENTBOX_PROJECT_INDEX` container env var. Pre-feature legacy boxes\n // never pass `projectRoot`; those records keep `projectIndex` undefined and\n // the dir segments fall back to `<id>-<mnemonic>`.\n let projectIndex: number | undefined;\n if (opts.projectRoot) {\n projectIndex = allocateProjectIndex(await readState(), opts.projectRoot);\n }\n\n // Repo detection + host-side carry-over capture. Branches are picked here\n // (against the host main repos' refs) so they're recorded on the BoxRecord\n // regardless of whether the in-container `git worktree add` succeeds later.\n // When restoring from a checkpoint, the source's per-worktree records are\n // restored from the manifest *here* (not after `docker run`) so the\n // `.git/` bind-mounts in `extraVolumes` know which host main repos to\n // wire up — without those binds the in-container `/workspace/.git` would\n // resolve to a path that doesn't exist in the new container.\n const repoCarryOvers: RepoCarryOver[] = [];\n const gitWorktreeRecords: GitWorktreeRecord[] = [];\n if (checkpointImage && restoredWorktrees && restoredWorktrees.length > 0) {\n gitWorktreeRecords.push(...restoredWorktrees);\n }\n if (!checkpointImage) {\n const repos = await detectGitRepos(workspace);\n if (repos.length > 0) {\n log(\n `detected ${String(repos.length)} git repo(s): ` +\n repos.map((r) => `${r.kind}${r.relPathFromWorkspace ? '@' + r.relPathFromWorkspace : ''}`).join(', '),\n );\n }\n for (const r of repos) {\n const branchBase =\n r.kind === 'root'\n ? `agentbox/${name}`\n : `agentbox/${name}--${r.relPathFromWorkspace.replace(/[^A-Za-z0-9._-]+/g, '_')}`;\n const branch = await pickFreshBranch(r.hostMainRepo, branchBase);\n const containerPath =\n r.kind === 'root' ? '/workspace' : `/workspace/${r.relPathFromWorkspace}`;\n const gitWorktreePath = gitWorktreePathFor(branch);\n const carry = await collectRepoCarryOver(r, branch, containerPath, gitWorktreePath);\n repoCarryOvers.push(carry);\n gitWorktreeRecords.push({\n kind: r.kind,\n hostMainRepo: r.hostMainRepo,\n containerPath,\n gitWorktreePath,\n branch,\n relPathFromWorkspace: r.relPathFromWorkspace,\n });\n }\n }\n\n // --host-snapshot: APFS clone the workspace into a per-box scratch dir.\n // Only the no-git, no-checkpoint path actually consumes the clone (as the\n // source of the tar pipe in seedWorkspaceFromDir). For the git path the\n // worktree content comes from `.git`'s object DB (bind-mounted) and the\n // untracked-file tar pipe reads from the live host main repo — neither\n // touches `snapshotDir`, so we skip it. For checkpoint restore there's no\n // seedWorkspace at all. Kept on the BoxRecord so destroyBox can clean it up.\n let snapshotDir: string | null = null;\n const snapshotIsUseful = !checkpointImage && repoCarryOvers.length === 0;\n if (opts.useSnapshot && snapshotIsUseful) {\n snapshotDir = snapshotPathFor({ id, name, projectIndex });\n log(`cloning workspace to ${snapshotDir} (APFS clone where available)`);\n const snap = await createSnapshot({ source: workspace, destination: snapshotDir });\n log(`pruned ${snap.prunedPaths.length} platform-dependent dirs from snapshot`);\n } else if (opts.useSnapshot && !checkpointImage) {\n log('skipping --host-snapshot: git worktree path reads content from .git, not from a workspace clone');\n }\n\n await ensureIdeVolumes(id);\n const dockerCacheShared = opts.docker?.sharedCache === true;\n const dockerVolume = dockerVolumeName(id, dockerCacheShared);\n await ensureVolume(dockerVolume);\n log(`prepared volumes ${vscodeServerVolumeName(id)}, ${cursorServerVolumeName(id)}, ${dockerVolume}`);\n const ide = buildIdeMounts(id);\n\n // Claude Code config volume. Shared by default so users sign in once across\n // every box; --isolate-claude-config opts into a per-box volume. Either way,\n // the host's ~/.claude is the authoritative source: we rsync host -> volume\n // on every create so updates on the host (new login, new skills, new MCP)\n // flow into the next box. Sync is additive — box-only state (session logs,\n // etc.) is preserved.\n const claudeSpec = resolveClaudeVolume({\n isolate: opts.claudeConfig?.isolate ?? false,\n boxId: id,\n });\n const claudeEnsured = await ensureClaudeVolume(claudeSpec, {\n syncFromHost: true,\n image: ensureRef,\n hostWorkspace: workspace,\n });\n if (claudeEnsured.synced) {\n log(`synced ${claudeSpec.volume} from ~/.claude`);\n if ((claudeEnsured.filteredHookCount ?? 0) > 0) {\n log(\n `filtered ${String(claudeEnsured.filteredHookCount)} host-path hook(s) (paths under ~/)`,\n );\n }\n if (claudeEnsured.installMethodFixed) {\n log('set installMethod=native in synced .claude.json (matches box native install)');\n }\n if (claudeEnsured.aliasedProjectKey) {\n log(`aliased project state for ${workspace} -> /workspace in synced .claude.json`);\n }\n if (claudeEnsured.workspaceTrusted) {\n log('pre-trusted /workspace in synced .claude.json (skips the trust dialog)');\n }\n } else if (claudeEnsured.created) {\n log(`created empty volume ${claudeSpec.volume} (no host ~/.claude to sync)`);\n } else {\n log(`reusing volume ${claudeSpec.volume} (no host ~/.claude to sync)`);\n }\n // Box-only: seed /agentbox-setup into the volume from the image. Never\n // touches the host's ~/.claude. Re-copied every run so an image upgrade\n // propagates to a long-lived shared volume.\n const seeded = await seedSetupSkillIntoVolume(claudeSpec.volume, ensureRef);\n if (seeded.seeded) log(`refreshed /agentbox-setup skill into ${claudeSpec.volume}`);\n // Mirror the in-box OAuth credentials with the host backup: extract a\n // box-written `.credentials.json` out to ~/.agentbox, or seed a fresh\n // volume from a previous box's login. Best-effort.\n const credSync = await syncClaudeCredentials(claudeSpec, {\n image: ensureRef,\n isolate: opts.claudeConfig?.isolate ?? false,\n });\n if (credSync.direction === 'extracted') {\n log('extracted box claude credentials to host backup');\n } else if (credSync.direction === 'seeded') {\n log(`seeded claude credentials into ${claudeSpec.volume} from host backup`);\n }\n const claudeMounts = buildClaudeMounts(claudeSpec, process.env);\n\n const boxDir = boxRunDirFor({ id, name, projectIndex });\n const socketDir = join(boxDir, 'run');\n const socketPath = join(socketDir, 'ctl.sock');\n // Per-box host dir that `agentbox open` refreshes the merged /workspace\n // into. Bound in at create time so `docker exec rsync` can write straight\n // to the host filesystem — no container restart needed.\n const mergedExportDir = join(boxDir, 'workspace');\n await mkdir(socketDir, { recursive: true });\n await mkdir(mergedExportDir, { recursive: true });\n\n const extraVolumes = await buildIdentityMounts();\n extraVolumes.push(...claudeMounts.extraVolumes);\n extraVolumes.push(...ide.extraVolumes);\n extraVolumes.push(`${socketDir}:/run/agentbox`);\n extraVolumes.push(`${mergedExportDir}:${CONTAINER_EXPORT_MERGED}`);\n // In-box dockerd's data root. Per-box (`agentbox-docker-<id>`, wiped on\n // destroy) by default; shared (`agentbox-docker-cache`, preserved) when\n // `box.dockerCacheShared` is set.\n extraVolumes.push(`${dockerVolume}:/var/lib/docker`);\n // Bind-mount each main repo's `.git/` at its identical absolute host path,\n // RW. The in-container `git worktree add` writes to <main>/.git/worktrees/\n // and the agent's commits write to refs/objects; both have to hit the same\n // path on host and inside the container so `git push` from the host main\n // repo sees the new commits without further sync.\n for (const w of gitWorktreeRecords) {\n extraVolumes.push(`${w.hostMainRepo}/.git:${w.hostMainRepo}/.git`);\n }\n for (const v of extraVolumes) log(`mounting agent dir: ${v}`);\n\n // Per-box bearer token for the host relay. Register *before* runBox so the\n // box's supervisor can post on boot. Skip if the relay isn't reachable —\n // the box still works, it just won't deliver events to the host.\n const relayToken = generateRelayToken();\n if (relayUp) {\n try {\n await registerBoxWithRelay({\n boxId: id,\n token: relayToken,\n name,\n containerName,\n createdAt,\n projectIndex,\n worktrees: gitWorktreeRecords,\n });\n log(`registered box token with relay`);\n } catch (err) {\n log(`relay register failed: ${err instanceof Error ? err.message : String(err)}`);\n relayUp = false;\n }\n }\n const relayEnv: Record<string, string> = relayUp\n ? {\n // host.docker.internal resolves to the host (where the relay node\n // process is running). The matching `--add-host` is set in runBox.\n AGENTBOX_RELAY_URL: `http://host.docker.internal:8787`,\n AGENTBOX_RELAY_TOKEN: relayToken,\n }\n : {};\n\n // VNC stack defaults on; the CLI surfaces `--no-vnc` for opt-out. Generate\n // the password and the port mapping up front so they're baked into the\n // container's env + `-p` flags before `docker run` — both must be set at\n // create time (env survives stop/start; port mappings are immutable).\n const vncEnabled = opts.vnc?.enabled !== false;\n const vncPassword = vncEnabled ? generateVncPassword() : undefined;\n const vncEnv: Record<string, string> = vncEnabled && vncPassword\n ? { AGENTBOX_VNC_PASSWORD: vncPassword }\n : {};\n const vncPortMappings = vncEnabled\n ? [{ hostPort: 0, containerPort: VNC_CONTAINER_PORT, hostIp: '127.0.0.1' }]\n : [];\n\n // Reserve the web port unconditionally: `docker run -p` is immutable, but the\n // `expose:`-flagged service is usually only known after the in-box wizard\n // writes agentbox.yaml. The supervisor forwards :80 to it later; here we just\n // guarantee a published host port exists for whenever that happens.\n const webPortMappings = [\n { hostPort: 0, containerPort: WEB_CONTAINER_PORT, hostIp: '127.0.0.1' },\n ];\n\n // Identity vars that make the box self-aware. `projectIndex` was allocated\n // earlier (right after `id`/`name`) so dir-segment helpers could see it; we\n // just read the binding here.\n const agentboxEnv: Record<string, string> = {\n AGENTBOX: '1',\n AGENTBOX_BOX_NAME: name,\n AGENTBOX_HOST_WORKSPACE: workspace,\n ...(opts.projectRoot ? { AGENTBOX_PROJECT_ROOT: opts.projectRoot } : {}),\n ...(projectIndex !== undefined\n ? { AGENTBOX_PROJECT_INDEX: String(projectIndex) }\n : {}),\n };\n const boxEnvForFile: Record<string, string> = {\n AGENTBOX_BOX_ID: id,\n ...agentboxEnv,\n };\n\n // `--storage-opt size=` is only enforced by devicemapper/btrfs/zfs.\n const appliedLimits: BoxLimitSpec | undefined = opts.limits;\n let effectiveLimits = appliedLimits;\n if (appliedLimits?.disk) {\n const driver = await dockerStorageDriver();\n if (!/^(devicemapper|btrfs|zfs|windowsfilter)$/.test(driver)) {\n log(\n `warning: --disk/box.disk is a no-op on this engine (storage-driver=${driver || 'unknown'}); ignoring`,\n );\n effectiveLimits = { ...appliedLimits, disk: null };\n }\n }\n\n await runBox({\n name: containerName,\n image: imageRef,\n extraVolumes,\n limits: effectiveLimits,\n portMappings: [...vncPortMappings, ...webPortMappings],\n env: {\n AGENTBOX_BOX_ID: id,\n ...agentboxEnv,\n ...claudeMounts.env,\n ...relayEnv,\n ...vncEnv,\n ...(opts.claudeEnv ?? {}),\n },\n });\n log(`container ${containerName} started`);\n\n // Flip the in-container parent dir of each bind-mounted `.git` to\n // vscode-owned. Docker auto-creates the intermediates (e.g. the project root\n // path that contains `.git`) as root:root 755 in the writable layer; without\n // this chown the agent can't write siblings of `.git` (`.turbo/`, `.next/`,\n // build caches) at the project root. Non-recursive — the bind-mounted `.git`\n // itself stays untouched (recursive chown would propagate to the host).\n if (gitWorktreeRecords.length > 0) {\n await chownGitBindParents({\n container: containerName,\n hostMainRepos: gitWorktreeRecords.map((w) => w.hostMainRepo),\n onLog: log,\n });\n }\n\n // /etc/agentbox/box.env: sourced by /etc/profile.d/agentbox.sh in login\n // shells (the docker-run env doesn't reach `agentbox shell <box>` cleanly\n // without it). Best-effort — env vars on the container are the primary\n // path; this file is for shells launched via tools that strip env.\n const boxEnv = await writeBoxEnvFile(containerName, boxEnvForFile);\n if (boxEnv.ok) log('wrote /etc/agentbox/box.env');\n else log(`writing /etc/agentbox/box.env failed: ${boxEnv.reason}`);\n\n // Re-own /home/vscode to vscode. Root-run exec steps (checkpoint cleanup,\n // dockerd setup) and boxes restored from a checkpoint can leave home-dir\n // files root-owned; the shell + agent run as vscode and would silently\n // fail to write them (e.g. dropped `.bash_history`). Best-effort.\n await ensureHomeOwnedByVscode(containerName);\n\n // Seed /workspace.\n // - Checkpoint restore: the image already has the source box's per-box\n // worktree dir populated; we only need to re-establish the bind mount\n // onto /workspace (docker commit doesn't capture bind-mount content).\n // - Git path: create in-container worktrees + bind + replay stash + untracked.\n // - No-git path: tar-pipe host workspace (or its APFS clone) into\n // /workspace (no bind — files live directly in the image's writable\n // layer at /workspace).\n if (!checkpointImage) {\n if (repoCarryOvers.length > 0) {\n try {\n await seedWorkspace({ container: containerName, repos: repoCarryOvers, onLog: log });\n log('seeded /workspace from in-container git worktree(s)');\n } catch (err) {\n log(\n `seedWorkspace failed; leaving ${containerName} running so you can inspect it`,\n );\n throw err;\n }\n } else {\n const source = snapshotDir ?? workspace;\n await seedWorkspaceFromDir({ container: containerName, hostSource: source, onLog: log });\n }\n } else if (restoredWorktrees && restoredWorktrees.length > 0) {\n // gitWorktreeRecords was populated above (pre-`docker run`) so the .git\n // bind-mounts in extraVolumes are wired. The /workspace bind itself\n // can't be set up until the container is running, so we apply it here.\n await bindWorktrees(\n containerName,\n restoredWorktrees.map((w) => ({\n kind: w.kind,\n containerPath: w.containerPath,\n gitWorktreePath: w.gitWorktreePath,\n })),\n log,\n );\n log('re-bound /workspace from checkpoint image');\n } else {\n log('using /workspace from checkpoint image (no worktrees recorded; no rebind)');\n }\n\n await repairIdeOwnership(containerName);\n log('.vscode-server + .cursor-server ownership verified');\n\n const ctl = await launchCtlDaemon(containerName, socketPath);\n if (ctl.up) log('agentbox-ctl daemon up');\n else log(`agentbox-ctl daemon did not become reachable: ${ctl.reason}`);\n\n // dockerd: always-on, mirrors launchVncDaemon. Best-effort — a slow start\n // shouldn't fail box creation; `agentbox start` will relaunch on restart\n // (the daemon dies with the container). The storage driver is selected at\n // runtime by agentbox-dockerd-start (overlay2, with a fuse-overlayfs\n // fallback) — see /var/log/agentbox/dockerd.log inside the box.\n const dockerd = await launchDockerdDaemon(containerName);\n if (dockerd.up) {\n log(`dockerd up (data root=${dockerVolume})`);\n } else {\n log(`dockerd did not become ready: ${dockerd.reason}`);\n }\n\n if (opts.withPlaywright) {\n log('installing @playwright/cli@latest (--with-playwright)');\n const result = await execa(\n 'docker',\n [\n 'exec',\n '--user',\n 'root',\n containerName,\n 'bash',\n '-lc',\n 'npm install -g @playwright/cli@latest 2>&1',\n ],\n { reject: false },\n );\n for (const line of (result.stdout ?? '').split('\\n')) {\n if (line.trim().length > 0) log(`[playwright] ${line}`);\n }\n if (result.exitCode !== 0) {\n throw new Error(\n `failed to install @playwright/cli (exit ${String(result.exitCode)}): ${(result.stderr ?? '').toString().slice(0, 400)}`,\n );\n }\n log('@playwright/cli installed');\n }\n\n if (opts.withEnv) {\n log('copying host env/config files into /workspace (--with-env)');\n const { copied } = await copyHostEnvFilesToBox({\n container: containerName,\n workspaceDir: workspace,\n patterns: DEFAULT_ENV_PATTERNS,\n onLog: log,\n });\n log(copied > 0 ? `copied ${String(copied)} env/config file(s)` : 'no env/config files found');\n }\n\n if (opts.envFilesToImport && opts.envFilesToImport.length > 0) {\n log(`copying ${String(opts.envFilesToImport.length)} selected env/config file(s) into /workspace`);\n const { copied } = await copyHostFilesToBox({\n container: containerName,\n workspaceDir: workspace,\n files: opts.envFilesToImport,\n onLog: log,\n });\n if (copied !== opts.envFilesToImport.length) {\n log(`copied ${String(copied)}/${String(opts.envFilesToImport.length)} selected env/config file(s)`);\n }\n }\n\n // VNC daemon (Xvnc + websockify). Best-effort, like launchCtlDaemon. The\n // host port mapping was wired into runBox above (hostPort=0 → random); we\n // resolve the assigned port here for storage. If the daemon fails to come\n // up we still record vncEnabled so `agentbox start` will retry the launch.\n let vncHostPort: number | null = null;\n if (vncEnabled) {\n const vnc = await launchVncDaemon(containerName);\n if (vnc.up) log('vnc stack up (Xvnc + websockify + noVNC)');\n else log(`vnc stack did not become reachable: ${vnc.reason}`);\n vncHostPort = await publishedHostPort(containerName, VNC_CONTAINER_PORT);\n if (vncHostPort) log(`vnc web on host 127.0.0.1:${String(vncHostPort)}`);\n }\n\n const webHostPort = await publishedHostPort(containerName, WEB_CONTAINER_PORT);\n if (webHostPort) {\n log(\n `web port reserved on host 127.0.0.1:${String(webHostPort)} ` +\n `(forwards to the web service once agentbox.yaml sets a service expose:)`,\n );\n }\n\n const record: BoxRecord = {\n id,\n name,\n container: containerName,\n image: imageRef,\n workspacePath: workspace,\n snapshotDir,\n socketPath,\n claudeConfigVolume: claudeSpec.volume,\n vscodeServerVolume: vscodeServerVolumeName(id),\n cursorServerVolume: cursorServerVolumeName(id),\n relayToken: relayUp ? relayToken : undefined,\n gitWorktrees: gitWorktreeRecords.length > 0 ? gitWorktreeRecords : undefined,\n withPlaywright: opts.withPlaywright ? true : undefined,\n withEnv: opts.withEnv ? true : undefined,\n vncEnabled: vncEnabled ? true : undefined,\n vncContainerPort: vncEnabled ? VNC_CONTAINER_PORT : undefined,\n vncHostPort: vncHostPort ?? undefined,\n vncPassword: vncPassword,\n webContainerPort: WEB_CONTAINER_PORT,\n webHostPort: webHostPort ?? undefined,\n dockerVolume,\n dockerCacheShared: dockerCacheShared || undefined,\n projectRoot: opts.projectRoot,\n projectIndex,\n checkpointImage,\n checkpointSource,\n resourceLimits: persistableLimits(effectiveLimits),\n createdAt,\n };\n await recordBox(record);\n\n return { record, imageBuilt: built };\n}\n","import { chmod, mkdir, readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { execa } from 'execa';\nimport type { ClaudeConfigSpec } from './claude.js';\nimport { STATE_DIR } from './state.js';\n\n/**\n * Host-side backup of the in-box Claude Code OAuth credentials.\n *\n * Claude's interactive login (inside a box) writes the only Linux auth file:\n * `~/.claude/.credentials.json`, holding a `claudeAiOauth` block with a real\n * `refreshToken` — that refresh token is what makes claude report \"Claude Max\"\n * instead of \"Claude API\". This host backup mirrors that blob so a fresh\n * `--isolate-claude-config` box, or a recreated shared volume, can be seeded\n * with the user's login instead of forcing a re-login.\n *\n * Security: the file holds a live refresh token. It is written mode 0600 under\n * `~/.agentbox/` (already the 0600-convention dir for `auth.json`). Plaintext\n * on disk matches the existing model — the claude-config volume already stores\n * the same blob in the clear; macOS Keychain is the *host* claude's store and\n * is unreachable from a Linux container.\n */\nexport const CREDENTIALS_BACKUP_FILE = join(STATE_DIR, 'claude-credentials.json');\n\nexport type CredentialSyncDirection = 'extracted' | 'seeded' | 'noop';\n\nexport interface SyncClaudeCredentialsResult {\n /**\n * `extracted` — the volume's live credentials were copied out to the host\n * backup. `seeded` — the host backup was copied into an empty volume.\n * `noop` — nothing to do (or the best-effort helper failed silently).\n */\n direction: CredentialSyncDirection;\n /**\n * Whether the volume holds a real OAuth blob *after* the sync (true also\n * right after a `seeded`). When false, the in-box claude will fall back to\n * the setup-token / API key, or prompt for an interactive login.\n */\n volumeHasCredentials: boolean;\n}\n\n/**\n * True iff the host backup file holds a real OAuth blob (a non-empty\n * `claudeAiOauth.refreshToken`). Used to decide whether to offer an\n * interactive sign-in before creating a box. Tolerant of a missing or\n * garbage file — returns false.\n */\nexport async function hostBackupHasCredentials(\n path: string = CREDENTIALS_BACKUP_FILE,\n): Promise<boolean> {\n try {\n const parsed = JSON.parse(await readFile(path, 'utf8')) as {\n claudeAiOauth?: { refreshToken?: unknown };\n };\n const rt = parsed?.claudeAiOauth?.refreshToken;\n return typeof rt === 'string' && rt.length > 0;\n } catch {\n return false;\n }\n}\n\n/**\n * Parse the `EXTRACTED=<yes|no> SEEDED=<yes|no> VOLREAL=<yes|no>` line the\n * helper container prints. Pure — unit-tested independently of docker.\n */\nexport function parseSyncResult(stdout: string): SyncClaudeCredentialsResult {\n const volumeHasCredentials = /\\bVOLREAL=yes\\b/.test(stdout);\n if (/\\bEXTRACTED=yes\\b/.test(stdout)) return { direction: 'extracted', volumeHasCredentials };\n if (/\\bSEEDED=yes\\b/.test(stdout)) return { direction: 'seeded', volumeHasCredentials };\n return { direction: 'noop', volumeHasCredentials };\n}\n\n// Bidirectional, best-effort. `jq` ships in the box image. A credentials blob\n// only counts as \"real\" when `claudeAiOauth.refreshToken` is a non-empty\n// string — a setup-token blob has an accessToken but no refreshToken, and\n// seeding/extracting it would just reproduce the \"Claude API\" label. The\n// final `echo` always runs so the caller can read the outcome; a failed `cp`\n// leaves the flag at `no` and the whole thing still exits 0.\nconst SYNC_SCRIPT = `\nEXTRACTED=no\nSEEDED=no\nVOL=/dst/.credentials.json\nHOST=/host-state/claude-credentials.json\nif [ -f \"$VOL\" ] && jq -e '(.claudeAiOauth.refreshToken // \"\") | length > 0' \"$VOL\" >/dev/null 2>&1; then VOL_REAL=yes; else VOL_REAL=no; fi\nif [ -f \"$HOST\" ] && jq -e '(.claudeAiOauth.refreshToken // \"\") | length > 0' \"$HOST\" >/dev/null 2>&1; then HOST_REAL=yes; else HOST_REAL=no; fi\nif [ \"$VOL_REAL\" = yes ] && [ \"$ISOLATE\" != yes ]; then\n cp -a \"$VOL\" \"$HOST\" && chmod 600 \"$HOST\" && EXTRACTED=yes\nelif [ \"$VOL_REAL\" = no ] && [ \"$HOST_REAL\" = yes ]; then\n cp -a \"$HOST\" \"$VOL\" && chown 1000:1000 \"$VOL\" && chmod 600 \"$VOL\" && SEEDED=yes && VOL_REAL=yes\nfi\necho \"EXTRACTED=$EXTRACTED SEEDED=$SEEDED VOLREAL=$VOL_REAL\"\n`;\n\n/**\n * Bidirectionally sync the box's `.credentials.json` with the host backup:\n *\n * - volume has a real OAuth blob and the box is **not** isolate → copy it OUT\n * to {@link CREDENTIALS_BACKUP_FILE} (the volume is authoritative — it is\n * the live copy the in-box claude refreshes).\n * - volume has no `.credentials.json` and the host backup is real → copy it\n * IN (seeds a fresh isolate box, or a recreated shared volume).\n * - otherwise → noop.\n *\n * Isolate boxes are read-seed only — never extract — so N isolate boxes can't\n * race on the single host backup.\n *\n * Best-effort: any failure resolves to `{ direction: 'noop' }` and never\n * throws into a box operation.\n */\nexport async function syncClaudeCredentials(\n spec: ClaudeConfigSpec,\n opts: { image: string; isolate: boolean },\n): Promise<SyncClaudeCredentialsResult> {\n try {\n await mkdir(STATE_DIR, { recursive: true });\n const { stdout } = await execa('docker', [\n 'run',\n '--rm',\n '--user',\n '0',\n '-v',\n `${spec.volume}:/dst`,\n '-v',\n `${STATE_DIR}:/host-state`,\n '-e',\n `ISOLATE=${opts.isolate ? 'yes' : 'no'}`,\n opts.image,\n 'sh',\n '-c',\n SYNC_SCRIPT,\n ]);\n const result = parseSyncResult(stdout);\n // The helper runs as root; re-assert 0600 from the host side so the backup\n // is owner-only regardless of how the bind mount maps ownership.\n if (result.direction === 'extracted') {\n await chmod(CREDENTIALS_BACKUP_FILE, 0o600).catch(() => {});\n }\n return result;\n } catch {\n return { direction: 'noop', volumeHasCredentials: false };\n }\n}\n","import { execa } from 'execa';\n\n/**\n * Writes /etc/agentbox/box.env inside the container as a POSIX-sourceable\n * key='value' file. Paired with /etc/profile.d/agentbox.sh (baked in the\n * image), which `set -a; . /etc/agentbox/box.env; set +a`s it on login.\n *\n * Best-effort: failure is logged by the caller; an unwritable file just\n * means interactive shells lose the AGENTBOX_* vars (the env vars baked\n * into docker run still survive).\n */\nexport async function writeBoxEnvFile(\n container: string,\n env: Record<string, string>,\n): Promise<{ ok: true } | { ok: false; reason: string }> {\n const body = formatBoxEnvBody(env);\n const result = await execa(\n 'docker',\n ['exec', '--user', 'root', '-i', container, 'sh', '-c', 'umask 022 && cat > /etc/agentbox/box.env'],\n { input: body, reject: false },\n );\n if (result.exitCode !== 0) {\n return {\n ok: false,\n reason: `docker exec failed (exit ${String(result.exitCode)}): ${(result.stderr ?? '').toString().slice(0, 400)}`,\n };\n }\n return { ok: true };\n}\n\n// Single-quote each value and escape embedded single quotes as '\\''. Avoids\n// double-quoted form because `. ` would expand $foo / `cmd` at source time.\nexport function formatBoxEnvBody(env: Record<string, string>): string {\n const lines: string[] = [];\n for (const [k, v] of Object.entries(env)) {\n lines.push(`${k}=${shellSingleQuote(v)}`);\n }\n return lines.join('\\n') + '\\n';\n}\n\nfunction shellSingleQuote(s: string): string {\n return `'${s.replace(/'/g, `'\\\\''`)}'`;\n}\n"],"mappings":"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","names":["mkdir","join","execa","execa","join","mkdir"]}
|
package/dist/chunk-M7I247BK.js
DELETED
|
@@ -1,525 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
RELAY_CONTAINER_NAME,
|
|
4
|
-
RELAY_IMAGE_REF,
|
|
5
|
-
RELAY_NETWORK_NAME,
|
|
6
|
-
SHARED_CLAUDE_VOLUME,
|
|
7
|
-
SHARED_CURSOR_EXTENSIONS_VOLUME,
|
|
8
|
-
SHARED_DOCKER_CACHE_VOLUME,
|
|
9
|
-
SHARED_VSCODE_EXTENSIONS_VOLUME,
|
|
10
|
-
SNAPSHOTS_ROOT,
|
|
11
|
-
VNC_CONTAINER_PORT,
|
|
12
|
-
WEB_CONTAINER_PORT,
|
|
13
|
-
bindWorktrees,
|
|
14
|
-
buildVncUrls,
|
|
15
|
-
claudeSessionInfo,
|
|
16
|
-
cursorServerVolumeName,
|
|
17
|
-
ensureHomeOwnedByVscode,
|
|
18
|
-
ensureRelay,
|
|
19
|
-
forgetBoxFromRelay,
|
|
20
|
-
launchCtlDaemon,
|
|
21
|
-
launchDockerdDaemon,
|
|
22
|
-
launchVncDaemon,
|
|
23
|
-
loadConfig,
|
|
24
|
-
registerBoxWithRelay,
|
|
25
|
-
removeInBoxWorktree,
|
|
26
|
-
vscodeServerVolumeName
|
|
27
|
-
} from "./chunk-HTTKML3C.js";
|
|
28
|
-
import {
|
|
29
|
-
findBox,
|
|
30
|
-
readState,
|
|
31
|
-
recordBox,
|
|
32
|
-
removeBoxRecord
|
|
33
|
-
} from "./chunk-HPZMD5DE.js";
|
|
34
|
-
import {
|
|
35
|
-
BOXES_ROOT,
|
|
36
|
-
CHECKPOINT_IMAGE_PREFIX,
|
|
37
|
-
boxRunDirFor,
|
|
38
|
-
detectEngine,
|
|
39
|
-
getHostPaths,
|
|
40
|
-
inspectContainer,
|
|
41
|
-
inspectContainerStatus,
|
|
42
|
-
listAgentboxContainers,
|
|
43
|
-
listAgentboxVolumes,
|
|
44
|
-
listAllCheckpointImages,
|
|
45
|
-
openInFinder,
|
|
46
|
-
pauseContainer,
|
|
47
|
-
publishedHostPort,
|
|
48
|
-
readBoxStatus,
|
|
49
|
-
removeContainer,
|
|
50
|
-
removeImage,
|
|
51
|
-
removeNetwork,
|
|
52
|
-
removeVolume,
|
|
53
|
-
startContainer,
|
|
54
|
-
stopContainer,
|
|
55
|
-
unpauseContainer
|
|
56
|
-
} from "./chunk-HHMWQNLF.js";
|
|
57
|
-
|
|
58
|
-
// ../../packages/sandbox-docker/dist/chunk-EPUCTJVM.js
|
|
59
|
-
import { execa } from "execa";
|
|
60
|
-
import { readdir, rm, stat } from "fs/promises";
|
|
61
|
-
import { join as join2 } from "path";
|
|
62
|
-
import { join } from "path";
|
|
63
|
-
async function getBoxEndpoints(record, engine, persisted) {
|
|
64
|
-
const domainIsOrb = engine === "orbstack";
|
|
65
|
-
const domain = domainIsOrb ? `${record.container}.orb.local` : "127.0.0.1";
|
|
66
|
-
const endpoints = [];
|
|
67
|
-
if (record.vncEnabled && record.vncPassword) {
|
|
68
|
-
const vncUrls = buildVncUrls(record, engine);
|
|
69
|
-
const url = vncUrls.orbUrl ?? vncUrls.loopbackUrl;
|
|
70
|
-
endpoints.push({
|
|
71
|
-
kind: "vnc",
|
|
72
|
-
name: "vnc",
|
|
73
|
-
containerPort: VNC_CONTAINER_PORT,
|
|
74
|
-
url,
|
|
75
|
-
reachable: Boolean(url)
|
|
76
|
-
});
|
|
77
|
-
}
|
|
78
|
-
let webServiceName = null;
|
|
79
|
-
const persistedWeb = persisted?.services.find((s) => s.expose);
|
|
80
|
-
if (persistedWeb) {
|
|
81
|
-
webServiceName = persistedWeb.name;
|
|
82
|
-
}
|
|
83
|
-
const pushService = (name, port) => {
|
|
84
|
-
if (name === webServiceName) return;
|
|
85
|
-
endpoints.push({
|
|
86
|
-
kind: "service",
|
|
87
|
-
name,
|
|
88
|
-
containerPort: port,
|
|
89
|
-
// Only OrbStack auto-routes arbitrary in-box ports; on other engines we
|
|
90
|
-
// don't publish service ports, so the URL isn't host-reachable.
|
|
91
|
-
...domainIsOrb ? { url: `http://${domain}:${String(port)}`, reachable: true } : { reachable: false }
|
|
92
|
-
});
|
|
93
|
-
};
|
|
94
|
-
const persistedServices = persisted?.services.filter(
|
|
95
|
-
(s) => typeof s.port === "number"
|
|
96
|
-
);
|
|
97
|
-
if (persistedServices && persistedServices.length > 0) {
|
|
98
|
-
for (const svc of persistedServices) pushService(svc.name, svc.port);
|
|
99
|
-
} else {
|
|
100
|
-
try {
|
|
101
|
-
const cfg = await loadConfig(join(record.workspacePath, "agentbox.yaml"));
|
|
102
|
-
if (!webServiceName) {
|
|
103
|
-
webServiceName = cfg.services.find((s) => s.expose)?.name ?? null;
|
|
104
|
-
}
|
|
105
|
-
for (const svc of cfg.services) {
|
|
106
|
-
if (svc.readyWhen?.kind !== "port") continue;
|
|
107
|
-
pushService(svc.name, svc.readyWhen.port);
|
|
108
|
-
}
|
|
109
|
-
} catch {
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
if (record.webContainerPort !== void 0) {
|
|
113
|
-
const hasTarget = webServiceName !== null && record.webHostPort !== void 0;
|
|
114
|
-
endpoints.push({
|
|
115
|
-
kind: "web",
|
|
116
|
-
name: webServiceName ?? "web",
|
|
117
|
-
containerPort: record.webContainerPort ?? WEB_CONTAINER_PORT,
|
|
118
|
-
...hasTarget ? { url: `http://127.0.0.1:${String(record.webHostPort)}`, reachable: true } : { reachable: false }
|
|
119
|
-
});
|
|
120
|
-
}
|
|
121
|
-
return { domain, domainIsOrb, endpoints };
|
|
122
|
-
}
|
|
123
|
-
async function listBoxes() {
|
|
124
|
-
const { boxes } = await readState();
|
|
125
|
-
const engine = await detectEngine();
|
|
126
|
-
return Promise.all(
|
|
127
|
-
boxes.map(async (b) => {
|
|
128
|
-
const state = await inspectContainerStatus(b.container);
|
|
129
|
-
const persisted = await readBoxStatus(b);
|
|
130
|
-
const endpoints = await getBoxEndpoints(b, engine, persisted);
|
|
131
|
-
return {
|
|
132
|
-
...b,
|
|
133
|
-
state,
|
|
134
|
-
endpoints,
|
|
135
|
-
claudeActivity: persisted?.claude.state,
|
|
136
|
-
claudeSessionTitle: persisted?.claude.sessionTitle
|
|
137
|
-
};
|
|
138
|
-
})
|
|
139
|
-
);
|
|
140
|
-
}
|
|
141
|
-
var BoxNotFoundError = class extends Error {
|
|
142
|
-
constructor(query) {
|
|
143
|
-
super(`no agentbox matches "${query}"`);
|
|
144
|
-
this.query = query;
|
|
145
|
-
this.name = "BoxNotFoundError";
|
|
146
|
-
}
|
|
147
|
-
query;
|
|
148
|
-
};
|
|
149
|
-
var AmbiguousBoxError = class extends Error {
|
|
150
|
-
constructor(query, matches) {
|
|
151
|
-
const ids = matches.map((m) => m.id).join(", ");
|
|
152
|
-
super(`"${query}" matches multiple boxes: ${ids}`);
|
|
153
|
-
this.query = query;
|
|
154
|
-
this.matches = matches;
|
|
155
|
-
this.name = "AmbiguousBoxError";
|
|
156
|
-
}
|
|
157
|
-
query;
|
|
158
|
-
matches;
|
|
159
|
-
};
|
|
160
|
-
async function pathExists(p) {
|
|
161
|
-
try {
|
|
162
|
-
await stat(p);
|
|
163
|
-
return true;
|
|
164
|
-
} catch {
|
|
165
|
-
return false;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
async function resolveBox(idOrName) {
|
|
169
|
-
const state = await readState();
|
|
170
|
-
const result = findBox(idOrName, state);
|
|
171
|
-
switch (result.kind) {
|
|
172
|
-
case "ok":
|
|
173
|
-
return result.box;
|
|
174
|
-
case "none":
|
|
175
|
-
throw new BoxNotFoundError(idOrName);
|
|
176
|
-
case "ambiguous":
|
|
177
|
-
throw new AmbiguousBoxError(idOrName, result.matches);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
async function pauseBox(idOrName) {
|
|
181
|
-
const box = await resolveBox(idOrName);
|
|
182
|
-
await pauseContainer(box.container);
|
|
183
|
-
return box;
|
|
184
|
-
}
|
|
185
|
-
async function unpauseBox(idOrName) {
|
|
186
|
-
const box = await resolveBox(idOrName);
|
|
187
|
-
await unpauseContainer(box.container);
|
|
188
|
-
return box;
|
|
189
|
-
}
|
|
190
|
-
async function stopBox(idOrName) {
|
|
191
|
-
const box = await resolveBox(idOrName);
|
|
192
|
-
await stopContainer(box.container);
|
|
193
|
-
return box;
|
|
194
|
-
}
|
|
195
|
-
async function startBox(idOrName) {
|
|
196
|
-
const box = await resolveBox(idOrName);
|
|
197
|
-
for (const w of box.gitWorktrees ?? []) {
|
|
198
|
-
if (!await pathExists(join2(w.hostMainRepo, ".git"))) {
|
|
199
|
-
throw new Error(
|
|
200
|
-
`main repo for box worktree missing: ${join2(w.hostMainRepo, ".git")} (recreate the box)`
|
|
201
|
-
);
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
await startContainer(box.container);
|
|
205
|
-
if ((box.gitWorktrees ?? []).length > 0) {
|
|
206
|
-
await bindWorktrees(
|
|
207
|
-
box.container,
|
|
208
|
-
(box.gitWorktrees ?? []).map((w) => ({
|
|
209
|
-
kind: w.kind,
|
|
210
|
-
containerPath: w.containerPath,
|
|
211
|
-
gitWorktreePath: w.gitWorktreePath
|
|
212
|
-
}))
|
|
213
|
-
);
|
|
214
|
-
}
|
|
215
|
-
await ensureHomeOwnedByVscode(box.container);
|
|
216
|
-
if (box.socketPath) {
|
|
217
|
-
await launchCtlDaemon(box.container, box.socketPath);
|
|
218
|
-
}
|
|
219
|
-
if (box.dockerVolume) {
|
|
220
|
-
await launchDockerdDaemon(box.container);
|
|
221
|
-
}
|
|
222
|
-
if (box.vncEnabled) {
|
|
223
|
-
await launchVncDaemon(box.container);
|
|
224
|
-
const freshHostPort = await publishedHostPort(box.container, VNC_CONTAINER_PORT);
|
|
225
|
-
if (freshHostPort && freshHostPort !== box.vncHostPort) {
|
|
226
|
-
box.vncHostPort = freshHostPort;
|
|
227
|
-
await recordBox(box);
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
if (box.webContainerPort !== void 0) {
|
|
231
|
-
const freshWebPort = await publishedHostPort(
|
|
232
|
-
box.container,
|
|
233
|
-
box.webContainerPort ?? WEB_CONTAINER_PORT
|
|
234
|
-
);
|
|
235
|
-
if (freshWebPort && freshWebPort !== box.webHostPort) {
|
|
236
|
-
box.webHostPort = freshWebPort;
|
|
237
|
-
await recordBox(box);
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
if (box.relayToken) {
|
|
241
|
-
try {
|
|
242
|
-
await ensureRelay();
|
|
243
|
-
await registerBoxWithRelay({
|
|
244
|
-
boxId: box.id,
|
|
245
|
-
token: box.relayToken,
|
|
246
|
-
name: box.name,
|
|
247
|
-
containerName: box.container,
|
|
248
|
-
createdAt: box.createdAt,
|
|
249
|
-
projectIndex: box.projectIndex,
|
|
250
|
-
worktrees: box.gitWorktrees
|
|
251
|
-
});
|
|
252
|
-
} catch {
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
return { record: box };
|
|
256
|
-
}
|
|
257
|
-
async function openBoxInFinder(idOrName, opts) {
|
|
258
|
-
const box = await resolveBox(idOrName);
|
|
259
|
-
const result = await openInFinder(box, opts);
|
|
260
|
-
return { ...result, record: box };
|
|
261
|
-
}
|
|
262
|
-
async function getBoxHostPaths(idOrName) {
|
|
263
|
-
const box = await resolveBox(idOrName);
|
|
264
|
-
const paths = await getHostPaths(box);
|
|
265
|
-
return { record: box, paths };
|
|
266
|
-
}
|
|
267
|
-
async function dirSizeBytes(path) {
|
|
268
|
-
try {
|
|
269
|
-
const result = await execa("du", ["-sk", path], { reject: false });
|
|
270
|
-
if (result.exitCode !== 0) return null;
|
|
271
|
-
const sizeKb = Number.parseInt((result.stdout ?? "").split(/\s+/)[0] ?? "", 10);
|
|
272
|
-
if (Number.isNaN(sizeKb)) return null;
|
|
273
|
-
return sizeKb * 1024;
|
|
274
|
-
} catch {
|
|
275
|
-
return null;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
async function inspectBox(idOrName) {
|
|
279
|
-
const record = await resolveBox(idOrName);
|
|
280
|
-
const state = await inspectContainerStatus(record.container);
|
|
281
|
-
const snapshotSizeBytes = record.snapshotDir ? await dirSizeBytes(record.snapshotDir) : null;
|
|
282
|
-
const dockerJson = await inspectContainer(record.container);
|
|
283
|
-
let claudeSession = null;
|
|
284
|
-
if (state === "running") {
|
|
285
|
-
try {
|
|
286
|
-
claudeSession = await claudeSessionInfo(record.container);
|
|
287
|
-
} catch {
|
|
288
|
-
claudeSession = null;
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
const hostPaths = await getHostPaths(record);
|
|
292
|
-
const engine = await detectEngine();
|
|
293
|
-
const persistedStatus = await readBoxStatus(record);
|
|
294
|
-
const endpoints = await getBoxEndpoints(record, engine, persistedStatus);
|
|
295
|
-
return {
|
|
296
|
-
record,
|
|
297
|
-
state,
|
|
298
|
-
snapshotSizeBytes,
|
|
299
|
-
dockerInspect: dockerJson,
|
|
300
|
-
claudeSession,
|
|
301
|
-
persistedStatus,
|
|
302
|
-
hostPaths,
|
|
303
|
-
endpoints
|
|
304
|
-
};
|
|
305
|
-
}
|
|
306
|
-
async function destroyBox(idOrName, opts = {}) {
|
|
307
|
-
const box = await resolveBox(idOrName);
|
|
308
|
-
if (box.relayToken) {
|
|
309
|
-
try {
|
|
310
|
-
await forgetBoxFromRelay(box.id);
|
|
311
|
-
} catch {
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
const ownsWorktrees = !box.checkpointImage;
|
|
315
|
-
if (ownsWorktrees) {
|
|
316
|
-
for (const w of box.gitWorktrees ?? []) {
|
|
317
|
-
try {
|
|
318
|
-
await removeInBoxWorktree({
|
|
319
|
-
hostMainRepo: w.hostMainRepo,
|
|
320
|
-
gitWorktreePath: w.gitWorktreePath
|
|
321
|
-
});
|
|
322
|
-
} catch {
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
const beforeContainer = await inspectContainerStatus(box.container);
|
|
327
|
-
await removeContainer(box.container);
|
|
328
|
-
const afterContainer = await inspectContainerStatus(box.container);
|
|
329
|
-
const removedContainer = beforeContainer !== "missing" && afterContainer === "missing";
|
|
330
|
-
const removedVolumes = [];
|
|
331
|
-
if (box.claudeConfigVolume && box.claudeConfigVolume !== SHARED_CLAUDE_VOLUME) {
|
|
332
|
-
await removeVolume(box.claudeConfigVolume);
|
|
333
|
-
removedVolumes.push(box.claudeConfigVolume);
|
|
334
|
-
}
|
|
335
|
-
const perBoxIdeVolumes = [
|
|
336
|
-
box.vscodeServerVolume ?? vscodeServerVolumeName(box.id),
|
|
337
|
-
box.cursorServerVolume ?? cursorServerVolumeName(box.id)
|
|
338
|
-
];
|
|
339
|
-
for (const v of perBoxIdeVolumes) {
|
|
340
|
-
await removeVolume(v);
|
|
341
|
-
removedVolumes.push(v);
|
|
342
|
-
}
|
|
343
|
-
if (box.dockerVolume && !box.dockerCacheShared) {
|
|
344
|
-
await removeVolume(box.dockerVolume);
|
|
345
|
-
removedVolumes.push(box.dockerVolume);
|
|
346
|
-
}
|
|
347
|
-
let removedSnapshot = null;
|
|
348
|
-
if (box.snapshotDir && !opts.keepSnapshot) {
|
|
349
|
-
try {
|
|
350
|
-
await rm(box.snapshotDir, { recursive: true, force: true });
|
|
351
|
-
removedSnapshot = box.snapshotDir;
|
|
352
|
-
} catch {
|
|
353
|
-
removedSnapshot = null;
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
try {
|
|
357
|
-
await rm(boxRunDirFor(box), { recursive: true, force: true });
|
|
358
|
-
} catch {
|
|
359
|
-
}
|
|
360
|
-
await removeBoxRecord(box.id);
|
|
361
|
-
return { record: box, removedContainer, removedVolumes, removedSnapshot };
|
|
362
|
-
}
|
|
363
|
-
async function listSnapshotDirs() {
|
|
364
|
-
try {
|
|
365
|
-
const entries = await readdir(SNAPSHOTS_ROOT, { withFileTypes: true });
|
|
366
|
-
return entries.filter((e) => e.isDirectory()).map((e) => join2(SNAPSHOTS_ROOT, e.name));
|
|
367
|
-
} catch {
|
|
368
|
-
return [];
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
async function listBoxDirs() {
|
|
372
|
-
try {
|
|
373
|
-
const entries = await readdir(BOXES_ROOT, { withFileTypes: true });
|
|
374
|
-
return entries.filter((e) => e.isDirectory()).map((e) => join2(BOXES_ROOT, e.name));
|
|
375
|
-
} catch {
|
|
376
|
-
return [];
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
async function listCheckpointImageTags() {
|
|
380
|
-
const r = await execa(
|
|
381
|
-
"docker",
|
|
382
|
-
["image", "ls", "--format", "{{.Repository}}:{{.Tag}}", `${CHECKPOINT_IMAGE_PREFIX}*`],
|
|
383
|
-
{ reject: false }
|
|
384
|
-
);
|
|
385
|
-
if (r.exitCode !== 0) return [];
|
|
386
|
-
return (r.stdout ?? "").split("\n").map((s) => s.trim()).filter((s) => s.startsWith(CHECKPOINT_IMAGE_PREFIX));
|
|
387
|
-
}
|
|
388
|
-
async function pruneBoxes(opts = {}) {
|
|
389
|
-
const dryRun = opts.dryRun ?? false;
|
|
390
|
-
const all = opts.all ?? false;
|
|
391
|
-
const { boxes } = await readState();
|
|
392
|
-
const stateChecks = await Promise.all(
|
|
393
|
-
boxes.map(async (b) => ({ box: b, status: await inspectContainerStatus(b.container) }))
|
|
394
|
-
);
|
|
395
|
-
const missingRecords = stateChecks.filter((c) => c.status === "missing").map((c) => c.box);
|
|
396
|
-
let orphanContainers = [];
|
|
397
|
-
let orphanVolumes = [];
|
|
398
|
-
let orphanSnapshots = [];
|
|
399
|
-
let orphanBoxDirs = [];
|
|
400
|
-
let orphanCheckpointImages = [];
|
|
401
|
-
if (all) {
|
|
402
|
-
const liveContainers = await listAgentboxContainers();
|
|
403
|
-
const liveVolumes = await listAgentboxVolumes();
|
|
404
|
-
const liveSnapshotDirs = await listSnapshotDirs();
|
|
405
|
-
const liveBoxDirs = await listBoxDirs();
|
|
406
|
-
const liveCheckpointImages = await listCheckpointImageTags();
|
|
407
|
-
const manifestPinnedImages = await listAllCheckpointImages();
|
|
408
|
-
const survivingBoxes = boxes.filter((b) => !missingRecords.some((m) => m.id === b.id));
|
|
409
|
-
const expectedContainers = /* @__PURE__ */ new Set([
|
|
410
|
-
...survivingBoxes.map((b) => b.container)
|
|
411
|
-
// The relay no longer runs as a container; leftovers are collected
|
|
412
|
-
// below.
|
|
413
|
-
]);
|
|
414
|
-
const expectedVolumes = /* @__PURE__ */ new Set([
|
|
415
|
-
...survivingBoxes.map((b) => b.claudeConfigVolume).filter((v) => typeof v === "string"),
|
|
416
|
-
...survivingBoxes.map((b) => b.vscodeServerVolume).filter((v) => typeof v === "string"),
|
|
417
|
-
...survivingBoxes.map((b) => b.cursorServerVolume).filter((v) => typeof v === "string"),
|
|
418
|
-
...survivingBoxes.map((b) => b.dockerVolume).filter((v) => typeof v === "string"),
|
|
419
|
-
// The shared claude-config volume holds user identity across every box;
|
|
420
|
-
// never reap it via prune even if no surviving box currently references it.
|
|
421
|
-
SHARED_CLAUDE_VOLUME,
|
|
422
|
-
// Shared across boxes: downloaded IDE extensions. Same reasoning.
|
|
423
|
-
SHARED_VSCODE_EXTENSIONS_VOLUME,
|
|
424
|
-
SHARED_CURSOR_EXTENSIONS_VOLUME,
|
|
425
|
-
// Shared in-box docker image cache — opt-in via `box.dockerCacheShared`,
|
|
426
|
-
// never auto-removed (image layers may be reused by future boxes).
|
|
427
|
-
SHARED_DOCKER_CACHE_VOLUME
|
|
428
|
-
]);
|
|
429
|
-
const expectedSnapshots = new Set(
|
|
430
|
-
survivingBoxes.filter(
|
|
431
|
-
(b) => typeof b.snapshotDir === "string"
|
|
432
|
-
).map((b) => b.snapshotDir)
|
|
433
|
-
);
|
|
434
|
-
const expectedBoxDirs = new Set(survivingBoxes.map((b) => boxRunDirFor(b)));
|
|
435
|
-
const expectedCheckpointImages = /* @__PURE__ */ new Set([
|
|
436
|
-
...survivingBoxes.map((b) => b.checkpointImage).filter((v) => typeof v === "string"),
|
|
437
|
-
...manifestPinnedImages
|
|
438
|
-
]);
|
|
439
|
-
orphanContainers = liveContainers.filter((c) => !expectedContainers.has(c));
|
|
440
|
-
orphanVolumes = liveVolumes.filter((v) => !expectedVolumes.has(v));
|
|
441
|
-
orphanSnapshots = liveSnapshotDirs.filter((d) => !expectedSnapshots.has(d));
|
|
442
|
-
orphanBoxDirs = liveBoxDirs.filter((d) => !expectedBoxDirs.has(d));
|
|
443
|
-
orphanCheckpointImages = liveCheckpointImages.filter(
|
|
444
|
-
(t) => !expectedCheckpointImages.has(t)
|
|
445
|
-
);
|
|
446
|
-
}
|
|
447
|
-
if (dryRun) {
|
|
448
|
-
return {
|
|
449
|
-
removedRecords: missingRecords.map((b) => b.id),
|
|
450
|
-
removedContainers: orphanContainers,
|
|
451
|
-
removedVolumes: orphanVolumes,
|
|
452
|
-
removedSnapshotDirs: orphanSnapshots,
|
|
453
|
-
removedBoxDirs: orphanBoxDirs,
|
|
454
|
-
removedCheckpointImages: orphanCheckpointImages,
|
|
455
|
-
dryRun: true
|
|
456
|
-
};
|
|
457
|
-
}
|
|
458
|
-
for (const b of missingRecords) await removeBoxRecord(b.id);
|
|
459
|
-
for (const c of orphanContainers) await removeContainer(c);
|
|
460
|
-
for (const v of orphanVolumes) await removeVolume(v);
|
|
461
|
-
for (const d of orphanSnapshots) {
|
|
462
|
-
try {
|
|
463
|
-
await rm(d, { recursive: true, force: true });
|
|
464
|
-
} catch {
|
|
465
|
-
}
|
|
466
|
-
}
|
|
467
|
-
for (const d of orphanBoxDirs) {
|
|
468
|
-
try {
|
|
469
|
-
await rm(d, { recursive: true, force: true });
|
|
470
|
-
} catch {
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
for (const img of orphanCheckpointImages) {
|
|
474
|
-
await removeImage(img, { force: true });
|
|
475
|
-
}
|
|
476
|
-
if (all) {
|
|
477
|
-
try {
|
|
478
|
-
await removeContainer(RELAY_CONTAINER_NAME);
|
|
479
|
-
} catch {
|
|
480
|
-
}
|
|
481
|
-
try {
|
|
482
|
-
await execa("docker", ["image", "rm", RELAY_IMAGE_REF], { reject: false });
|
|
483
|
-
} catch {
|
|
484
|
-
}
|
|
485
|
-
try {
|
|
486
|
-
await removeNetwork(RELAY_NETWORK_NAME);
|
|
487
|
-
} catch {
|
|
488
|
-
}
|
|
489
|
-
}
|
|
490
|
-
return {
|
|
491
|
-
removedRecords: missingRecords.map((b) => b.id),
|
|
492
|
-
removedContainers: orphanContainers,
|
|
493
|
-
removedVolumes: orphanVolumes,
|
|
494
|
-
removedSnapshotDirs: orphanSnapshots,
|
|
495
|
-
removedBoxDirs: orphanBoxDirs,
|
|
496
|
-
removedCheckpointImages: orphanCheckpointImages,
|
|
497
|
-
dryRun: false
|
|
498
|
-
};
|
|
499
|
-
}
|
|
500
|
-
async function snapshotPresent(path) {
|
|
501
|
-
if (!path) return false;
|
|
502
|
-
try {
|
|
503
|
-
const s = await stat(path);
|
|
504
|
-
return s.isDirectory();
|
|
505
|
-
} catch {
|
|
506
|
-
return false;
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
|
|
510
|
-
export {
|
|
511
|
-
listBoxes,
|
|
512
|
-
BoxNotFoundError,
|
|
513
|
-
AmbiguousBoxError,
|
|
514
|
-
pauseBox,
|
|
515
|
-
unpauseBox,
|
|
516
|
-
stopBox,
|
|
517
|
-
startBox,
|
|
518
|
-
openBoxInFinder,
|
|
519
|
-
getBoxHostPaths,
|
|
520
|
-
inspectBox,
|
|
521
|
-
destroyBox,
|
|
522
|
-
pruneBoxes,
|
|
523
|
-
snapshotPresent
|
|
524
|
-
};
|
|
525
|
-
//# sourceMappingURL=chunk-M7I247BK.js.map
|