Skip to content

Commit 8093386

Browse files
Add Client struct wrapper and update module path for go get
Adds a Client struct that wraps the function-based API for consumers that prefer method receivers (e.g. orchestra). Updates module path to github.com/russellballestrini/un-inception/clients/go/sync/src for public go get resolution.
1 parent 8d17b2b commit 8093386

2 files changed

Lines changed: 201 additions & 1 deletion

File tree

clients/go/sync/src/client.go

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
// Client provides a struct-based API around the function-based SDK.
2+
// Designed for consumers like orchestra that prefer method receivers
3+
// and context-aware patterns.
4+
package un
5+
6+
import (
7+
"context"
8+
"fmt"
9+
)
10+
11+
// Client wraps resolved credentials for method-based API access.
12+
type Client struct {
13+
Creds *Credentials
14+
}
15+
16+
// NewClient creates a client from explicit credentials.
17+
func NewClient(publicKey, secretKey string) *Client {
18+
return &Client{Creds: &Credentials{PublicKey: publicKey, SecretKey: secretKey}}
19+
}
20+
21+
// NewClientFromEnv resolves credentials from the 4-tier priority system.
22+
func NewClientFromEnv() (*Client, error) {
23+
creds, err := ResolveCredentials("", "")
24+
if err != nil {
25+
return nil, err
26+
}
27+
return &Client{Creds: creds}, nil
28+
}
29+
30+
// ExecuteResult holds typed execution output.
31+
type ExecuteResult struct {
32+
JobID string
33+
Status string
34+
Output string
35+
Error string
36+
Raw map[string]interface{}
37+
}
38+
39+
func toExecuteResult(raw map[string]interface{}) *ExecuteResult {
40+
r := &ExecuteResult{Raw: raw}
41+
if v, ok := raw["job_id"].(string); ok {
42+
r.JobID = v
43+
}
44+
if v, ok := raw["status"].(string); ok {
45+
r.Status = v
46+
}
47+
// unsandbox API returns stdout/stderr, not output/error
48+
if v, ok := raw["output"].(string); ok {
49+
r.Output = v
50+
}
51+
if v, ok := raw["stdout"].(string); ok && r.Output == "" {
52+
r.Output = v
53+
}
54+
if v, ok := raw["error"].(string); ok {
55+
r.Error = v
56+
}
57+
if v, ok := raw["stderr"].(string); ok && r.Error == "" {
58+
r.Error = v
59+
}
60+
return r
61+
}
62+
63+
// Execute runs code synchronously (blocks until completion).
64+
func (c *Client) Execute(_ context.Context, language, code string) (*ExecuteResult, error) {
65+
return c.ExecuteWithOpts(context.Background(), language, code, "")
66+
}
67+
68+
// ExecuteWithOpts runs code with optional network mode.
69+
func (c *Client) ExecuteWithOpts(_ context.Context, language, code, networkMode string) (*ExecuteResult, error) {
70+
// Use the low-level makeRequest to support network_mode
71+
data := map[string]interface{}{
72+
"language": language,
73+
"code": code,
74+
}
75+
if networkMode != "" {
76+
data["network_mode"] = networkMode
77+
}
78+
79+
resp, err := makeRequest("POST", "/execute", c.Creds, data)
80+
if err != nil {
81+
return nil, err
82+
}
83+
84+
result := toExecuteResult(resp)
85+
86+
// Poll if job is not terminal
87+
if result.JobID != "" && result.Status != "completed" && result.Status != "failed" {
88+
polled, err := WaitForJob(c.Creds, result.JobID)
89+
if err != nil {
90+
return nil, err
91+
}
92+
return toExecuteResult(polled), nil
93+
}
94+
95+
return result, nil
96+
}
97+
98+
// WaitForJobResult polls a job until completion.
99+
func (c *Client) WaitForJobResult(_ context.Context, jobID string) (*ExecuteResult, error) {
100+
raw, err := WaitForJob(c.Creds, jobID)
101+
if err != nil {
102+
return nil, err
103+
}
104+
return toExecuteResult(raw), nil
105+
}
106+
107+
// SessionResult holds typed session info.
108+
type SessionResult struct {
109+
ID string
110+
Status string
111+
Raw map[string]interface{}
112+
}
113+
114+
// CreateSession creates a new execution session.
115+
func (c *Client) CreateSession(_ context.Context, language, networkMode string) (*SessionResult, error) {
116+
opts := &SessionOptions{
117+
Shell: language,
118+
}
119+
if networkMode != "" {
120+
opts.NetworkMode = networkMode
121+
}
122+
123+
raw, err := CreateSession(c.Creds, opts)
124+
if err != nil {
125+
return nil, err
126+
}
127+
128+
s := &SessionResult{Raw: raw}
129+
if v, ok := raw["session_id"].(string); ok {
130+
s.ID = v
131+
}
132+
if v, ok := raw["id"].(string); ok && s.ID == "" {
133+
s.ID = v
134+
}
135+
if v, ok := raw["status"].(string); ok {
136+
s.Status = v
137+
}
138+
return s, nil
139+
}
140+
141+
// ShellExec runs a command in an existing session.
142+
func (c *Client) ShellExec(_ context.Context, sessionID, command string) (*ExecuteResult, error) {
143+
raw, err := ShellSession(c.Creds, sessionID, command)
144+
if err != nil {
145+
return nil, err
146+
}
147+
148+
result := toExecuteResult(raw)
149+
150+
// Poll if async
151+
if result.JobID != "" && result.Status != "completed" && result.Status != "failed" {
152+
polled, err := WaitForJob(c.Creds, result.JobID)
153+
if err != nil {
154+
return nil, err
155+
}
156+
return toExecuteResult(polled), nil
157+
}
158+
159+
return result, nil
160+
}
161+
162+
// DestroySession deletes a session.
163+
func (c *Client) DestroySession(_ context.Context, sessionID string) error {
164+
_, err := DeleteSession(c.Creds, sessionID)
165+
return err
166+
}
167+
168+
// Sessions returns all active sessions.
169+
func (c *Client) Sessions(_ context.Context) ([]map[string]interface{}, error) {
170+
return ListSessions(c.Creds)
171+
}
172+
173+
// Services returns all services.
174+
func (c *Client) Services(_ context.Context) ([]map[string]interface{}, error) {
175+
return ListServices(c.Creds)
176+
}
177+
178+
// ServiceLogs retrieves logs for a service.
179+
func (c *Client) ServiceLogs(_ context.Context, serviceID string) (string, error) {
180+
raw, err := GetServiceLogs(c.Creds, serviceID, false)
181+
if err != nil {
182+
return "", err
183+
}
184+
if v, ok := raw["logs"].(string); ok {
185+
return v, nil
186+
}
187+
return "", nil
188+
}
189+
190+
// CheckKeys verifies credentials are valid and returns key info.
191+
func (c *Client) CheckKeys(_ context.Context) (map[string]interface{}, error) {
192+
return ValidateKeys(c.Creds)
193+
}
194+
195+
// InjectDirectory uploads a local directory to a session as a tarball.
196+
// This is a higher-level operation that creates a tar.gz, base64-chunks it,
197+
// and streams it into the session via shell commands.
198+
func (c *Client) InjectDirectory(_ context.Context, sessionID, localDir, remoteDir string) error {
199+
return fmt.Errorf("InjectDirectory not implemented in SDK client — use orchestra's upload package")
200+
}

clients/go/sync/src/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
module unsandbox.com/un
1+
module github.com/russellballestrini/un-inception/clients/go/sync/src
22

33
go 1.23.6

0 commit comments

Comments
 (0)