@clack-platform/cli 0.3.2 → 0.3.3

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 +5 -2
  2. package/dist/cli.js +361 -189
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -149,7 +149,7 @@ pnpm check # typecheck → 스키마 동기화 확인 → 테스트 →
149
149
  npm pack --dry-run
150
150
  ```
151
151
 
152
- `pnpm check`는 나란히 체크아웃한 클랙 모노레포가 있으면(`CLACK_MONOREPO_DIR`, 기본 `../clack`) MCP 입력 스키마 일치도 함께 확인하고, 없으면 건너뛴다. 배포 전 로컬에서 이 확인을 강제하려면 `pnpm check:release`를 쓴다. `scripts/sync-vendor-types.mjs [--check]`는 `src/vendor/` 아래 복사해 둔 클랙 공개 타입·계약 파일을 같은 방식으로 모노레포와 동기화한다.
152
+ `pnpm check`는 나란히 체크아웃한 클랙 모노레포가 있으면(`CLACK_MONOREPO_DIR`, 기본 `../clack`) MCP 입력 스키마 일치도 함께 확인하고, 없으면 건너뛴다. 배포 전 로컬에서 이 확인을 강제하려면 `pnpm check:release`를 쓴다(스키마 일치 엄격 검사와 `src/vendor/` 사본 `sync-vendor-types.mjs --check`를 함께 실행한다). `scripts/sync-vendor-types.mjs [--check]`는 `src/vendor/` 아래 복사해 둔 클랙 공개 타입·계약 파일을 같은 방식으로 모노레포와 동기화한다.
153
153
 
154
154
  GitHub Actions는 Linux·macOS·Windows × Node 20·22에서 `pnpm check`와 패키지 설치 스모크를 실행한다(`.github/workflows/ci.yml`). 배포는 `v0.1.0` 형태의 버전 태그를 push하면 `.github/workflows/publish.yml`이 태그와 `package.json` 버전 일치를 확인한 뒤 GitHub Actions의 OIDC 신뢰 게시로 npm에 공개한다.
155
155
 
@@ -181,11 +181,14 @@ clack content preview --env dev <콘텐츠-UUID> <버전-UUID>
181
181
 
182
182
  ZIP에 `clack.content.json` 또는 `.clack/`가 있으면 업로드 전에 선언·경로·크기와 프로필 파일 대응을 확인하고 표준 오류에 경고합니다. 선언이 없는 기존 정적 ZIP도 계속 업로드할 수 있습니다. 경고는 업로드를 막지 않으며 서버의 최종 검증·심사 결과를 대신하지 않습니다. 전송 없이 확인하려면 `content upload ... --dry-run`을 사용하세요.
183
183
 
184
- `content update`는 제목·설명·종류·썸네일·메타데이터·태그를 서버 PATCH와 같은 규칙으로 수정합니다(`--title`, `--description`, `--kind`, `--thumbnail-id <썸네일 작업 ID|none>`, `--metadata-mode single|localized`, `--metadata-lang ko|en|none`, `--localized-metadata '<JSON>'|none`, `--tags 쉼표목록`). 지정한 필드만 바뀌고 `--tags`·`--localized-metadata`는 통째 교체됩니다. 단일 메타데이터 모드에서는 제목을 비우면 「제목 없는 콘텐츠」로 표시되고, 언어별(`localized`) 모드에서는 빈 제목을 서버가 거부합니다(400). 썸네일 작업 ID는 `POST /v4/creator/thumbnails` 등으로 발급하며 CLI에는 썸네일 업로드 명령이 없어 보통 `none`(기본 이미지로 되돌리기)만 씁니다. 언어별 메타데이터·태그는 서버 게이트가 켜진 환경에서만 쓸 수 있습니다. 인트로 저장은 개인 액세스 토큰으로 할 수 없어 크리에이터 센터에서만 합니다.
184
+ `content update`는 제목·설명·종류·썸네일·메타데이터·태그를 서버 PATCH와 같은 규칙으로 수정합니다(`--title`, `--description`, `--kind`, `--thumbnail-id <썸네일 작업 ID|none>`, `--metadata-mode single|localized`, `--metadata-lang ko|en|none`, `--localized-metadata '<JSON>'|none`, `--tags 쉼표목록`). 지정한 필드만 바뀌고 `--tags`·`--localized-metadata`는 통째 교체됩니다. 단일 메타데이터 모드에서는 제목을 비우면 「제목 없는 콘텐츠」로 표시되고, 언어별(`localized`) 모드에서는 빈 제목을 서버가 거부합니다(400). CLI 0.3.3부터 `content thumbnail <파일>`로 본인 썸네일 작업 ID를 발급합니다. `--content <콘텐츠-UUID>`를 함께 주면 업로드 후 연결까지 수행합니다. `create`·`update`의 `--thumbnail-file <파일>`도 같은 경로를 사용하며 `--thumbnail-id`와 함께 지정할 수 없습니다. 일반 `clack upload`의 CDN URL은 썸네일 작업 ID가 아닙니다. 서버 `content config`의 `thumbnail_upload` 형식·용량·픽셀 조건을 조회하고 업로드합니다. `--dry-run`은 설정 GET과 로컬 검사만 수행합니다. 연결 실패는 오류의 `recovery.thumbnail_id`와 재연결 명령을 제공하므로 상태를 확인한 뒤 ID를 재사용하고 이미지를 다시 올리지 마세요. 언어별 메타데이터·태그는 서버 게이트가 켜진 환경에서만 쓸 수 있습니다. 인트로 저장은 개인 액세스 토큰으로 할 수 없어 크리에이터 센터에서만 합니다.
185
185
 
186
186
  `content info-version <콘텐츠-UUID> <버전-UUID>`는 승인·반려된 버전의 번들을 재사용해 바뀐 콘텐츠 정보만 담은 새 초안 버전을 만듭니다(새로 만들면 201, 같은 정보의 미게시 버전(초안·심사 중·승인 미게시)이 있으면 200이며 응답의 `created`로 구분). 새 버전은 앱 확인과 심사를 다시 받아야 합니다. 서버 게이트가 꺼진 환경에서는 `CONTENT_INFO_VERSION_DISABLED`(409)가 반환되며 `clack content config`의 `info_version.enabled`로 확인할 수 있습니다.
187
187
 
188
188
  ```bash
189
+ clack content thumbnail --env dev ./cover.png --content <콘텐츠-UUID> --json
190
+ clack content update --env dev <콘텐츠-UUID> --thumbnail-file ./cover.webp
191
+ clack content create --env dev --title "내 작품" --policy-version 2026-09-22 --thumbnail-file ./cover.png
189
192
  clack content update --env dev <콘텐츠-UUID> --title "새 제목" --tags 여행,사진
190
193
  clack content info-version --env dev <콘텐츠-UUID> <버전-UUID>
191
194
  ```
package/dist/cli.js CHANGED
@@ -7,16 +7,18 @@ import { z as z2 } from "zod";
7
7
 
8
8
  // src/core/errors.ts
9
9
  var CliError = class extends Error {
10
- constructor(message, code = "VALIDATION_ERROR", status = 400, retryAfter) {
10
+ constructor(message, code = "VALIDATION_ERROR", status = 400, retryAfter, recovery) {
11
11
  super(message);
12
12
  this.code = code;
13
13
  this.status = status;
14
14
  this.retryAfter = retryAfter;
15
+ this.recovery = recovery;
15
16
  this.name = "CliError";
16
17
  }
17
18
  code;
18
19
  status;
19
20
  retryAfter;
21
+ recovery;
20
22
  get exitCode() {
21
23
  if (this.code === "USER_API_UNAVAILABLE") return 8;
22
24
  if (this.status === 401) return 3;
@@ -184,7 +186,7 @@ function redact(value2) {
184
186
  // package.json
185
187
  var package_default = {
186
188
  name: "@clack-platform/cli",
187
- version: "0.3.2",
189
+ version: "0.3.3",
188
190
  description: "\uD074\uB799 \uACC4\uC815\uC758 \uC0C1\uD488\xB7\uCF58\uD150\uCE20\xB7\uD504\uB85C\uD544\uC744 \uAD00\uB9AC\uD558\uB294 CLI",
189
191
  type: "module",
190
192
  bin: {
@@ -223,7 +225,7 @@ var package_default = {
223
225
  test: "node scripts/test.mjs",
224
226
  build: "tsup src/cli.ts --format esm --target node20 --clean",
225
227
  check: "pnpm typecheck && node scripts/check-schema-sync.mjs && pnpm test && pnpm build",
226
- "check:release": "pnpm typecheck && node scripts/check-schema-sync.mjs --strict && pnpm test && pnpm build",
228
+ "check:release": "pnpm typecheck && node scripts/check-schema-sync.mjs --strict && node scripts/sync-vendor-types.mjs --check && pnpm test && pnpm build",
227
229
  prepack: "pnpm build"
228
230
  },
229
231
  dependencies: {
@@ -635,8 +637,8 @@ async function authoringRequest(client, raw, surface = "cli") {
635
637
  if (input.action === "tool") return client.request("GET", `${path}/tools/${input.call_id}`);
636
638
  if (input.action === "fill-status") return client.request("GET", `${path}/fill/${input.job_id}`);
637
639
  if (input.action === "upload") {
638
- const stat3 = await lstat2(input.file);
639
- if (!stat3.isFile() || stat3.isSymbolicLink() || stat3.size > 4 * 1024 * 1024 || !stat3.size) throw new CliError("\uC774\uBBF8\uC9C0\uB294 \uC77C\uBC18 \uD30C\uC77C\uC774\uBA70 4 MiB \uC774\uD558\uC5EC\uC57C \uD569\uB2C8\uB2E4.");
640
+ const stat4 = await lstat2(input.file);
641
+ if (!stat4.isFile() || stat4.isSymbolicLink() || stat4.size > 4 * 1024 * 1024 || !stat4.size) throw new CliError("\uC774\uBBF8\uC9C0\uB294 \uC77C\uBC18 \uD30C\uC77C\uC774\uBA70 4 MiB \uC774\uD558\uC5EC\uC57C \uD569\uB2C8\uB2E4.");
640
642
  const bytes2 = await readFile2(input.file);
641
643
  const body2 = new FormData();
642
644
  body2.append("field", input.field);
@@ -726,8 +728,8 @@ var ConfigStore = class {
726
728
  async read(name2) {
727
729
  const path = join(this.directory, name2);
728
730
  try {
729
- const stat3 = await lstat3(path);
730
- if (!stat3.isFile() || stat3.isSymbolicLink()) throw new CliError("\uC124\uC815 \uD30C\uC77C\uC740 \uC77C\uBC18 \uD30C\uC77C\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4.", "CONFIG_UNSAFE");
731
+ const stat4 = await lstat3(path);
732
+ if (!stat4.isFile() || stat4.isSymbolicLink()) throw new CliError("\uC124\uC815 \uD30C\uC77C\uC740 \uC77C\uBC18 \uD30C\uC77C\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4.", "CONFIG_UNSAFE");
731
733
  if ((name2 === "credentials.json" || name2 === "device-request.json") && process.platform !== "win32") await chmod(path, 384);
732
734
  const body = JSON.parse(await readFile3(path, "utf8"));
733
735
  if (!body || typeof body !== "object" || !("profiles" in body) || !body.profiles || typeof body.profiles !== "object" || Array.isArray(body.profiles)) throw new Error("\uD615\uC2DD");
@@ -2045,22 +2047,87 @@ import { Option as Option2 } from "commander";
2045
2047
 
2046
2048
  // src/lib/platform-mcp.ts
2047
2049
  import { createInterface } from "readline";
2048
- import { z as z10 } from "zod";
2050
+ import { z as z11 } from "zod";
2049
2051
 
2050
2052
  // src/commands/skill.ts
2051
2053
  import { spawn } from "child_process";
2052
2054
  import { existsSync } from "fs";
2053
2055
  import { writeFile as writeFile2 } from "fs/promises";
2054
2056
  import { createRequire } from "module";
2055
- import { join as join3, resolve as resolve8 } from "path";
2056
- import { z as z9 } from "zod";
2057
+ import { join as join3, resolve as resolve9 } from "path";
2058
+ import { z as z10 } from "zod";
2059
+
2060
+ // src/lib/content-thumbnail.ts
2061
+ import { readFile as readFile8, stat as stat2 } from "fs/promises";
2062
+ import { basename as basename3, resolve as resolve6 } from "path";
2063
+ import { imageSize as imageSize2 } from "image-size";
2064
+ import { z as z8 } from "zod";
2065
+ var policySchema = z8.object({
2066
+ thumbnail_upload_enabled: z8.boolean(),
2067
+ thumbnail_upload: z8.object({
2068
+ max_upload_bytes: z8.number().int().positive(),
2069
+ max_input_pixels: z8.number().int().positive(),
2070
+ formats: z8.array(z8.enum(["image/png", "image/jpeg", "image/webp"])).min(1)
2071
+ })
2072
+ });
2073
+ var thumbnailSchema = z8.object({ id: z8.uuid(), status: z8.literal("succeeded"), image_url: z8.url() });
2074
+ async function prepareContentThumbnail(ctx, source) {
2075
+ const response = await ctx.api.request("GET", "/v4/creator/config", { auth: false });
2076
+ const result = policySchema.safeParse(response.data);
2077
+ if (!result.success) throw new CliError("\uC11C\uBC84 \uC378\uB124\uC77C \uC5C5\uB85C\uB4DC \uC124\uC815\uC774 \uD544\uC694\uD569\uB2C8\uB2E4. \uC11C\uBC84 \uC5C5\uB370\uC774\uD2B8 \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.", "THUMBNAIL_CONFIG_REQUIRED", 409);
2078
+ if (!result.data.thumbnail_upload_enabled) throw new CliError("\uC774 \uD658\uACBD\uC5D0\uC11C\uB294 \uC378\uB124\uC77C \uC5C5\uB85C\uB4DC\uB97C \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. content config\uB85C \uAE30\uB2A5 \uC0C1\uD0DC\uB97C \uD655\uC778\uD558\uC138\uC694.", "THUMBNAIL_UPLOAD_DISABLED", 503);
2079
+ const policy = result.data.thumbnail_upload;
2080
+ const path = resolve6(source);
2081
+ const info = await stat2(path).catch(() => {
2082
+ throw new CliError("\uC378\uB124\uC77C \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.", "THUMBNAIL_FILE_NOT_FOUND", 400);
2083
+ });
2084
+ if (!info.isFile() || !info.size) throw new CliError("\uBE44\uC5B4 \uC788\uC9C0 \uC54A\uC740 \uB85C\uCEEC \uC774\uBBF8\uC9C0 \uD30C\uC77C\uC744 \uC9C0\uC815\uD558\uC138\uC694.", "THUMBNAIL_EMPTY_FILE", 400);
2085
+ if (info.size > policy.max_upload_bytes) throw new CliError(`\uC378\uB124\uC77C\uC740 ${policy.max_upload_bytes}\uBC14\uC774\uD2B8 \uC774\uD558\uC5EC\uC57C \uD569\uB2C8\uB2E4.`, "THUMBNAIL_TOO_LARGE", 413);
2086
+ const bytes2 = await readFile8(path).catch(() => {
2087
+ throw new CliError("\uC378\uB124\uC77C \uD30C\uC77C\uC744 \uC77D\uC744 \uAD8C\uD55C\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.", "THUMBNAIL_FILE_UNREADABLE", 400);
2088
+ });
2089
+ if (!bytes2.length || bytes2.length > policy.max_upload_bytes) throw new CliError("\uC77D\uB294 \uB3D9\uC548 \uC378\uB124\uC77C \uD06C\uAE30\uAC00 \uBC14\uB00C\uC5C8\uC2B5\uB2C8\uB2E4. \uD30C\uC77C\uC744 \uB2E4\uC2DC \uD655\uC778\uD558\uC138\uC694.", "THUMBNAIL_TOO_LARGE", 413);
2090
+ let mime;
2091
+ try {
2092
+ const image2 = imageSize2(bytes2);
2093
+ mime = { png: "image/png", jpg: "image/jpeg", webp: "image/webp" }[image2.type ?? ""] ?? "";
2094
+ if (!policy.formats.includes(mime) || !image2.width || !image2.height || image2.width * image2.height > policy.max_input_pixels) throw new Error();
2095
+ } catch {
2096
+ throw new CliError("\uC11C\uBC84\uAC00 \uC9C0\uC6D0\uD558\uB294 PNG\xB7JPEG\xB7WebP \uC774\uBBF8\uC9C0\uC640 \uC720\uD6A8\uD55C \uD06C\uAE30\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. \uC190\uC0C1\uB41C \uD30C\uC77C\uC740 \uB2E4\uC2DC \uC800\uC7A5\uD558\uC138\uC694.", "THUMBNAIL_INVALID_IMAGE", 422);
2097
+ }
2098
+ return { path, bytes: bytes2, mime };
2099
+ }
2100
+ async function uploadContentThumbnail(ctx, image2) {
2101
+ const body = new FormData();
2102
+ body.append("file", new Blob([new Uint8Array(image2.bytes)], { type: image2.mime }), basename3(image2.path));
2103
+ const response = await ctx.api.request("POST", "/v4/creator/thumbnails", { body });
2104
+ const parsed = thumbnailSchema.safeParse(response.data);
2105
+ if (!parsed.success) throw new CliError("\uC644\uB8CC\uB41C \uC378\uB124\uC77C \uC791\uC5C5 ID\uB97C \uC751\uB2F5\uC5D0\uC11C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uC790\uB3D9 \uC7AC\uC5C5\uB85C\uB4DC\uD558\uC9C0 \uB9C8\uC138\uC694.", "INVALID_RESPONSE", 502);
2106
+ return { data: parsed.data, time_contract: response.time_contract };
2107
+ }
2108
+ function thumbnailLinkFailure(ctx, cause, thumbnailId, contentId, creation) {
2109
+ const failure = asCliError(cause);
2110
+ const quote = (value2) => `'${value2.replaceAll("'", "'\\''")}'`;
2111
+ const prefix = `clack --base-url ${quote(ctx.api.settings.baseUrl)}${ctx.options.profile ? ` --profile ${quote(ctx.options.profile)}` : ""}`;
2112
+ const createArgs = ["title", "description", "kind", "policy_version"].flatMap((key) => creation?.[key] === void 0 ? [] : [`--${key.replaceAll("_", "-")} ${quote(String(creation[key]))}`]).join(" ");
2113
+ const command = contentId ? `${prefix} content update ${contentId} --thumbnail-id ${thumbnailId}` : `${prefix} content create ${createArgs} --thumbnail-id ${thumbnailId}`;
2114
+ const verify = contentId ? `${prefix} content status ${contentId}` : `${prefix} content list`;
2115
+ return new CliError(
2116
+ `${failure.message}
2117
+ \uC378\uB124\uC77C \uC5C5\uB85C\uB4DC\uB294 \uC644\uB8CC\uB410\uC2B5\uB2C8\uB2E4 (ID: ${thumbnailId}). ${verify}\uB85C \uC800\uC7A5 \uC5EC\uBD80\uB97C \uD655\uC778\uD55C \uB4A4 ${command}\uB85C \uC5F0\uACB0\uD558\uC138\uC694.`,
2118
+ failure.code,
2119
+ failure.status,
2120
+ failure.retryAfter,
2121
+ { thumbnail_id: thumbnailId, ...contentId ? { content_id: contentId } : {}, verification_command: verify, retry_command: command }
2122
+ );
2123
+ }
2057
2124
 
2058
2125
  // src/commands/content.ts
2059
2126
  import { createHash as createHash2 } from "crypto";
2060
- import { readFile as readFile8, stat as stat2 } from "fs/promises";
2061
- import { basename as basename3, extname, resolve as resolve6 } from "path";
2127
+ import { readFile as readFile9, stat as stat3 } from "fs/promises";
2128
+ import { basename as basename4, extname, resolve as resolve7 } from "path";
2062
2129
  import { Option } from "commander";
2063
- import { z as z8 } from "zod";
2130
+ import { z as z9 } from "zod";
2064
2131
 
2065
2132
  // src/lib/content-package.ts
2066
2133
  import { unzipSync } from "fflate";
@@ -2192,28 +2259,29 @@ function inspectContentPackage(bytes2) {
2192
2259
 
2193
2260
  // src/commands/content.ts
2194
2261
  var MAX_BYTES = 30 * 1024 * 1024;
2195
- var uuid = (value2) => parse(z8.uuid(), value2);
2196
- var createSchema = z8.object({
2197
- title: z8.string().trim().min(1).max(120),
2198
- description: z8.string().trim().max(2e3).default(""),
2199
- kind: z8.enum(["html", "gallery", "slideshow", "video"]).default("html"),
2200
- policy_version: z8.literal("2026-09-22")
2262
+ var uuid = (value2) => parse(z9.uuid(), value2);
2263
+ var createSchema = z9.object({
2264
+ title: z9.string().trim().min(1).max(120),
2265
+ description: z9.string().trim().max(2e3).default(""),
2266
+ kind: z9.enum(["html", "gallery", "slideshow", "video"]).default("html"),
2267
+ policy_version: z9.literal("2026-09-22"),
2268
+ thumbnail_id: z9.uuid().nullable().optional()
2201
2269
  }).strict();
2202
2270
  var METADATA_LANGS = ["ko", "en"];
2203
2271
  var TAG_PATTERN = /^[\p{L}\p{M}\p{N}_]{1,30}$/u;
2204
- var localizedEntry = z8.object({
2205
- title: z8.string().trim().max(120),
2206
- description: z8.string().trim().max(2e3).optional()
2272
+ var localizedEntry = z9.object({
2273
+ title: z9.string().trim().max(120),
2274
+ description: z9.string().trim().max(2e3).optional()
2207
2275
  }).strict();
2208
- var updateSchema = z8.object({
2209
- title: z8.string().trim().max(120).optional(),
2210
- description: z8.string().trim().max(2e3).optional(),
2211
- kind: z8.enum(["html", "gallery", "slideshow", "video"]).optional(),
2212
- thumbnail_id: z8.uuid().nullable().optional(),
2213
- metadata_mode: z8.enum(["single", "localized"]).optional(),
2214
- metadata_lang: z8.enum(METADATA_LANGS).nullable().optional(),
2215
- localized_metadata: z8.partialRecord(z8.enum(METADATA_LANGS), localizedEntry).nullable().optional(),
2216
- tags: z8.array(z8.string().max(64)).max(50).optional()
2276
+ var updateSchema = z9.object({
2277
+ title: z9.string().trim().max(120).optional(),
2278
+ description: z9.string().trim().max(2e3).optional(),
2279
+ kind: z9.enum(["html", "gallery", "slideshow", "video"]).optional(),
2280
+ thumbnail_id: z9.uuid().nullable().optional(),
2281
+ metadata_mode: z9.enum(["single", "localized"]).optional(),
2282
+ metadata_lang: z9.enum(METADATA_LANGS).nullable().optional(),
2283
+ localized_metadata: z9.partialRecord(z9.enum(METADATA_LANGS), localizedEntry).nullable().optional(),
2284
+ tags: z9.array(z9.string().max(64)).max(50).optional()
2217
2285
  }).strict().superRefine((value2, ctx) => {
2218
2286
  const issue = (message) => ctx.addIssue({ code: "custom", message });
2219
2287
  if (!Object.values(value2).some((entry) => entry !== void 0)) issue("\uBCC0\uACBD\uD560 \uD544\uB4DC\uB97C \uD558\uB098 \uC774\uC0C1 \uC9C0\uC815\uD558\uC138\uC694.");
@@ -2249,14 +2317,14 @@ function jsonOption(name2, value2) {
2249
2317
  }
2250
2318
  }
2251
2319
  async function prepareContentFile(source) {
2252
- const file = resolve6(source);
2320
+ const file = resolve7(source);
2253
2321
  const extension = extname(file).toLowerCase();
2254
2322
  if (![".html", ".htm", ".zip"].includes(extension)) throw new CliError("HTML \uB610\uB294 ZIP \uD30C\uC77C\uC744 \uC9C0\uC815\uD558\uC138\uC694.");
2255
2323
  let bytes2;
2256
2324
  try {
2257
- const info = await stat2(file);
2325
+ const info = await stat3(file);
2258
2326
  if (!info.isFile() || info.size === 0 || info.size > MAX_BYTES) throw new Error();
2259
- bytes2 = await readFile8(file);
2327
+ bytes2 = await readFile9(file);
2260
2328
  if (!bytes2.length || bytes2.length > MAX_BYTES) throw new Error();
2261
2329
  } catch {
2262
2330
  throw new CliError("30 MiB \uC774\uD558\uC758 \uBE44\uC5B4 \uC788\uC9C0 \uC54A\uC740 \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC788\uC5B4\uC57C \uD569\uB2C8\uB2E4.");
@@ -2265,7 +2333,7 @@ async function prepareContentFile(source) {
2265
2333
  bytes: bytes2,
2266
2334
  warnings: extension === ".zip" ? inspectContentPackage(bytes2) : [],
2267
2335
  metadata: {
2268
- filename: basename3(file),
2336
+ filename: basename4(file),
2269
2337
  content_type: extension === ".zip" ? "application/zip" : "text/html",
2270
2338
  byte_size: bytes2.length,
2271
2339
  sha256: createHash2("sha256").update(bytes2).digest("hex")
@@ -2300,8 +2368,25 @@ async function putContentFile(session2, bytes2, fetcher = fetch) {
2300
2368
  throw new CliError("\uD30C\uC77C \uC5C5\uB85C\uB4DC\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4. \uC0C8 \uC5C5\uB85C\uB4DC\uB97C \uC2DC\uC791\uD558\uC138\uC694.", "CONTENT_UPLOAD_FAILED", 0);
2301
2369
  }
2302
2370
  }
2371
+ async function saveWithThumbnail(ctx, method, path, body, file, contentId) {
2372
+ if (!file) {
2373
+ await mutate(ctx, method, path, body);
2374
+ return;
2375
+ }
2376
+ const image2 = await prepareContentThumbnail(ctx, file);
2377
+ if (ctx.options.dryRun) {
2378
+ ctx.output({ dry_run: true, method, path, body: { ...body, thumbnail_id: "<\uC5C5\uB85C\uB4DC \uD6C4 \uBC1C\uAE09>" }, thumbnail: { file: image2.path, byte_size: image2.bytes.length } });
2379
+ return;
2380
+ }
2381
+ const uploaded = await uploadContentThumbnail(ctx, image2);
2382
+ try {
2383
+ ctx.output(await ctx.api.request(method, path, { body: { ...body, thumbnail_id: uploaded.data.id } }));
2384
+ } catch (failure) {
2385
+ throw thumbnailLinkFailure(ctx, failure, uploaded.data.id, contentId, method === "POST" ? body : void 0);
2386
+ }
2387
+ }
2303
2388
  var SERVER_KEY_CENTER_ONLY = "\uC11C\uBC84 \uD0A4 \uBC1C\uAE09\xB7\uD68C\uC804\uC740 \uD06C\uB9AC\uC5D0\uC774\uD130 \uC13C\uD130(\uB0B4 \uCF58\uD150\uCE20 \u2192 \uC11C\uBC84 \uD0A4)\uC5D0\uC11C\uB9CC \uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4. CLI\xB7MCP\uC758 \uAC1C\uC778 \uC561\uC138\uC2A4 \uD1A0\uD070\uC73C\uB85C\uB294 \uC11C\uBC84 \uD0A4 \uC870\uD68C\xB7\uD3D0\uAE30\uB9CC \uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.";
2304
- var collection = z8.string().regex(/^[A-Za-z0-9_-]{1,64}$/, "\uC774\uB984\uC740 \uC601\uC22B\uC790\xB7\uBC11\uC904\xB7\uBD99\uC784\uD45C 64\uC790 \uC774\uD558\uC5EC\uC57C \uD569\uB2C8\uB2E4.");
2389
+ var collection = z9.string().regex(/^[A-Za-z0-9_-]{1,64}$/, "\uC774\uB984\uC740 \uC601\uC22B\uC790\xB7\uBC11\uC904\xB7\uBD99\uC784\uD45C 64\uC790 \uC774\uD558\uC5EC\uC57C \uD569\uB2C8\uB2E4.");
2305
2390
  var documentKey = collection;
2306
2391
  var keyId = (value2) => {
2307
2392
  const id2 = String(value2);
@@ -2313,22 +2398,41 @@ function registerContentCommands(program2, runtime2) {
2313
2398
  runtime2.action(content.command("config").description("\uCF58\uD150\uCE20 \uAE30\uB2A5 \uC81C\uACF5 \uC5EC\uBD80\xB7\uC124\uC815 \uC870\uD68C (\uB85C\uADF8\uC778 \uBD88\uD544\uC694)"), async (ctx) => ctx.output(await ctx.api.request("GET", "/v4/creator/config", { auth: false })));
2314
2399
  runtime2.action(content.command("list").description("\uB0B4 \uCF58\uD150\uCE20 \uBAA9\uB85D (creator-content:read)"), async (ctx) => ctx.output(await ctx.api.request("GET", "/v4/creator/contents")));
2315
2400
  runtime2.action(content.command("status <id>").description("\uCF58\uD150\uCE20\uC640 \uBC84\uC804\uBCC4 \uC5C5\uB85C\uB4DC\xB7\uC2EC\uC0AC\xB7\uACF5\uAC1C \uC0C1\uD0DC (creator-content:read)"), async (ctx, [id2]) => ctx.output(await ctx.api.request("GET", `/v4/creator/contents/${uuid(id2)}`)));
2316
- runtime2.action(content.command("create").description("\uCF58\uD150\uCE20 \uCD08\uC548 \uB4F1\uB85D (creator-content:write)").requiredOption("--title <text>", "\uC81C\uBAA9").option("--description <text>", "\uC124\uBA85", "").option("--kind <kind>", "html|gallery|slideshow|video", "html").requiredOption("--policy-version <version>", "\uB3D9\uC758\uD55C \uCF58\uD150\uCE20 \uC81C\uACF5 \uC815\uCC45 \uBC84\uC804"), async (ctx, _args, opts) => {
2317
- const body = parse(createSchema, { title: opts.title, description: opts.description, kind: opts.kind, policy_version: opts.policyVersion });
2318
- await mutate(ctx, "POST", "/v4/creator/contents", body);
2401
+ runtime2.action(content.command("create").description("\uCF58\uD150\uCE20 \uCD08\uC548 \uB4F1\uB85D (creator-content:write)").requiredOption("--title <text>", "\uC81C\uBAA9").option("--description <text>", "\uC124\uBA85", "").option("--kind <kind>", "html|gallery|slideshow|video", "html").requiredOption("--policy-version <version>", "\uB3D9\uC758\uD55C \uCF58\uD150\uCE20 \uC81C\uACF5 \uC815\uCC45 \uBC84\uC804").addOption(new Option("--thumbnail-file <file>", "\uB85C\uCEEC \uC378\uB124\uC77C \uC774\uBBF8\uC9C0 \uC5C5\uB85C\uB4DC \uD6C4 \uC5F0\uACB0").conflicts("thumbnailId")).addOption(new Option("--thumbnail-id <uuid|none>", "\uBC1C\uAE09\uBC1B\uC740 \uC378\uB124\uC77C ID \uB610\uB294 \uAE30\uBCF8 \uC774\uBBF8\uC9C0").conflicts("thumbnailFile")), async (ctx, _args, opts) => {
2402
+ const body = parse(createSchema, { title: opts.title, description: opts.description, kind: opts.kind, policy_version: opts.policyVersion, thumbnail_id: opts.thumbnailId === void 0 ? void 0 : opts.thumbnailId === "none" ? null : opts.thumbnailId });
2403
+ await saveWithThumbnail(ctx, "POST", "/v4/creator/contents", body, opts.thumbnailFile);
2319
2404
  });
2320
- runtime2.action(content.command("update <id>").description("\uCF58\uD150\uCE20 \uC815\uBCF4(\uC81C\uBAA9\xB7\uC124\uBA85\xB7\uC885\uB958\xB7\uC378\uB124\uC77C\xB7\uBA54\uD0C0\uB370\uC774\uD130\xB7\uD0DC\uADF8) \uC218\uC815 (creator-content:write)").option("--title <text>", "\uC81C\uBAA9 (120\uC790 \uC774\uD558, \uBE48 \uAC12\uC774\uBA74 \uC81C\uBAA9 \uC5C6\uC74C)").option("--description <text>", "\uC124\uBA85 (2000\uC790 \uC774\uD558)").addOption(new Option("--kind <kind>", "\uCF58\uD150\uCE20 \uC885\uB958").choices(["html", "gallery", "slideshow", "video"])).option("--thumbnail-id <uuid|none>", "\uC378\uB124\uC77C \uC791\uC5C5 ID, none\uC774\uBA74 \uAE30\uBCF8 \uC774\uBBF8\uC9C0\uB85C \uB418\uB3CC\uB9BC").addOption(new Option("--metadata-mode <mode>", "\uBA54\uD0C0\uB370\uC774\uD130 \uC785\uB825 \uBC29\uC2DD").choices(["single", "localized"])).option("--metadata-lang <ko|en|none>", "\uB300\uD45C \uC5B8\uC5B4").option("--localized-metadata <json|none>", `\uB300\uD45C \uC5B8\uC5B4 \uC678 \uC5B8\uC5B4\uBCC4 \uC81C\uBAA9\xB7\uC124\uBA85 JSON, \uC608: '{"en":{"title":"Title","description":"About"}}'`).option("--tags <list>", "\uD574\uC2DC\uD0DC\uADF8\uB97C \uC27C\uD45C\uB85C \uAD6C\uBD84\uD574 \uD1B5\uC9F8 \uAD50\uCCB4, \uBE48 \uBB38\uC790\uC5F4\uC774\uBA74 \uBAA8\uB450 \uC0AD\uC81C"), async (ctx, [id2], opts) => {
2405
+ runtime2.action(content.command("update <id>").description("\uCF58\uD150\uCE20 \uC815\uBCF4(\uC81C\uBAA9\xB7\uC124\uBA85\xB7\uC885\uB958\xB7\uC378\uB124\uC77C\xB7\uBA54\uD0C0\uB370\uC774\uD130\xB7\uD0DC\uADF8) \uC218\uC815 (creator-content:write)").option("--title <text>", "\uC81C\uBAA9 (120\uC790 \uC774\uD558, \uBE48 \uAC12\uC774\uBA74 \uC81C\uBAA9 \uC5C6\uC74C)").option("--description <text>", "\uC124\uBA85 (2000\uC790 \uC774\uD558)").addOption(new Option("--kind <kind>", "\uCF58\uD150\uCE20 \uC885\uB958").choices(["html", "gallery", "slideshow", "video"])).addOption(new Option("--thumbnail-id <uuid|none>", "\uC378\uB124\uC77C \uC791\uC5C5 ID, none\uC774\uBA74 \uAE30\uBCF8 \uC774\uBBF8\uC9C0\uB85C \uB418\uB3CC\uB9BC").conflicts("thumbnailFile")).addOption(new Option("--thumbnail-file <file>", "\uB85C\uCEEC \uC378\uB124\uC77C \uC774\uBBF8\uC9C0 \uC5C5\uB85C\uB4DC \uD6C4 \uC5F0\uACB0").conflicts("thumbnailId")).addOption(new Option("--metadata-mode <mode>", "\uBA54\uD0C0\uB370\uC774\uD130 \uC785\uB825 \uBC29\uC2DD").choices(["single", "localized"])).option("--metadata-lang <ko|en|none>", "\uB300\uD45C \uC5B8\uC5B4").option("--localized-metadata <json|none>", `\uB300\uD45C \uC5B8\uC5B4 \uC678 \uC5B8\uC5B4\uBCC4 \uC81C\uBAA9\xB7\uC124\uBA85 JSON, \uC608: '{"en":{"title":"Title","description":"About"}}'`).option("--tags <list>", "\uD574\uC2DC\uD0DC\uADF8\uB97C \uC27C\uD45C\uB85C \uAD6C\uBD84\uD574 \uD1B5\uC9F8 \uAD50\uCCB4, \uBE48 \uBB38\uC790\uC5F4\uC774\uBA74 \uBAA8\uB450 \uC0AD\uC81C"), async (ctx, [id2], opts) => {
2321
2406
  const body = parse(updateSchema, {
2322
2407
  title: opts.title,
2323
2408
  description: opts.description,
2324
2409
  kind: opts.kind,
2325
- thumbnail_id: opts.thumbnailId === void 0 ? void 0 : opts.thumbnailId === "none" ? null : opts.thumbnailId,
2410
+ thumbnail_id: opts.thumbnailFile ? null : opts.thumbnailId === void 0 ? void 0 : opts.thumbnailId === "none" ? null : opts.thumbnailId,
2326
2411
  metadata_mode: opts.metadataMode,
2327
2412
  metadata_lang: opts.metadataLang === void 0 ? void 0 : opts.metadataLang === "none" ? null : opts.metadataLang,
2328
2413
  localized_metadata: opts.localizedMetadata === void 0 ? void 0 : jsonOption("--localized-metadata", opts.localizedMetadata),
2329
2414
  tags: opts.tags === void 0 ? void 0 : csv(opts.tags)
2330
2415
  });
2331
- await mutate(ctx, "PATCH", `/v4/creator/contents/${uuid(id2)}`, body);
2416
+ await saveWithThumbnail(ctx, "PATCH", `/v4/creator/contents/${uuid(id2)}`, body, opts.thumbnailFile, uuid(id2));
2417
+ });
2418
+ runtime2.action(content.command("thumbnail <file>").description("\uB85C\uCEEC \uC378\uB124\uC77C \uC5C5\uB85C\uB4DC\uC640 \uC120\uD0DD\uC801 \uCF58\uD150\uCE20 \uC5F0\uACB0 (creator-content:write)").option("--content <uuid>", "\uC5C5\uB85C\uB4DC \uD6C4 \uC774 \uCF58\uD150\uCE20\uC5D0 \uC5F0\uACB0"), async (ctx, [file], opts) => {
2419
+ const contentId = opts.content === void 0 ? void 0 : uuid(opts.content);
2420
+ const image2 = await prepareContentThumbnail(ctx, file);
2421
+ if (ctx.options.dryRun) {
2422
+ ctx.output({ dry_run: true, path: "/v4/creator/thumbnails", file: image2.path, byte_size: image2.bytes.length, ...contentId ? { content_id: contentId } : {} });
2423
+ return;
2424
+ }
2425
+ const uploaded = await uploadContentThumbnail(ctx, image2);
2426
+ if (!contentId) {
2427
+ ctx.output(uploaded);
2428
+ return;
2429
+ }
2430
+ try {
2431
+ const result = await ctx.api.request("PATCH", `/v4/creator/contents/${contentId}`, { body: { thumbnail_id: uploaded.data.id } });
2432
+ ctx.output({ ...result, thumbnail: uploaded.data });
2433
+ } catch (failure) {
2434
+ throw thumbnailLinkFailure(ctx, failure, uploaded.data.id, contentId);
2435
+ }
2332
2436
  });
2333
2437
  runtime2.action(content.command("upload <id> <file>").description("HTML/ZIP \uC5C5\uB85C\uB4DC\uB85C \uC0C8 \uBE44\uACF5\uAC1C \uBC84\uC804 \uC0DD\uC131 (creator-content:write)").addOption(new Option("--header <mode>", "\uD5E4\uB354 \uD45C\uC2DC").choices(["fixed", "scroll_hide", "translucent_scroll_hide", "floating_close", "floating_close_safe_area"])).addOption(new Option("--color <scheme>", "\uD5E4\uB354 \uC0C9\uC0C1").choices(["light", "dark"])), async (ctx, [id2, file], opts) => {
2334
2438
  const path = `/v4/creator/contents/${uuid(id2)}`;
@@ -2434,15 +2538,52 @@ function dataRootField(schema, name2) {
2434
2538
  }
2435
2539
  return field2;
2436
2540
  }
2437
- function inspectEditorSemantics(skill, data, fail2) {
2541
+ var SEMANTICS_NOTES = {
2542
+ // 스킬 에디터 선언
2543
+ editor_document_permissions: () => "\uC5D0\uB514\uD130\uC5D0\uB294 document.read\uC640 document.write \uAD8C\uD55C\uC774 \uBAA8\uB450 \uD544\uC694\uD569\uB2C8\uB2E4.",
2544
+ ai_suggest_needs_ai_fill: () => "ai.suggest \uAD8C\uD55C\uC5D0\uB294 authoring.ai_fill.enabled\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.",
2545
+ image_permission_needs_tool: () => "tools.image.generate \uAD8C\uD55C\uC5D0\uB294 authoring.tools\uC758 image.generate \uC120\uC5B8\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.",
2546
+ editor_slot_aspect: () => "\uC5D0\uB514\uD130 \uC2A4\uD0AC\uC740 \uC2AC\uB86F \uBE44\uC728(aspect: slot)\uC744 \uC4F8 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uC790\uC0B0 \uB178\uB4DC\uC758 x-clack-asset.aspect\uB97C \uC4F0\uC138\uC694.",
2547
+ asset_limits_missing: () => "\uC790\uC0B0 \uB178\uB4DC\uAC00 \uC788\uB294 \uB370\uC774\uD130 \uC2A4\uD0A4\uB9C8\uC5D0\uB294 output.data.assets \uD55C\uB3C4\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.",
2548
+ asset_limits_unused: () => "\uC790\uC0B0 \uB178\uB4DC\uAC00 \uC5C6\uB294\uB370 \uC790\uC0B0 \uD55C\uB3C4\uB97C \uC120\uC5B8\uD588\uC2B5\uB2C8\uB2E4.",
2549
+ image_permission_no_asset: () => "\uC774\uBBF8\uC9C0\uB97C \uB123\uC744 \uC790\uC0B0 \uB178\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
2550
+ ai_suggest_no_node: () => "x-clack-ai.suggest\uAC00 \uCF1C\uC9C4 \uB178\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
2551
+ metadata_text_key: () => "\uACF5\uAC1C \uBB38\uC790\uC5F4 \uB8E8\uD2B8 \uD0A4\uB97C \uAC00\uB9AC\uCF1C\uC57C \uD569\uB2C8\uB2E4.",
2552
+ metadata_asset_key: () => "\uACF5\uAC1C \uC790\uC0B0 \uB8E8\uD2B8 \uD0A4\uB97C \uAC00\uB9AC\uCF1C\uC57C \uD569\uB2C8\uB2E4(\uC778\uB371\uC2A4 \uD45C\uAE30 \uBD88\uAC00).",
2553
+ // 콘텐츠 인트로
2554
+ intro_image_not_slot: () => "\uC778\uD2B8\uB85C \uC774\uBBF8\uC9C0\uB294 output.asset_slots\uC758 \uD544\uB4DC\uB9CC \uC4F8 \uC218 \uC788\uC2B5\uB2C8\uB2E4.",
2555
+ intro_images_over: ({ max }) => `\uC778\uD2B8\uB85C \uC774\uBBF8\uC9C0\uB294 \uC2AC\uB86F \uCD5C\uB300 \uC7A5\uC218 \uD569\uACC4\uAC00 ${max}\uC7A5 \uC774\uD558\uC5EC\uC57C \uD569\uB2C8\uB2E4.`,
2556
+ intro_text_field: () => "\uACF5\uAC1C \uBB38\uC790\uC5F4(text\xB7textarea) \uD3FC \uD544\uB4DC\uB97C \uAC00\uB9AC\uCF1C\uC57C \uD569\uB2C8\uB2E4.",
2557
+ intro_text_too_long: ({ max }) => `\uAC00\uB9AC\uD0A4\uB294 \uD3FC \uD544\uB4DC\uC758 \uCD5C\uB300 \uAE38\uC774\uAC00 ${max}\uC790 \uC774\uD558\uC5EC\uC57C \uD569\uB2C8\uB2E4.`,
2558
+ intro_section_field: () => "\uC18C\uAC1C \uC139\uC158\uC740 \uACF5\uAC1C \uBB38\uC790\uC5F4(text\xB7textarea) \uD3FC \uD544\uB4DC\uB9CC \uAC00\uB9AC\uD0AC \uC218 \uC788\uC2B5\uB2C8\uB2E4.",
2559
+ intro_section_too_long: () => "\uC18C\uAC1C \uC139\uC158\uC758 \uCD5C\uB300 \uAE38\uC774\uB294 2,000\uC790\uC785\uB2C8\uB2E4.",
2560
+ intro_sections_total: () => "\uC18C\uAC1C \uC139\uC158\uC758 \uCD5C\uB300 \uAE38\uC774 \uD569\uACC4\uB294 6,000\uC790\uC785\uB2C8\uB2E4.",
2561
+ intro_fallback_field: () => "\uBB38\uC790\uC5F4(text\xB7textarea) \uD3FC \uD544\uB4DC\uB9CC \uC790\uB3D9 \uAD6C\uC131\uC5D0 \uC4F8 \uC218 \uC788\uC2B5\uB2C8\uB2E4.",
2562
+ intro_fallback_too_long: ({ max }) => `\uC790\uB3D9 \uAD6C\uC131 \uCD5C\uB300 \uAE38\uC774(\uB77C\uBCA8 \uD3EC\uD568)\uAC00 ${max}\uC790\uB97C \uB118\uC2B5\uB2C8\uB2E4.`,
2563
+ // 도구·이미지 슬롯
2564
+ tool_duplicate: () => "\uB3C4\uAD6C \uC774\uB984\uC744 \uC911\uBCF5 \uC120\uC5B8\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
2565
+ tool_batch_over_calls: () => "\uC77C\uAD04 \uC0DD\uC131 \uC218\uB294 \uC138\uC158 \uD638\uCD9C \uC218\uB97C \uB118\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
2566
+ slot_field_duplicate: () => "\uC2AC\uB86F \uD544\uB4DC\uB294 \uC911\uBCF5\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
2567
+ slot_multiple_range: () => "\uB2E4\uC911 \uC2AC\uB86F \uCD5C\uC18C \uC218\uB294 \uCD5C\uB300 \uC218\uBCF4\uB2E4 \uD074 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
2568
+ slot_path_duplicate: () => "\uC2AC\uB86F \uD30C\uC77C \uACBD\uB85C\uB294 \uC911\uBCF5\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
2569
+ slot_widget_mismatch: () => "\uC2AC\uB86F\uACFC \uC774\uBBF8\uC9C0 \uC704\uC82F \uC885\uB958\uAC00 \uC77C\uCE58\uD574\uC57C \uD569\uB2C8\uB2E4.",
2570
+ // 폼 필드 참조
2571
+ form_field_missing: () => "\uC120\uC5B8\uD55C \uD3FC \uD544\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
2572
+ slot_index_invalid: () => "\uB2E4\uC911 \uC774\uBBF8\uC9C0 \uC2AC\uB86F\uC758 \uC720\uD6A8\uD55C \uC778\uB371\uC2A4\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.",
2573
+ content_ref_declaration: () => "content_ref\uC5D0\uB294 \uBB38\uC790\uC5F4\uACFC x-clack-source \uC120\uC5B8\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.",
2574
+ content_ref_import_chain: () => "\uB2E4\uB978 content_ref \uD544\uB4DC\uB85C \uAC00\uC838\uC62C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
2575
+ source_without_content_ref: () => "x-clack-source\uB294 content_ref\uC5D0\uB9CC \uC120\uC5B8\uD569\uB2C8\uB2E4.",
2576
+ image_list_declaration: () => "image_list\uC5D0\uB294 \uAC1D\uCCB4 \uBC30\uC5F4\uACFC \uC790\uC0B0 \uC120\uC5B8\uC774 \uD544\uC694\uD569\uB2C8\uB2E4."
2577
+ };
2578
+ function inspectEditorSemantics(skill, data, fail) {
2438
2579
  const editor = skill.authoring?.editor;
2439
2580
  if (!editor) return;
2440
2581
  const permissions = new Set(editor.permissions ?? []);
2441
- if (!permissions.has("document.read") || !permissions.has("document.write")) fail2("/authoring/editor/permissions", "\uC5D0\uB514\uD130\uC5D0\uB294 document.read\uC640 document.write \uAD8C\uD55C\uC774 \uBAA8\uB450 \uD544\uC694\uD569\uB2C8\uB2E4.");
2442
- if (permissions.has("ai.suggest") && skill.authoring?.ai_fill?.enabled !== true) fail2("/authoring/editor/permissions", "ai.suggest \uAD8C\uD55C\uC5D0\uB294 authoring.ai_fill.enabled\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.");
2582
+ if (!permissions.has("document.read") || !permissions.has("document.write")) fail("/authoring/editor/permissions", "editor_document_permissions");
2583
+ if (permissions.has("ai.suggest") && skill.authoring?.ai_fill?.enabled !== true) fail("/authoring/editor/permissions", "ai_suggest_needs_ai_fill");
2443
2584
  const imageTool = (skill.authoring?.tools ?? []).find((tool) => tool.tool === "image.generate");
2444
- if (permissions.has("tools.image.generate") && !imageTool) fail2("/authoring/editor/permissions", "tools.image.generate \uAD8C\uD55C\uC5D0\uB294 authoring.tools\uC758 image.generate \uC120\uC5B8\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.");
2445
- if (imageTool?.aspect === "slot") fail2("/authoring/tools", "\uC5D0\uB514\uD130 \uC2A4\uD0AC\uC740 \uC2AC\uB86F \uBE44\uC728(aspect: slot)\uC744 \uC4F8 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uC790\uC0B0 \uB178\uB4DC\uC758 x-clack-asset.aspect\uB97C \uC4F0\uC138\uC694.");
2585
+ if (permissions.has("tools.image.generate") && !imageTool) fail("/authoring/editor/permissions", "image_permission_needs_tool");
2586
+ if (imageTool?.aspect === "slot") fail("/authoring/tools", "editor_slot_aspect");
2446
2587
  if (!data) return;
2447
2588
  let assets = 0;
2448
2589
  let suggest = 0;
@@ -2450,20 +2591,20 @@ function inspectEditorSemantics(skill, data, fail2) {
2450
2591
  if (node["x-clack-asset"]) assets++;
2451
2592
  if (node["x-clack-ai"]?.suggest === true) suggest++;
2452
2593
  });
2453
- if (assets > 0 && !skill.output?.data?.assets) fail2("/output/data/assets", "\uC790\uC0B0 \uB178\uB4DC\uAC00 \uC788\uB294 \uB370\uC774\uD130 \uC2A4\uD0A4\uB9C8\uC5D0\uB294 output.data.assets \uD55C\uB3C4\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.");
2454
- if (assets === 0 && skill.output?.data?.assets) fail2("/output/data/assets", "\uC790\uC0B0 \uB178\uB4DC\uAC00 \uC5C6\uB294\uB370 \uC790\uC0B0 \uD55C\uB3C4\uB97C \uC120\uC5B8\uD588\uC2B5\uB2C8\uB2E4.");
2455
- if (assets === 0 && permissions.has("tools.image.generate")) fail2("/authoring/editor/permissions", "\uC774\uBBF8\uC9C0\uB97C \uB123\uC744 \uC790\uC0B0 \uB178\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.");
2456
- if (suggest === 0 && permissions.has("ai.suggest")) fail2("/authoring/editor/permissions", "x-clack-ai.suggest\uAC00 \uCF1C\uC9C4 \uB178\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.");
2594
+ if (assets > 0 && !skill.output?.data?.assets) fail("/output/data/assets", "asset_limits_missing");
2595
+ if (assets === 0 && skill.output?.data?.assets) fail("/output/data/assets", "asset_limits_unused");
2596
+ if (assets === 0 && permissions.has("tools.image.generate")) fail("/authoring/editor/permissions", "image_permission_no_asset");
2597
+ if (suggest === 0 && permissions.has("ai.suggest")) fail("/authoring/editor/permissions", "ai_suggest_no_node");
2457
2598
  const metadata = skill.output?.metadata ?? {};
2458
2599
  for (const key of ["title", "description"]) {
2459
2600
  if (metadata[key] === void 0) continue;
2460
2601
  const field2 = dataRootField(data, metadata[key]);
2461
- if (!field2 || field2.type !== "string" || field2["x-clack-asset"] || field2["x-clack-visibility"] !== "public") fail2(`/output/metadata/${key}`, "\uACF5\uAC1C \uBB38\uC790\uC5F4 \uB8E8\uD2B8 \uD0A4\uB97C \uAC00\uB9AC\uCF1C\uC57C \uD569\uB2C8\uB2E4.");
2602
+ if (!field2 || field2.type !== "string" || field2["x-clack-asset"] || field2["x-clack-visibility"] !== "public") fail(`/output/metadata/${key}`, "metadata_text_key");
2462
2603
  }
2463
2604
  for (const key of ["thumbnail", "thumbnail_fallback"]) {
2464
2605
  if (metadata[key] === void 0) continue;
2465
2606
  const field2 = dataRootField(data, metadata[key]);
2466
- if (!field2 || !field2["x-clack-asset"] || field2["x-clack-visibility"] !== "public") fail2(`/output/metadata/${key}`, "\uACF5\uAC1C \uC790\uC0B0 \uB8E8\uD2B8 \uD0A4\uB97C \uAC00\uB9AC\uCF1C\uC57C \uD569\uB2C8\uB2E4(\uC778\uB371\uC2A4 \uD45C\uAE30 \uBD88\uAC00).");
2607
+ if (!field2 || !field2["x-clack-asset"] || field2["x-clack-visibility"] !== "public") fail(`/output/metadata/${key}`, "metadata_asset_key");
2467
2608
  }
2468
2609
  }
2469
2610
  var INTRO_MAX_IMAGES = 8;
@@ -2480,19 +2621,19 @@ function introFieldLabelMaxLength(name2, field2) {
2480
2621
  }
2481
2622
  return Math.max(...lengths);
2482
2623
  }
2483
- function inspectIntroSemantics(skill, form, slots, fail2) {
2624
+ function inspectIntroSemantics(skill, form, slots, fail) {
2484
2625
  const intro = skill.output?.intro;
2485
2626
  if (!intro) return;
2486
2627
  let images = 0;
2487
2628
  for (const name2 of intro.images ?? []) {
2488
2629
  const slot = slots.find((entry) => entry.field === name2);
2489
2630
  if (!slot) {
2490
- fail2("/output/intro/images", "\uC778\uD2B8\uB85C \uC774\uBBF8\uC9C0\uB294 output.asset_slots\uC758 \uD544\uB4DC\uB9CC \uC4F8 \uC218 \uC788\uC2B5\uB2C8\uB2E4.");
2631
+ fail("/output/intro/images", "intro_image_not_slot");
2491
2632
  continue;
2492
2633
  }
2493
2634
  images += slot.multiple ? slot.multiple.max : 1;
2494
2635
  }
2495
- if (images > INTRO_MAX_IMAGES) fail2("/output/intro/images", `\uC778\uD2B8\uB85C \uC774\uBBF8\uC9C0\uB294 \uC2AC\uB86F \uCD5C\uB300 \uC7A5\uC218 \uD569\uACC4\uAC00 ${INTRO_MAX_IMAGES}\uC7A5 \uC774\uD558\uC5EC\uC57C \uD569\uB2C8\uB2E4.`);
2636
+ if (images > INTRO_MAX_IMAGES) fail("/output/intro/images", "intro_images_over", { max: INTRO_MAX_IMAGES });
2496
2637
  if (!form) return;
2497
2638
  const properties = form.properties ?? {};
2498
2639
  const textField = (name2) => {
@@ -2502,64 +2643,64 @@ function inspectIntroSemantics(skill, form, slots, fail2) {
2502
2643
  for (const [key, max] of [["creator_comment", INTRO_CREATOR_COMMENT_MAX], ["description", INTRO_DESCRIPTION_MAX]]) {
2503
2644
  if (intro[key] === void 0) continue;
2504
2645
  const field2 = textField(intro[key]);
2505
- if (!field2 || field2["x-clack-visibility"] !== "public") fail2(`/output/intro/${key}`, "\uACF5\uAC1C \uBB38\uC790\uC5F4(text\xB7textarea) \uD3FC \uD544\uB4DC\uB97C \uAC00\uB9AC\uCF1C\uC57C \uD569\uB2C8\uB2E4.");
2506
- else if (field2.maxLength > max) fail2(`/output/intro/${key}`, `\uAC00\uB9AC\uD0A4\uB294 \uD3FC \uD544\uB4DC\uC758 \uCD5C\uB300 \uAE38\uC774\uAC00 ${max}\uC790 \uC774\uD558\uC5EC\uC57C \uD569\uB2C8\uB2E4.`);
2646
+ if (!field2 || field2["x-clack-visibility"] !== "public") fail(`/output/intro/${key}`, "intro_text_field");
2647
+ else if (field2.maxLength > max) fail(`/output/intro/${key}`, "intro_text_too_long", { max });
2507
2648
  }
2508
2649
  let sectionLength = 0;
2509
2650
  for (const [key, name2] of Object.entries(intro.sections ?? {})) {
2510
2651
  const field2 = textField(name2);
2511
- if (!field2 || field2["x-clack-visibility"] !== "public") fail2(`/output/intro/sections/${key}`, "\uC18C\uAC1C \uC139\uC158\uC740 \uACF5\uAC1C \uBB38\uC790\uC5F4(text\xB7textarea) \uD3FC \uD544\uB4DC\uB9CC \uAC00\uB9AC\uD0AC \uC218 \uC788\uC2B5\uB2C8\uB2E4.");
2652
+ if (!field2 || field2["x-clack-visibility"] !== "public") fail(`/output/intro/sections/${key}`, "intro_section_field");
2512
2653
  else {
2513
- if (field2.maxLength > 2e3) fail2(`/output/intro/sections/${key}`, "\uC18C\uAC1C \uC139\uC158\uC758 \uCD5C\uB300 \uAE38\uC774\uB294 2,000\uC790\uC785\uB2C8\uB2E4.");
2654
+ if (field2.maxLength > 2e3) fail(`/output/intro/sections/${key}`, "intro_section_too_long");
2514
2655
  sectionLength += field2.maxLength;
2515
2656
  }
2516
2657
  }
2517
- if (sectionLength > 6e3) fail2("/output/intro/sections", "\uC18C\uAC1C \uC139\uC158\uC758 \uCD5C\uB300 \uAE38\uC774 \uD569\uACC4\uB294 6,000\uC790\uC785\uB2C8\uB2E4.");
2658
+ if (sectionLength > 6e3) fail("/output/intro/sections", "intro_sections_total");
2518
2659
  const fallback = intro.description_fallback ?? [];
2519
2660
  let composed = 0;
2520
2661
  for (const name2 of fallback) {
2521
2662
  const field2 = textField(name2);
2522
2663
  if (!field2) {
2523
- fail2("/output/intro/description_fallback", "\uBB38\uC790\uC5F4(text\xB7textarea) \uD3FC \uD544\uB4DC\uB9CC \uC790\uB3D9 \uAD6C\uC131\uC5D0 \uC4F8 \uC218 \uC788\uC2B5\uB2C8\uB2E4.");
2664
+ fail("/output/intro/description_fallback", "intro_fallback_field");
2524
2665
  continue;
2525
2666
  }
2526
2667
  composed += introFieldLabelMaxLength(name2, field2) + 1 + field2.maxLength;
2527
2668
  }
2528
2669
  composed += Math.max(0, fallback.length - 1) * 2;
2529
- if (composed > INTRO_DESCRIPTION_MAX) fail2("/output/intro/description_fallback", `\uC790\uB3D9 \uAD6C\uC131 \uCD5C\uB300 \uAE38\uC774(\uB77C\uBCA8 \uD3EC\uD568)\uAC00 ${INTRO_DESCRIPTION_MAX}\uC790\uB97C \uB118\uC2B5\uB2C8\uB2E4.`);
2670
+ if (composed > INTRO_DESCRIPTION_MAX) fail("/output/intro/description_fallback", "intro_fallback_too_long", { max: INTRO_DESCRIPTION_MAX });
2530
2671
  }
2531
2672
  function inspectAuthoringSemantics(skill, form, data) {
2532
2673
  const errors = [];
2533
- const fail2 = (path, message) => errors.push({ path, message });
2534
- inspectEditorSemantics(skill, data, fail2);
2674
+ const fail = (path, code, args) => errors.push({ path, message: SEMANTICS_NOTES[code](args ?? {}), code, ...args ? { args } : {} });
2675
+ inspectEditorSemantics(skill, data, fail);
2535
2676
  const tools = skill.authoring?.tools ?? [];
2536
- if (new Set(tools.map((tool) => tool.tool)).size !== tools.length) fail2("/authoring/tools", "\uB3C4\uAD6C \uC774\uB984\uC744 \uC911\uBCF5 \uC120\uC5B8\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
2537
- for (const tool of tools) if (tool.batch_max > tool.max_calls) fail2("/authoring/tools", "\uC77C\uAD04 \uC0DD\uC131 \uC218\uB294 \uC138\uC158 \uD638\uCD9C \uC218\uB97C \uB118\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
2677
+ if (new Set(tools.map((tool) => tool.tool)).size !== tools.length) fail("/authoring/tools", "tool_duplicate");
2678
+ for (const tool of tools) if (tool.batch_max > tool.max_calls) fail("/authoring/tools", "tool_batch_over_calls");
2538
2679
  const slots = skill.output?.asset_slots ?? [];
2539
2680
  const paths = /* @__PURE__ */ new Set();
2540
2681
  const fields3 = /* @__PURE__ */ new Set();
2541
2682
  for (const slot of slots) {
2542
- if (fields3.has(slot.field)) fail2("/output/asset_slots", "\uC2AC\uB86F \uD544\uB4DC\uB294 \uC911\uBCF5\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
2683
+ if (fields3.has(slot.field)) fail("/output/asset_slots", "slot_field_duplicate");
2543
2684
  fields3.add(slot.field);
2544
- if (slot.multiple && slot.multiple.min > slot.multiple.max) fail2("/output/asset_slots", "\uB2E4\uC911 \uC2AC\uB86F \uCD5C\uC18C \uC218\uB294 \uCD5C\uB300 \uC218\uBCF4\uB2E4 \uD074 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
2685
+ if (slot.multiple && slot.multiple.min > slot.multiple.max) fail("/output/asset_slots", "slot_multiple_range");
2545
2686
  for (let index = 0; index < (slot.multiple?.max ?? 1); index++) {
2546
2687
  const path = slot.multiple ? slot.multiple.path.replace("{n}", String(index)) : slot.path;
2547
- if (paths.has(path)) fail2("/output/asset_slots", "\uC2AC\uB86F \uD30C\uC77C \uACBD\uB85C\uB294 \uC911\uBCF5\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
2688
+ if (paths.has(path)) fail("/output/asset_slots", "slot_path_duplicate");
2548
2689
  paths.add(path);
2549
2690
  }
2550
2691
  }
2551
- inspectIntroSemantics(skill, form, slots, fail2);
2692
+ inspectIntroSemantics(skill, form, slots, fail);
2552
2693
  if (!form) return errors;
2553
2694
  const properties = form.properties ?? {};
2554
2695
  const exists = (expression, path) => {
2555
2696
  const match = /^([a-z][a-z0-9_-]*)(?:\[(\d+)\])?$/.exec(expression);
2556
2697
  if (!match || !Object.hasOwn(properties, match[1])) {
2557
- fail2(path, "\uC120\uC5B8\uD55C \uD3FC \uD544\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.");
2698
+ fail(path, "form_field_missing");
2558
2699
  return;
2559
2700
  }
2560
2701
  if (match[2] !== void 0) {
2561
2702
  const slot = slots.find((value2) => value2.field === match[1]);
2562
- if (!slot?.multiple || Number(match[2]) >= slot.multiple.max) fail2(path, "\uB2E4\uC911 \uC774\uBBF8\uC9C0 \uC2AC\uB86F\uC758 \uC720\uD6A8\uD55C \uC778\uB371\uC2A4\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.");
2703
+ if (!slot?.multiple || Number(match[2]) >= slot.multiple.max) fail(path, "slot_index_invalid");
2563
2704
  }
2564
2705
  };
2565
2706
  const entity = skill.output?.entity_map;
@@ -2569,17 +2710,17 @@ function inspectAuthoringSemantics(skill, form, data) {
2569
2710
  }
2570
2711
  for (const slot of slots) {
2571
2712
  const node = properties[slot.field];
2572
- if (!node || node["x-clack-widget"] !== (slot.multiple ? "image_list" : "image")) fail2("/output/asset_slots", "\uC2AC\uB86F\uACFC \uC774\uBBF8\uC9C0 \uC704\uC82F \uC885\uB958\uAC00 \uC77C\uCE58\uD574\uC57C \uD569\uB2C8\uB2E4.");
2713
+ if (!node || node["x-clack-widget"] !== (slot.multiple ? "image_list" : "image")) fail("/output/asset_slots", "slot_widget_mismatch");
2573
2714
  }
2574
2715
  for (const [name2, node] of Object.entries(properties)) {
2575
2716
  if (node["x-clack-widget"] === "content_ref") {
2576
- if (node.type !== "string" || !node["x-clack-source"]) fail2(`/properties/${name2}`, "content_ref\uC5D0\uB294 \uBB38\uC790\uC5F4\uACFC x-clack-source \uC120\uC5B8\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.");
2717
+ if (node.type !== "string" || !node["x-clack-source"]) fail(`/properties/${name2}`, "content_ref_declaration");
2577
2718
  for (const target of Object.keys(node["x-clack-source"]?.import ?? {})) {
2578
2719
  exists(target, `/properties/${name2}/x-clack-source/import`);
2579
- if (properties[target]?.["x-clack-widget"] === "content_ref") fail2(`/properties/${name2}`, "\uB2E4\uB978 content_ref \uD544\uB4DC\uB85C \uAC00\uC838\uC62C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
2720
+ if (properties[target]?.["x-clack-widget"] === "content_ref") fail(`/properties/${name2}`, "content_ref_import_chain");
2580
2721
  }
2581
- } else if (node["x-clack-source"]) fail2(`/properties/${name2}`, "x-clack-source\uB294 content_ref\uC5D0\uB9CC \uC120\uC5B8\uD569\uB2C8\uB2E4.");
2582
- if (node["x-clack-widget"] === "image_list" && (node.type !== "array" || node.items?.type !== "object" || !node["x-clack-asset"])) fail2(`/properties/${name2}`, "image_list\uC5D0\uB294 \uAC1D\uCCB4 \uBC30\uC5F4\uACFC \uC790\uC0B0 \uC120\uC5B8\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.");
2722
+ } else if (node["x-clack-source"]) fail(`/properties/${name2}`, "source_without_content_ref");
2723
+ if (node["x-clack-widget"] === "image_list" && (node.type !== "array" || node.items?.type !== "object" || !node["x-clack-asset"])) fail(`/properties/${name2}`, "image_list_declaration");
2583
2724
  }
2584
2725
  return errors;
2585
2726
  }
@@ -2588,7 +2729,7 @@ function inspectAuthoringSemantics(skill, form, data) {
2588
2729
  import { createHash as createHash4 } from "crypto";
2589
2730
  import { lstat as lstat4, open as open3, readdir } from "fs/promises";
2590
2731
  import { constants } from "fs";
2591
- import { basename as basename4, extname as extname2, join as join2, resolve as resolve7 } from "path";
2732
+ import { basename as basename5, extname as extname2, join as join2, resolve as resolve8 } from "path";
2592
2733
  import { Ajv2020 as Ajv20202 } from "ajv/dist/2020.js";
2593
2734
  import { unzipSync as unzipSync2, zipSync } from "fflate";
2594
2735
  import { parseDocument } from "yaml";
@@ -2599,7 +2740,6 @@ import { Ajv2020 } from "ajv/dist/2020.js";
2599
2740
 
2600
2741
  // src/vendor/clack-api-v4-contracts/output-data-inspect.mjs
2601
2742
  var bytes = (value2) => Buffer.byteLength(JSON.stringify(value2), "utf8");
2602
- var fail = (path, message) => ({ path, message });
2603
2743
  var OUTPUT_DATA_LIMITS = { schema_bytes: 65536, depth: 6, properties: 400, public_bytes: 262144, private_bytes: 65536, ui_state_bytes: 262144 };
2604
2744
  var SCALAR_TYPES = /* @__PURE__ */ new Set(["string", "integer", "number", "boolean"]);
2605
2745
  var SCALAR_KEYS = {
@@ -2611,84 +2751,115 @@ var SCALAR_KEYS = {
2611
2751
  boolean: []
2612
2752
  };
2613
2753
  var TYPED_KEYS = new Set(Object.values(SCALAR_KEYS).flat());
2754
+ var RANGE_LABELS = { length: "\uAE38\uC774", items: "\uD56D\uBAA9 \uC218", value: "\uAC12" };
2755
+ var OUTPUT_DATA_NOTES = {
2756
+ schema_too_large: () => "\uB370\uC774\uD130 \uC2A4\uD0A4\uB9C8\uB294 64 KiB \uC774\uD558\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4.",
2757
+ root_required_undeclared: ({ key }) => `\uC120\uC5B8\uD558\uC9C0 \uC54A\uC740 \uB8E8\uD2B8 \uD0A4 ${key}\uC785\uB2C8\uB2E4.`,
2758
+ public_root_key_missing: () => "\uACF5\uAC1C \uB8E8\uD2B8 \uD0A4\uAC00 \uD558\uB098 \uC774\uC0C1 \uD544\uC694\uD569\uB2C8\uB2E4.",
2759
+ ref_cycle: () => "\uC21C\uD658 \uCC38\uC870\uB294 \uD5C8\uC6A9\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
2760
+ ref_not_local: () => "\uB85C\uCEEC $defs \uCC38\uC870\uB9CC \uD5C8\uC6A9\uD569\uB2C8\uB2E4.",
2761
+ depth_over: () => "\uB370\uC774\uD130 \uBB38\uC11C \uAE4A\uC774\uB294 \uB8E8\uD2B8 \uC544\uB798 6 \uC774\uD558\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4.",
2762
+ ref_extra_keys: () => "$ref\uC5D0\uB294 \uC124\uBA85 \uD0A4\uB9CC \uD568\uAED8 \uB458 \uC218 \uC788\uC2B5\uB2C8\uB2E4.",
2763
+ one_of_discriminator_missing: () => "oneOf\uC5D0\uB294 x-clack-discriminator\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.",
2764
+ one_of_typed_keys: () => "oneOf \uB178\uB4DC\uC5D0\uB294 \uD615\uC2DD \uD0A4\uB97C \uD568\uAED8 \uB458 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
2765
+ one_of_branch_shape: () => "\uAC01 oneOf \uBD84\uAE30\uB294 \uD310\uBCC4\uC790 \uBB38\uC790\uC5F4 const\uB97C \uD544\uC218\uB85C \uAC00\uC9C4 \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4.",
2766
+ discriminator_duplicate: ({ value: value2 }) => `\uD310\uBCC4\uC790 \uAC12 ${value2}\uC774 \uC911\uBCF5\uB429\uB2C8\uB2E4.`,
2767
+ discriminator_misplaced: () => "x-clack-discriminator\uB294 oneOf\uC5D0\uB9CC \uB461\uB2C8\uB2E4.",
2768
+ type_missing: () => "type\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.",
2769
+ key_not_for_type: ({ type }) => `${type}\uC5D0 \uC4F8 \uC218 \uC5C6\uB294 \uD0A4\uC785\uB2C8\uB2E4.`,
2770
+ range_invalid: ({ range }) => `${RANGE_LABELS[range]} \uBC94\uC704\uAC00 \uC798\uBABB\uB418\uC5C8\uC2B5\uB2C8\uB2E4.`,
2771
+ string_max_length_missing: () => "\uBB38\uC790\uC5F4 maxLength\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.",
2772
+ array_items_missing: () => "\uBC30\uC5F4\uC5D0\uB294 items\uC640 maxItems\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.",
2773
+ unique_items_not_scalar: () => "uniqueItems\uB294 \uC2A4\uCE7C\uB77C \uBC30\uC5F4\uC5D0\uB9CC \uC4F8 \uC218 \uC788\uC2B5\uB2C8\uB2E4.",
2774
+ object_shape: () => "\uAC1D\uCCB4\uC5D0\uB294 properties\uC640 additionalProperties: false\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.",
2775
+ required_undeclared: ({ key }) => `\uC120\uC5B8\uD558\uC9C0 \uC54A\uC740 \uD0A4 ${key}\uC744 required\uC5D0 \uB450\uC5C8\uC2B5\uB2C8\uB2E4.`,
2776
+ enum_type_mismatch: () => "enum \uAC12\uC758 \uD615\uC2DD\uC774 type\uACFC \uB2E4\uB985\uB2C8\uB2E4.",
2777
+ asset_node_shape: () => "\uC790\uC0B0 \uB178\uB4DC\uB294 {type:string, format:uuid, maxLength:36}\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4.",
2778
+ asset_in_private: () => "\uBE44\uACF5\uAC1C \uD0A4\uC5D0\uB294 \uC790\uC0B0\uC744 \uB458 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
2779
+ format_not_string: () => "format\uC740 \uBB38\uC790\uC5F4\uC5D0\uB9CC \uC501\uB2C8\uB2E4.",
2780
+ ai_suggest_private: () => "AI \uC81C\uC548\uC740 \uACF5\uAC1C \uD0A4\uC5D0\uB9CC \uCF24 \uC218 \uC788\uC2B5\uB2C8\uB2E4.",
2781
+ properties_over: () => "\uB370\uC774\uD130 \uC2A4\uD0A4\uB9C8 \uC18D\uC131\uC740 400\uAC1C \uC774\uD558\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4.",
2782
+ ai_suggest_has_asset: () => "AI \uC81C\uC548 \uD558\uC704 \uD2B8\uB9AC\uC5D0 \uC790\uC0B0 \uB178\uB4DC\uB97C \uB458 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."
2783
+ };
2614
2784
  function inspectOutputData(schema) {
2615
2785
  const errors = [];
2616
- if (bytes(schema) > OUTPUT_DATA_LIMITS.schema_bytes) errors.push(fail("/", "\uB370\uC774\uD130 \uC2A4\uD0A4\uB9C8\uB294 64 KiB \uC774\uD558\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4."));
2617
- for (const name2 of schema.required ?? []) if (!Object.hasOwn(schema.properties, name2)) errors.push(fail("/required", `\uC120\uC5B8\uD558\uC9C0 \uC54A\uC740 \uB8E8\uD2B8 \uD0A4 ${name2}\uC785\uB2C8\uB2E4.`));
2618
- if (!Object.values(schema.properties).some((field2) => field2["x-clack-visibility"] === "public")) errors.push(fail("/properties", "\uACF5\uAC1C \uB8E8\uD2B8 \uD0A4\uAC00 \uD558\uB098 \uC774\uC0C1 \uD544\uC694\uD569\uB2C8\uB2E4."));
2786
+ const note = (path, code, args) => errors.push({ path, message: OUTPUT_DATA_NOTES[code](args ?? {}), code, ...args ? { args } : {} });
2787
+ if (bytes(schema) > OUTPUT_DATA_LIMITS.schema_bytes) note("/", "schema_too_large");
2788
+ for (const name2 of schema.required ?? []) if (!Object.hasOwn(schema.properties, name2)) note("/required", "root_required_undeclared", { key: name2 });
2789
+ if (!Object.values(schema.properties).some((field2) => field2["x-clack-visibility"] === "public")) note("/properties", "public_root_key_missing");
2619
2790
  const defs = schema.$defs ?? {};
2620
- const resolve9 = (node) => typeof node?.$ref === "string" ? defs[node.$ref.slice("#/$defs/".length)] : node;
2791
+ const resolve10 = (node) => typeof node?.$ref === "string" ? defs[node.$ref.slice("#/$defs/".length)] : node;
2621
2792
  let properties = 0;
2622
2793
  const suggestRoots = [];
2623
2794
  const assetPointers = [];
2624
2795
  walkDataSchema(schema, (node, info) => {
2625
2796
  const at = info.pointer || "/";
2626
2797
  if (info.cycle) {
2627
- errors.push(fail(at, "\uC21C\uD658 \uCC38\uC870\uB294 \uD5C8\uC6A9\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4."));
2798
+ note(at, "ref_cycle");
2628
2799
  return;
2629
2800
  }
2630
2801
  if (info.unresolved) {
2631
- errors.push(fail(at, "\uB85C\uCEEC $defs \uCC38\uC870\uB9CC \uD5C8\uC6A9\uD569\uB2C8\uB2E4."));
2802
+ note(at, "ref_not_local");
2632
2803
  return;
2633
2804
  }
2634
- if (info.depth > OUTPUT_DATA_LIMITS.depth) errors.push(fail(at, "\uB370\uC774\uD130 \uBB38\uC11C \uAE4A\uC774\uB294 \uB8E8\uD2B8 \uC544\uB798 6 \uC774\uD558\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4."));
2805
+ if (info.depth > OUTPUT_DATA_LIMITS.depth) note(at, "depth_over");
2635
2806
  if (info.depth === 0) return;
2636
2807
  if (typeof node.$ref === "string") {
2637
- if (Object.keys(node).some((key) => !["$ref", "title", "description", "x-clack-i18n", "x-clack-visibility"].includes(key))) errors.push(fail(at, "$ref\uC5D0\uB294 \uC124\uBA85 \uD0A4\uB9CC \uD568\uAED8 \uB458 \uC218 \uC788\uC2B5\uB2C8\uB2E4."));
2808
+ if (Object.keys(node).some((key) => !["$ref", "title", "description", "x-clack-i18n", "x-clack-visibility"].includes(key))) note(at, "ref_extra_keys");
2638
2809
  return;
2639
2810
  }
2640
2811
  if (!info.branch) properties++;
2641
2812
  if (Array.isArray(node.oneOf)) {
2642
2813
  const discriminator = node["x-clack-discriminator"];
2643
- if (!discriminator) errors.push(fail(at, "oneOf\uC5D0\uB294 x-clack-discriminator\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4."));
2644
- if (node.type || node.properties || node.items || node.enum || Object.hasOwn(node, "const")) errors.push(fail(at, "oneOf \uB178\uB4DC\uC5D0\uB294 \uD615\uC2DD \uD0A4\uB97C \uD568\uAED8 \uB458 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."));
2814
+ if (!discriminator) note(at, "one_of_discriminator_missing");
2815
+ if (node.type || node.properties || node.items || node.enum || Object.hasOwn(node, "const")) note(at, "one_of_typed_keys");
2645
2816
  const seen = /* @__PURE__ */ new Set();
2646
2817
  for (const raw of node.oneOf) {
2647
- const branch = resolve9(raw);
2818
+ const branch = resolve10(raw);
2648
2819
  const tag = branch?.properties?.[discriminator];
2649
2820
  if (branch?.type !== "object" || typeof tag?.const !== "string" || tag.type !== "string" || !(branch.required ?? []).includes(discriminator)) {
2650
- errors.push(fail(at, "\uAC01 oneOf \uBD84\uAE30\uB294 \uD310\uBCC4\uC790 \uBB38\uC790\uC5F4 const\uB97C \uD544\uC218\uB85C \uAC00\uC9C4 \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4."));
2651
- } else if (seen.has(tag.const)) errors.push(fail(at, `\uD310\uBCC4\uC790 \uAC12 ${tag.const}\uC774 \uC911\uBCF5\uB429\uB2C8\uB2E4.`));
2821
+ note(at, "one_of_branch_shape");
2822
+ } else if (seen.has(tag.const)) note(at, "discriminator_duplicate", { value: tag.const });
2652
2823
  else seen.add(tag.const);
2653
2824
  }
2654
2825
  return;
2655
2826
  }
2656
- if (node["x-clack-discriminator"] !== void 0) errors.push(fail(at, "x-clack-discriminator\uB294 oneOf\uC5D0\uB9CC \uB461\uB2C8\uB2E4."));
2827
+ if (node["x-clack-discriminator"] !== void 0) note(at, "discriminator_misplaced");
2657
2828
  if (!node.type) {
2658
- errors.push(fail(at, "type\uC774 \uD544\uC694\uD569\uB2C8\uB2E4."));
2829
+ note(at, "type_missing");
2659
2830
  return;
2660
2831
  }
2661
- for (const key of Object.keys(node)) if (TYPED_KEYS.has(key) && !SCALAR_KEYS[node.type].includes(key)) errors.push(fail(`${at}/${key}`, `${node.type}\uC5D0 \uC4F8 \uC218 \uC5C6\uB294 \uD0A4\uC785\uB2C8\uB2E4.`));
2662
- const range = (low, high, label) => {
2663
- if (typeof node[low] === "number" && typeof node[high] === "number" && node[low] > node[high]) errors.push(fail(at, `${label} \uBC94\uC704\uAC00 \uC798\uBABB\uB418\uC5C8\uC2B5\uB2C8\uB2E4.`));
2832
+ for (const key of Object.keys(node)) if (TYPED_KEYS.has(key) && !SCALAR_KEYS[node.type].includes(key)) note(`${at}/${key}`, "key_not_for_type", { type: node.type });
2833
+ const range = (low, high, kind) => {
2834
+ if (typeof node[low] === "number" && typeof node[high] === "number" && node[low] > node[high]) note(at, "range_invalid", { range: kind });
2664
2835
  };
2665
- range("minLength", "maxLength", "\uAE38\uC774");
2666
- range("minItems", "maxItems", "\uD56D\uBAA9 \uC218");
2667
- range("minimum", "maximum", "\uAC12");
2668
- if (node.type === "string" && typeof node.maxLength !== "number") errors.push(fail(at, "\uBB38\uC790\uC5F4 maxLength\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4."));
2669
- if (node.type === "array" && (!node.items || typeof node.maxItems !== "number")) errors.push(fail(at, "\uBC30\uC5F4\uC5D0\uB294 items\uC640 maxItems\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4."));
2836
+ range("minLength", "maxLength", "length");
2837
+ range("minItems", "maxItems", "items");
2838
+ range("minimum", "maximum", "value");
2839
+ if (node.type === "string" && typeof node.maxLength !== "number") note(at, "string_max_length_missing");
2840
+ if (node.type === "array" && (!node.items || typeof node.maxItems !== "number")) note(at, "array_items_missing");
2670
2841
  if (node.type === "array" && node.uniqueItems === true) {
2671
2842
  let items = node.items;
2672
- for (let hops = 0; typeof items?.$ref === "string" && hops < 64; hops++) items = resolve9(items);
2673
- if (!items || Array.isArray(items.oneOf) || !SCALAR_TYPES.has(items.type)) errors.push(fail(`${at}/uniqueItems`, "uniqueItems\uB294 \uC2A4\uCE7C\uB77C \uBC30\uC5F4\uC5D0\uB9CC \uC4F8 \uC218 \uC788\uC2B5\uB2C8\uB2E4."));
2843
+ for (let hops = 0; typeof items?.$ref === "string" && hops < 64; hops++) items = resolve10(items);
2844
+ if (!items || Array.isArray(items.oneOf) || !SCALAR_TYPES.has(items.type)) note(`${at}/uniqueItems`, "unique_items_not_scalar");
2674
2845
  }
2675
2846
  if (node.type === "object") {
2676
- if (node.additionalProperties !== false || !node.properties || !Object.keys(node.properties).length) errors.push(fail(at, "\uAC1D\uCCB4\uC5D0\uB294 properties\uC640 additionalProperties: false\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4."));
2677
- for (const name2 of node.required ?? []) if (!Object.hasOwn(node.properties ?? {}, name2)) errors.push(fail(at, `\uC120\uC5B8\uD558\uC9C0 \uC54A\uC740 \uD0A4 ${name2}\uC744 required\uC5D0 \uB450\uC5C8\uC2B5\uB2C8\uB2E4.`));
2847
+ if (node.additionalProperties !== false || !node.properties || !Object.keys(node.properties).length) note(at, "object_shape");
2848
+ for (const name2 of node.required ?? []) if (!Object.hasOwn(node.properties ?? {}, name2)) note(at, "required_undeclared", { key: name2 });
2678
2849
  }
2679
- for (const value2 of node.enum ?? []) if (typeof value2 !== (node.type === "integer" ? "number" : node.type)) errors.push(fail(at, "enum \uAC12\uC758 \uD615\uC2DD\uC774 type\uACFC \uB2E4\uB985\uB2C8\uB2E4."));
2850
+ for (const value2 of node.enum ?? []) if (typeof value2 !== (node.type === "integer" ? "number" : node.type)) note(at, "enum_type_mismatch");
2680
2851
  if (node["x-clack-asset"]) {
2681
2852
  assetPointers.push(at);
2682
- if (node.type !== "string" || node.format !== "uuid" || node.maxLength !== 36 || node.enum) errors.push(fail(at, "\uC790\uC0B0 \uB178\uB4DC\uB294 {type:string, format:uuid, maxLength:36}\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4."));
2683
- if (info.visibility === "private") errors.push(fail(at, "\uBE44\uACF5\uAC1C \uD0A4\uC5D0\uB294 \uC790\uC0B0\uC744 \uB458 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."));
2684
- } else if (node.format === "uuid" && node.type !== "string") errors.push(fail(at, "format\uC740 \uBB38\uC790\uC5F4\uC5D0\uB9CC \uC501\uB2C8\uB2E4."));
2853
+ if (node.type !== "string" || node.format !== "uuid" || node.maxLength !== 36 || node.enum) note(at, "asset_node_shape");
2854
+ if (info.visibility === "private") note(at, "asset_in_private");
2855
+ } else if (node.format === "uuid" && node.type !== "string") note(at, "format_not_string");
2685
2856
  if (node["x-clack-ai"]?.suggest === true) {
2686
- if (info.visibility !== "public") errors.push(fail(at, "AI \uC81C\uC548\uC740 \uACF5\uAC1C \uD0A4\uC5D0\uB9CC \uCF24 \uC218 \uC788\uC2B5\uB2C8\uB2E4."));
2857
+ if (info.visibility !== "public") note(at, "ai_suggest_private");
2687
2858
  suggestRoots.push(at);
2688
2859
  }
2689
2860
  });
2690
- if (properties > OUTPUT_DATA_LIMITS.properties) errors.push(fail("/", "\uB370\uC774\uD130 \uC2A4\uD0A4\uB9C8 \uC18D\uC131\uC740 400\uAC1C \uC774\uD558\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4."));
2691
- for (const root of suggestRoots) if (assetPointers.some((asset) => asset === root || asset.startsWith(`${root}/`))) errors.push(fail(root, "AI \uC81C\uC548 \uD558\uC704 \uD2B8\uB9AC\uC5D0 \uC790\uC0B0 \uB178\uB4DC\uB97C \uB458 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."));
2861
+ if (properties > OUTPUT_DATA_LIMITS.properties) note("/", "properties_over");
2862
+ for (const root of suggestRoots) if (assetPointers.some((asset) => asset === root || asset.startsWith(`${root}/`))) note(root, "ai_suggest_has_asset");
2692
2863
  return errors;
2693
2864
  }
2694
2865
  var ZWJ_AFTER = new RegExp("\\p{Extended_Pictographic}", "u");
@@ -5426,7 +5597,7 @@ function fillEditorHash(files) {
5426
5597
  return new TextEncoder().encode(next);
5427
5598
  }
5428
5599
  async function prepareSkillPackage(source, options2 = {}) {
5429
- const path = resolve7(source);
5600
+ const path = resolve8(source);
5430
5601
  let info;
5431
5602
  try {
5432
5603
  info = await lstat4(path);
@@ -5451,34 +5622,34 @@ async function prepareSkillPackage(source, options2 = {}) {
5451
5622
  else return invalid2("\uC2A4\uD0AC \uB514\uB809\uD130\uB9AC \uB610\uB294 ZIP \uD30C\uC77C\uC744 \uC9C0\uC815\uD558\uC138\uC694.");
5452
5623
  const inspected = inspectZip(
5453
5624
  bytes2,
5454
- info.isDirectory() ? basename4(path) : void 0,
5625
+ info.isDirectory() ? basename5(path) : void 0,
5455
5626
  info.isDirectory() && !options2.fillEditorHash ? "warn" : "error"
5456
5627
  );
5457
5628
  return { bytes: bytes2, sha256: hash(bytes2), ...inspected, ...filledManifest ? { filled_manifest: filledManifest } : {} };
5458
5629
  }
5459
5630
 
5460
5631
  // src/commands/skill.ts
5461
- var uuid2 = z9.uuid();
5462
- var createdSchema = z9.object({ id: uuid2, slug: z9.string(), type: z9.string() });
5463
- var uploadSchema = z9.object({
5632
+ var uuid2 = z10.uuid();
5633
+ var createdSchema = z10.object({ id: uuid2, slug: z10.string(), type: z10.string() });
5634
+ var uploadSchema = z10.object({
5464
5635
  version_id: uuid2,
5465
- upload_url: z9.url(),
5466
- headers: z9.record(z9.string(), z9.string()),
5467
- expires_at: z9.string(),
5468
- max_upload_bytes: z9.number().int().positive()
5636
+ upload_url: z10.url(),
5637
+ headers: z10.record(z10.string(), z10.string()),
5638
+ expires_at: z10.string(),
5639
+ max_upload_bytes: z10.number().int().positive()
5469
5640
  });
5470
- var versionSchema = z9.object({ id: uuid2, skill_id: uuid2, version: z9.string(), review_status: z9.string() });
5471
- var skillSlug = z9.string().regex(/^[a-z][a-z0-9-]{0,63}$/);
5472
- var semver = z9.string().regex(/^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-[0-9A-Za-z.-]+)?$/);
5473
- var category = z9.enum(["character_chat", "quiz", "worldbuilding", "gallery", "story", "agent_tool"]);
5474
- var visibility = z9.enum(["private", "unlisted", "public"]);
5475
- var managedVersionSchema = z9.object({
5641
+ var versionSchema = z10.object({ id: uuid2, skill_id: uuid2, version: z10.string(), review_status: z10.string() });
5642
+ var skillSlug = z10.string().regex(/^[a-z][a-z0-9-]{0,63}$/);
5643
+ var semver = z10.string().regex(/^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-[0-9A-Za-z.-]+)?$/);
5644
+ var category = z10.enum(["character_chat", "quiz", "worldbuilding", "gallery", "story", "agent_tool"]);
5645
+ var visibility = z10.enum(["private", "unlisted", "public"]);
5646
+ var managedVersionSchema = z10.object({
5476
5647
  id: uuid2,
5477
5648
  skill_id: uuid2,
5478
- version: z9.string(),
5479
- review_status: z9.string(),
5480
- release_status: z9.string(),
5481
- package_hash: z9.string().nullable()
5649
+ version: z10.string(),
5650
+ review_status: z10.string(),
5651
+ release_status: z10.string(),
5652
+ package_hash: z10.string().nullable()
5482
5653
  });
5483
5654
  function serverData(schema, value2) {
5484
5655
  const parsed = schema.safeParse(value2);
@@ -5578,7 +5749,7 @@ ${hint}`, error2.code, error2.status, error2.retryAfter);
5578
5749
  function registerSkillCommands(program2, runtime2) {
5579
5750
  const skill = program2.command("skill").description("\uC2A4\uD0AC \uD328\uD0A4\uC9C0 \uAC80\uC0AC\xB7\uC5C5\uB85C\uB4DC\xB7\uC2EC\uC0AC\xB7\uAC8C\uC2DC (skill:read\xB7write\xB7publish)");
5580
5751
  runtime2.action(skill.command("list").description("\uC2A4\uD0AC \uBAA9\uB85D \uAC80\uC0C9 (skill:read)").option("-q, --query <text>", "\uC2A4\uD0AC \uC774\uB984 \uAC80\uC0C9").option("--category <category>", "\uBD84\uB958").option("--entity <entity>", "\uC0B0\uCD9C\uBB3C \uC5D4\uD2F0\uD2F0(character)").option("--type <type>", "\uC2A4\uD0AC \uC720\uD615(template|instruction|tool)").option("--official <value>", "\uACF5\uC2DD \uC2A4\uD0AC\uB9CC true \uB610\uB294 false").option("--cursor <id>", "\uB2E4\uC74C \uD398\uC774\uC9C0 UUID").option("--all", "\uBAA8\uB4E0 \uD398\uC774\uC9C0 \uC870\uD68C"), async (ctx, _args, opts) => {
5581
- const query = opts.query === void 0 ? void 0 : z9.string().trim().min(1).max(100).safeParse(opts.query);
5752
+ const query = opts.query === void 0 ? void 0 : z10.string().trim().min(1).max(100).safeParse(opts.query);
5582
5753
  if (query && !query.success) throw new CliError("\uAC80\uC0C9\uC5B4\uB294 1~100\uC790\uC5EC\uC57C \uD569\uB2C8\uB2E4.");
5583
5754
  const selectedCategory = opts.category === void 0 ? void 0 : category.safeParse(opts.category);
5584
5755
  if (selectedCategory && !selectedCategory.success) throw new CliError("\uC2A4\uD0AC \uBD84\uB958\uB97C \uD655\uC778\uD558\uC138\uC694.");
@@ -5653,11 +5824,11 @@ function registerSkillCommands(program2, runtime2) {
5653
5824
  runtime2.action(skill.command("pack [dir]").description("\uC2A4\uD0AC \uD328\uD0A4\uC9C0 ZIP \uB9CC\uB4E4\uAE30 (\uB85C\uADF8\uC778 \uBD88\uD544\uC694, \uC5D0\uB514\uD130 \uC2A4\uD0AC\uC740 authoring.editor.sha256\uC744 \uCC44\uC6C0)").option("-o, --out <file>", "ZIP \uC800\uC7A5 \uACBD\uB85C (\uAE30\uBCF8: <\uC774\uB984>-<\uBC84\uC804>.zip)").option("--write-manifest", "\uCC44\uC6B4 sha256\uC744 \uB514\uB809\uD130\uB9AC\uC758 clack.skill.json\uC5D0\uB3C4 \uAE30\uB85D"), async (ctx, [dir], opts) => {
5654
5825
  const source = dir ?? ".";
5655
5826
  const prepared = await prepareSkillPackage(source, { fillEditorHash: true });
5656
- const out = resolve8(opts.out ?? `${prepared.manifest.name}-${prepared.manifest.version}.zip`);
5827
+ const out = resolve9(opts.out ?? `${prepared.manifest.name}-${prepared.manifest.version}.zip`);
5657
5828
  const writeManifest = Boolean(opts.writeManifest && prepared.filled_manifest);
5658
5829
  if (!ctx.options.dryRun) {
5659
5830
  await writeFile2(out, prepared.bytes);
5660
- if (writeManifest) await writeFile2(join3(resolve8(source), "clack.skill.json"), prepared.filled_manifest);
5831
+ if (writeManifest) await writeFile2(join3(resolve9(source), "clack.skill.json"), prepared.filled_manifest);
5661
5832
  }
5662
5833
  ctx.output({
5663
5834
  ...ctx.options.dryRun ? { dry_run: true } : {},
@@ -5687,7 +5858,7 @@ ${SDK_LOCATION_HELP}
5687
5858
  ]) {
5688
5859
  if (value2 === void 0) continue;
5689
5860
  if ((flag === "host-port" || flag === "editor-port") && !/^\d{2,5}$/.test(value2)) throw new CliError("\uD3EC\uD2B8\uB294 \uC22B\uC790\uC5EC\uC57C \uD569\uB2C8\uB2E4.");
5690
- args.push(`--${flag}`, flag.endsWith("port") ? value2 : resolve8(value2));
5861
+ args.push(`--${flag}`, flag.endsWith("port") ? value2 : resolve9(value2));
5691
5862
  }
5692
5863
  const sdkDir = resolveSdkDir(opts.sdkDir);
5693
5864
  const serve = join3(sdkDir, "mock-host/serve.mjs");
@@ -5695,10 +5866,10 @@ ${SDK_LOCATION_HELP}
5695
5866
  ctx.output({ dry_run: true, sdk_dir: sdkDir, command: ["node", serve, ...args] });
5696
5867
  return;
5697
5868
  }
5698
- await new Promise((done, fail2) => {
5869
+ await new Promise((done, fail) => {
5699
5870
  const child = spawn(process.execPath, [serve, ...args], { stdio: "inherit" });
5700
- child.on("error", () => fail2(new CliError("\uBAA8\uC758 \uD638\uC2A4\uD2B8\uB97C \uC2E4\uD589\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.", "SKILL_EDITOR_DEV_FAILED")));
5701
- child.on("exit", (code) => code ? fail2(new CliError(`\uBAA8\uC758 \uD638\uC2A4\uD2B8\uAC00 \uC885\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4(\uCF54\uB4DC ${code}). SDK\uC5D0\uC11C pnpm build\uB97C \uBA3C\uC800 \uC2E4\uD589\uD588\uB294\uC9C0 \uD655\uC778\uD558\uC138\uC694.`, "SKILL_EDITOR_DEV_FAILED")) : done());
5871
+ child.on("error", () => fail(new CliError("\uBAA8\uC758 \uD638\uC2A4\uD2B8\uB97C \uC2E4\uD589\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.", "SKILL_EDITOR_DEV_FAILED")));
5872
+ child.on("exit", (code) => code ? fail(new CliError(`\uBAA8\uC758 \uD638\uC2A4\uD2B8\uAC00 \uC885\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4(\uCF54\uB4DC ${code}). SDK\uC5D0\uC11C pnpm build\uB97C \uBA3C\uC800 \uC2E4\uD589\uD588\uB294\uC9C0 \uD655\uC778\uD558\uC138\uC694.`, "SKILL_EDITOR_DEV_FAILED")) : done());
5702
5873
  });
5703
5874
  }
5704
5875
  );
@@ -5827,14 +5998,14 @@ function editorReport(prepared) {
5827
5998
  var SDK_LOCATION_HELP = "SDK\uB294 \uC544\uC9C1 npm\uC5D0 \uACF5\uAC1C\uB418\uC9C0 \uC54A\uC544 CLI\uC5D0 \uD3EC\uD568\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. SDK \uC18C\uC2A4 \uB514\uB809\uD130\uB9AC(\uD074\uB799 \uB0B4\uBD80 \uC791\uC5C5\uC790\uB294 \uB0B4\uBD80 \uC800\uC7A5\uC18C\uC758 clack-skill-editor-sdk)\uC5D0\uC11C pnpm install && pnpm build\uB97C \uC2E4\uD589\uD55C \uB4A4 --sdk-dir <SDK \uB514\uB809\uD130\uB9AC>, CLACK_SKILL_EDITOR_SDK_DIR=<SDK \uB514\uB809\uD130\uB9AC>, CLACK_MONOREPO_DIR=<\uB0B4\uBD80 \uC800\uC7A5\uC18C> \uC911 \uD558\uB098\uB85C \uC704\uCE58\uB97C \uC9C0\uC815\uD558\uC138\uC694.";
5828
5999
  function resolveSdkDir(option) {
5829
6000
  const candidates = [];
5830
- if (option) candidates.push(resolve8(option));
6001
+ if (option) candidates.push(resolve9(option));
5831
6002
  else {
5832
- if (process.env.CLACK_SKILL_EDITOR_SDK_DIR) candidates.push(resolve8(process.env.CLACK_SKILL_EDITOR_SDK_DIR));
6003
+ if (process.env.CLACK_SKILL_EDITOR_SDK_DIR) candidates.push(resolve9(process.env.CLACK_SKILL_EDITOR_SDK_DIR));
5833
6004
  try {
5834
- candidates.push(resolve8(createRequire(join3(process.cwd(), "noop.js")).resolve("@clack/skill-editor-sdk/package.json"), ".."));
6005
+ candidates.push(resolve9(createRequire(join3(process.cwd(), "noop.js")).resolve("@clack/skill-editor-sdk/package.json"), ".."));
5835
6006
  } catch {
5836
6007
  }
5837
- if (process.env.CLACK_MONOREPO_DIR) candidates.push(resolve8(process.env.CLACK_MONOREPO_DIR, "clack-skill-editor-sdk"));
6008
+ if (process.env.CLACK_MONOREPO_DIR) candidates.push(resolve9(process.env.CLACK_MONOREPO_DIR, "clack-skill-editor-sdk"));
5838
6009
  }
5839
6010
  const found = candidates.find((dir) => existsSync(join3(dir, "mock-host/serve.mjs")));
5840
6011
  if (!found) throw new CliError(`\uC2A4\uD0AC \uC5D0\uB514\uD130 SDK(@clack/skill-editor-sdk)\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. ${SDK_LOCATION_HELP}`, "SKILL_EDITOR_SDK_NOT_FOUND");
@@ -5850,24 +6021,24 @@ function skillVersionIds(skillId, versionId) {
5850
6021
  // src/lib/platform-mcp.ts
5851
6022
  var protocolVersions = /* @__PURE__ */ new Set(["2024-11-05", "2025-03-26", "2025-06-18", "2025-11-25"]);
5852
6023
  var maxLineBytes = 1024 * 1024;
5853
- var collectionKey = z10.string().regex(/^[A-Za-z0-9_-]{1,64}$/);
6024
+ var collectionKey = z11.string().regex(/^[A-Za-z0-9_-]{1,64}$/);
5854
6025
  var patSchemas = {
5855
- skillList: z10.object({ q: z10.string().trim().min(1).max(100).optional() }).strict(),
5856
- skillGet: z10.object({ slug: z10.string().regex(/^[a-z][a-z0-9-]{0,63}$/) }).strict(),
5857
- skillPush: z10.object({ dir: z10.string().min(1).max(4096).optional(), skill_id: z10.uuid().optional() }).strict(),
5858
- skillVersionRef: z10.object({ skill_id: z10.uuid(), version_id: z10.uuid() }).strict(),
5859
- skillSubmit: z10.object({ skill_id: z10.uuid(), version_id: z10.uuid(), confirm: z10.boolean().optional() }).strict(),
5860
- skillRelease: z10.object({
5861
- skill_id: z10.uuid(),
5862
- version_id: z10.uuid(),
5863
- visibility: z10.enum(["private", "unlisted", "public"]),
5864
- confirm: z10.boolean().optional()
6026
+ skillList: z11.object({ q: z11.string().trim().min(1).max(100).optional() }).strict(),
6027
+ skillGet: z11.object({ slug: z11.string().regex(/^[a-z][a-z0-9-]{0,63}$/) }).strict(),
6028
+ skillPush: z11.object({ dir: z11.string().min(1).max(4096).optional(), skill_id: z11.uuid().optional() }).strict(),
6029
+ skillVersionRef: z11.object({ skill_id: z11.uuid(), version_id: z11.uuid() }).strict(),
6030
+ skillSubmit: z11.object({ skill_id: z11.uuid(), version_id: z11.uuid(), confirm: z11.boolean().optional() }).strict(),
6031
+ skillRelease: z11.object({
6032
+ skill_id: z11.uuid(),
6033
+ version_id: z11.uuid(),
6034
+ visibility: z11.enum(["private", "unlisted", "public"]),
6035
+ confirm: z11.boolean().optional()
5865
6036
  }).strict(),
5866
- contentPublish: z10.object({ content_id: z10.uuid(), version_id: z10.uuid(), confirm: z10.boolean().optional() }).strict(),
5867
- sharedCollections: z10.object({ content_id: z10.uuid() }).strict(),
5868
- sharedList: z10.object({ content_id: z10.uuid(), collection: collectionKey, limit: z10.number().int().min(1).max(100).optional() }).strict(),
5869
- sharedDocument: z10.object({ content_id: z10.uuid(), collection: collectionKey, key: collectionKey }).strict(),
5870
- sharedModerate: z10.object({ content_id: z10.uuid(), collection: collectionKey, key: collectionKey, confirm: z10.boolean().optional() }).strict()
6037
+ contentPublish: z11.object({ content_id: z11.uuid(), version_id: z11.uuid(), confirm: z11.boolean().optional() }).strict(),
6038
+ sharedCollections: z11.object({ content_id: z11.uuid() }).strict(),
6039
+ sharedList: z11.object({ content_id: z11.uuid(), collection: collectionKey, limit: z11.number().int().min(1).max(100).optional() }).strict(),
6040
+ sharedDocument: z11.object({ content_id: z11.uuid(), collection: collectionKey, key: collectionKey }).strict(),
6041
+ sharedModerate: z11.object({ content_id: z11.uuid(), collection: collectionKey, key: collectionKey, confirm: z11.boolean().optional() }).strict()
5871
6042
  };
5872
6043
  var patToolNames = /* @__PURE__ */ new Set([
5873
6044
  "platform_authoring",
@@ -5897,120 +6068,120 @@ function parsePat(schema, input, label) {
5897
6068
  return parsed.data;
5898
6069
  }
5899
6070
  var toolDefinitions = [
5900
- { name: "platform_authoring", description: "\uACF5\uAC1C \uC2A4\uD0AC \uC81C\uC791 \uC138\uC158\xB7\uD3FC\xB7\uC790\uC0B0\xB7\uC774\uBBF8\uC9C0 \uC0DD\uC131\xB7\uD328\uD0A4\uC9D5\xB7\uC644\uB8CC\xB7\uD3EC\uAE30\uB97C \uCC98\uB9AC\uD569\uB2C8\uB2E4. complete/abandon\uC740 \uD3FC \uC138\uC158 \uC804\uC6A9\uC774\uBA70 complete\uB294 \uD604\uC7AC revision \uD328\uD0A4\uC9D5, abandon\uC740 confirm:true\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. \uC5D0\uB514\uD130\xB7\uC9C0\uCE68\uD615\uC740 \uC13C\uD130\uC5D0\uC11C \uC791\uC5C5\uD569\uB2C8\uB2E4. \uC774\uBBF8\uC9C0 \uC0DD\uC131\uC5D0\uB294 \uACAC\uC801 \uC2B9\uC778 \uAC00\uACA9\uACFC \uBA71\uB4F1\uC131 \uD0A4\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. creator-content:write \uAD8C\uD55C\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.", inputSchema: { type: "object", ...z10.toJSONSchema(authoringInput) }, annotations: { readOnlyHint: false, destructiveHint: true } },
6071
+ { name: "platform_authoring", description: "\uACF5\uAC1C \uC2A4\uD0AC \uC81C\uC791 \uC138\uC158\xB7\uD3FC\xB7\uC790\uC0B0\xB7\uC774\uBBF8\uC9C0 \uC0DD\uC131\xB7\uD328\uD0A4\uC9D5\xB7\uC644\uB8CC\xB7\uD3EC\uAE30\uB97C \uCC98\uB9AC\uD569\uB2C8\uB2E4. complete/abandon\uC740 \uD3FC \uC138\uC158 \uC804\uC6A9\uC774\uBA70 complete\uB294 \uD604\uC7AC revision \uD328\uD0A4\uC9D5, abandon\uC740 confirm:true\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. \uC5D0\uB514\uD130\xB7\uC9C0\uCE68\uD615\uC740 \uC13C\uD130\uC5D0\uC11C \uC791\uC5C5\uD569\uB2C8\uB2E4. \uC774\uBBF8\uC9C0 \uC0DD\uC131\uC5D0\uB294 \uACAC\uC801 \uC2B9\uC778 \uAC00\uACA9\uACFC \uBA71\uB4F1\uC131 \uD0A4\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. creator-content:write \uAD8C\uD55C\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.", inputSchema: { type: "object", ...z11.toJSONSchema(authoringInput) }, annotations: { readOnlyHint: false, destructiveHint: true } },
5901
6072
  {
5902
6073
  name: "platform_usage_get",
5903
6074
  description: "\uC11C\uBC84 \uD0A4\uAC00 \uC18D\uD55C \uCF58\uD150\uCE20\uC758 \uD50C\uB7AB\uD3FC \uC0AC\uC6A9\uB7C9\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4.",
5904
- inputSchema: z10.toJSONSchema(platformReadSchemas.usage),
6075
+ inputSchema: z11.toJSONSchema(platformReadSchemas.usage),
5905
6076
  annotations: { readOnlyHint: true, destructiveHint: false }
5906
6077
  },
5907
6078
  {
5908
6079
  name: "platform_server_data_get",
5909
6080
  description: "\uC11C\uBC84 \uD0A4\uB85C \uCF58\uD150\uCE20 \uB370\uC774\uD130 \uBB38\uC11C\uB97C \uC870\uD68C\uD569\uB2C8\uB2E4. data:read \uAD8C\uD55C\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.",
5910
- inputSchema: z10.toJSONSchema(platformReadSchemas.get),
6081
+ inputSchema: z11.toJSONSchema(platformReadSchemas.get),
5911
6082
  annotations: { readOnlyHint: true, destructiveHint: false }
5912
6083
  },
5913
6084
  {
5914
6085
  name: "platform_server_data_list",
5915
6086
  description: "\uC11C\uBC84 \uD0A4\uB85C \uCF58\uD150\uCE20 \uB370\uC774\uD130 \uBB38\uC11C \uBAA9\uB85D\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4. data:read \uAD8C\uD55C\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.",
5916
- inputSchema: z10.toJSONSchema(platformReadSchemas.list),
6087
+ inputSchema: z11.toJSONSchema(platformReadSchemas.list),
5917
6088
  annotations: { readOnlyHint: true, destructiveHint: false }
5918
6089
  },
5919
6090
  {
5920
6091
  name: "platform_server_data_leaderboard",
5921
6092
  description: "\uC11C\uBC84 \uD0A4\uB85C \uACF5\uC720 \uB370\uC774\uD130 \uC21C\uC704\uB97C \uC870\uD68C\uD569\uB2C8\uB2E4. data:read \uAD8C\uD55C\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.",
5922
- inputSchema: z10.toJSONSchema(platformReadSchemas.leaderboard),
6093
+ inputSchema: z11.toJSONSchema(platformReadSchemas.leaderboard),
5923
6094
  annotations: { readOnlyHint: true, destructiveHint: false }
5924
6095
  },
5925
6096
  {
5926
6097
  name: "platform_server_data_put",
5927
6098
  description: "\uC11C\uBC84 \uD0A4\uB85C \uB2E8\uC77C \uBB38\uC11C \uC804\uCCB4\uB97C \uC800\uC7A5\uD569\uB2C8\uB2E4. \uAC1C\uC815 \uBC88\uD638 \uB610\uB294 \uBD80\uC7AC \uC870\uAC74\uACFC confirm:true\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.",
5928
- inputSchema: z10.toJSONSchema(platformReadSchemas.put),
6099
+ inputSchema: z11.toJSONSchema(platformReadSchemas.put),
5929
6100
  annotations: { readOnlyHint: false, destructiveHint: true }
5930
6101
  },
5931
6102
  {
5932
6103
  name: "platform_server_data_patch",
5933
6104
  description: "\uC11C\uBC84 \uD0A4\uB85C \uB2E8\uC77C \uBB38\uC11C\uB97C \uBCD1\uD569 \uC218\uC815\uD569\uB2C8\uB2E4. \uAC1C\uC815 \uBC88\uD638\uC640 confirm:true\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.",
5934
- inputSchema: z10.toJSONSchema(platformReadSchemas.patch),
6105
+ inputSchema: z11.toJSONSchema(platformReadSchemas.patch),
5935
6106
  annotations: { readOnlyHint: false, destructiveHint: true }
5936
6107
  },
5937
6108
  {
5938
6109
  name: "platform_server_data_delete",
5939
6110
  description: "\uC11C\uBC84 \uD0A4\uB85C \uB2E8\uC77C \uBB38\uC11C\uB97C \uC0AD\uC81C\uD558\uAC70\uB098 \uC228\uAE41\uB2C8\uB2E4. confirm:true\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.",
5940
- inputSchema: z10.toJSONSchema(platformReadSchemas.delete),
6111
+ inputSchema: z11.toJSONSchema(platformReadSchemas.delete),
5941
6112
  annotations: { readOnlyHint: false, destructiveHint: true }
5942
6113
  },
5943
6114
  // 아래는 개인 액세스 토큰(CLACK_TOKEN, pat_)으로 동작하는 개인 도구다. 서버 키 도구와 인증 평면이 다르다.
5944
6115
  {
5945
6116
  name: "platform_skill_list",
5946
6117
  description: "\uACF5\uAC1C \uC2A4\uD0AC \uBAA9\uB85D\uC744 \uC774\uB984\uC73C\uB85C \uAC80\uC0C9\uD569\uB2C8\uB2E4. skill:read \uAD8C\uD55C\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.",
5947
- inputSchema: z10.toJSONSchema(patSchemas.skillList),
6118
+ inputSchema: z11.toJSONSchema(patSchemas.skillList),
5948
6119
  annotations: { readOnlyHint: true, destructiveHint: false }
5949
6120
  },
5950
6121
  {
5951
6122
  name: "platform_skill_get",
5952
6123
  description: "\uC2A4\uD0AC \uC0C1\uC138\uB97C \uC774\uB984(slug)\uC73C\uB85C \uC870\uD68C\uD569\uB2C8\uB2E4. skill:read \uAD8C\uD55C\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.",
5953
- inputSchema: z10.toJSONSchema(patSchemas.skillGet),
6124
+ inputSchema: z11.toJSONSchema(patSchemas.skillGet),
5954
6125
  annotations: { readOnlyHint: true, destructiveHint: false }
5955
6126
  },
5956
6127
  {
5957
6128
  name: "platform_skill_push",
5958
6129
  description: "\uB85C\uCEEC \uC2A4\uD0AC \uB514\uB809\uD130\uB9AC\uB97C \uAC80\uC99D\uD558\uACE0 \uC0C8 \uBC84\uC804\uC73C\uB85C \uC5C5\uB85C\uB4DC\uD569\uB2C8\uB2E4. dir\uC744 \uC0DD\uB7B5\uD558\uBA74 \uD604\uC7AC \uB514\uB809\uD130\uB9AC\uB97C \uC0AC\uC6A9\uD569\uB2C8\uB2E4. skill:write \uAD8C\uD55C\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.",
5959
- inputSchema: z10.toJSONSchema(patSchemas.skillPush),
6130
+ inputSchema: z11.toJSONSchema(patSchemas.skillPush),
5960
6131
  annotations: { readOnlyHint: false, destructiveHint: false }
5961
6132
  },
5962
6133
  {
5963
6134
  name: "platform_skill_status",
5964
6135
  description: "\uB0B4 \uC2A4\uD0AC \uBC84\uC804\uC758 \uC2EC\uC0AC\xB7\uAC8C\uC2DC \uC0C1\uD0DC\uB97C \uC870\uD68C\uD569\uB2C8\uB2E4. skill:read \uAD8C\uD55C\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.",
5965
- inputSchema: z10.toJSONSchema(patSchemas.skillVersionRef),
6136
+ inputSchema: z11.toJSONSchema(patSchemas.skillVersionRef),
5966
6137
  annotations: { readOnlyHint: true, destructiveHint: false }
5967
6138
  },
5968
6139
  {
5969
6140
  name: "platform_skill_submit",
5970
6141
  description: "\uAC80\uC99D \uC644\uB8CC\uD55C \uC2A4\uD0AC \uBC84\uC804\uC744 \uC2EC\uC0AC\uC5D0 \uC81C\uCD9C\uD569\uB2C8\uB2E4. skill:write\uC640 skill:publish \uAD8C\uD55C(\uB458 \uB2E4)\uACFC confirm:true\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.",
5971
- inputSchema: z10.toJSONSchema(patSchemas.skillSubmit),
6142
+ inputSchema: z11.toJSONSchema(patSchemas.skillSubmit),
5972
6143
  annotations: { readOnlyHint: false, destructiveHint: true }
5973
6144
  },
5974
6145
  {
5975
6146
  name: "platform_skill_release",
5976
6147
  description: "\uC2B9\uC778\uB41C \uC2A4\uD0AC \uBC84\uC804\uC744 \uACF5\uAC1C \uBC94\uC704\uB85C \uAC8C\uC2DC\uD569\uB2C8\uB2E4. skill:publish \uAD8C\uD55C\uACFC confirm:true\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.",
5977
- inputSchema: z10.toJSONSchema(patSchemas.skillRelease),
6148
+ inputSchema: z11.toJSONSchema(patSchemas.skillRelease),
5978
6149
  annotations: { readOnlyHint: false, destructiveHint: true }
5979
6150
  },
5980
6151
  {
5981
6152
  name: "platform_content_publish",
5982
6153
  description: "\uC2B9\uC778\uB41C \uB0B4 HTML \uCF58\uD150\uCE20 \uBC84\uC804\uC744 \uACF5\uAC1C\uD569\uB2C8\uB2E4. creator-content:publish \uAD8C\uD55C\uACFC confirm:true\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.",
5983
- inputSchema: z10.toJSONSchema(patSchemas.contentPublish),
6154
+ inputSchema: z11.toJSONSchema(patSchemas.contentPublish),
5984
6155
  annotations: { readOnlyHint: false, destructiveHint: true }
5985
6156
  },
5986
6157
  {
5987
6158
  name: "platform_shared_collections_list",
5988
6159
  description: "\uB0B4 \uCF58\uD150\uCE20\uC758 \uC774\uC6A9\uC790 \uACF5\uAC1C(shared) \uBB38\uC11C \uCEEC\uB809\uC158 \uC774\uB984\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4. platform:read \uAD8C\uD55C\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.",
5989
- inputSchema: z10.toJSONSchema(patSchemas.sharedCollections),
6160
+ inputSchema: z11.toJSONSchema(patSchemas.sharedCollections),
5990
6161
  annotations: { readOnlyHint: true, destructiveHint: false }
5991
6162
  },
5992
6163
  {
5993
6164
  name: "platform_shared_documents_list",
5994
6165
  description: "\uB0B4 \uCF58\uD150\uCE20\uC758 \uACF5\uC720 \uBB38\uC11C \uBAA9\uB85D\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4. platform:read \uAD8C\uD55C\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.",
5995
- inputSchema: z10.toJSONSchema(patSchemas.sharedList),
6166
+ inputSchema: z11.toJSONSchema(patSchemas.sharedList),
5996
6167
  annotations: { readOnlyHint: true, destructiveHint: false }
5997
6168
  },
5998
6169
  {
5999
6170
  name: "platform_shared_document_get",
6000
6171
  description: "\uB0B4 \uCF58\uD150\uCE20\uC758 \uACF5\uC720 \uBB38\uC11C \uD558\uB098\uB97C \uC870\uD68C\uD569\uB2C8\uB2E4. platform:read \uAD8C\uD55C\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.",
6001
- inputSchema: z10.toJSONSchema(patSchemas.sharedDocument),
6172
+ inputSchema: z11.toJSONSchema(patSchemas.sharedDocument),
6002
6173
  annotations: { readOnlyHint: true, destructiveHint: false }
6003
6174
  },
6004
6175
  {
6005
6176
  name: "platform_shared_document_hide",
6006
6177
  description: "\uC774\uC6A9\uC790\uAC00 \uC4F4 \uACF5\uC720 \uBB38\uC11C\uB97C \uC228\uAE41\uB2C8\uB2E4. platform:write \uAD8C\uD55C\uACFC confirm:true\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.",
6007
- inputSchema: z10.toJSONSchema(patSchemas.sharedModerate),
6178
+ inputSchema: z11.toJSONSchema(patSchemas.sharedModerate),
6008
6179
  annotations: { readOnlyHint: false, destructiveHint: true }
6009
6180
  },
6010
6181
  {
6011
6182
  name: "platform_shared_document_delete",
6012
6183
  description: "\uC774\uC6A9\uC790\uAC00 \uC4F4 \uACF5\uC720 \uBB38\uC11C\uB97C \uC601\uAD6C \uC0AD\uC81C\uD569\uB2C8\uB2E4. platform:write \uAD8C\uD55C\uACFC confirm:true\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.",
6013
- inputSchema: z10.toJSONSchema(patSchemas.sharedModerate),
6184
+ inputSchema: z11.toJSONSchema(patSchemas.sharedModerate),
6014
6185
  annotations: { readOnlyHint: false, destructiveHint: true }
6015
6186
  }
6016
6187
  ];
@@ -6251,14 +6422,14 @@ function registerIntegrationCommands(program2, runtime2) {
6251
6422
 
6252
6423
  // src/commands/page.ts
6253
6424
  import { Option as Option3 } from "commander";
6254
- import { z as z11 } from "zod";
6255
- var uuid3 = (value2) => parse(z11.uuid(), value2);
6256
- var revision3 = (value2) => parse(z11.coerce.number().int().nonnegative().max(4294967295), value2);
6425
+ import { z as z12 } from "zod";
6426
+ var uuid3 = (value2) => parse(z12.uuid(), value2);
6427
+ var revision3 = (value2) => parse(z12.coerce.number().int().nonnegative().max(4294967295), value2);
6257
6428
  var base = "/v4/creator/custom-pages";
6258
- var presentationSchema = z11.object({
6259
- schema_version: z11.literal(1),
6260
- header_mode: z11.enum(["fixed", "scroll_hide", "translucent_scroll_hide", "floating_close", "floating_close_safe_area"]),
6261
- color_scheme: z11.enum(["light", "dark"])
6429
+ var presentationSchema = z12.object({
6430
+ schema_version: z12.literal(1),
6431
+ header_mode: z12.enum(["fixed", "scroll_hide", "translucent_scroll_hide", "floating_close", "floating_close_safe_area"]),
6432
+ color_scheme: z12.enum(["light", "dark"])
6262
6433
  }).strict();
6263
6434
  function registerPageCommands(program2, runtime2) {
6264
6435
  const page = program2.command("page").description("\uC138\uACC4\uAD00\xB7\uB0B4 \uACF5\uAC1C \uD648 HTML \uAFB8\uBBF8\uAE30 (custom-page:read\xB7write\xB7publish)");
@@ -6266,7 +6437,7 @@ function registerPageCommands(program2, runtime2) {
6266
6437
  runtime2.action(page.command("status <id>").description("\uD398\uC774\uC9C0\uC640 \uBC84\uC804\xB7\uC218\uC815 \uBC88\uD638 \uC0C1\uD0DC (custom-page:read)"), async (ctx, [id2]) => ctx.output(await ctx.api.request("GET", `${base}/${uuid3(id2)}`)));
6267
6438
  runtime2.action(page.command("create").description("\uAFB8\uBC00 \uD398\uC774\uC9C0 \uB4F1\uB85D (custom-page:write)").addOption(new Option3("--target <type>", "\uAFB8\uBC00 \uB300\uC0C1").choices(["profile", "space"]).makeOptionMandatory()).option("--space-id <uuid>", "\uC138\uACC4\uAD00 ID").requiredOption("--policy-version <version>", "\uB3D9\uC758\uD55C \uCF58\uD150\uCE20 \uC81C\uACF5 \uC815\uCC45 \uBC84\uC804"), async (ctx, _args, opts) => {
6268
6439
  if (opts.target === "profile" && opts.spaceId) throw new CliError("\uB0B4 \uACF5\uAC1C \uD648\uC5D0\uB294 \uC138\uACC4\uAD00 ID\uB97C \uC9C0\uC815\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.");
6269
- const policy = parse(z11.literal("2026-09-22"), opts.policyVersion);
6440
+ const policy = parse(z12.literal("2026-09-22"), opts.policyVersion);
6270
6441
  await mutate(ctx, "POST", base, { target_type: opts.target, ...opts.target === "space" ? { space_id: uuid3(opts.spaceId) } : {}, policy_version: policy });
6271
6442
  });
6272
6443
  runtime2.action(page.command("upload <id> <file>").description("HTML/ZIP \uC5C5\uB85C\uB4DC\uB85C \uC0C8 \uBE44\uACF5\uAC1C \uBC84\uC804 \uC0DD\uC131 (custom-page:write)").addOption(new Option3("--header <mode>", "\uD5E4\uB354 \uD45C\uC2DC").choices(["fixed", "scroll_hide", "translucent_scroll_hide", "floating_close", "floating_close_safe_area"]).default("fixed")).addOption(new Option3("--color <scheme>", "\uD5E4\uB354 \uC0C9\uC0C1").choices(["light", "dark"]).default("light")), async (ctx, [id2, file], opts) => {
@@ -6331,7 +6502,8 @@ try {
6331
6502
  status: failure.status,
6332
6503
  code: failure.code,
6333
6504
  message: failure.message,
6334
- ...failure.retryAfter ? { retry_after: failure.retryAfter } : {}
6505
+ ...failure.retryAfter ? { retry_after: failure.retryAfter } : {},
6506
+ ...failure.recovery ? { recovery: failure.recovery } : {}
6335
6507
  });
6336
6508
  const json2 = program.opts().json || process.argv.includes("--json") || await store.resolve(program.opts(), true).then((value2) => value2.options.json).catch(() => false);
6337
6509
  if (json2) process.stdout.write(JSON.stringify(envelope) + "\n");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@clack-platform/cli",
3
- "version": "0.3.2",
3
+ "version": "0.3.3",
4
4
  "description": "클랙 계정의 상품·콘텐츠·프로필을 관리하는 CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -39,7 +39,7 @@
39
39
  "test": "node scripts/test.mjs",
40
40
  "build": "tsup src/cli.ts --format esm --target node20 --clean",
41
41
  "check": "pnpm typecheck && node scripts/check-schema-sync.mjs && pnpm test && pnpm build",
42
- "check:release": "pnpm typecheck && node scripts/check-schema-sync.mjs --strict && pnpm test && pnpm build",
42
+ "check:release": "pnpm typecheck && node scripts/check-schema-sync.mjs --strict && node scripts/sync-vendor-types.mjs --check && pnpm test && pnpm build",
43
43
  "prepack": "pnpm build"
44
44
  },
45
45
  "dependencies": {