Skip to main content

Installation

go get github.com/getnenai/nen-sdk-go
Zero external dependencies — uses only the Go standard library.

Quick Start

package main

import (
	"context"
	"fmt"

	nendesktop "github.com/getnenai/nen-sdk-go"
)

func main() {
	client := nendesktop.New(nendesktop.Config{APIKey: "sk_nen_..."})
	ctx := context.Background()

	// Create a desktop
	desktop, err := client.CreateDesktop(ctx, "sandbox")
	if err != nil {
		panic(err)
	}
	fmt.Printf("Created: %s (status: %s)\n", desktop.DesktopID, desktop.Status)

	// Take a screenshot
	result, err := client.Execute(ctx, desktop.DesktopID, "computer", "screenshot", nil)
	if err != nil {
		panic(err)
	}
	// result.Base64Image is a PNG, base64-encoded
	_ = result

	// List available tools
	tools, _ := client.ListTools(ctx, desktop.DesktopID)
	_ = tools

	// Clean up
	client.DeleteDesktop(ctx, desktop.DesktopID)
}

Configuration

client := nendesktop.New(nendesktop.Config{
	APIKey:  "sk_nen_...",
	BaseURL: "https://desktop.api.getnen.ai", // default
	Timeout: 30 * time.Second,                // default
})
The Execute method always uses a 120-second timeout, regardless of the client timeout.

Method Reference

Desktop CRUD

MethodReturnsDescription
CreateDesktop(ctx, desktopType)(*Desktop, error)Create a new desktop
ListDesktops(ctx)([]Desktop, error)List all active desktops
GetDesktop(ctx, desktopID)(*Desktop, error)Get a single desktop by ID
UpdateDesktop(ctx, desktopID, name)(*Desktop, error)Rename a desktop
DeleteDesktop(ctx, desktopID)(*DeleteResponse, error)Delete a desktop

Tool Execution

MethodReturnsDescription
Execute(ctx, desktopID, tool, action, params)(*ExecuteResult, error)Execute a tool action
ListTools(ctx, desktopID)([]ToolSchema, error)List available tools as JSON Schema
GetToolLogs(ctx, desktopID)(json.RawMessage, error)Get tool execution logs

Sessions

MethodReturnsDescription
CreateSession(ctx, desktopID)(*SessionInfo, error)Create or reconnect an RDP session
GetSession(ctx, desktopID)(*SessionInfo, error)Get current session status
DeleteSession(ctx, desktopID)errorDisconnect the session

Files

Per-desktop shared drive (EFS-backed; persists across controller rebinds; 100 MiB upload cap). All three methods use a 120-second per-call deadline. All three methods accept the same FileOption variadic; WithPath("subdir") selects a subdirectory for the operation.
MethodReturnsDescription
ListFiles(ctx, desktopID, opts...)([]File, error)List files on the desktop’s drive. Entries have IsDir: true for directories.
UploadFile(ctx, desktopID, name, body, contentType, opts...)(*UploadFileResponse, error)Upload from any io.Reader; empty contentType sends application/octet-stream. With WithPath, missing intermediate directories are created on the server.
DownloadFile(ctx, desktopID, name, opts...)(io.ReadCloser, string, error)Stream the file body; second return is the response Content-Type. Caller must Close the body.
// Upload, list, then download.
f, _ := os.Open("report.pdf")
defer f.Close()
up, _ := client.UploadFile(ctx, did, "report.pdf", f, "application/pdf")
fmt.Printf("%d bytes uploaded as %s\n", up.Size, up.Filename)

files, _ := client.ListFiles(ctx, did)
for _, entry := range files {
    suffix := ""
    if entry.IsDir {
        suffix = "/"
    }
    fmt.Println(entry.Name+suffix, entry.Size)
}

// Round-trip within a subdirectory.
f2, _ := os.Open("report.pdf")
defer f2.Close()
client.UploadFile(ctx, did, "report.pdf", f2, "application/pdf", nendesktop.WithPath("Documents"))
nested, _ := client.ListFiles(ctx, did, nendesktop.WithPath("Documents"))
for _, entry := range nested {
    fmt.Println(entry.Name)
}
rc, _, _ := client.DownloadFile(ctx, did, "report.pdf", nendesktop.WithPath("Documents"))
defer rc.Close()
// io.Copy(out, rc) etc.

Error Handling

All API errors return *APIError, which carries StatusCode and Body:
import "errors"

desktop, err := client.GetDesktop(ctx, "dsk_nonexistent")
if err != nil {
	var apiErr *nendesktop.APIError
	if errors.As(err, &apiErr) {
		fmt.Printf("Error %d: %s\n", apiErr.StatusCode, apiErr.Body)
	}
}
Inspect apiErr.StatusCode to differentiate (401, 404, 409, 5xx).