オブジェクトとクリーチャー
Nodaro の SDK を使って、TypeScript からオブジェクトとクリーチャーを作成し、メイン画像とバリエーションを生成して、アニメーション化し、クリーチャーに話をさせます。
client.objects は、小道具、商品、乗り物向けに、オブジェクト/小道具スタジオでできることをすべてスクリプトから行います。client.creatures は、動物やクリーチャー向けに同じことを行います。どちらも、アイテムの作成と編集、メイン画像候補の生成、その承認、バリエーションとモーションクリップの追加、そして以降のプロンプトでアイテムの一貫性を保つ説明の作成を行います。これらのメソッドは、オブジェクトとクリーチャーの REST API を呼び出します。エディターでの見方については、オブジェクトと小道具と動物とクリーチャーを参照してください。
メソッド
この 2 つのリソースは、同じメソッドを持ちます。
| メソッド | 内容 |
|---|---|
objects.list(params?)、creatures.list(params?) | アイテムを一覧表示します |
objects.listArchived(params?) | アーカイブしたアイテムを一覧表示します |
objects.get(id) | 1 つのアイテムを、進行中のジョブとともに読み取ります |
objects.create(input)、creatures.create(input) | アイテムを作成します |
objects.update(id, input)、creatures.update(id, input) | アイテムを変更します |
objects.delete(id) と restore(id) | アイテムをアーカイブするか、元に戻します |
objects.permanentDelete(id) | アーカイブしたアイテムと、そのファイルを完全に削除します |
objects.generate(input)、creatures.generate(input) | メイン画像の候補を生成します |
objects.generateAsset(input)、creatures.generateAsset(input) | バリエーションを生成します |
objects.generateMotion(input)、creatures.generateMotion(input) | メイン画像をアニメーション化してクリップにします |
objects.approveMainImage(id, candidateJobId, expectedUpdatedAt?) | 候補をメイン画像にします |
objects.recaption(id) | 説明を作成し直します |
client.objects
オブジェクトは、メイン画像を sourceImageUrl に持ち、angles、materials、variations、motionClips の 4 つのコレクション(いずれも { name, url } のリストです)、boards、referencePhotos、canonicalDescription、styleLock を持ちます。category は、furniture、vehicle、weapon、food、clothing、electronics、nature、tool、animal、other のいずれかです。
Object という名前は、JavaScript のグローバルと同じです。両方が必要な場合は、別名でインポートしてください。import type { Object as NodaroObject } from "@nodaro/sdk" のようにします。
objects.list(params?)
自分のオブジェクトを一覧表示します。デフォルトでは、有効なオブジェクトだけを返します。ページングは任意です。limit を指定しない場合は一覧全体を、limit(最大 500)を指定すると 1 ページ分と nextCursor を取得します。
list(params?: { archived?: boolean; projectId?: string; limit?: number; cursor?: string }): Promise<{
objects: Object[]
nextCursor?: string | null
}>Prop
Type
const { objects } = await client.objects.list()
const page = await client.objects.list({ limit: 100 })objects.listArchived(params?)
自分のアーカイブしたオブジェクトを一覧表示します。list({ archived: true }) のショートカットです。creatures.listArchived() も同じように動作します。
listArchived(params?: { projectId?: string; limit?: number; cursor?: string }): Promise<{ objects: Object[]; nextCursor?: string | null }>Prop
Type
const { objects: archived } = await client.objects.listArchived()objects.get(id)
1 つのオブジェクトを、pendingJobs(まだ生成中のバリエーション)とともに読み取ります。アーカイブしたオブジェクトは返されません。NotFoundError がスローされます。creatures.get() も同じように動作します。
get(id: string): Promise<ObjectDetail>Prop
Type
const object = await client.objects.get(objectId)
console.log(object.sourceImageUrl, object.materials)objects.create(input)
オブジェクトを作成します。name と nodeId は必須です。キャンバスのノードがないスクリプトでは、nodeId に "mcp-managed" を渡せます。
create(input: CreateObjectInput): Promise<{ id: string }>Prop
Type
const { id: objectId } = await client.objects.create({
nodeId: "mcp-managed",
name: "Antique Lantern",
description: "Weathered brass lantern with hand-engraved filigree",
category: "tool",
style: "realistic",
})objects.update(id, input)
オブジェクトを変更します。送信したフィールドだけが書き込まれます。バリエーションのコレクションは、この呼び出しの対象ではありません。作業中に、生成ジョブがコレクションへ追加していくためです。
update(id: string, input: UpdateObjectInput): Promise<{ id: string; updatedAt: string }>Prop
Type
await client.objects.update(objectId, {
canonicalDescription: "A weathered brass lantern with engraved filigree and a glass chimney",
expectedUpdatedAt: object.updatedAt,
})409 concurrent_modification は、通常の NodaroError として返されます。オブジェクトを読み取り直し、マージしてから、もう一度試してください。
objects.delete(id) と restore(id)
delete() はオブジェクトをアーカイブします。アーカイブ済みのオブジェクトに対して繰り返しても、何も変わりません。restore() は元に戻します。大文字と小文字を区別せずに、有効なオブジェクトの名前と一致する場合、サーバーは (restored) を付けて、使った名前を返します。
delete(id: string): Promise<{ success: true; archived: true }>
restore(id: string): Promise<{ id: string; name: string }>Prop
Type
await client.objects.delete(objectId)
const { name } = await client.objects.restore(objectId)objects.permanentDelete(id)
アーカイブしたオブジェクトと、それが参照する保存済みのすべてのファイルを削除します。アーカイブしたオブジェクトにしか使えません。有効なオブジェクトでは 400 not_archived で失敗します。先に delete() でアーカイブしてください。creatures.permanentDelete() も同じように動作します。
permanentDelete(id: string): Promise<{ success: true; permanent: true }>Prop
Type
await client.objects.delete(objectId)
await client.objects.permanentDelete(objectId)Nodaro の MCP ツールには、この操作は用意されていません。そのため、AI アシスタントがオブジェクトを削除することはできません。
objects.generate(input)
メイン画像の候補を生成します(POST /v1/generate-object)。count が 1 より大きい場合、どのジョブも開始する前に、すべてのジョブ分を確保します。そのため、途中で失敗すると、バッチ全体がロールバックされます。
generate(input: GenerateObjectInput): Promise<{ jobIds: string[]; jobId?: string }>Prop
Type
const { jobIds } = await client.objects.generate({ name: "Antique Lantern", count: 4 })
for (const jobId of jobIds) {
// poll each candidate with client.jobs.getStatus(jobId)
}jobIds は常に存在し、候補ごとに 1 つの ID を持ちます。jobId は、候補が 1 つの場合の古いエイリアスです。jobIds を使ってください。attachToObjectId を指定して候補が 1 つの場合、ジョブが完了すると、その結果がメイン画像になります。それ以外の場合は、approveMainImage() で 1 つを選んでください。
objects.generateAsset(input)
1 つのバリエーションを生成します(POST /v1/generate-object-asset)。attachToObjectId、attachToColumn、attachName を指定すると、ジョブが完了したときに、結果がそのコレクションに追加されます。
generateAsset(input: GenerateObjectAssetInput): Promise<{ jobId: string }>Prop
Type
const { jobId } = await client.objects.generateAsset({
name: "Antique Lantern",
assetType: "materials",
variant: "gold",
attachToObjectId: objectId,
attachToColumn: "materials",
attachName: "gold",
})angles、materials、variations、motion では、コレクションはアセットの種類から決まります。custom のバリエーションでは、attachToColumn が必要です。
objects.generateMotion(input)
オブジェクトの画像を、動画生成(Generate Video)の画像から動画へのモードで、クリップにアニメーション化します(POST /v1/generate-object-motion)。クリップは常に motionClips に入ります。デフォルトは商品撮影向けで、モデルは kling-turbo、フレームは 1:1 です。
generateMotion(input: GenerateObjectMotionInput): Promise<{ jobId: string }>Prop
Type
const { jobId } = await client.objects.generateMotion({
name: "Antique Lantern",
motionPrompt: "Slow 360-degree rotation, soft golden rim light",
sourceImageUrl: object.sourceImageUrl!,
attachToObjectId: objectId,
attachName: "rotate-360",
})objects.approveMainImage(id, candidateJobId, expectedUpdatedAt?)
generate() からの完了した候補を、オブジェクトのメイン画像にします。続けて、ビジョンモデルがオブジェクトの説明を作成し、このメソッドは両方を返します。
approveMainImage(id: string, candidateJobId: string, expectedUpdatedAt?: string): Promise<{
sourceImageUrl: string
canonicalDescription: string | null
}>Prop
Type
const { sourceImageUrl, canonicalDescription } = await client.objects.approveMainImage(objectId, jobIds[0])説明を作成できなかった場合、canonicalDescription は null になります。メイン画像は設定されたままなので、もう一度試すには recaption() を呼び出してください。
objects.recaption(id)
現在のメイン画像から、オブジェクトの説明を作成し直します。この呼び出しは繰り返しても安全で、同時実行制御用のトークンは必要ありません。
recaption(id: string): Promise<{ canonicalDescription: string }>Prop
Type
const { canonicalDescription } = await client.objects.recaption(objectId)オブジェクトにメイン画像がない場合は 400 main_image_required で、ビジョンモデルが失敗した場合は 502 で失敗します。
client.creatures
クリーチャーは、動物や空想上の生き物です。オブジェクトと同じように動作しますが、4 つの違いがあります。
speciesは、dragonやwolfなどの自由記述のタイプで、メイン画像のプロンプトの主題になります。categoryも自由記述です。posesがmaterialsの代わりになるため、コレクションはangles、poses、variations、motionClipsです。バリエーションの種類はangles、poses、variations、customです。boardsは、最大 24 枚の名前付きのクリーチャーボード(Creature Board)を保持します。クリーチャーボードは、画像生成(Generate Image)のクリーチャーボードプリセットで作る、情報量の多いリファレンスシートです。このリストは自分で管理するもので、create()とupdate()は、リスト全体を置き換えます。voiceを設定すると、そのクリーチャーは話せるようになります。キャラクターのボイスと同じ形で、{ voiceId, voiceName, traits, voiceType?, previewUrl?, ttsProvider? }です。取り除くにはvoice: nullを渡します。
creatures.listArchived()、get()、delete()、restore()、permanentDelete()、approveMainImage()、recaption() は、同じ引数を取り、上のオブジェクトのメソッドと同じように動作します。
creatures.list(params?)
list(params?: { archived?: boolean; projectId?: string; limit?: number; cursor?: string }): Promise<{
creatures: Creature[]
nextCursor?: string | null
}>Prop
Type
const { creatures } = await client.creatures.list()creatures.create(input)
create(input: CreateCreatureInput): Promise<{ id: string }>Prop
Type
const { id: creatureId } = await client.creatures.create({
nodeId: "mcp-managed",
name: "Biscuit",
species: "ginger cat",
style: "realistic",
})creatures.update(id, input)
nodeId を除く create() のフィールドに加え、boards、selectedAssetByVariant、expectedUpdatedAt を取ります。送信したフィールドだけが書き込まれます。
update(id: string, input: UpdateCreatureInput): Promise<{ id: string; updatedAt: string }>Prop
Type
await client.creatures.update(creatureId, {
voice: { voiceId: chosenVoiceId, voiceName: "Aria", traits: "smug, unhurried" },
})creatures.generate(input)
メイン画像の候補を生成します。objects.generate() のフィールドに加えて species を取り、{ jobIds } を返します。
generate(input: GenerateCreatureInput): Promise<{ jobIds: string[]; jobId?: string }>Prop
Type
const { jobIds } = await client.creatures.generate({ name: "Biscuit", species: "ginger cat", count: 4 })creatures.generateAsset(input)
1 つのバリエーションを生成します。attachToCreatureId がある点を除き、objects.generateAsset() と同じように動作します。
generateAsset(input: GenerateCreatureAssetInput): Promise<{ jobId: string }>Prop
Type
await client.creatures.generateAsset({
name: "Biscuit",
assetType: "poses",
variant: "sitting",
attachToCreatureId: creatureId,
attachToColumn: "poses",
attachName: "sitting",
})creatures.generateMotion(input)
クリーチャーの画像をクリップにアニメーション化します。同じデフォルト(kling-turbo と 1:1)で objects.generateMotion() と同じように動作し、クリップを motionClips に追加します。
generateMotion(input: GenerateCreatureMotionInput): Promise<{ jobId: string }>Prop
Type
await client.creatures.generateMotion({
name: "Biscuit",
motionPrompt: "The cat stretches, then yawns",
sourceImageUrl: creature.sourceImageUrl!,
attachToCreatureId: creatureId,
})クリーチャーに話をさせる
音声に、クリーチャー専用のメソッドは必要ありません。クリーチャーのボイスでセリフをレンダリングし、それをクリーチャーの画像にリップシンクします。
const creature = await client.creatures.get(creatureId)
// 1. Speak the line in the creature's voice
const speech = await client.nodes.runAndWait("text-to-speech", {
text: "I knocked the vase off the shelf. I regret nothing.",
voice: creature.voice!.voiceId,
provider: creature.voice!.ttsProvider,
voiceType: creature.voice!.voiceType,
})
// 2. Lip-sync the audio onto the creature's main image
const clip = await client.nodes.runAndWait("lip-sync", {
imageUrl: creature.sourceImageUrl!,
audioUrl: speech.audioUrl,
provider: "kling-avatar",
})
console.log(clip.videoUrl)リップシンク(Lip Sync)ノードは、既存の動画を吹き替えることもできます。videoUrl と、動画に対応したモデルを渡します。volcengine-lipsync は、吹き替えに使えるモデルの中でいちばん料金が安く、複数の話者を扱える唯一のモデルです。
const dub = await client.nodes.runAndWait("lip-sync", {
videoUrl: "https://example.com/scene.mp4",
audioUrl: "https://example.com/new-vocal.mp3",
provider: "volcengine-lipsync",
mode: "basic", // for complex scenes
openScenedet: true, // several speakers: scene and speaker detection
audioDurationSec: 42, // sets the per-second price; without it, you pay for 5 minutes
})クリーチャーをリファレンスとしてショットに組み込むには、@nodaro/shared の toConnectedReference({ kind: "creature", id, name, url, description }) でリファレンスを作ります。画像生成が、クリーチャーの体のつくり、模様、色を保つ一文を追加します。リファレンスを参照してください。
よくある質問
関連ページ
オブジェクトと小道具
動物とクリーチャー
オブジェクト
クリーチャー
キャラクター
最終更新