yao/sandbox/v2/jsapi/host.go
Max df420978f0 feat(workspace): implement JSAPI with archive support and full test coverage
- tai/volume: add WithRemotePath sync option, 8 archive/compression methods
  (Zip/Unzip/Gzip/Gunzip/Tar/Untar/Tgz/Untgz) with local+remote impls
- workspace/jsapi: implement 24-method WorkspaceFS (file I/O, Copy with
  local:///tmp:// URI dispatch, archive ops) and 4 static methods
- workspace/manager: add Rename, MkdirAll, Volume() accessors via singleton
- Tests: comprehensive Go tests for tai/volume, workspace, workspace/jsapi
  covering both local and remote modes via SANDBOX_TEST_REMOTE_ADDR
- CI: update tai Docker image to latest in pr-test.yml
- sandbox/v2: add JSAPI docs and host/node module stubs

Made-with: Cursor
2026-03-08 14:17:39 +08:00

87 lines
3.3 KiB
Go

package jsapi
import (
"rogchap.com/v8go"
)
// sbHost: `sandbox.Host(pool?)` → Host
//
// Go: Manager.Host(ctx, pool) (*Host, error)
//
// Args:
//
// pool: string (optional) — pool name; empty = default pool
//
// Returns: Host object if the pool has host_exec capability, otherwise throws.
//
// Host executes commands on the Tai host machine (no container). Available only
// when the pool's Tai server exposes HostExec gRPC.
func sbHost(info *v8go.FunctionCallbackInfo) *v8go.Value {
// TODO: Phase 2
// 1. pool := ""; if len(info.Args()) > 0 && info.Args()[0].IsString() { pool = info.Args()[0].String() }
// 2. host, err := sandbox.M().Host(ctx, pool)
// 3. if err != nil { throw in V8 }
// 4. Return NewHostObject(v8ctx, host.Pool())
return v8go.Undefined(info.Context().Isolate())
}
// NewHostObject creates a JS Host object backed by a pool name string.
// All methods delegate to the Go sandbox.M() singleton — no Go *Host passed to V8.
//
// # Properties (read-only)
//
// host.pool → string // pool name ← Host.Pool()
//
// # Methods — Go mapping
//
// host.Exec(cmd, args, options?) → HostExecResult
//
// Go: Host.Exec(ctx, cmd string, args []string, opts ...HostExecOption) (*HostExecResult, error)
//
// JS args:
// cmd: string → cmd string
// args: string[] → args []string
// options: { → HostExecOption
// workdir: string, → WithHostWorkDir(dir)
// env: object, → WithHostEnv(map[string]string)
// stdin: string, → WithHostStdin([]byte)
// timeout: number, → WithHostTimeout(ms int64)
// max_output: number → WithHostMaxOutput(bytes int64)
// }
// JS returns: {
// exit_code: number, ← HostExecResult.ExitCode
// stdout: string (UTF-8), ← HostExecResult.Stdout
// stderr: string (UTF-8), ← HostExecResult.Stderr
// duration_ms: number, ← HostExecResult.DurationMs
// error: string, ← HostExecResult.Error
// truncated: boolean ← HostExecResult.Truncated
// }
//
// host.Stream(cmd, args, callback) / host.Stream(cmd, args, options, callback)
//
// Go: Host.Stream(ctx, cmd string, args []string, opts ...HostExecOption) (*HostExecStream, error)
//
// Blocks until the process exits. The last argument must be a JS function.
// Callback signature: function(type, data)
// type = "stdout" → data is string (chunk)
// type = "stderr" → data is string (chunk)
// type = "exit" → data is number (exit code)
//
// JS args:
// cmd: string
// args: string[]
// options: { workdir, env, stdin, timeout, max_output } (optional, same as host.Exec)
// callback: function(type, data)
//
// host.Workspace(sessionID) → WorkspaceFS
//
// Implemented in workspace/jsapi package. This method calls:
// workspace.NewFSObject(v8ctx, sessionID)
// and returns the resulting WorkspaceFS object directly.
func NewHostObject(v8ctx *v8go.Context, pool string) (*v8go.Value, error) {
// TODO: Phase 2 implementation
// 1. Create JS object via v8go.NewObjectTemplate
// 2. Set read-only property: pool
// 3. Bind methods: Exec, Stream, Workspace (each resolves Host via sandbox.M().Host(ctx, pool))
return nil, nil
}