Skip to content

Add Go SDK#254

Open
nemo1105 wants to merge 1 commit into
TencentCloud:masterfrom
nemo1105:codex-add-go-sdk
Open

Add Go SDK#254
nemo1105 wants to merge 1 commit into
TencentCloud:masterfrom
nemo1105:codex-add-go-sdk

Conversation

@nemo1105
Copy link
Copy Markdown

Add a standalone standard-library Go SDK for CubeSandbox with lifecycle, execution, command, file read, proxy transport, README, unit tests, and opt-in integration tests.

Autonomously-by: Codex:GPT-5

@fslongjin
Copy link
Copy Markdown
Member

/cubebot review

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 14, 2026

Claude finished @fslongjin's task in 1m 44s —— View job


Go SDK Code Review

Overall the Go SDK is well-structured with clean API design, comprehensive test coverage (including edge cases), and thorough documentation. Below are specific findings organized by severity.


Bugs / Correctness Issues

1. Fragile exit code parsing in Commands.Run (sdk/go/commands.go:43-56)

The exit code is parsed from the last line of stdout output. If a command's output happens to end with a line containing only an integer, that line will be silently consumed as the exit code rather than returned as output.

// If a user runs: echo 42
// result.Stdout will be "" (empty) and result.ExitCode will be 42
// even though the actual exit code was 0

This is a known pattern carried over from the E2B Python SDK design, but it's worth documenting this limitation. Consider evaluating whether a different encoding scheme (e.g., a delimiter or separate file descriptor) would be more robust for production use.

2. Silent JSON marshal error discard (sdk/go/sandbox.go:150)

raw, _ := json.Marshal(payload)

This silently discards a marshaling error. While payload only contains simple types (making failure unlikely), a json.Marshal error would produce a nil/empty byte slice which then gets sent as an empty request body, causing a confusing failure downstream. Should at minimum log or document why the error is safe to ignore.


Design / API Concerns

3. Hardcoded HTTP scheme for data-plane requests (sdk/go/sandbox.go:152)

req, err := http.NewRequestWithContext(ctx, http.MethodPost, "http://"+s.GetHost(JupyterPort)+"/execute", bytes.NewReader(raw))

The http:// scheme is hardcoded. If the proxy or sandbox data-plane endpoint ever requires HTTPS, this would break. Consider making the scheme configurable or deriving it from the control-plane API URL.

4. Commands.Run uses shell=True (sdk/go/commands.go:22)

Python subprocess.run with shell=True is inherently risky. The input is properly quoted with strconv.Quote, but this should be clearly documented in a security consideration section since this API accepts arbitrary command strings from callers.


Minor (Style / Conventions)

5. Error message capitalization (sdk/go/files.go:23)

return "", fmt.Errorf("Failed to read %s: %s", path, execution.Error.Value)

Go convention requires error messages to start with lowercase. Should be "failed to read %s: %s".

6. coverage_test.go:570 — Helper function name is misleading

func errorFromRunCode(_ *Execution, err error) error {
    return err
}

This discards the *Execution return value. The name errorFromRunCode suggests it's extracting an error from Execution, but it's actually a multi-value adapter. A more descriptive name or inline handling would improve readability.


Strengths

  • Excellent test coverage: Unit tests cover config parsing edges, stream parsing (including malformed input), API error mapping, pause wait loops, context cancellation, transport failures, and nil receiver protection.
  • Well-structured error handling: APIError.Is implements proper errors.Is compatibility, including distinction between template-not-found and sandbox-not-found via message heuristics.
  • Good use of strconv.Quote: Safely escapes file paths and shell commands for embedding in generated Python code, preventing injection.
  • Proper context propagation: Timeouts, cancellation, and deadlines flow correctly through all operations.
  • Clean separation of concerns: codeRunner interface cleanly decouples Commands and Files from Sandbox.
  • Comprehensive README with working examples for all key features.
  • Proxy transport design is clean — preserving the virtual host header while routing to the proxy node.
  • Integration tests are well-structured with auto-discovery of templates, proper cleanup via t.Cleanup, and are gated behind a build tag.

Summary

The SDK is production-quality in its structure and testing rigor. The exit code parsing fragility in Commands.Run is the most notable concern and warrants either documentation or a more robust encoding approach. Other findings are minor style improvements or design tradeoffs worth considering.

@tinklone
Copy link
Copy Markdown
Collaborator

Commands.Run should use envd Process API instead of Python code execution

The current Commands.Run implementation wraps shell commands in Python code (subprocess.run(...)) and executes them via the /execute endpoint. This is architecturally wrong — it should call envd's process.Process/Start (Connect Protocol) interface directly.

How E2B does it (the correct approach)

E2B's official SDK calls:

POST http://49999-{sandboxID}.{domain}/process.Process/Start

With payload:

{
  "process": {
    "cmd": "/bin/bash",
    "args": ["-l", "-c", "<user-command>"],
    "envs": {},
    "cwd": "<optional>"
  }
}

The response is a server-stream of ProcessEvent messages:

  • StartEvent → gives PID
  • DataEvent → stdout/stderr bytes
  • EndEvent → exit_code, exited status

Why the current approach is problematic

  1. Requires Python in the sandbox — breaks for Go/Rust/C-only templates
  2. Performance overhead — spawns a Python interpreter just to call subprocess.run
  3. Fragile exit code handling — parsing return code from expression result text vs getting EndEvent.exit_code directly
  4. Inconsistent with E2B SDK — the Go SDK should behave the same as the Python/JS SDKs

Suggested fix

Implement a Connect Protocol (gRPC over HTTP/JSON) client for process.Process/Start in the Go SDK. The proto definition is at spec/envd/process/process.proto. The wire format is simple HTTP POST with JSON body + server-sent streaming response.

This also enables implementing Commands.List, Commands.Kill, Commands.SendStdin, and Commands.Connect — all available via the same envd process service.

Similarly, Files.Read should use the envd HTTP file API (GET /files?path=...) instead of generating Python open() code through /execute.

Copy link
Copy Markdown
Collaborator

@tinklone tinklone left a comment

Choose a reason for hiding this comment

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

CR Review: New Commits (6b03855 + 17aad98)

Summary: Refactored Commands/Files from Python RunCode hack to native envd Process API + HTTP File API. Significant improvement in correctness and maintainability.

✅ Highlights

  1. Architecture: Commands and Files now use independent processStarter / fileReader interfaces instead of shared codeRunner — cleaner SRP
  2. Correctness: Exit code from EndEvent field instead of fragile stdout last-line parsing
  3. Connect protocol: Proper envelope parsing, end-stream error handling, compressed flag rejection
  4. ProxyScheme: Smart default (port 443 → https, else http) with explicit override support
  5. Test coverage: New integration + unit tests with clean mock boundaries

🟡 Minor Suggestions (non-blocking)

# Location Issue Suggestion
1 envd.go:186 io.ErrUnexpectedEOF treated as normal EOF in readConnectEnvelope Should return error for mid-stream truncation (only io.EOF is clean end)
2 envd.go:167 No break after sawEnd = true in stream parse loop After EndEvent, remaining frames are unexpected — consider breaking early
3 envd.go:81 Dual field ExitCode + ExitCodeSnake without comment Add a brief comment explaining why both exist (backend version compat?)
4 commands.go:21 Empty map init for nil Envs If envd accepts "envs":null, this is unnecessary; if not, keep it but add a comment

❓ Questions for Author

  1. 64MB envelope limit: Could a large stdout cat OOM? Consider streaming for large outputs.
  2. /bin/bash -l -c: Login shell loads .bash_profile etc. Is this intentional? May add latency in some images.
  3. readFile unbounded: io.ReadAll without LimitReader — risk for very large files?

Verdict

LGTM — core logic correct, tests thorough. Minor items can be follow-up.

@tinklone
Copy link
Copy Markdown
Collaborator

tinklone commented May 18, 2026

Hi @wangchenglong-hj 👋

CI has two failing checks, one of which requires action on your side:

❌ DCO (Developer Certificate of Origin)

All 3 commits are missing the Signed-off-by line:

37b25030 Add Go SDK
6b03855d Fix Go SDK command exit code handling
17aad989 fix(go-sdk): use envd process and file APIs

To fix:

git rebase --signoff HEAD~3 && git push --force-with-lease

This will automatically append to each commit message:

Signed-off-by: wangchenglong-hj <wangchenglong-hj@huafang.com>

Reference: https://developercertificate.org/


⚠️ review check (no action needed from you)

This is a repo CI configuration issue (GitHub App token missing app-id). Unrelated to your PR code — known issue.


Code itself is LGTM ✅ — once the DCO sign-off is added, this is good to merge.

@ls-ggg
Copy link
Copy Markdown
Collaborator

ls-ggg commented May 19, 2026

Could you please squash these commits into a single one? Thanks! @wangchenglong-hj

@nemo1105 nemo1105 force-pushed the codex-add-go-sdk branch from 17aad98 to 0c6d83b Compare May 19, 2026 08:29
Add a standalone standard-library Go SDK for CubeSandbox with lifecycle management, command execution, file reads, proxy transport, README, unit tests, and opt-in integration tests.

Route command execution through envd process APIs, return exit codes through execution results, support configurable data-plane proxy scheme, and use envd HTTP file APIs for file reads.

Assisted-by: Codex:GPT-5
Signed-off-by: Nemo <ssdn@vip.qq.com>
@nemo1105 nemo1105 force-pushed the codex-add-go-sdk branch from 0c6d83b to 6e52542 Compare May 19, 2026 08:33
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.

4 participants