Artifacts

Upload, download, and share persistent files across sandboxes.

Artifacts

Artifacts provide persistent file storage that lives outside any single sandbox. Use them to export outputs, share files between sandboxes, and build data pipelines.

Artifact operations are available on the extended client (StateSetSandboxExtended in Node.js).


Upload an Artifact

Upload a file from a running sandbox to persistent storage.

Node.js

typescript
import { StateSetSandboxExtended } from '@stateset/sandbox-sdk';

const client = new StateSetSandboxExtended({
  baseUrl: 'https://api.sandbox.stateset.app',
  authToken: process.env.SANDBOX_API_KEY!,
});

const artifact = await client.uploadArtifact(sandboxId, {
  path: '/workspace/output/report.pdf',
  remote_path: 'reports/weekly-report.pdf',
  content_type: 'application/pdf',
  metadata: { generated_by: 'analytics-pipeline' },
});

console.log(`Artifact ID: ${artifact.id}`);
console.log(`Size: ${artifact.size} bytes`);
console.log(`Checksum: ${artifact.checksum}`);

Python

python
artifact = client.upload_artifact(
    sandbox_id,
    path="/workspace/output/report.pdf",
    remote_path="reports/weekly-report.pdf",
    content_type="application/pdf",
    metadata={"generated_by": "analytics-pipeline"},
)

print(f"Artifact ID: {artifact.id}")
print(f"Size: {artifact.size} bytes")

Download an Artifact

Download a previously stored artifact into a sandbox.

Node.js

typescript
await client.downloadArtifact(sandboxId, artifact.id, '/workspace/input/report.pdf');

// Verify the file
const result = await client.execute(sandboxId, {
  command: ['ls', '-la', '/workspace/input/report.pdf'],
});
console.log(result.stdout);

Python

python
client.download_artifact(sandbox_id, artifact.id, "/workspace/input/report.pdf")

result = client.execute(sandbox_id, command=["ls", "-la", "/workspace/input/report.pdf"])
print(result.stdout)

Get a Presigned Download URL

Generate a time-limited URL to download an artifact directly (useful for browser downloads or sharing).

Node.js

typescript
const { url, expires_in } = await client.getArtifactUrl(artifact.id, 3600);
console.log(`Download URL (valid for ${expires_in}s): ${url}`);

Python

python
result = client.get_artifact_url(artifact.id, expires_in=3600)
print(f"Download URL (valid for {result.expires_in}s): {result.url}")

List Artifacts

Node.js

typescript
// List all artifacts for the organization
const { artifacts } = await client.listArtifacts();

// Filter by sandbox
const { artifacts: sandboxArtifacts } = await client.listArtifacts(sandboxId);

for (const a of artifacts) {
  console.log(`${a.id}${a.path} (${a.size} bytes, ${a.createdAt})`);
}

Python

python
# All artifacts
artifacts = client.list_artifacts()

# Filter by sandbox
artifacts = client.list_artifacts(sandbox_id=sandbox_id)

for a in artifacts:
    print(f"{a.id}{a.path} ({a.size} bytes)")

Use Cases

Export Build Outputs

typescript
// Run build inside sandbox
await client.execute(sandboxId, { command: ['npm', 'run', 'build'] });

// Upload the build artifact
const artifact = await client.uploadArtifact(sandboxId, {
  path: '/workspace/dist/bundle.js',
  remote_path: `builds/${buildId}/bundle.js`,
  content_type: 'application/javascript',
});

Share Data Between Sandboxes

typescript
// Sandbox A: produce data
const artifact = await client.uploadArtifact(sandboxA, {
  path: '/workspace/data/processed.csv',
});

// Sandbox B: consume data
await client.downloadArtifact(sandboxB, artifact.id, '/workspace/input/processed.csv');

Data Pipeline

python
# Step 1: Extract
extract_result = client.execute(sandbox_id, command=["python3", "extract.py"])

# Step 2: Upload intermediate result
artifact = client.upload_artifact(sandbox_id, path="/workspace/raw_data.parquet")

# Step 3: Download into a fresh sandbox for transformation
transform_sandbox = client.create(cpus="4", memory="8Gi", timeout_seconds=600)
client.download_artifact(transform_sandbox.sandbox_id, artifact.id, "/workspace/raw_data.parquet")
client.execute(transform_sandbox.sandbox_id, command=["python3", "transform.py"])

# Step 4: Upload final output
final = client.upload_artifact(transform_sandbox.sandbox_id, path="/workspace/output.parquet")
print(f"Pipeline complete. Final artifact: {final.id}")

Agent Session Artifacts

If you are using agent sessions, the extended client provides a convenience method:

typescript
import { StateSetSandboxExtended } from '@stateset/sandbox-sdk';

const client = new StateSetSandboxExtended({
  baseUrl: 'https://api.sandbox.stateset.app',
  authToken: process.env.SANDBOX_API_KEY!,
});

// Upload from the session's current sandbox (auto-tags with session ID)
const artifact = await client.uploadAgentSessionArtifact(sessionId, {
  path: '/workspace/result.json',
  metadata: { step: 'final' },
});