# アプリとテンプレート

> TypeScript から、公開された Nodaro のアプリを閲覧して実行し、実行履歴を読み取り、ワークフローのテンプレートをプロジェクトに複製し、チュートリアルを一覧表示します。

Source: https://nodaro.ai/ja/docs/developers/sdk/apps-and-templates

**`client.apps`** は、公開されたアプリ（入力と出力のシンプルなフォームで包んだワークフロー）を閲覧し、実行します。**`client.templates`** はテンプレートマーケットプレイスを閲覧し、テンプレートをあなたのプロジェクトの 1 つに複製します。**`client.tutorials`** は、チュートリアル動画とチュートリアルワークフローを一覧表示します。概念については、[アプリ](https://nodaro.ai/docs/concepts/apps)と[チュートリアルとテンプレート](https://nodaro.ai/docs/get-started/tutorials-and-templates)を参照してください。

## メソッド
| メソッド | 内容 |
| --- | --- |
| [`apps.list(params?)`](#appslistparams) | 公開されたアプリを閲覧します |
| [`apps.get(slug)`](#appsgetslug) | アプリを、その入力と出力とともに読み取ります |
| [`apps.run(slug, inputs?, opts?)`](#appsrunslug-inputs-opts) | アプリを実行します |
| [`apps.listRuns(slug, params?)`](#appslistrunsslug-params) | アプリの自分の実行を一覧表示します |
| [`apps.getRun(slug, runId)`](#appsgetrunslug-runid) | 1 件の実行を読み取ります |
| [`apps.deleteRun(slug, runId)`](#appsdeleterunslug-runid) | 実行をアーカイブします |
| [`templates.browse(params?)`](#templatesbrowseparams) | テンプレートマーケットプレイスを閲覧します |
| [`templates.get(slug)`](#templatesgetslug) | テンプレートを、そのワークフローとともに読み取ります |
| [`templates.clone(slug, params)`](#templatescloneslug-params) | テンプレートをプロジェクトに複製します |
| [`tutorials.list()`](#tutorialslist) | チュートリアルをカテゴリー別に一覧表示します |

## client.apps
`list()` と `get()` は公開されています。`run()` と実行履歴は、ログイン中の呼び出し元として動作します。

### apps.list(params?)
公開されたアプリを、ページ単位で閲覧します。

```ts
list(params?: { search?: string; category?: string; limit?: number; cursor?: string }): Promise<{
data: PublishedApp[]
nextCursor?: string | null
}>
```

<TypeTable
type={{
search: { type: 'string', description: "検索する語句です。" },
category: { type: 'string', description: "指定したカテゴリーだけに絞ります。" },
limit: { type: 'number', description: "ページのサイズで、最大 50 です。" },
cursor: { type: 'string', description: "前のページの nextCursor です。" },
}}
/>

```ts
const { data: apps, nextCursor } = await client.apps.list({ search: "headshot", limit: 20 })
```

`PublishedApp` は、`id`、`slug`、`name`、`description`、`creatorId`、`creatorName`、`thumbnailUrl`、`category`、`isFeatured`、`runCount`、`createdAt`、`updatedAt` を持ちます。

### apps.get(slug)
1 つのアプリを、エンドユーザーが入力するフィールドである `inputSchema` と、返す内容である `outputs` とともに読み取ります。

```ts
get(slug: string): Promise<{ data: PublishedAppDetail }>
```

<TypeTable
type={{
slug: { type: 'string', required: true, description: "アプリのスラッグです。" },
}}
/>

```ts
const { data: app } = await client.apps.get("pro-headshot")
console.log(app.inputSchema, app.outputs) // outputs: [{ nodeId, label, type }]
```

### apps.run(slug, inputs?, opts?)
アプリを実行し、`{ executionId, status, runId? }` ですぐに返ります。結果は [`client.executions.get(executionId)`](https://nodaro.ai/docs/developers/sdk/jobs-and-executions) をポーリングして取得します。

```ts
run(slug: string, inputs?: Record<string, unknown>, opts?: {
inputOverrides?: Record<string, Record<string, unknown>>
}): Promise<{ executionId: string; status: "pending" | "running"; runId?: string }>
```

<TypeTable
type={{
slug: { type: 'string', required: true, description: "アプリのスラッグです。" },
inputs: { type: 'Record<string, unknown>', description: "アプリの入力値です。キーは inputSchema のフィールド名と一致します。" },
inputOverrides: { type: 'Record<string, Record<string, unknown>>', description: "この実行だけに適用する、ノード ID ごとの生のノード設定です。たとえば { n1: { promptPrefix: '...' } } のようになります。アプリがユーザーに表示していないフィールドも設定できます。" },
}}
/>

```ts
const { executionId } = await client.apps.run("pro-headshot", { photo: photoUrl })

// Add hidden text before the app's prompt, for this run only
await client.apps.run(
"pro-headshot",
{ photo: photoUrl },
{ inputOverrides: { n1: { promptPrefix: "Studio portrait of" } } },
)
```

`inputOverrides` は高度なオプションです。たとえば、ノードの[プロンプトの前後のテキスト](https://nodaro.ai/docs/concepts/prompt-pre-post-text)（`promptPrefix` と `promptSuffix`）を設定できます。**Webhook 出力**（Webhook Output）の URL や、公開先のアカウント、スクレイパーの対象など、出力ノードの送信先は設定できません。そのようなオーバーライドは、`400 locked_field` で拒否されます。

### apps.listRuns(slug, params?)
アプリの自分の実行を、ページ単位で一覧表示します。

```ts
listRuns(slug: string, params?: { limit?: number; cursor?: string }): Promise<{ data: AppRun[]; nextCursor?: string | null }>
```

<TypeTable
type={{
slug: { type: 'string', required: true, description: "アプリのスラッグです。" },
limit: { type: 'number', description: "ページのサイズです。" },
cursor: { type: 'string', description: "前のページの nextCursor です。" },
}}
/>

```ts
const { data: runs } = await client.apps.listRuns("pro-headshot", { limit: 10 })
for (const run of runs) console.log(run.status, run.outputs)
```

`AppRun` は、`id`、`appSlug`、`executionId`、`status`（`pending`、`running`、`completed`、`failed`、`cancelled` のいずれか）、`inputs`、`outputs`（それぞれ `{ nodeId, type, url?, text? }`）、`startedAt`、`finishedAt` を持ちます。

### apps.getRun(slug, runId)
アプリの 1 件の実行を読み取ります。

```ts
getRun(slug: string, runId: string): Promise<{ data: AppRun }>
```

<TypeTable
type={{
slug: { type: 'string', required: true, description: "アプリのスラッグです。" },
runId: { type: 'string', required: true, description: "実行の ID です。" },
}}
/>

```ts
const { data: run } = await client.apps.getRun("pro-headshot", runId)
```

### apps.deleteRun(slug, runId)
実行をアーカイブします。実行の復元と完全な削除は、Nodaro アプリでしかできないため、スクリプトがデータを破壊することはありません。

```ts
deleteRun(slug: string, runId: string): Promise<{ success: true; archived: true }>
```

<TypeTable
type={{
slug: { type: 'string', required: true, description: "アプリのスラッグです。" },
runId: { type: 'string', required: true, description: "実行の ID です。" },
}}
/>

```ts
await client.apps.deleteRun("pro-headshot", runId)
```

## client.templates
テンプレートマーケットプレイスは、設計上、公開されています。閲覧したり、ワークフロー全体を含むテンプレートを読み取ったり、無料で自分のプロジェクトに複製したりできます。

### templates.browse(params?)
マーケットプレイスを、ページ単位で閲覧します（`GET /v1/templates/browse`）。ログインは不要です。

```ts
browse(params?: BrowseTemplatesParams): Promise<{ data: TemplateBrowseCard[]; nextCursor: string | null }>
```

<TypeTable
type={{
search: { type: 'string', description: "全文検索です。" },
category: { type: 'string', description: "指定したカテゴリーだけに絞ります。" },
outputType: { type: 'string', description: "この種類の出力を生成するテンプレートだけに絞ります。" },
tag: { type: 'string', description: "このタグを持つテンプレートだけに絞ります。" },
nodeType: { type: 'string', description: "このノードタイプを使うテンプレートだけに絞ります。" },
provider: { type: 'string', description: "このモデルを使うテンプレートだけに絞ります。" },
complexity: { type: 'string', description: "この複雑さのテンプレートだけに絞ります。" },
sort: { type: '"newest" | "popular" | "most-favorited"', default: '"newest"', description: "並び順です。" },
limit: { type: 'number', description: "ページのサイズです。" },
cursor: { type: 'string', description: "前のページの nextCursor です。" },
}}
/>

```ts
const page = await client.templates.browse({ sort: "popular", search: "trailer" })
```

### templates.get(slug)
1 つの公開テンプレートを、そのワークフローのスナップショット全体（`snapshotNodes`、`snapshotEdges`、`snapshotSettings`）とともに読み取ります（`GET /v1/templates/:slug`）。

```ts
get(slug: string): Promise<Template>
```

<TypeTable
type={{
slug: { type: 'string', required: true, description: "テンプレートのスラッグです。" },
}}
/>

```ts
const template = await client.templates.get("noir-trailer")
console.log(template.snapshotNodes.length)
```

スラッグが不明、非掲載、無効のいずれかの場合、`NotFoundError` をスローします。

### templates.clone(slug, params)
テンプレートを、新しいワークフローとしてあなたのプロジェクトの 1 つに複製します（`POST /v1/templates/:slug/clone`）。クレジットはかかりません。

```ts
clone(slug: string, params: { projectId: string; name?: string }): Promise<{ workflowId: string; projectId: string }>
```

<TypeTable
type={{
slug: { type: 'string', required: true, description: "テンプレートのスラッグです。" },
projectId: { type: 'string', required: true, description: "複製先のプロジェクトです。" },
name: { type: 'string', description: "新しいワークフローの名前です。デフォルトはテンプレートの名前です。" },
}}
/>

```ts
const { workflowId } = await client.templates.clone("noir-trailer", { projectId })
await client.workflows.run(workflowId)
```

## client.tutorials
### tutorials.list()
すべてのチュートリアルのカテゴリーを、そのチュートリアル動画とチュートリアルワークフローとともに一覧表示します（`GET /v1/tutorials`）。公開されていて、読み取り専用です。

```ts
list(): Promise<{ categories: TutorialCategory[] }>
```

```ts
const { categories } = await client.tutorials.list()
for (const category of categories) {
console.log(category.name, category.videos.length, category.flows.length)
}
```

各カテゴリーは、`id`、`name`、`slug`、`sortOrder`、`videos`、`flows` を持ちます。動画は `title`、`videoUrl`、`thumbnailUrl` を持ちます。フローは、チュートリアルとしてマークされたテンプレートです。`title`、`complexity`、`estimatedCredits`、使用するノードタイプとモデル、そして `templates.get()` と `templates.clone()` に渡せる `slug` を持ちます。

## Frequently asked questions

### コードから Nodaro のアプリを実行するには、どうすればよいですか？

アプリのスラッグと、アプリの入力フィールドに対応するキーを持つ inputs オブジェクトを指定して、client.apps.run を呼び出します。executionId が返るので、結果は client.executions.get(executionId) をポーリングして取得します。

### アプリが必要とする入力は、どうすれば分かりますか？

client.apps.get(slug) を呼び出します。その inputSchema には、エンドユーザーが入力するフィールドが一覧表示され、outputs には、アプリが返す内容が一覧表示されます。

### テンプレートを複製すると、クレジットがかかりますか？

かかりません。client.templates.clone は、テンプレートを無料であなたのプロジェクトの 1 つに複製します。料金がかかるのは、新しいワークフローを実行したときだけです。

### inputOverrides で、アプリが結果を送る先を変更できますか？

できません。オーバーライドでは、Webhook 出力（Webhook Output）の URL や公開先のアカウントなど、送信先を設定できません。実行は 400 locked_field で拒否されます。
