Skip to content

refactor: rename Sandbox -> Box (Part 1: apps/api epicenter)#706

Merged
DorianZheng merged 17 commits into
mainfrom
refactor/sandbox-to-box
Jun 9, 2026
Merged

refactor: rename Sandbox -> Box (Part 1: apps/api epicenter)#706
DorianZheng merged 17 commits into
mainfrom
refactor/sandbox-to-box

Conversation

@law-chain-hot

Copy link
Copy Markdown
Contributor

What

Part 1 of N of the repo-wide product-entity rename SandboxBox.
This PR renames the apps/api epicenter (the ~851-ref apps/api/src/sandbox/
mega-module and its consumers within apps/api), and is intentionally a WIP
draft
: CI will be red until the remaining subsystems + generated-client regen
land (see Remaining).

Context: BoxLite is one feature; internally we no longer want sandbox to name
the product entity — it should be box. The OS/security isolation primitive
sandbox (jailer/seccomp/Seatbelt) is a different concept and is kept.

Done in this PR (verified)

  • apps/api product-entity symbols renamed Sandbox*Box*, sandboxIdboxId,
    files + module dir sandbox/box/ (231 files).
  • Zero-DDL by construction (audited): @Entity('sandbox') pinned; FK columns
    pinned to name: 'sandboxId'; sandbox_state_enum + all sandbox_*
    index/table names unchanged; no migration added.
  • Internal CreateSandboxDtoCreateBoxInternalDto (avoids colliding with the
    existing boxlite-rest CreateBoxDto).
  • runner-api-client references temporarily kept as generated Sandbox* names
    (flip when the runner is renamed + its client regenerated, Part 2).
  • Verification: tsc --build apps/api == pre-change baseline (0 new type errors).
    No local DB/KVM here, so the remote E2E suite was not run.

Frozen sandbox allowlist (deliberately NOT renamed)

Category Examples
DB table/column/index/enum table sandbox, col sandboxId, sandbox_state_enum, sandbox_*_idx, sandbox_usage_periods*
OS isolation primitive (KEEP) src/boxlite/jailer/*, seccomp/Seatbelt/sandbox-exec, --no-sandbox, IS_SANDBOX, Cargo keywords
Wire / env BOXLITE_SANDBOX_ID
Webhook events sandbox.created, sandbox.state.updated, …
Telemetry meters boxlite.sandbox.*
QueryBuilder aliases raw-SQL 'sandbox.<col>' join/where strings

Reproducible codemod recipe (for the overnight/a2pr deploy line to rebase)

Identifier-level, quote/snake/dot-protected (so frozen literals survive):

s/Sandbox/Box/g;
s/(?<!")\bsandboxId\b(?!")/boxId/g;       # protect "sandboxId" SQL columns
s/(?<!")\bsandboxIds\b(?!")/boxIds/g;
s/(?<!['"`])sandbox(?=[A-Z])/box/g;        # camelCase ids; protect quotes/snake
s/(?<!['"`])sandbox(?![\w'"`])/box/g;      # bare ids
s/sandboxes/boxes/g; s/sandboxOtel/boxOtel/g; s/sandboxActivity/boxActivity/g;
s/boxlite\.box/boxlite.sandbox/g;          # restore frozen telemetry

then git mv paths sandbox→box, pin entity @Entity/column names, regen clients.

Remaining (separate installments — NOT in this PR)

  1. Codemod runner / cli / dashboard / glue (proxy/ssh-gateway/otel/daemon) / sdk-typescript.
  2. Regen the 9 generated OpenAPI clients (api-client, runner/analytics/toolbox-api-client, api-client-go, swaggo docs, cli docs).
  3. Build all + remote E2E (e4bbd0ea).
  4. PR BUUID → BoxId id model (public box-id lookup, dashboard shows box id), stacked on this.

⚠️ Do not merge yet — WIP. CI red is expected until 1–3 land.

…er) [WIP part 1 of N]

Renames apps/api product-entity symbols (types/services/enums/DTOs/controllers/
methods/vars/files, dir sandbox/ -> box/) while FREEZING DB/wire/event literals:
@entity('sandbox') pinned, FK cols pinned to name:'sandboxId', sandbox_state_enum
and sandbox_* indexes/tables unchanged; webhook 'sandbox.*', telemetry
boxlite.sandbox.*, BOXLITE_SANDBOX_ID kept. Internal CreateSandboxDto ->
CreateBoxInternalDto. runner-api-client refs kept as generated Sandbox* until
runner renamed+regenerated. Verified: tsc --build api == baseline (0 new errors).
CI red until consumer subsystems + 9-client regen land.
@coderabbitai

coderabbitai Bot commented Jun 9, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 300 files, which is 150 over the limit of 150.

To get a review, narrow the scope:
• coderabbit review --type committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 078b09a4-7b70-48ed-a0ff-d159ebd7ad6f

📥 Commits

Reviewing files that changed from the base of the PR and between 41bd185 and b8d2ece.

📒 Files selected for processing (300)
  • apps/api-client-go/.openapi-generator/FILES
  • apps/api-client-go/.openapi-generator/VERSION
  • apps/api-client-go/api/openapi.yaml
  • apps/api-client-go/api_admin.go
  • apps/api-client-go/api_audit.go
  • apps/api-client-go/api_auth.go
  • apps/api-client-go/api_box.go
  • apps/api-client-go/api_box_lite_rest.go
  • apps/api-client-go/api_health.go
  • apps/api-client-go/api_jobs.go
  • apps/api-client-go/api_organizations.go
  • apps/api-client-go/api_preview.go
  • apps/api-client-go/api_runners.go
  • apps/api-client-go/api_snapshots.go
  • apps/api-client-go/api_toolbox.go
  • apps/api-client-go/api_workspace.go
  • apps/api-client-go/client.go
  • apps/api-client-go/configuration.go
  • apps/api-client-go/model_account_provider.go
  • apps/api-client-go/model_admin_create_runner.go
  • apps/api-client-go/model_announcement.go
  • apps/api-client-go/model_api_key_list.go
  • apps/api-client-go/model_api_key_response.go
  • apps/api-client-go/model_audit_log.go
  • apps/api-client-go/model_box.go
  • apps/api-client-go/model_box_class.go
  • apps/api-client-go/model_box_desired_state.go
  • apps/api-client-go/model_box_info.go
  • apps/api-client-go/model_box_labels.go
  • apps/api-client-go/model_box_state.go
  • apps/api-client-go/model_box_volume.go
  • apps/api-client-go/model_boxlite_configuration.go
  • apps/api-client-go/model_build_info.go
  • apps/api-client-go/model_command.go
  • apps/api-client-go/model_completion_context.go
  • apps/api-client-go/model_completion_item.go
  • apps/api-client-go/model_completion_list.go
  • apps/api-client-go/model_compressed_screenshot_response.go
  • apps/api-client-go/model_computer_use_start_response.go
  • apps/api-client-go/model_computer_use_status_response.go
  • apps/api-client-go/model_computer_use_stop_response.go
  • apps/api-client-go/model_create_api_key.go
  • apps/api-client-go/model_create_box.go
  • apps/api-client-go/model_create_build_info.go
  • apps/api-client-go/model_create_docker_registry.go
  • apps/api-client-go/model_create_linked_account.go
  • apps/api-client-go/model_create_organization.go
  • apps/api-client-go/model_create_organization_invitation.go
  • apps/api-client-go/model_create_organization_quota.go
  • apps/api-client-go/model_create_organization_role.go
  • apps/api-client-go/model_create_region.go
  • apps/api-client-go/model_create_region_response.go
  • apps/api-client-go/model_create_runner.go
  • apps/api-client-go/model_create_runner_response.go
  • apps/api-client-go/model_create_session_request.go
  • apps/api-client-go/model_create_snapshot.go
  • apps/api-client-go/model_create_user.go
  • apps/api-client-go/model_create_volume.go
  • apps/api-client-go/model_create_workspace.go
  • apps/api-client-go/model_display_info_response.go
  • apps/api-client-go/model_docker_registry.go
  • apps/api-client-go/model_download_files.go
  • apps/api-client-go/model_execute_request.go
  • apps/api-client-go/model_execute_response.go
  • apps/api-client-go/model_file_info.go
  • apps/api-client-go/model_file_status.go
  • apps/api-client-go/model_git_add_request.go
  • apps/api-client-go/model_git_branch_request.go
  • apps/api-client-go/model_git_checkout_request.go
  • apps/api-client-go/model_git_clone_request.go
  • apps/api-client-go/model_git_commit_info.go
  • apps/api-client-go/model_git_commit_request.go
  • apps/api-client-go/model_git_commit_response.go
  • apps/api-client-go/model_git_delete_branch_request.go
  • apps/api-client-go/model_git_repo_request.go
  • apps/api-client-go/model_git_status.go
  • apps/api-client-go/model_health_controller_check_200_response.go
  • apps/api-client-go/model_health_controller_check_200_response_info_value.go
  • apps/api-client-go/model_health_controller_check_503_response.go
  • apps/api-client-go/model_job.go
  • apps/api-client-go/model_job_status.go
  • apps/api-client-go/model_job_type.go
  • apps/api-client-go/model_keyboard_hotkey_request.go
  • apps/api-client-go/model_keyboard_press_request.go
  • apps/api-client-go/model_keyboard_type_request.go
  • apps/api-client-go/model_list_branch_response.go
  • apps/api-client-go/model_log_entry.go
  • apps/api-client-go/model_lsp_completion_params.go
  • apps/api-client-go/model_lsp_document_request.go
  • apps/api-client-go/model_lsp_location.go
  • apps/api-client-go/model_lsp_server_request.go
  • apps/api-client-go/model_lsp_symbol.go
  • apps/api-client-go/model_match.go
  • apps/api-client-go/model_metric_data_point.go
  • apps/api-client-go/model_metric_series.go
  • apps/api-client-go/model_metrics_response.go
  • apps/api-client-go/model_mouse_click_request.go
  • apps/api-client-go/model_mouse_click_response.go
  • apps/api-client-go/model_mouse_drag_request.go
  • apps/api-client-go/model_mouse_drag_response.go
  • apps/api-client-go/model_mouse_move_request.go
  • apps/api-client-go/model_mouse_move_response.go
  • apps/api-client-go/model_mouse_position.go
  • apps/api-client-go/model_mouse_scroll_request.go
  • apps/api-client-go/model_mouse_scroll_response.go
  • apps/api-client-go/model_oidc_config.go
  • apps/api-client-go/model_organization.go
  • apps/api-client-go/model_organization_box_default_limited_network_egress.go
  • apps/api-client-go/model_organization_invitation.go
  • apps/api-client-go/model_organization_role.go
  • apps/api-client-go/model_organization_sandbox_default_limited_network_egress.go
  • apps/api-client-go/model_organization_suspension.go
  • apps/api-client-go/model_organization_usage_overview.go
  • apps/api-client-go/model_organization_user.go
  • apps/api-client-go/model_otel_config.go
  • apps/api-client-go/model_paginated_audit_logs.go
  • apps/api-client-go/model_paginated_boxes.go
  • apps/api-client-go/model_paginated_jobs.go
  • apps/api-client-go/model_paginated_logs.go
  • apps/api-client-go/model_paginated_snapshots.go
  • apps/api-client-go/model_paginated_traces.go
  • apps/api-client-go/model_poll_jobs_response.go
  • apps/api-client-go/model_port_preview_url.go
  • apps/api-client-go/model_position.go
  • apps/api-client-go/model_posthog_config.go
  • apps/api-client-go/model_process_errors_response.go
  • apps/api-client-go/model_process_logs_response.go
  • apps/api-client-go/model_process_restart_response.go
  • apps/api-client-go/model_process_status_response.go
  • apps/api-client-go/model_project_dir_response.go
  • apps/api-client-go/model_pty_create_request.go
  • apps/api-client-go/model_pty_create_response.go
  • apps/api-client-go/model_pty_list_response.go
  • apps/api-client-go/model_pty_resize_request.go
  • apps/api-client-go/model_pty_session_info.go
  • apps/api-client-go/model_range.go
  • apps/api-client-go/model_rate_limit_config.go
  • apps/api-client-go/model_rate_limit_entry.go
  • apps/api-client-go/model_regenerate_api_key_response.go
  • apps/api-client-go/model_region.go
  • apps/api-client-go/model_region_quota.go
  • apps/api-client-go/model_region_screenshot_response.go
  • apps/api-client-go/model_region_type.go
  • apps/api-client-go/model_region_usage_overview.go
  • apps/api-client-go/model_registry_push_access_dto.go
  • apps/api-client-go/model_replace_request.go
  • apps/api-client-go/model_replace_result.go
  • apps/api-client-go/model_resize_box.go
  • apps/api-client-go/model_runner.go
  • apps/api-client-go/model_runner_full.go
  • apps/api-client-go/model_runner_health_metrics.go
  • apps/api-client-go/model_runner_healthcheck.go
  • apps/api-client-go/model_runner_service_health.go
  • apps/api-client-go/model_runner_snapshot_dto.go
  • apps/api-client-go/model_runner_state.go
  • apps/api-client-go/model_sandbox_class.go
  • apps/api-client-go/model_sandbox_desired_state.go
  • apps/api-client-go/model_sandbox_state.go
  • apps/api-client-go/model_screenshot_response.go
  • apps/api-client-go/model_search_files_response.go
  • apps/api-client-go/model_send_webhook_dto.go
  • apps/api-client-go/model_session.go
  • apps/api-client-go/model_session_execute_request.go
  • apps/api-client-go/model_session_execute_response.go
  • apps/api-client-go/model_set_snapshot_general_status_dto.go
  • apps/api-client-go/model_signed_port_preview_url.go
  • apps/api-client-go/model_snapshot_dto.go
  • apps/api-client-go/model_snapshot_manager_credentials.go
  • apps/api-client-go/model_snapshot_state.go
  • apps/api-client-go/model_ssh_access_dto.go
  • apps/api-client-go/model_ssh_access_validation_dto.go
  • apps/api-client-go/model_storage_access_dto.go
  • apps/api-client-go/model_toolbox_proxy_url.go
  • apps/api-client-go/model_trace_span.go
  • apps/api-client-go/model_trace_summary.go
  • apps/api-client-go/model_update_box_state_dto.go
  • apps/api-client-go/model_update_docker_registry.go
  • apps/api-client-go/model_update_job_status.go
  • apps/api-client-go/model_update_organization_default_region.go
  • apps/api-client-go/model_update_organization_invitation.go
  • apps/api-client-go/model_update_organization_member_access.go
  • apps/api-client-go/model_update_organization_quota.go
  • apps/api-client-go/model_update_organization_region_quota.go
  • apps/api-client-go/model_update_organization_role.go
  • apps/api-client-go/model_update_region.go
  • apps/api-client-go/model_url.go
  • apps/api-client-go/model_user.go
  • apps/api-client-go/model_user_home_dir_response.go
  • apps/api-client-go/model_user_public_key.go
  • apps/api-client-go/model_volume_dto.go
  • apps/api-client-go/model_volume_state.go
  • apps/api-client-go/model_webhook_app_portal_access.go
  • apps/api-client-go/model_webhook_controller_get_status_200_response.go
  • apps/api-client-go/model_webhook_event.go
  • apps/api-client-go/model_webhook_initialization_status.go
  • apps/api-client-go/model_windows_response.go
  • apps/api-client-go/model_work_dir_response.go
  • apps/api-client-go/model_workspace.go
  • apps/api-client-go/model_workspace_port_preview_url.go
  • apps/api-client-go/version.go
  • apps/api/eslint.config.mjs
  • apps/api/src/admin/admin.module.ts
  • apps/api/src/admin/controllers/box.controller.ts
  • apps/api/src/admin/controllers/runner.controller.ts
  • apps/api/src/admin/dto/create-runner.dto.ts
  • apps/api/src/analytics/services/analytics.service.ts
  • apps/api/src/api-key/api-key.module.ts
  • apps/api/src/api-key/api-key.service.ts
  • apps/api/src/app.module.ts
  • apps/api/src/app.service.ts
  • apps/api/src/audit/audit.module.ts
  • apps/api/src/audit/enums/audit-action.enum.ts
  • apps/api/src/audit/enums/audit-target.enum.ts
  • apps/api/src/audit/interceptors/audit.interceptor.ts
  • apps/api/src/audit/services/audit.service.ts
  • apps/api/src/auth/api-key.strategy.ts
  • apps/api/src/auth/auth.module.ts
  • apps/api/src/box-telemetry/box-telemetry.module.ts
  • apps/api/src/box-telemetry/controllers/box-telemetry.controller.ts
  • apps/api/src/box-telemetry/dto/index.ts
  • apps/api/src/box-telemetry/dto/log-entry.dto.ts
  • apps/api/src/box-telemetry/dto/metrics-response.dto.ts
  • apps/api/src/box-telemetry/dto/paginated-logs.dto.ts
  • apps/api/src/box-telemetry/dto/paginated-traces.dto.ts
  • apps/api/src/box-telemetry/dto/telemetry-query-params.dto.ts
  • apps/api/src/box-telemetry/dto/trace-span.dto.ts
  • apps/api/src/box-telemetry/dto/trace-summary.dto.ts
  • apps/api/src/box-telemetry/guards/analytics-api-disabled.guard.ts
  • apps/api/src/box-telemetry/index.ts
  • apps/api/src/box-telemetry/services/box-telemetry.service.ts
  • apps/api/src/box/box.module.ts
  • apps/api/src/box/common/redis-lock.provider.ts
  • apps/api/src/box/common/runner-service-info.ts
  • apps/api/src/box/constants/box-events.constants.ts
  • apps/api/src/box/constants/box.constants.ts
  • apps/api/src/box/constants/errors-for-recovery.ts
  • apps/api/src/box/constants/runner-events.ts
  • apps/api/src/box/constants/runner-name-regex.constant.ts
  • apps/api/src/box/constants/snapshot-events.ts
  • apps/api/src/box/constants/volume-events.ts
  • apps/api/src/box/constants/warmpool-events.constants.ts
  • apps/api/src/box/controllers/box.controller.ts
  • apps/api/src/box/controllers/job.controller.ts
  • apps/api/src/box/controllers/preview.controller.ts
  • apps/api/src/box/controllers/runner.controller.ts
  • apps/api/src/box/controllers/snapshot.controller.ts
  • apps/api/src/box/controllers/toolbox.deprecated.controller.ts
  • apps/api/src/box/controllers/volume.controller.ts
  • apps/api/src/box/controllers/workspace.deprecated.controller.ts
  • apps/api/src/box/dto/box.dto.ts
  • apps/api/src/box/dto/build-info.dto.ts
  • apps/api/src/box/dto/create-box.dto.ts
  • apps/api/src/box/dto/create-build-info.dto.ts
  • apps/api/src/box/dto/create-runner-internal.dto.ts
  • apps/api/src/box/dto/create-runner-response.dto.ts
  • apps/api/src/box/dto/create-runner.dto.ts
  • apps/api/src/box/dto/create-snapshot.dto.ts
  • apps/api/src/box/dto/create-volume.dto.ts
  • apps/api/src/box/dto/create-workspace.deprecated.dto.ts
  • apps/api/src/box/dto/download-files.dto.ts
  • apps/api/src/box/dto/job-type-map.dto.ts
  • apps/api/src/box/dto/job.dto.ts
  • apps/api/src/box/dto/list-boxes-query.dto.ts
  • apps/api/src/box/dto/list-snapshots-query.dto.ts
  • apps/api/src/box/dto/lsp.dto.ts
  • apps/api/src/box/dto/paginated-boxes.dto.ts
  • apps/api/src/box/dto/paginated-snapshots.dto.ts
  • apps/api/src/box/dto/port-preview-url.dto.ts
  • apps/api/src/box/dto/registry-push-access-dto.ts
  • apps/api/src/box/dto/resize-box.dto.ts
  • apps/api/src/box/dto/runner-full.dto.ts
  • apps/api/src/box/dto/runner-health.dto.ts
  • apps/api/src/box/dto/runner-snapshot.dto.ts
  • apps/api/src/box/dto/runner-status.dto.ts
  • apps/api/src/box/dto/runner.dto.ts
  • apps/api/src/box/dto/snapshot.dto.ts
  • apps/api/src/box/dto/ssh-access.dto.ts
  • apps/api/src/box/dto/storage-access-dto.ts
  • apps/api/src/box/dto/toolbox-proxy-url.dto.ts
  • apps/api/src/box/dto/toolbox.deprecated.dto.ts
  • apps/api/src/box/dto/update-box-network-settings.dto.ts
  • apps/api/src/box/dto/update-box-state.dto.ts
  • apps/api/src/box/dto/update-snapshot.dto.ts
  • apps/api/src/box/dto/upload-file.dto.ts
  • apps/api/src/box/dto/volume.dto.ts
  • apps/api/src/box/dto/workspace-port-preview-url.deprecated.dto.ts
  • apps/api/src/box/dto/workspace.deprecated.dto.ts
  • apps/api/src/box/entities/box-last-activity.entity.ts
  • apps/api/src/box/entities/box.entity.ts
  • apps/api/src/box/entities/build-info.entity.ts
  • apps/api/src/box/entities/job.entity.ts
  • apps/api/src/box/entities/runner.entity.ts
  • apps/api/src/box/entities/snapshot-region.entity.ts
  • apps/api/src/box/entities/snapshot-runner.entity.ts
  • apps/api/src/box/entities/snapshot.entity.ts
  • apps/api/src/box/entities/ssh-access.entity.ts
  • apps/api/src/box/entities/volume.entity.ts
  • apps/api/src/box/entities/warm-pool.entity.ts
  • apps/api/src/box/enums/backup-state.enum.ts
  • apps/api/src/box/enums/box-class.enum.ts

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/sandbox-to-box

Comment @coderabbitai help to get the list of available commands and usage tips.

…IP part 2 of N]

Codemods runner/cli/dashboard/proxy/ssh-gateway/otel-collector/daemon/common-go/
sdk-typescript symbols Sandbox->Box, sandboxId->boxId (frozen literals protected)
+ file/dir renames. Generated artifacts NOT committed (regen as build step).
Pipeline verified: swag -> Box swagger -> openapi-generator -> BoxApi. WIP/CI-red:
regen 9 clients, un-isolate api runner-adapter, rename remaining Sandbox*-cased
files, finish runner swag residue + .md, build all + remote E2E. api stays
typecheck-baseline-green (adapter isolated).
Comment thread apps/dashboard/src/hooks/useBoxSession.ts Fixed
…ums, ApiTags, file casing [WIP part 3 of N]

Continues the rename with categories the identifier codemod initially froze:
- Go route-param ctx.Param(sandboxId)->boxId (mismatched vs renamed :boxId routes)
- Go uppercase enum/jobtype SANDBOX->BOX (BOXLITE_SANDBOX_* wire frozen)
- api @ApiTags/@controller 'sandbox'->'box' (drives generated BoxApi not SandboxApi)
- remaining Sandbox-cased files (dashboard SandboxTable/, sdk Sandbox.ts)
- removed dead ToolboxApi client (never called)
Regen pipeline verified end-to-end: swag->Box swagger->runner-api-client BoxApi;
generate-openapi->Box spec(137 boxId/0 sandboxId)->api-client BoxApi+api-client-go BoxAPI.
WIP/CI-red tail: api JobType enum values + re-spec + regen + go/nx builds + E2E.
api stays typecheck-baseline-green (adapter isolated).
Comment thread apps/dashboard/src/hooks/useBoxSession.ts Fixed
…-verified [WIP part 4 of N]

Finishes the rename tail + regenerates generated clients so the tree is Box-consistent
and compiles across every subsystem:
- api JobType enum CREATE_SANDBOX->CREATE_BOX (was snake/upper double-frozen); reverted
  RBAC OrganizationResourcePermission.*_SANDBOXES (persisted/migration-referenced)
- runnerAdapter un-isolated -> regenerated BoxApi/EnumsBoxState/CreateBoxDTO
- Go+TS import-path tails /sandbox -> /box (trailing-quote was protected)
- regenerated runner-api-client/api-client/api-client-go (BoxApi/BoxAPI; spec 137 boxId/0 sandboxId)
Compile-verified (0 sandbox errors): api tsc==baseline; runner/cli/proxy/ssh-gw/otel/daemon/
common-go go vet clean; sdk-typescript tsc clean; dashboard 0 sandbox errors.
Remaining (env/remote, not rename): CGO link needs libboxlite.a (make); build libs before
dashboard; remote E2E (KVM). Generated client comments keep cosmetic 'sandbox'. PR B not started.
Comment thread apps/cli/cmd/box/delete.go Fixed
Comment thread apps/cli/mcp/tools/create_box.go Fixed
Comment thread apps/cli/mcp/tools/destroy_box.go Fixed
Comment thread apps/proxy/pkg/proxy/get_box_target.go Fixed
Comment thread apps/ssh-gateway/main.go Fixed
Comment thread apps/ssh-gateway/main.go Fixed
Comment thread apps/ssh-gateway/main.go Fixed
@DorianZheng DorianZheng marked this pull request as ready for review June 9, 2026 12:57
@DorianZheng DorianZheng requested a review from a team as a code owner June 9, 2026 12:57
@DorianZheng

Copy link
Copy Markdown
Member

🐞 Runtime bug survives into Part 4: @Param('sandboxIdOrName') no longer matches the renamed route token

apps/api/src/box/controllers/box.controller.ts (head b20fc6cb): the route path tokens were renamed to :boxIdOrName, but 20 @Param('sandboxIdOrName') decorators + their @ApiParam({ name: 'sandboxIdOrName' }) were left frozen. NestJS resolves @Param('sandboxIdOrName') against a route token that no longer exists, so the handler argument is undefined at runtime.

Concrete (the @Audit reader in the same handler proves the mismatch):

@Delete(':boxIdOrName')                                  // route token = boxIdOrName
@ApiParam({ name: 'sandboxIdOrName', ... })              // stale → OpenAPI param name wrong
targetIdFromRequest: (req) => req.params.boxIdOrName,    // ✓ reads the real token
async deleteBox(
  @Param('sandboxIdOrName') boxIdOrName: string,         // ✗ reads dead token → undefined
) {
  const box = await this.boxService.destroy(boxIdOrName, ...)   // destroy(undefined, ...)
}

Why "compile-verified" (tsc == baseline) doesn't catch it: the argument to @Param() is just a string literal, so it's type-valid — the break is purely runtime binding + the generated OpenAPI param name. It would only surface in E2E / a live request.

Why only this token slipped: the codemod's quote-protection. :sandboxIdOrName (colon-prefixed) gets renamed; 'sandboxIdOrName' (quote-prefixed) is protected. And the \bsandboxId\b rule doesn't match the compound sandboxIdOrName (no word boundary), so the plain sandboxId params renamed cleanly while sandboxIdOrName did not.

Affected endpoints (20): GET/DELETE :boxIdOrName, recover, start, stop, resize, labels, backup, public/:isPublic, autostop, autoarchive, autodelete, archive, ports/:port/preview-url (+ signed / expire), build-logs, build-logs-url, ssh-access (POST + DELETE).

Stale @Param lines: 368, 404, 438, 478, 529, 573, 611, 669, 710, 771, 813, 856, 930, 959, 994, 1028, 1063, 1111, 1155, 1198. Stale @ApiParam name: lines: 350, 385, 419, 459, 499, 546, 587, 650, 681, 742, ….

Fix: rename sandboxIdOrNameboxIdOrName in every @Param(...) and @ApiParam({ name: ... }) in this file (the @Param('boxId') / :boxId routes are already consistent). A general guard for the remaining parts: after the codemod, assert every @Param('x') / @ApiParam({name:'x'}) has a matching :x route token.

@DorianZheng

Copy link
Copy Markdown
Member

Request: complete the rename — the frozen sandbox literals should become box too

The rename currently stops at internal identifiers and keeps the externally-/persistently-observable sandbox names frozen for zero-DDL + backward-compat. For the product entity to actually be box, these should be renamed as well (accepting that each is a breaking/coordinated change rather than a pure refactor). Could we extend the PR (or stack a follow-up) to cover them?

1. DB schema@Entity('sandbox'), @Column/@PrimaryColumn/@JoinColumn({ name: 'sandboxId' }), sandbox_state_enum, sandbox_usage_periods / sandbox_usage_periods_archive / sandbox_last_activity, organization columns (max_*_per_sandbox, sandbox_*_rate_limit*), and all sandbox_* indexes.

  • Needs a DDL migration: ALTER TABLE … RENAME TO, RENAME COLUMN, ALTER TYPE sandbox_state_enum RENAME TO box_state_enum, ALTER INDEX … RENAME TO, with a reversible down(). Flip the entity @Entity/name: pins to box* so code and schema agree. Run as a post-deploy/contract migration.

2. Wire — webhook eventsbox-events.constants.ts / webhook-events.constants.ts still emit 'sandbox.created', 'sandbox.state.updated', … These are delivered to customer endpoints, so renaming to box.* breaks consumers. Options: version the webhook payload, or dual-emit (sandbox.* + box.*) for a deprecation window, then drop the old names.

3. RBAC permission enumOrganizationResourcePermission.*_SANDBOXES was reverted because the values are persisted/migration-referenced. Renaming the string values to *_BOXES needs a data migration of stored permission rows (and any policy docs); the TS member names can rename independently.

4. Env varsRATE_LIMIT_SANDBOX_*, DEFAULT_ORG_QUOTA_MAX_*_PER_SANDBOX, ADMIN_MAX_*_PER_SANDBOX, ORGANIZATION_SANDBOX_DEFAULT_*, RUNNER_*_STARTED_SANDBOXES*, BOXLITE_SANDBOX_ID, SANDBOX_OTEL_*, etc. Rename to *_BOX* and update all deployment manifests / .env.example / infra configs in lockstep.

Understood that the zero-DDL freeze was deliberate to keep the PR non-breaking — the ask is to treat these as the breaking-rename tail (migrations + coordinated wire/env changes) so no sandbox remains as the product entity anywhere, not just in source identifiers. Happy to take any of these (e.g. the DDL migration) as a stacked PR if that's easier to review.

The e2e bootstrap ran 'cd apps && yarn install' but yarn only treats apps/ as its
own project root when a lockfile anchors it. The repo doesn't commit one (make/dev.mk
seeds an empty one for the same reason), so on any fresh checkout — including the CI
e2e-stack runner — yarn aborted instantly with 'apps isn't part of the project',
failing 'make test:e2e:setup' at step 4 before the stack ever built. Seed the empty
lockfile first, matching make/dev.mk. Unblocks the e2e-stack gate.
…[part 5]

Addresses DorianZheng's review on #706: the codemod's quote-protection renamed
colon-prefixed route tokens (:sandboxIdOrName -> :boxIdOrName) but froze the
single-quoted @Param('sandboxIdOrName') / @ApiParam({name:'sandboxIdOrName'}),
so NestJS bound a dead token -> handler arg undefined at runtime on ~20 box
endpoints (get/delete/recover/start/stop/resize/labels/backup/autostop/...).

- rename sandboxIdOrName -> boxIdOrName in all @Param/@ApiParam (route param, no DB column)
- rename controller-level 'sandboxId' -> 'boxId' in @Param/@ApiParam ONLY
  (entities keep @Column/@joincolumn name:'sandboxId' pinned -> DB still zero-DDL)
- guard: every @Param('x')/@ApiParam name:'x' now has a matching :x route token
  (audited all api controllers; 0 sandbox mismatches)

tsc --build api == baseline (the break was runtime binding only, type-invisible).
@law-chain-hot

Copy link
Copy Markdown
Contributor Author

Fixed in cfeb019c — thanks for the precise catch (and the guard suggestion).

Root cause exactly as you diagnosed: the codemod's quote-protection renamed :sandboxIdOrName (colon-prefixed) but froze 'sandboxIdOrName' (quote-prefixed), and the \bsandboxId\b rule didn't match the compound token — so the route token moved to :boxIdOrName while @Param('sandboxIdOrName') bound a dead token → undefined at runtime.

Fix:

  • sandboxIdOrNameboxIdOrName in every @Param(...) / @ApiParam({ name }) (pure route param, no DB column).
  • controller-level 'sandboxId''boxId' in @Param/@ApiParam only — entities keep @Column/@JoinColumn({ name: 'sandboxId' }) pinned, so the physical table/columns stay unchanged (zero-DDL preserved, deferred to the UUID→boxId PR).
  • Implemented your guard across all api controllers: every @Param('x') / @ApiParam({name:'x'}) now has a matching :x route token — 0 sandbox mismatches. (The only 3 hits are organizationId, which lives on the class-level @Controller(':organizationId/...') prefix, not a method token — false positive in a method-only scan.)

tsc --build api stays at baseline (the break was runtime binding only, type-invisible as you noted); the E2E stack run will confirm the live binding once the bootstrap is unblocked.

…ation, wire, RBAC names, env) [part 6]

Completes the product-entity rename by un-freezing the literals parts 1-5
deliberately kept for zero-DDL/backward-compat. Source-only change; generated
clients are left at their part-5 state for CI regeneration.

- DB: entity pins flipped sandbox*->box* (@entity, @Column/@joincolumn name,
  @Index, ::sandbox_state_enum) + QueryBuilder aliases/raw-SQL refs. New
  reversible contract migration post-deploy/1781016743403 renames the physical
  schema (table sandbox/_last_activity/_usage_periods/_archive, columns
  sandboxId + organization sandbox_*_rate_limit*/max_*_per_sandbox, enum
  sandbox_state_enum, all sandbox_*_idx + idx_sandbox_* indexes).
- Wire: webhook event values sandbox.*->box.*, redis channel
  sandbox.event.channel->box.event.channel, analytics meter keys
  boxlite.sandbox.*->boxlite.box.* + sandbox_*->box_* (producers + dashboard/ws
  consumers flipped in lockstep).
- RBAC: enum MEMBER names WRITE_SANDBOXES/DELETE_SANDBOXES->*_BOXES (string
  VALUES were already 'write:boxes'/'delete:boxes' - no data migration).
- Env: SANDBOX env vars -> BOX (RATE_LIMIT_*, *_PER_*, RUNNER_*_STARTED_*,
  *_OTEL_*, BOXLITE_SANDBOX_ID/USER/AUTH_KEY, DEFAULT_*_SORTING) across
  api/runner/proxy/daemon/sdk + infra .env.example.

Verification: apps/api tsc == cfeb019 baseline (identical error set, 0 new);
Go apps (cli/daemon/runner/common-go/proxy/otel/ssh-gateway) go build/vet clean.

NOT verified / follow-ups (env-bound, intentionally deferred to CI):
- DDL migration is NOT DB-verified (no database here) - needs a real run + review.
- Generated clients (api-client, api-client-go, analytics/runner/toolbox-api-client)
  + cli cobra docs + daemon swaggo MUST be regenerated from the renamed specs.
- PostHog feature flag DASHBOARD_CREATE_SANDBOX -> DASHBOARD_CREATE_BOX must also
  be renamed in the PostHog dashboard or the flag silently goes dead.
- Webhook event rename is breaking for external consumers (no dual-emit window).

Signed-off-by: dorianzheng <xingzhengde72@gmail.com>
@DorianZheng

Copy link
Copy Markdown
Member

Pushed 47526870 (part 6) — un-freezes the remaining sandbox literals to box, completing the rename per the request above. Source-only (163 files, +1014/−831); generated clients left at part-5 state for CI regen.

Done:

  • DB: entity pins flipped sandbox*box* (@Entity/@Column name/@Index/::sandbox_state_enum) + QueryBuilder aliases & raw-SQL refs. New reversible contract migration post-deploy/1781016743403 renames the physical schema (tables sandbox/_last_activity/_usage_periods/_archive, sandboxId + organization.sandbox_*/max_*_per_sandbox columns, enum sandbox_state_enum, all sandbox_*_idx + idx_sandbox_* indexes; down() reverses).
  • Wire: webhook events sandbox.*box.*, redis channel, analytics meters boxlite.sandbox.*boxlite.box.* (producers + dashboard/ws consumers in lockstep).
  • RBAC: enum member names WRITE_SANDBOXES/DELETE_SANDBOXES*_BOXES (values stay write:boxes/delete:boxesno data migration).
  • Env: SANDBOXBOX env vars across api/runner/proxy/daemon/sdk + infra.

Verified: apps/api tsc == this PR's prior-head baseline (identical error set, 0 new); Go apps go build/vet clean.

⚠️ Follow-ups (env-bound, NOT done here):

  1. DDL migration is not DB-verified (no DB in my env) — needs a real run + review before merge.
  2. Regenerate the 5 clients + cli cobra docs + daemon swaggo from the renamed specs (I reverted my local-env regen to keep this source-only).
  3. PostHog flag DASHBOARD_CREATE_SANDBOXDASHBOARD_CREATE_BOX must also be renamed in the PostHog dashboard, or the flag goes dead.
  4. Webhook rename is breaking for external consumers (no dual-emit window) — version/announce as needed.

Pushed directly at the maintainer's request; revert with git revert 47526870 if you'd rather stage it differently.

@DorianZheng DorianZheng changed the title refactor: rename Sandbox -> Box (Part 1: apps/api epicenter) [WIP] refactor: rename Sandbox -> Box (Part 1: apps/api epicenter) Jun 9, 2026
…wait)

Addresses the github-code-quality 'Missing await' comment on useBoxSession.ts:
make queryFn async and await client.get(boxId), preserving the
'Client not initialized' guard. Behavior unchanged (React Query already
awaited the returned promise); silences the analyzer and reads clearer.

Signed-off-by: dorianzheng <xingzhengde72@gmail.com>
@DorianZheng

Copy link
Copy Markdown
Member

Addressed the review-bot comments in 0b03aaec:

✅ Fixed — github-code-quality "Missing await" (apps/dashboard/src/hooks/useBoxSession.ts:128)
Made boxQuery.queryFn async and await client.get(boxId), keeping the Client not initialized guard. (Behavior was already correct — React Query awaited the returned promise — but the async form is clearer and clears the analyzer.)

ℹ️ CodeQL "Clear-text logging of sensitive information" (7 alerts) — pre-existing, not from this PR
These flag log.*("... %s: %v", id, err) where err comes from the generated API client. They are identical on main (e.g. apps/ssh-gateway/main.go:217, apps/proxy/pkg/proxy/get_sandbox_target.go:313/326/333) and only surfaced here because the rename put these files in the PR diff (CodeQL default-setup re-scans changed files). apps/ssh-gateway/main.go isn't even modified by the rename commits.

I deliberately did not bundle a fix into this rename PR — sanitizing credential-tainted API-client errors across cli/proxy/ssh-gateway is a security change that deserves its own reviewed PR (shared redaction helper), not a drive-by in a rename. Happy to open that separately. (They also look like conservative taint flow — the generated client's GenericOpenAPIError.Error() returns status+body, not the request ApiKey/Password — so likely false positives, but worth a dedicated look.)

DorianZheng and others added 7 commits June 9, 2026 23:28
…f sensitive information'

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Signed-off-by: dorianzheng <8065637+DorianZheng@users.noreply.github.com>
…f sensitive information'

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Signed-off-by: dorianzheng <8065637+DorianZheng@users.noreply.github.com>
…f sensitive information'

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Signed-off-by: dorianzheng <8065637+DorianZheng@users.noreply.github.com>
…f sensitive information'

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Signed-off-by: dorianzheng <8065637+DorianZheng@users.noreply.github.com>
…f sensitive information'

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Signed-off-by: dorianzheng <8065637+DorianZheng@users.noreply.github.com>
…f sensitive information'

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Signed-off-by: dorianzheng <8065637+DorianZheng@users.noreply.github.com>
Reverts changes that crept into the un-freeze commit but are NOT part of the
Sandbox->Box rename:
- apps/otel-collector/exporter/go.sum: stray go.mod checksum additions (dep churn)
- apps/dashboard/.../useBoxSession.ts: async/await queryFn refactor (behavioral,
  was a code-quality suggestion, not a rename) -> restored original one-liner
- Sidebar.tsx / Onboarding.tsx / eslint.config.mjs: prettier re-wrapping triggered
  by shorter identifiers -> restored original wrapping (rename tokens kept)

Kept: all sandbox->box token renames, and the DB-rename DDL migration (that IS
the rename of the physical schema). Copilot Autofix commits for the CodeQL
clear-text-logging findings are left intact (not mine, deliberate security fix).

Signed-off-by: dorianzheng <xingzhengde72@gmail.com>
Comment thread apps/dashboard/src/hooks/useBoxSession.ts Fixed
DorianZheng and others added 2 commits June 10, 2026 00:17
Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
Signed-off-by: dorianzheng <8065637+DorianZheng@users.noreply.github.com>
The committed runner swagger was stale (pre-rename 'Create sandbox' descriptions)
and the runner-api-client was incomplete — its index.ts exported BoxApi/CreateBoxDTO/
RecoverBoxDTO/EnumsBoxState but the backing files were never committed, so apps/api
failed to compile (6x TS2305 'no exported member' in runnerAdapter.v0/v2).

- regenerated runner swagger via swag from the (already box) runner Go handlers
  (apps/runner/pkg/api/docs/*): 0 sandbox, 111 box
- regenerated apps/libs/runner-api-client from it (openapi-generator 7.23,
  typescript-axios): exports BoxApi/CreateBoxDTO/RecoverBoxDTO/EnumsBoxState, 0 sandbox

Effect: apps/api TS2305 errors 6 -> 0 (api now compiles re: the runner client;
remaining ~160 tsc errors are pre-existing WIP type issues unrelated to this).

NOT done (blocked): api-client + api-client-go regen still needs the api OpenAPI
spec, whose generation bootstraps the full NestJS app (DB/Redis/Kafka/OIDC/S3,
~205 env vars) — not runnable in this worktree; must run in the configured dev/CI env.

Signed-off-by: dorianzheng <xingzhengde72@gmail.com>
@DorianZheng

Copy link
Copy Markdown
Member

✅ Rename-only verification (94-agent sweep over all 937 changed files)

Ran an automated check across every changed file: normalize sandboxbox on the pre-rename (main) and current versions, diff the residual, classify. Result:

Verdict Files
Pure rename 427
Generated churn 376
Added (rename-driven, incl. files + DDL migration) 65
Formatter reflow (shorter identifiers) 49
Flagged for review 20

All 20 review flags triaged to non-issues for the rename: the intended DB-name pins (@Entity('box')/@Column({name:'boxId'}), paired with the contract migration), the documented CreateSandboxDtoCreateBoxInternalDto collision rename, false-positive git rename-pairings (the dashboard mutation hooks are R100 renames calling the correct APIs — verified), the CodeQL clear-text-logging autofix commits, and generated-client churn. No hidden logic change is attributable to the rename.

Two things surfaced that are not rename-related and worth your eyes:

1. runnerAdapter dropped its ToolboxApi integration (part 4, b20fc6cb). Both v0 and v2 no longer reference ToolboxApi. On main the field was declared + initialized but never called (this.toolboxApiClient. → 0 hits), so this looks like dead-code cleanup — just confirming it was intentional and nothing relied on it.

2. Generated clients were regenerated with a newer openapi-generator than main usesmain is on 7.12.0, the regen produced 7.23.0. That version jump (not the rename) is what added unrelated scaffolding to the clients: AWS SigV4 config, Set serialization, GIT_USER_ID/GIT_REPO_ID placeholders in git_push.sh, Accept headers. There's no generator version pinned in-repo (openapitools.json absent on main). Recommend pinning the generator to 7.12.0 (or deciding to bump repo-wide) before the final client regen, so the generated diff stays rename-only instead of carrying a generator upgrade.

@DorianZheng DorianZheng merged commit 490d093 into main Jun 9, 2026
30 of 32 checks passed
@DorianZheng DorianZheng deleted the refactor/sandbox-to-box branch June 9, 2026 16:50
DorianZheng added a commit that referenced this pull request Jun 10, 2026
…clients (#716)

Closes the "regenerate api-client/api-client-go" follow-up from #711
(post-#706 rename), and makes the regeneration reproducible so the
clients can't silently go stale again.

## Why the clients were stale

The `apps/` subtree import from daytonaio/daytona dropped the root-level
machinery that upstream uses to regenerate clients (researched against
upstream — `daytonaio/daytona@main`):

| Upstream piece | State in boxlite before this PR | Consequence |
|---|---|---|
| `openapitools.json` generator pin | missing | every machine floats to
the latest jar (committed tree mixed 7.12/7.23 output) |
| `hack/{ts,go}-client/` postprocess + Go mustache templates | missing
(referenced by nx targets!) | Go client lost `version.go` + branded
`User-Agent`; targets failed at the postprocess step |
| `format-lint-api-clients` CI drift gate (`pr_checks.yaml`) | missing |
nothing detected stale clients — how `WebhookEvent="sandbox.created"`
survived the rename merge |
| committed `apps/api/.env` dev env | missing (boxlite gitignores
`.env`) | `generate-openapi.ts` (boots the full NestJS app) had no env
to run under |
| repo-root-relative nx target paths | broken (workspace root moved into
`apps/`) | `nx run api:openapi` resolved `apps/apps/api/...` |

## Commit 1 — machinery

- **`apps/openapitools.json`** pins openapi-generator at **7.23.0**
(matches the committed clients; upstream pins 7.21.0 — deliberate
deviation to minimize this diff).
- **`apps/hack/{ts,go}-client/`** copied byte-identical from upstream
(User-Agent branding, `version.go` via `go:embed`, Go model templates).
- **nx path fixes** in `api`, `libs/api-client`, `api-client-go`
project.json (apps/-rooted workspace), plus
`TS_NODE_TRANSPILE_ONLY=true` (api has pre-existing WIP type errors) and
upstream's `enumUnknownDefaultCase=true` restored on both clients.
- **`apps/api/.env.example`** — offline placeholder env for spec
generation (`cp api/.env.example api/.env`), following the repo's
existing `.env.example` gitignore-exception precedent.
`SKIP_CONNECTIONS=true` (already in the nx target) keeps it offline;
values are docker-compose placeholders.
- **`@ApiExcludeController()` on the four `boxlite-rest` controllers.**
That surface is spec-first — its contract is `openapi/box.openapi.yaml`
(the controllers' own docs say so) — and in the swagger-emitted product
spec it is OpenAPI-3.0-invalid: `:prefix` route tokens have no declared
path param, and the proxy's `@All()` expands to the `SEARCH` verb, which
OpenAPI 3.0 cannot express. This invalidity is what broke
`openapi-generator` entirely. The generated `BoxLiteREST` surface had
**zero consumers** (verified across
cli/sdk-go/sdk-typescript/dashboard/daemon).
- **`.github/workflows/api-client-drift.yml`** — port of upstream's
drift gate: regenerate both clients on `apps/**` PRs and fail on diff
(redis service container avoids a bootstrap retry-race;
actionlint/shellcheck clean).

## Commit 2 — regenerated clients (generator output only, no hand edits)

- `WebhookEvent` enums now match the server: `sandbox.created` →
**`box.created`**, `sandbox.state.updated` → **`box.state.updated`** (TS
+ Go) — this was the consumer-facing break from #711.
- `sandboxIdOrName` path-template vars → `boxIdOrName`; stale "…of the
sandbox" doc text → box. Both clients now contain **zero** `sandbox`
tokens.
- Go client regression fixed: `version.go` (`go:embed VERSION`)
restored, `UserAgent: "api-client-go/" + ClientVersion` instead of the
stock `"OpenAPI-Generator/1.0.0/go"`.
- `UNKNOWN_DEFAULT_OPEN_API` enum members added across both clients
(upstream's `enumUnknownDefaultCase=true` — forward-compat with unknown
server enum values).
- New TS models/apis picked up (drift catch-up: DTOs and tags added to
the api since the last regen), plus the generator's `src/docs/*.md` —
not previously committed, but the drift gate requires committed output
to match emitted output exactly, so they're in (upstream suppresses
docs; we commit-as-emitted).
- `api_box_lite_rest.go` deleted (−4801 lines): the excluded,
consumer-less spec-first surface above.

## Verification

- Spec generates cleanly: 0 sandbox tokens, `box.*` webhook events, 0
`{prefix}` paths.
- Regeneration is **idempotent** (two consecutive runs → identical `git
status`).
- `tsc -p libs/api-client/tsconfig.lib.json` → exit 0.
- `go build ./...` clean in `api-client-go` **and** `apps/cli` (the Go
consumer).
- No source anywhere references the removed `BoxLiteREST` generated API
or old `SANDBOX_*` enum members.
- New workflow passes actionlint + shellcheck.

## Not in scope

- `analytics-api-client`: its input swagger comes from the external
analytics service (upstream excludes it from regen for the same reason);
it still contains `sandbox.*` until that service renames.
- `apps/yarn.lock` stays uncommitted (explicitly gitignored by repo
policy); the drift job does a plain `yarn install`. Committing a
lockfile would make the gate fully deterministic — separate policy
decision.
- Remaining cosmetic `sandbox` strings in cli docs / shim (tracked in
#711).

---------

Signed-off-by: dorianzheng <xingzhengde72@gmail.com>
G4614 added a commit to G4614/boxlite that referenced this pull request Jun 10, 2026
…column names

Migration1781016743403 (PR boxlite-ai#706) renamed most sandbox-vocabulary objects
to box-vocabulary equivalents but missed three pieces; the API and runner
crashed on every box-touching path until those caught up. fixture_setup
also still wrote the old organization-quota column names.

Schema gaps added in a follow-up post-deploy migration:
- `runner.currentStartedSandboxes`           -> `currentStartedBoxes`
- `organization.sandboxLimitedNetworkEgress` -> `boxLimitedNetworkEgress`
- `job_resourcetype_enum` value `SANDBOX`    -> `BOX`

Symptoms before the fix:
- ResourceManager / SnapshotManager queries: `column Runner.currentStartedBoxes does not exist`
- Box service create path: `column Organization.boxLimitedNetworkEgress does not exist`
- Runner job poll: `invalid input value for enum job_resourcetype_enum: "BOX"`
- fixture_setup: `column "max_cpu_per_sandbox" of relation "organization" does not exist`

Each migration step is guarded with a state check (`information_schema` /
`pg_enum`), so partial-state environments are safe to re-run and the
migration is idempotent on hosts where someone has already hand-applied
any of the three renames. The down() restores the previous names.

Verified locally: applied the migration to a partial-state DB; API +
runner come up clean; `scripts/test/e2e/cases/test_unread_stream_back_pressure.py`
(3 cases) and existing e2e cases pass against the restored stack.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
G4614 added a commit to G4614/boxlite that referenced this pull request Jun 10, 2026
Building on the path/tsconfig blockers fixed in the previous commit,
this clears the remaining 163 TS errors that surface once webpack
reaches tsc:

- 157 errors from Express 5's @types/express-serve-static-core@5.1.1
  widening ParamsDictionary[key] from `string` to `string | string[]`.
  Express still only returns strings at runtime, so narrow the three
  Request types that funnel into the controllers + interceptors:
    audit.decorator.ts        — AuditContext + TypedRequest
    audit.interceptor.ts      — RequestWithUser
    metrics.interceptor.ts    — RequestWithUser

- 3 errors from an OTel package version skew: sdk-node@^0.218.0 brings
  otlp-exporter-base@0.218 to the hoisted root, but the http exporters
  (exporter-trace/metrics/logs-otlp-http) are still pinned at ^0.207
  and carry their own nested 0.207 copy with an incompatible `headers`
  typing. Cast at the three call sites in tracing.ts until the http
  exporters get bumped to 0.218 in a follow-up dep-bump PR.

- 2 TS2307 errors from runner-api-client not being reachable. The
  Dockerfile COPYs libs to /boxlite/libs/ (which nx's project sourceRoot
  expects) but tsconfig paths declare libs at apps/libs/ relative to
  apps/tsconfig.base.json. Bridge with `ln -s ../libs apps/libs` so both
  consumers see the libs where they expect.

- TS6059 (lib files outside rootDir) that surfaced once the symlink let
  TS reach the lib sources. Widen tsconfig.base.json rootDir from "."
  to ".." so /boxlite/libs/* falls inside the rootDir of /boxlite/.

End state: `docker build -f apps/api/Dockerfile .` produces a working
2.21 MiB dist/apps/api/main.js. Dashboard build (Step 22 of the same
Dockerfile) is still blocked by an *independent* boxlite-ai#706 fallout —
apps/libs/api-client/src/models/index.ts references 12 box-*.ts files
that boxlite-ai#706 forgot to regenerate via openapi-generator. That regen
machinery was restored in upstream boxlite-ai#716; rebasing onto post-boxlite-ai#716 main
picks up the regenerated files and dashboard should follow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
DorianZheng pushed a commit that referenced this pull request Jun 10, 2026
…olumn names (#720)

## Bug

Migration1781016743403 (#706) renamed most sandbox-vocabulary objects to
box-vocabulary equivalents but missed three pieces, leaving the schema
inconsistent with the renamed TypeORM entities and TS enum. Every box-
touching API path crashed until I added the catch-up rename below;
fixture_setup also still wrote the old organization-quota column names.

| Layer | What got missed | Symptom |
|---|---|---|
| `runner.currentStartedSandboxes` | entity renamed to
`currentStartedBoxes`, no schema migration | every ResourceManager /
SnapshotManager query: \`column Runner.currentStartedBoxes does not
exist\` |
| `organization.sandboxLimitedNetworkEgress` | entity / service renamed
to `boxLimitedNetworkEgress`, no schema migration | box create: \`column
Organization.boxLimitedNetworkEgress does not exist\` |
| `job_resourcetype_enum` value `SANDBOX` | TS `ResourceType.BOX =
'BOX'`, Postgres enum still only knows 'SANDBOX' | runner job poll:
\`invalid input value for enum job_resourcetype_enum: \"BOX\"\` |
| `scripts/test/e2e/fixture_setup.py` | still UPDATEs
`max_cpu_per_sandbox` / `max_memory_per_sandbox` /
`max_disk_per_sandbox` (#706's main migration already renamed those
columns to `max_*_per_box`) | fixture_setup: \`column
\"max_cpu_per_sandbox\" of relation \"organization\" does not exist\` |

## Fix

New post-deploy migration `Migration1781072797240` performs the three
schema renames the original missed. Each step is guarded with an
`information_schema` / `pg_enum` state check, so partial-state
environments are safe to re-run and the migration is idempotent on
hosts where someone has already hand-applied any of the three renames.

`fixture_setup.py` is updated to use the new column names.

## Test plan

Verified locally on a partial-state DB:

1. Confirmed the bug: API 500 on box create with the three errors above,
   fixture_setup 500 on quota patch.
2. Applied this migration: schema renames cleanly (guards skip no-ops),
   `migrations` row recorded.
3. Restarted API + ran the e2e back-pressure suite — all 3 cases pass:

```
[same-box]              elapsed_b=0.456s rc=0 out='fast-b\n'
[cross-box]             elapsed_b=0.482s rc=0 out='fast-b\n'
[slow-consumer x-box]   elapsed_b=0.419s rc=0 out='fast-b\n'
```

No down() change to data — the down() just inverts the renames so the
migration is reversible if needed. Each down step is also guarded.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Chores**
* Applied post-deployment database migration to standardize schema
naming conventions across multiple tables.
* Updated database enumeration values to align with current system
terminology.

* **Tests**
* Updated e2e test fixture quota configuration to ensure test
environment compatibility with revised resource limit parameters.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
DorianZheng added a commit that referenced this pull request Jun 10, 2026
…wagger spec (#721)

## Summary

Closes out the **analytics-api-client** item of #711 (Sandbox→Box Part 1
follow-ups).

#706 renamed the dashboard's analytics consumers to Box-named symbols
(`ModelsBoxUsage`, `organizationOrganizationIdUsageBoxGet`, …) but the
committed client still only exported Sandbox-named ones — the dashboard
could not typecheck against the in-repo client. The client also couldn't
be regenerated from this repo at all: its generator input was an
uncommitted `tmp/swagger.json` from the external analytics service (the
reason #716 excluded it).

## Changes

- **Commit the contract in-repo**:
`apps/libs/analytics-api-client/swagger.json`, reconstructed 1:1 from
the previously committed client with only sandbox→box renamed (paths
`/organization/{organizationId}/box/{boxId}/…`, `models.BoxUsage`,
`boxId`, `boxCount`). This is now the spec any future analytics service
must implement — same spec-first direction as
`openapi/box.openapi.yaml`.
- **Point `generate:api-client` at it** (`project.json`), with nx cache
inputs tracking the spec instead of the unrelated `apiClient` named
input.
- **Regenerate** with the pinned openapi-generator **7.23.0** (committed
client was stale 7.12.0 output — the last client on the old generator).
Zero `sandbox` tokens remain.
- **Drift gate**: drop the analytics exclusion from
`api-client-drift.yml` — it now regenerates + diffs analytics like the
other clients (actionlint clean).
- **eslint**: ignore `**/.nx/**` — the generate target caches its `src/`
output under the gitignored `apps/.nx/cache`, and lint after a local
regen tripped on the cached copy of generated code.

## Verification

- Two-side dashboard typecheck: against the **old** client → 13
rename-mismatch errors (`'ModelsBoxUsage'… Did you mean
'ModelsSandboxUsage'?`); against the **new** client → **zero**
analytics-related errors (remaining 24 are pre-existing, unrelated:
Playground/VNC files + unbuilt `@boxlite-ai/sdk` dist).
- Regen is byte-identical across runs (`--skip-nx-cache`), so the drift
gate won't false-positive.
- `make lint:apps` exit 0.

## Notes

- Runtime risk is nil: the analytics client is only instantiated when
`ANALYTICS_API_URL` is configured, no analytics service exists in the
org today, and the API's own (already-renamed) `box/:boxId/telemetry/*`
endpoints serve the dashboard otherwise (`AnalyticsApiDisabledGuard`
makes the two mutually exclusive).
- Pre-existing, **not** fixed here (scope):
`apps/dashboard/tsconfig.app.json` path mappings (`\"@boxlite-ai/*\":
[\"../../libs/*\"]`) have pointed outside the workspace since the apps
tree was flattened (#460), so raw `tsc -p` never resolved any
`@boxlite-ai/*` import. Worth its own follow-up.

Part of #711.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
  * Added AWS Signature V4 support for API requests.
* Telemetry and usage endpoints now operate at box (per-box) and
organization levels (replacing sandbox-level variants).

* **Documentation**
* Comprehensive API docs added/updated with examples for box telemetry,
per-box usage, aggregated usage, and usage chart endpoints.

* **Chores**
* Drift detection updated to include the analytics API client; linting
ignores extended for generated files.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Signed-off-by: dorianzheng <xingzhengde72@gmail.com>
law-chain-hot added a commit to law-chain-hot/boxlite that referenced this pull request Jun 10, 2026
Admin observability module (new in Task12B) was authored pre-boxlite-ai#706 and never
renamed: broken imports (sandbox-telemetry/, sandbox/ paths) + queried the
wrong sandbox- service prefix / boxlite.sandbox_id while the daemon already
emits box-<id>. Renamed emitter + consumer in lockstep so new data is fully box:

- daemon: trace tracer scope boxlite.sandbox -> boxlite.box
- api interceptor: consolidate to { keys:[boxId,boxIdOrName], attr:boxlite.box_id }
- admin (11 files): symbols (SandboxState->BoxState, sandboxId->boxId), broken
  import paths -> box*, wire strings sandbox-/boxlite.sandbox_id -> box-/boxlite.box_id
- box-telemetry trace-span doc example sandbox-<id> -> box-<id>

Old ClickHouse data (sandbox-keyed) is disposable per decision; no CH schema
change (attribute keys are Map values). Fixed two latent boxlite-ai#706 bugs in passing
(currentStartedSandboxes->currentStartedBoxes, /admin/sandbox->/admin/box).

Left intentionally sandbox: box.manager raw SQL (frozen sandbox DB table/column),
PostHog product-analytics event names (separate system, pending decision).
DorianZheng added a commit that referenced this pull request Jun 10, 2026
…lient, fix dead env vars + stale refs (#723)

Part of #711 — closes out the "Part 1 of N" remainder. A repo-wide sweep
found the rename effectively complete in source
(`runner`/`cli`/`daemon`/`dashboard`/`proxy` at zero tokens; `apps/api`
leftovers are historical migrations, correct as-is). What remained is
below.

## Generated client (the bulk of the diff)

**`toolbox-api-client` regenerated** from the committed
`apps/daemon/pkg/toolbox/docs/swagger.json` (which #718 already renamed
— `git grep "sandbox entrypoint"` has zero hits outside the generated
client). This kills the last 4 generated `sandbox entrypoint session`
doc-comments and moves the client from generator 7.12.0 to the pinned
7.23.0, same modernization #721 did for analytics (hence the
`src/docs/*.md` additions — 7.23 emits them, and the drift gate requires
committed == emitted).

Determinism verified: regen run twice locally (with and without
`--excludeTaskDependencies`), byte-identical both times.

**Drift gate now covers it** (`api-client-drift.yml`): regen via
`--excludeTaskDependencies` so the committed daemon swagger is the
contract and the job needs no Go/swag toolchain. Daemon-source↔swagger
drift stays ungated (the cli/daemon-docs sibling gate noted in #711
remains a follow-up). actionlint + shellcheck clean.

## Why the toolbox client was never regenerated: broken nx paths

The regen target failed outright: `-i
apps/daemon/pkg/toolbox/docs/swagger.json` resolves relative to the nx
workspace root, which *is* `apps/` → `apps/apps/daemon/...` (spec not
found). Kept from upstream daytona's repo layout; #716 fixed only the
product clients' targets. Fixed here:

- `libs/toolbox-api-client/project.json` — `-i
daemon/pkg/toolbox/docs/swagger.json`
- `libs/runner-api-client/project.json` — same latent bug (`-i
runner/...` + its `{workspaceRoot}` input)
- `nx.json` — `apiClient`/`goProduction` named inputs pointed at
`{workspaceRoot}/apps/...` paths that don't exist, so swagger/go.work
changes never invalidated task caches

## Functional config fixes

- **`scripts/test/e2e/bootstrap.sh` +
`apps/infra-local/configs/api.env`** wrote
`ADMIN_MAX_{CPU,MEMORY,DISK}_PER_SANDBOX`, but the API reads
`ADMIN_MAX_*_PER_BOX` (`apps/api/src/config/configuration.ts:292-294`)
with default `0` — admin-org per-box quotas were silently unset.
- **`apps/infra-local/scripts/stack-reset.sh` + README runbook
snippets** ran `TRUNCATE TABLE sandbox, ...` / `SELECT ... FROM sandbox`
— that table was renamed to `box` by migration `1781016743403`, so these
failed as written. Also `/api/sandbox` → `/api/box` (route is
`@Controller('box')`) and `{{sandboxId}}` → `{{boxId}}` (matches the
`configuration.dto.ts` example). infra-local merged in #595 on a
pre-#706 vocabulary.

## Vocabulary sweep (no behavior change)

Comments, docstrings, and three e2e test function names
(`test_*_above_per_sandbox_limit_*` → `per_box`; no `-k` selector,
workflow, or Makefile references them) across `scripts/test/e2e/`,
`scripts/deploy/runner-update-binary.sh`, and `apps/infra-local/`.
File/path references verified against the tree (`box.service.ts`,
`volume.manager.ts:47`, `box_sync.go`, `CREATE_BOX` journal tokens). All
three trees now grep-clean for `sandbox`.

Also includes 3 files prettier-normalized by the repo's `make lint:fix`
pre-commit autofixer (`eslint.config.mjs`, `Sidebar.tsx`,
`Onboarding.tsx`) — they were out of format on `main` and the repo-wide
hook re-applies them on every commit until committed.

## Verification

- Regen byte-identical across repeated runs, including the exact CI
invocation
- `actionlint` + `shellcheck` on the workflow; `shellcheck` on all 6
edited shell scripts (only pre-existing findings on untouched lines);
`py_compile` on all 7 edited Python files
- Every text replacement applied with an exact-unique-match assertion
(count==1 before replace)
- OS-isolation uses of "sandbox" (jailer/seatbelt/bwrap, docs/faq)
deliberately untouched — different concept per the rename's scope

Signed-off-by: dorianzheng <xingzhengde72@gmail.com>
law-chain-hot added a commit to law-chain-hot/boxlite that referenced this pull request Jun 10, 2026
The convergence merge (623a043) left apps/api internally inconsistent: some
files used pre-boxlite-ai#706 'sandbox' naming while the enums/constants they reference
were already 'box' on main (and vice-versa) -> 65 compile errors. main is the
authoritative box-consistent version.

Renamed identifiers to box (NOT wholesale checkout from main, which would
re-introduce the deleted image system): AuditTarget.SANDBOX->BOX,
WRITE/DELETE_SANDBOXES->WRITE/DELETE_BOXES, SANDBOX_EVENT_CHANNEL->BOX_EVENT_CHANNEL,
SANDBOX_STATES_CONSUMING_*->BOX_*, SANDBOX_SORT_*->BOX_*, SANDBOX_WARM_POOL_*->BOX_*,
SANDBOXES_ADMIN->BOXES_ADMIN (UUID unchanged), plus duplicate-field merge artifacts
in box-lookup-cache-invalidation + box.repository.

Frozen (kept sandbox, matches main): DB entity/column/enum/index, raw SQL aliases,
PostHog event names, JobType enum values. Image/snapshot deletion untouched.

api tsc: 65 -> 0 errors. 16 files.
law-chain-hot added a commit to law-chain-hot/boxlite that referenced this pull request Jun 10, 2026
The convergence merge (623a043) regressed nearly all of apps code from main's
post-boxlite-ai#706 box naming back to pre-boxlite-ai#706 sandbox. main's non-migration code has
ZERO sandbox (verified) -- it is the authoritative all-box version. This branch
had 834 sandbox in apps/api/src alone (JobType.CREATE_SANDBOX vs main's
CREATE_BOX, @Index('sandbox_*') vs box_*, etc.).

Applied sandbox->box codemod across all apps source to match main, keeping image
deletion intact (rename only, no content from main):
- apps/api (834->0), apps/dashboard (115->0), apps/libs/sdk-typescript (2->0)
- apps/cli (25->0), apps/runner (88->0), apps/daemon (5->0), apps/infra (3->0)
- fixed codemod duplicate-property collisions (sandboxId+boxId -> boxId) in admin
  observability dashboard hooks
- @entity('box') restored to match main

Verified: api tsc 0 errors; cli/daemon go build clean; runner only the pre-existing
boxlite.WithPort gap; dashboard remaining errors are module-resolution (generated
clients not linked in this worktree, 272->244 pre/post codemod). Migrations and
generated clients untouched. No image/snapshot symbols reintroduced.
DorianZheng added a commit that referenced this pull request Jun 10, 2026
…715)

## What this is

A **convergence "super PR"**: it merges everything sitting on the
`codex/overnight-20260608` line into `main`, so that `main` becomes the
single source of truth again. Intended to be **reviewed then split** —
opened as one piece for visibility, not to be squash-merged blind.

`main` ← merge of (52 commits ahead): MVP box journey + agent-ready
image catalog + A2 snapshot-manager deletion + Task12B observability
wiring + default-org membership + W3C traceId, reconciled against
`main`'s `Sandbox→Box` rename (#706) and recent REST/CI fixes.

**Net vs main:** 771 files, +85k/−23.7k.

## Suggested split for review (5 logical blocks)

| # | Block | Rough surface |
|---|---|---|
| 1 | **A2 — delete snapshot-manager machinery** | drops self-hosted
registry / backup / build / docker-registry; collapses to `box_template`
+ `runner_artifact_cache`; direct ghcr pull; 68 files deleted;
migrations `17809…`–`17812…` |
| 2 | **MVP box journey** | simplified onboarding, templates-as-images,
public Box IDs, archive-lifecycle retirement |
| 3 | **Agent-ready image catalog** | `boxlite/base|python|node`
pinned-digest runtime catalog |
| 4 | **Observability (Task12B)** | admin diagnose UX + runner/daemon
traceId propagation |
| 5 | **Org membership** | default-org state moved into memberships
(backward-compatible) |

## Conflict-resolution policy (527 conflicts)

- **A2-deleted machinery** (snapshot/backup/build/docker-registry):
**deletion wins** over main's edits to those files.
- **Generated clients** (`api-client`, `runner-api-client`,
`api-client-go`): took **main's regenerated side**; a fresh regen
against the merged API surface is still pending (see Caveats).
- **Source files**: kept the A2/MVP side and re-applied **#706's
published codemod recipe** (`Sandbox→Box`, with the frozen-literal
allowlist preserved — DB table/column/enum, OS-isolation `sandbox`,
telemetry `boxlite.sandbox.*`, webhook event names).
- **Renamed-path ports**: 3-way merged onto the post-#706 `box/` paths.
- **Go consumers** aligned to the regenerated client enum names
(`BOXSTATE_*`).

## Caveats — read before merging

- ⚠️ **CI will be red until generated clients are regenerated** against
the merged API surface, and a full build + remote E2E is run. This was
**not** done locally (no DB/KVM here); **build/test verification is
deferred to CI on purpose**, stated honestly rather than claimed green.
- ⚠️ Single migration variant of `1780200000000` is present (verified) —
do not reintroduce a second.
- ⚠️ Deploy ordering for the A2 block is **runner-first** (swap runner
binary before the registry-deleting deploy), per Task 13.

## Not in this PR

`feat/admin-ui-redesign` (POL-14 admin UI v2) and the unmerged Task12
ClickHouse `sst.config.ts` wiring are intentionally left out — separate
follow-ups.


---

## Commit inventory — 48 commits, 6 blocks (+1 merge)

**A2 — delete snapshot-manager machinery (7)**
p2 direct-ghcr · p3 drop build · p4 drop backup · p5 drop registry→2
tables · p6 rebuild `box_template` + `runner_artifact_cache` · P1
runtime-scoped ghcr auth · ghcr credential delivery (Secrets Manager)

**MVP box journey (16)**
streamline box journey · templates-as-images · public BoxID + archive
retirement · simplified onboarding · BoxID/SDK onboarding polish ·
quickstart + dev-smoke

**Template-artifact refactor (11)**
from `d3a60c7c`: cloud templates + runtime artifacts refactor and
follow-up fixes

**Observability (3)**
Task12B admin diagnose + saved-image fix · runner W3C traceparent ·
daemon traceId

**Agent-ready images (3)**
runtime catalog · pin digests · catalog merged into MVP journey

**Org membership (3)**
default-org state → memberships · backward-compatible · post-merge
overview fix

**Misc (5)**
JWT issuer validation · ESLint flat-config fix · lint config · logo
assets · yarn.lock


---

## Superseded branches — intentionally NOT included

Verified by file-level comparison: these are earlier iterations whose
final form is already in this PR. Re-submitting them would regress newer
code.

- `feat/admin-overview`, `feat/frontend-slim-box-rename` — fully
superseded by Task12/12B + #706/MVP journey
- `feat/admin-ui-redesign`, `codex/task7-observability-data-layer` —
~52% file-overlap with Task12B's rewrite already here; any truly-missing
UI/data-layer fragments will come later as small focused PRs
- `codex/snapshot-image-naming`, `codex/runtime-artifact-split`,
`codex/api-template-contract` — abandoned saved_image naming line
(carries a conflicting variant of migration `1780200000000`; must never
merge)

**Generated clients now carry ZERO diff in this PR** (reset to main in
`f9ea0730`) — regenerate upstream against the merged API surface.


---

## Image subsystem removed (commit fbf99d9)

The inherited Daytona-fork image-management (box_template +
runner_artifact_cache) is **fully removed** so it can be rebuilt the
team's own way. Box image-pull/boot is intentionally non-functional
until the rebuild PR. Old impl preserved at tag `pre-image-rewrite`.

- **Deleted (45):** box-template.* + runner-artifact-cache.* +
RuntimeArtifactManager + the dashboard templates UI + template-states
usage constant
- **Gutted (49):** box.entity (template field), box.service (image
resolution), box-start.action (pull state machine → ERROR stub), runner
svc/controller, notifications/webhooks, metrics/openapi, org-usage
billing (template metering → 0, `TODO(billing-rewrite)`), dashboard
create/playground/routes
- **Untouched:** migrations, generated clients (apps/libs,
api-client-go), box list/delete + all non-image subsystems
- Verified: zero dangling references to deleted symbols; zero conflict
markers

---------

Signed-off-by: dorianzheng <xingzhengde72@gmail.com>
Co-authored-by: dorianzheng <xingzhengde72@gmail.com>
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.

3 participants