yao/sandbox/v2/jsapi/node.go
Max 7cccf62841 feat(tai): refactor Dial* functions and remove requireKubeConfig hard-fail
- Consolidate DialRemote/DialTunnel common logic into buildResources + dialEnv interface
- Remove strict capability check that prevented ConnResources creation for host-exec-only nodes
- Merge gRPC-discovered capabilities with registration-declared capabilities in DialTunnel
- Replace requireKubeConfig hard-fail with graceful skip when kubeconfig is absent
- Introduce tai/types package for shared Ports/Capabilities/SystemInfo/AuthInfo/NodeMeta
- Add tai/conn.go (ConnResources) and tai/dial.go (DialRemote/DialTunnel/DialLocal)
- Rename tai/sandbox → tai/runtime for clarity
- Update sandbox/v2, workspace, agent/sandbox/v2 test utilities for build-tag isolation

Made-with: Cursor
2026-03-12 15:44:24 +08:00

144 lines
4 KiB
Go

package jsapi
import (
"encoding/json"
"time"
"github.com/yaoapp/yao/tai/registry"
taitypes "github.com/yaoapp/yao/tai/types"
"rogchap.com/v8go"
)
// sbGetNode: `sandbox.GetNode(taiID)` → NodeInfo | null
func sbGetNode(info *v8go.FunctionCallbackInfo) *v8go.Value {
iso := info.Context().Isolate()
args := info.Args()
if len(args) < 1 || !args[0].IsString() {
return throwError(info, "GetNode requires taiID (string)")
}
reg := registry.Global()
if reg == nil {
return throwError(info, "registry not initialized")
}
snap, ok := reg.Get(args[0].String())
if !ok {
return v8go.Null(iso)
}
val, err := snapshotToJS(info.Context(), snap)
if err != nil {
return throwError(info, err.Error())
}
return val
}
// sbNodes: `sandbox.Nodes()` → NodeInfo[]
func sbNodes(info *v8go.FunctionCallbackInfo) *v8go.Value {
v8ctx := info.Context()
reg := registry.Global()
if reg == nil {
return throwError(info, "registry not initialized")
}
snaps := reg.List()
return snapshotsToJSArray(v8ctx, snaps)
}
// sbNodesByTeam: `sandbox.NodesByTeam(teamID)` → NodeInfo[]
func sbNodesByTeam(info *v8go.FunctionCallbackInfo) *v8go.Value {
v8ctx := info.Context()
args := info.Args()
if len(args) < 1 || !args[0].IsString() {
return throwError(info, "NodesByTeam requires teamID (string)")
}
reg := registry.Global()
if reg == nil {
return throwError(info, "registry not initialized")
}
snaps := reg.ListByTeam(args[0].String())
return snapshotsToJSArray(v8ctx, snaps)
}
// snapshotToJS converts a NodeMeta to a JS NodeInfo object.
// Auth and YaoBase are excluded for security.
func snapshotToJS(v8ctx *v8go.Context, snap *taitypes.NodeMeta) (*v8go.Value, error) {
ports := map[string]interface{}{
"grpc": snap.Ports.GRPC, "http": snap.Ports.HTTP,
"vnc": snap.Ports.VNC, "docker": snap.Ports.Docker, "k8s": snap.Ports.K8s,
}
caps := map[string]interface{}{
"docker": snap.Capabilities.Docker, "k8s": snap.Capabilities.K8s,
"host_exec": snap.Capabilities.HostExec,
}
data, err := json.Marshal(map[string]interface{}{
"tai_id": snap.TaiID,
"machine_id": snap.MachineID,
"version": snap.Version,
"mode": snap.Mode,
"addr": snap.Addr,
"status": snap.Status,
"display_name": snap.DisplayName,
"connected_at": snap.ConnectedAt.Format(time.RFC3339),
"last_ping": snap.LastPing.Format(time.RFC3339),
"ports": ports,
"capabilities": caps,
"system": map[string]interface{}{
"os": snap.System.OS,
"arch": snap.System.Arch,
"hostname": snap.System.Hostname,
"num_cpu": snap.System.NumCPU,
"total_mem": snap.System.TotalMem,
},
})
if err != nil {
return nil, err
}
return v8go.JSONParse(v8ctx, string(data))
}
func snapshotsToJSArray(v8ctx *v8go.Context, snaps []taitypes.NodeMeta) *v8go.Value {
items := make([]interface{}, 0, len(snaps))
for i := range snaps {
snap := &snaps[i]
ports := map[string]interface{}{
"grpc": snap.Ports.GRPC, "http": snap.Ports.HTTP,
"vnc": snap.Ports.VNC, "docker": snap.Ports.Docker, "k8s": snap.Ports.K8s,
}
caps := map[string]interface{}{
"docker": snap.Capabilities.Docker, "k8s": snap.Capabilities.K8s,
"host_exec": snap.Capabilities.HostExec,
}
items = append(items, map[string]interface{}{
"tai_id": snap.TaiID,
"node_id": snap.TaiID,
"machine_id": snap.MachineID,
"version": snap.Version,
"mode": snap.Mode,
"addr": snap.Addr,
"status": snap.Status,
"display_name": snap.DisplayName,
"connected_at": snap.ConnectedAt.Format(time.RFC3339),
"last_ping": snap.LastPing.Format(time.RFC3339),
"ports": ports,
"capabilities": caps,
"system": map[string]interface{}{
"os": snap.System.OS,
"arch": snap.System.Arch,
"hostname": snap.System.Hostname,
"num_cpu": snap.System.NumCPU,
"total_mem": snap.System.TotalMem,
},
})
}
data, _ := json.Marshal(items)
val, _ := v8go.JSONParse(v8ctx, string(data))
return val
}