Nodaro 문서
문서노드 레퍼런스모델템플릿AI 에이전트(MCP)개발자셀프 호스팅리서치비교
TypeScript SDK

피커, 프리셋, 프롬프트

TypeScript로 피커 옵션을 읽고, 설명으로 피커를 채우고, 노드 프리셋, 팔레트, 무드보드를 관리하며, 프롬프트 마법사로 프롬프트를 개선합니다.

피커는 분위기나 렌즈 같은 크리에이티브 컨트롤 노드로, 선택한 내용이 검증된 문구를 프롬프트에 추가합니다. client.pickerCatalogs와 client.catalogs는 각 피커의 유효한 옵션을 반환하고, analyzeText()는 장면 설명으로 피커를 채웁니다. client.presets는 저장된 노드 설정을 관리하고, client.palettes와 client.moodboards는 내 색상과 스타일 레퍼런스를 보관하며, client.promptHelper는 프롬프트 마법사로, 모든 생성 노드의 프롬프트를 개선합니다. 개념은 크리에이티브 컨트롤과 피커 카탈로그를 참조하세요.

메서드

메서드하는 일
pickerCatalogs.list()모든 피커와 그 카탈로그를 나열합니다
pickerCatalogs.get(nodeType, opts?)피커 하나의 옵션을 읽습니다
pickerCatalogs.analyzeText(params)텍스트 설명으로 피커 선택을 채웁니다
catalogs.list(opts?)이 배포의 모든 카탈로그를 한 번의 호출로 읽습니다
presets.list(nodeType?)내가 저장한 프리셋을 나열합니다
presets.listGroups(nodeType?)내 프리셋 폴더와 섹션을 나열합니다
presets.listFactory(nodeType)로그인 없이 노드 유형의 기본 제공 프리셋을 나열합니다
presets.create(input) 외내 프리셋을 만들고, 수정하고, 삭제하고, 가져오고, 순서를 바꿉니다
presets.favorites(nodeType) 외프리셋 즐겨찾기와 폴더를 관리합니다
palettes.create(input) 외이미지 생성에 쓸 컬러 팔레트를 저장합니다
moodboards.create(input) 외이미지 생성에 쓸 무드보드를 저장합니다
promptHelper.analyze(input)대략적인 아이디어를 질문으로 바꿉니다
promptHelper.generate(input)답변으로 프롬프트를 만듭니다
promptHelper.enhance(input)프롬프트를 한 단계로 개선합니다

client.pickerCatalogs

피커 노드의 옵션 목록입니다. 두 읽기 메서드는 모두 공개이며, 토큰이 필요 없고, 서버가 5분 동안 캐시할 수 있습니다. 코드에서 @nodaro/prompts를 가져올 수 있다면 같은 카탈로그가 타입이 지정된 데이터로 그 패키지에 들어 있습니다. 이 메서드들은 그 패키지를 번들로 포함할 수 없는 클라이언트를 위한 것입니다.

pickerCatalogs.list()

모든 피커를 나열합니다(GET /v1/picker-catalogs).

list(): Promise<{ data: PickerCatalogSummary[] }>
const { data: pickers } = await client.pickerCatalogs.list()
const mood = pickers.find((p) => p.nodeType === "mood")

각 항목에는 nodeType, label, catalogId, kind(single 또는 multi), 단일 차원 피커의 valueField 또는 다중 차원 피커의 fields, optionCount, 그리고 사진이 있는 옵션의 수인 imageCount가 있습니다.

pickerCatalogs.get(nodeType, opts?)

피커 하나의 옵션을 읽습니다(GET /v1/picker-catalogs/:nodeType). 분위기 같은 단일 차원 피커에는 options가 있습니다. 인물 같은 다중 차원 피커에는 dimensions가 있으며, 각각 { field, label, options }입니다. 일부 단일 차원 피커에는 주 선택 옆에 추가 설정도 있습니다. transition과 character-fx에는 position, duration, intensity가 있고, character-motion에는 position과 pace가 있습니다.

get(nodeType: string, opts?: { detail?: "compact" | "full"; category?: string; field?: string }): Promise<{ data: PickerCatalog }>

속성

타입

const { data } = await client.pickerCatalogs.get("mood", { detail: "full" })
const serene = data.options?.find((o) => o.id === "serene")
console.log(serene?.term)       // the short phrase, added in Compact mode
console.log(serene?.promptHint) // the full sentence, added in Full mode

알 수 없는 노드 유형이면 NotFoundError를 발생시킵니다.

label은 표시하고, term은 추가하세요. 모든 옵션에는 두 detail 수준 모두에서 term이 있습니다. 프롬프트에 넣을 짧은 전문 용어입니다. label은 표시 전용입니다. 한쪽에서 다른 쪽을 만들어 내지 마세요. auto나 none처럼 아무것도 추가하지 않는 옵션은 term이 비어 있습니다.

사진. 사진이 있는 옵션에는 두 detail 수준 모두에서 절대 URL인 imageUrl이 있습니다. person과 styling은 sections도 반환합니다. 에디터가 설정을 묶어 보여 주는 주제이며, 각각 { label, fields, imageUrl? }입니다. 사진과 음악, 보이스 아트는 설치 자체에서 제공합니다. 렌더링된 룩 미리 보기는 Nodaro CDN에서 제공되며, Nodaro Cloud만 반환합니다. 파일 이름에 콘텐츠 해시가 들어 있으므로 사진을 만료 없이 캐시할 수 있습니다.

const { data: person } = await client.pickerCatalogs.get("person")
for (const section of person.sections ?? []) {
  const settings = person.dimensions?.filter((d) => section.fields.includes(d.field)) ?? []
  for (const setting of settings) {
    for (const option of setting.options) renderTile(option.label, option.imageUrl)
  }
}

캐릭터 모션. character-motion의 옵션에는 두 detail 수준 모두에서 선택적인 motion 객체가 들어 있습니다. 이 객체는 동작에 필요한 것과 동작 후에 남는 것을 알려 줍니다. requires, startPose와 endPose, endVisibility, handsAfter, needsFreeHands, kind, fixedPace, counterpart, 검색용 aliases, 그리고 replacementId가 딸린 deprecated입니다. 필드가 없으면 알 수 없다는 뜻입니다. 저장된 워크플로를 불러올 때는 폐기된 ID를 유지하고, 새로 선택할 때는 보여 주지 마세요. 캐릭터 모션을 참조하세요.

pickerCatalogs.analyzeText(params)

자유 형식의 장면 설명으로 피커 선택을 채웁니다(POST /v1/text-to-picker). 피커로 설명의 텍스트 버전입니다. 피커 유형, 차원, 선택된 ID 하나 또는 여러 개 순서로 키가 지정된 pickerJson을 반환합니다. 이 값을 그대로 피커에 불러온 다음, 사용자가 조정하게 하세요. 크레딧이 들며, 피커로 설명과 같은 요금이 청구됩니다. 기본 모델인 Claude Opus 5.5를 high로 쓰면 실행 한 번에 28크레딧이 듭니다.

analyzeText(params: TextToPickerParams): Promise<{
  jobId: string
  pickerJson: Record<string, Record<string, string | string[]>>
  gaps?: { missingItems: object[]; missingCategories: object[] }
}>

속성

타입

const { pickerJson, gaps } = await client.pickerCatalogs.analyzeText({
  text: "Neon-soaked Tokyo alley at night, rain, handheld tracking shot, moody synthwave",
})
console.log(pickerJson["setting"], pickerJson["camera-motion"])

텍스트가 아무것도 말하지 않는 차원은 빠집니다. 분석은 추측하지 않습니다. gaps는 텍스트가 설명하지만 어떤 카탈로그 옵션도 잘 나타내지 못하는 것을 missingItems와 missingCategories에 나열합니다. 사용자에게는 “X를 찾지 못했습니다. 직접 선택하세요”처럼 보여 주세요.

client.catalogs

catalogs.list(opts?)

이 배포가 제공하는 그대로, 모든 피커 카탈로그를 한 번의 호출로 반환합니다(GET /v1/catalogs). 배포는 옵션을 교체하거나, 확장하거나, 숨기는 카탈로그 팩을 등록할 수 있습니다. 피커를 직접 그리는 클라이언트는 이를 반영하도록 이 목록을 읽어야 합니다. 공개이며, 5분 동안 캐시할 수 있습니다.

list(opts?: { detail?: "compact" | "full" }): Promise<{
  curated: boolean
  packs: number
  version: number
  data?: ProjectedCatalog[]
}>

속성

타입

const { curated, data } = await client.catalogs.list({ detail: "full" })
if (curated) {
  const setting = data?.find((c) => c.catalogId === "setting")
  console.log(setting?.options?.[0]?.term)
}

data는 배포가 카탈로그 팩을 등록한 경우(curated: true)에만 있습니다. 팩이 없으면 카탈로그는 표준 카탈로그입니다. pickerCatalogs.get()으로 하나씩 읽으세요. 옵션에는 같은 imageUrl이 있고, person과 styling에는 같은 sections가 있습니다.

client.presets

내가 저장한 노드 프리셋, 내 즐겨찾기, 기본 제공 카탈로그입니다. 프리셋의 data는 노드의 저장된 설정입니다. 프리셋을 적용하려면 워크플로를 만들 때 그 data를 노드의 데이터에 병합하거나, 이미지 생성 실행에 presetId를 전달하세요. 프리셋을 참조하세요.

listFactory()의 기본 제공 카탈로그는 공개이며 토큰이 필요 없습니다. 내 프리셋의 경우 OAuth 토큰에는 읽기에 presets:read, 쓰기에 assets:write 스코프가 필요합니다. 내 API 토큰에는 둘 다 필요 없지만, 일부 워크플로로 제한된 토큰은 내 프리셋에 접근할 수 없습니다.

내 프리셋과 기본 제공 프리셋 모두에 필드가 두 개 더 있습니다. 이 필드가 생기기 전의 서버는 이를 생략합니다.

  • minReferences는 프리셋이 쓰는 연결된 레퍼런스 이미지의 수이며, 이 값이 0보다 크면 needsImage는 true입니다. 레퍼런스 시트, 인물 사진 변신, 편집, 그리고 {image:N}을 쓰는 모든 템플릿은 하나 이상이 필요합니다. 이런 프리셋은 그 수만큼 레퍼런스 이미지와 함께 실행하세요.
  • previewUrl은 예시 이미지이거나 null입니다. 내 프리셋에서는 프리셋과 함께 저장한 이미지입니다.

presets.list(nodeType?)

내가 저장한 프리셋을 최신 항목부터 나열합니다(GET /v1/node-presets).

list(nodeType?: string): Promise<NodePreset[]>

속성

타입

const presets = await client.presets.list("generate-image")
const cinematic = presets.find((p) => p.name === "Cinematic Portrait")
// apply: spread cinematic.data into the node's data when you create the workflow

NodePreset에는 id, nodeType, name, description, data, groupId, tags, sortOrder, minReferences, needsImage, previewUrl, createdAt, updatedAt이 있습니다.

presets.listGroups(nodeType?)

내 프리셋 폴더와 섹션을 나열합니다(GET /v1/node-preset-groups).

listGroups(nodeType?: string): Promise<NodePresetGroup[]>

속성

타입

const groups = await client.presets.listGroups("generate-image")

각 그룹에는 id, nodeType, name, kind(folder 또는 section), sortOrder, createdAt, updatedAt이 있습니다.

presets.listFactory(nodeType)

노드 유형의 기본 제공 프리셋을 나열합니다(GET /v1/node-presets/factory).

listFactory(nodeType: string): Promise<{ data: FactoryPreset[] }>

속성

타입

로그인은 선택 사항입니다. 카탈로그는 공개이므로 자격 증명이 없는 클라이언트도 읽을 수 있습니다. 방문자에게 이름, 예시 이미지, 사진 수를 보여 주고, 생성할 때만 로그인을 요청하세요.

// Signed out: a client with no credential can still read the catalog
const visitor = createClient({ baseUrl, auth: { getToken: async () => null } })
const { data } = await visitor.presets.listFactory("generate-image")
const withPicture = data.filter((p) => p.previewUrl)

// A preset that works from photos runs with at least minReferences of them.
const board = data.find((p) => p.id === "generate-image/character-board")!
if (board.needsImage && photos.length >= (board.minReferences ?? 0)) {
  await client.nodes.runAndWait("generate-image", { presetId: board.id, referenceImageUrls: photos })
}

presets.create(input)

프리셋을 저장합니다(POST /v1/node-presets).

create(input: CreateNodePresetInput): Promise<NodePreset>

속성

타입

const preset = await client.presets.create({
  nodeType: "generate-image",
  name: "Studio portrait",
  data: { provider: "gpt-image-2", aspectRatio: "3:4", prompt: "{image:1:person}, studio portrait" },
  previewUrl: imageUrl, // the image of one of your completed jobs, or an upload
})
preset.needsImage // true, because the prompt names {image:1}

프리셋은 이미지의 자체 사본을 보관하므로, 반환된 previewUrl은 사본의 주소입니다. 계정 하나에는 프리셋이 최대 5,000개입니다. 이를 넘으면 호출은 409 limit_reached로 실패합니다.

presets.update(id, patch, opts?)와 delete(id, opts?)

update(id: string, patch: UpdateNodePresetInput, opts?: { expectedUpdatedAt?: string }): Promise<NodePreset>
delete(id: string, opts?: { expectedUpdatedAt?: string }): Promise<{ success: true }>

update()는 name, description, data, groupId, tags, sortOrder, previewUrl(null이면 이미지 제거) 중 원하는 것을 바꿉니다. delete()는 프리셋을 제거하며, 내 프리셋이 아니면 404로 실패합니다. 읽은 updatedAt을 expectedUpdatedAt으로 전달하면, 그 뒤로 누군가 프리셋을 바꾼 경우 호출이 409 conflict로 실패합니다.

presets.import(presets)와 reorder(input)

import(presets: ImportNodePresetInput[]): Promise<{ imported: number }>
reorder(input: ReorderNodePresetsInput): Promise<{ ok: true }>

import()는 프리셋을 한 번에 최대 500개 저장하며, 각각 { nodeType, name, description?, data, previewUrl? }입니다. 이미 쓰는 이름은 “… (imported 2)”로 저장됩니다. 런타임 키를 제거한 뒤 data가 비는 프리셋은 건너뜁니다. nodaro presets export가 쓰는 파일의 항목은 그대로 맞습니다.

reorder()는 { groups?: [{ id, sortOrder }], presets?: [{ id, groupId?, sortOrder }] }를 받습니다. 위치를 정하고 프리셋을 폴더로 옮기며, groupId: null이면 폴더 밖으로 옮깁니다.

프리셋 즐겨찾기와 폴더

favorites(nodeType: string): Promise<string[]>
addFavorite(input: { nodeType: string; presetId: string }): Promise<{ success: true }>
removeFavorite(input: { nodeType: string; presetId: string }): Promise<{ success: true }>

createGroup(input: { nodeType: string; name: string; kind?: "folder" | "section"; sortOrder?: number }): Promise<NodePresetGroup>
updateGroup(id: string, patch: { name?: string; sortOrder?: number }): Promise<NodePresetGroup>
deleteGroup(id: string): Promise<{ success: true }>

즐겨찾기는 "generate-image/character-board" 같은 기본 제공 ID나 내 프리셋 ID입니다. 즐겨찾기 추가와 제거는 반복해도 안전합니다. 폴더를 삭제하면 그 안의 프리셋은 폴더 밖으로 옮겨지며, 하나도 삭제되지 않습니다.

client.palettes

팔레트는 이름 아래에 #RRGGBB 형식의 색상 2~12개를 묶은 것입니다. generate-image에서 팔레트는 연출 값입니다. direction.colorLook을 paletteDirectionId(id), 즉 "palette:<id>"로 설정하세요. 그러면 모든 색상이 일상적인 이름과 16진수 코드로 프롬프트에 들어갑니다. 팔레트는 내 것만 쓸 수 있으며, 다른 사람의 ID는 404로 응답합니다.

import { paletteDirectionId } from "@nodaro/sdk"

const palette = await client.palettes.create({ name: "Harbour", colors: ["#0F4C5C", "#E3B23C", "#F28C7A"] })
await client.nodes.runAndWait("generate-image", {
  prompt: "a lighthouse at dusk",
  direction: { colorLook: paletteDirectionId(palette.id) },
})
메서드반환참고
list(params?: ListPalettesParams)Promise<PalettePage>최신순입니다. limit(1~100, 기본값 50)과 cursor를 받습니다. 페이지에 nextCursor가 담깁니다.
get(id: string)Promise<ColorPalette>팔레트 하나입니다.
create(input: CreatePaletteInput)Promise<ColorPalette>name(180자), colors(대소문자 상관없는 #RRGGBB 값 212개로, 대문자로 저장됨), description(최대 500자의 내 메모로, 프롬프트에는 절대 들어가지 않음)입니다.
update(id: string, input: UpdatePaletteInput)Promise<ColorPalette>전달한 필드만 씁니다. description: null이면 지웁니다.
delete(id: string)Promise<void>이후 이 팔레트를 지정하는 실행은 404 direction_not_found로 실패합니다.

client.moodboards

무드보드는 설명과 이미지 1~12장입니다. generate-image에서 무드보드는 연출 값입니다. direction.style을 moodboardDirectionId(id), 즉 "moodboard:<id>"로 설정하세요. 설명은 프롬프트에 들어가고, 이미지는 스타일 레퍼런스로 모델에 전달됩니다. 이미지는 내 레퍼런스 뒤에, 모델의 레퍼런스 한도까지 들어갑니다. 이미지는 사본이 아니라 링크이며, localhost나 사설 주소가 아닌 http(s) 주소여야 합니다.

import { moodboardDirectionId } from "@nodaro/sdk"

const board = await client.moodboards.create({
  name: "Nordic calm",
  description: "Moody Nordic interiors, soft daylight, linen and oak",
  images: [{ url: "https://example.com/a.png", caption: "soft daylight" }, { url: "https://example.com/b.png" }],
})
const out = await client.nodes.run("generate-image", {
  prompt: "a reading corner",
  direction: { style: moodboardDirectionId(board.id) },
})
// out.warnings: [{ code: "moodboard_images_not_sent", message }] when some images did not fit
메서드반환참고
list(params?: ListMoodboardsParams)Promise<MoodboardPage>최신순입니다. limit(1~100, 기본값 50)과 cursor를 받습니다. 페이지에 nextCursor가 담깁니다.
get(id: string)Promise<Moodboard>무드보드 하나입니다.
create(input: CreateMoodboardInput)Promise<Moodboard>name(180자), description(최대 1,000자로, 프롬프트에 들어가는 문구), images({ url, caption? } 112개로, 각 캡션은 최대 200자)입니다.
update(id: string, input: UpdateMoodboardInput)Promise<Moodboard>전달한 필드만 씁니다. images는 모든 이미지를 바꿉니다. description: null이면 지웁니다.
delete(id: string)Promise<void>무드보드만 삭제합니다. 연결된 이미지는 그대로입니다.

둘 다에 적용되는 규칙

  • PALETTE_DIRECTION_PREFIX("palette:"), MOODBOARD_DIRECTION_PREFIX("moodboard:"), paletteDirectionId(id), moodboardDirectionId(id)가 연출 값을 만듭니다.
  • GenerateImageParams.direction의 타입은 DirectionFields이며, 실행 응답의 타입에는 warnings?: RunNodeWarning[]이 있습니다.
  • 형식이 잘못된 값은 400으로 응답합니다. 내 것이 아닌 ID는 404 direction_not_found로 응답합니다. 둘 다 무언가를 만들거나 청구하기 전에 실패합니다.
  • 팔레트나 무드보드는 presetId와 함께 쓸 수 없습니다. 그런 실행은 400 validation_error로 응답하며, 연출에 팔레트나 무드보드를 담아 저장한 프리셋도 마찬가지입니다.
  • OAuth 앱 토큰에는 읽기에 assets:read, 쓰기에 assets:write 스코프가 필요합니다.

client.promptHelper

프롬프트 마법사입니다. 생성 노드의 프롬프트 작성을 AI가 돕습니다. 세 메서드는 모두 POST /v1/prompt-helper/wizard로 요청을 보내며, 호출할 때마다 크레딧이 듭니다. REST 관점은 프롬프트 마법사를 참조하세요.

세 메서드는 모두 다음 공통 필드를 받습니다.

속성

타입

CLI에서도 nodaro prompt wizard, analyze, generate, enhance와 --llm-model, --reasoning-effort로 같은 기능을 사용할 수 있습니다. CLI를 참조하세요.

promptHelper.analyze(input)

대략적인 아이디어를 노드 유형에 맞는 안내 질문으로 바꿉니다. 질문에 답한 다음, 그 답변을 generate()에 전달하세요.

analyze(input: AnalyzeInput): Promise<{ jobId: string; questions: WizardQuestion[] }>

속성

타입

const { questions } = await client.promptHelper.analyze({
  nodeType: "generate-image",
  prompt: "a snow leopard",
})

promptHelper.generate(input)

선택한 답변으로 최적화된 프롬프트 하나를 만듭니다. 각 선택은 { category, value, isCustom }입니다.

generate(input: GenerateInput): Promise<{ jobId: string; prompt: string; recommendedModel?: RecommendedModel }>

속성

타입

const { prompt, recommendedModel } = await client.promptHelper.generate({
  nodeType: "generate-image",
  selections: [{ category: "subject", value: "snow leopard", isCustom: false }],
})

promptHelper.enhance(input)

질문 없이 프롬프트를 한 단계로 개선합니다.

enhance(input: EnhanceInput): Promise<{ jobId: string; prompt: string; recommendedModel?: RecommendedModel }>

속성

타입

const { prompt } = await client.promptHelper.enhance({
  nodeType: "generate-image",
  prompt: "snow leopard on a rock",
  reasoningEffort: "high",
})

자주 묻는 질문

direction 객체에 쓸 수 있는 유효한 ID는 어떻게 찾나요?

모든 피커는 client.pickerCatalogs.list()로, 피커 하나의 옵션은 client.pickerCatalogs.get(nodeType)으로 확인하세요. 각 옵션의 id가 direction과 피커 노드가 받는 값입니다.

label, term, promptHint는 어떻게 다른가요?

label은 표시 이름입니다. term은 간략 모드에서 피커가 추가하는 짧은 전문 용어입니다. promptHint는 전체 모드에서 추가하는 더 긴 문장이며, detail이 full일 때만 반환됩니다.

SDK로 프롬프트를 개선하려면 어떻게 하나요?

대상 nodeType과 내 프롬프트로 client.promptHelper.enhance를 호출하세요. 개선된 프롬프트가 반환되며, 추천 모델이 함께 반환되기도 합니다. 호출할 때마다 크레딧이 듭니다.

SDK로 노드 프리셋을 만들거나 변경할 수 있나요?

네. client.presets는 내 프리셋을 만들고, 수정하고, 삭제하고, 가져오고, 순서를 바꾸며, 즐겨찾기와 폴더를 관리합니다. listFactory의 기본 제공 카탈로그는 로그인이 필요 없습니다.

이미지 생성 실행에 내 색상을 쓰려면 어떻게 하나요?

client.palettes.create로 팔레트를 저장한 다음, generate-image에 direction.colorLook을 paletteDirectionId(palette.id)로 설정해 전달하세요. 모든 색상이 일상적인 이름과 16진수 코드로 프롬프트에 들어갑니다.

최종 업데이트

목차