Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions apps/docs/content/docs/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
"cli/cli-reference",
"cli/host-server",
"cli/env-vars",
"---Package TypeScript SDK---",
"sdk/getting-started",
"sdk/reference",
"sdk/advanced",
"---BookOpen Guides---",
"setup-teardown-scripts",
"use-with-ide",
Expand Down
190 changes: 190 additions & 0 deletions apps/docs/content/docs/sdk/advanced.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
---
title: Advanced Usage
description: Real-world recipes that combine the SDK's primitives — webhooks, triage scripts, and on-demand agents.
---

<Callout type="warn">
The SDK is in early alpha — it is not meant for production use and may be removed in the future. Stay on the latest version (`@superset_sh/sdk@latest`) while we iterate.
</Callout>

The SDK's individual methods are simple. The interesting part is what you compose them into. These are end-to-end recipes you can copy as starting points.

## Sentry webhook → agent on the bug

<Callout type="info">
**TODO** — the `agents: [{ prompt }]` shorthand below is the API we're aiming for. Today the SDK sends a minimal `agentConfig: { id: 'claude', kind: 'terminal' }`; the host service still needs full launch info (`command`, `promptCommand`, …) to actually start the agent. We'll bake in defaults for built-in presets in a follow-up. Until then you can pass a full `agentConfig` per agent — crib one from `(await client.automations.list())[0].agentConfig`.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The docs example can throw when no automations exist because it dereferences [0].agentConfig without checking for an empty list.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/docs/content/docs/sdk/advanced.mdx, line 15:

<comment>The docs example can throw when no automations exist because it dereferences `[0].agentConfig` without checking for an empty list.</comment>

<file context>
@@ -11,6 +11,10 @@ The SDK's individual methods are simple. The interesting part is what you compos
 ## Sentry webhook → agent on the bug
 
+<Callout type="info">
+**TODO** — the `agents: [{ prompt }]` shorthand below is the API we're aiming for. Today the SDK sends a minimal `agentConfig: { id: 'claude', kind: 'terminal' }`; the host service still needs full launch info (`command`, `promptCommand`, …) to actually start the agent. We'll bake in defaults for built-in presets in a follow-up. Until then you can pass a full `agentConfig` per agent — crib one from `(await client.automations.list())[0].agentConfig`.
+</Callout>
+
</file context>
Suggested change
**TODO** — the `agents: [{ prompt }]` shorthand below is the API we're aiming for. Today the SDK sends a minimal `agentConfig: { id: 'claude', kind: 'terminal' }`; the host service still needs full launch info (`command`, `promptCommand`, …) to actually start the agent. We'll bake in defaults for built-in presets in a follow-up. Until then you can pass a full `agentConfig` per agent — crib one from `(await client.automations.list())[0].agentConfig`.
**TODO** — the `agents: [{ prompt }]` shorthand below is the API we're aiming for. Today the SDK sends a minimal `agentConfig: { id: 'claude', kind: 'terminal' }`; the host service still needs full launch info (`command`, `promptCommand`, …) to actually start the agent. We'll bake in defaults for built-in presets in a follow-up. Until then you can pass a full `agentConfig` per agent — for example, read the first automation safely: `const first = (await client.automations.list())[0]; if (!first) throw new Error('Create an automation first'); const agentConfig = first.agentConfig;`.

</Callout>

When Sentry fires an alert, file a task and spawn an agent to investigate — all in two SDK calls. (In production, verify the Sentry signature header before trusting any payload — that part is omitted here for clarity.)

```ts title="sentry-webhook.ts"
import { Hono } from 'hono';
import Superset from '@superset_sh/sdk';

const app = new Hono();
const client = new Superset();
// reads SUPERSET_API_KEY + SUPERSET_ORGANIZATION_ID from env

const TARGET_HOST_ID = process.env.SUPERSET_TARGET_HOST_ID!; // see hosts.list()
const PROJECT_ID = process.env.SUPERSET_PROJECT_ID!; // see projects.list()

app.post('/webhooks/sentry', async (c) => {
const event = await c.req.json();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: Verify webhook authenticity before processing Sentry payloads; this handler currently trusts any POST body and can be abused to trigger task/workspace/automation actions.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/docs/content/docs/sdk/advanced.mdx, line 34:

<comment>Verify webhook authenticity before processing Sentry payloads; this handler currently trusts any POST body and can be abused to trigger task/workspace/automation actions.</comment>

<file context>
@@ -0,0 +1,149 @@
+const PROJECT_ID = process.env.SUPERSET_PROJECT_ID!;          // projects.list() to find yours
+
+app.post('/webhooks/sentry', async (c) => {
+  const event = await c.req.json();
+  const { title, web_url: sentryUrl, culprit } = event.data.issue;
+  const slug = `sentry-${event.data.issue.short_id.toLowerCase()}`;
</file context>

const { title, web_url: sentryUrl, culprit, short_id } = event.data.issue;
const slug = `sentry-${short_id.toLowerCase()}`;

// 1. File a task so the bug shows up in the dashboard
const task = await client.tasks.create({
title: `[Sentry] ${title}`,
description: `Triggered by ${culprit}\n\nSentry: ${sentryUrl}`,
priority: 'high',
labels: ['sentry', 'bug'],
});

// 2. Spin up a worktree on a developer's machine and dispatch an agent
// inside it. The SDK creates the worktree, then runs the agent with
// your prompt — you don't manage the dispatch loop.
const workspace = await client.workspaces.create({
hostId: TARGET_HOST_ID,
projectId: PROJECT_ID,
name: slug,
branch: slug,
agents: [
{
agent: 'claude',
prompt: [
`A Sentry alert just fired:`,
` Title: ${title}`,
` Culprit: ${culprit}`,
` URL: ${sentryUrl}`,
``,
`Reproduce the failure, fix it, push a branch, and update task`,
`${task.id} with what you found.`,
].join('\n'),
},
],
});

return c.json({
ok: true,
taskId: task.id,
workspaceId: workspace.id,
runId: workspace.agentRuns[0]?.runId,
});
});

export default app;
```

What just happened, end-to-end:

- The task lives in the cloud and shows up in the dashboard for everyone.
- The workspace is a real `git worktree` on the target machine, ready for a human to take over if the agent gets stuck.
- The agent runs inside the new workspace, with the prompt above and access to the codebase + whatever MCP servers the host has configured.

You can spawn multiple agents in parallel by adding more entries to `agents`:

```ts
agents: [
{ agent: 'claude', prompt: 'Reproduce the bug and write a failing test.' },
{ agent: 'claude', prompt: 'Investigate related Sentry issues from the last 7 days.' },
],
```

## Burn down the P0 backlog overnight

Pick up every urgent task in the queue and spawn an agent on each one in parallel. Comes back the next morning with a branch and a comment per task.

```ts title="burn-p0s.ts"
import Superset from '@superset_sh/sdk';

const client = new Superset();
const HOST_ID = process.env.SUPERSET_HOST_ID!;
const PROJECT_ID = process.env.SUPERSET_PROJECT_ID!; // see projects.list()

const p0s = await client.tasks.list({ priority: 'urgent', limit: 50 });

const dispatched = await Promise.all(
p0s.map((task) =>
client.workspaces.create({
hostId: HOST_ID,
projectId: PROJECT_ID,
name: task.slug,
branch: task.slug,
agents: [
{
agent: 'claude',
prompt: [
`Pick up task ${task.slug}: "${task.title}".`,
``,
task.description ?? '(no description — read recent changes for context)',
``,
`Investigate, make the fix, run the relevant tests, push the branch, and`,
`update task ${task.id} with a one-paragraph summary of what changed and why.`,
`If you can't make confident progress in ~30 minutes, leave a comment with`,
`where you got stuck and stop.`,
].join('\n'),
},
],
}),
),
);

console.log(`dispatched ${dispatched.length} agents across the P0 backlog`);
```

Run it from a daily cron on a beefy host and you wake up to a branch per bug. Ones the agent could fix have a PR-ready branch; ones it couldn't have a comment explaining why so a human picks up where the agent stopped.

## Sweep a refactor across every repo

Internal API rename, dependency bump, lint rule rollout — anything that has to land identically in dozens of repos is a one-loop, fan-out problem:

```ts title="sweep-rename-getUser.ts"
import Superset from '@superset_sh/sdk';

const client = new Superset();
const HOST_ID = process.env.SUPERSET_HOST_ID!;

const projects = await client.projects.list();

const wave = await Promise.all(
projects.map((p) =>
client.workspaces.create({
hostId: HOST_ID,
projectId: p.id,
name: `rename-getUser-${p.slug}`,
branch: 'refactor/rename-getUser',
agents: [
{
agent: 'claude',
prompt: [
`Rename every caller of \`getUser()\` to \`fetchUser()\` across this repo.`,
``,
`1. Find call sites with grep.`,
`2. Update each one and re-run the test suite.`,
`3. If green, push the branch and open a draft PR titled`,
` "refactor: rename getUser → fetchUser".`,
`4. If a repo doesn't actually use getUser, exit cleanly with a note.`,
].join('\n'),
},
],
}),
),
);

const totalAgents = wave.reduce((n, ws) => n + ws.agentRuns.length, 0);
console.log(`fanned out ${totalAgents} agents across ${projects.length} repos`);
```

Same loop works for "bump TypeScript to 5.7", "swap Sentry SDK for the new one", "add a `lint:strict` script to every package.json". Anything you'd otherwise farm out to a long Slack thread.

## On-demand agent dispatch from your tools

If you've already created a recurring automation in the dashboard, the SDK can fire it off-schedule from anywhere — a Slack slash command, a CI job, an internal admin panel:

```ts
const run = await client.automations.run('<automation-id>');
console.log(`dispatched run ${run.id} to host ${run.hostId}`);
```

The automation's pinned host has to be online and tunneling. The SDK will surface a `503 Host not connected` error if it isn't, so you can retry later or page someone.
149 changes: 149 additions & 0 deletions apps/docs/content/docs/sdk/getting-started.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
---
title: Getting Started
description: Drive Superset programmatically from any TypeScript or JavaScript codebase.
---

<Callout type="warn">
The SDK is in early alpha — it is not meant for production use and may be removed in the future. Stay on the latest version (`@superset_sh/sdk@latest`) while we iterate.
</Callout>

The Superset TypeScript SDK lets you create and manage your workspaces, tasks, projects, and automations from code — anything you can do in the dashboard, your scripts and services can do too.

A few things you might use it for:

- **Bulk task management** — script the creation, triage, or cleanup of tasks across one or many projects.
- **Integrations** — sync tasks to/from your CRM, issue tracker, or internal tools.
- **Provisioning** — spin up workspaces on a developer's machine when an issue is opened, a PR is reviewed, or a customer ticket lands.
- **On-demand agents** — kick off a recurring automation off-schedule when a webhook fires.

Everything is fully typed and works in Node, Bun, and Deno. The bundle also runs in browsers, but **don't ship `sk_live_…` keys to a frontend** — they're long-lived and grant full org access. From the browser, talk to a thin server proxy that holds the key.

## Install

<Tabs items={["npm", "pnpm", "yarn", "bun"]}>
<Tab value="npm">
```bash
npm install @superset_sh/sdk
```
</Tab>
<Tab value="pnpm">
```bash
pnpm add @superset_sh/sdk
```
</Tab>
<Tab value="yarn">
```bash
yarn add @superset_sh/sdk
```
</Tab>
<Tab value="bun">
```bash
bun add @superset_sh/sdk
```
</Tab>
</Tabs>

We're under active development — the SDK follows the API as it grows, so **stay on the latest version** rather than pinning. New methods get added; existing ones don't change shape without a version bump and a note in the changelog.

## Get an API key

1. Open the [Superset desktop app](https://superset.sh/download) → **Settings → API Keys** → **Create**. The key starts with `sk_live_…` — copy it now (you won't be able to see it again).
2. Note your **organization ID** — find it in the org switcher inside the desktop app.

```bash
export SUPERSET_API_KEY=sk_live_…
export SUPERSET_ORGANIZATION_ID=…
```

## Create the client

```ts
import Superset from '@superset_sh/sdk';

const client = new Superset();
// reads SUPERSET_API_KEY and SUPERSET_ORGANIZATION_ID from env
```

That's all the setup. From here, every resource works the same way:

```ts
// Create a task
const task = await client.tasks.create({
title: 'Wire up auth',
priority: 'high',
});

// Update it
await client.tasks.update({ id: task.id, statusId: '<uuid>' });

// Look one up by id or slug
const got = await client.tasks.retrieve('SUPER-172'); // returns null if missing

// Find tasks with rich filters
const urgent = await client.tasks.list({
priority: 'urgent',
search: 'auth',
creatorMe: true,
});

// Inspect what else you have
const projects = await client.projects.list();
const hosts = await client.hosts.list();
const automations = await client.automations.list();

// Trigger an automation off-schedule
await client.automations.run('<automation-id>');

// Create a workspace on a developer's machine
await client.workspaces.create({
hostId: '<machine-id>',
projectId: '<project-id>',
name: 'fix-auth-bug',
branch: 'fix/auth',
});
```

See the [reference](/docs/sdk/reference) for every method, parameter, and return type.

## Configuration

You can configure the client explicitly instead of (or alongside) the env vars:

```ts
const client = new Superset({
apiKey: 'sk_live_…',
organizationId: '…',
timeout: 60_000, // default: 60s per request
maxRetries: 2, // default: 2 retries with exponential backoff
logLevel: 'warn', // 'off' | 'error' | 'warn' | 'info' | 'debug'
});
```

The client retries automatically on network errors and 429/5xx responses, so transient failures usually fix themselves.

## Handling errors

The SDK throws typed errors for any non-success response, so you can catch what you actually care about:

```ts
import Superset, { APIError, NotFoundError, RateLimitError } from '@superset_sh/sdk';

const client = new Superset();

try {
await client.tasks.create({ title: '' });
} catch (err) {
if (err instanceof RateLimitError) {
// 429 — already retried up to maxRetries; back off further if you keep hitting it
} else if (err instanceof NotFoundError) {
// 404
} else if (err instanceof APIError) {
// anything else — err.status, err.headers, err.error (the parsed body)
console.error(`Superset returned ${err.status}:`, err.error);
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
```

## Next

Head to the [reference](/docs/sdk/reference) for the complete surface.
5 changes: 5 additions & 0 deletions apps/docs/content/docs/sdk/meta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"title": "TypeScript SDK",
"icon": "Package",
"pages": ["getting-started", "reference", "advanced"]
}
Loading
Loading