# Cliente

> Crie um cliente do SDK do Nodaro com createClient, defina URL base, autenticação, tempo limite e espaço de trabalho, e veja todos os recursos do cliente.

Source: https://nodaro.ai/pt-BR/docs/developers/sdk/client

O **cliente** é o objeto que `createClient()` retorna: um `NodaroClient` que guarda a URL base, o provedor de autenticação e as configurações, e expõe cada parte da API do Nodaro como um recurso, como `client.workflows` ou `client.nodes`. Você o cria uma vez e o reutiliza em todas as chamadas.

## Criar um cliente
```ts

const client = createClient({
baseUrl: "https://app.nodaro.ai",
auth: new StaticTokenAuth(process.env.NODARO_TOKEN!),
timeoutMs: 120_000,
})
```

```ts
createClient(options: ClientOptions): NodaroClient
```

<TypeTable
type={{
baseUrl: {
type: 'string',
required: true,
description: "O servidor do Nodaro, por exemplo https://app.nodaro.ai ou o endereço da sua instalação self-hosted. Use uma string vazia para requisições de mesma origem em um app de navegador. Uma barra no final é removida.",
},
auth: {
type: 'Auth',
required: true,
description: "O provedor de autenticação: new StaticTokenAuth(token), supabaseAuth(supabase), new CallbackAuth(fn) ou qualquer objeto com um método getToken().",
typeDescriptionLink: '/docs/developers/sdk/auth',
},
fetch: {
type: 'typeof fetch',
default: 'globalThis.fetch',
description: "Uma função fetch personalizada, para testes, novas tentativas ou rastreamento.",
},
timeoutMs: {
type: 'number',
default: '60000',
description: "O tempo limite de cada requisição, em milissegundos. A requisição é abortada quando o tempo se esgota.",
},
workspaceId: {
type: 'string',
description: "O espaço de trabalho em que todas as requisições agem, enviado como o cabeçalho X-Nodaro-Workspace. Omita-o para trabalhar no seu espaço pessoal. Apenas para organizações do Nodaro Cloud.",
},
clientLabel: {
type: 'string',
default: "'sdk/<version>'",
description: "O valor do cabeçalho X-Nodaro-Client. O Nodaro o registra como a origem de cada job. Defina-o apenas quando criar outra ferramenta sobre o SDK.",
},
}}
/>

`NodaroClient` também é exportado como classe, então você pode tipar uma função que recebe um cliente:

```ts

async function countWorkflows(client: NodaroClient, projectId: string) {
const { data } = await client.workflows.list({ projectId })
return data.length
}
```

## Recursos do cliente
Cada recurso é criado por `createClient` e acessado como `client.<resource>`.

| Recurso | O que cobre | Referência |
| --- | --- | --- |
| `client.workflows` | Workflows: criar, atualizar, compartilhar, exportar, importar e executar | [Workflows e projetos](https://nodaro.ai/docs/developers/sdk/workflows) |
| `client.projects` | Os projetos que guardam os workflows | [Workflows e projetos](https://nodaro.ai/docs/developers/sdk/workflows) |
| `client.executions` | Execuções de um workflow inteiro | [Jobs e execuções](https://nodaro.ai/docs/developers/sdk/jobs-and-executions) |
| `client.jobs` | Jobs de geração avulsos | [Jobs e execuções](https://nodaro.ai/docs/developers/sdk/jobs-and-executions) |
| `client.videoPro` | Interromper ou continuar uma execução do **Gerar vídeo Pro** (Generate Video Pro) | [Jobs e execuções](https://nodaro.ai/docs/developers/sdk/jobs-and-executions) |
| `client.nodes` | O catálogo de nós e as execuções de um único nó | [Executar nós](https://nodaro.ai/docs/developers/sdk/nodes) |
| `client.apps` | Apps publicados e as execuções deles | [Apps e templates](https://nodaro.ai/docs/developers/sdk/apps-and-templates) |
| `client.templates` | O marketplace de templates | [Apps e templates](https://nodaro.ai/docs/developers/sdk/apps-and-templates) |
| `client.tutorials` | Vídeos de tutorial e workflows de tutorial | [Apps e templates](https://nodaro.ai/docs/developers/sdk/apps-and-templates) |
| `client.llm` | Saída estruturada de um modelo de linguagem | [LLM e Reduce](https://nodaro.ai/docs/developers/sdk/llm-and-reduce) |
| `client.reduce` | Escolher o melhor de muitos resultados, ou combiná-los | [LLM e Reduce](https://nodaro.ai/docs/developers/sdk/llm-and-reduce) |
| `client.uploads` | Upload de arquivos | [Mídia e uploads](https://nodaro.ai/docs/developers/sdk/media-and-uploads) |
| `client.library` | As suas mídias armazenadas | [Mídia e uploads](https://nodaro.ai/docs/developers/sdk/media-and-uploads) |
| `client.media` | Baixar, cortar, legendar, sobrepor e fazer colagens de mídias | [Mídia e uploads](https://nodaro.ai/docs/developers/sdk/media-and-uploads) |
| `client.voices` | Vozes, modificador de voz, design de voz e dublagem | [Vozes e áudio](https://nodaro.ai/docs/developers/sdk/voices-and-audio) |
| `client.audio` | Separar, isolar, mixar, cortar e transcrever áudio | [Vozes e áudio](https://nodaro.ai/docs/developers/sdk/voices-and-audio) |
| `client.edit` | Detecção de silêncio, sincronização de áudio, planos de edição e renderizações de EDL | [Edição](https://nodaro.ai/docs/developers/sdk/editing) |
| `client.scene3d` | Cenas 3D editáveis e **Renderização 3D Pro** (3D Render Pro) | [Cenas 3D](https://nodaro.ai/docs/developers/sdk/scenes-3d) |
| `client.characters` | Personagens | [Personagens](https://nodaro.ai/docs/developers/sdk/characters) |
| `client.locations` | Locais | [Locais](https://nodaro.ai/docs/developers/sdk/locations) |
| `client.objects` | Objetos e adereços | [Objetos e criaturas](https://nodaro.ai/docs/developers/sdk/objects-and-creatures) |
| `client.creatures` | Animais e criaturas | [Objetos e criaturas](https://nodaro.ai/docs/developers/sdk/objects-and-creatures) |
| `client.community` | A biblioteca compartilhada de entidades da comunidade | [Biblioteca da comunidade](https://nodaro.ai/docs/developers/sdk/community) |
| `client.studio` | Produções do Studio | [Produções do Studio](https://nodaro.ai/docs/developers/sdk/studio) |
| `client.shots` | Registros compartilhados de tomadas, por trás dos links de compartilhamento | [Produções do Studio](https://nodaro.ai/docs/developers/sdk/studio) |
| `client.recast` | Execuções do Recast e roteiros próprios | [Recast](https://nodaro.ai/docs/developers/sdk/recast) |
| `client.pipelines` | Pipelines de história para vídeo | [Pipelines](https://nodaro.ai/docs/developers/sdk/pipelines) |
| `client.copilot` | Threads do Copilot, apenas dentro do app do Nodaro | [Copilot](https://nodaro.ai/docs/developers/sdk/copilot) |
| `client.models` | O catálogo de modelos | [Modelos e créditos](https://nodaro.ai/docs/developers/sdk/models-and-credits) |
| `client.credits` | O seu saldo e os preços dos modelos | [Modelos e créditos](https://nodaro.ai/docs/developers/sdk/models-and-credits) |
| `client.pickerCatalogs` | As opções válidas de cada seletor | [Seletores, predefinições e prompts](https://nodaro.ai/docs/developers/sdk/pickers-and-prompts) |
| `client.catalogs` | Todos os catálogos de seletores em uma única chamada | [Seletores, predefinições e prompts](https://nodaro.ai/docs/developers/sdk/pickers-and-prompts) |
| `client.presets` | Predefinições de nó salvas e nativas | [Seletores, predefinições e prompts](https://nodaro.ai/docs/developers/sdk/pickers-and-prompts) |
| `client.promptHelper` | O Assistente de prompt | [Seletores, predefinições e prompts](https://nodaro.ai/docs/developers/sdk/pickers-and-prompts) |
| `client.organizations` | Organizações, membros e convites | [Organizações e espaços de trabalho](https://nodaro.ai/docs/developers/sdk/organizations) |
| `client.workspaces` | Espaços de trabalho, membros e códigos de entrada | [Organizações e espaços de trabalho](https://nodaro.ai/docs/developers/sdk/organizations) |
| `client.developerApps` | Os apps OAuth que você possui | [OAuth e apps de desenvolvedor](https://nodaro.ai/docs/developers/sdk/developer-apps) |
| `client.oauth` | Troca de código, revogação de tokens e dados da tela de consentimento | [OAuth e apps de desenvolvedor](https://nodaro.ai/docs/developers/sdk/developer-apps) |

O próprio cliente tem mais três métodos: [`me()`](#me), [`withWorkspace()`](#withworkspaceworkspaceid) e [`request()`](#requestmethod-path-options).

## O que os métodos retornam
- **Os envelopes são mantidos.** Quando um endpoint responde `{ "data": ... }`, o método resolve com esse envelope, então você escreve `const { data } = await client.workflows.get(id)`. As listas paginadas acrescentam um cursor ao lado de `data`, como `nextCursor`.
- **Alguns recursos retornam o payload.** Alguns métodos desembrulham a resposta: por exemplo, `client.characters.list()` resolve com `{ characters, nextCursor }`, e `client.credits.balance()`, com o próprio saldo. Cada página de referência mostra o tipo de retorno exato.
- **Exclusões e cancelamentos** em geral resolvem com `{ success: true }`.
- **Os nomes dos campos seguem o formato da API.** Um `Job` usa campos em snake_case, como `output_data` e `created_at`, porque a API os envia assim. Um `Workflow` e um `WorkflowExecution` usam camelCase.

Todos os tipos de resposta e de entrada são exportados, então você pode importá-los com `import type`. Veja [Tipos](https://nodaro.ai/docs/developers/sdk/types).

## me()
```ts
me(): Promise<UserIdentity & MeOrganizations>
```

Retorna a identidade por trás do token atual (`GET /v1/me`). Qualquer token válido leva ao dono dele, seja um token de API, um token de acesso OAuth ou uma sessão do navegador. Um token ausente ou inválido lança `UnauthorizedError`.

```ts
const me = await client.me()
console.log(me.email, me.tier)
```

| Campo | Tipo | Descrição |
| --- | --- | --- |
| `id` | `string` | O ID do usuário no Nodaro. |
| `email` | `string` | O endereço de e-mail do usuário. |
| `displayName` | `string \| null` | O nome de exibição, ou `null` quando não está definido. |
| `avatarUrl` | `string \| null` | A URL do avatar, ou `null` quando não está definida. |
| `tier` | `string` | O nível de assinatura armazenado, como `"free"` ou `"pro"`. Para o nível realmente aplicado, incluindo o pagamento conforme o uso, leia `effectiveTier` em [`client.credits.balance()`](https://nodaro.ai/docs/developers/sdk/models-and-credits). |
| `isAdmin` | `boolean` | Se o usuário é administrador. Use-o apenas para decidir o que mostrar. O próprio servidor verifica todas as permissões. |

Em uma instância do Nodaro Cloud com organizações, o resultado também traz `organizations`, `workspaces`, `lastWorkspaceId` e `organizationsUnavailable`. Trate os três estados deles de formas diferentes:

| O que você vê | O que significa | O que fazer |
| --- | --- | --- |
| Os campos estão ausentes | A instância não tem organizações | Não mostre um seletor de espaço de trabalho. |
| Os campos estão presentes e vazios | A conta não pertence a nenhuma organização | Ofereça criar uma organização ou entrar em uma. |
| `organizationsUnavailable: true` | A consulta falhou | Mantenha a seleção que você já tinha. Não diga ao usuário que ele perdeu o acesso. |

## withWorkspace(workspaceId)
```ts
withWorkspace(workspaceId: string | null): NodaroClient
```

Retorna um **novo** cliente que age em `workspaceId`. O novo cliente compartilha a autenticação, a URL base, o tempo limite e o fetch do original. Passe `null` para usar o seu espaço pessoal.

```ts
const classroom = client.withWorkspace(workspaceId)

await classroom.workflows.run(workflowId) // runs in the workspace
await client.workflows.run(workflowId)    // runs in the personal space
```

O método retorna um novo cliente em vez de alterar o atual. Por isso, duas operações executadas ao mesmo tempo em um mesmo cliente nunca misturam os espaços de trabalho.

O espaço de trabalho define o **escopo**, nunca o **acesso**. Ele escolhe de qual espaço de trabalho uma lista lê e onde um novo item é criado. Ler, alterar, excluir ou executar um item que você indica pelo ID depende do espaço de trabalho do próprio item. Esquecer o espaço de trabalho não esconde o seu trabalho, e um espaço de trabalho errado não alcança o trabalho de outras pessoas.

Os espaços de trabalho pertencem às organizações no Nodaro Cloud. Veja [Organizações e espaços de trabalho](https://nodaro.ai/docs/developers/sdk/organizations) e [Espaços de trabalho](https://nodaro.ai/docs/concepts/workspaces).

## request(method, path, options)
```ts
request<T>(method: string, path: string, options?: {
body?: unknown
query?: Record<string, string | number | boolean | undefined>
headers?: Record<string, string>
signal?: AbortSignal
}): Promise<T>
```

Envia uma requisição a qualquer endpoint, para os poucos que ainda não têm método de recurso. Adiciona o seu cabeçalho de autenticação e o seu espaço de trabalho, envia `body` como JSON, aplica `timeoutMs` e lança os mesmos [erros tipados](https://nodaro.ai/docs/developers/sdk/errors) que os métodos de recurso.

```ts
// The same request that client.jobs.list() sends
const page = await client.request<{ data: unknown[]; next: string | null }>("GET", "/v1/jobs", {
query: { type: "llm-structured", limit: 20 },
})
```

Um corpo `FormData` é enviado como upload multipart. Os valores de query que são `undefined` ficam de fora. A [referência da API REST](https://nodaro.ai/docs/developers/api) lista todos os endpoints e os campos deles.

## Tempos limite e um fetch personalizado
`timeoutMs` aborta uma requisição que demora mais que o limite, de 60 segundos por padrão. A maioria das gerações demora mais que qualquer tempo limite HTTP razoável, então, em vez disso, inicie a geração e consulte o job periodicamente: [`client.nodes.runAndWait()`](https://nodaro.ai/docs/developers/sdk/nodes) faz as duas coisas por você. Os métodos de streaming, `client.copilot.stream()` e `client.media.downloadVideoProgress()`, não aplicam o tempo limite, porque foram feitos para ficar abertos por minutos.

Passe o seu próprio `fetch` para mudar o caminho das requisições:

- **Testes.** Retorne objetos `Response` prontos a partir de um mock.
- **Novas tentativas.** Envolva o `fetch` global em um helper que tente de novo diante de uma resposta 5xx.
- **Rastreamento.** Envolva-o com a sua biblioteca de rastreamento ou de monitoramento.

```ts
const client = createClient({
baseUrl: "https://app.nodaro.ai",
auth: new StaticTokenAuth(process.env.NODARO_TOKEN!),
fetch: (input, init) => tracedFetch(input, init),
})
```

## Runtimes e navegadores
O SDK usa apenas `fetch` e `URL`, que são globais no Node.js 20 ou mais recente, nos navegadores modernos, no React Native, no Cloudflare Workers, no Deno e no Bun. Não é preciso nenhum polyfill.

- **CORS com tokens OAuth.** Um app de navegador que chama o Nodaro com um token de acesso OAuth precisa rodar em uma origem listada em `allowedOrigins` do app de desenvolvedor. Veja [OAuth e apps de desenvolvedor](https://nodaro.ai/docs/developers/sdk/developer-apps).
- **CORS com uma sessão.** Um app de navegador que usa `supabaseAuth` não é verificado com essa lista.
- **O rótulo do cliente.** Em um servidor, o SDK envia `X-Nodaro-Client: sdk/<version>`, e o Nodaro o registra como a origem de cada job. No navegador, o rótulo padrão não é enviado, porque o cabeçalho `Origin` do navegador já identifica o seu app. Um `clientLabel` definido por você é sempre enviado.

## Frequently asked questions

### Qual URL base devo usar com o SDK do Nodaro?

Use https://app.nodaro.ai para o Nodaro Cloud e o endereço da sua instância para uma instalação self-hosted. Em um app de navegador servido pela mesma origem que o Nodaro, use uma string vazia.

### Qual é o tempo limite padrão das requisições?

60 segundos. Mude-o com a opção timeoutMs. Uma geração demora mais que qualquer requisição, então inicie a geração e consulte o job periodicamente em vez de aumentar o tempo limite.

### Como fazer requisições em um espaço de trabalho?

Chame client.withWorkspace(workspaceId). Ele retorna um novo cliente que envia todas as requisições nesse espaço de trabalho, e o cliente original continua trabalhando no seu espaço pessoal.

### Como chamar um endpoint que não tem método no SDK?

Use client.request(method, path, options). Ele envia o mesmo cabeçalho de autenticação, aplica o mesmo tempo limite e lança os mesmos erros tipados que todos os métodos de recurso.
