Skip to content

Import @amika/sandbox from amika-mono as a JS workspace - #330

Open
dbmikus wants to merge 2 commits into
mainfrom
dylan/js-sandbox-oss-migration
Open

Import @amika/sandbox from amika-mono as a JS workspace#330
dbmikus wants to merge 2 commits into
mainfrom
dylan/js-sandbox-oss-migration

Conversation

@dbmikus

@dbmikus dbmikus commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Implements the amika-side half of amika-mono's specs/020-sandbox-oss-submodule-migration.md. @amika/sandbox — the Amika-agnostic sandbox provider abstraction — moves out of the closed-source amika-mono repo and into this one. amika-mono consumes it back as a git submodule mounted at repos/amika/, registered as a normal pnpm workspace member, so all four existing consumers keep importing @amika/sandbox through workspace:* with no import-site changes.

This repo becomes the single source of truth for the package: changes land here and reach amika-mono through a pointer bump.

What's here

Import @amika/sandbox from amika-mono as a JS workspace

  • A JS workspace at the repo root: pnpm-workspace.yaml + package.json declaring js/* and eslint-rules as members, plus a committed pnpm-lock.yaml.
  • js/sandbox/, copied from amika-mono.
  • eslint-rules/, which came along out of necessity — js/sandbox/eslint.config.mjs loads the no-cross-package-internal rule from it via a relative path. The spec didn't account for this; without the copy the package cannot lint in either repo, since after the move that path would point at repos/amika/eslint-rules.
  • js/AGENTS.md, adapted so its examples and links resolve against this repo.
  • A check-sandbox job (format check → typecheck → lint → test) and a check-eslint-rules job in CI.
  • A JS branch in githooks/pre-commit, driven by a new .lintstagedrc.mjs, gated on staged JS paths so a Go-only commit doesn't pay for a pnpm resolution.
  • setup-repo.sh now installs the JS workspace.

Add a recurseSubmodules option to the clone inputs

CloneRepoInput gains recurseSubmodules, defaulting to true, honored in buildRefreshClonedRepoScript. amika-mono now carries a submodule, so a sandbox provisioned from it needs recursion or pnpm install breaks on a missing workspace member. The amika-mono side (resolveCloneRepo) lands separately.

Two things worth a look

sdk/typescript is deliberately not a workspace member. The spec called for including it. It is already a self-contained pnpm workspace — own pnpm-workspace.yaml, own pnpm-lock.yaml, own packageManager pin (11.1.3 vs. 10.18.2 here) — and both sdk-typescript.yml and the OIDC publish workflow install from that lockfile. Absorbing it would nest a workspace inside a workspace and orphan a published package's release pipeline, for no benefit to this migration.

prettier is pinned to an exact 3.8.3, not ^3.6.2. The two repos resolve lockfiles independently, so a caret range lets them pick different patch versions — and prettier's output changes across minors. This bit during the migration: on byte-identical source, formatcheck passed under 3.8.3 and failed under 3.9.6. The exact pin is what keeps both repos and both pre-commit hooks agreeing on formatting. Bump it in all four places together.

Verification

Run at the repo root against this branch:

Check Result
pnpm install 3 workspace projects, sdk/typescript correctly excluded
@amika/sandbox formatcheck clean
@amika/sandbox typecheck clean
@amika/sandbox lint clean
@amika/sandbox test 321 passed (34 files)
eslint-rules test 16 passed

`@amika/sandbox`, the Amika-agnostic sandbox provider abstraction, moves
out of the closed-source `amika-mono` repo and into this one. amika-mono
consumes it back as a git submodule pinned to an exact commit, so this
repo becomes the single source of truth for the package: changes land
here and reach amika-mono through a pointer bump.

The package is a clean extraction candidate. It has no `@amika/*`
dependencies of its own, it already exposes a deliberate public surface
through its export entries, and it already follows the `internal/`
convention that keeps helpers off that surface.

Establishes a JS workspace at the repo root to host it:

- `pnpm-workspace.yaml` and `package.json` declare `js/*` and
  `eslint-rules` as members. `sdk/typescript` is deliberately excluded:
  it is a self-contained workspace with its own lockfile and
  `packageManager` pin, and its CI and npm publish workflows install
  from that lockfile.
- `eslint-rules/` comes along because `js/sandbox/eslint.config.mjs`
  loads the `no-cross-package-internal` rule from it. Without it the
  package cannot lint in either repo.
- `js/AGENTS.md` carries the workspace conventions, adapted so its
  examples and links resolve against this repo.
- A `check-sandbox` CI job and a JS branch in `githooks/pre-commit`
  give the package the source-quality gates amika-mono can no longer
  provide for it: a superproject sees only a gitlink for a submodule,
  so it cannot lint or format this source.

`prettier` is pinned to an exact 3.8.3 rather than the `^3.6.2` it
carried in amika-mono. The two repos resolve lockfiles independently, so
a range lets them pick different patch versions, and prettier's output
does change across minors: on byte-identical source, `formatcheck`
passed under 3.8.3 and failed under 3.9.6. An exact pin is what keeps
the two repos agreeing on formatting.
A repo that declares git submodules is not usable without them, and a
clone that silently leaves the submodule directory empty fails later,
further away, and far more confusingly than a slow clone does. This is
not hypothetical: amika-mono now carries `js/sandbox` as a submodule, so
a sandbox provisioned from it needs submodule recursion or `pnpm
install` breaks on a missing workspace member.

Adds `recurseSubmodules` to `CloneRepoInput`, defaulting to `true` when
omitted, and honors it in `buildRefreshClonedRepoScript` — the fast path
that refreshes a repo already baked into the booted snapshot. The
refresh runs `submodule sync --recursive` before `submodule update`:
`update --init` alone would keep using the URL recorded in
`.git/config`, missing a `.gitmodules` URL that changed between the
baked-in commit and the one being checked out. Both run after the branch
checkout, since the commits a superproject pins are a property of the
checked out tree.

No provider SDK exposes a submodule option on its native clone
primitive — Daytona's `git.clone` takes url, path, branch, commitId,
credentials, and a TLS flag, and neither `@daytonaio/sdk` nor
`@daytona/api-client` mentions submodules anywhere. Honoring a `true`
value therefore has to mean cloning over the exec port instead. The flag
is threaded through here; `resolveCloneRepo` in amika-mono's
`@amika/sandbox-provisioning` is what acts on it.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e4c0a08d2c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .lintstagedrc.mjs
}

function shellQuote(path) {
return JSON.stringify(path);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Quote staged paths safely for the shell

When a staged, formattable filename contains shell syntax such as $() or backticks, JSON.stringify merely surrounds it with double quotes, so the shell still performs command substitution when lint-staged executes the generated Prettier command. A valid Git path such as js/sandbox/src/$(command).ts can therefore execute arbitrary commands during commit; use POSIX-safe single-quote escaping or pass paths without constructing a shell command string.

Useful? React with 👍 / 👎.

Comment thread .lintstagedrc.mjs
// a git submodule, and a formatting disagreement between the two repos
// would surface as a `formatcheck` failure in whichever one did not write
// the file. Bump both repos together.
return [`pnpm dlx prettier@3.8.3 --write ${quotedPaths}`];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use the installed workspace Prettier

For any commit staging a formattable js/ file, the hook runs pnpm dlx rather than the Prettier dependency installed by setup-repo.sh; on a fresh machine without a cached dlx package, this adds a registry dependency and can prevent offline commits even though the workspace was installed successfully. Invoke the workspace binary, for example through pnpm exec, so the hook uses the lockfile-resolved tool as intended.

AGENTS.md reference: js/AGENTS.md:L153-L159

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant