democut 0.5.0 → 0.6.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +9 -0
  2. package/dist/cli.js +2979 -902
  3. package/package.json +3 -2
package/dist/cli.js CHANGED
@@ -1298,21 +1298,21 @@ var require_errors = __commonJS({
1298
1298
  function extendErrors({ gen, keyword, schemaValue, data, errsCount, it }) {
1299
1299
  if (errsCount === void 0)
1300
1300
  throw new Error("ajv implementation error");
1301
- const err3 = gen.name("err");
1301
+ const err2 = gen.name("err");
1302
1302
  gen.forRange("i", errsCount, names_1.default.errors, (i) => {
1303
- gen.const(err3, (0, codegen_1._)`${names_1.default.vErrors}[${i}]`);
1304
- gen.if((0, codegen_1._)`${err3}.instancePath === undefined`, () => gen.assign((0, codegen_1._)`${err3}.instancePath`, (0, codegen_1.strConcat)(names_1.default.instancePath, it.errorPath)));
1305
- gen.assign((0, codegen_1._)`${err3}.schemaPath`, (0, codegen_1.str)`${it.errSchemaPath}/${keyword}`);
1303
+ gen.const(err2, (0, codegen_1._)`${names_1.default.vErrors}[${i}]`);
1304
+ gen.if((0, codegen_1._)`${err2}.instancePath === undefined`, () => gen.assign((0, codegen_1._)`${err2}.instancePath`, (0, codegen_1.strConcat)(names_1.default.instancePath, it.errorPath)));
1305
+ gen.assign((0, codegen_1._)`${err2}.schemaPath`, (0, codegen_1.str)`${it.errSchemaPath}/${keyword}`);
1306
1306
  if (it.opts.verbose) {
1307
- gen.assign((0, codegen_1._)`${err3}.schema`, schemaValue);
1308
- gen.assign((0, codegen_1._)`${err3}.data`, data);
1307
+ gen.assign((0, codegen_1._)`${err2}.schema`, schemaValue);
1308
+ gen.assign((0, codegen_1._)`${err2}.data`, data);
1309
1309
  }
1310
1310
  });
1311
1311
  }
1312
1312
  exports.extendErrors = extendErrors;
1313
1313
  function addError(gen, errObj) {
1314
- const err3 = gen.const("err", errObj);
1315
- gen.if((0, codegen_1._)`${names_1.default.vErrors} === null`, () => gen.assign(names_1.default.vErrors, (0, codegen_1._)`[${err3}]`), (0, codegen_1._)`${names_1.default.vErrors}.push(${err3})`);
1314
+ const err2 = gen.const("err", errObj);
1315
+ gen.if((0, codegen_1._)`${names_1.default.vErrors} === null`, () => gen.assign(names_1.default.vErrors, (0, codegen_1._)`[${err2}]`), (0, codegen_1._)`${names_1.default.vErrors}.push(${err2})`);
1316
1316
  gen.code((0, codegen_1._)`${names_1.default.errors}++`);
1317
1317
  }
1318
1318
  function returnErrors(it, errs) {
@@ -6915,6 +6915,9 @@ var require_dist = __commonJS({
6915
6915
  import { realpathSync } from "fs";
6916
6916
  import { fileURLToPath } from "url";
6917
6917
 
6918
+ // src/version.ts
6919
+ var VERSION = "0.6.15";
6920
+
6918
6921
  // src/device-flow.ts
6919
6922
  var DeviceFlowError = class extends Error {
6920
6923
  constructor(code, message) {
@@ -6929,7 +6932,9 @@ var SLOW_DOWN_INCREMENT_MS = 5e3;
6929
6932
  async function postJson(url, body, fetchImpl) {
6930
6933
  const res = await fetchImpl(url, {
6931
6934
  method: "POST",
6932
- headers: { "Content-Type": "application/json" },
6935
+ // api-client 와 같은 이유로 버전을 밝힌다 — device flow 는 그 파일을 거치지 않는
6936
+ // 별도 HTTP 경로라, 여기를 빼면 로그인 경로의 설치본 분포만 보이지 않는다.
6937
+ headers: { "Content-Type": "application/json", "User-Agent": `democut/${VERSION}` },
6933
6938
  body: JSON.stringify(body)
6934
6939
  });
6935
6940
  const text = await res.text();
@@ -6951,19 +6956,19 @@ function oauthError(json) {
6951
6956
  return "invalid_grant";
6952
6957
  }
6953
6958
  async function requestDeviceAuthorization(baseUrl, fetchImpl = fetch) {
6954
- const { ok, status: status2, json } = await postJson(
6959
+ const { ok, status, json } = await postJson(
6955
6960
  `${baseUrl}/api/oauth/device/authorize`,
6956
6961
  {},
6957
6962
  fetchImpl
6958
6963
  );
6959
6964
  if (!ok) {
6960
- if (status2 === 503) {
6965
+ if (status === 503) {
6961
6966
  throw new DeviceFlowError(
6962
6967
  "disabled",
6963
6968
  "\uC11C\uBC84\uC5D0\uC11C device flow \uAC00 \uBE44\uD65C\uC131\uD654\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4 (DEMOCUT_DEVICE_TOKEN_SECRET \uBBF8\uC124\uC815)."
6964
6969
  );
6965
6970
  }
6966
- throw new DeviceFlowError("authorize_failed", `device authorize \uC2E4\uD328 (HTTP ${status2}).`);
6971
+ throw new DeviceFlowError("authorize_failed", `device authorize \uC2E4\uD328 (HTTP ${status}).`);
6967
6972
  }
6968
6973
  return json;
6969
6974
  }
@@ -6974,10 +6979,10 @@ async function pollTokenOnce(baseUrl, deviceCode, fetchImpl = fetch) {
6974
6979
  fetchImpl
6975
6980
  );
6976
6981
  if (ok) return { status: "approved", tokens: json };
6977
- const err3 = oauthError(json);
6978
- if (err3 === "authorization_pending") return { status: "pending" };
6979
- if (err3 === "slow_down") return { status: "slow_down" };
6980
- throw new DeviceFlowError(err3, describeError(err3));
6982
+ const err2 = oauthError(json);
6983
+ if (err2 === "authorization_pending") return { status: "pending" };
6984
+ if (err2 === "slow_down") return { status: "slow_down" };
6985
+ throw new DeviceFlowError(err2, describeError(err2));
6981
6986
  }
6982
6987
  async function refreshToken(baseUrl, refresh, fetchImpl = fetch) {
6983
6988
  const { ok, json } = await postJson(
@@ -6996,9 +7001,9 @@ async function pollUntilApproved(baseUrl, auth, deps = {}) {
6996
7001
  const deadline = now() + auth.expires_in;
6997
7002
  while (now() < deadline) {
6998
7003
  await sleep(intervalMs);
6999
- const out3 = await pollTokenOnce(baseUrl, auth.device_code, fetchImpl);
7000
- if (out3.status === "approved") return out3.tokens;
7001
- if (out3.status === "slow_down") intervalMs += SLOW_DOWN_INCREMENT_MS;
7004
+ const out2 = await pollTokenOnce(baseUrl, auth.device_code, fetchImpl);
7005
+ if (out2.status === "approved") return out2.tokens;
7006
+ if (out2.status === "slow_down") intervalMs += SLOW_DOWN_INCREMENT_MS;
7002
7007
  deps.onPending?.();
7003
7008
  }
7004
7009
  throw new DeviceFlowError("expired_token", describeError("expired_token"));
@@ -7239,222 +7244,7 @@ function logoutCommand(opts) {
7239
7244
  return 0;
7240
7245
  }
7241
7246
 
7242
- // src/api-client.ts
7243
- var ApiError = class extends Error {
7244
- constructor(status2, code, message, retriable, extra = {}) {
7245
- super(message);
7246
- this.status = status2;
7247
- this.code = code;
7248
- this.retriable = retriable;
7249
- this.extra = extra;
7250
- this.name = "ApiError";
7251
- }
7252
- status;
7253
- code;
7254
- retriable;
7255
- extra;
7256
- };
7257
- function parseDetail(status2, body) {
7258
- const detail = body?.detail;
7259
- if (detail && typeof detail === "object" && !Array.isArray(detail)) {
7260
- const d = detail;
7261
- if (typeof d.code === "string" || typeof d.message === "string") {
7262
- const { code, message, retriable, ...extra } = d;
7263
- return new ApiError(
7264
- status2,
7265
- typeof code === "string" ? code : `HTTP_${status2}`,
7266
- typeof message === "string" ? message : `HTTP ${status2}`,
7267
- typeof retriable === "boolean" ? retriable : null,
7268
- extra
7269
- );
7270
- }
7271
- }
7272
- if (typeof detail === "string") {
7273
- return new ApiError(status2, `HTTP_${status2}`, detail, null);
7274
- }
7275
- if (Array.isArray(detail)) {
7276
- const lines = detail.map((e) => {
7277
- const item = e;
7278
- const where = Array.isArray(item.loc) ? item.loc.filter((p) => p !== "body").join(".") : "";
7279
- const msg = typeof item.msg === "string" ? item.msg : JSON.stringify(e);
7280
- return where ? `${where}: ${msg}` : msg;
7281
- });
7282
- return new ApiError(status2, "VALIDATION_ERROR", lines.join("\n"), true);
7283
- }
7284
- return new ApiError(status2, `HTTP_${status2}`, `\uC694\uCCAD\uC774 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4 (HTTP ${status2}).`, null);
7285
- }
7286
- async function apiRequest(baseUrl, path, init = {}, deps = {}) {
7287
- const fetchImpl = deps.fetchImpl ?? fetch;
7288
- const token = await getValidAccessToken(baseUrl, deps);
7289
- const headers = { Authorization: `Bearer ${token}` };
7290
- if (init.body !== void 0) headers["Content-Type"] = "application/json";
7291
- const res = await fetchImpl(`${baseUrl}${path}`, {
7292
- method: init.method ?? "GET",
7293
- headers,
7294
- ...init.body === void 0 ? {} : { body: JSON.stringify(init.body) }
7295
- });
7296
- const text = await res.text();
7297
- let json = null;
7298
- if (text) {
7299
- try {
7300
- json = JSON.parse(text);
7301
- } catch {
7302
- json = null;
7303
- }
7304
- }
7305
- if (!res.ok) {
7306
- if (res.status === 401) {
7307
- throw new ApiError(
7308
- 401,
7309
- "UNAUTHENTICATED",
7310
- "\uC778\uC99D\uC774 \uB9CC\uB8CC\uB418\uC5C8\uAC70\uB098 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. 'democut auth login' \uC73C\uB85C \uB2E4\uC2DC \uB85C\uADF8\uC778\uD558\uC138\uC694.",
7311
- false
7312
- );
7313
- }
7314
- throw parseDetail(res.status, json);
7315
- }
7316
- return json;
7317
- }
7318
-
7319
- // src/api-errors.ts
7320
- function usd(v) {
7321
- return v < 0.01 ? `$${v.toFixed(4)}` : `$${v.toFixed(2)}`;
7322
- }
7323
- function classifyApiError(e, ctx) {
7324
- const hint = ctx.statusHint ?? "\uC0C1\uD0DC \uC870\uD68C";
7325
- if (e.status === 402) {
7326
- const lines = [];
7327
- const limit = e.extra.limit_usd;
7328
- const would = e.extra.would_cost_usd;
7329
- if (typeof limit === "number" && typeof would === "number") {
7330
- lines.push(`\uC0C1\uD55C ${usd(limit)} \xB7 \uC774 \uC694\uCCAD ${usd(would)}`);
7331
- }
7332
- lines.push("\uD574\uC0C1\uB3C4\uB098 \uAE38\uC774\uB97C \uB0AE\uCDB0 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694. \uC544\uC9C1 \uACFC\uAE08\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.");
7333
- return { safety: "fixable", notCharged: true, lines };
7334
- }
7335
- if (e.status === 422) {
7336
- return {
7337
- safety: "fixable",
7338
- notCharged: true,
7339
- lines: ["\uD30C\uB77C\uBBF8\uD130\uB97C \uACE0\uCCD0 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694. \uC544\uC9C1 \uACFC\uAE08\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4."]
7340
- };
7341
- }
7342
- if (e.status === 503) {
7343
- return {
7344
- safety: "safe",
7345
- notCharged: true,
7346
- lines: ["\uC77C\uC2DC\uC801\uC778 \uC624\uB958\uC785\uB2C8\uB2E4. \uC7A0\uC2DC \uD6C4 \uADF8\uB300\uB85C \uB2E4\uC2DC \uC2DC\uB3C4\uD574\uB3C4 \uC548\uC804\uD569\uB2C8\uB2E4."]
7347
- };
7348
- }
7349
- if (e.status === 502) {
7350
- return ctx.submitted ? {
7351
- safety: "unsafe",
7352
- notCharged: false,
7353
- lines: [
7354
- "\u26A0\uFE0F \uC81C\uCD9C\uC774 \uC774\uBBF8 \uC811\uC218\uB410\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC81C\uCD9C\uD558\uBA74 \uB450 \uBC88 \uACFC\uAE08\uB429\uB2C8\uB2E4 \u2014",
7355
- `\uC7AC\uC2DC\uB3C4\uD558\uC9C0 \uB9D0\uACE0 ${hint} \uB85C \uD655\uC778\uD558\uAC70\uB098 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uBB38\uC758\uD558\uC138\uC694.`
7356
- ]
7357
- } : {
7358
- safety: "safe",
7359
- notCharged: true,
7360
- lines: [
7361
- "\uC5C5\uC2A4\uD2B8\uB9BC \uC624\uB958\uC785\uB2C8\uB2E4. \uC774 \uC694\uCCAD\uC740 \uBD80\uC791\uC6A9\uC774 \uC5C6\uC73C\uBBC0\uB85C \uC7A0\uC2DC \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD574\uB3C4 \uC548\uC804\uD569\uB2C8\uB2E4."
7362
- ]
7363
- };
7364
- }
7365
- if (e.status === 404) {
7366
- const lines = [];
7367
- if (e.code === "VIDEOGEN_PROJECT_NOT_FOUND") {
7368
- lines.push(
7369
- "democut_list_projects \uB85C \uC774 \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4\uC758 \uD504\uB85C\uC81D\uD2B8 \uBAA9\uB85D\uC744 \uD655\uC778\uD558\uACE0 \uADF8\uC911 \uD558\uB098\uC758 \uC2AC\uB7EC\uADF8\uB97C \uC4F0\uC138\uC694. \uBAA9\uB85D\uC774 \uBE44\uC5B4 \uC788\uC73C\uBA74 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uD504\uB85C\uC81D\uD2B8\uB97C \uBA3C\uC800 \uB9CC\uB4E4\uC5B4 \uB2EC\uB77C\uACE0 \uC54C\uB9AC\uC138\uC694."
7370
- );
7371
- }
7372
- lines.push("\uC544\uC9C1 \uACFC\uAE08\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4 \u2014 \uAC12\uC744 \uACE0\uCCD0 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.");
7373
- return { safety: "fixable", notCharged: true, lines };
7374
- }
7375
- if (e.status === 403) {
7376
- return {
7377
- safety: "unknown",
7378
- notCharged: true,
7379
- lines: ["\uC774 \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4\uC5D0\uC11C \uC601\uC0C1\uC744 \uC0DD\uC131\uD560 \uAD8C\uD55C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4(\uBA64\uBC84 \uC774\uC0C1 \uD544\uC694)."]
7380
- };
7381
- }
7382
- return { safety: "unknown", notCharged: false, lines: [] };
7383
- }
7384
-
7385
- // src/confirm.ts
7386
- import { createInterface } from "readline/promises";
7387
- function isInteractive() {
7388
- return Boolean(process.stdin.isTTY && process.stdout.isTTY);
7389
- }
7390
- async function confirm(question) {
7391
- const rl = createInterface({ input: process.stdin, output: process.stdout });
7392
- try {
7393
- const answer = await rl.question(`${question} [y/N] `);
7394
- return /^y(es)?$/i.test(answer.trim());
7395
- } finally {
7396
- rl.close();
7397
- }
7398
- }
7399
-
7400
- // src/videogen.ts
7401
- var TERMINAL_STATUSES = /* @__PURE__ */ new Set(["succeeded", "failed", "cancelled"]);
7402
- function isTerminal(status2) {
7403
- return TERMINAL_STATUSES.has(status2);
7404
- }
7405
- function estimate(baseUrl, body, deps = {}) {
7406
- return apiRequest(baseUrl, "/api/videogen/estimate", { method: "POST", body }, deps);
7407
- }
7408
- function submit(baseUrl, body, deps = {}) {
7409
- return apiRequest(baseUrl, "/api/videogen", { method: "POST", body }, deps);
7410
- }
7411
- function status(baseUrl, jobId, projectSlug, deps = {}) {
7412
- const q = projectSlug ? `?project_slug=${encodeURIComponent(projectSlug)}` : "";
7413
- return apiRequest(
7414
- baseUrl,
7415
- `/api/videogen/${encodeURIComponent(jobId)}${q}`,
7416
- {},
7417
- deps
7418
- );
7419
- }
7420
- var WaitTimeout = class extends Error {
7421
- constructor(jobId, waitedS) {
7422
- super(
7423
- `${waitedS}\uCD08 \uC548\uC5D0 \uC644\uB8CC\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uC791\uC5C5\uC740 \uC11C\uBC84\uC5D0\uC11C \uACC4\uC18D \uC9C4\uD589\uB429\uB2C8\uB2E4 \u2014 'democut video status ${jobId}' \uB85C \uB2E4\uC2DC \uD655\uC778\uD558\uC138\uC694 (\uC0C8\uB85C \uC81C\uCD9C\uD558\uBA74 \uB2E4\uC2DC \uACFC\uAE08\uB429\uB2C8\uB2E4).`
7424
- );
7425
- this.jobId = jobId;
7426
- this.waitedS = waitedS;
7427
- this.name = "WaitTimeout";
7428
- }
7429
- jobId;
7430
- waitedS;
7431
- };
7432
- var POLL_INTERVAL_S = 5;
7433
- async function waitForCompletion(baseUrl, jobId, projectSlug, timeoutS, deps = {}) {
7434
- const sleep = deps.sleep ?? ((ms) => new Promise((r) => setTimeout(r, ms)));
7435
- const now = deps.now ?? (() => Math.floor(Date.now() / 1e3));
7436
- const deadline = now() + timeoutS;
7437
- for (; ; ) {
7438
- const job = await status(baseUrl, jobId, projectSlug, deps);
7439
- if (isTerminal(job.status)) return job;
7440
- deps.onTick?.(job);
7441
- if (now() >= deadline) throw new WaitTimeout(jobId, timeoutS);
7442
- await sleep(POLL_INTERVAL_S * 1e3);
7443
- }
7444
- }
7445
- async function download(videoUrl, outPath, fetchImpl = fetch) {
7446
- const { writeFile } = await import("fs/promises");
7447
- const res = await fetchImpl(videoUrl);
7448
- if (!res.ok) {
7449
- throw new Error(
7450
- `\uC0B0\uCD9C\uBB3C \uB0B4\uB824\uBC1B\uAE30 \uC2E4\uD328 (HTTP ${res.status}).`
7451
- );
7452
- }
7453
- await writeFile(outPath, Buffer.from(await res.arrayBuffer()));
7454
- }
7455
-
7456
- // src/commands/video.ts
7457
- var MOCK_NOTICE = "\u26A0\uFE0F mock \uBAA8\uB4DC \u2014 \uC774 \uBC30\uD3EC\uB294 \uC601\uC0C1\uC744 \uC2E4\uC81C\uB85C \uC0DD\uC131\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 (API \uD0A4 \uBBF8\uC124\uC815). \uC544\uB798 \uAE08\uC561\xB7job id\xB7URL \uC740 \uC804\uBD80 \uAC00\uC9DC\uC774\uBA70 \uACFC\uAE08\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.";
7247
+ // src/commands/common.ts
7458
7248
  var EXIT = {
7459
7249
  OK: 0,
7460
7250
  /** 일반 오류 — 미로그인·사용자 취소·생성 실패·알 수 없음. */
@@ -7473,278 +7263,8 @@ var EXIT = {
7473
7263
  */
7474
7264
  WAIT_TIMEOUT: 5
7475
7265
  };
7476
- var out2 = (s) => process.stdout.write(`${s}
7477
- `);
7478
- var err2 = (s) => process.stderr.write(`${s}
7479
- `);
7480
- function emit(opts, ok, payload) {
7481
- if (opts.json) out2(JSON.stringify({ ok, ...payload }));
7482
- }
7483
- function usd2(v) {
7484
- return v < 0.01 ? `$${v.toFixed(4)}` : `$${v.toFixed(2)}`;
7485
- }
7486
- function parseIntOpt(raw, name) {
7487
- if (raw === void 0) return void 0;
7488
- const n = Number(raw);
7489
- if (!Number.isInteger(n)) throw new UsageError(`--${name} \uC740 \uC815\uC218\uC5EC\uC57C \uD569\uB2C8\uB2E4: ${raw}`);
7490
- return n;
7491
- }
7492
7266
  var UsageError = class extends Error {
7493
7267
  };
7494
- var EXIT_BY_SAFETY = {
7495
- fixable: 2,
7496
- // EXIT.FIXABLE
7497
- safe: 3,
7498
- // EXIT.TRANSIENT
7499
- unsafe: 4,
7500
- // EXIT.UPSTREAM_REJECTED
7501
- unknown: 1
7502
- // EXIT.GENERAL
7503
- };
7504
- function renderApiError(e, opts) {
7505
- const g = classifyApiError(e, { submitted: true, statusHint: "democut video status <job_id>" });
7506
- const code = EXIT_BY_SAFETY[g.safety];
7507
- if (opts.json) {
7508
- emit(opts, false, {
7509
- error: { status: e.status, code: e.code, message: e.message, retriable: e.retriable, ...e.extra },
7510
- exit_code: code
7511
- });
7512
- return code;
7513
- }
7514
- err2(`\uC624\uB958 [${e.code}] ${e.message}`);
7515
- for (const line of g.lines) err2(` ${line}`);
7516
- return code;
7517
- }
7518
- async function guarded(opts, fn) {
7519
- try {
7520
- return await fn();
7521
- } catch (e) {
7522
- if (e instanceof NotLoggedInError) {
7523
- if (opts.json) emit(opts, false, { error: { code: "NOT_LOGGED_IN", message: e.message } });
7524
- else err2(e.message);
7525
- return EXIT.GENERAL;
7526
- }
7527
- if (e instanceof ApiError) return renderApiError(e, opts);
7528
- if (e instanceof UsageError) {
7529
- if (opts.json) emit(opts, false, { error: { code: "USAGE", message: e.message } });
7530
- else err2(`\uC0AC\uC6A9\uBC95 \uC624\uB958: ${e.message}`);
7531
- return EXIT.FIXABLE;
7532
- }
7533
- if (e instanceof WaitTimeout) {
7534
- if (opts.json) {
7535
- emit(opts, false, {
7536
- error: { code: "WAIT_TIMEOUT", message: e.message, retriable: false },
7537
- job_id: e.jobId,
7538
- still_running: true
7539
- });
7540
- } else {
7541
- err2(e.message);
7542
- }
7543
- return EXIT.WAIT_TIMEOUT;
7544
- }
7545
- const msg = e?.message ?? String(e);
7546
- if (opts.json) emit(opts, false, { error: { code: "UNKNOWN", message: msg } });
7547
- else err2(`\uC624\uB958: ${msg}`);
7548
- return EXIT.GENERAL;
7549
- }
7550
- }
7551
- function buildEstimateIn(opts) {
7552
- return {
7553
- duration_s: parseIntOpt(opts.duration, "duration") ?? 5,
7554
- resolution: opts.resolution ?? "720p",
7555
- ratio: opts.ratio ?? "16:9",
7556
- ...opts.model ? { model: opts.model } : {}
7557
- };
7558
- }
7559
- function estimateCommand(opts, deps = {}) {
7560
- return guarded(opts, async () => {
7561
- const base = resolveBaseUrl(opts.baseUrl);
7562
- const r = await estimate(base, buildEstimateIn(opts), deps);
7563
- if (opts.json) {
7564
- emit(opts, true, { tokens: r.tokens, cost_usd: r.cost_usd, model: r.model, mock: r.mock });
7565
- } else {
7566
- if (r.mock) err2(MOCK_NOTICE);
7567
- out2(`\uC608\uC0C1 \uBE44\uC6A9: ${usd2(r.cost_usd)} (${r.tokens.toLocaleString()} tokens \xB7 ${r.model})`);
7568
- }
7569
- return EXIT.OK;
7570
- });
7571
- }
7572
- function generateCommand(prompt, opts, deps = {}) {
7573
- return guarded(opts, async () => {
7574
- if (!prompt || !prompt.trim()) {
7575
- throw new UsageError('\uD504\uB86C\uD504\uD2B8\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. \uC608: democut video generate "\uACE0\uC591\uC774\uAC00 \uCDA4\uCD98\uB2E4"');
7576
- }
7577
- if (!opts.project || !opts.project.trim()) {
7578
- throw new UsageError(
7579
- "--project <slug> \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. \uBE44\uC6A9\uC774 \uADC0\uC18D\uB420 \uBCF8\uC778 \uC18C\uC720 \uD504\uB85C\uC81D\uD2B8\uB97C \uC9C0\uC815\uD558\uC138\uC694. \uC2AC\uB7EC\uADF8\uB294 \uC6F9\uC758 \uD504\uB85C\uC81D\uD2B8 \uC8FC\uC18C `/projects/<\uC2AC\uB7EC\uADF8>` \uC5D0\uC11C \uD655\uC778\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4."
7580
- );
7581
- }
7582
- const base = resolveBaseUrl(opts.baseUrl);
7583
- const est = buildEstimateIn(opts);
7584
- const quote = await estimate(base, est, deps);
7585
- if (!opts.json) {
7586
- if (quote.mock) err2(MOCK_NOTICE);
7587
- out2(`\uC608\uC0C1 \uBE44\uC6A9: ${usd2(quote.cost_usd)} (${quote.tokens.toLocaleString()} tokens \xB7 ${quote.model})`);
7588
- out2(` ${est.duration_s}\uCD08 \xB7 ${est.resolution} \xB7 ${est.ratio}`);
7589
- }
7590
- if (!opts.yes) {
7591
- if (!(deps.isInteractiveImpl ?? isInteractive)()) {
7592
- const message = `\uBE44\uB300\uD654\uD615 \uD658\uACBD\uC774\uB77C \uBE44\uC6A9 \uC2B9\uC778\uC744 \uBC1B\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (\uC608\uC0C1 ${usd2(quote.cost_usd)}). \uD655\uC778\uD588\uB2E4\uBA74 --yes \uB97C \uBD99\uC5EC \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694.`;
7593
- if (opts.json) {
7594
- emit(opts, false, {
7595
- error: { code: "CONFIRMATION_REQUIRED", message },
7596
- estimated_cost_usd: quote.cost_usd
7597
- });
7598
- } else {
7599
- err2(message);
7600
- }
7601
- return EXIT.FIXABLE;
7602
- }
7603
- if (opts.json) {
7604
- const message = `--json \uBAA8\uB4DC\uC5D0\uC11C\uB294 --yes \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4 (\uC608\uC0C1 ${usd2(quote.cost_usd)}).`;
7605
- emit(opts, false, {
7606
- error: { code: "CONFIRMATION_REQUIRED", message },
7607
- estimated_cost_usd: quote.cost_usd
7608
- });
7609
- return EXIT.FIXABLE;
7610
- }
7611
- const ask = deps.confirmImpl ?? confirm;
7612
- const question = quote.mock ? "mock \uBAA8\uB4DC\uC785\uB2C8\uB2E4 \u2014 \uC2E4\uC81C \uC601\uC0C1\uC740 \uC0DD\uC131\uB418\uC9C0 \uC54A\uACE0 \uACFC\uAE08\uB3C4 \uC5C6\uC2B5\uB2C8\uB2E4. \uADF8\uB798\uB3C4 \uC9C4\uD589\uD560\uAE4C\uC694?" : `${usd2(quote.cost_usd)} \uAC00 \uCCAD\uAD6C\uB429\uB2C8\uB2E4. \uC0DD\uC131\uD560\uAE4C\uC694?`;
7613
- if (!await ask(question)) {
7614
- out2("\uCDE8\uC18C\uD588\uC2B5\uB2C8\uB2E4. \uACFC\uAE08\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.");
7615
- return EXIT.GENERAL;
7616
- }
7617
- }
7618
- const body = {
7619
- prompt,
7620
- duration_s: est.duration_s,
7621
- resolution: est.resolution,
7622
- ratio: est.ratio,
7623
- generate_audio: Boolean(opts.audio),
7624
- watermark: Boolean(opts.watermark),
7625
- ...opts.model ? { model: opts.model } : {},
7626
- ...opts.seed !== void 0 ? { seed: parseIntOpt(opts.seed, "seed") } : {},
7627
- ...opts.imageUrls?.length ? { image_urls: opts.imageUrls } : {},
7628
- project_slug: opts.project
7629
- };
7630
- const job = await submit(base, body, deps);
7631
- if (job.duplicate) {
7632
- if (job.pending) {
7633
- const message = "\uAC19\uC740 \uC694\uCCAD\uC774 \uBC29\uAE08 \uC81C\uCD9C\uB418\uC5B4 \uC9C4\uD589 \uC911\uC785\uB2C8\uB2E4. \uC7A0\uC2DC \uD6C4 'democut video status <job_id>' \uB85C \uD655\uC778\uD558\uC138\uC694.";
7634
- if (opts.json) emit(opts, true, { duplicate: true, pending: true, job_id: null, message });
7635
- else out2(message);
7636
- return EXIT.OK;
7637
- }
7638
- if (!opts.json) out2(`\uAC19\uC740 \uC694\uCCAD\uC774 \uC774\uBBF8 \uC9C4\uD589 \uC911\uC785\uB2C8\uB2E4 (job ${job.job_id}). \uC0C8\uB85C \uACFC\uAE08\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.`);
7639
- } else if (!opts.json) {
7640
- if (job.mock) err2(MOCK_NOTICE);
7641
- out2(`\uC81C\uCD9C\uB428: job ${job.job_id} (\uC608\uC0C1 ${usd2(job.estimated_cost_usd)})`);
7642
- }
7643
- if (!opts.wait) {
7644
- if (opts.json) {
7645
- emit(opts, true, {
7646
- job_id: job.job_id,
7647
- status: job.status,
7648
- model: job.model,
7649
- estimated_cost_usd: job.estimated_cost_usd,
7650
- duplicate: job.duplicate,
7651
- pending: job.pending,
7652
- mock: job.mock
7653
- });
7654
- } else {
7655
- out2(`\uC644\uB8CC \uD655\uC778: democut video status ${job.job_id}`);
7656
- }
7657
- return EXIT.OK;
7658
- }
7659
- return finishWithWait(base, job.job_id, opts, deps);
7660
- });
7661
- }
7662
- function statusCommand2(jobId, opts, deps = {}) {
7663
- return guarded(opts, async () => {
7664
- if (!jobId) throw new UsageError("job_id \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. \uC608: democut video status <job_id>");
7665
- const base = resolveBaseUrl(opts.baseUrl);
7666
- if (opts.wait) return finishWithWait(base, jobId, opts, deps);
7667
- const job = await status(base, jobId, opts.project, deps);
7668
- return report(job, opts, deps);
7669
- });
7670
- }
7671
- async function finishWithWait(base, jobId, opts, deps) {
7672
- const timeoutS = parseIntOpt(opts.timeout, "timeout") ?? 900;
7673
- if (!opts.json) process.stderr.write(" \uC0DD\uC131 \uB300\uAE30 \uC911");
7674
- const job = await waitForCompletion(base, jobId, opts.project, timeoutS, {
7675
- ...deps,
7676
- onTick: () => {
7677
- if (!opts.json) process.stderr.write(".");
7678
- }
7679
- });
7680
- if (!opts.json) err2("");
7681
- return report(job, opts, deps);
7682
- }
7683
- async function report(job, opts, deps = {}) {
7684
- if (job.status === "failed" || job.status === "cancelled") {
7685
- if (opts.json) {
7686
- emit(opts, false, { job_id: job.job_id, status: job.status, error: job.error });
7687
- } else {
7688
- err2(`\uC0DD\uC131 \uC2E4\uD328 (${job.status}): ${job.error ?? "\uC0AC\uC720 \uBBF8\uC0C1"}`);
7689
- }
7690
- return EXIT.GENERAL;
7691
- }
7692
- if (job.mock) {
7693
- if (opts.json) {
7694
- emit(opts, true, {
7695
- job_id: job.job_id,
7696
- status: job.status,
7697
- model: job.model,
7698
- video_url: job.video_url ?? null,
7699
- saved_to: null,
7700
- mock: true
7701
- });
7702
- } else {
7703
- err2(MOCK_NOTICE);
7704
- out2(`\uC0C1\uD0DC: ${job.status} (\uAC00\uC9DC)`);
7705
- if (job.video_url) out2(` URL : ${job.video_url} \u2190 \uC2E4\uC7AC\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4`);
7706
- out2(" \uB0B4\uB824\uBC1B\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uC2E4\uC81C \uC0DD\uC131\uC744 \uD558\uB824\uBA74 \uC6B4\uC601\uC790\uC5D0\uAC8C API \uD0A4 \uC124\uC815\uC744 \uC694\uCCAD\uD558\uC138\uC694.");
7707
- }
7708
- return EXIT.OK;
7709
- }
7710
- let saved = null;
7711
- let downloadError = null;
7712
- if (job.video_url && opts.output) {
7713
- try {
7714
- await (deps.downloadImpl ?? download)(job.video_url, opts.output);
7715
- saved = opts.output;
7716
- } catch (e) {
7717
- downloadError = e?.message ?? String(e);
7718
- }
7719
- }
7720
- if (opts.json) {
7721
- emit(opts, downloadError === null, {
7722
- job_id: job.job_id,
7723
- status: job.status,
7724
- model: job.model,
7725
- video_url: job.video_url ?? null,
7726
- tokens: job.tokens ?? null,
7727
- cost_usd: job.cost_usd ?? null,
7728
- seed: job.seed ?? null,
7729
- saved_to: saved,
7730
- ...downloadError ? { download_error: downloadError } : {}
7731
- });
7732
- } else {
7733
- out2(`\uC0C1\uD0DC: ${job.status}`);
7734
- if (job.cost_usd != null) out2(` \uBE44\uC6A9: ${usd2(job.cost_usd)}${job.tokens != null ? ` (${job.tokens.toLocaleString()} tokens)` : ""}`);
7735
- if (saved) out2(` \uC800\uC7A5\uB428: ${saved}`);
7736
- else if (job.video_url) {
7737
- out2(` URL : ${job.video_url}`);
7738
- if (downloadError) {
7739
- err2(` \uB0B4\uB824\uBC1B\uAE30 \uC2E4\uD328: ${downloadError}`);
7740
- err2(" \u26A0\uFE0F \uC704 URL \uB85C \uC9C1\uC811 \uBC1B\uC73C\uC138\uC694 \u2014 \uC11C\uBC84\uB294 \uBCF4\uAD00\uD558\uC9C0 \uC54A\uC73C\uBA70, \uB2E4\uC2DC \uB9CC\uB4E4\uBA74 \uB610 \uACFC\uAE08\uB429\uB2C8\uB2E4.");
7741
- } else {
7742
- out2(" \u26A0\uFE0F \uC11C\uBC84\uB294 \uC774 \uC0B0\uCD9C\uBB3C\uC744 \uBCF4\uAD00\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2014 \uC9C0\uAE08 \uB0B4\uB824\uBC1B\uC73C\uC138\uC694 (--output <\uACBD\uB85C>).");
7743
- }
7744
- }
7745
- }
7746
- return downloadError === null ? EXIT.OK : EXIT.GENERAL;
7747
- }
7748
7268
 
7749
7269
  // node_modules/zod/v4/core/core.js
7750
7270
  var _a;
@@ -12967,11 +12487,11 @@ var ZodPipe = /* @__PURE__ */ $constructor("ZodPipe", (inst, def) => {
12967
12487
  inst.in = def.in;
12968
12488
  inst.out = def.out;
12969
12489
  });
12970
- function pipe(in_, out3) {
12490
+ function pipe(in_, out2) {
12971
12491
  return new ZodPipe({
12972
12492
  type: "pipe",
12973
12493
  in: in_,
12974
- out: out3
12494
+ out: out2
12975
12495
  // ...util.normalizeParams(params),
12976
12496
  });
12977
12497
  }
@@ -14536,8 +14056,8 @@ var UrlElicitationRequiredError = class extends McpError {
14536
14056
  };
14537
14057
 
14538
14058
  // node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.js
14539
- function isTerminal2(status2) {
14540
- return status2 === "completed" || status2 === "failed" || status2 === "cancelled";
14059
+ function isTerminal(status) {
14060
+ return status === "completed" || status === "failed" || status === "cancelled";
14541
14061
  }
14542
14062
 
14543
14063
  // node_modules/zod-to-json-schema/dist/esm/parsers/string.js
@@ -14634,11 +14154,11 @@ var Protocol = class {
14634
14154
  if (!task) {
14635
14155
  throw new McpError(ErrorCode.InvalidParams, `Task not found: ${taskId}`);
14636
14156
  }
14637
- if (!isTerminal2(task.status)) {
14157
+ if (!isTerminal(task.status)) {
14638
14158
  await this._waitForTaskUpdate(taskId, extra.signal);
14639
14159
  return await handleTaskResult();
14640
14160
  }
14641
- if (isTerminal2(task.status)) {
14161
+ if (isTerminal(task.status)) {
14642
14162
  const result = await this._taskStore.getTaskResult(taskId, extra.sessionId);
14643
14163
  this._clearTaskQueue(taskId);
14644
14164
  return {
@@ -14673,7 +14193,7 @@ var Protocol = class {
14673
14193
  if (!task) {
14674
14194
  throw new McpError(ErrorCode.InvalidParams, `Task not found: ${request.params.taskId}`);
14675
14195
  }
14676
- if (isTerminal2(task.status)) {
14196
+ if (isTerminal(task.status)) {
14677
14197
  throw new McpError(ErrorCode.InvalidParams, `Cannot cancel task in terminal status: ${task.status}`);
14678
14198
  }
14679
14199
  await this._taskStore.updateTaskStatus(request.params.taskId, "cancelled", "Client cancelled task execution.", extra.sessionId);
@@ -15041,7 +14561,7 @@ var Protocol = class {
15041
14561
  while (true) {
15042
14562
  const task2 = await this.getTask({ taskId }, options);
15043
14563
  yield { type: "taskStatus", task: task2 };
15044
- if (isTerminal2(task2.status)) {
14564
+ if (isTerminal(task2.status)) {
15045
14565
  if (task2.status === "completed") {
15046
14566
  const result = await this.getTaskResult({ taskId }, resultSchema, options);
15047
14567
  yield { type: "result", result };
@@ -15454,8 +14974,8 @@ var Protocol = class {
15454
14974
  }
15455
14975
  return task;
15456
14976
  },
15457
- storeTaskResult: async (taskId, status2, result) => {
15458
- await taskStore.storeTaskResult(taskId, status2, result, sessionId);
14977
+ storeTaskResult: async (taskId, status, result) => {
14978
+ await taskStore.storeTaskResult(taskId, status, result, sessionId);
15459
14979
  const task = await taskStore.getTask(taskId, sessionId);
15460
14980
  if (task) {
15461
14981
  const notification = TaskStatusNotificationSchema.parse({
@@ -15463,7 +14983,7 @@ var Protocol = class {
15463
14983
  params: task
15464
14984
  });
15465
14985
  await this.notification(notification);
15466
- if (isTerminal2(task.status)) {
14986
+ if (isTerminal(task.status)) {
15467
14987
  this._cleanupTaskProgressHandler(taskId);
15468
14988
  }
15469
14989
  }
@@ -15471,15 +14991,15 @@ var Protocol = class {
15471
14991
  getTaskResult: (taskId) => {
15472
14992
  return taskStore.getTaskResult(taskId, sessionId);
15473
14993
  },
15474
- updateTaskStatus: async (taskId, status2, statusMessage) => {
14994
+ updateTaskStatus: async (taskId, status, statusMessage) => {
15475
14995
  const task = await taskStore.getTask(taskId, sessionId);
15476
14996
  if (!task) {
15477
14997
  throw new McpError(ErrorCode.InvalidParams, `Task "${taskId}" not found - it may have been cleaned up`);
15478
14998
  }
15479
- if (isTerminal2(task.status)) {
15480
- throw new McpError(ErrorCode.InvalidParams, `Cannot update task "${taskId}" from terminal status "${task.status}" to "${status2}". Terminal states (completed, failed, cancelled) cannot transition to other states.`);
14999
+ if (isTerminal(task.status)) {
15000
+ throw new McpError(ErrorCode.InvalidParams, `Cannot update task "${taskId}" from terminal status "${task.status}" to "${status}". Terminal states (completed, failed, cancelled) cannot transition to other states.`);
15481
15001
  }
15482
- await taskStore.updateTaskStatus(taskId, status2, statusMessage, sessionId);
15002
+ await taskStore.updateTaskStatus(taskId, status, statusMessage, sessionId);
15483
15003
  const updatedTask = await taskStore.getTask(taskId, sessionId);
15484
15004
  if (updatedTask) {
15485
15005
  const notification = TaskStatusNotificationSchema.parse({
@@ -15487,7 +15007,7 @@ var Protocol = class {
15487
15007
  params: updatedTask
15488
15008
  });
15489
15009
  await this.notification(notification);
15490
- if (isTerminal2(updatedTask.status)) {
15010
+ if (isTerminal(updatedTask.status)) {
15491
15011
  this._cleanupTaskProgressHandler(taskId);
15492
15012
  }
15493
15013
  }
@@ -16312,32 +15832,2547 @@ var StdioServerTransport = class {
16312
15832
  }
16313
15833
  };
16314
15834
 
16315
- // src/version.ts
16316
- var VERSION = "0.5.0";
16317
-
16318
- // src/mcp/tools.ts
16319
- import { existsSync as existsSync2 } from "fs";
16320
-
16321
- // src/credits.ts
16322
- var LEDGER_MAX_LIMIT = 200;
16323
- var LEDGER_DEFAULT_LIMIT = 10;
16324
- function balance(baseUrl, workspaceId, deps = {}) {
16325
- return apiRequest(
16326
- baseUrl,
16327
- `/api/workspaces/${encodeURIComponent(workspaceId)}/credits`,
16328
- {},
16329
- deps
16330
- );
16331
- }
16332
- function ledger(baseUrl, workspaceId, opts = {}, deps = {}) {
16333
- const q = new URLSearchParams();
16334
- q.set("limit", String(opts.limit ?? LEDGER_DEFAULT_LIMIT));
16335
- if (opts.beforeId !== void 0) q.set("before_id", String(opts.beforeId));
16336
- return apiRequest(
16337
- baseUrl,
16338
- `/api/workspaces/${encodeURIComponent(workspaceId)}/credits/ledger?${q}`,
16339
- {},
16340
- deps
15835
+ // src/mcp/tools.generated.ts
15836
+ var GENERATED_TOOLS = [
15837
+ {
15838
+ "name": "democut_auth_status",
15839
+ "description": "\uB85C\uADF8\uC778 \uC0C1\uD0DC\xB7\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4\xB7\uC5ED\uD560\xB7\uD2F0\uC5B4\uB97C \uD655\uC778\uD55C\uB2E4. \uBD80\uC791\uC6A9\uB3C4 \uACFC\uAE08\uB3C4 \uC5C6\uB2E4. \uB2E4\uB978 \uB3C4\uAD6C\uAC00 \uAD8C\uD55C \uC624\uB958\uB97C \uB0B4\uBA74 \uBA3C\uC800 \uC774\uAC78\uB85C \uD655\uC778\uD558\uB77C. \uB2E4\uC74C \uD638\uCD9C: democut_list_projects, democut_create_project.",
15840
+ "inputSchema": {
15841
+ "type": "object",
15842
+ "properties": {},
15843
+ "required": [],
15844
+ "additionalProperties": false
15845
+ },
15846
+ "annotations": {
15847
+ "readOnlyHint": true,
15848
+ "destructiveHint": false,
15849
+ "idempotentHint": true,
15850
+ "openWorldHint": false
15851
+ }
15852
+ },
15853
+ {
15854
+ "name": "democut_list_projects",
15855
+ "description": "\uC774 \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4\uC758 \uD504\uB85C\uC81D\uD2B8 \uBAA9\uB85D(\uCD5C\uADFC \uC218\uC815\uC21C). \uBD80\uC791\uC6A9\uB3C4 \uACFC\uAE08\uB3C4 \uC5C6\uB2E4. \uCEF7\uC740 \uD504\uB85C\uC81D\uD2B8 \uC5C6\uC774 \uBABB \uB9CC\uB4E0\uB2E4(\uBE44\uC6A9 \uADC0\uC18D \uB2E8\uC704) \u2014 \uC5C6\uC73C\uBA74 democut_create_project. \uD504\uB85C\uC81D\uD2B8\uB97C \uC694\uAD6C\uD558\uB294 \uB3C4\uAD6C\uC758 slug \uB294 \uC2E4\uC7AC\uD558\uB294 \uBCF8\uC778 \uAC83\uC774\uC5B4\uC57C \uD558\uACE0 \uD2C0\uB9AC\uBA74 404 \uB2E4 \u2014 \uC2AC\uB7EC\uADF8\uB97C \uBAA8\uB974\uBA74 \uCD94\uCE21\uD558\uC9C0 \uB9D0\uACE0 \uC774\uAC78\uB85C \uBA3C\uC800 \uD655\uC778\uD558\uB77C. \uB2E4\uC74C \uD638\uCD9C: democut_create_project, democut_get_options.",
15856
+ "inputSchema": {
15857
+ "type": "object",
15858
+ "properties": {
15859
+ "limit": {
15860
+ "default": 30,
15861
+ "description": "\uAC00\uC838\uC62C \uAC74\uC218 1~100",
15862
+ "maximum": 100,
15863
+ "minimum": 1,
15864
+ "title": "Limit",
15865
+ "type": "integer"
15866
+ },
15867
+ "query": {
15868
+ "anyOf": [
15869
+ {
15870
+ "maxLength": 200,
15871
+ "type": "string"
15872
+ },
15873
+ {
15874
+ "type": "null"
15875
+ }
15876
+ ],
15877
+ "default": null,
15878
+ "description": "\uC2AC\uB7EC\uADF8\xB7\uC81C\uBAA9 \uBD80\uBD84 \uC77C\uCE58(\uB300\uC18C\uBB38\uC790 \uBB34\uC2DC). \uC774\uB984\uC744 \uC54C \uB54C\uB9CC \uC4F4\uB2E4 \u2014 \uBE44\uC6B0\uBA74 \uCD5C\uADFC \uC218\uC815\uC21C \uC804\uCCB4",
15879
+ "title": "Query"
15880
+ }
15881
+ },
15882
+ "required": [],
15883
+ "additionalProperties": false
15884
+ },
15885
+ "annotations": {
15886
+ "readOnlyHint": true,
15887
+ "destructiveHint": false,
15888
+ "idempotentHint": true,
15889
+ "openWorldHint": false
15890
+ }
15891
+ },
15892
+ {
15893
+ "name": "democut_create_project",
15894
+ "description": "\uD504\uB85C\uC81D\uD2B8\uB97C \uB9CC\uB4E0\uB2E4 \u2014 \uACFC\uAE08\uC740 \uC5C6\uC9C0\uB9CC \uACC4\uC815\uC5D0 \uB0A8\uB294 **\uC4F0\uAE30** \uC791\uC5C5\uC774\uB2E4. \uBE44\uC728(16:9|9:16|1:1)\uC740 \uC0DD\uC131 \uD6C4 \uBC14\uAFC0 \uC218 \uC5C6\uB2E4. \uAC19\uC740 \uC81C\uBAA9\uC73C\uB85C \uB2E4\uC2DC \uBD80\uB974\uBA74 \uC0C8\uB85C \uB9CC\uB4E4\uC9C0 \uC54A\uACE0 \uAE30\uC874 \uAC83\uC744 \uB3CC\uB824\uC900\uB2E4(\uC7AC\uC2DC\uB3C4 \uC548\uC804). \u2605 \uBA3C\uC800 democut_list_projects \uB85C \uC4F8 \uB9CC\uD55C \uAC83\uC774 \uC774\uBBF8 \uC788\uB294\uC9C0 \uBCF4\uACE0 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uC5B4\uB290 \uAC83\uC5D0 \uB123\uC744\uC9C0 \uBB3C\uC5B4\uB77C \u2014 \uBB3B\uC9C0 \uC54A\uACE0 \uB9CC\uB4E4\uBA74 \uBE44\uC2B7\uD55C \uD504\uB85C\uC81D\uD2B8\uAC00 \uC313\uC778\uB2E4. \uB2E4\uC74C \uD638\uCD9C: democut_get_options, democut_upload_asset, democut_estimate_cuts.",
15895
+ "inputSchema": {
15896
+ "type": "object",
15897
+ "properties": {
15898
+ "title": {
15899
+ "maxLength": 200,
15900
+ "minLength": 1,
15901
+ "title": "Title",
15902
+ "type": "string"
15903
+ },
15904
+ "ratio": {
15905
+ "default": "16:9",
15906
+ "description": "\uC0DD\uC131 \uD6C4 \uACE0\uC815(R24)",
15907
+ "enum": [
15908
+ "16:9",
15909
+ "9:16",
15910
+ "1:1"
15911
+ ],
15912
+ "title": "Ratio",
15913
+ "type": "string"
15914
+ },
15915
+ "resolution": {
15916
+ "default": "1080p",
15917
+ "enum": [
15918
+ "720p",
15919
+ "1080p"
15920
+ ],
15921
+ "title": "Resolution",
15922
+ "type": "string"
15923
+ },
15924
+ "folder_id": {
15925
+ "anyOf": [
15926
+ {
15927
+ "type": "string"
15928
+ },
15929
+ {
15930
+ "type": "null"
15931
+ }
15932
+ ],
15933
+ "default": null,
15934
+ "title": "Folder Id"
15935
+ },
15936
+ "defaults": {
15937
+ "anyOf": [
15938
+ {
15939
+ "$ref": "#/$defs/ProjectDefaults"
15940
+ },
15941
+ {
15942
+ "type": "null"
15943
+ }
15944
+ ],
15945
+ "default": null
15946
+ }
15947
+ },
15948
+ "required": [
15949
+ "title"
15950
+ ],
15951
+ "additionalProperties": false,
15952
+ "$defs": {
15953
+ "Brand": {
15954
+ "additionalProperties": false,
15955
+ "properties": {
15956
+ "accent_hex": {
15957
+ "anyOf": [
15958
+ {
15959
+ "pattern": "^#[0-9A-Fa-f]{6}$",
15960
+ "type": "string"
15961
+ },
15962
+ {
15963
+ "type": "null"
15964
+ }
15965
+ ],
15966
+ "default": null,
15967
+ "title": "Accent Hex"
15968
+ },
15969
+ "heading_font": {
15970
+ "anyOf": [
15971
+ {
15972
+ "type": "string"
15973
+ },
15974
+ {
15975
+ "type": "null"
15976
+ }
15977
+ ],
15978
+ "default": null,
15979
+ "title": "Heading Font"
15980
+ },
15981
+ "body_font": {
15982
+ "anyOf": [
15983
+ {
15984
+ "type": "string"
15985
+ },
15986
+ {
15987
+ "type": "null"
15988
+ }
15989
+ ],
15990
+ "default": null,
15991
+ "title": "Body Font"
15992
+ },
15993
+ "logo_asset_id": {
15994
+ "anyOf": [
15995
+ {
15996
+ "type": "string"
15997
+ },
15998
+ {
15999
+ "type": "null"
16000
+ }
16001
+ ],
16002
+ "default": null,
16003
+ "title": "Logo Asset Id"
16004
+ }
16005
+ },
16006
+ "title": "Brand",
16007
+ "type": "object"
16008
+ },
16009
+ "ProjectDefaults": {
16010
+ "additionalProperties": false,
16011
+ "properties": {
16012
+ "voice_id": {
16013
+ "anyOf": [
16014
+ {
16015
+ "type": "string"
16016
+ },
16017
+ {
16018
+ "type": "null"
16019
+ }
16020
+ ],
16021
+ "default": null,
16022
+ "title": "Voice Id"
16023
+ },
16024
+ "character_id": {
16025
+ "anyOf": [
16026
+ {
16027
+ "type": "string"
16028
+ },
16029
+ {
16030
+ "type": "null"
16031
+ }
16032
+ ],
16033
+ "default": null,
16034
+ "title": "Character Id"
16035
+ },
16036
+ "subtitles": {
16037
+ "default": false,
16038
+ "title": "Subtitles",
16039
+ "type": "boolean"
16040
+ },
16041
+ "brand": {
16042
+ "$ref": "#/$defs/Brand"
16043
+ }
16044
+ },
16045
+ "title": "ProjectDefaults",
16046
+ "type": "object"
16047
+ }
16048
+ }
16049
+ },
16050
+ "annotations": {
16051
+ "readOnlyHint": false,
16052
+ "destructiveHint": false,
16053
+ "idempotentHint": false,
16054
+ "openWorldHint": false
16055
+ }
16056
+ },
16057
+ {
16058
+ "name": "democut_get_options",
16059
+ "description": "\uC774 \uD504\uB85C\uC81D\uD2B8\uC5D0\uC11C \uC4F8 \uC218 \uC788\uB294 \uBC29\uC2DD\xB7\uC9C4\uD589(host)\xB7\uD544\uC218 \uD544\uB4DC\xB7BYO \uD544\uB4DC\xB7\uBCF4\uC774\uC2A4\xB7\uC544\uBC14\uD0C0\xB7\uBC30\uACBD\xB7\uB2E8\uAC00\xB7\uC794\uC561. recommended_mode=ai_video. \uB2E8\uAC00\uB294 \uCC38\uACE0\uAC12\uC774\uB2E4 \u2014 \uCCAD\uAD6C\uB294 democut_estimate_cuts \uC758 \uAC12\uC73C\uB85C. \uB2E4\uC74C \uD638\uCD9C: democut_upload_asset, democut_estimate_cuts.",
16060
+ "inputSchema": {
16061
+ "type": "object",
16062
+ "properties": {
16063
+ "slug": {
16064
+ "type": "string",
16065
+ "description": "\uD504\uB85C\uC81D\uD2B8 \uC2AC\uB7EC\uADF8(list_projects)"
16066
+ }
16067
+ },
16068
+ "required": [
16069
+ "slug"
16070
+ ],
16071
+ "additionalProperties": false
16072
+ },
16073
+ "annotations": {
16074
+ "readOnlyHint": true,
16075
+ "destructiveHint": false,
16076
+ "idempotentHint": true,
16077
+ "openWorldHint": false
16078
+ }
16079
+ },
16080
+ {
16081
+ "name": "democut_upload_asset",
16082
+ "description": "\uC774\uBBF8\uC9C0\xB7\uB098\uB808\uC774\uC158 wav\xB7\uC6D0\uBCF8 \uC601\uC0C1\xB7tsx\xB7\uC644\uC131 \uCEF7 \uC601\uC0C1\uC744 \uC790\uC0B0\uC73C\uB85C \uC62C\uB9B0\uB2E4(0\uC6D0). \uC751\uB2F5\uC758 state \uAC00 ready \uC5EC\uC57C spec \uC5D0 \uC4F8 \uC218 \uC788\uB2E4. \uB2E4\uC74C \uD638\uCD9C: democut_estimate_cuts, democut_import_cut.",
16083
+ "inputSchema": {
16084
+ "type": "object",
16085
+ "properties": {
16086
+ "kind": {
16087
+ "enum": [
16088
+ "image",
16089
+ "video",
16090
+ "audio",
16091
+ "subtitle",
16092
+ "code",
16093
+ "archive"
16094
+ ],
16095
+ "title": "Kind",
16096
+ "type": "string"
16097
+ },
16098
+ "project_slug": {
16099
+ "anyOf": [
16100
+ {
16101
+ "type": "string"
16102
+ },
16103
+ {
16104
+ "type": "null"
16105
+ }
16106
+ ],
16107
+ "default": null,
16108
+ "title": "Project Slug"
16109
+ },
16110
+ "source_url": {
16111
+ "anyOf": [
16112
+ {
16113
+ "minLength": 1,
16114
+ "type": "string"
16115
+ },
16116
+ {
16117
+ "type": "null"
16118
+ }
16119
+ ],
16120
+ "default": null,
16121
+ "description": "\uC6D0\uACA9: https \uACF5\uAC1C \uC8FC\uC18C\uC5D0\uC11C \uAC00\uC838\uC628\uB2E4(202, \uC7A1). stdio \uB294 local_path",
16122
+ "title": "Source Url"
16123
+ },
16124
+ "filename": {
16125
+ "anyOf": [
16126
+ {
16127
+ "type": "string"
16128
+ },
16129
+ {
16130
+ "type": "null"
16131
+ }
16132
+ ],
16133
+ "default": null,
16134
+ "title": "Filename"
16135
+ },
16136
+ "local_path": {
16137
+ "type": "string",
16138
+ "description": "\uC62C\uB9B4 \uD30C\uC77C\uC758 \uB85C\uCEEC \uACBD\uB85C(stdio \uC804\uC6A9). source_url \uACFC \uB458 \uC911 \uD558\uB098"
16139
+ }
16140
+ },
16141
+ "required": [
16142
+ "kind"
16143
+ ],
16144
+ "additionalProperties": false,
16145
+ "oneOf": [
16146
+ {
16147
+ "required": [
16148
+ "local_path"
16149
+ ]
16150
+ },
16151
+ {
16152
+ "required": [
16153
+ "source_url"
16154
+ ]
16155
+ }
16156
+ ]
16157
+ },
16158
+ "annotations": {
16159
+ "readOnlyHint": false,
16160
+ "destructiveHint": false,
16161
+ "idempotentHint": false,
16162
+ "openWorldHint": false
16163
+ }
16164
+ },
16165
+ {
16166
+ "name": "democut_estimate_cuts",
16167
+ "description": "\uCEF7 N \uAC1C\uC758 \uACAC\uC801(0\uC6D0, \uBD80\uC791\uC6A9 \uC5C6\uC74C). \uC751\uB2F5\uC758 total_krw\xB7rate_version\xB7can_afford \uB97C \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uBCF4\uC5EC \uC8FC\uACE0 \uADF8\uB300\uB85C democut_make_cuts \uC5D0 \uB123\uC5B4\uB77C. BYO \uD544\uB4DC(image_asset_id\xB7narration_asset_id\xB7tsx\xB7source.asset_id)\uB97C \uC8FC\uBA74 \uADF8 \uD589\uC740 0\uC6D0\uC774\uB2E4. \uB2E4\uC74C \uD638\uCD9C: democut_make_cuts.",
16168
+ "inputSchema": {
16169
+ "type": "object",
16170
+ "properties": {
16171
+ "slug": {
16172
+ "type": "string",
16173
+ "description": "\uD504\uB85C\uC81D\uD2B8 \uC2AC\uB7EC\uADF8(list_projects)"
16174
+ },
16175
+ "items": {
16176
+ "items": {
16177
+ "$ref": "#/$defs/EstimateItem"
16178
+ },
16179
+ "maxItems": 50,
16180
+ "title": "Items",
16181
+ "type": "array"
16182
+ },
16183
+ "imports": {
16184
+ "items": {
16185
+ "$ref": "#/$defs/ImportEstimateItem"
16186
+ },
16187
+ "maxItems": 50,
16188
+ "title": "Imports",
16189
+ "type": "array"
16190
+ }
16191
+ },
16192
+ "required": [
16193
+ "slug"
16194
+ ],
16195
+ "additionalProperties": false,
16196
+ "$defs": {
16197
+ "AiVideoSpec": {
16198
+ "additionalProperties": false,
16199
+ "properties": {
16200
+ "dialogue": {
16201
+ "items": {
16202
+ "$ref": "#/$defs/DialogueLine"
16203
+ },
16204
+ "title": "Dialogue",
16205
+ "type": "array"
16206
+ },
16207
+ "narration_asset_id": {
16208
+ "anyOf": [
16209
+ {
16210
+ "minLength": 1,
16211
+ "type": "string"
16212
+ },
16213
+ {
16214
+ "type": "null"
16215
+ }
16216
+ ],
16217
+ "default": null,
16218
+ "description": "BYO: \uCEF7 \uC804\uCCB4 \uB098\uB808\uC774\uC158 wav \uD55C \uAC1C(\uB300\uC0AC\uAC00 \uC788\uC5B4\uC57C \uD55C\uB2E4 \u2014 \uC790\uB9C9\xB7\uD0C0\uC774\uBC0D\uC758 \uADFC\uAC70). \uC904\uBCC4 audio_asset_id \uC640 \uB3D9\uC2DC \uC9C0\uC815 422",
16219
+ "title": "Narration Asset Id"
16220
+ },
16221
+ "subtitles": {
16222
+ "default": false,
16223
+ "title": "Subtitles",
16224
+ "type": "boolean"
16225
+ },
16226
+ "host": {
16227
+ "anyOf": [
16228
+ {
16229
+ "type": "string"
16230
+ },
16231
+ {
16232
+ "type": "null"
16233
+ }
16234
+ ],
16235
+ "default": null,
16236
+ "description": "\uC9C4\uD589 \u2014 \uD5C8\uC6A9 \uC9D1\uD569\uC740 \uCE74\uD0C8\uB85C\uADF8(\uBC29\uC2DD\uBCC4). \uBAA8\uC158\uADF8\uB798\uD53D\uC740 \uD544\uC218",
16237
+ "title": "Host"
16238
+ },
16239
+ "voice_id": {
16240
+ "anyOf": [
16241
+ {
16242
+ "minLength": 1,
16243
+ "type": "string"
16244
+ },
16245
+ {
16246
+ "type": "null"
16247
+ }
16248
+ ],
16249
+ "default": null,
16250
+ "description": "host\u2208{voice,avatar} \uC774\uACE0 BYO \uC624\uB514\uC624\uAC00 \uC5C6\uC744 \uB54C \uD544\uC218",
16251
+ "title": "Voice Id"
16252
+ },
16253
+ "pip": {
16254
+ "anyOf": [
16255
+ {
16256
+ "$ref": "#/$defs/PipSpec"
16257
+ },
16258
+ {
16259
+ "type": "null"
16260
+ }
16261
+ ],
16262
+ "default": null,
16263
+ "description": "host=avatar \uC77C \uB54C \uD544\uC218"
16264
+ },
16265
+ "image_asset_id": {
16266
+ "anyOf": [
16267
+ {
16268
+ "minLength": 1,
16269
+ "type": "string"
16270
+ },
16271
+ {
16272
+ "type": "null"
16273
+ }
16274
+ ],
16275
+ "default": null,
16276
+ "description": "\uCEF7 \uC774\uBBF8\uC9C0\uB97C spec \uC5D0\uC11C \uC9C1\uC811 \uC9C0\uC815(\uC5D0\uC774\uC804\uD2B8 \uC6D0\uC0F7\uC6A9)",
16277
+ "title": "Image Asset Id"
16278
+ },
16279
+ "duration_s": {
16280
+ "default": 5,
16281
+ "maximum": 15,
16282
+ "minimum": 4,
16283
+ "title": "Duration S",
16284
+ "type": "number"
16285
+ },
16286
+ "mode": {
16287
+ "const": "ai_video",
16288
+ "default": "ai_video",
16289
+ "title": "Mode",
16290
+ "type": "string"
16291
+ },
16292
+ "image_prompt": {
16293
+ "maxLength": 4e3,
16294
+ "minLength": 1,
16295
+ "title": "Image Prompt",
16296
+ "type": "string"
16297
+ },
16298
+ "seed": {
16299
+ "anyOf": [
16300
+ {
16301
+ "minimum": 0,
16302
+ "type": "integer"
16303
+ },
16304
+ {
16305
+ "type": "null"
16306
+ }
16307
+ ],
16308
+ "default": null,
16309
+ "title": "Seed"
16310
+ },
16311
+ "image": {
16312
+ "anyOf": [
16313
+ {
16314
+ "discriminator": {
16315
+ "mapping": {
16316
+ "asset": "#/$defs/AssetImageRef",
16317
+ "from_cut": "#/$defs/FromCutImageRef",
16318
+ "from_version": "#/$defs/FromVersionImageRef"
16319
+ },
16320
+ "propertyName": "kind"
16321
+ },
16322
+ "oneOf": [
16323
+ {
16324
+ "$ref": "#/$defs/AssetImageRef"
16325
+ },
16326
+ {
16327
+ "$ref": "#/$defs/FromCutImageRef"
16328
+ },
16329
+ {
16330
+ "$ref": "#/$defs/FromVersionImageRef"
16331
+ }
16332
+ ]
16333
+ },
16334
+ {
16335
+ "type": "null"
16336
+ }
16337
+ ],
16338
+ "default": null,
16339
+ "description": "\uCCAB \uD504\uB808\uC784. \uC5C6\uC73C\uBA74 image_asset_id \u2192 \uCEF7 \uC774\uBBF8\uC9C0 \u2192 t2v \uC21C",
16340
+ "title": "Image"
16341
+ },
16342
+ "end_image": {
16343
+ "anyOf": [
16344
+ {
16345
+ "discriminator": {
16346
+ "mapping": {
16347
+ "asset": "#/$defs/AssetImageRef",
16348
+ "from_cut": "#/$defs/FromCutImageRef",
16349
+ "from_version": "#/$defs/FromVersionImageRef"
16350
+ },
16351
+ "propertyName": "kind"
16352
+ },
16353
+ "oneOf": [
16354
+ {
16355
+ "$ref": "#/$defs/AssetImageRef"
16356
+ },
16357
+ {
16358
+ "$ref": "#/$defs/FromCutImageRef"
16359
+ },
16360
+ {
16361
+ "$ref": "#/$defs/FromVersionImageRef"
16362
+ }
16363
+ ]
16364
+ },
16365
+ {
16366
+ "type": "null"
16367
+ }
16368
+ ],
16369
+ "default": null,
16370
+ "title": "End Image"
16371
+ }
16372
+ },
16373
+ "required": [
16374
+ "image_prompt"
16375
+ ],
16376
+ "title": "AiVideoSpec",
16377
+ "type": "object"
16378
+ },
16379
+ "AssetImageRef": {
16380
+ "additionalProperties": false,
16381
+ "properties": {
16382
+ "kind": {
16383
+ "const": "asset",
16384
+ "default": "asset",
16385
+ "title": "Kind",
16386
+ "type": "string"
16387
+ },
16388
+ "asset_id": {
16389
+ "minLength": 1,
16390
+ "title": "Asset Id",
16391
+ "type": "string"
16392
+ }
16393
+ },
16394
+ "required": [
16395
+ "asset_id"
16396
+ ],
16397
+ "title": "AssetImageRef",
16398
+ "type": "object"
16399
+ },
16400
+ "DialogueLine": {
16401
+ "additionalProperties": false,
16402
+ "properties": {
16403
+ "id": {
16404
+ "description": "\uC904 id ln_<6> \u2014 \uC81C\uC548 diff \uC758 \uB300\uC870 \uD0A4",
16405
+ "pattern": "^ln_[a-z0-9]{6}$",
16406
+ "title": "Id",
16407
+ "type": "string",
16408
+ "x-diff": "ignore"
16409
+ },
16410
+ "text": {
16411
+ "description": "\uBC1C\uD654 \uD14D\uC2A4\uD2B8(\uC22B\uC790\uB294 \uC11C\uBC84\uAC00 \uC77D\uB294 \uBC95\uC744 \uC815\uD55C\uB2E4 \u2014 \uD480\uC5B4\uC4F0\uC9C0 \uC54A\uB294\uB2E4)",
16412
+ "maxLength": 2e3,
16413
+ "minLength": 1,
16414
+ "title": "Text",
16415
+ "type": "string"
16416
+ },
16417
+ "subtitle_text": {
16418
+ "anyOf": [
16419
+ {
16420
+ "maxLength": 2e3,
16421
+ "type": "string"
16422
+ },
16423
+ {
16424
+ "type": "null"
16425
+ }
16426
+ ],
16427
+ "default": null,
16428
+ "description": "\uC790\uB9C9 \uD45C\uAE30(\uC6D0\uBB38 \uD45C\uAE30). \uC5C6\uC73C\uBA74 text",
16429
+ "title": "Subtitle Text"
16430
+ },
16431
+ "audio_asset_id": {
16432
+ "anyOf": [
16433
+ {
16434
+ "minLength": 1,
16435
+ "type": "string"
16436
+ },
16437
+ {
16438
+ "type": "null"
16439
+ }
16440
+ ],
16441
+ "default": null,
16442
+ "description": "BYO: \uC774 \uC904\uC758 \uB098\uB808\uC774\uC158 \uC624\uB514\uC624 \uC790\uC0B0 \u2014 TTS \uD589 0\uC6D0, \uC815\uB82C \uD589\uB9CC",
16443
+ "title": "Audio Asset Id"
16444
+ }
16445
+ },
16446
+ "required": [
16447
+ "id",
16448
+ "text"
16449
+ ],
16450
+ "title": "DialogueLine",
16451
+ "type": "object"
16452
+ },
16453
+ "EstimateItem": {
16454
+ "additionalProperties": false,
16455
+ "properties": {
16456
+ "cut_id": {
16457
+ "anyOf": [
16458
+ {
16459
+ "type": "string"
16460
+ },
16461
+ {
16462
+ "type": "null"
16463
+ }
16464
+ ],
16465
+ "default": null,
16466
+ "description": "\uC5C6\uC73C\uBA74 \uC0C8 \uCEF7(\uC11C\uBC84\uAC00 ULID \uB97C \uB9CC\uB4E0\uB2E4)",
16467
+ "title": "Cut Id"
16468
+ },
16469
+ "spec": {
16470
+ "discriminator": {
16471
+ "mapping": {
16472
+ "ai_video": "#/$defs/AiVideoSpec",
16473
+ "motion_graphic": "#/$defs/MotionGraphicSpec",
16474
+ "youtube_clip": "#/$defs/YoutubeClipSpec"
16475
+ },
16476
+ "propertyName": "mode"
16477
+ },
16478
+ "oneOf": [
16479
+ {
16480
+ "$ref": "#/$defs/MotionGraphicSpec"
16481
+ },
16482
+ {
16483
+ "$ref": "#/$defs/AiVideoSpec"
16484
+ },
16485
+ {
16486
+ "$ref": "#/$defs/YoutubeClipSpec"
16487
+ }
16488
+ ],
16489
+ "title": "Spec"
16490
+ }
16491
+ },
16492
+ "required": [
16493
+ "spec"
16494
+ ],
16495
+ "title": "EstimateItem",
16496
+ "type": "object"
16497
+ },
16498
+ "FromCutImageRef": {
16499
+ "additionalProperties": false,
16500
+ "properties": {
16501
+ "kind": {
16502
+ "const": "from_cut",
16503
+ "default": "from_cut",
16504
+ "title": "Kind",
16505
+ "type": "string"
16506
+ },
16507
+ "from_cut": {
16508
+ "description": "\uB2E4\uB978 \uCEF7\uC758 \uB300\uD45C \uBC84\uC804 \uD504\uB808\uC784(\uC5F0\uC18D\uC131 \uB3C4\uC6B0\uBBF8)",
16509
+ "minLength": 1,
16510
+ "title": "From Cut",
16511
+ "type": "string"
16512
+ },
16513
+ "frame": {
16514
+ "default": "last",
16515
+ "enum": [
16516
+ "first",
16517
+ "last"
16518
+ ],
16519
+ "title": "Frame",
16520
+ "type": "string"
16521
+ }
16522
+ },
16523
+ "required": [
16524
+ "from_cut"
16525
+ ],
16526
+ "title": "FromCutImageRef",
16527
+ "type": "object"
16528
+ },
16529
+ "FromVersionImageRef": {
16530
+ "additionalProperties": false,
16531
+ "properties": {
16532
+ "kind": {
16533
+ "const": "from_version",
16534
+ "default": "from_version",
16535
+ "title": "Kind",
16536
+ "type": "string"
16537
+ },
16538
+ "from_version": {
16539
+ "description": "\uC774 \uCEF7\uC758 \uD2B9\uC815 \uBC84\uC804 \uD504\uB808\uC784",
16540
+ "minimum": 1,
16541
+ "title": "From Version",
16542
+ "type": "integer"
16543
+ },
16544
+ "frame": {
16545
+ "default": "last",
16546
+ "enum": [
16547
+ "first",
16548
+ "last"
16549
+ ],
16550
+ "title": "Frame",
16551
+ "type": "string"
16552
+ }
16553
+ },
16554
+ "required": [
16555
+ "from_version"
16556
+ ],
16557
+ "title": "FromVersionImageRef",
16558
+ "type": "object"
16559
+ },
16560
+ "ImportEstimateItem": {
16561
+ "additionalProperties": false,
16562
+ "properties": {
16563
+ "cut_id": {
16564
+ "title": "Cut Id",
16565
+ "type": "string"
16566
+ },
16567
+ "video_asset_id": {
16568
+ "title": "Video Asset Id",
16569
+ "type": "string"
16570
+ },
16571
+ "transcribe": {
16572
+ "default": false,
16573
+ "title": "Transcribe",
16574
+ "type": "boolean"
16575
+ }
16576
+ },
16577
+ "required": [
16578
+ "cut_id",
16579
+ "video_asset_id"
16580
+ ],
16581
+ "title": "ImportEstimateItem",
16582
+ "type": "object"
16583
+ },
16584
+ "MotionGraphicSpec": {
16585
+ "additionalProperties": false,
16586
+ "properties": {
16587
+ "dialogue": {
16588
+ "items": {
16589
+ "$ref": "#/$defs/DialogueLine"
16590
+ },
16591
+ "title": "Dialogue",
16592
+ "type": "array"
16593
+ },
16594
+ "narration_asset_id": {
16595
+ "anyOf": [
16596
+ {
16597
+ "minLength": 1,
16598
+ "type": "string"
16599
+ },
16600
+ {
16601
+ "type": "null"
16602
+ }
16603
+ ],
16604
+ "default": null,
16605
+ "description": "BYO: \uCEF7 \uC804\uCCB4 \uB098\uB808\uC774\uC158 wav \uD55C \uAC1C(\uB300\uC0AC\uAC00 \uC788\uC5B4\uC57C \uD55C\uB2E4 \u2014 \uC790\uB9C9\xB7\uD0C0\uC774\uBC0D\uC758 \uADFC\uAC70). \uC904\uBCC4 audio_asset_id \uC640 \uB3D9\uC2DC \uC9C0\uC815 422",
16606
+ "title": "Narration Asset Id"
16607
+ },
16608
+ "subtitles": {
16609
+ "default": false,
16610
+ "title": "Subtitles",
16611
+ "type": "boolean"
16612
+ },
16613
+ "host": {
16614
+ "anyOf": [
16615
+ {
16616
+ "type": "string"
16617
+ },
16618
+ {
16619
+ "type": "null"
16620
+ }
16621
+ ],
16622
+ "default": null,
16623
+ "description": "\uC9C4\uD589 \u2014 \uD5C8\uC6A9 \uC9D1\uD569\uC740 \uCE74\uD0C8\uB85C\uADF8(\uBC29\uC2DD\uBCC4). \uBAA8\uC158\uADF8\uB798\uD53D\uC740 \uD544\uC218",
16624
+ "title": "Host"
16625
+ },
16626
+ "voice_id": {
16627
+ "anyOf": [
16628
+ {
16629
+ "minLength": 1,
16630
+ "type": "string"
16631
+ },
16632
+ {
16633
+ "type": "null"
16634
+ }
16635
+ ],
16636
+ "default": null,
16637
+ "description": "host\u2208{voice,avatar} \uC774\uACE0 BYO \uC624\uB514\uC624\uAC00 \uC5C6\uC744 \uB54C \uD544\uC218",
16638
+ "title": "Voice Id"
16639
+ },
16640
+ "pip": {
16641
+ "anyOf": [
16642
+ {
16643
+ "$ref": "#/$defs/PipSpec"
16644
+ },
16645
+ {
16646
+ "type": "null"
16647
+ }
16648
+ ],
16649
+ "default": null,
16650
+ "description": "host=avatar \uC77C \uB54C \uD544\uC218"
16651
+ },
16652
+ "image_asset_id": {
16653
+ "anyOf": [
16654
+ {
16655
+ "minLength": 1,
16656
+ "type": "string"
16657
+ },
16658
+ {
16659
+ "type": "null"
16660
+ }
16661
+ ],
16662
+ "default": null,
16663
+ "description": "\uCEF7 \uC774\uBBF8\uC9C0\uB97C spec \uC5D0\uC11C \uC9C1\uC811 \uC9C0\uC815(\uC5D0\uC774\uC804\uD2B8 \uC6D0\uC0F7\uC6A9)",
16664
+ "title": "Image Asset Id"
16665
+ },
16666
+ "duration_s": {
16667
+ "anyOf": [
16668
+ {
16669
+ "exclusiveMinimum": 0,
16670
+ "maximum": 600,
16671
+ "type": "number"
16672
+ },
16673
+ {
16674
+ "type": "null"
16675
+ }
16676
+ ],
16677
+ "default": null,
16678
+ "title": "Duration S"
16679
+ },
16680
+ "mode": {
16681
+ "const": "motion_graphic",
16682
+ "default": "motion_graphic",
16683
+ "title": "Mode",
16684
+ "type": "string"
16685
+ },
16686
+ "motion_prompt": {
16687
+ "anyOf": [
16688
+ {
16689
+ "maxLength": 4e3,
16690
+ "type": "string"
16691
+ },
16692
+ {
16693
+ "type": "null"
16694
+ }
16695
+ ],
16696
+ "default": null,
16697
+ "title": "Motion Prompt"
16698
+ },
16699
+ "tsx": {
16700
+ "anyOf": [
16701
+ {
16702
+ "maxLength": 2e5,
16703
+ "type": "string"
16704
+ },
16705
+ {
16706
+ "type": "null"
16707
+ }
16708
+ ],
16709
+ "default": null,
16710
+ "description": "BYO Remotion \uCF54\uB4DC(UTF-8 \u2264 200KB) \u2014 codegen \uD589 0\uC6D0, \uAC8C\uC774\uD2B8\uB294 codegen \uACFC \uB3D9\uAE09",
16711
+ "title": "Tsx"
16712
+ },
16713
+ "brand_override": {
16714
+ "anyOf": [
16715
+ {
16716
+ "additionalProperties": true,
16717
+ "type": "object"
16718
+ },
16719
+ {
16720
+ "type": "null"
16721
+ }
16722
+ ],
16723
+ "default": null,
16724
+ "title": "Brand Override"
16725
+ }
16726
+ },
16727
+ "title": "MotionGraphicSpec",
16728
+ "type": "object"
16729
+ },
16730
+ "PipSpec": {
16731
+ "additionalProperties": false,
16732
+ "properties": {
16733
+ "avatar_id": {
16734
+ "minLength": 1,
16735
+ "title": "Avatar Id",
16736
+ "type": "string"
16737
+ },
16738
+ "background_asset_id": {
16739
+ "description": "\uBC30\uACBD\uC740 \uC5B8\uC81C\uB098 \uC790\uC0B0(origin=background \uCE74\uD0C8\uB85C\uADF8 \uB610\uB294 \uC62C\uB9B0 \uC774\uBBF8\uC9C0)",
16740
+ "minLength": 1,
16741
+ "title": "Background Asset Id",
16742
+ "type": "string"
16743
+ },
16744
+ "position": {
16745
+ "default": "br",
16746
+ "enum": [
16747
+ "tl",
16748
+ "tr",
16749
+ "bl",
16750
+ "br"
16751
+ ],
16752
+ "title": "Position",
16753
+ "type": "string"
16754
+ },
16755
+ "size": {
16756
+ "default": "m",
16757
+ "enum": [
16758
+ "s",
16759
+ "m",
16760
+ "l"
16761
+ ],
16762
+ "title": "Size",
16763
+ "type": "string"
16764
+ }
16765
+ },
16766
+ "required": [
16767
+ "avatar_id",
16768
+ "background_asset_id"
16769
+ ],
16770
+ "title": "PipSpec",
16771
+ "type": "object"
16772
+ },
16773
+ "YoutubeAssetSource": {
16774
+ "additionalProperties": false,
16775
+ "properties": {
16776
+ "kind": {
16777
+ "const": "asset",
16778
+ "default": "asset",
16779
+ "title": "Kind",
16780
+ "type": "string"
16781
+ },
16782
+ "asset_id": {
16783
+ "description": "BYO: \uC62C\uB9B0 \uC6D0\uBCF8 \uC601\uC0C1 \u2014 \uB0B4\uB824\uBC1B\uAE30 \uD589 0\uC6D0",
16784
+ "minLength": 1,
16785
+ "title": "Asset Id",
16786
+ "type": "string"
16787
+ },
16788
+ "start_s": {
16789
+ "anyOf": [
16790
+ {
16791
+ "minimum": 0,
16792
+ "type": "number"
16793
+ },
16794
+ {
16795
+ "type": "null"
16796
+ }
16797
+ ],
16798
+ "default": null,
16799
+ "title": "Start S"
16800
+ },
16801
+ "end_s": {
16802
+ "anyOf": [
16803
+ {
16804
+ "exclusiveMinimum": 0,
16805
+ "type": "number"
16806
+ },
16807
+ {
16808
+ "type": "null"
16809
+ }
16810
+ ],
16811
+ "default": null,
16812
+ "title": "End S"
16813
+ }
16814
+ },
16815
+ "required": [
16816
+ "asset_id"
16817
+ ],
16818
+ "title": "YoutubeAssetSource",
16819
+ "type": "object"
16820
+ },
16821
+ "YoutubeClipSpec": {
16822
+ "additionalProperties": false,
16823
+ "properties": {
16824
+ "dialogue": {
16825
+ "items": {
16826
+ "$ref": "#/$defs/DialogueLine"
16827
+ },
16828
+ "title": "Dialogue",
16829
+ "type": "array"
16830
+ },
16831
+ "narration_asset_id": {
16832
+ "anyOf": [
16833
+ {
16834
+ "minLength": 1,
16835
+ "type": "string"
16836
+ },
16837
+ {
16838
+ "type": "null"
16839
+ }
16840
+ ],
16841
+ "default": null,
16842
+ "description": "BYO: \uCEF7 \uC804\uCCB4 \uB098\uB808\uC774\uC158 wav \uD55C \uAC1C(\uB300\uC0AC\uAC00 \uC788\uC5B4\uC57C \uD55C\uB2E4 \u2014 \uC790\uB9C9\xB7\uD0C0\uC774\uBC0D\uC758 \uADFC\uAC70). \uC904\uBCC4 audio_asset_id \uC640 \uB3D9\uC2DC \uC9C0\uC815 422",
16843
+ "title": "Narration Asset Id"
16844
+ },
16845
+ "subtitles": {
16846
+ "default": false,
16847
+ "title": "Subtitles",
16848
+ "type": "boolean"
16849
+ },
16850
+ "host": {
16851
+ "anyOf": [
16852
+ {
16853
+ "type": "string"
16854
+ },
16855
+ {
16856
+ "type": "null"
16857
+ }
16858
+ ],
16859
+ "default": null,
16860
+ "description": "\uC9C4\uD589 \u2014 \uD5C8\uC6A9 \uC9D1\uD569\uC740 \uCE74\uD0C8\uB85C\uADF8(\uBC29\uC2DD\uBCC4). \uBAA8\uC158\uADF8\uB798\uD53D\uC740 \uD544\uC218",
16861
+ "title": "Host"
16862
+ },
16863
+ "voice_id": {
16864
+ "anyOf": [
16865
+ {
16866
+ "minLength": 1,
16867
+ "type": "string"
16868
+ },
16869
+ {
16870
+ "type": "null"
16871
+ }
16872
+ ],
16873
+ "default": null,
16874
+ "description": "host\u2208{voice,avatar} \uC774\uACE0 BYO \uC624\uB514\uC624\uAC00 \uC5C6\uC744 \uB54C \uD544\uC218",
16875
+ "title": "Voice Id"
16876
+ },
16877
+ "pip": {
16878
+ "anyOf": [
16879
+ {
16880
+ "$ref": "#/$defs/PipSpec"
16881
+ },
16882
+ {
16883
+ "type": "null"
16884
+ }
16885
+ ],
16886
+ "default": null,
16887
+ "description": "host=avatar \uC77C \uB54C \uD544\uC218"
16888
+ },
16889
+ "image_asset_id": {
16890
+ "anyOf": [
16891
+ {
16892
+ "minLength": 1,
16893
+ "type": "string"
16894
+ },
16895
+ {
16896
+ "type": "null"
16897
+ }
16898
+ ],
16899
+ "default": null,
16900
+ "description": "\uCEF7 \uC774\uBBF8\uC9C0\uB97C spec \uC5D0\uC11C \uC9C1\uC811 \uC9C0\uC815(\uC5D0\uC774\uC804\uD2B8 \uC6D0\uC0F7\uC6A9)",
16901
+ "title": "Image Asset Id"
16902
+ },
16903
+ "duration_s": {
16904
+ "anyOf": [
16905
+ {
16906
+ "exclusiveMinimum": 0,
16907
+ "maximum": 600,
16908
+ "type": "number"
16909
+ },
16910
+ {
16911
+ "type": "null"
16912
+ }
16913
+ ],
16914
+ "default": null,
16915
+ "title": "Duration S"
16916
+ },
16917
+ "mode": {
16918
+ "const": "youtube_clip",
16919
+ "default": "youtube_clip",
16920
+ "title": "Mode",
16921
+ "type": "string"
16922
+ },
16923
+ "source": {
16924
+ "discriminator": {
16925
+ "mapping": {
16926
+ "asset": "#/$defs/YoutubeAssetSource",
16927
+ "url": "#/$defs/YoutubeUrlSource"
16928
+ },
16929
+ "propertyName": "kind"
16930
+ },
16931
+ "oneOf": [
16932
+ {
16933
+ "$ref": "#/$defs/YoutubeUrlSource"
16934
+ },
16935
+ {
16936
+ "$ref": "#/$defs/YoutubeAssetSource"
16937
+ }
16938
+ ],
16939
+ "title": "Source"
16940
+ }
16941
+ },
16942
+ "required": [
16943
+ "source"
16944
+ ],
16945
+ "title": "YoutubeClipSpec",
16946
+ "type": "object"
16947
+ },
16948
+ "YoutubeUrlSource": {
16949
+ "additionalProperties": false,
16950
+ "properties": {
16951
+ "kind": {
16952
+ "const": "url",
16953
+ "default": "url",
16954
+ "title": "Kind",
16955
+ "type": "string"
16956
+ },
16957
+ "url": {
16958
+ "description": "https://youtube.com|youtu.be \uACF5\uAC1C \uC8FC\uC18C\uB9CC",
16959
+ "minLength": 1,
16960
+ "title": "Url",
16961
+ "type": "string"
16962
+ },
16963
+ "start_s": {
16964
+ "minimum": 0,
16965
+ "title": "Start S",
16966
+ "type": "number"
16967
+ },
16968
+ "end_s": {
16969
+ "exclusiveMinimum": 0,
16970
+ "title": "End S",
16971
+ "type": "number"
16972
+ }
16973
+ },
16974
+ "required": [
16975
+ "url",
16976
+ "start_s",
16977
+ "end_s"
16978
+ ],
16979
+ "title": "YoutubeUrlSource",
16980
+ "type": "object"
16981
+ }
16982
+ }
16983
+ },
16984
+ "annotations": {
16985
+ "readOnlyHint": false,
16986
+ "destructiveHint": false,
16987
+ "idempotentHint": false,
16988
+ "openWorldHint": false
16989
+ }
16990
+ },
16991
+ {
16992
+ "name": "democut_make_cuts",
16993
+ "description": "\uCEF7 N \uAC1C\uB97C \uB9CC\uB4E0\uB2E4(\uCCAD\uAD6C \u2014 \uC81C\uCD9C \uC2DC \uC794\uC561\uC5D0\uC11C \uCC28\uAC10, \uC2E4\uD328 \uC2DC \uD658\uBD88). \uAE30\uBCF8 \uBC29\uC2DD\uC740 ai_video. items \uB294 all-or-nothing \uC774\uB2E4. \uB3C8\uC774 \uB098\uAC00\uB294 \uB3C4\uAD6C\uB2E4: estimate_cuts(\uB610\uB294 \uC9DD\uC774 \uB418\uB294 estimate \uB3C4\uAD6C)\uB85C \uAE08\uC561\uC744 \uBCF4\uACE0 \uC0AC\uC6A9\uC790 \uB3D9\uC758\uB97C \uBC1B\uC740 \uB4A4 confirmed_*_krw \uC640 rate_version \uC744 \uADF8\uB300\uB85C \uB123\uC5B4\uB77C. \uC5B4\uAE0B\uB098\uBA74 409 QUOTE_MISMATCH \uB85C \uC0C8 \uACAC\uC801\uC744 \uC900\uB2E4(\uACFC\uAE08 \uC5C6\uC74C). \uC2E4\uD328\uD574\uB3C4 \uC7AC\uC81C\uCD9C\uD558\uC9C0 \uB9C8\uB77C \u2014 \uAC19\uC740 spec \uC774 \uD65C\uC131\uC774\uBA74 \uC11C\uBC84\uAC00 \uAE30\uC874 \uC7A1\uC744 \uB3CC\uB824\uC900\uB2E4(deduplicated=true). \uC751\uB2F5\uC740 202 \u2014 \uACB0\uACFC\uB294 democut_wait_jobs \uB85C \uBC1B\uB294\uB2E4. \uB2E4\uC74C \uD638\uCD9C: democut_wait_jobs.",
16994
+ "inputSchema": {
16995
+ "type": "object",
16996
+ "properties": {
16997
+ "slug": {
16998
+ "type": "string",
16999
+ "description": "\uD504\uB85C\uC81D\uD2B8 \uC2AC\uB7EC\uADF8(list_projects)"
17000
+ },
17001
+ "items": {
17002
+ "items": {
17003
+ "$ref": "#/$defs/MakeItem"
17004
+ },
17005
+ "maxItems": 50,
17006
+ "minItems": 1,
17007
+ "title": "Items",
17008
+ "type": "array"
17009
+ },
17010
+ "confirmed_total_krw": {
17011
+ "description": "estimate_cuts \uC758 total_krw \uADF8\uB300\uB85C",
17012
+ "minimum": 0,
17013
+ "title": "Confirmed Total Krw",
17014
+ "type": "integer"
17015
+ },
17016
+ "rate_version": {
17017
+ "description": "estimate_cuts \uC758 rate_version \uADF8\uB300\uB85C",
17018
+ "minimum": 1,
17019
+ "title": "Rate Version",
17020
+ "type": "integer"
17021
+ }
17022
+ },
17023
+ "required": [
17024
+ "slug",
17025
+ "items",
17026
+ "confirmed_total_krw",
17027
+ "rate_version"
17028
+ ],
17029
+ "additionalProperties": false,
17030
+ "$defs": {
17031
+ "AiVideoSpec": {
17032
+ "additionalProperties": false,
17033
+ "properties": {
17034
+ "dialogue": {
17035
+ "items": {
17036
+ "$ref": "#/$defs/DialogueLine"
17037
+ },
17038
+ "title": "Dialogue",
17039
+ "type": "array"
17040
+ },
17041
+ "narration_asset_id": {
17042
+ "anyOf": [
17043
+ {
17044
+ "minLength": 1,
17045
+ "type": "string"
17046
+ },
17047
+ {
17048
+ "type": "null"
17049
+ }
17050
+ ],
17051
+ "default": null,
17052
+ "description": "BYO: \uCEF7 \uC804\uCCB4 \uB098\uB808\uC774\uC158 wav \uD55C \uAC1C(\uB300\uC0AC\uAC00 \uC788\uC5B4\uC57C \uD55C\uB2E4 \u2014 \uC790\uB9C9\xB7\uD0C0\uC774\uBC0D\uC758 \uADFC\uAC70). \uC904\uBCC4 audio_asset_id \uC640 \uB3D9\uC2DC \uC9C0\uC815 422",
17053
+ "title": "Narration Asset Id"
17054
+ },
17055
+ "subtitles": {
17056
+ "default": false,
17057
+ "title": "Subtitles",
17058
+ "type": "boolean"
17059
+ },
17060
+ "host": {
17061
+ "anyOf": [
17062
+ {
17063
+ "type": "string"
17064
+ },
17065
+ {
17066
+ "type": "null"
17067
+ }
17068
+ ],
17069
+ "default": null,
17070
+ "description": "\uC9C4\uD589 \u2014 \uD5C8\uC6A9 \uC9D1\uD569\uC740 \uCE74\uD0C8\uB85C\uADF8(\uBC29\uC2DD\uBCC4). \uBAA8\uC158\uADF8\uB798\uD53D\uC740 \uD544\uC218",
17071
+ "title": "Host"
17072
+ },
17073
+ "voice_id": {
17074
+ "anyOf": [
17075
+ {
17076
+ "minLength": 1,
17077
+ "type": "string"
17078
+ },
17079
+ {
17080
+ "type": "null"
17081
+ }
17082
+ ],
17083
+ "default": null,
17084
+ "description": "host\u2208{voice,avatar} \uC774\uACE0 BYO \uC624\uB514\uC624\uAC00 \uC5C6\uC744 \uB54C \uD544\uC218",
17085
+ "title": "Voice Id"
17086
+ },
17087
+ "pip": {
17088
+ "anyOf": [
17089
+ {
17090
+ "$ref": "#/$defs/PipSpec"
17091
+ },
17092
+ {
17093
+ "type": "null"
17094
+ }
17095
+ ],
17096
+ "default": null,
17097
+ "description": "host=avatar \uC77C \uB54C \uD544\uC218"
17098
+ },
17099
+ "image_asset_id": {
17100
+ "anyOf": [
17101
+ {
17102
+ "minLength": 1,
17103
+ "type": "string"
17104
+ },
17105
+ {
17106
+ "type": "null"
17107
+ }
17108
+ ],
17109
+ "default": null,
17110
+ "description": "\uCEF7 \uC774\uBBF8\uC9C0\uB97C spec \uC5D0\uC11C \uC9C1\uC811 \uC9C0\uC815(\uC5D0\uC774\uC804\uD2B8 \uC6D0\uC0F7\uC6A9)",
17111
+ "title": "Image Asset Id"
17112
+ },
17113
+ "duration_s": {
17114
+ "default": 5,
17115
+ "maximum": 15,
17116
+ "minimum": 4,
17117
+ "title": "Duration S",
17118
+ "type": "number"
17119
+ },
17120
+ "mode": {
17121
+ "const": "ai_video",
17122
+ "default": "ai_video",
17123
+ "title": "Mode",
17124
+ "type": "string"
17125
+ },
17126
+ "image_prompt": {
17127
+ "maxLength": 4e3,
17128
+ "minLength": 1,
17129
+ "title": "Image Prompt",
17130
+ "type": "string"
17131
+ },
17132
+ "seed": {
17133
+ "anyOf": [
17134
+ {
17135
+ "minimum": 0,
17136
+ "type": "integer"
17137
+ },
17138
+ {
17139
+ "type": "null"
17140
+ }
17141
+ ],
17142
+ "default": null,
17143
+ "title": "Seed"
17144
+ },
17145
+ "image": {
17146
+ "anyOf": [
17147
+ {
17148
+ "discriminator": {
17149
+ "mapping": {
17150
+ "asset": "#/$defs/AssetImageRef",
17151
+ "from_cut": "#/$defs/FromCutImageRef",
17152
+ "from_version": "#/$defs/FromVersionImageRef"
17153
+ },
17154
+ "propertyName": "kind"
17155
+ },
17156
+ "oneOf": [
17157
+ {
17158
+ "$ref": "#/$defs/AssetImageRef"
17159
+ },
17160
+ {
17161
+ "$ref": "#/$defs/FromCutImageRef"
17162
+ },
17163
+ {
17164
+ "$ref": "#/$defs/FromVersionImageRef"
17165
+ }
17166
+ ]
17167
+ },
17168
+ {
17169
+ "type": "null"
17170
+ }
17171
+ ],
17172
+ "default": null,
17173
+ "description": "\uCCAB \uD504\uB808\uC784. \uC5C6\uC73C\uBA74 image_asset_id \u2192 \uCEF7 \uC774\uBBF8\uC9C0 \u2192 t2v \uC21C",
17174
+ "title": "Image"
17175
+ },
17176
+ "end_image": {
17177
+ "anyOf": [
17178
+ {
17179
+ "discriminator": {
17180
+ "mapping": {
17181
+ "asset": "#/$defs/AssetImageRef",
17182
+ "from_cut": "#/$defs/FromCutImageRef",
17183
+ "from_version": "#/$defs/FromVersionImageRef"
17184
+ },
17185
+ "propertyName": "kind"
17186
+ },
17187
+ "oneOf": [
17188
+ {
17189
+ "$ref": "#/$defs/AssetImageRef"
17190
+ },
17191
+ {
17192
+ "$ref": "#/$defs/FromCutImageRef"
17193
+ },
17194
+ {
17195
+ "$ref": "#/$defs/FromVersionImageRef"
17196
+ }
17197
+ ]
17198
+ },
17199
+ {
17200
+ "type": "null"
17201
+ }
17202
+ ],
17203
+ "default": null,
17204
+ "title": "End Image"
17205
+ }
17206
+ },
17207
+ "required": [
17208
+ "image_prompt"
17209
+ ],
17210
+ "title": "AiVideoSpec",
17211
+ "type": "object"
17212
+ },
17213
+ "AssetImageRef": {
17214
+ "additionalProperties": false,
17215
+ "properties": {
17216
+ "kind": {
17217
+ "const": "asset",
17218
+ "default": "asset",
17219
+ "title": "Kind",
17220
+ "type": "string"
17221
+ },
17222
+ "asset_id": {
17223
+ "minLength": 1,
17224
+ "title": "Asset Id",
17225
+ "type": "string"
17226
+ }
17227
+ },
17228
+ "required": [
17229
+ "asset_id"
17230
+ ],
17231
+ "title": "AssetImageRef",
17232
+ "type": "object"
17233
+ },
17234
+ "DialogueLine": {
17235
+ "additionalProperties": false,
17236
+ "properties": {
17237
+ "id": {
17238
+ "description": "\uC904 id ln_<6> \u2014 \uC81C\uC548 diff \uC758 \uB300\uC870 \uD0A4",
17239
+ "pattern": "^ln_[a-z0-9]{6}$",
17240
+ "title": "Id",
17241
+ "type": "string",
17242
+ "x-diff": "ignore"
17243
+ },
17244
+ "text": {
17245
+ "description": "\uBC1C\uD654 \uD14D\uC2A4\uD2B8(\uC22B\uC790\uB294 \uC11C\uBC84\uAC00 \uC77D\uB294 \uBC95\uC744 \uC815\uD55C\uB2E4 \u2014 \uD480\uC5B4\uC4F0\uC9C0 \uC54A\uB294\uB2E4)",
17246
+ "maxLength": 2e3,
17247
+ "minLength": 1,
17248
+ "title": "Text",
17249
+ "type": "string"
17250
+ },
17251
+ "subtitle_text": {
17252
+ "anyOf": [
17253
+ {
17254
+ "maxLength": 2e3,
17255
+ "type": "string"
17256
+ },
17257
+ {
17258
+ "type": "null"
17259
+ }
17260
+ ],
17261
+ "default": null,
17262
+ "description": "\uC790\uB9C9 \uD45C\uAE30(\uC6D0\uBB38 \uD45C\uAE30). \uC5C6\uC73C\uBA74 text",
17263
+ "title": "Subtitle Text"
17264
+ },
17265
+ "audio_asset_id": {
17266
+ "anyOf": [
17267
+ {
17268
+ "minLength": 1,
17269
+ "type": "string"
17270
+ },
17271
+ {
17272
+ "type": "null"
17273
+ }
17274
+ ],
17275
+ "default": null,
17276
+ "description": "BYO: \uC774 \uC904\uC758 \uB098\uB808\uC774\uC158 \uC624\uB514\uC624 \uC790\uC0B0 \u2014 TTS \uD589 0\uC6D0, \uC815\uB82C \uD589\uB9CC",
17277
+ "title": "Audio Asset Id"
17278
+ }
17279
+ },
17280
+ "required": [
17281
+ "id",
17282
+ "text"
17283
+ ],
17284
+ "title": "DialogueLine",
17285
+ "type": "object"
17286
+ },
17287
+ "FromCutImageRef": {
17288
+ "additionalProperties": false,
17289
+ "properties": {
17290
+ "kind": {
17291
+ "const": "from_cut",
17292
+ "default": "from_cut",
17293
+ "title": "Kind",
17294
+ "type": "string"
17295
+ },
17296
+ "from_cut": {
17297
+ "description": "\uB2E4\uB978 \uCEF7\uC758 \uB300\uD45C \uBC84\uC804 \uD504\uB808\uC784(\uC5F0\uC18D\uC131 \uB3C4\uC6B0\uBBF8)",
17298
+ "minLength": 1,
17299
+ "title": "From Cut",
17300
+ "type": "string"
17301
+ },
17302
+ "frame": {
17303
+ "default": "last",
17304
+ "enum": [
17305
+ "first",
17306
+ "last"
17307
+ ],
17308
+ "title": "Frame",
17309
+ "type": "string"
17310
+ }
17311
+ },
17312
+ "required": [
17313
+ "from_cut"
17314
+ ],
17315
+ "title": "FromCutImageRef",
17316
+ "type": "object"
17317
+ },
17318
+ "FromVersionImageRef": {
17319
+ "additionalProperties": false,
17320
+ "properties": {
17321
+ "kind": {
17322
+ "const": "from_version",
17323
+ "default": "from_version",
17324
+ "title": "Kind",
17325
+ "type": "string"
17326
+ },
17327
+ "from_version": {
17328
+ "description": "\uC774 \uCEF7\uC758 \uD2B9\uC815 \uBC84\uC804 \uD504\uB808\uC784",
17329
+ "minimum": 1,
17330
+ "title": "From Version",
17331
+ "type": "integer"
17332
+ },
17333
+ "frame": {
17334
+ "default": "last",
17335
+ "enum": [
17336
+ "first",
17337
+ "last"
17338
+ ],
17339
+ "title": "Frame",
17340
+ "type": "string"
17341
+ }
17342
+ },
17343
+ "required": [
17344
+ "from_version"
17345
+ ],
17346
+ "title": "FromVersionImageRef",
17347
+ "type": "object"
17348
+ },
17349
+ "MakeItem": {
17350
+ "additionalProperties": false,
17351
+ "properties": {
17352
+ "cut_id": {
17353
+ "anyOf": [
17354
+ {
17355
+ "type": "string"
17356
+ },
17357
+ {
17358
+ "type": "null"
17359
+ }
17360
+ ],
17361
+ "default": null,
17362
+ "title": "Cut Id"
17363
+ },
17364
+ "spec": {
17365
+ "discriminator": {
17366
+ "mapping": {
17367
+ "ai_video": "#/$defs/AiVideoSpec",
17368
+ "motion_graphic": "#/$defs/MotionGraphicSpec",
17369
+ "youtube_clip": "#/$defs/YoutubeClipSpec"
17370
+ },
17371
+ "propertyName": "mode"
17372
+ },
17373
+ "oneOf": [
17374
+ {
17375
+ "$ref": "#/$defs/MotionGraphicSpec"
17376
+ },
17377
+ {
17378
+ "$ref": "#/$defs/AiVideoSpec"
17379
+ },
17380
+ {
17381
+ "$ref": "#/$defs/YoutubeClipSpec"
17382
+ }
17383
+ ],
17384
+ "title": "Spec"
17385
+ },
17386
+ "note": {
17387
+ "anyOf": [
17388
+ {
17389
+ "maxLength": 500,
17390
+ "type": "string"
17391
+ },
17392
+ {
17393
+ "type": "null"
17394
+ }
17395
+ ],
17396
+ "default": null,
17397
+ "title": "Note"
17398
+ }
17399
+ },
17400
+ "required": [
17401
+ "spec"
17402
+ ],
17403
+ "title": "MakeItem",
17404
+ "type": "object"
17405
+ },
17406
+ "MotionGraphicSpec": {
17407
+ "additionalProperties": false,
17408
+ "properties": {
17409
+ "dialogue": {
17410
+ "items": {
17411
+ "$ref": "#/$defs/DialogueLine"
17412
+ },
17413
+ "title": "Dialogue",
17414
+ "type": "array"
17415
+ },
17416
+ "narration_asset_id": {
17417
+ "anyOf": [
17418
+ {
17419
+ "minLength": 1,
17420
+ "type": "string"
17421
+ },
17422
+ {
17423
+ "type": "null"
17424
+ }
17425
+ ],
17426
+ "default": null,
17427
+ "description": "BYO: \uCEF7 \uC804\uCCB4 \uB098\uB808\uC774\uC158 wav \uD55C \uAC1C(\uB300\uC0AC\uAC00 \uC788\uC5B4\uC57C \uD55C\uB2E4 \u2014 \uC790\uB9C9\xB7\uD0C0\uC774\uBC0D\uC758 \uADFC\uAC70). \uC904\uBCC4 audio_asset_id \uC640 \uB3D9\uC2DC \uC9C0\uC815 422",
17428
+ "title": "Narration Asset Id"
17429
+ },
17430
+ "subtitles": {
17431
+ "default": false,
17432
+ "title": "Subtitles",
17433
+ "type": "boolean"
17434
+ },
17435
+ "host": {
17436
+ "anyOf": [
17437
+ {
17438
+ "type": "string"
17439
+ },
17440
+ {
17441
+ "type": "null"
17442
+ }
17443
+ ],
17444
+ "default": null,
17445
+ "description": "\uC9C4\uD589 \u2014 \uD5C8\uC6A9 \uC9D1\uD569\uC740 \uCE74\uD0C8\uB85C\uADF8(\uBC29\uC2DD\uBCC4). \uBAA8\uC158\uADF8\uB798\uD53D\uC740 \uD544\uC218",
17446
+ "title": "Host"
17447
+ },
17448
+ "voice_id": {
17449
+ "anyOf": [
17450
+ {
17451
+ "minLength": 1,
17452
+ "type": "string"
17453
+ },
17454
+ {
17455
+ "type": "null"
17456
+ }
17457
+ ],
17458
+ "default": null,
17459
+ "description": "host\u2208{voice,avatar} \uC774\uACE0 BYO \uC624\uB514\uC624\uAC00 \uC5C6\uC744 \uB54C \uD544\uC218",
17460
+ "title": "Voice Id"
17461
+ },
17462
+ "pip": {
17463
+ "anyOf": [
17464
+ {
17465
+ "$ref": "#/$defs/PipSpec"
17466
+ },
17467
+ {
17468
+ "type": "null"
17469
+ }
17470
+ ],
17471
+ "default": null,
17472
+ "description": "host=avatar \uC77C \uB54C \uD544\uC218"
17473
+ },
17474
+ "image_asset_id": {
17475
+ "anyOf": [
17476
+ {
17477
+ "minLength": 1,
17478
+ "type": "string"
17479
+ },
17480
+ {
17481
+ "type": "null"
17482
+ }
17483
+ ],
17484
+ "default": null,
17485
+ "description": "\uCEF7 \uC774\uBBF8\uC9C0\uB97C spec \uC5D0\uC11C \uC9C1\uC811 \uC9C0\uC815(\uC5D0\uC774\uC804\uD2B8 \uC6D0\uC0F7\uC6A9)",
17486
+ "title": "Image Asset Id"
17487
+ },
17488
+ "duration_s": {
17489
+ "anyOf": [
17490
+ {
17491
+ "exclusiveMinimum": 0,
17492
+ "maximum": 600,
17493
+ "type": "number"
17494
+ },
17495
+ {
17496
+ "type": "null"
17497
+ }
17498
+ ],
17499
+ "default": null,
17500
+ "title": "Duration S"
17501
+ },
17502
+ "mode": {
17503
+ "const": "motion_graphic",
17504
+ "default": "motion_graphic",
17505
+ "title": "Mode",
17506
+ "type": "string"
17507
+ },
17508
+ "motion_prompt": {
17509
+ "anyOf": [
17510
+ {
17511
+ "maxLength": 4e3,
17512
+ "type": "string"
17513
+ },
17514
+ {
17515
+ "type": "null"
17516
+ }
17517
+ ],
17518
+ "default": null,
17519
+ "title": "Motion Prompt"
17520
+ },
17521
+ "tsx": {
17522
+ "anyOf": [
17523
+ {
17524
+ "maxLength": 2e5,
17525
+ "type": "string"
17526
+ },
17527
+ {
17528
+ "type": "null"
17529
+ }
17530
+ ],
17531
+ "default": null,
17532
+ "description": "BYO Remotion \uCF54\uB4DC(UTF-8 \u2264 200KB) \u2014 codegen \uD589 0\uC6D0, \uAC8C\uC774\uD2B8\uB294 codegen \uACFC \uB3D9\uAE09",
17533
+ "title": "Tsx"
17534
+ },
17535
+ "brand_override": {
17536
+ "anyOf": [
17537
+ {
17538
+ "additionalProperties": true,
17539
+ "type": "object"
17540
+ },
17541
+ {
17542
+ "type": "null"
17543
+ }
17544
+ ],
17545
+ "default": null,
17546
+ "title": "Brand Override"
17547
+ }
17548
+ },
17549
+ "title": "MotionGraphicSpec",
17550
+ "type": "object"
17551
+ },
17552
+ "PipSpec": {
17553
+ "additionalProperties": false,
17554
+ "properties": {
17555
+ "avatar_id": {
17556
+ "minLength": 1,
17557
+ "title": "Avatar Id",
17558
+ "type": "string"
17559
+ },
17560
+ "background_asset_id": {
17561
+ "description": "\uBC30\uACBD\uC740 \uC5B8\uC81C\uB098 \uC790\uC0B0(origin=background \uCE74\uD0C8\uB85C\uADF8 \uB610\uB294 \uC62C\uB9B0 \uC774\uBBF8\uC9C0)",
17562
+ "minLength": 1,
17563
+ "title": "Background Asset Id",
17564
+ "type": "string"
17565
+ },
17566
+ "position": {
17567
+ "default": "br",
17568
+ "enum": [
17569
+ "tl",
17570
+ "tr",
17571
+ "bl",
17572
+ "br"
17573
+ ],
17574
+ "title": "Position",
17575
+ "type": "string"
17576
+ },
17577
+ "size": {
17578
+ "default": "m",
17579
+ "enum": [
17580
+ "s",
17581
+ "m",
17582
+ "l"
17583
+ ],
17584
+ "title": "Size",
17585
+ "type": "string"
17586
+ }
17587
+ },
17588
+ "required": [
17589
+ "avatar_id",
17590
+ "background_asset_id"
17591
+ ],
17592
+ "title": "PipSpec",
17593
+ "type": "object"
17594
+ },
17595
+ "YoutubeAssetSource": {
17596
+ "additionalProperties": false,
17597
+ "properties": {
17598
+ "kind": {
17599
+ "const": "asset",
17600
+ "default": "asset",
17601
+ "title": "Kind",
17602
+ "type": "string"
17603
+ },
17604
+ "asset_id": {
17605
+ "description": "BYO: \uC62C\uB9B0 \uC6D0\uBCF8 \uC601\uC0C1 \u2014 \uB0B4\uB824\uBC1B\uAE30 \uD589 0\uC6D0",
17606
+ "minLength": 1,
17607
+ "title": "Asset Id",
17608
+ "type": "string"
17609
+ },
17610
+ "start_s": {
17611
+ "anyOf": [
17612
+ {
17613
+ "minimum": 0,
17614
+ "type": "number"
17615
+ },
17616
+ {
17617
+ "type": "null"
17618
+ }
17619
+ ],
17620
+ "default": null,
17621
+ "title": "Start S"
17622
+ },
17623
+ "end_s": {
17624
+ "anyOf": [
17625
+ {
17626
+ "exclusiveMinimum": 0,
17627
+ "type": "number"
17628
+ },
17629
+ {
17630
+ "type": "null"
17631
+ }
17632
+ ],
17633
+ "default": null,
17634
+ "title": "End S"
17635
+ }
17636
+ },
17637
+ "required": [
17638
+ "asset_id"
17639
+ ],
17640
+ "title": "YoutubeAssetSource",
17641
+ "type": "object"
17642
+ },
17643
+ "YoutubeClipSpec": {
17644
+ "additionalProperties": false,
17645
+ "properties": {
17646
+ "dialogue": {
17647
+ "items": {
17648
+ "$ref": "#/$defs/DialogueLine"
17649
+ },
17650
+ "title": "Dialogue",
17651
+ "type": "array"
17652
+ },
17653
+ "narration_asset_id": {
17654
+ "anyOf": [
17655
+ {
17656
+ "minLength": 1,
17657
+ "type": "string"
17658
+ },
17659
+ {
17660
+ "type": "null"
17661
+ }
17662
+ ],
17663
+ "default": null,
17664
+ "description": "BYO: \uCEF7 \uC804\uCCB4 \uB098\uB808\uC774\uC158 wav \uD55C \uAC1C(\uB300\uC0AC\uAC00 \uC788\uC5B4\uC57C \uD55C\uB2E4 \u2014 \uC790\uB9C9\xB7\uD0C0\uC774\uBC0D\uC758 \uADFC\uAC70). \uC904\uBCC4 audio_asset_id \uC640 \uB3D9\uC2DC \uC9C0\uC815 422",
17665
+ "title": "Narration Asset Id"
17666
+ },
17667
+ "subtitles": {
17668
+ "default": false,
17669
+ "title": "Subtitles",
17670
+ "type": "boolean"
17671
+ },
17672
+ "host": {
17673
+ "anyOf": [
17674
+ {
17675
+ "type": "string"
17676
+ },
17677
+ {
17678
+ "type": "null"
17679
+ }
17680
+ ],
17681
+ "default": null,
17682
+ "description": "\uC9C4\uD589 \u2014 \uD5C8\uC6A9 \uC9D1\uD569\uC740 \uCE74\uD0C8\uB85C\uADF8(\uBC29\uC2DD\uBCC4). \uBAA8\uC158\uADF8\uB798\uD53D\uC740 \uD544\uC218",
17683
+ "title": "Host"
17684
+ },
17685
+ "voice_id": {
17686
+ "anyOf": [
17687
+ {
17688
+ "minLength": 1,
17689
+ "type": "string"
17690
+ },
17691
+ {
17692
+ "type": "null"
17693
+ }
17694
+ ],
17695
+ "default": null,
17696
+ "description": "host\u2208{voice,avatar} \uC774\uACE0 BYO \uC624\uB514\uC624\uAC00 \uC5C6\uC744 \uB54C \uD544\uC218",
17697
+ "title": "Voice Id"
17698
+ },
17699
+ "pip": {
17700
+ "anyOf": [
17701
+ {
17702
+ "$ref": "#/$defs/PipSpec"
17703
+ },
17704
+ {
17705
+ "type": "null"
17706
+ }
17707
+ ],
17708
+ "default": null,
17709
+ "description": "host=avatar \uC77C \uB54C \uD544\uC218"
17710
+ },
17711
+ "image_asset_id": {
17712
+ "anyOf": [
17713
+ {
17714
+ "minLength": 1,
17715
+ "type": "string"
17716
+ },
17717
+ {
17718
+ "type": "null"
17719
+ }
17720
+ ],
17721
+ "default": null,
17722
+ "description": "\uCEF7 \uC774\uBBF8\uC9C0\uB97C spec \uC5D0\uC11C \uC9C1\uC811 \uC9C0\uC815(\uC5D0\uC774\uC804\uD2B8 \uC6D0\uC0F7\uC6A9)",
17723
+ "title": "Image Asset Id"
17724
+ },
17725
+ "duration_s": {
17726
+ "anyOf": [
17727
+ {
17728
+ "exclusiveMinimum": 0,
17729
+ "maximum": 600,
17730
+ "type": "number"
17731
+ },
17732
+ {
17733
+ "type": "null"
17734
+ }
17735
+ ],
17736
+ "default": null,
17737
+ "title": "Duration S"
17738
+ },
17739
+ "mode": {
17740
+ "const": "youtube_clip",
17741
+ "default": "youtube_clip",
17742
+ "title": "Mode",
17743
+ "type": "string"
17744
+ },
17745
+ "source": {
17746
+ "discriminator": {
17747
+ "mapping": {
17748
+ "asset": "#/$defs/YoutubeAssetSource",
17749
+ "url": "#/$defs/YoutubeUrlSource"
17750
+ },
17751
+ "propertyName": "kind"
17752
+ },
17753
+ "oneOf": [
17754
+ {
17755
+ "$ref": "#/$defs/YoutubeUrlSource"
17756
+ },
17757
+ {
17758
+ "$ref": "#/$defs/YoutubeAssetSource"
17759
+ }
17760
+ ],
17761
+ "title": "Source"
17762
+ }
17763
+ },
17764
+ "required": [
17765
+ "source"
17766
+ ],
17767
+ "title": "YoutubeClipSpec",
17768
+ "type": "object"
17769
+ },
17770
+ "YoutubeUrlSource": {
17771
+ "additionalProperties": false,
17772
+ "properties": {
17773
+ "kind": {
17774
+ "const": "url",
17775
+ "default": "url",
17776
+ "title": "Kind",
17777
+ "type": "string"
17778
+ },
17779
+ "url": {
17780
+ "description": "https://youtube.com|youtu.be \uACF5\uAC1C \uC8FC\uC18C\uB9CC",
17781
+ "minLength": 1,
17782
+ "title": "Url",
17783
+ "type": "string"
17784
+ },
17785
+ "start_s": {
17786
+ "minimum": 0,
17787
+ "title": "Start S",
17788
+ "type": "number"
17789
+ },
17790
+ "end_s": {
17791
+ "exclusiveMinimum": 0,
17792
+ "title": "End S",
17793
+ "type": "number"
17794
+ }
17795
+ },
17796
+ "required": [
17797
+ "url",
17798
+ "start_s",
17799
+ "end_s"
17800
+ ],
17801
+ "title": "YoutubeUrlSource",
17802
+ "type": "object"
17803
+ }
17804
+ }
17805
+ },
17806
+ "annotations": {
17807
+ "readOnlyHint": false,
17808
+ "destructiveHint": false,
17809
+ "idempotentHint": false,
17810
+ "openWorldHint": false
17811
+ }
17812
+ },
17813
+ {
17814
+ "name": "democut_wait_jobs",
17815
+ "description": "\uC7A1\uC774 \uB05D\uB0A0 \uB54C\uAE4C\uC9C0 \uAE30\uB2E4\uB9B0\uB2E4(\uC694\uCCAD\uD55C \uBAA8\uB4E0 id \uC758 \uC2A4\uB0C5\uC0F7 + all_done). all_done=false \uBA74 \uC544\uC9C1 \uC548 \uB05D\uB09C id \uB9CC \uAC19\uC740 \uB3C4\uAD6C\uB85C \uB2E4\uC2DC \uBD80\uB978\uB2E4. \uB05D\uB09C \uC7A1\uC758 result.download \uB85C \uBC14\uB85C \uB0B4\uB824\uBC1B\uB294\uB2E4. job_ids\xB7batch_id\xB7project \uC911 \uD558\uB098\uB294 \uC788\uC5B4\uC57C \uD55C\uB2E4. \uB2E4\uC74C \uD638\uCD9C: democut_download, democut_get_cut.",
17816
+ "inputSchema": {
17817
+ "type": "object",
17818
+ "properties": {
17819
+ "job_ids": {
17820
+ "anyOf": [
17821
+ {
17822
+ "items": {
17823
+ "type": "string"
17824
+ },
17825
+ "maxItems": 100,
17826
+ "minItems": 1,
17827
+ "type": "array"
17828
+ },
17829
+ {
17830
+ "type": "null"
17831
+ }
17832
+ ],
17833
+ "default": null,
17834
+ "title": "Job Ids"
17835
+ },
17836
+ "batch_id": {
17837
+ "anyOf": [
17838
+ {
17839
+ "type": "string"
17840
+ },
17841
+ {
17842
+ "type": "null"
17843
+ }
17844
+ ],
17845
+ "default": null,
17846
+ "title": "Batch Id"
17847
+ },
17848
+ "project": {
17849
+ "anyOf": [
17850
+ {
17851
+ "type": "string"
17852
+ },
17853
+ {
17854
+ "type": "null"
17855
+ }
17856
+ ],
17857
+ "default": null,
17858
+ "title": "Project"
17859
+ },
17860
+ "state": {
17861
+ "anyOf": [
17862
+ {
17863
+ "enum": [
17864
+ "queued",
17865
+ "running",
17866
+ "done",
17867
+ "error",
17868
+ "cancelled"
17869
+ ],
17870
+ "type": "string"
17871
+ },
17872
+ {
17873
+ "type": "null"
17874
+ }
17875
+ ],
17876
+ "default": null,
17877
+ "title": "State"
17878
+ },
17879
+ "timeout_s": {
17880
+ "default": 600,
17881
+ "description": "0 = \uAE30\uB2E4\uB9AC\uC9C0 \uC54A\uACE0 \uC9C0\uAE08 \uC0C1\uD0DC\uB9CC. all_done=false \uBA74 \uB0A8\uC740 id \uB9CC \uB2E4\uC2DC \uBD80\uB978\uB2E4",
17882
+ "maximum": 600,
17883
+ "minimum": 0,
17884
+ "title": "Timeout S",
17885
+ "type": "integer"
17886
+ }
17887
+ },
17888
+ "required": [],
17889
+ "additionalProperties": false,
17890
+ "anyOf": [
17891
+ {
17892
+ "required": [
17893
+ "job_ids"
17894
+ ]
17895
+ },
17896
+ {
17897
+ "required": [
17898
+ "batch_id"
17899
+ ]
17900
+ },
17901
+ {
17902
+ "required": [
17903
+ "project"
17904
+ ]
17905
+ }
17906
+ ]
17907
+ },
17908
+ "annotations": {
17909
+ "readOnlyHint": true,
17910
+ "destructiveHint": false,
17911
+ "idempotentHint": true,
17912
+ "openWorldHint": false
17913
+ }
17914
+ },
17915
+ {
17916
+ "name": "democut_get_cut",
17917
+ "description": "\uCEF7 \uD558\uB098 \u2014 \uBC84\uC804 \uBAA9\uB85D\uACFC \uBC84\uC804\uB9C8\uB2E4 download{video,audio,subtitle,still} \uC11C\uBA85 URL. \uD0C0\uC774\uBC0D\uC740 versions[].timing \uC5D0 \uC778\uB77C\uC778. current_version \uC774 \uC5C6\uC73C\uBA74 \uACC4\uD68D \uCEF7\uC774\uB2E4. \uB2E4\uC74C \uD638\uCD9C: democut_download, democut_make_cuts.",
17918
+ "inputSchema": {
17919
+ "type": "object",
17920
+ "properties": {
17921
+ "slug": {
17922
+ "type": "string",
17923
+ "description": "\uD504\uB85C\uC81D\uD2B8 \uC2AC\uB7EC\uADF8(list_projects)"
17924
+ },
17925
+ "cut_id": {
17926
+ "type": "string",
17927
+ "description": "\uCEF7 id(cut_\u2026)"
17928
+ }
17929
+ },
17930
+ "required": [
17931
+ "slug",
17932
+ "cut_id"
17933
+ ],
17934
+ "additionalProperties": false
17935
+ },
17936
+ "annotations": {
17937
+ "readOnlyHint": true,
17938
+ "destructiveHint": false,
17939
+ "idempotentHint": true,
17940
+ "openWorldHint": false
17941
+ }
17942
+ },
17943
+ {
17944
+ "name": "democut_download",
17945
+ "description": "\uBC84\uC804 \uC0B0\uCD9C\uBB3C \uD558\uB098\uB97C \uB0B4\uB824\uBC1B\uB294\uB2E4(kind: video|audio|subtitle|still). stdio \uB294 output_path \uC5D0 \uC800\uC7A5, \uC6D0\uACA9\uC740 \uC11C\uBA85 URL(1\uC2DC\uAC04).",
17946
+ "inputSchema": {
17947
+ "type": "object",
17948
+ "properties": {
17949
+ "slug": {
17950
+ "type": "string",
17951
+ "description": "\uD504\uB85C\uC81D\uD2B8 \uC2AC\uB7EC\uADF8(list_projects)"
17952
+ },
17953
+ "cut_id": {
17954
+ "type": "string",
17955
+ "description": "\uCEF7 id(cut_\u2026)"
17956
+ },
17957
+ "version": {
17958
+ "type": "string",
17959
+ "description": "\uBC84\uC804 \uBC88\uD638(1\uBD80\uD130)"
17960
+ },
17961
+ "kind": {
17962
+ "default": "video",
17963
+ "enum": [
17964
+ "video",
17965
+ "audio",
17966
+ "subtitle",
17967
+ "still"
17968
+ ],
17969
+ "title": "Kind",
17970
+ "type": "string"
17971
+ },
17972
+ "output_path": {
17973
+ "type": "string",
17974
+ "description": "\uC800\uC7A5\uD560 \uB85C\uCEEC \uACBD\uB85C(stdio \uC804\uC6A9). \uC5C6\uC73C\uBA74 URL \uB9CC \uB3CC\uB824\uC900\uB2E4"
17975
+ }
17976
+ },
17977
+ "required": [
17978
+ "slug",
17979
+ "cut_id",
17980
+ "version"
17981
+ ],
17982
+ "additionalProperties": false
17983
+ },
17984
+ "annotations": {
17985
+ "readOnlyHint": true,
17986
+ "destructiveHint": false,
17987
+ "idempotentHint": true,
17988
+ "openWorldHint": false
17989
+ }
17990
+ },
17991
+ {
17992
+ "name": "democut_import_cut",
17993
+ "description": "\uC0AC\uC6A9\uC790\uAC00 \uB9CC\uB4E0 \uC644\uC131 \uCEF7 \uC601\uC0C1\uC744 0\uC6D0 \uBC84\uC804\uC73C\uB85C \uB4F1\uB85D\uD55C\uB2E4(S3: \uC7AC\uB8CC\uB294 \uB0B4\uAC00, \uC870\uB9BD\uC740 DemoCut). transcribe=true \uBA74 \uC790\uB9C9 \uC804\uC0AC \uD589\uB9CC \uCCAD\uAD6C(democut_estimate_cuts \uC758 imports \uB85C \uBA3C\uC800 \uBCF8\uB2E4). \uC751\uB2F5\uC740 202 \u2014 \uACB0\uACFC\uB294 democut_wait_jobs \uB85C \uBC1B\uB294\uB2E4. \uB2E4\uC74C \uD638\uCD9C: democut_wait_jobs, democut_get_cut.",
17994
+ "inputSchema": {
17995
+ "type": "object",
17996
+ "properties": {
17997
+ "slug": {
17998
+ "type": "string",
17999
+ "description": "\uD504\uB85C\uC81D\uD2B8 \uC2AC\uB7EC\uADF8(list_projects)"
18000
+ },
18001
+ "cut_id": {
18002
+ "type": "string",
18003
+ "description": "\uCEF7 id(cut_\u2026)"
18004
+ },
18005
+ "video_asset_id": {
18006
+ "title": "Video Asset Id",
18007
+ "type": "string"
18008
+ },
18009
+ "dialogue": {
18010
+ "anyOf": [
18011
+ {
18012
+ "items": {
18013
+ "additionalProperties": true,
18014
+ "type": "object"
18015
+ },
18016
+ "type": "array"
18017
+ },
18018
+ {
18019
+ "type": "null"
18020
+ }
18021
+ ],
18022
+ "default": null,
18023
+ "description": "[{id?, text}] \u2014 \uC790\uB9C9\xB7\uD0C0\uC774\uBC0D \uADFC\uAC70",
18024
+ "title": "Dialogue"
18025
+ },
18026
+ "transcribe": {
18027
+ "default": false,
18028
+ "description": "True \uBA74 asr:transcribe \uD589 \uCCAD\uAD6C",
18029
+ "title": "Transcribe",
18030
+ "type": "boolean"
18031
+ },
18032
+ "confirmed_total_krw": {
18033
+ "default": 0,
18034
+ "minimum": 0,
18035
+ "title": "Confirmed Total Krw",
18036
+ "type": "integer"
18037
+ },
18038
+ "rate_version": {
18039
+ "anyOf": [
18040
+ {
18041
+ "type": "integer"
18042
+ },
18043
+ {
18044
+ "type": "null"
18045
+ }
18046
+ ],
18047
+ "default": null,
18048
+ "title": "Rate Version"
18049
+ }
18050
+ },
18051
+ "required": [
18052
+ "slug",
18053
+ "cut_id",
18054
+ "video_asset_id"
18055
+ ],
18056
+ "additionalProperties": false
18057
+ },
18058
+ "annotations": {
18059
+ "readOnlyHint": false,
18060
+ "destructiveHint": false,
18061
+ "idempotentHint": false,
18062
+ "openWorldHint": false
18063
+ }
18064
+ },
18065
+ {
18066
+ "name": "democut_cancel_job",
18067
+ "description": "\uC7A1\uC744 \uCDE8\uC18C\uD55C\uB2E4. queued \uB294 \uC989\uC2DC \uD658\uBD88, running \uC740 \uC2E4\uD589\uAE30\uAC00 1\uCD08 \uC548\uC5D0 \uBA48\uCD94\uACE0 \uC815\uC0B0(\uACF5\uAE09\uC790\uAC00 \uACFC\uAE08\uD55C \uD589\uC740 \uBE44\uD658\uBD88). AI \uC601\uC0C1\uC740 \uACF5\uAE09\uC790 \uC81C\uCD9C \uB4A4 \uCDE8\uC18C \uBD88\uAC00(409 NOT_CANCELLABLE).",
18068
+ "inputSchema": {
18069
+ "type": "object",
18070
+ "properties": {
18071
+ "job_id": {
18072
+ "type": "string",
18073
+ "description": "\uC7A1 id(job_\u2026)"
18074
+ }
18075
+ },
18076
+ "required": [
18077
+ "job_id"
18078
+ ],
18079
+ "additionalProperties": false
18080
+ },
18081
+ "annotations": {
18082
+ "readOnlyHint": false,
18083
+ "destructiveHint": true,
18084
+ "idempotentHint": true,
18085
+ "openWorldHint": false
18086
+ }
18087
+ },
18088
+ {
18089
+ "name": "democut_credit_balance",
18090
+ "description": "\uB0B4 \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4\uC758 \uD06C\uB808\uB527 \uC794\uC561. \uBD80\uC791\uC6A9\uB3C4 \uACFC\uAE08\uB3C4 \uC5C6\uB2E4. \uB3C8\uC774 \uB098\uAC00\uB294 \uC791\uC5C5(democut_make_cuts \uB4F1)\uC744 \uC81C\uCD9C\uD558\uAE30 **\uC804\uC5D0** \uD655\uC778\uD558\uB77C \u2014 \uBD80\uC871\uD558\uBA74 \uC81C\uCD9C\uC774 402 \uB85C \uAC70\uBD80\uB418\uACE0, \uADF8\uAC83\uC744 \uBBF8\uB9AC \uC544\uB294 \uD3B8\uC774 \uB0AB\uB2E4(democut_estimate_cuts \uC758 can_afford \uAC00 \uC788\uC73C\uBA74 \uB9E4\uBC88 \uBD80\uB97C \uD544\uC694\uB294 \uC5C6\uB2E4). \uBAA8\uC790\uB77C\uBA74 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uC54C\uB9AC\uACE0 \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uAD00\uB9AC\uC790\uC5D0\uAC8C \uCDA9\uC804\uC744 \uC694\uCCAD\uD558\uB3C4\uB85D \uC548\uB0B4\uD558\uB77C \u2014 \uB124\uAC00 \uC9C1\uC811 \uCDA9\uC804\uD560 \uC218\uB294 \uC5C6\uB2E4.",
18091
+ "inputSchema": {
18092
+ "type": "object",
18093
+ "properties": {},
18094
+ "required": [],
18095
+ "additionalProperties": false
18096
+ },
18097
+ "annotations": {
18098
+ "readOnlyHint": true,
18099
+ "destructiveHint": false,
18100
+ "idempotentHint": true,
18101
+ "openWorldHint": false
18102
+ }
18103
+ },
18104
+ {
18105
+ "name": "democut_credit_ledger",
18106
+ "description": "\uD06C\uB808\uB527 \uC0AC\uC6A9 \uB0B4\uC5ED(\uCD5C\uC2E0\uC21C, keyset). \u300C\uC65C \uC794\uC561\uC774 \uC774\uB9CC\uD07C \uC904\uC5C8\uB098\u300D \uB97C \uB2F5\uD560 \uB54C \uC4F4\uB2E4 \u2014 \uD589\uB9C8\uB2E4 \uBB34\uC5C7\uC5D0\xB7\uC5B8\uC81C\xB7\uC5BC\uB9C8\uC778\uC9C0\uAC00 \uC788\uB2E4.",
18107
+ "inputSchema": {
18108
+ "type": "object",
18109
+ "properties": {
18110
+ "cursor": {
18111
+ "anyOf": [
18112
+ {
18113
+ "type": "string"
18114
+ },
18115
+ {
18116
+ "type": "null"
18117
+ }
18118
+ ],
18119
+ "default": null,
18120
+ "title": "Cursor"
18121
+ },
18122
+ "limit": {
18123
+ "default": 50,
18124
+ "maximum": 200,
18125
+ "minimum": 1,
18126
+ "title": "Limit",
18127
+ "type": "integer"
18128
+ }
18129
+ },
18130
+ "required": [],
18131
+ "additionalProperties": false
18132
+ },
18133
+ "annotations": {
18134
+ "readOnlyHint": true,
18135
+ "destructiveHint": false,
18136
+ "idempotentHint": true,
18137
+ "openWorldHint": false
18138
+ }
18139
+ }
18140
+ ];
18141
+
18142
+ // src/api-errors.ts
18143
+ var TOOL_BASE_NAMES = GENERATED_TOOLS.map((t) => t.name.replace(/^democut_/, "")).sort((a, b) => b.length - a.length);
18144
+ var escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
18145
+ var NAME_PATTERNS = TOOL_BASE_NAMES.map((name) => [
18146
+ new RegExp(`(?<!democut_)(?<![\\w.])${escapeRe(name)}(?![\\w{(])`, "g"),
18147
+ `democut_${name}`
18148
+ ]);
18149
+ function callableToolNames(text) {
18150
+ let out2 = text;
18151
+ for (const [re, replacement] of NAME_PATTERNS) out2 = out2.replace(re, replacement);
18152
+ return out2;
18153
+ }
18154
+ function usd(v) {
18155
+ return v < 0.01 ? `$${v.toFixed(4)}` : `$${v.toFixed(2)}`;
18156
+ }
18157
+ function classifyApiError(e, ctx) {
18158
+ const hint = ctx.statusHint ?? "\uC0C1\uD0DC \uC870\uD68C";
18159
+ const contractHint = typeof e.extra.hint === "string" ? callableToolNames(e.extra.hint) : null;
18160
+ if (contractHint) {
18161
+ const lines = [contractHint];
18162
+ if (ctx.submitted) {
18163
+ lines.push(
18164
+ e.status < 500 ? "\uC544\uC9C1 \uC544\uBB34\uAC83\uB3C4 \uC81C\uCD9C\uB418\uC9C0 \uC54A\uC558\uACE0 \uACFC\uAE08\uB3C4 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \uC694\uCCAD\uC744 \uACE0\uCCD0 \uB2E4\uC2DC \uD638\uCD9C\uD558\uC138\uC694." : `\u26A0\uFE0F \uC81C\uCD9C\uC774 \uC811\uC218\uB410\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4 \u2014 \uB2E4\uC2DC \uBCF4\uB0B4\uC9C0 \uB9D0\uACE0 ${hint} \uB85C \uC0C1\uD0DC\uB97C \uBA3C\uC800 \uD655\uC778\uD558\uC138\uC694.`
18165
+ );
18166
+ }
18167
+ return {
18168
+ safety: e.status >= 500 && ctx.submitted ? "unsafe" : e.retriable === false ? "fixable" : "safe",
18169
+ notCharged: e.status < 500,
18170
+ lines
18171
+ };
18172
+ }
18173
+ if (e.status === 422 && e.code === "VIDEOGEN_UPSTREAM_REJECTED") {
18174
+ return {
18175
+ safety: "unsafe",
18176
+ // 돈이 나갔다가 돌아왔다 — "애초에 과금이 없었다" 와는 다르다. 그 구분을 흐리면
18177
+ // 원장을 보는 사람이 이 거래를 설명하지 못한다.
18178
+ notCharged: false,
18179
+ lines: [
18180
+ "\uC5C5\uC2A4\uD2B8\uB9BC\uC774 \uC774 \uC791\uC5C5\uC744 \uC601\uAD6C \uAC70\uBD80\uD588\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC870\uD68C\uD574\uB3C4 \uBCF5\uAD6C\uB418\uC9C0 \uC54A\uC73C\uBA70, \uACFC\uAE08\uC740 \uD658\uBD88\uB410\uC2B5\uB2C8\uB2E4.",
18181
+ "\uACE0\uCE60 \uD30C\uB77C\uBBF8\uD130\uAC00 \uC788\uB294 \uC624\uB958\uAC00 \uC544\uB2D9\uB2C8\uB2E4 \u2014 \uC0C8\uB85C \uB9CC\uB4E4\uB824\uBA74 \uD504\uB86C\uD504\uD2B8\xB7\uC124\uC815\uC744 \uBC14\uAFD4 **\uC0C8\uB85C \uC81C\uCD9C**\uD574\uC57C \uD558\uACE0, \uADF8\uAC74 \uB2E4\uC2DC \uACFC\uAE08\uB429\uB2C8\uB2E4(\uC0AC\uC6A9\uC790 \uB3D9\uC758\uB97C \uBC1B\uC73C\uC138\uC694)."
18182
+ ]
18183
+ };
18184
+ }
18185
+ if (e.status === 404 && e.code === "VIDEOGEN_JOB_NOT_FOUND") {
18186
+ return {
18187
+ safety: "unsafe",
18188
+ notCharged: false,
18189
+ lines: [
18190
+ "\uADF8 \uC791\uC5C5\uC744 \uD655\uC778\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. job_id \uAC00 \uB9DE\uB294\uC9C0 \uBA3C\uC800 \uD655\uC778\uD558\uC138\uC694.",
18191
+ "\u26A0\uFE0F \uC791\uC5C5\uC774 \uC0B4\uC544 \uC788\uB294\uB370 \uC870\uD68C\uB9CC \uC5B4\uAE0B\uB0AC\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4 \u2014 \uC0C8\uB85C \uC81C\uCD9C\uD558\uBA74 **\uBCC4\uB3C4 \uC791\uC5C5\uC73C\uB85C \uB2E4\uC2DC \uACFC\uAE08**\uB429\uB2C8\uB2E4."
18192
+ ]
18193
+ };
18194
+ }
18195
+ if (e.status === 402) {
18196
+ const lines = [];
18197
+ const limit = e.extra.limit_usd;
18198
+ const would = e.extra.would_cost_usd;
18199
+ if (typeof limit === "number" && typeof would === "number") {
18200
+ lines.push(`\uC0C1\uD55C ${usd(limit)} \xB7 \uC774 \uC694\uCCAD ${usd(would)}`);
18201
+ }
18202
+ lines.push("\uD574\uC0C1\uB3C4\uB098 \uAE38\uC774\uB97C \uB0AE\uCDB0 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694. \uC544\uC9C1 \uACFC\uAE08\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.");
18203
+ return { safety: "fixable", notCharged: true, lines };
18204
+ }
18205
+ if (e.status === 422) {
18206
+ return {
18207
+ safety: "fixable",
18208
+ notCharged: true,
18209
+ lines: ["\uD30C\uB77C\uBBF8\uD130\uB97C \uACE0\uCCD0 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694. \uC544\uC9C1 \uACFC\uAE08\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4."]
18210
+ };
18211
+ }
18212
+ if (e.status === 503) {
18213
+ return {
18214
+ safety: "safe",
18215
+ notCharged: true,
18216
+ lines: ["\uC77C\uC2DC\uC801\uC778 \uC624\uB958\uC785\uB2C8\uB2E4. \uC7A0\uC2DC \uD6C4 \uADF8\uB300\uB85C \uB2E4\uC2DC \uC2DC\uB3C4\uD574\uB3C4 \uC548\uC804\uD569\uB2C8\uB2E4."]
18217
+ };
18218
+ }
18219
+ if (e.status === 502) {
18220
+ return ctx.submitted ? {
18221
+ safety: "unsafe",
18222
+ notCharged: false,
18223
+ lines: [
18224
+ "\u26A0\uFE0F \uC81C\uCD9C\uC774 \uC774\uBBF8 \uC811\uC218\uB410\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC81C\uCD9C\uD558\uBA74 \uB450 \uBC88 \uACFC\uAE08\uB429\uB2C8\uB2E4 \u2014",
18225
+ `\uC7AC\uC2DC\uB3C4\uD558\uC9C0 \uB9D0\uACE0 ${hint} \uB85C \uD655\uC778\uD558\uAC70\uB098 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uBB38\uC758\uD558\uC138\uC694.`
18226
+ ]
18227
+ } : {
18228
+ safety: "safe",
18229
+ notCharged: true,
18230
+ lines: [
18231
+ "\uC5C5\uC2A4\uD2B8\uB9BC \uC624\uB958\uC785\uB2C8\uB2E4. \uC774 \uC694\uCCAD\uC740 \uBD80\uC791\uC6A9\uC774 \uC5C6\uC73C\uBBC0\uB85C \uC7A0\uC2DC \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD574\uB3C4 \uC548\uC804\uD569\uB2C8\uB2E4."
18232
+ ]
18233
+ };
18234
+ }
18235
+ if (e.status === 404) {
18236
+ const lines = [];
18237
+ if (e.code === "VIDEOGEN_PROJECT_NOT_FOUND") {
18238
+ lines.push(
18239
+ "democut_list_projects \uB85C \uC774 \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4\uC758 \uD504\uB85C\uC81D\uD2B8 \uBAA9\uB85D\uC744 \uD655\uC778\uD558\uACE0 \uADF8\uC911 \uD558\uB098\uC758 \uC2AC\uB7EC\uADF8\uB97C \uC4F0\uC138\uC694. \uBAA9\uB85D\uC774 \uBE44\uC5B4 \uC788\uC73C\uBA74 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uD504\uB85C\uC81D\uD2B8\uB97C \uBA3C\uC800 \uB9CC\uB4E4\uC5B4 \uB2EC\uB77C\uACE0 \uC54C\uB9AC\uC138\uC694."
18240
+ );
18241
+ }
18242
+ lines.push("\uC544\uC9C1 \uACFC\uAE08\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4 \u2014 \uAC12\uC744 \uACE0\uCCD0 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.");
18243
+ return { safety: "fixable", notCharged: true, lines };
18244
+ }
18245
+ if (e.status === 403) {
18246
+ return {
18247
+ safety: "unknown",
18248
+ notCharged: true,
18249
+ lines: ["\uC774 \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4\uC5D0\uC11C \uC601\uC0C1\uC744 \uC0DD\uC131\uD560 \uAD8C\uD55C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4(\uBA64\uBC84 \uC774\uC0C1 \uD544\uC694)."]
18250
+ };
18251
+ }
18252
+ return { safety: "unknown", notCharged: false, lines: [] };
18253
+ }
18254
+
18255
+ // src/mcp/tools.ts
18256
+ import { createWriteStream, existsSync as existsSync2 } from "fs";
18257
+ import { readFile } from "fs/promises";
18258
+ import { basename } from "path";
18259
+ import { Readable } from "stream";
18260
+ import { pipeline as streamPipeline } from "stream/promises";
18261
+
18262
+ // src/api-client.ts
18263
+ var ApiError = class extends Error {
18264
+ constructor(status, code, message, retriable, extra = {}) {
18265
+ super(message);
18266
+ this.status = status;
18267
+ this.code = code;
18268
+ this.retriable = retriable;
18269
+ this.extra = extra;
18270
+ this.name = "ApiError";
18271
+ }
18272
+ status;
18273
+ code;
18274
+ retriable;
18275
+ extra;
18276
+ };
18277
+ function parseDetail(status, body) {
18278
+ const wrapped = body?.error;
18279
+ if (wrapped && typeof wrapped === "object" && !Array.isArray(wrapped)) {
18280
+ const w = wrapped;
18281
+ if (typeof w.code === "string") {
18282
+ const data = w.data && typeof w.data === "object" ? w.data : {};
18283
+ return new ApiError(
18284
+ status,
18285
+ w.code,
18286
+ typeof w.message === "string" ? w.message : `HTTP ${status}`,
18287
+ typeof w.retry_safe === "boolean" ? w.retry_safe : null,
18288
+ { ...data, ...typeof w.hint === "string" ? { hint: w.hint } : {} }
18289
+ );
18290
+ }
18291
+ }
18292
+ const detail = body?.detail;
18293
+ if (detail && typeof detail === "object" && !Array.isArray(detail)) {
18294
+ const d = detail;
18295
+ if (typeof d.code === "string" || typeof d.message === "string") {
18296
+ const { code, message, retriable, ...extra } = d;
18297
+ return new ApiError(
18298
+ status,
18299
+ typeof code === "string" ? code : `HTTP_${status}`,
18300
+ typeof message === "string" ? message : `HTTP ${status}`,
18301
+ typeof retriable === "boolean" ? retriable : null,
18302
+ extra
18303
+ );
18304
+ }
18305
+ }
18306
+ if (typeof detail === "string") {
18307
+ return new ApiError(status, `HTTP_${status}`, detail, null);
18308
+ }
18309
+ if (Array.isArray(detail)) {
18310
+ const lines = detail.map((e) => {
18311
+ const item = e;
18312
+ const where = Array.isArray(item.loc) ? item.loc.filter((p) => p !== "body").join(".") : "";
18313
+ const msg = typeof item.msg === "string" ? item.msg : JSON.stringify(e);
18314
+ return where ? `${where}: ${msg}` : msg;
18315
+ });
18316
+ return new ApiError(status, "VALIDATION_ERROR", lines.join("\n"), true);
18317
+ }
18318
+ return new ApiError(status, `HTTP_${status}`, `\uC694\uCCAD\uC774 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4 (HTTP ${status}).`, null);
18319
+ }
18320
+ async function apiRequest(baseUrl, path, init = {}, deps = {}) {
18321
+ const fetchImpl = deps.fetchImpl ?? fetch;
18322
+ const token = await getValidAccessToken(baseUrl, deps);
18323
+ const headers = {
18324
+ Authorization: `Bearer ${token}`,
18325
+ "User-Agent": `democut/${VERSION}`
18326
+ };
18327
+ if (init.body !== void 0) headers["Content-Type"] = "application/json";
18328
+ const res = await fetchImpl(`${baseUrl}${path}`, {
18329
+ method: init.method ?? "GET",
18330
+ headers,
18331
+ ...init.body === void 0 ? {} : { body: JSON.stringify(init.body) }
18332
+ });
18333
+ const text = await res.text();
18334
+ let json = null;
18335
+ if (text) {
18336
+ try {
18337
+ json = JSON.parse(text);
18338
+ } catch {
18339
+ json = null;
18340
+ }
18341
+ }
18342
+ if (!res.ok) {
18343
+ if (res.status === 401) {
18344
+ throw new ApiError(
18345
+ 401,
18346
+ "UNAUTHENTICATED",
18347
+ "\uC778\uC99D\uC774 \uB9CC\uB8CC\uB418\uC5C8\uAC70\uB098 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. 'democut auth login' \uC73C\uB85C \uB2E4\uC2DC \uB85C\uADF8\uC778\uD558\uC138\uC694.",
18348
+ false
18349
+ );
18350
+ }
18351
+ throw parseDetail(res.status, json);
18352
+ }
18353
+ return json;
18354
+ }
18355
+
18356
+ // src/credits.ts
18357
+ var LEDGER_MAX_LIMIT = 200;
18358
+ var LEDGER_DEFAULT_LIMIT = 10;
18359
+ function balance(baseUrl, workspaceId, deps = {}) {
18360
+ return apiRequest(
18361
+ baseUrl,
18362
+ `/api/workspaces/${encodeURIComponent(workspaceId)}/credits`,
18363
+ {},
18364
+ deps
18365
+ );
18366
+ }
18367
+ function ledger(baseUrl, workspaceId, opts = {}, deps = {}) {
18368
+ const q = new URLSearchParams();
18369
+ q.set("limit", String(opts.limit ?? LEDGER_DEFAULT_LIMIT));
18370
+ if (opts.cursor !== void 0) q.set("cursor", opts.cursor);
18371
+ return apiRequest(
18372
+ baseUrl,
18373
+ `/api/workspaces/${encodeURIComponent(workspaceId)}/credits/ledger?${q}`,
18374
+ {},
18375
+ deps
16341
18376
  );
16342
18377
  }
16343
18378
 
@@ -16351,13 +18386,13 @@ function create(baseUrl, body, deps = {}) {
16351
18386
  return apiRequest(baseUrl, "/api/projects", { method: "POST", body }, deps);
16352
18387
  }
16353
18388
  async function defaultVoiceId(baseUrl, deps = {}) {
16354
- const out3 = await apiRequest(
18389
+ const out2 = await apiRequest(
16355
18390
  baseUrl,
16356
18391
  "/api/projects/voices",
16357
18392
  {},
16358
18393
  deps
16359
18394
  );
16360
- const id = out3.default_voice_id;
18395
+ const id = out2.default_voice_id;
16361
18396
  if (!id) {
16362
18397
  throw new Error("\uC11C\uBC84\uAC00 \uAE30\uBCF8 \uC74C\uC131\uC744 \uC54C\uB824\uC8FC\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4 (default_voice_id \uC5C6\uC74C).");
16363
18398
  }
@@ -16365,10 +18400,6 @@ async function defaultVoiceId(baseUrl, deps = {}) {
16365
18400
  }
16366
18401
 
16367
18402
  // src/mcp/tools.ts
16368
- var COST_TOLERANCE_USD = 5e-3;
16369
- function usd3(v) {
16370
- return v < 0.01 ? `$${v.toFixed(4)}` : `$${v.toFixed(2)}`;
16371
- }
16372
18403
  function describeError2(e, ctx) {
16373
18404
  if (e instanceof NotLoggedInError) {
16374
18405
  return {
@@ -16382,7 +18413,39 @@ function describeError2(e, ctx) {
16382
18413
  }
16383
18414
  return { isError: true, text: `\uC624\uB958: ${e?.message ?? String(e)}` };
16384
18415
  }
16385
- async function guarded2(ctx, fn) {
18416
+ async function saveUrlToFile(url, outPath, fetchImpl = fetch) {
18417
+ const res = await fetchImpl(url);
18418
+ if (!res.ok || !res.body) {
18419
+ throw new Error(`\uB0B4\uB824\uBC1B\uAE30 \uC2E4\uD328: ${res.status} ${res.statusText}`);
18420
+ }
18421
+ await streamPipeline(Readable.fromWeb(res.body), createWriteStream(outPath));
18422
+ }
18423
+ async function uploadFile(base, path, meta2, deps) {
18424
+ const fetchImpl = deps.fetchImpl ?? fetch;
18425
+ const token = await getValidAccessToken(base, deps);
18426
+ const bytes = await readFile(path);
18427
+ const form = new FormData();
18428
+ form.set("kind", meta2.kind);
18429
+ if (meta2.projectSlug) form.set("project_slug", meta2.projectSlug);
18430
+ form.set("file", new Blob([bytes]), meta2.filename ?? basename(path));
18431
+ const res = await fetchImpl(`${base}/api/assets`, {
18432
+ method: "POST",
18433
+ headers: { Authorization: `Bearer ${token}`, "User-Agent": `democut/${VERSION}` },
18434
+ body: form
18435
+ });
18436
+ const text = await res.text();
18437
+ let json = null;
18438
+ if (text) {
18439
+ try {
18440
+ json = JSON.parse(text);
18441
+ } catch {
18442
+ json = null;
18443
+ }
18444
+ }
18445
+ if (!res.ok) throw parseDetail(res.status, json);
18446
+ return json;
18447
+ }
18448
+ async function guarded(ctx, fn) {
16386
18449
  try {
16387
18450
  return await fn();
16388
18451
  } catch (e) {
@@ -16392,10 +18455,10 @@ async function guarded2(ctx, fn) {
16392
18455
  var READ_ONLY = { submitted: false };
16393
18456
  var SUBMITTING = {
16394
18457
  submitted: true,
16395
- statusHint: "democut_get_video_status"
18458
+ statusHint: "democut_credit_ledger"
16396
18459
  };
16397
18460
  function authStatus(deps = {}) {
16398
- return guarded2(READ_ONLY, async () => {
18461
+ return guarded(READ_ONLY, async () => {
16399
18462
  const base = resolveBaseUrl(deps.baseUrl);
16400
18463
  const token = await getValidAccessToken(base, deps);
16401
18464
  const me = await whoami(base, token, deps.fetchImpl);
@@ -16408,159 +18471,6 @@ function authStatus(deps = {}) {
16408
18471
  };
16409
18472
  });
16410
18473
  }
16411
- function toEstimateIn(a) {
16412
- return {
16413
- duration_s: a.duration_s ?? 5,
16414
- resolution: a.resolution ?? "720p",
16415
- ratio: a.ratio ?? "16:9",
16416
- ...a.model ? { model: a.model } : {}
16417
- };
16418
- }
16419
- var MOCK_BANNER = "\u26A0\uFE0F \uC774 \uBC30\uD3EC\uB294 mock \uBAA8\uB4DC\uC785\uB2C8\uB2E4 \u2014 \uC601\uC0C1\uC774 **\uC2E4\uC81C\uB85C \uC0DD\uC131\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4**. \uC544\uB798 \uAE08\uC561\xB7job_id\xB7URL \uC740 \uC804\uBD80 \uAC00\uC9DC\uC774\uBA70 \uC2E4\uC81C\uB85C \uACFC\uAE08\uB418\uC9C0\uB3C4 \uC54A\uC2B5\uB2C8\uB2E4. \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uC0DD\uC131\uC774 \uB418\uC5C8\uB2E4\uACE0 \uBCF4\uACE0\uD558\uC9C0 \uB9C8\uC138\uC694.";
16420
- function estimateCost(a, deps = {}) {
16421
- return guarded2(READ_ONLY, async () => {
16422
- const base = resolveBaseUrl(deps.baseUrl);
16423
- const r = await estimate(base, toEstimateIn(a), deps);
16424
- if (r.mock) {
16425
- return {
16426
- text: [
16427
- MOCK_BANNER,
16428
- "",
16429
- `(\uCC38\uACE0\uC6A9 \uCD94\uC815\uCE58: ${usd3(r.cost_usd)} \xB7 ${r.model})`,
16430
- "",
16431
- "\uC0AC\uC6A9\uC790\uC5D0\uAC8C \uBE44\uC6A9 \uB3D9\uC758\uB97C \uAD6C\uD558\uC9C0 \uB9C8\uC138\uC694 \u2014 \uC774 \uBC30\uD3EC\uC5D0\uC11C\uB294 \uC81C\uCD9C\uD574\uB3C4 \uC2E4\uC81C \uC601\uC0C1\uC774 \uB9CC\uB4E4\uC5B4\uC9C0\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uC6B4\uC601\uC790\uC5D0\uAC8C API \uD0A4 \uC124\uC815\uC744 \uC694\uCCAD\uD558\uC138\uC694."
16432
- ].join("\n")
16433
- };
16434
- }
16435
- return {
16436
- text: [
16437
- `\uC608\uC0C1 \uBE44\uC6A9: ${usd3(r.cost_usd)} (${r.tokens.toLocaleString()} tokens \xB7 ${r.model})`,
16438
- `cost_usd \uC6D0\uAC12: ${r.cost_usd}`,
16439
- "",
16440
- `\uC0DD\uC131\uD558\uB824\uBA74 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uC774 \uAE08\uC561\uC744 \uC54C\uB9AC\uACE0 \uB3D9\uC758\uB97C \uBC1B\uC740 \uB4A4, generate_video \uC5D0 confirmed_cost_usd=${r.cost_usd} \uB97C \uADF8\uB300\uB85C \uB123\uC5B4 \uD638\uCD9C\uD558\uC138\uC694.`
16441
- ].join("\n")
16442
- };
16443
- });
16444
- }
16445
- function generateVideo(a, deps = {}) {
16446
- return guarded2(SUBMITTING, async () => {
16447
- if (!a.prompt?.trim()) {
16448
- return { isError: true, text: "prompt \uAC00 \uBE44\uC5B4 \uC788\uC2B5\uB2C8\uB2E4." };
16449
- }
16450
- if (typeof a.project_slug !== "string" || !a.project_slug.trim()) {
16451
- return {
16452
- isError: true,
16453
- text: "project_slug \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. \uBE44\uC6A9\uC744 \uADC0\uC18D\uC2DC\uD0AC \uBCF8\uC778 \uC18C\uC720 \uD504\uB85C\uC81D\uD2B8\uC758 \uC2AC\uB7EC\uADF8\uB97C \uB123\uC5B4 \uB2E4\uC2DC \uD638\uCD9C\uD558\uC138\uC694. \uC544\uC9C1 \uACFC\uAE08\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.\n\uC2AC\uB7EC\uADF8\uB97C \uBAA8\uB974\uBA74 democut_list_projects \uB85C \uC774 \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4\uC758 \uD504\uB85C\uC81D\uD2B8 \uBAA9\uB85D\uC744 \uD655\uC778\uD558\uC138\uC694. \uBAA9\uB85D\uC774 \uBE44\uC5B4 \uC788\uC73C\uBA74 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uD504\uB85C\uC81D\uD2B8\uB97C \uBA3C\uC800 \uB9CC\uB4E4\uC5B4 \uB2EC\uB77C\uACE0 \uC54C\uB9AC\uC138\uC694."
16454
- };
16455
- }
16456
- const base = resolveBaseUrl(deps.baseUrl);
16457
- const est = toEstimateIn(a);
16458
- const quote = await estimate(base, est, deps);
16459
- if (typeof a.confirmed_cost_usd !== "number") {
16460
- return {
16461
- isError: true,
16462
- text: `confirmed_cost_usd \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. \uC774 \uC694\uCCAD\uC758 \uC608\uC0C1 \uBE44\uC6A9\uC740 ${usd3(quote.cost_usd)} (\uC6D0\uAC12 ${quote.cost_usd}) \uC785\uB2C8\uB2E4. \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uC54C\uB9AC\uACE0 \uB3D9\uC758\uB97C \uBC1B\uC740 \uB4A4 \uADF8 \uAC12\uC744 \uB123\uC5B4 \uB2E4\uC2DC \uD638\uCD9C\uD558\uC138\uC694.`
16463
- };
16464
- }
16465
- if (Math.abs(a.confirmed_cost_usd - quote.cost_usd) > COST_TOLERANCE_USD) {
16466
- return {
16467
- isError: true,
16468
- text: `\uD655\uC778\uD55C \uAE08\uC561(${usd3(a.confirmed_cost_usd)})\uC774 \uC2E4\uC81C \uC608\uC0C1 \uBE44\uC6A9(${usd3(quote.cost_usd)})\uACFC \uB2E4\uB985\uB2C8\uB2E4. \uD30C\uB77C\uBBF8\uD130\uAC00 \uADF8 \uC0AC\uC774 \uBC14\uB00C\uC5C8\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4. estimate_video_cost \uB97C \uB2E4\uC2DC \uD638\uCD9C\uD574 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uC0C8 \uAE08\uC561\uC744 \uD655\uC778\uBC1B\uC73C\uC138\uC694. \uC544\uC9C1 \uACFC\uAE08\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.`
16469
- };
16470
- }
16471
- const body = {
16472
- prompt: a.prompt,
16473
- duration_s: est.duration_s,
16474
- resolution: est.resolution,
16475
- ratio: est.ratio,
16476
- generate_audio: Boolean(a.generate_audio),
16477
- watermark: Boolean(a.watermark),
16478
- ...a.model ? { model: a.model } : {},
16479
- ...a.seed !== void 0 ? { seed: a.seed } : {},
16480
- ...a.image_urls?.length ? { image_urls: a.image_urls } : {},
16481
- project_slug: a.project_slug.trim()
16482
- };
16483
- const job = await submit(base, body, deps);
16484
- if (job.duplicate) {
16485
- return {
16486
- text: job.pending ? "\uAC19\uC740 \uC694\uCCAD\uC774 \uBC29\uAE08 \uC81C\uCD9C\uB418\uC5B4 \uC9C4\uD589 \uC911\uC785\uB2C8\uB2E4. job_id \uAC00 \uC544\uC9C1 \uC5C6\uC73C\uB2C8 \uC7A0\uC2DC \uD6C4 \uAC19\uC740 \uD30C\uB77C\uBBF8\uD130\uB85C \uB2E4\uC2DC \uD638\uCD9C\uD558\uC9C0 \uB9D0\uACE0 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uC54C\uB9AC\uC138\uC694(\uC0C8\uB85C \uB9CC\uB4E4\uBA74 \uB2E4\uC2DC \uACFC\uAE08\uB429\uB2C8\uB2E4)." : `\uAC19\uC740 \uC694\uCCAD\uC774 \uC774\uBBF8 \uC9C4\uD589 \uC911\uC785\uB2C8\uB2E4 (job_id: ${job.job_id}). \uC0C8\uB85C \uACFC\uAE08\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. get_video_status \uB85C \uD655\uC778\uD558\uC138\uC694.`
16487
- };
16488
- }
16489
- if (job.mock) {
16490
- return {
16491
- text: [
16492
- MOCK_BANNER,
16493
- "",
16494
- `(\uAC00\uC9DC job_id: ${job.job_id} \xB7 \uBAA8\uB378 ${job.model})`,
16495
- "",
16496
- "\uC2E4\uC81C\uB85C \uC81C\uCD9C\uB418\uC9C0 \uC54A\uC558\uACE0 \uACFC\uAE08\uB3C4 \uC5C6\uC2B5\uB2C8\uB2E4. \uC774 job_id \uB85C \uC0C1\uD0DC\uB97C \uC870\uD68C\uD558\uBA74 \uAC00\uC9DC URL \uC774 \uB098\uC635\uB2C8\uB2E4 \u2014 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uC601\uC0C1\uC774 \uB9CC\uB4E4\uC5B4\uC84C\uB2E4\uACE0 \uB9D0\uD558\uC9C0 \uB9C8\uC138\uC694."
16497
- ].join("\n")
16498
- };
16499
- }
16500
- return {
16501
- text: [
16502
- `\uC81C\uCD9C\uB428 \u2014 job_id: ${job.job_id}`,
16503
- `\uC608\uC0C1 \uBE44\uC6A9 ${usd3(job.estimated_cost_usd)} \xB7 \uBAA8\uB378 ${job.model}`,
16504
- "",
16505
- "\uC0DD\uC131\uC5D0\uB294 \uBCF4\uD1B5 90\uCD08 \uC774\uC0C1 \uAC78\uB9BD\uB2C8\uB2E4. get_video_status \uB85C \uD655\uC778\uD558\uC138\uC694.",
16506
- "\u26A0\uFE0F \uC2E4\uD328\uD574 \uBCF4\uC5EC\uB3C4 \uB2E4\uC2DC \uC81C\uCD9C\uD558\uC9C0 \uB9C8\uC138\uC694 \u2014 \uC7AC\uC81C\uCD9C\uC740 \uBCC4\uAC1C \uC0DD\uC131\uC774\uB77C \uB2E4\uC2DC \uACFC\uAE08\uB429\uB2C8\uB2E4."
16507
- ].join("\n")
16508
- };
16509
- });
16510
- }
16511
- function videoStatus(a, deps = {}) {
16512
- return guarded2(READ_ONLY, async () => {
16513
- if (!a.job_id?.trim()) return { isError: true, text: "job_id \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4." };
16514
- const base = resolveBaseUrl(deps.baseUrl);
16515
- const job = await status(base, a.job_id, a.project_slug, deps);
16516
- if (job.mock) {
16517
- return {
16518
- text: [
16519
- MOCK_BANNER,
16520
- "",
16521
- `(\uAC00\uC9DC \uC0C1\uD0DC: ${job.status} \xB7 \uAC00\uC9DC \uACB0\uACFC: ${job.video_url ?? "\uC5C6\uC74C"})`,
16522
- "",
16523
- "\uC774 URL \uC740 \uC2E4\uC7AC\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uB0B4\uB824\uBC1B\uC9C0 \uC54A\uC558\uACE0, \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uC804\uB2EC\uD558\uC9C0 \uB9C8\uC138\uC694. \uC644\uB8CC\uB97C \uAE30\uB2E4\uB9AC\uBA70 \uB2E4\uC2DC \uC870\uD68C\uD558\uC9C0\uB3C4 \uB9C8\uC138\uC694."
16524
- ].join("\n")
16525
- };
16526
- }
16527
- if (job.status === "failed" || job.status === "cancelled") {
16528
- return {
16529
- isError: true,
16530
- text: `\uC0DD\uC131 \uC2E4\uD328 (${job.status}): ${job.error ?? "\uC0AC\uC720 \uBBF8\uC0C1"}`
16531
- };
16532
- }
16533
- if (job.status !== "succeeded") {
16534
- return { text: `\uC544\uC9C1 \uC9C4\uD589 \uC911\uC785\uB2C8\uB2E4 (${job.status}). \uC7A0\uC2DC \uD6C4 \uB2E4\uC2DC \uD655\uC778\uD558\uC138\uC694.` };
16535
- }
16536
- const lines = [`\uC644\uB8CC \u2014 ${job.status}`];
16537
- if (job.cost_usd != null) {
16538
- lines.push(
16539
- `\uC2E4\uC81C \uBE44\uC6A9 ${usd3(job.cost_usd)}${job.tokens != null ? ` (${job.tokens.toLocaleString()} tokens)` : ""}`
16540
- );
16541
- }
16542
- if (job.video_url && a.output_path) {
16543
- if (existsSync2(a.output_path)) {
16544
- lines.push(`\uC800\uC7A5\uD558\uC9C0 \uC54A\uC74C: ${a.output_path} \uC5D0 \uC774\uBBF8 \uD30C\uC77C\uC774 \uC788\uC2B5\uB2C8\uB2E4(\uB36E\uC5B4\uC4F0\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4).`);
16545
- lines.push(`URL: ${job.video_url}`);
16546
- lines.push("\uB2E4\uB978 \uACBD\uB85C\uB97C \uC9C0\uC815\uD558\uAC70\uB098 \uC704 URL \uB85C \uC9C1\uC811 \uBC1B\uC73C\uC138\uC694 \u2014 \uC11C\uBC84\uB294 \uBCF4\uAD00\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.");
16547
- return { text: lines.join("\n") };
16548
- }
16549
- try {
16550
- await (deps.downloadImpl ?? download)(job.video_url, a.output_path);
16551
- lines.push(`\uC800\uC7A5\uB428: ${a.output_path}`);
16552
- } catch (e) {
16553
- lines.push(`\uB0B4\uB824\uBC1B\uAE30 \uC2E4\uD328: ${e?.message ?? String(e)}`);
16554
- lines.push(`URL: ${job.video_url}`);
16555
- lines.push("\u26A0\uFE0F \uC704 URL \uB85C \uC9C1\uC811 \uBC1B\uC73C\uC138\uC694 \u2014 \uC11C\uBC84\uB294 \uBCF4\uAD00\uD558\uC9C0 \uC54A\uC73C\uBA70 \uB2E4\uC2DC \uB9CC\uB4E4\uBA74 \uB610 \uACFC\uAE08\uB429\uB2C8\uB2E4.");
16556
- }
16557
- } else if (job.video_url) {
16558
- lines.push(`URL: ${job.video_url}`);
16559
- lines.push("\u26A0\uFE0F \uC11C\uBC84\uB294 \uC774 \uC0B0\uCD9C\uBB3C\uC744 \uBCF4\uAD00\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2014 output_path \uB97C \uC8FC\uBA74 \uBC14\uB85C \uC800\uC7A5\uD569\uB2C8\uB2E4.");
16560
- }
16561
- return { text: lines.join("\n") };
16562
- });
16563
- }
16564
18474
  var krw = (v) => `${v.toLocaleString("ko-KR")}\uC6D0`;
16565
18475
  var NO_WORKSPACE = {
16566
18476
  isError: true,
@@ -16572,7 +18482,7 @@ async function resolveWorkspace(base, deps) {
16572
18482
  return me.workspace_id ?? null;
16573
18483
  }
16574
18484
  function creditBalance(deps = {}) {
16575
- return guarded2(READ_ONLY, async () => {
18485
+ return guarded(READ_ONLY, async () => {
16576
18486
  const base = resolveBaseUrl(deps.baseUrl);
16577
18487
  const ws = await resolveWorkspace(base, deps);
16578
18488
  if (!ws) return NO_WORKSPACE;
@@ -16597,46 +18507,53 @@ function creditBalance(deps = {}) {
16597
18507
  );
16598
18508
  } else {
16599
18509
  lines.push(
16600
- "\uB3C8\uC774 \uB098\uAC00\uB294 \uC791\uC5C5 \uC804\uC5D0 democut_estimate_video_cost \uB85C \uAE08\uC561\uC744 \uD655\uC778\uD558\uACE0 \uC774 \uC794\uC561\uACFC \uACAC\uC8FC\uC138\uC694. \uD06C\uB808\uB527\uC740 \uC6D0(KRW)\uC774\uACE0 \uACAC\uC801\uC740 USD \uB77C \uB2E8\uC704\uAC00 \uB2E4\uB985\uB2C8\uB2E4 \u2014 \uD655\uC2E4\uD558\uC9C0 \uC54A\uC73C\uBA74 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uB450 \uAC12\uC744 \uADF8\uB300\uB85C \uBCF4\uC5EC \uC8FC\uC138\uC694."
18510
+ "\uB3C8\uC774 \uB098\uAC00\uB294 \uC791\uC5C5 \uC804\uC5D0 \uACAC\uC801 \uB3C4\uAD6C(S1 \uC5D0\uC11C \uC81C\uACF5)\uB85C \uAE08\uC561\uC744 \uD655\uC778\uD558\uACE0 \uC774 \uC794\uC561\uACFC \uACAC\uC8FC\uC138\uC694. \uD06C\uB808\uB527\uC740 \uC6D0(KRW)\uC774\uACE0 \uACAC\uC801\uC740 USD \uB77C \uB2E8\uC704\uAC00 \uB2E4\uB985\uB2C8\uB2E4 \u2014 \uD655\uC2E4\uD558\uC9C0 \uC54A\uC73C\uBA74 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uB450 \uAC12\uC744 \uADF8\uB300\uB85C \uBCF4\uC5EC \uC8FC\uC138\uC694."
16601
18511
  );
16602
18512
  }
16603
18513
  return { text: lines.join("\n") };
16604
18514
  });
16605
18515
  }
18516
+ function nextPage(out2) {
18517
+ if (out2.next_cursor != null) return out2.next_cursor;
18518
+ return out2.next_before_id != null ? String(out2.next_before_id) : null;
18519
+ }
16606
18520
  function creditLedger(a = {}, deps = {}) {
16607
- return guarded2(READ_ONLY, async () => {
18521
+ return guarded(READ_ONLY, async () => {
16608
18522
  if (a.limit !== void 0 && !Number.isFinite(a.limit)) {
16609
18523
  return { isError: true, text: `limit \uC740 \uC22B\uC790\uC5EC\uC57C \uD569\uB2C8\uB2E4: ${JSON.stringify(a.limit)}` };
16610
18524
  }
16611
- if (a.before_id !== void 0 && !Number.isFinite(a.before_id)) {
18525
+ const pageKey = a.cursor !== void 0 ? "cursor" : "before_id";
18526
+ const rawPage = a.cursor !== void 0 ? a.cursor : a.before_id;
18527
+ const pageNum = rawPage === void 0 ? void 0 : Number(rawPage);
18528
+ if (pageNum !== void 0 && (!Number.isFinite(pageNum) || typeof rawPage === "boolean")) {
16612
18529
  return {
16613
18530
  isError: true,
16614
- text: `before_id \uB294 \uC22B\uC790\uC5EC\uC57C \uD569\uB2C8\uB2E4: ${JSON.stringify(a.before_id)}`
18531
+ text: `${pageKey} \uB294 \uC774\uC804 \uC751\uB2F5\uC774 \uC900 \uAC12\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4: ${JSON.stringify(rawPage)}`
16615
18532
  };
16616
18533
  }
16617
18534
  const limit = Math.max(
16618
18535
  1,
16619
18536
  Math.min(Math.trunc(a.limit ?? LEDGER_DEFAULT_LIMIT), LEDGER_MAX_LIMIT)
16620
18537
  );
16621
- if (a.before_id !== void 0 && a.before_id < 1) {
16622
- return { isError: true, text: "before_id \uB294 1 \uC774\uC0C1\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4." };
18538
+ if (pageNum !== void 0 && (!Number.isInteger(pageNum) || pageNum < 1)) {
18539
+ return { isError: true, text: `${pageKey} \uB294 1 \uC774\uC0C1\uC758 \uC815\uC218\uC5EC\uC57C \uD569\uB2C8\uB2E4.` };
16623
18540
  }
16624
18541
  const base = resolveBaseUrl(deps.baseUrl);
16625
18542
  const ws = await resolveWorkspace(base, deps);
16626
18543
  if (!ws) return NO_WORKSPACE;
16627
- const out3 = await ledger(
18544
+ const out2 = await ledger(
16628
18545
  base,
16629
18546
  ws,
16630
- { limit, ...a.before_id !== void 0 ? { beforeId: Math.trunc(a.before_id) } : {} },
18547
+ { limit, ...pageNum !== void 0 ? { cursor: String(pageNum) } : {} },
16631
18548
  deps
16632
18549
  );
16633
- if (out3.entries.length === 0) {
18550
+ if (out2.entries.length === 0) {
16634
18551
  return {
16635
- text: !out3.enabled && out3.next_before_id == null ? "\uC774 \uBC30\uD3EC\uC5D0\uB294 \uD06C\uB808\uB527 \uAE30\uB2A5\uC774 \uC5C6\uC5B4 \uC0AC\uC6A9 \uB0B4\uC5ED\uC774 \uC5C6\uC2B5\uB2C8\uB2E4(0\uAC74\uC774 \uC544\uB2C8\uB77C \uAC1C\uB150 \uBD80\uC7AC)." : "\uC0AC\uC6A9 \uB0B4\uC5ED\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."
18552
+ text: !out2.enabled && nextPage(out2) == null ? "\uC774 \uBC30\uD3EC\uC5D0\uB294 \uD06C\uB808\uB527 \uAE30\uB2A5\uC774 \uC5C6\uC5B4 \uC0AC\uC6A9 \uB0B4\uC5ED\uC774 \uC5C6\uC2B5\uB2C8\uB2E4(0\uAC74\uC774 \uC544\uB2C8\uB77C \uAC1C\uB150 \uBD80\uC7AC)." : "\uC0AC\uC6A9 \uB0B4\uC5ED\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."
16636
18553
  };
16637
18554
  }
16638
18555
  const lines = ["\uD06C\uB808\uB527 \uC0AC\uC6A9 \uB0B4\uC5ED (\uCD5C\uC2E0\uC21C)", ""];
16639
- for (const e of out3.entries) {
18556
+ for (const e of out2.entries) {
16640
18557
  const when = (e.created_at ?? "").slice(0, 16).replace("T", " ");
16641
18558
  const parts = [e.display_name];
16642
18559
  if (e.units_label) parts.push(e.units_label);
@@ -16647,8 +18564,9 @@ function creditLedger(a = {}, deps = {}) {
16647
18564
  `${when} \xB7 ${parts.join(" \xB7 ")} \xB7 ${sign}${e.amount_krw.toLocaleString("ko-KR")}\uC6D0 (\uC794\uC561 ${e.balance_after_krw.toLocaleString("ko-KR")}\uC6D0)`
16648
18565
  );
16649
18566
  }
16650
- if (out3.next_before_id != null) {
16651
- lines.push("", `\uB354 \uC788\uC2B5\uB2C8\uB2E4 \u2014 before_id=${out3.next_before_id} \uB85C \uB2E4\uC2DC \uD638\uCD9C\uD558\uC138\uC694.`);
18567
+ const next = nextPage(out2);
18568
+ if (next != null) {
18569
+ lines.push("", `\uB354 \uC788\uC2B5\uB2C8\uB2E4 \u2014 ${pageKey}=${next} \uB85C \uB2E4\uC2DC \uD638\uCD9C\uD558\uC138\uC694.`);
16652
18570
  }
16653
18571
  return { text: lines.join("\n") };
16654
18572
  });
@@ -16662,7 +18580,7 @@ function projectLine(p) {
16662
18580
  return parts.join(" \xB7 ");
16663
18581
  }
16664
18582
  function listProjects(a = {}, deps = {}) {
16665
- return guarded2(READ_ONLY, async () => {
18583
+ return guarded(READ_ONLY, async () => {
16666
18584
  if (a.limit !== void 0 && !Number.isFinite(a.limit)) {
16667
18585
  return { isError: true, text: `limit \uC740 \uC22B\uC790\uC5EC\uC57C \uD569\uB2C8\uB2E4: ${JSON.stringify(a.limit)}` };
16668
18586
  }
@@ -16672,8 +18590,8 @@ function listProjects(a = {}, deps = {}) {
16672
18590
  );
16673
18591
  const query = (a.query ?? "").trim().toLowerCase();
16674
18592
  const base = resolveBaseUrl(deps.baseUrl);
16675
- const out3 = await list(base, deps);
16676
- const all = out3.projects ?? [];
18593
+ const out2 = await list(base, deps);
18594
+ const all = out2.projects ?? [];
16677
18595
  const total = all.length;
16678
18596
  const matched = query ? all.filter(
16679
18597
  (p) => (p.slug ?? "").toLowerCase().includes(query) || (p.title ?? "").toLowerCase().includes(query)
@@ -16694,13 +18612,26 @@ function listProjects(a = {}, deps = {}) {
16694
18612
  );
16695
18613
  }
16696
18614
  lines.push(
16697
- "democut_generate_video \uC758 project_slug \uC5D0\uB294 \uC704 **slug**(\uB9E8 \uC55E \uAC12)\uB97C \uADF8\uB300\uB85C \uB123\uC73C\uC138\uC694. \uC5B4\uB290 \uAC83\uC5D0 \uADC0\uC18D\uC2DC\uD0AC\uC9C0 \uBD84\uBA85\uD558\uC9C0 \uC54A\uC73C\uBA74 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uBB3C\uC73C\uC138\uC694."
18615
+ "\uD504\uB85C\uC81D\uD2B8\uB97C \uC694\uAD6C\uD558\uB294 \uB3C4\uAD6C\uC758 project_slug \uC5D0\uB294 \uC704 **slug**(\uB9E8 \uC55E \uAC12)\uB97C \uADF8\uB300\uB85C \uB123\uC73C\uC138\uC694. \uC5B4\uB290 \uAC83\uC5D0 \uADC0\uC18D\uC2DC\uD0AC\uC9C0 \uBD84\uBA85\uD558\uC9C0 \uC54A\uC73C\uBA74 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uBB3C\uC73C\uC138\uC694."
16698
18616
  );
16699
18617
  return { text: lines.join("\n") };
16700
18618
  });
16701
18619
  }
18620
+ var PROJECT_FIELD_DEFAULTS = {
18621
+ ratio: "16:9",
18622
+ resolution: "1080p",
18623
+ folder_id: null,
18624
+ defaults: null
18625
+ };
18626
+ function unsupportedProjectFields(a) {
18627
+ const given = a;
18628
+ return Object.keys(PROJECT_FIELD_DEFAULTS).filter((key) => {
18629
+ const v = given[key];
18630
+ return v !== void 0 && v !== null && v !== PROJECT_FIELD_DEFAULTS[key];
18631
+ });
18632
+ }
16702
18633
  function createProject(a = {}, deps = {}) {
16703
- return guarded2(READ_ONLY, async () => {
18634
+ return guarded(READ_ONLY, async () => {
16704
18635
  const title = (a.title ?? "").trim();
16705
18636
  if (!title) {
16706
18637
  return {
@@ -16708,6 +18639,13 @@ function createProject(a = {}, deps = {}) {
16708
18639
  text: "title \uC774 \uD544\uC694\uD569\uB2C8\uB2E4. \uC0AC\uC6A9\uC790\uAC00 \uC54C\uC544\uBCFC \uC218 \uC788\uB294 \uD504\uB85C\uC81D\uD2B8 \uC774\uB984\uC744 \uB123\uC5B4 \uB2E4\uC2DC \uD638\uCD9C\uD558\uC138\uC694."
16709
18640
  };
16710
18641
  }
18642
+ const unsupported = unsupportedProjectFields(a);
18643
+ if (unsupported.length > 0) {
18644
+ return {
18645
+ isError: true,
18646
+ text: `${unsupported.join(", ")} \uC740 \uC544\uC9C1 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4(\uAE30\uBCF8\uAC12\uB9CC \uAC00\uB2A5). \uADF8 \uC778\uC790\uB97C \uBE7C\uACE0 \uB2E4\uC2DC \uD638\uCD9C\uD558\uBA74 ${PROJECT_FIELD_DEFAULTS.ratio}\xB7${PROJECT_FIELD_DEFAULTS.resolution} \uB85C \uB9CC\uB4E4\uC5B4\uC9D1\uB2C8\uB2E4.`
18647
+ };
18648
+ }
16711
18649
  const base = resolveBaseUrl(deps.baseUrl);
16712
18650
  const voiceId = await defaultVoiceId(base, deps);
16713
18651
  const p = await create(
@@ -16720,147 +18658,315 @@ function createProject(a = {}, deps = {}) {
16720
18658
  `\uD504\uB85C\uC81D\uD2B8 \uC900\uBE44\uB428 \u2014 slug: ${p.slug}`,
16721
18659
  `\uC81C\uBAA9: ${p.title}`,
16722
18660
  "",
16723
- "democut_generate_video \uC758 project_slug \uC5D0 \uC704 slug \uB97C \uADF8\uB300\uB85C \uB123\uC73C\uC138\uC694.",
18661
+ "\uD504\uB85C\uC81D\uD2B8\uB97C \uC694\uAD6C\uD558\uB294 \uB3C4\uAD6C\uC758 project_slug \uC5D0 \uC704 slug \uB97C \uADF8\uB300\uB85C \uB123\uC73C\uC138\uC694.",
16724
18662
  "\uAC19\uC740 \uC81C\uBAA9\uC73C\uB85C \uC774 \uB3C4\uAD6C\uB97C \uB2E4\uC2DC \uBD88\uB7EC\uB3C4 \uC0C8\uB85C \uB9CC\uB4E4\uC9C0 \uC54A\uACE0 \uC774 \uD504\uB85C\uC81D\uD2B8\uAC00 \uB098\uC635\uB2C8\uB2E4."
16725
18663
  ].join("\n")
16726
18664
  };
16727
18665
  });
16728
18666
  }
16729
-
16730
- // src/mcp/server.ts
16731
- var RESOLUTIONS = ["480p", "720p", "1080p", "4k"];
16732
- var RATIOS = ["16:9", "9:16", "1:1", "4:3", "21:9", "adaptive"];
16733
- var GENERATION_PARAMS = {
16734
- duration_s: {
16735
- type: "number",
16736
- description: "\uC601\uC0C1 \uAE38\uC774(\uCD08). \uAE30\uBCF8 5. \uBAA8\uB378\uBCC4 \uD5C8\uC6A9 \uBC94\uC704\uAC00 \uB2E4\uB974\uB2E4(2.0 \uACC4\uC5F4 4~15, 2.5 \uACC4\uC5F4 4~30)."
16737
- },
16738
- resolution: { type: "string", enum: RESOLUTIONS, description: "\uAE30\uBCF8 720p." },
16739
- ratio: { type: "string", enum: RATIOS, description: "\uAE30\uBCF8 16:9." },
16740
- model: { type: "string", description: "\uC0DD\uB7B5\uD558\uBA74 \uC11C\uBC84 \uAE30\uBCF8 \uBAA8\uB378." }
16741
- };
16742
- var TOOLS = [
16743
- {
16744
- name: "democut_auth_status",
16745
- description: "DemoCut \uB85C\uADF8\uC778 \uC0C1\uD0DC\uC640 \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4\xB7\uC5ED\uD560\xB7\uD2F0\uC5B4\uB97C \uD655\uC778\uD55C\uB2E4. \uBD80\uC791\uC6A9\uB3C4 \uACFC\uAE08\uB3C4 \uC5C6\uB2E4. \uB2E4\uB978 \uB3C4\uAD6C\uAC00 '\uAD8C\uD55C \uC5C6\uC74C' \uC73C\uB85C \uC2E4\uD328\uD558\uBA74 \uC774\uAC78\uB85C \uC0C1\uD0DC\uB97C \uBA3C\uC800 \uD655\uC778\uD558\uB77C.",
16746
- inputSchema: { type: "object", properties: {}, additionalProperties: false }
16747
- },
16748
- {
16749
- name: "democut_estimate_video_cost",
16750
- description: "\uC601\uC0C1 \uC0DD\uC131 \uC608\uC0C1 \uBE44\uC6A9\uC744 \uACC4\uC0B0\uD55C\uB2E4. **\uBD80\uC791\uC6A9\uB3C4 \uACFC\uAE08\uB3C4 \uC5C6\uB2E4.** \uBE44\uC6A9\uC740 \uD574\uC0C1\uB3C4\xB7\uAE38\uC774\xB7\uBE44\uC728\uB85C\uB9CC \uACB0\uC815\uB418\uBBC0\uB85C \uC815\uD655\uD558\uB2E4. democut_generate_video \uB97C \uBD80\uB974\uAE30 \uC804\uC5D0 \uBC18\uB4DC\uC2DC \uBA3C\uC800 \uD638\uCD9C\uD574 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uAE08\uC561\uC744 \uC54C\uB9AC\uACE0 \uB3D9\uC758\uB97C \uBC1B\uC544\uB77C.",
16751
- inputSchema: {
16752
- type: "object",
16753
- properties: { ...GENERATION_PARAMS },
16754
- additionalProperties: false
18667
+ var projectPath = (slug, tail) => `/api/projects/${encodeURIComponent(slug)}${tail}`;
18668
+ function needSlug(a) {
18669
+ const slug = (a.slug ?? "").trim();
18670
+ if (!slug) {
18671
+ return { isError: true, text: "slug \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4 \u2014 democut_list_projects \uB85C \uD655\uC778\uD558\uC138\uC694." };
18672
+ }
18673
+ return slug;
18674
+ }
18675
+ function getOptions(a = {}, deps = {}) {
18676
+ return guarded(READ_ONLY, async () => {
18677
+ const slug = needSlug(a);
18678
+ if (typeof slug !== "string") return slug;
18679
+ const base = resolveBaseUrl(deps.baseUrl);
18680
+ const out2 = await apiRequest(base, projectPath(slug, "/options"), {}, deps);
18681
+ const modes = (out2.modes ?? []).map((m) => String(m.mode ?? "?")).join(", ") || "-";
18682
+ return {
18683
+ text: [
18684
+ `\uD504\uB85C\uC81D\uD2B8 ${out2.project.slug} \u2014 ${out2.project.ratio} ${out2.project.resolution}`,
18685
+ `\uAD8C\uC7A5 \uBC29\uC2DD: ${out2.recommended_mode} \xB7 \uAC00\uB2A5\uD55C \uBC29\uC2DD: ${modes}`,
18686
+ `\uBCF4\uC774\uC2A4 ${(out2.voices ?? []).length}\uAC1C \xB7 \uC544\uBC14\uD0C0 ${(out2.characters ?? []).length}\uAC1C \xB7 \uBC30\uACBD ${(out2.backgrounds ?? []).length}\uAC1C`,
18687
+ `\uC794\uC561 ${krw(out2.balance_krw)} \xB7 \uC694\uC728 v${out2.rate_version}`,
18688
+ "\uB2E8\uAC00\uB294 \uCC38\uACE0\uAC12\uC785\uB2C8\uB2E4 \u2014 \uCCAD\uAD6C\uB294 democut_estimate_cuts \uAC00 \uC900 total_krw\xB7rate_version \uC73C\uB85C \uD569\uB2C8\uB2E4."
18689
+ ].join("\n")
18690
+ };
18691
+ });
18692
+ }
18693
+ function uploadAsset(a = {}, deps = {}) {
18694
+ return guarded(READ_ONLY, async () => {
18695
+ const base = resolveBaseUrl(deps.baseUrl);
18696
+ if (!a.kind) return { isError: true, text: "kind \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4 (image\xB7audio\xB7video\xB7subtitle\xB7code \uB4F1)." };
18697
+ if (a.local_path && a.source_url) {
18698
+ return { isError: true, text: "local_path \uC640 source_url \uC911 \uD558\uB098\uB9CC \uC8FC\uC138\uC694." };
16755
18699
  }
16756
- },
16757
- {
16758
- name: "democut_generate_video",
16759
- description: "\uC601\uC0C1 \uC0DD\uC131\uC744 \uC81C\uCD9C\uD55C\uB2E4. **\uC2E4\uC81C\uB85C \uB3C8\uC774 \uB098\uAC04\uB2E4.** \uBA3C\uC800 democut_estimate_video_cost \uB85C \uAE08\uC561\uC744 \uD655\uC778\uD558\uACE0 \uC0AC\uC6A9\uC790 \uB3D9\uC758\uB97C \uBC1B\uC740 \uB4A4, \uADF8 \uAE08\uC561\uC744 confirmed_cost_usd \uC5D0 \uADF8\uB300\uB85C \uB123\uC5B4 \uD638\uCD9C\uD558\uB77C \u2014 \uC11C\uBC84\uAC00 \uB2E4\uC2DC \uACC4\uC0B0\uD574 \uB300\uC870\uD558\uACE0 \uC5B4\uAE0B\uB098\uBA74 \uAC70\uBD80\uD55C\uB2E4. \uC2E4\uD328\uD574\uB3C4 **\uB2E4\uC2DC \uC81C\uCD9C\uD558\uC9C0 \uB9C8\uB77C**: \uC7AC\uC81C\uCD9C\uC740 \uBCC4\uAC1C \uC0DD\uC131\uC774\uB77C \uB2E4\uC2DC \uACFC\uAE08\uB41C\uB2E4. \uC911\uBCF5 \uC81C\uCD9C \uBC29\uC5B4\uB294 \uC11C\uBC84 \uD504\uB85C\uC138\uC2A4 \uC548\uC5D0\uC11C\uB9CC \uB3D9\uC791\uD558\uBBC0\uB85C, \uAC19\uC740 \uC694\uCCAD\uC744 \uB3D9\uC2DC\uC5D0 \uB450 \uBC88 \uBCF4\uB0B4\uBA74 \uB450 \uAC74\uC774 \uC81C\uCD9C\uB420 \uC218 \uC788\uB2E4 \u2014 \uD55C \uBC88\uB9CC \uBCF4\uB0B4\uACE0 \uACB0\uACFC\uB97C \uC870\uD68C\uD558\uB77C.",
16760
- inputSchema: {
16761
- type: "object",
16762
- properties: {
16763
- prompt: { type: "string", description: "\uC0DD\uC131\uD560 \uC601\uC0C1 \uC124\uBA85." },
16764
- confirmed_cost_usd: {
16765
- type: "number",
16766
- description: "democut_estimate_video_cost \uAC00 \uB3CC\uB824\uC900 cost_usd \uC6D0\uAC12. \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uC774 \uAE08\uC561\uC744 \uC54C\uB9AC\uACE0 \uB3D9\uC758\uB97C \uBC1B\uC740 \uB4A4\uC5D0 \uB123\uC5B4\uB77C."
16767
- },
16768
- ...GENERATION_PARAMS,
16769
- seed: { type: "number", description: "\uACB0\uC815\uB860\uC801 \uC0DD\uC131\uC6A9 seed." },
16770
- image_urls: {
16771
- type: "array",
16772
- items: { type: "string" },
16773
- description: "image\u2192video \uCC38\uC870. **\uACF5\uAC1C HTTPS URL** \uB9CC \uAC00\uB2A5(\uCD5C\uB300 4\uAC1C)."
16774
- },
16775
- generate_audio: { type: "boolean", description: "\uC624\uB514\uC624 \uD568\uAED8 \uC0DD\uC131. \uAE30\uBCF8 false." },
16776
- watermark: { type: "boolean", description: "\uC6CC\uD130\uB9C8\uD06C. \uAE30\uBCF8 false." },
16777
- project_slug: {
16778
- type: "string",
16779
- description: "\uBE44\uC6A9\uC774 \uADC0\uC18D\uB420 \uD504\uB85C\uC81D\uD2B8 \uC2AC\uB7EC\uADF8 \u2014 **\uD544\uC218**. \uBCF8\uC778 \uC18C\uC720\uAC00 \uC544\uB2C8\uBA74 404. \uC601\uC0C1 \uC0DD\uC131\uC740 \uACFC\uAE08 \uB300\uC0C1\uC774\uB77C \uADC0\uC18D \uC5C6\uB294 \uC9C0\uCD9C\uC740 \uB300\uC870\xB7\uC815\uC0B0\uC774 \uBD88\uAC00\uB2A5\uD558\uB2E4. \uC2AC\uB7EC\uADF8\uB97C \uBAA8\uB974\uBA74 democut_list_projects \uB85C \uBA3C\uC800 \uD655\uC778\uD558\uB77C \u2014 \uCD94\uCE21\uD558\uC9C0 \uB9C8\uB77C."
16780
- }
16781
- },
16782
- required: ["prompt", "confirmed_cost_usd", "project_slug"],
16783
- additionalProperties: false
18700
+ if (a.local_path) {
18701
+ const path = a.local_path;
18702
+ if (!existsSync2(path)) {
18703
+ return { isError: true, text: `\uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4: ${path}` };
18704
+ }
18705
+ const view2 = await uploadFile(base, path, { kind: a.kind, projectSlug: a.project_slug, filename: a.filename }, deps);
18706
+ return {
18707
+ text: [
18708
+ `\uC790\uC0B0 ${view2.id} (${view2.kind}) \uB97C \uC62C\uB838\uC2B5\uB2C8\uB2E4 \u2014 state=${view2.state}`,
18709
+ `${view2.mime} \xB7 ${view2.bytes.toLocaleString("ko-KR")} \uBC14\uC774\uD2B8`,
18710
+ "\uC774 id \uB97C spec \uC758 BYO \uD544\uB4DC(image_asset_id\xB7narration_asset_id\xB7source.asset_id \uB4F1)\uC5D0 \uC4F0\uC138\uC694."
18711
+ ].join("\n")
18712
+ };
16784
18713
  }
16785
- },
16786
- {
16787
- name: "democut_get_video_status",
16788
- description: "\uC0DD\uC131 \uC791\uC5C5 \uC0C1\uD0DC\uB97C \uD655\uC778\uD558\uACE0, \uC644\uB8CC\uB410\uC73C\uBA74 \uC0B0\uCD9C\uBB3C\uC744 \uB0B4\uB824\uBC1B\uB294\uB2E4. \uBD80\uC791\uC6A9\uB3C4 \uACFC\uAE08\uB3C4 \uC5C6\uB2E4. \uC11C\uBC84\uB294 \uC0B0\uCD9C\uBB3C\uC744 \uBCF4\uAD00\uD558\uC9C0 \uC54A\uC73C\uBBC0\uB85C \uC644\uB8CC \uC989\uC2DC output_path \uB85C \uC800\uC7A5\uD558\uB77C.",
16789
- inputSchema: {
16790
- type: "object",
16791
- properties: {
16792
- job_id: { type: "string", description: "democut_generate_video \uAC00 \uB3CC\uB824\uC900 job_id." },
16793
- output_path: { type: "string", description: "\uC800\uC7A5\uD560 \uB85C\uCEEC \uACBD\uB85C(\uC808\uB300\uACBD\uB85C \uAD8C\uC7A5)." },
16794
- project_slug: {
16795
- type: "string",
16796
- description: "(\uBB34\uC2DC\uB428) \uADC0\uC18D\uC740 \uC81C\uCD9C \uC2DC\uC810 \uAC12\uC73C\uB85C \uC11C\uBC84\uAC00 \uACB0\uC815\uD55C\uB2E4."
16797
- }
16798
- },
16799
- required: ["job_id"],
16800
- additionalProperties: false
18714
+ if (!a.source_url) {
18715
+ return { isError: true, text: "local_path \uB610\uB294 source_url \uC774 \uD544\uC694\uD569\uB2C8\uB2E4." };
16801
18716
  }
16802
- },
16803
- {
16804
- name: "democut_credit_balance",
16805
- description: "DemoCut \uD06C\uB808\uB527 \uC794\uC561\uC744 \uD655\uC778\uD55C\uB2E4. \uBD80\uC791\uC6A9\uB3C4 \uACFC\uAE08\uB3C4 \uC5C6\uB2E4. \uB3C8\uC774 \uB098\uAC00\uB294 \uC791\uC5C5(democut_generate_video)\uC744 \uC81C\uCD9C\uD558\uAE30 **\uC804\uC5D0** \uD638\uCD9C\uD574 \uC794\uC561\uC774 \uCDA9\uBD84\uD55C\uC9C0 \uBCF4\uB77C \u2014 \uBD80\uC871\uD558\uBA74 \uC81C\uCD9C\uC774 402 \uB85C \uAC70\uBD80\uB418\uACE0, \uADF8\uAC83\uC744 \uBBF8\uB9AC \uC544\uB294 \uD3B8\uC774 \uB0AB\uB2E4. \uC794\uC561\uC774 \uBAA8\uC790\uB77C\uBA74 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uC54C\uB9AC\uACE0 \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uAD00\uB9AC\uC790\uC5D0\uAC8C \uCDA9\uC804\uC744 \uC694\uCCAD\uD558\uB3C4\uB85D \uC548\uB0B4\uD558\uB77C. \uB124\uAC00 \uC9C1\uC811 \uCDA9\uC804\uD560 \uC218\uB294 \uC5C6\uB2E4.",
16806
- inputSchema: { type: "object", properties: {}, additionalProperties: false }
16807
- },
16808
- {
16809
- name: "democut_credit_ledger",
16810
- description: '\uD06C\uB808\uB527 \uC0AC\uC6A9 \uB0B4\uC5ED\uC744 \uCD5C\uC2E0\uC21C\uC73C\uB85C \uD655\uC778\uD55C\uB2E4. \uBD80\uC791\uC6A9\uB3C4 \uACFC\uAE08\uB3C4 \uC5C6\uB2E4. "\uC65C \uC794\uC561\uC774 \uC774\uB9CC\uD07C \uC904\uC5C8\uB098" \uB97C \uB2F5\uD560 \uB54C \uC4F4\uB2E4 \u2014 \uAC01 \uD589\uC740 \uBB34\uC5C7\uC5D0\xB7\uC5B8\uC81C\xB7\uC5BC\uB9C8\uC640, \uAC00\uB2A5\uD558\uBA74 \uC0AC\uC6A9\uB7C9(\uC608: 40,594 tokens)\uAE4C\uC9C0 \uB9D0\uD55C\uB2E4.',
16811
- inputSchema: {
16812
- type: "object",
16813
- properties: {
16814
- limit: { type: "number", description: "\uAC00\uC838\uC62C \uAC74\uC218 1~200. \uAE30\uBCF8 10." },
16815
- before_id: {
16816
- type: "number",
16817
- description: "\uC774\uC804 \uD638\uCD9C\uC774 \uB3CC\uB824\uC900 next_before_id. \uB2E4\uC74C \uD398\uC774\uC9C0\uB97C \uBCFC \uB54C\uB9CC \uB123\uC5B4\uB77C."
16818
- }
16819
- },
16820
- additionalProperties: false
18717
+ const view = await apiRequest(
18718
+ base,
18719
+ "/api/assets/fetch",
18720
+ { method: "POST", body: { kind: a.kind, project_slug: a.project_slug, filename: a.filename, source_url: a.source_url } },
18721
+ deps
18722
+ );
18723
+ return {
18724
+ text: [
18725
+ `\uC790\uC0B0 ${view.id} (${view.kind}) \uB97C \uAC00\uC838\uC624\uB294 \uC911\uC785\uB2C8\uB2E4 \u2014 state=${view.state}`,
18726
+ "democut_get_cut \uC774 \uC544\uB2C8\uB77C \uC790\uC0B0 \uC870\uD68C\uB85C state \uAC00 ready \uAC00 \uB420 \uB54C\uAE4C\uC9C0 \uAE30\uB2E4\uB9B0 \uB4A4 \uC4F0\uC138\uC694.",
18727
+ "\uC2E4\uD328\uD558\uBA74 state=deleted \uAC00 \uB418\uACE0 \uC0AC\uC720\uB294 meta.error \uC5D0 \uB0A8\uC2B5\uB2C8\uB2E4."
18728
+ ].join("\n")
18729
+ };
18730
+ });
18731
+ }
18732
+ function estimateCuts(a = {}, deps = {}) {
18733
+ return guarded(READ_ONLY, async () => {
18734
+ const slug = needSlug(a);
18735
+ if (typeof slug !== "string") return slug;
18736
+ const base = resolveBaseUrl(deps.baseUrl);
18737
+ const out2 = await apiRequest(
18738
+ base,
18739
+ projectPath(slug, "/cuts/estimate"),
18740
+ { method: "POST", body: { items: a.items ?? [], imports: a.imports ?? [] } },
18741
+ deps
18742
+ );
18743
+ const lines = [`\uACAC\uC801 (\uC694\uC728 v${out2.rate_version}) \u2014 \uBD80\uC791\uC6A9\uB3C4 \uACFC\uAE08\uB3C4 \uC5C6\uC2B5\uB2C8\uB2E4`];
18744
+ for (const item of out2.items ?? []) {
18745
+ const label = item.cut_id ?? "(\uC0C8 \uCEF7)";
18746
+ if (item.blocked_reason) {
18747
+ lines.push(` ${label}: \uACAC\uC801 \uBD88\uAC00 \u2014 ${item.blocked_reason}`);
18748
+ continue;
18749
+ }
18750
+ const rows = (item.rows ?? []).map((r) => `${r.key} ${krw(r.krw)}`).join(", ") || "\uD589 \uC5C6\uC74C";
18751
+ lines.push(` ${label}: ${krw(item.total_krw ?? 0)} (${rows})`);
16821
18752
  }
16822
- },
16823
- {
16824
- name: "democut_list_projects",
16825
- description: "\uC774 \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4\uC758 \uD504\uB85C\uC81D\uD2B8 \uBAA9\uB85D\uC744 \uCD5C\uADFC \uC218\uC815\uC21C\uC73C\uB85C \uD655\uC778\uD55C\uB2E4. \uBD80\uC791\uC6A9\uB3C4 \uACFC\uAE08\uB3C4 \uC5C6\uB2E4. democut_generate_video \uC758 project_slug \uB294 **\uC2E4\uC7AC\uD558\uB294 \uBCF8\uC778 \uD504\uB85C\uC81D\uD2B8\uC5EC\uC57C** \uD558\uACE0 \uD2C0\uB9AC\uBA74 404 \uB2E4 \u2014 \uC2AC\uB7EC\uADF8\uB97C \uBAA8\uB974\uBA74 \uCD94\uCE21\uD558\uC9C0 \uB9D0\uACE0 \uC774\uAC78\uB85C \uBA3C\uC800 \uD655\uC778\uD558\uB77C. \uBAA9\uB85D\uC774 \uBE44\uC5B4 \uC788\uAC70\uB098 \uB9C8\uB545\uD55C \uAC83\uC774 \uC5C6\uC73C\uBA74 democut_create_project \uB85C \uB9CC\uB4E4 \uC218 \uC788\uB2E4 \u2014 \uB2E8 \uBA3C\uC800 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uC5B4\uB290 \uAC83\uC5D0 \uB123\uC744\uC9C0, \uC0C8\uB85C \uB9CC\uB4E0\uB2E4\uBA74 \uC5B4\uB5A4 \uC774\uB984\uC73C\uB85C \uD560\uC9C0 \uBB3C\uC5B4\uB77C.",
16826
- inputSchema: {
16827
- type: "object",
16828
- properties: {
16829
- limit: { type: "number", description: "\uAC00\uC838\uC62C \uAC74\uC218 1~100. \uAE30\uBCF8 30." },
16830
- query: {
16831
- type: "string",
16832
- description: "\uC2AC\uB7EC\uADF8\xB7\uC81C\uBAA9\uC5D0 \uC774 \uBB38\uC790\uC5F4\uC774 \uB4E0 \uAC83\uB9CC (\uB300\uC18C\uBB38\uC790 \uBB34\uC2DC). \uCC3E\uB294 \uD504\uB85C\uC81D\uD2B8 \uC774\uB984\uC744 \uC54C \uB54C\uB9CC \uC4F0\uB77C \u2014 \uBE44\uC6B0\uBA74 \uCD5C\uADFC \uC218\uC815\uC21C \uC804\uCCB4\uB2E4."
18753
+ lines.push(
18754
+ `\uCD1D\uC561 ${krw(out2.total_krw)} \xB7 \uC794\uC561 ${krw(out2.balance_krw)} \u2192 \uB0A8\uC744 \uC794\uC561 ${krw(out2.balance_after_krw)}`,
18755
+ out2.can_afford ? "\uCDA9\uBD84\uD569\uB2C8\uB2E4." : "\u26A0\uFE0F \uC794\uC561\uC774 \uBD80\uC871\uD569\uB2C8\uB2E4 \u2014 \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uAD00\uB9AC\uC790\uC5D0\uAC8C \uCDA9\uC804\uC744 \uC694\uCCAD\uD558\uC138\uC694.",
18756
+ "\uC774 total_krw\xB7rate_version \uC744 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uBCF4\uC5EC \uC8FC\uACE0 **\uADF8\uB300\uB85C** democut_make_cuts \uC5D0 \uB123\uC73C\uC138\uC694."
18757
+ );
18758
+ if (out2.mock) lines.push("(mock \uB2E8\uAC00\uC785\uB2C8\uB2E4 \u2014 \uC2E4\uC81C \uCCAD\uAD6C\uC640 \uB2E4\uB97C \uC218 \uC788\uC2B5\uB2C8\uB2E4.)");
18759
+ return { text: lines.join("\n") };
18760
+ });
18761
+ }
18762
+ function makeCuts(a = {}, deps = {}) {
18763
+ return guarded(SUBMITTING, async () => {
18764
+ const slug = needSlug(a);
18765
+ if (typeof slug !== "string") return slug;
18766
+ const base = resolveBaseUrl(deps.baseUrl);
18767
+ const out2 = await apiRequest(
18768
+ base,
18769
+ projectPath(slug, "/cuts/make"),
18770
+ {
18771
+ method: "POST",
18772
+ body: {
18773
+ items: a.items ?? [],
18774
+ confirmed_total_krw: a.confirmed_total_krw,
18775
+ rate_version: a.rate_version
16833
18776
  }
16834
18777
  },
16835
- additionalProperties: false
18778
+ deps
18779
+ );
18780
+ const lines = [`\uCEF7 ${(out2.jobs ?? []).length}\uAC1C\uB97C \uC81C\uCD9C\uD588\uC2B5\uB2C8\uB2E4 (batch ${out2.batch_id}) \u2014 \uC794\uC561\uC5D0\uC11C \uCC28\uAC10\uB410\uC2B5\uB2C8\uB2E4`];
18781
+ for (const ref of out2.jobs ?? []) {
18782
+ const tail = ref.deduplicated ? " (\uC774\uBBF8 \uAC19\uC740 \uC791\uC5C5\uC774 \uB3CC\uACE0 \uC788\uC5B4 \uC7AC\uC0AC\uC6A9)" : "";
18783
+ lines.push(` ${ref.cut_id} \u2192 ${ref.job_id} ${krw(ref.quote.total_krw)}${tail}`);
16836
18784
  }
16837
- },
16838
- {
16839
- name: "democut_create_project",
16840
- description: "\uD504\uB85C\uC81D\uD2B8\uB97C \uC0C8\uB85C \uB9CC\uB4E0\uB2E4. **\uACFC\uAE08\uC740 \uC5C6\uC9C0\uB9CC \uACC4\uC815\uC5D0 \uB0A8\uB294 \uC4F0\uAE30 \uC791\uC5C5\uC774\uB2E4.** \uC601\uC0C1 \uC0DD\uC131\uC740 \uBE44\uC6A9\uC744 \uADC0\uC18D\uC2DC\uD0AC \uD504\uB85C\uC81D\uD2B8\uAC00 \uC788\uC5B4\uC57C \uD558\uBBC0\uB85C, democut_list_projects \uAC00 \uBE44\uC5B4 \uC788\uAC70\uB098 \uB9C8\uB545\uD55C \uAC83\uC774 \uC5C6\uC744 \uB54C \uC4F4\uB2E4. \u2605 \uBA3C\uC800 democut_list_projects \uB85C **\uC4F8 \uB9CC\uD55C \uAC83\uC774 \uC774\uBBF8 \uC788\uB294\uC9C0 \uBCF4\uACE0**, \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uC5B4\uB290 \uAC83\uC5D0 \uB123\uC744\uC9C0 \uBB3C\uC5B4\uB77C \u2014 \uBB3C\uC5B4\uBCF4\uC9C0 \uC54A\uACE0 \uC0C8\uB85C \uB9CC\uB4E4\uBA74 \uBE44\uC2B7\uD55C \uD504\uB85C\uC81D\uD2B8\uAC00 \uC313\uC778\uB2E4. \uAC19\uC740 \uC81C\uBAA9\uC73C\uB85C \uB2E4\uC2DC \uBD80\uB974\uBA74 \uC0C8\uB85C \uB9CC\uB4E4\uC9C0 \uC54A\uACE0 **\uAE30\uC874 \uAC83\uC744 \uB3CC\uB824\uC900\uB2E4**(\uC7AC\uC2DC\uB3C4 \uC548\uC804).",
16841
- inputSchema: {
16842
- type: "object",
16843
- properties: {
16844
- title: {
16845
- type: "string",
16846
- description: "\uD504\uB85C\uC81D\uD2B8 \uC774\uB984 1~100\uC790. \uC0AC\uC6A9\uC790\uAC00 \uC54C\uC544\uBCFC \uC218 \uC788\uAC8C \uC9D3\uACE0, \uAC00\uB2A5\uD558\uBA74 \uC0AC\uC6A9\uC790\uAC00 \uBD80\uB978 \uC774\uB984\uC744 \uADF8\uB300\uB85C \uC4F0\uB77C."
18785
+ lines.push("\uB2E4\uC74C \uD638\uCD9C: democut_wait_jobs \uC5D0 \uC704 job_id \uB4E4\uC744 \uB123\uC73C\uC138\uC694.");
18786
+ return { text: lines.join("\n") };
18787
+ });
18788
+ }
18789
+ var WAIT_CHUNK_S = 30;
18790
+ var LIST_LINES = 20;
18791
+ function listing(lines) {
18792
+ if (lines.length <= LIST_LINES) return lines;
18793
+ return [...lines.slice(0, LIST_LINES), ` \u2026\uC678 ${lines.length - LIST_LINES}\uAC1C`];
18794
+ }
18795
+ var ACTIVE_JOB_STATES = /* @__PURE__ */ new Set(["queued", "running"]);
18796
+ function jobLine(j) {
18797
+ const bits = [`${j.job_id} [${j.state}] ${j.kind}`];
18798
+ if (j.progress?.pct != null) bits.push(`${Math.round(j.progress.pct)}%`);
18799
+ if (j.error) bits.push(`\uC624\uB958 ${j.error.code}: ${j.error.message}`);
18800
+ if (j.result?.version != null) bits.push(`\u2192 v${j.result.version}`);
18801
+ return bits.join(" \xB7 ");
18802
+ }
18803
+ function waitJobs(a = {}, deps = {}) {
18804
+ return guarded(READ_ONLY, async () => {
18805
+ const base = resolveBaseUrl(deps.baseUrl);
18806
+ const budget = Math.max(0, Math.min(Math.trunc(a.timeout_s ?? 600), 600));
18807
+ const deadline = Date.now() + budget * 1e3;
18808
+ let ids = [...a.job_ids ?? []];
18809
+ let narrowed = false;
18810
+ const settled = /* @__PURE__ */ new Map();
18811
+ let pending = [];
18812
+ const exploreState = ids.length ? void 0 : a.state;
18813
+ const ignoredState = ids.length && a.state ? a.state : void 0;
18814
+ for (; ; ) {
18815
+ const left = Math.max(0, Math.floor((deadline - Date.now()) / 1e3));
18816
+ const q = new URLSearchParams();
18817
+ if (ids.length) q.set("ids", ids.join(","));
18818
+ if (!narrowed && a.batch_id) q.set("batch_id", a.batch_id);
18819
+ if (!narrowed && a.project) q.set("project", a.project);
18820
+ if (!narrowed && exploreState) q.set("state", exploreState);
18821
+ q.set("wait", String(Math.min(WAIT_CHUNK_S, left)));
18822
+ const out2 = await apiRequest(base, `/api/jobs?${q}`, {}, deps);
18823
+ pending = (out2.jobs ?? []).filter((j) => ACTIVE_JOB_STATES.has(j.state));
18824
+ for (const j of out2.jobs ?? []) {
18825
+ if (!ACTIVE_JOB_STATES.has(j.state)) settled.set(j.job_id, j);
18826
+ }
18827
+ if (pending.length === 0 || left <= 0) break;
18828
+ ids = pending.map((j) => j.job_id);
18829
+ narrowed = true;
18830
+ }
18831
+ const jobs = [...settled.values(), ...pending];
18832
+ const allDone = pending.length === 0;
18833
+ const ordered = [...jobs].sort(
18834
+ (x, y) => Number(!ACTIVE_JOB_STATES.has(x.state)) - Number(!ACTIVE_JOB_STATES.has(y.state))
18835
+ );
18836
+ const lines = [`\uC7A1 ${jobs.length}\uAC1C \u2014 ${allDone ? "\uC804\uBD80 \uB05D\uB0AC\uC2B5\uB2C8\uB2E4" : "\uC544\uC9C1 \uC9C4\uD589 \uC911\uC785\uB2C8\uB2E4"}`];
18837
+ lines.push(...listing(ordered.map((j) => ` ${jobLine(j)}`)));
18838
+ if (ignoredState !== void 0) {
18839
+ lines.push(`(job_ids \uB97C \uC8FC\uC168\uC73C\uBBC0\uB85C state=${ignoredState} \uD544\uD130\uB294 \uC4F0\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4 \u2014 \uC694\uCCAD\uD55C id \uC804\uBD80\uB97C \uBD05\uB2C8\uB2E4.)`);
18840
+ }
18841
+ lines.push(
18842
+ allDone ? "\uB05D\uB09C \uC7A1\uC758 \uACB0\uACFC\uB294 democut_get_cut \uC758 versions \uB85C \uD655\uC778\uD558\uACE0 democut_download \uB85C \uB0B4\uB824\uBC1B\uC2B5\uB2C8\uB2E4." : "\uC544\uC9C1 \uC548 \uB05D\uB09C job_ids \uB85C democut_wait_jobs \uB97C \uB2E4\uC2DC \uBD80\uB974\uC138\uC694."
18843
+ );
18844
+ return { text: lines.join("\n") };
18845
+ });
18846
+ }
18847
+ function getCut(a = {}, deps = {}) {
18848
+ return guarded(READ_ONLY, async () => {
18849
+ const slug = needSlug(a);
18850
+ if (typeof slug !== "string") return slug;
18851
+ const cutId = (a.cut_id ?? "").trim();
18852
+ if (!cutId) return { isError: true, text: "cut_id \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4." };
18853
+ const base = resolveBaseUrl(deps.baseUrl);
18854
+ const cut = await apiRequest(base, projectPath(slug, `/cuts/${encodeURIComponent(cutId)}`), {}, deps);
18855
+ const head = [`${cut.id} [${cut.state}] ${cut.mode}`];
18856
+ if (cut.title) head.push(`\u300C${cut.title}\u300D`);
18857
+ if (cut.current_version != null) head.push(`\uB300\uD45C v${cut.current_version}`);
18858
+ if (cut.dirty_fields?.length) head.push(`\uB2EC\uB77C\uC9C4 \uD544\uB4DC: ${cut.dirty_fields.join(", ")}`);
18859
+ if (cut.estimated_cost_krw != null) head.push(`\uC608\uC0C1 ${krw(cut.estimated_cost_krw)}`);
18860
+ if (cut.blocked_reason) head.push(`\uB9C9\uD798: ${cut.blocked_reason}`);
18861
+ const versions = [...cut.versions ?? []].sort((x, y) => y.version - x.version);
18862
+ const lines = [head.join(" \xB7 ")];
18863
+ lines.push(
18864
+ ...listing(
18865
+ versions.map((v) => {
18866
+ const roles = ["video", "audio", "subtitle", "still"].filter((r) => v.download?.[r]);
18867
+ return ` v${v.version} (${v.origin}) ${v.width}x${v.height} ${v.duration_ms}ms \xB7 \uB0B4\uB824\uBC1B\uAE30: ${roles.join(", ") || "\uC5C6\uC74C"}`;
18868
+ })
18869
+ )
18870
+ );
18871
+ if (cut.next_calls?.length) lines.push(`\uB2E4\uC74C \uD638\uCD9C: ${cut.next_calls.join(", ")}`);
18872
+ return { text: lines.join("\n") };
18873
+ });
18874
+ }
18875
+ function download(a = {}, deps = {}) {
18876
+ return guarded(READ_ONLY, async () => {
18877
+ const slug = needSlug(a);
18878
+ if (typeof slug !== "string") return slug;
18879
+ const cutId = (a.cut_id ?? "").trim();
18880
+ if (!cutId) return { isError: true, text: "cut_id \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4." };
18881
+ if (typeof a.version === "boolean" || !Number.isInteger(Number(a.version)) || Number(a.version) < 1) {
18882
+ return { isError: true, text: `version \uC740 1 \uC774\uC0C1\uC758 \uC815\uC218\uC5EC\uC57C \uD569\uB2C8\uB2E4: ${JSON.stringify(a.version)}` };
18883
+ }
18884
+ const version2 = Number(a.version);
18885
+ const kind = a.kind ?? "video";
18886
+ const base = resolveBaseUrl(deps.baseUrl);
18887
+ const q = new URLSearchParams({ kind });
18888
+ const out2 = await apiRequest(
18889
+ base,
18890
+ projectPath(slug, `/cuts/${encodeURIComponent(cutId)}/versions/${version2}/download?${q}`),
18891
+ {},
18892
+ deps
18893
+ );
18894
+ if (a.output_path) {
18895
+ await (deps.downloadImpl ? deps.downloadImpl(out2.url, a.output_path) : saveUrlToFile(out2.url, a.output_path, deps.fetchImpl ?? fetch));
18896
+ return { text: `${kind} \uB97C \uC800\uC7A5\uD588\uC2B5\uB2C8\uB2E4: ${a.output_path}` };
18897
+ }
18898
+ return {
18899
+ text: [
18900
+ `${kind} \uB0B4\uB824\uBC1B\uAE30 \uC8FC\uC18C (\uB9CC\uB8CC ${out2.expires_at}):`,
18901
+ out2.url,
18902
+ "output_path \uB97C \uC8FC\uBA74 \uC774 \uD30C\uC77C\uC744 \uADF8 \uACBD\uB85C\uC5D0 \uBC14\uB85C \uC800\uC7A5\uD569\uB2C8\uB2E4."
18903
+ ].join("\n")
18904
+ };
18905
+ });
18906
+ }
18907
+ function importCut(a = {}, deps = {}) {
18908
+ return guarded(SUBMITTING, async () => {
18909
+ const slug = needSlug(a);
18910
+ if (typeof slug !== "string") return slug;
18911
+ const cutId = (a.cut_id ?? "").trim();
18912
+ if (!cutId) return { isError: true, text: "cut_id \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4 \u2014 \uB4F1\uB85D\uD560 \uCEF7\uC774 \uC774\uBBF8 \uC788\uC5B4\uC57C \uD569\uB2C8\uB2E4." };
18913
+ const base = resolveBaseUrl(deps.baseUrl);
18914
+ const out2 = await apiRequest(
18915
+ base,
18916
+ projectPath(slug, `/cuts/${encodeURIComponent(cutId)}/import`),
18917
+ {
18918
+ method: "POST",
18919
+ body: {
18920
+ video_asset_id: a.video_asset_id,
18921
+ dialogue: a.dialogue,
18922
+ transcribe: a.transcribe ?? false,
18923
+ confirmed_total_krw: a.confirmed_total_krw ?? 0,
18924
+ rate_version: a.rate_version
16847
18925
  }
16848
18926
  },
16849
- required: ["title"],
16850
- additionalProperties: false
18927
+ deps
18928
+ );
18929
+ const total = out2.quote?.total_krw ?? 0;
18930
+ return {
18931
+ text: [
18932
+ `\uCEF7 ${out2.cut_id} \uC5D0 \uC601\uC0C1\uC744 \uB4F1\uB85D\uD558\uB294 \uC911\uC785\uB2C8\uB2E4 \u2014 ${out2.job_id}`,
18933
+ `\uCCAD\uAD6C ${krw(total)}${total === 0 ? " (\uD569\uC131\uC740 \uD558\uC9C0 \uC54A\uC73C\uBBC0\uB85C 0\uC6D0\uC785\uB2C8\uB2E4)" : " (\uC790\uB9C9 \uC804\uC0AC)"}`,
18934
+ "democut_wait_jobs \uB85C \uB05D\uB098\uBA74 democut_get_cut \uC758 versions \uC5D0 \uC0C8 \uBC84\uC804\uC774 \uC0DD\uAE41\uB2C8\uB2E4."
18935
+ ].join("\n")
18936
+ };
18937
+ });
18938
+ }
18939
+ function cancelJob(a = {}, deps = {}) {
18940
+ return guarded(READ_ONLY, async () => {
18941
+ const jobId = (a.job_id ?? "").trim();
18942
+ if (!jobId) return { isError: true, text: "job_id \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4." };
18943
+ const base = resolveBaseUrl(deps.baseUrl);
18944
+ const out2 = await apiRequest(
18945
+ base,
18946
+ `/api/jobs/${encodeURIComponent(jobId)}`,
18947
+ { method: "DELETE" },
18948
+ deps
18949
+ );
18950
+ const lines = [];
18951
+ if (out2.cancelled?.length) lines.push(`\uCDE8\uC18C\uD588\uC2B5\uB2C8\uB2E4: ${out2.cancelled.join(", ")}`);
18952
+ if (out2.not_cancellable?.length) lines.push(`\uCDE8\uC18C\uD560 \uC218 \uC5C6\uC5C8\uC2B5\uB2C8\uB2E4: ${out2.not_cancellable.join(", ")}`);
18953
+ if (lines.length === 0) {
18954
+ lines.push("\uC774\uBBF8 \uB05D\uB09C \uC7A1\uC785\uB2C8\uB2E4 \u2014 \uCDE8\uC18C\uD560 \uAC83\uC774 \uC5C6\uC2B5\uB2C8\uB2E4(\uAC19\uC740 \uC694\uCCAD\uC744 \uB2E4\uC2DC \uBCF4\uB0B4\uB3C4 \uC548\uC804\uD569\uB2C8\uB2E4).");
16851
18955
  }
16852
- }
16853
- ];
18956
+ lines.push(
18957
+ `\uD658\uBD88 ${krw(out2.refunded_krw ?? 0)}`,
18958
+ "queued \uC600\uB2E4\uBA74 \uC804\uC561, running \uC774\uBA74 \uC2E4\uD589\uAE30\uAC00 \uBA48\uCD98 \uB4A4 \uC815\uC0B0\uD569\uB2C8\uB2E4 \u2014 \uACF5\uAE09\uC790\uAC00 \uC774\uBBF8 \uACFC\uAE08\uD55C \uD589\uC740 \uD658\uBD88\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4(AI \uC601\uC0C1\uC740 \uC81C\uCD9C \uB4A4 \uCDE8\uC18C\uAC00 \uB9C9\uD799\uB2C8\uB2E4)."
18959
+ );
18960
+ return { text: lines.join("\n") };
18961
+ });
18962
+ }
18963
+
18964
+ // src/mcp/server.ts
18965
+ var TOOLS = GENERATED_TOOLS;
16854
18966
  function dispatch(name, args, deps = {}) {
16855
18967
  switch (name) {
16856
18968
  case "democut_auth_status":
16857
18969
  return authStatus(deps);
16858
- case "democut_estimate_video_cost":
16859
- return estimateCost(args, deps);
16860
- case "democut_generate_video":
16861
- return generateVideo(args, deps);
16862
- case "democut_get_video_status":
16863
- return videoStatus(args, deps);
16864
18970
  case "democut_credit_balance":
16865
18971
  return creditBalance(deps);
16866
18972
  case "democut_credit_ledger":
@@ -16869,6 +18975,24 @@ function dispatch(name, args, deps = {}) {
16869
18975
  return listProjects(args, deps);
16870
18976
  case "democut_create_project":
16871
18977
  return createProject(args, deps);
18978
+ case "democut_get_options":
18979
+ return getOptions(args, deps);
18980
+ case "democut_upload_asset":
18981
+ return uploadAsset(args, deps);
18982
+ case "democut_estimate_cuts":
18983
+ return estimateCuts(args, deps);
18984
+ case "democut_make_cuts":
18985
+ return makeCuts(args, deps);
18986
+ case "democut_wait_jobs":
18987
+ return waitJobs(args, deps);
18988
+ case "democut_get_cut":
18989
+ return getCut(args, deps);
18990
+ case "democut_download":
18991
+ return download(args, deps);
18992
+ case "democut_import_cut":
18993
+ return importCut(args, deps);
18994
+ case "democut_cancel_job":
18995
+ return cancelJob(args, deps);
16872
18996
  default:
16873
18997
  return Promise.resolve({ isError: true, text: `\uC54C \uC218 \uC5C6\uB294 \uB3C4\uAD6C: ${name}` });
16874
18998
  }
@@ -16896,37 +19020,18 @@ async function runMcpServer(deps = {}) {
16896
19020
  }
16897
19021
 
16898
19022
  // src/cli.ts
16899
- var HELP = `democut \u2014 DemoCut \uC5D0\uC774\uC804\uD2B8 CLI (OAuth2 device flow + \uC601\uC0C1 \uC0DD\uC131)
19023
+ var HELP = `democut \u2014 DemoCut \uC5D0\uC774\uC804\uD2B8 CLI (OAuth2 device flow + MCP)
16900
19024
 
16901
19025
  \uC0AC\uC6A9\uBC95:
16902
19026
  democut auth login [--no-browser] [--json] [--base-url <url>]
16903
19027
  democut auth status [--json] [--base-url <url>]
16904
19028
  democut auth logout [--base-url <url>]
16905
19029
 
16906
- democut video estimate [--duration <\uCD08>] [--resolution <\uD574\uC0C1\uB3C4>] [--ratio <\uBE44\uC728>] [--model <id>]
16907
- democut video generate "<\uD504\uB86C\uD504\uD2B8>" --project <slug> [--yes] [--wait] [--output <\uACBD\uB85C>] [\uC0DD\uC131 \uC635\uC158\u2026]
16908
- democut video status <job_id> [--wait] [--output <\uACBD\uB85C>]
16909
-
16910
19030
  democut mcp MCP \uC11C\uBC84(stdio) \u2014 \uC5D0\uC774\uC804\uD2B8\uC5D0 \uB3C4\uAD6C\uB85C \uBD99\uC778\uB2E4
16911
19031
  \uB4F1\uB85D: claude mcp add democut -- npx -y democut@latest mcp
16912
19032
  (@latest \uD544\uC218 \u2014 -y \uB294 npx \uCE90\uC2DC\uC758 \uC61B \uBC84\uC804\uC744 \uADF8\uB300\uB85C \uB744\uC6B4\uB2E4)
16913
19033
 
16914
- \uC0DD\uC131 \uC635\uC158:
16915
- --duration <\uCD08> \uAE30\uBCF8 5 (\uBAA8\uB378\uBCC4 \uD5C8\uC6A9 \uBC94\uC704 \uB2E4\uB984: 2.0=4~15, 2.5=4~30)
16916
- --resolution <r> 480p | 720p(\uAE30\uBCF8) | 1080p | 4k
16917
- --ratio <a> 16:9(\uAE30\uBCF8) | 9:16 | 1:1 | 4:3 | 21:9 | adaptive
16918
- --model <id> \uAE30\uBCF8 \uC11C\uBC84 \uC124\uC815\uAC12
16919
- --seed <n> \uACB0\uC815\uB860\uC801 \uC0DD\uC131\uC6A9 seed
16920
- --image-url <url> image\u2192video \uCC38\uC870 (\uACF5\uAC1C HTTPS URL, \uCD5C\uB300 4\uD68C \uBC18\uBCF5)
16921
- --audio \uC624\uB514\uC624 \uD568\uAED8 \uC0DD\uC131
16922
- --watermark \uC6CC\uD130\uB9C8\uD06C \uB123\uAE30
16923
- --project <slug> \uBE44\uC6A9\uC744 \uC774 \uD504\uB85C\uC81D\uD2B8\uB85C \uADC0\uC18D (generate \uD544\uC218 \u2014 \uBCF8\uC778 \uC18C\uC720 \uC2AC\uB7EC\uADF8)
16924
-
16925
19034
  \uACF5\uD1B5 \uC635\uC158:
16926
- -y, --yes \uBE44\uC6A9 \uD655\uC778 \uC5C6\uC774 \uC9C4\uD589 (\uBE44\uB300\uD654\uD615\uC5D0\uC11C\uB294 \uD544\uC218)
16927
- --wait \uC644\uB8CC\uAE4C\uC9C0 \uAE30\uB2E4\uB9BC
16928
- --timeout <\uCD08> --wait \uC2DC\uD55C (\uAE30\uBCF8 900)
16929
- -o, --output <\uACBD\uB85C> \uC0B0\uCD9C\uBB3C \uC800\uC7A5 (\uC11C\uBC84\uB294 \uBCF4\uAD00\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2014 \uBC1B\uC544 \uB450\uC138\uC694)
16930
19035
  --json \uAE30\uACC4 \uD310\uB3C5\uC6A9 JSON \uD55C \uC904
16931
19036
  --base-url <u> DemoCut \uC11C\uBC84 URL (\uAE30\uBCF8: $DEMOCUT_BASE_URL \uB610\uB294 prod)
16932
19037
  -h, --help \uB3C4\uC6C0\uB9D0
@@ -16935,32 +19040,18 @@ var HELP = `democut \u2014 DemoCut \uC5D0\uC774\uC804\uD2B8 CLI (OAuth2 device f
16935
19040
  \uC885\uB8CC \uCF54\uB4DC:
16936
19041
  0 \uC131\uACF5 1 \uC77C\uBC18 \uC624\uB958(\uBBF8\uB85C\uADF8\uC778\xB7\uCDE8\uC18C\xB7\uC0DD\uC131 \uC2E4\uD328) 2 \uACE0\uCCD0\uC11C \uC7AC\uC2DC\uB3C4 \uAC00\uB2A5(\uACFC\uAE08 \uC5C6\uC74C)
16937
19042
  3 \uC77C\uC2DC \uC624\uB958(\uADF8\uB300\uB85C \uC7AC\uC2DC\uB3C4 \uC548\uC804) 4 \uC5C5\uC2A4\uD2B8\uB9BC \uAC70\uBD80(\uC7AC\uC2DC\uB3C4 \uC2DC \uC7AC\uACFC\uAE08 \uC704\uD5D8)
16938
- 5 --wait \uC2DC\uD55C \uCD08\uACFC \u2014 \uC2E4\uD328 \uC544\uB2D8. \uC791\uC5C5\uC740 \uACC4\uC18D \uC9C4\uD589 \uC911\uC774\uB2C8 status \uB85C \uD655\uC778(\uC7AC\uC81C\uCD9C \uAE08\uC9C0)`;
19043
+ 5 \uB300\uAE30 \uC2DC\uD55C \uCD08\uACFC(\uC608\uC57D \u2014 \uC61B video --wait \uC758 \uCF54\uB4DC, \uC2E4\uD328 \uC544\uB2D8\xB7\uC7AC\uC81C\uCD9C \uAE08\uC9C0 \uC758\uBBF8\uB294 \uC720\uC9C0)`;
16939
19044
  var VALUE_FLAGS = {
16940
- "--base-url": "baseUrl",
16941
- "--duration": "duration",
16942
- "--resolution": "resolution",
16943
- "--ratio": "ratio",
16944
- "--model": "model",
16945
- "--seed": "seed",
16946
- "--project": "project",
16947
- "--output": "output",
16948
- "-o": "output",
16949
- "--timeout": "timeout"
19045
+ "--base-url": "baseUrl"
16950
19046
  };
16951
19047
  var BOOL_FLAGS = {
16952
19048
  "--no-browser": "noBrowser",
16953
19049
  "--device": "noBrowser",
16954
- "--json": "json",
16955
- "--yes": "yes",
16956
- "-y": "yes",
16957
- "--wait": "wait",
16958
- "--audio": "audio",
16959
- "--watermark": "watermark"
19050
+ "--json": "json"
16960
19051
  };
16961
19052
  var HELP_FLAGS = /* @__PURE__ */ new Set(["-h", "--help", "-v", "--version"]);
16962
19053
  function isKnownFlag(token) {
16963
- return token in VALUE_FLAGS || token in BOOL_FLAGS || HELP_FLAGS.has(token) || token === "--image-url" || token.startsWith("--image-url=");
19054
+ return token in VALUE_FLAGS || token in BOOL_FLAGS || HELP_FLAGS.has(token);
16964
19055
  }
16965
19056
  function parseArgs(argv) {
16966
19057
  const positional = [];
@@ -16978,11 +19069,6 @@ function parseArgs(argv) {
16978
19069
  version2 = true;
16979
19070
  continue;
16980
19071
  }
16981
- if (a === "--image-url" || a.startsWith("--image-url=")) {
16982
- const v = a.startsWith("--image-url=") ? a.slice("--image-url=".length) : argv[++i];
16983
- if (v) (options.imageUrls ??= []).push(v);
16984
- continue;
16985
- }
16986
19072
  const boolKey = BOOL_FLAGS[a];
16987
19073
  if (boolKey) {
16988
19074
  options[boolKey] = true;
@@ -17043,15 +19129,6 @@ ${HELP}
17043
19129
  return EXIT.FIXABLE;
17044
19130
  }
17045
19131
  if (group === "mcp") return runMcpServer({ ...deps, baseUrl: options.baseUrl });
17046
- if (group === "video") {
17047
- if (sub === "estimate") return estimateCommand(options, deps);
17048
- if (sub === "generate") return generateCommand(arg, options, deps);
17049
- if (sub === "status") return statusCommand2(arg, options, deps);
17050
- process.stderr.write(`\uC54C \uC218 \uC5C6\uB294 video \uBA85\uB839: ${sub ?? "(\uC5C6\uC74C)"}
17051
- ${HELP}
17052
- `);
17053
- return EXIT.FIXABLE;
17054
- }
17055
19132
  process.stderr.write(`\uC54C \uC218 \uC5C6\uB294 \uBA85\uB839: ${group}
17056
19133
  ${HELP}
17057
19134
  `);