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

워크플로와 프로젝트

TypeScript에서 client.workflows로 Nodaro 워크플로를 만들고, 수정하고, 공유하고, 내보내고, 가져오고, 실행하며, client.projects로 프로젝트에 정리합니다.

client.workflows는 Nodaro 워크플로를 읽고, 쓰고, 공유하고, 실행하며, client.projects는 워크플로가 들어 있는 프로젝트를 관리합니다. client.sessions는 프로젝트 안에서 단일 생성을 세션으로 묶습니다. 워크플로는 연결된 노드로 이루어진 캔버스이고, 모든 워크플로는 프로젝트 하나에 속합니다. 이 메서드들은 워크플로 REST API와 같은 엔드포인트를 호출합니다. 개념은 워크플로와 프로젝트를 참고하세요.

메서드

메서드기능
workflows.list(params)프로젝트의 워크플로를 그래프 없이 나열
workflows.get(id)노드, 연결, 설정을 포함해 워크플로 하나 읽기
workflows.getPublic(id)링크로 공유된 워크플로를 토큰 없이 읽기
workflows.create(input)프로젝트에 워크플로 만들기
workflows.update(id, input)워크플로의 필드 변경
workflows.delete(id)워크플로 삭제
workflows.run(id, params?)워크플로 실행 시작
workflows.renderFinal(id, params)미리 보기를 검토한 뒤 EDL 적용 렌더의 최종본 렌더링
workflows.estimateRenderFinal(id, params)최종 렌더링을 시작하기 전에 견적 받기
workflows.export(workflowId, opts?)워크플로를 이식 가능한 JSON 번들로 내보내기
workflows.import(input)번들을 프로젝트로 가져오기
workflows.setVisibility(id, visibility)워크플로를 비공개로 하거나 워크스페이스에 공개
workflows.move(id, params)워크플로를 다른 프로젝트로 옮기기
workflows.sharedWithMe()다른 사람이 나와 공유한 워크플로 나열
workflows.collaborators.*워크플로를 공유받은 사람을 나열, 추가, 변경, 제거
projects.list() 외프로젝트를 나열, 읽기, 만들기, 수정, 삭제
sessions.create(input) 외프로젝트 안의 생성 세션과 그 세션이 담은 작업

client.workflows

list(params)

프로젝트의 워크플로를 나열합니다. 목록은 메타데이터만 반환하며 nodes, edges, settings, sourcePrompt는 빠집니다. 전체 그래프가 필요하면 get()으로 워크플로 하나를 읽으세요.

list(params: { projectId: string }): Promise<{ data: Workflow[] }>

속성

타입

const { data: workflows } = await client.workflows.list({ projectId })
for (const wf of workflows) console.log(wf.id, wf.name, wf.updatedAt)

내가 볼 수 없는 프로젝트이면 NotFoundError가 발생합니다.

get(id)

전체 nodes, edges, settings를 포함해 워크플로 하나를 읽습니다.

get(id: string): Promise<{ data: Workflow }>

속성

타입

const { data: wf } = await client.workflows.get(workflowId)
console.log(`${wf.name}: ${wf.nodes?.length ?? 0} nodes, version ${wf.version}`)

워크플로를 수정할 계획이라면 version을 보관하세요. 다른 사람이 동시에 쓰더라도 업데이트를 안전하게 만들어 줍니다.

getPublic(id)

소유자가 링크로 공유한 워크플로를 읽습니다(GET /v1/public/workflows/:id). 토큰은 필요 없습니다. 워크플로는 공유가 켜져 있는 동안에만 반환되며, 그렇지 않으면 메서드가 NotFoundError를 발생시킵니다. UGC 동영상은 소유자가 아닌 모든 사람에게 게시된 앱이 내보내는 형태로 반환됩니다. UGC 노드와 그 노드에서 만든 모든 노드는 결과 없이 반환되며, 소유자의 사진은 절대 포함되지 않습니다.

getPublic(id: string): Promise<{ data: Workflow }>

속성

타입

const { data: shared } = await client.workflows.getPublic(workflowId)

인증 공급자가 토큰을 반환하지 않으면 요청은 토큰 없이 전송됩니다.

공유된 스튜디오 프로덕션은 소유자의 작업 상태 없이 반환됩니다. 휴지통, 진행 중인 실행, 비어 있는 미디어 슬롯, 저장하지 않은 편집기 초안은 settings.studio에서 빠집니다.

create(input)

프로젝트에 워크플로를 만들고 전체 레코드를 반환합니다. 필요한 것은 projectId와 name뿐이며, 나머지 필드에는 서버 기본값이 적용됩니다.

create(input: CreateWorkflowInput): Promise<{ data: Workflow }>

속성

타입

const { data: wf } = await client.workflows.create({
  projectId,
  name: "Product launch video",
  nodes: [],
  edges: [],
})

미리 만들어진 그래프가 필요하면 편집기에서 만든 워크플로를 내보내 그 nodes와 edges를 읽거나, 템플릿을 복제하세요.

update(id, input)

워크플로 필드의 일부를 바꾸고, 업데이트된 전체 레코드를 반환합니다(PATCH /v1/workflows/:id). 생략한 필드는 그대로 유지됩니다.

update(id: string, input: UpdateWorkflowInput): Promise<{ data: Workflow }>

속성

타입

await client.workflows.update(workflowId, { name: "Renamed", expectedVersion: 7 })

안전한 업데이트. expectedVersion이나 expectedUpdatedAt이 없으면 마지막에 쓴 내용이 남습니다. 둘 중 하나가 있으면, 내가 읽은 뒤로 아무도 워크플로를 바꾸지 않았을 때만 업데이트가 적용됩니다. 그렇지 않으면 WorkflowConflictError가 발생하며, 그 currentRecord에 현재 워크플로가 들어 있으므로 다시 읽지 않고 병합할 수 있습니다.

import { WorkflowConflictError } from "@nodaro/sdk"

try {
  await client.workflows.update(workflowId, { settings, expectedVersion: wf.version })
} catch (err) {
  if (err instanceof WorkflowConflictError && err.currentRecord) {
    const merged = mergeSettings(err.currentRecord.settings, settings)
    await client.workflows.update(workflowId, {
      settings: merged,
      expectedVersion: err.currentVersion,
    })
  } else {
    throw err
  }
}

실행 중 상태나 현재 작업 id처럼 노드 데이터에 담긴 실행 상태 값은 서버가 제거하며, 저장되지 않습니다.

delete(id)

워크플로를 삭제합니다.

delete(id: string): Promise<{ success: true }>

속성

타입

await client.workflows.delete(workflowId)

id가 없거나 내 것이 아니면 NotFoundError가 발생하므로, 삭제가 조용히 실패하는 일은 없습니다.

run(id, params?)

워크플로 실행을 시작하고 { executionId, status }와 함께 곧바로 반환합니다. 실행은 서버에서 계속됩니다. 진행 상황과 결과는 client.executions.get()으로 폴링하세요.

run(id: string, params?: {
  nodeIds?: string[]
  inputOverrides?: Record<string, Record<string, unknown>>
  continueFromExecutionId?: string
}): Promise<{ executionId: string; status: "pending" | "running" }>

속성

타입

const { executionId } = await client.workflows.run(workflowId, {
  nodeIds: ["text-prompt-1", "image-gen-2"],
})
const { data: execution } = await client.executions.get(executionId)
  • 계정이 실행에 들 수 있는 최대 비용을 감당할 수 없으면 InsufficientCreditsError가 발생합니다.
  • OAuth 토큰에는 workflows:execute 범위가 필요합니다.
  • 워크스페이스에서 워크플로를 실행하려면 client.withWorkspace()로 얻은 클라이언트에서 run을 호출하세요.

이전 실행 이어 가기. continueFromExecutionId를 사용하면 nodeIds에 있는 노드만 실행됩니다. 다른 모든 노드는 워크플로에 저장된 결과가 아니라 이전 실행이 만든 결과를 넘겨줍니다. 편집 계획은 현재 검토 내용을 적용한 계획을 넘겨줍니다. 이전 실행의 inputOverrides가 다시 적용되고, 지금 보내는 값이 필드별로 그 위에 적용됩니다.

await client.workflows.run(workflowId, {
  nodeIds: [renderNodeId, captionsNodeId],
  inputOverrides: { [renderNodeId]: { quality: "final" } },
  continueFromExecutionId: previewRunId,
})

거부된 이어 가기는 실행이 만들어지기 전에 오류를 발생시킵니다. 이전 실행을 찾을 수 없으면 코드가 continuation_not_found인 NotFoundError가 발생합니다. 그 밖의 경우에는 code가 continuation_subset_required(nodeIds 없음), continuation_workflow_mismatch, continuation_version_mismatch, continuation_not_completed 중 하나인 NodaroError가 발생합니다.

미리 보기에서 실행을 멈추고 검토하는 설치에서는, EDL 적용 렌더가 미리 보기로 설정된 워크플로의 실행이 400 preview_review_required로 거부됩니다. SDK 실행은 아무도 검토하지 않기 때문입니다. 그 실행에서만 inputOverrides: { [renderNodeId]: { quality: "final" } }로 렌더를 최종으로 설정하세요. Nodaro Cloud에서는 실행이 미리 보기에서 멈추지 않으므로 이 거부가 일어나지 않습니다.

renderFinal(id, params)

미리 보기를 만든 EDL 적용 렌더의 최종본을, 내가 검토한 뒤 렌더링합니다. 편집기의 최종 렌더링 버튼과 같은 작업을 실행합니다. 이번 실행에서만 렌더를 최종으로 실행한 다음, 그 뒤의 모든 노드를 실행합니다. 노드는 자신의 품질 설정을 유지합니다. 편집 계획과 렌더 사이에 카메라 전환이 있으면 카메라 전환이 먼저 다시 실행됩니다.

renderFinal(id: string, params: { renderNodeId: string; continueFromExecutionId: string }): Promise<{ executionId: string; status: "pending" | "running" }>

속성

타입

서버가 편집기와 같은 규칙으로 실행할 노드를 정하므로, 렌더와 이전 실행만 보내면 됩니다. 실행되지 않는 모든 노드는 이전 실행이 만든 결과를 넘겨줍니다.

const { executionId } = await client.workflows.renderFinal(workflowId, {
  renderNodeId,
  continueFromExecutionId: previewRunId,
})
  • 존재하지 않는 노드 id는 render_final_node_not_found로, EDL 적용 렌더가 아닌 노드는 render_final_not_a_render로 거부됩니다. run()의 이어 가기 코드도 적용됩니다.
  • 비용을 내는 쪽이 감당할 수 없는 실행은 실행이 만들어지기 전에 required와 available을 담은 InsufficientCreditsError(402)를 발생시킵니다.
  • OAuth 토큰에는 workflows:execute 범위가 필요합니다.

estimateRenderFinal(id, params)

renderFinal()의 견적을 내며 아무것도 만들지 않습니다. 같은 매개변수를 받습니다.

estimateRenderFinal(id: string, params: { renderNodeId: string; continueFromExecutionId: string }): Promise<{ data: RenderFinalQuote }>
const { data: quote } = await client.workflows.estimateRenderFinal(workflowId, {
  renderNodeId,
  continueFromExecutionId: previewRunId,
})
if (quote.sufficient) {
  await client.workflows.renderFinal(workflowId, { renderNodeId, continueFromExecutionId: previewRunId })
}

RenderFinalQuote에는 renderNodeId, nodeIds(실행될 노드), inputOverrides(실행에 쓰이는 재정의), estimatedCredits, sufficient(비용을 내는 쪽이 감당할 수 있는지), available(비용을 내는 쪽이 쓸 수 있는 크레딧)이 있습니다. 워크스페이스 예산이 비용을 내면 available은 null입니다. 크레딧이 없는 에디션에서는 수치가 null입니다.

export(workflowId, opts?)

워크플로의 저장된 버전을 이식 가능한 JSON 번들로 내보냅니다. 편집기의 내보내기 메뉴가 쓰는 것과 같은 파일 형식입니다. assets: true를 주면 편집기의 에셋 포함처럼, 워크플로가 사용하는 에셋도 번들에 담깁니다.

export(workflowId: string, opts?: { assets?: boolean }): Promise<{ data: WorkflowExport }>

속성

타입

const { data: bundle } = await client.workflows.export(workflowId, { assets: true })

이식성. 번들은 다른 인스턴스가 가져올 수 있는 미디어만 가리킬 수 있습니다. 노드가 다른 인스턴스에서 접근할 수 없는 URL을 쓰면, 예를 들어 localhost에 있는 셀프 호스팅 설치의 자체 스토리지, 로컬 네트워크 주소, .internal 이름을 쓰면, 번들은 그 URL을 portability.unreachableMedia 아래에 나열합니다.

bundle.portability?.unreachableMedia
// [{ nodeId: "n1", nodeLabel: "Video URL", field: "videoUrl", url: "http://localhost:3000/storage/..." }]

모든 미디어 URL이 공개되어 있으면 이 필드는 없습니다. 이런 번들도 가져올 수는 있지만, 해당 노드는 미디어를 다른 인스턴스에 다시 업로드할 때까지 그곳에서 실행할 수 없습니다. 가져오기와 내보내기를 참고하세요.

import(input)

내보낸 번들을 내 프로젝트 중 하나로 가져오고 새 워크플로를 반환합니다.

import(input: WorkflowExport & { projectId: string }): Promise<{
  data: Workflow
  importReport?: WorkflowImportReport
}>

속성

타입

const { data: wf, importReport } = await client.workflows.import({ ...bundle, projectId })

가져오기가 하는 일:

  • 에셋이 다시 만들어집니다. 번들에 있는 캐릭터, 오브젝트, 크리처, 장소는 내 계정의 새 항목이 됩니다. 워크플로는 에셋 노드, 노드 데이터의 모든 @ 멘션, 워크플로 설정에서 그 새 항목을 가리키도록 바뀝니다.
  • 미디어가 복사됩니다. 다른 호스트에 있는 미디어는 접근할 수 있으면 이 인스턴스의 스토리지로 복사되므로, 워크플로가 다른 사람의 서버에 의존하지 않습니다. 한도는 워크플로의 미디어 25개와 번들된 에셋용 25개이며, 이미지는 최대 20MB, 동영상과 오디오는 최대 50MB입니다.
  • 복사본은 내 스토리지 용량에 포함됩니다. 스토리지가 부족해도 워크플로는 가져와지며, 들어가지 못한 에셋은 보고서에 나열됩니다.

importReport가 결과를 알려 줍니다.

importReport
// {
//   rehosted: 3,                                    // files copied to this instance
//   unreachable: [{ nodeId, nodeLabel, field, url }], // private hosts, left as they were
//   skipped: [{ nodeId, field, url, reason: "HTTP 404" }],
//   assetIdMap: { "<bundled asset id>": "<new asset id>" },
//   assetsSkipped: [{ kind: "character", id, name: "Kira", reason: "Storage limit exceeded" }],
// }

assetIdMap은 번들에 에셋이 담겨 있으면 항상 있습니다. 서버가 워크플로 안의 모든 멘션을 이미 업데이트했으니, 이 맵은 워크플로 밖에 보관하는 참조에만 쓰세요. assetsSkipped는 빠진 항목이 있을 때만 나타납니다.

setVisibility(id, visibility)

워크플로를 만든 사람과 내가 추가한 공동 작업자만 볼 수 있는 "private"이나, 워크스페이스의 모든 사람이 볼 수 있는 "workspace"로 설정합니다.

setVisibility(id: string, visibility: "private" | "workspace"): Promise<{ data: Workflow }>

속성

타입

await client.workflows.setVisibility(workflowId, "workspace")

만든 사람이나 워크스페이스 관리자만 바꿀 수 있으며, 그 밖의 사람은 ForbiddenError를 받습니다. 워크스페이스는 Nodaro Cloud의 조직에 있습니다.

move(id, params)

워크플로를 다른 프로젝트로 옮깁니다(POST /v1/workflows/:id/move). 워크플로는 원래 폴더에서 빠집니다.

move(id: string, params: { projectId: string }): Promise<{
  data: Workflow
  droppedCollaborators: { userId: string; name: string | null }[]
}>

속성

타입

const { droppedCollaborators } = await client.workflows.move(workflowId, { projectId: archiveProjectId })

이동으로 워크플로가 워크스페이스 밖으로 나가면, 그 워크스페이스에서 받은 접근 권한은 사라집니다. 해당하는 사람은 droppedCollaborators로 반환됩니다.

sharedWithMe()

다른 사람이 나와 직접 공유한 워크플로를 나열합니다. 내가 속한 워크스페이스의 작업물은 이미 그 워크스페이스의 목록에 나타나므로 포함되지 않습니다. 각 워크플로에는 내가 가진 grantedRole이 담겨 있습니다.

sharedWithMe(): Promise<{ data: (Workflow & { grantedRole: "viewer" | "editor" })[] }>
const { data: shared } = await client.workflows.sharedWithMe()
for (const wf of shared) console.log(wf.name, wf.grantedRole)

조직이 없는 인스턴스에서는 목록이 비어 있습니다.

collaborators

워크플로를 공유받은 사람들로, client.workflows.collaborators로 접근합니다. 이 메서드들은 조직이 있는 인스턴스에만 있으며, 그 밖의 인스턴스에서는 NotFoundError를 발생시킵니다.

collaborators.list(workflowId: string): Promise<{ data: Collaborator[] }>
collaborators.add(workflowId: string, input: { userId?: string; email?: string; role: "viewer" | "editor" }): Promise<{ data: { userId: string; role: "viewer" | "editor" } }>
collaborators.update(workflowId: string, userId: string, input: { role: "viewer" | "editor" }): Promise<{ data: { userId: string; role: "viewer" | "editor" } }>
collaborators.remove(workflowId: string, userId: string): Promise<{ success: true }>

속성

타입

await client.workflows.collaborators.add(workflowId, { email: "editor@example.com", role: "editor" })

const { data: people } = await client.workflows.collaborators.list(workflowId)
// [{ userId, name, avatar, role }]: email addresses are never returned

공동 작업자는 자신의 사용자 id로 remove()를 호출해 워크플로에서 나갈 수 있습니다.

client.projects

프로젝트는 워크플로를 묶습니다. 모든 워크플로는 정확히 하나의 프로젝트에 속합니다.

projects.list()

내 프로젝트를 나열합니다.

list(): Promise<{ data: Project[] }>
const { data: projects } = await client.projects.list()

projects.get(id)

프로젝트 하나를 읽습니다.

get(id: string): Promise<{ data: Project }>

속성

타입

const { data: project } = await client.projects.get(projectId)

projects.create(input)

프로젝트를 만듭니다.

create(input: { name: string; description?: string; settings?: Record<string, unknown> }): Promise<{ data: Project }>

속성

타입

const { data: project } = await client.projects.create({ name: "Spring campaign" })

projects.update(id, input)

프로젝트를 변경합니다. 필드를 하나 이상 전달하세요.

update(id: string, input: { name?: string; description?: string; settings?: Record<string, unknown> }): Promise<{ data: Project }>

속성

타입

await client.projects.update(projectId, { description: "Assets for the spring launch" })

projects.delete(id)

프로젝트를 삭제합니다.

delete(id: string): Promise<{ success: true }>

속성

타입

await client.projects.delete(projectId)

client.sessions

생성 세션은 프로젝트 안에서 단일 생성을 묶으며, 모든 기기에서 똑같이 보입니다. 프로젝트는 세션을 담고, 각 세션은 항목을 담습니다. 세션의 프로젝트는 만들 때 정해집니다. 각 항목은 내 작업 하나의 사본이 아니라 링크입니다.

const { data: session } = await client.sessions.create({ projectId, tool: "create-image", title: "Beach" })

// A job joins a session when the generation request names it.
await client.nodes.runAndWait("generate-image", {
  prompt: "a red car",
  sessionId: session.id,
  origin: "create-image",
})

const { data: sessions, next } = await client.sessions.list({ projectId })
const { data: items } = await client.sessions.items(session.id)
메서드시그니처참고
createcreate(input: CreateSessionInput): Promise<{ data: GenerationSession }>tool은 필수이며, 예를 들어 create-image입니다. projectId와 title은 선택 사항입니다.
listlist(params?: ListSessionsParams): Promise<SessionPage<GenerationSession>>최신순입니다. 필터는 projectId, tool, includeArchived, limit, cursor입니다. 각 세션에는 itemCount와 최대 4개의 previewUrls가 담깁니다.
getget(id: string): Promise<{ data: GenerationSession }>세션 하나입니다.
updateupdate(id: string, input: UpdateSessionInput): Promise<{ data: GenerationSession }>title(null이면 제목 없음)과 archived입니다. 프로젝트는 바꿀 수 없습니다.
deletedelete(id: string): Promise<{ success: true }>세션을 삭제합니다. 세션의 작업은 유지되며, 그 작업을 담은 다른 모든 세션에도 그대로 남습니다.
itemsitems(id: string, params?: ListSessionItemsParams): Promise<SessionPage<SessionItem>>피드이며, 최신순입니다. 각 항목에는 jobId, addedAt, imported, importedFrom, job이 있습니다.
importItemsimportItems(id: string, input: { jobIds: string[] }): Promise<{ data: { imported: number; requested: number } }>내 작업을 최대 100개까지 세션에 연결합니다. 전부 성공하거나 전부 실패합니다. 내 것이 아닌 작업이 하나라도 있으면 호출이 404로 실패합니다.
removeItemremoveItem(id: string, jobId: string): Promise<{ success: true }>링크만 제거합니다. 작업과, 그 작업을 담은 다른 모든 세션은 그대로입니다.

프로젝트 사이의 이동은 없습니다. 작업물을 다른 프로젝트에 넣으려면 그 프로젝트의 세션에서 importItems()를 호출한 다음, 이전 세션에서 removeItem()을 호출하세요.

작업을 만드는 모든 요청은 sessionId와 origin을 받으며, 플랫폼은 이를 작업에 기록합니다. 이 필드는 GenerateImageParams, GenerateVideoParams, TextToSpeechParams 같은 모든 생성 매개변수 타입과, 캐릭터, 오브젝트, 장소, 크리처의 generate* 입력에 타입이 지정되어 있습니다. origin은 슬러그여야 합니다. 소문자, 숫자, 대시로 된 최대 40자입니다. 다른 값은 400 validation_error로 실패합니다. 작업의 session_id는 client.jobs.get()과 client.jobs.list()에 나옵니다.

타입

타입구조
Workflowid, projectId, userId, name, description?, folderId?, version?, thumbnailUrl?, nodes?, edges?, settings?, sourcePrompt?, createdAt, updatedAt. 그래프 필드는 전체 레코드에만 있습니다.
RunWorkflowParamsnodeIds?, inputOverrides?, continueFromExecutionId?
RenderFinalParamsrenderNodeId, continueFromExecutionId
RenderFinalQuoterenderNodeId, nodeIds, inputOverrides, estimatedCredits, sufficient, available
Projectid, userId, name, description?, settings?, createdAt, updatedAt
GenerationSession, SessionItem, SessionPage생성 세션, 그 항목 하나, 그리고 둘 중 하나의 페이지
CollaboratoruserId, name?, avatar?, role
WorkflowExportexport()가 반환하고 import()가 받는 이식 가능한 번들
GenericNode, GenericEdgenodes와 edges에 쓰이는 노드와 연결의 구조

모두 @nodaro/sdk가 내보냅니다. 타입을 참고하세요.

자주 묻는 질문

코드에서 Nodaro 워크플로를 실행하려면 어떻게 하나요?

client.workflows.run(workflowId)를 호출하세요. 곧바로 executionId가 반환됩니다. 상태가 completed, failed, cancelled, timed_out 중 하나가 될 때까지 client.executions.get(executionId)를 폴링하세요.

워크플로의 일부 노드만 실행할 수 있나요?

네. 실행할 노드 id 목록인 nodeIds를 담은 객체를 client.workflows.run의 두 번째 인수로 전달하세요. 그 노드만 실행됩니다.

코드에서 미리 보기의 최종본을 렌더링하려면 어떻게 하나요?

EDL 적용 렌더의 노드 id와 미리 보기를 만든 실행의 id로 client.workflows.renderFinal을 호출하세요. 렌더를 최종 품질로 실행하고 그 뒤의 모든 노드를 실행합니다. client.workflows.estimateRenderFinal로 먼저 견적을 받을 수 있습니다.

다른 사람의 변경 사항을 덮어쓰지 않으려면 어떻게 하나요?

읽어 온 버전을 expectedVersion으로 client.workflows.update에 전달하세요. 그사이에 워크플로가 바뀌었다면 호출은 현재 레코드를 담은 WorkflowConflictError를 발생시키므로, 병합한 다음 다시 시도할 수 있습니다.

워크플로를 다른 Nodaro 인스턴스로 복사하려면 어떻게 하나요?

assets 옵션을 true로 설정해 client.workflows.export로 내보낸 다음, 다른 인스턴스에서 client.workflows.import와 projectId로 번들을 가져오세요. 접근할 수 있는 미디어는 새 인스턴스의 스토리지로 복사됩니다.

최종 업데이트

목차