@clack-platform/cli 0.1.1 → 0.3.0
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.
- package/README.md +36 -6
- package/dist/cli.js +1405 -143
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -39,6 +39,9 @@ function apiError(status, body, retryAfter) {
|
|
|
39
39
|
if (code === "SKILL_VERSION_MAJOR_REQUIRED") {
|
|
40
40
|
message += " clack.skill.json\xB7SKILL.md\uC758 version\uC744 \uC62C\uB824 \uB2E4\uC2DC push\uD558\uC138\uC694. \uAC19\uC740 \uBC84\uC804\uC744 \uD638\uD658\uB418\uAC8C \uACE0\uCCD0 \uC62C\uB9AC\uB824\uBA74 \uBA3C\uC800 clack skill cancel\uB85C \uC774 \uBC84\uC804\uC744 \uC9C0\uC6B0\uC138\uC694.";
|
|
41
41
|
}
|
|
42
|
+
if (code === "CONTENT_INFO_VERSION_DISABLED") message += " clack content config\uC758 info_version.enabled\uAC00 true\uC778 \uD658\uACBD\uC5D0\uC11C\uB9CC \uC0AC\uC6A9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.";
|
|
43
|
+
if (code === "CONTENT_INFO_UNCHANGED") message += " \uC815\uBCF4\uB97C \uBA3C\uC800 clack content update\uB85C \uC218\uC815\uD55C \uB4A4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.";
|
|
44
|
+
if (code === "CONTENT_METADATA_EXTRAS_DISABLED") message += " \uC774 \uD658\uACBD\uC5D0\uC11C\uB294 \uC5B8\uC5B4\uBCC4 \uBA54\uD0C0\uB370\uC774\uD130\xB7\uD0DC\uADF8\uB97C \uC544\uC9C1 \uC4F8 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.";
|
|
42
45
|
if (status === 423 || code === "USER_BANNED") message += " \uACC4\uC815 \uC81C\uD55C \uC0C1\uD0DC\uB97C \uC571\uC5D0\uC11C \uD655\uC778\uD558\uC138\uC694.";
|
|
43
46
|
if (status === 503 && code.endsWith("DISABLED")) message += " \uD604\uC7AC \uC678\uBD80 \uB3C4\uAD6C \uC811\uADFC\uC774 \uC77C\uC2DC \uC911\uB2E8\uB418\uC5C8\uC2B5\uB2C8\uB2E4.";
|
|
44
47
|
const raw = Number(value2.retry_after ?? retryAfter);
|
|
@@ -150,7 +153,7 @@ function redact(value2) {
|
|
|
150
153
|
// package.json
|
|
151
154
|
var package_default = {
|
|
152
155
|
name: "@clack-platform/cli",
|
|
153
|
-
version: "0.
|
|
156
|
+
version: "0.3.0",
|
|
154
157
|
description: "\uD074\uB799 \uACC4\uC815\uC758 \uC0C1\uD488\xB7\uCF58\uD150\uCE20\xB7\uD504\uB85C\uD544\uC744 \uAD00\uB9AC\uD558\uB294 CLI",
|
|
155
158
|
type: "module",
|
|
156
159
|
bin: {
|
|
@@ -399,9 +402,9 @@ function documentPath(collection2, key) {
|
|
|
399
402
|
return `/platform/v1/data/${collection2}/docs/${key}`;
|
|
400
403
|
}
|
|
401
404
|
function bodySize(body) {
|
|
402
|
-
const
|
|
403
|
-
if (
|
|
404
|
-
return
|
|
405
|
+
const bytes2 = Buffer.byteLength(JSON.stringify(body), "utf8");
|
|
406
|
+
if (bytes2 > maxBodyBytes) throw new CliError("\uBB38\uC11C \uBCF8\uBB38\uC740 64 KiB \uC774\uD558\uC5EC\uC57C \uD569\uB2C8\uB2E4.", "VALIDATION_ERROR");
|
|
407
|
+
return bytes2;
|
|
405
408
|
}
|
|
406
409
|
async function readPlatformBodyFile(file) {
|
|
407
410
|
let raw;
|
|
@@ -598,11 +601,11 @@ async function authoringRequest(client, raw, surface = "cli") {
|
|
|
598
601
|
if (input.action === "upload") {
|
|
599
602
|
const stat3 = await lstat2(input.file);
|
|
600
603
|
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.");
|
|
601
|
-
const
|
|
604
|
+
const bytes2 = await readFile2(input.file);
|
|
602
605
|
const body2 = new FormData();
|
|
603
606
|
body2.append("field", input.field);
|
|
604
607
|
if (input.item_index !== void 0) body2.append("item_index", String(input.item_index));
|
|
605
|
-
body2.append("file", new Blob([
|
|
608
|
+
body2.append("file", new Blob([bytes2]), basename(input.file));
|
|
606
609
|
return client.request("POST", `${path}/assets`, { body: body2 });
|
|
607
610
|
}
|
|
608
611
|
if (input.action === "image" || input.action === "image-batch") {
|
|
@@ -1262,9 +1265,9 @@ function isRemoteImage(value2) {
|
|
|
1262
1265
|
return false;
|
|
1263
1266
|
}
|
|
1264
1267
|
}
|
|
1265
|
-
function dimensions(
|
|
1268
|
+
function dimensions(bytes2) {
|
|
1266
1269
|
try {
|
|
1267
|
-
const result = imageSize(
|
|
1270
|
+
const result = imageSize(bytes2);
|
|
1268
1271
|
if (!result.width || !result.height || result.width > 2e4 || result.height > 2e4 || !result.type || !MIME[result.type]) throw new Error();
|
|
1269
1272
|
return { width: result.width, height: result.height, mime: MIME[result.type] };
|
|
1270
1273
|
} catch {
|
|
@@ -1291,26 +1294,26 @@ async function prepareImage(source, baseDir = process.cwd(), measureRemote = fal
|
|
|
1291
1294
|
} finally {
|
|
1292
1295
|
await reader.cancel();
|
|
1293
1296
|
}
|
|
1294
|
-
const
|
|
1297
|
+
const bytes3 = new Uint8Array(size);
|
|
1295
1298
|
let offset = 0;
|
|
1296
1299
|
for (const chunk of chunks) {
|
|
1297
|
-
|
|
1300
|
+
bytes3.set(chunk, offset);
|
|
1298
1301
|
offset += chunk.length;
|
|
1299
1302
|
}
|
|
1300
|
-
return { source, ...dimensions(
|
|
1303
|
+
return { source, ...dimensions(bytes3) };
|
|
1301
1304
|
}
|
|
1302
1305
|
if (/^[a-z][a-z0-9+.-]*:/i.test(source) && !/^[a-z]:[\\/]/i.test(source)) throw new CliError("\uC774\uBBF8\uC9C0\uB294 \uB85C\uCEEC \uD30C\uC77C \uB610\uB294 http(s) URL\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4.");
|
|
1303
1306
|
const path = resolve(baseDir, source);
|
|
1304
|
-
let
|
|
1307
|
+
let bytes2;
|
|
1305
1308
|
try {
|
|
1306
1309
|
const info = await stat(path);
|
|
1307
1310
|
if (!info.isFile() || info.size === 0 || info.size > MAX_IMAGE_BYTES) throw new Error();
|
|
1308
|
-
|
|
1309
|
-
if (
|
|
1311
|
+
bytes2 = await readFile5(path);
|
|
1312
|
+
if (bytes2.length > MAX_IMAGE_BYTES) throw new Error();
|
|
1310
1313
|
} catch {
|
|
1311
1314
|
throw new CliError(`10MB \uC774\uD558 \uC774\uBBF8\uC9C0 \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${path}`);
|
|
1312
1315
|
}
|
|
1313
|
-
return { source: path, bytes, ...dimensions(
|
|
1316
|
+
return { source: path, bytes: bytes2, ...dimensions(bytes2) };
|
|
1314
1317
|
}
|
|
1315
1318
|
async function uploadPrepared(ctx, image2) {
|
|
1316
1319
|
if (!image2.bytes) return { originalUrl: image2.source };
|
|
@@ -1998,6 +2001,11 @@ import { createInterface } from "readline";
|
|
|
1998
2001
|
import { z as z10 } from "zod";
|
|
1999
2002
|
|
|
2000
2003
|
// src/commands/skill.ts
|
|
2004
|
+
import { spawn } from "child_process";
|
|
2005
|
+
import { existsSync } from "fs";
|
|
2006
|
+
import { writeFile as writeFile2 } from "fs/promises";
|
|
2007
|
+
import { createRequire } from "module";
|
|
2008
|
+
import { join as join3, resolve as resolve8 } from "path";
|
|
2001
2009
|
import { z as z9 } from "zod";
|
|
2002
2010
|
|
|
2003
2011
|
// src/commands/content.ts
|
|
@@ -2034,15 +2042,15 @@ var CAPABILITIES = /* @__PURE__ */ new Set([
|
|
|
2034
2042
|
function isObject(value2) {
|
|
2035
2043
|
return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
|
|
2036
2044
|
}
|
|
2037
|
-
function parseObject(
|
|
2045
|
+
function parseObject(bytes2) {
|
|
2038
2046
|
try {
|
|
2039
|
-
const value2 = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(
|
|
2047
|
+
const value2 = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes2));
|
|
2040
2048
|
return isObject(value2) ? value2 : null;
|
|
2041
2049
|
} catch {
|
|
2042
2050
|
return null;
|
|
2043
2051
|
}
|
|
2044
2052
|
}
|
|
2045
|
-
function inspectContentPackage(
|
|
2053
|
+
function inspectContentPackage(bytes2) {
|
|
2046
2054
|
const warnings = [];
|
|
2047
2055
|
const paths = /* @__PURE__ */ new Set();
|
|
2048
2056
|
const profileFiles = /* @__PURE__ */ new Set();
|
|
@@ -2061,7 +2069,7 @@ function inspectContentPackage(bytes) {
|
|
|
2061
2069
|
let tooLargeExpanded = false;
|
|
2062
2070
|
let files;
|
|
2063
2071
|
try {
|
|
2064
|
-
files = unzipSync(
|
|
2072
|
+
files = unzipSync(bytes2, { filter(entry) {
|
|
2065
2073
|
entries++;
|
|
2066
2074
|
if (entries > MAX_FILES) {
|
|
2067
2075
|
tooManyFiles = true;
|
|
@@ -2144,31 +2152,80 @@ var createSchema = z8.object({
|
|
|
2144
2152
|
kind: z8.enum(["html", "gallery", "slideshow", "video"]).default("html"),
|
|
2145
2153
|
policy_version: z8.literal("2026-09-22")
|
|
2146
2154
|
}).strict();
|
|
2155
|
+
var METADATA_LANGS = ["ko", "en"];
|
|
2156
|
+
var TAG_PATTERN = /^[\p{L}\p{M}\p{N}_]{1,30}$/u;
|
|
2157
|
+
var localizedEntry = z8.object({
|
|
2158
|
+
title: z8.string().trim().max(120),
|
|
2159
|
+
description: z8.string().trim().max(2e3).optional()
|
|
2160
|
+
}).strict();
|
|
2161
|
+
var updateSchema = z8.object({
|
|
2162
|
+
title: z8.string().trim().max(120).optional(),
|
|
2163
|
+
description: z8.string().trim().max(2e3).optional(),
|
|
2164
|
+
kind: z8.enum(["html", "gallery", "slideshow", "video"]).optional(),
|
|
2165
|
+
thumbnail_id: z8.uuid().nullable().optional(),
|
|
2166
|
+
metadata_mode: z8.enum(["single", "localized"]).optional(),
|
|
2167
|
+
metadata_lang: z8.enum(METADATA_LANGS).nullable().optional(),
|
|
2168
|
+
localized_metadata: z8.partialRecord(z8.enum(METADATA_LANGS), localizedEntry).nullable().optional(),
|
|
2169
|
+
tags: z8.array(z8.string().max(64)).max(50).optional()
|
|
2170
|
+
}).strict().superRefine((value2, ctx) => {
|
|
2171
|
+
const issue = (message) => ctx.addIssue({ code: "custom", message });
|
|
2172
|
+
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.");
|
|
2173
|
+
if (value2.tags) {
|
|
2174
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2175
|
+
for (const raw of value2.tags) {
|
|
2176
|
+
const tag = raw.trim().replace(/^#+/, "").trim().normalize("NFC");
|
|
2177
|
+
if (!TAG_PATTERN.test(tag)) issue("\uD574\uC2DC\uD0DC\uADF8\uB294 \uAE00\uC790\xB7\uC22B\uC790\xB7\uBC11\uC904(_)\uB9CC \uC0AC\uC6A9\uD574 1~30\uC790\uB85C \uC785\uB825\uD558\uC138\uC694.");
|
|
2178
|
+
const key = tag.normalize("NFKC").toLowerCase();
|
|
2179
|
+
if (key.length > 40) issue("\uD574\uC2DC\uD0DC\uADF8\uAC00 \uB108\uBB34 \uAE41\uB2C8\uB2E4. \uB354 \uC9E7\uAC8C \uC785\uB825\uD558\uC138\uC694.");
|
|
2180
|
+
seen.add(key);
|
|
2181
|
+
}
|
|
2182
|
+
if (seen.size > 10) issue("\uD574\uC2DC\uD0DC\uADF8\uB294 \uCD5C\uB300 10\uAC1C\uAE4C\uC9C0 \uB4F1\uB85D\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.");
|
|
2183
|
+
}
|
|
2184
|
+
if (value2.metadata_mode === "localized") {
|
|
2185
|
+
if (!value2.metadata_lang) issue("\uC5B8\uC5B4\uBCC4\uB85C \uC785\uB825\uD558\uB824\uBA74 --metadata-lang\uC73C\uB85C \uB300\uD45C \uC5B8\uC5B4\uB97C \uC9C0\uC815\uD558\uC138\uC694.");
|
|
2186
|
+
if (!value2.localized_metadata) issue("\uC5B8\uC5B4\uBCC4\uB85C \uC785\uB825\uD558\uB824\uBA74 --localized-metadata\uB85C \uB300\uD45C \uC5B8\uC5B4 \uC678 \uC5B8\uC5B4\uB97C 1\uAC1C \uC774\uC0C1 \uC9C0\uC815\uD558\uC138\uC694.");
|
|
2187
|
+
}
|
|
2188
|
+
if (value2.localized_metadata) {
|
|
2189
|
+
for (const [lang, entry] of Object.entries(value2.localized_metadata)) {
|
|
2190
|
+
if (entry && !entry.title.trim()) issue("\uC120\uD0DD\uD55C \uBAA8\uB4E0 \uC5B8\uC5B4\uC758 \uC81C\uBAA9\uC744 \uC785\uB825\uD558\uC138\uC694.");
|
|
2191
|
+
if (lang === value2.metadata_lang) issue("\uB300\uD45C \uC5B8\uC5B4\uB294 --title\xB7--description\uC5D0 \uB123\uACE0, \uC5B8\uC5B4\uBCC4 \uAC12\uC5D0\uB294 \uB2E4\uB978 \uC5B8\uC5B4\uB9CC \uB123\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4.");
|
|
2192
|
+
}
|
|
2193
|
+
if (value2.metadata_mode === "single" && Object.keys(value2.localized_metadata).length) issue("\uD55C \uC5B8\uC5B4(single)\uB85C \uC785\uB825\uD560 \uB54C\uB294 \uC5B8\uC5B4\uBCC4 \uAC12\uC744 \uBCF4\uB0BC \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
2194
|
+
}
|
|
2195
|
+
});
|
|
2196
|
+
function jsonOption(name2, value2) {
|
|
2197
|
+
if (value2 === "none") return null;
|
|
2198
|
+
try {
|
|
2199
|
+
return JSON.parse(value2);
|
|
2200
|
+
} catch {
|
|
2201
|
+
throw new CliError(`${name2} \uAC12\uC740 JSON \uAC1D\uCCB4\uC774\uAC70\uB098 none\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4.`);
|
|
2202
|
+
}
|
|
2203
|
+
}
|
|
2147
2204
|
async function prepareContentFile(source) {
|
|
2148
2205
|
const file = resolve6(source);
|
|
2149
2206
|
const extension = extname(file).toLowerCase();
|
|
2150
2207
|
if (![".html", ".htm", ".zip"].includes(extension)) throw new CliError("HTML \uB610\uB294 ZIP \uD30C\uC77C\uC744 \uC9C0\uC815\uD558\uC138\uC694.");
|
|
2151
|
-
let
|
|
2208
|
+
let bytes2;
|
|
2152
2209
|
try {
|
|
2153
2210
|
const info = await stat2(file);
|
|
2154
2211
|
if (!info.isFile() || info.size === 0 || info.size > MAX_BYTES) throw new Error();
|
|
2155
|
-
|
|
2156
|
-
if (!
|
|
2212
|
+
bytes2 = await readFile8(file);
|
|
2213
|
+
if (!bytes2.length || bytes2.length > MAX_BYTES) throw new Error();
|
|
2157
2214
|
} catch {
|
|
2158
2215
|
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.");
|
|
2159
2216
|
}
|
|
2160
2217
|
return {
|
|
2161
|
-
bytes,
|
|
2162
|
-
warnings: extension === ".zip" ? inspectContentPackage(
|
|
2218
|
+
bytes: bytes2,
|
|
2219
|
+
warnings: extension === ".zip" ? inspectContentPackage(bytes2) : [],
|
|
2163
2220
|
metadata: {
|
|
2164
2221
|
filename: basename3(file),
|
|
2165
2222
|
content_type: extension === ".zip" ? "application/zip" : "text/html",
|
|
2166
|
-
byte_size:
|
|
2167
|
-
sha256: createHash2("sha256").update(
|
|
2223
|
+
byte_size: bytes2.length,
|
|
2224
|
+
sha256: createHash2("sha256").update(bytes2).digest("hex")
|
|
2168
2225
|
}
|
|
2169
2226
|
};
|
|
2170
2227
|
}
|
|
2171
|
-
async function putContentFile(session2,
|
|
2228
|
+
async function putContentFile(session2, bytes2, fetcher = fetch) {
|
|
2172
2229
|
uuid(session2.upload_id);
|
|
2173
2230
|
let url;
|
|
2174
2231
|
try {
|
|
@@ -2179,7 +2236,7 @@ async function putContentFile(session2, bytes, fetcher = fetch) {
|
|
|
2179
2236
|
}
|
|
2180
2237
|
const contentType = session2.headers?.["Content-Type"];
|
|
2181
2238
|
const checksum = session2.headers?.["x-amz-checksum-sha256"];
|
|
2182
|
-
if (!["text/html", "application/zip"].includes(contentType ?? "") || checksum !== createHash2("sha256").update(
|
|
2239
|
+
if (!["text/html", "application/zip"].includes(contentType ?? "") || checksum !== createHash2("sha256").update(bytes2).digest("base64")) {
|
|
2183
2240
|
throw new CliError("\uC5C5\uB85C\uB4DC \uCCB4\uD06C\uC12C\uACFC \uD30C\uC77C \uD615\uC2DD\uC774 \uC77C\uCE58\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.", "INVALID_UPLOAD_RESPONSE", 502);
|
|
2184
2241
|
}
|
|
2185
2242
|
rememberSecret(session2.upload_url);
|
|
@@ -2187,7 +2244,7 @@ async function putContentFile(session2, bytes, fetcher = fetch) {
|
|
|
2187
2244
|
const result = await fetcher(url, {
|
|
2188
2245
|
method: "PUT",
|
|
2189
2246
|
headers: { "Content-Type": contentType, "x-amz-checksum-sha256": checksum },
|
|
2190
|
-
body: new Uint8Array(
|
|
2247
|
+
body: new Uint8Array(bytes2),
|
|
2191
2248
|
redirect: "error",
|
|
2192
2249
|
signal: AbortSignal.timeout(12e4)
|
|
2193
2250
|
});
|
|
@@ -2213,7 +2270,20 @@ function registerContentCommands(program2, runtime2) {
|
|
|
2213
2270
|
const body = parse(createSchema, { title: opts.title, description: opts.description, kind: opts.kind, policy_version: opts.policyVersion });
|
|
2214
2271
|
await mutate(ctx, "POST", "/v4/creator/contents", body);
|
|
2215
2272
|
});
|
|
2216
|
-
runtime2.action(content.command("
|
|
2273
|
+
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) => {
|
|
2274
|
+
const body = parse(updateSchema, {
|
|
2275
|
+
title: opts.title,
|
|
2276
|
+
description: opts.description,
|
|
2277
|
+
kind: opts.kind,
|
|
2278
|
+
thumbnail_id: opts.thumbnailId === void 0 ? void 0 : opts.thumbnailId === "none" ? null : opts.thumbnailId,
|
|
2279
|
+
metadata_mode: opts.metadataMode,
|
|
2280
|
+
metadata_lang: opts.metadataLang === void 0 ? void 0 : opts.metadataLang === "none" ? null : opts.metadataLang,
|
|
2281
|
+
localized_metadata: opts.localizedMetadata === void 0 ? void 0 : jsonOption("--localized-metadata", opts.localizedMetadata),
|
|
2282
|
+
tags: opts.tags === void 0 ? void 0 : csv(opts.tags)
|
|
2283
|
+
});
|
|
2284
|
+
await mutate(ctx, "PATCH", `/v4/creator/contents/${uuid(id2)}`, body);
|
|
2285
|
+
});
|
|
2286
|
+
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) => {
|
|
2217
2287
|
const path = `/v4/creator/contents/${uuid(id2)}`;
|
|
2218
2288
|
const prepared = await prepareContentFile(file);
|
|
2219
2289
|
for (const warning of prepared.warnings) process.stderr.write(`\uD328\uD0A4\uC9C0 \uACBD\uACE0: ${warning}
|
|
@@ -2233,6 +2303,13 @@ function registerContentCommands(program2, runtime2) {
|
|
|
2233
2303
|
runtime2.action(content.command("preview <id> <version-id>").description("\uC571 \uD655\uC778\uC6A9 \uBBF8\uB9AC\uBCF4\uAE30 \uB9C1\uD06C \uC870\uD68C (creator-content:read)"), async (ctx, [id2, versionId]) => ctx.output(await ctx.api.request("GET", `/v4/creator/contents/${uuid(id2)}/versions/${uuid(versionId)}/preview`)));
|
|
2234
2304
|
runtime2.action(content.command("submit <id> <version-id>").description("\uC571\uC5D0\uC11C \uD655\uC778 \uC644\uB8CC\uD55C \uBC84\uC804\uC758 \uC2EC\uC0AC \uC81C\uCD9C, \uD1B5\uACFC \uC2DC \uACF5\uAC1C (creator-content:write\uC640 creator-content:publish \uB458 \uB2E4 \uD544\uC694)"), async (ctx, [id2, versionId]) => mutate(ctx, "POST", `/v4/creator/contents/${uuid(id2)}/versions/${uuid(versionId)}/submit`, { publish_on_approval: true }, "\uC774 \uBC84\uC804\uC744 \uC2EC\uC0AC\uD558\uACE0 \uD1B5\uACFC\uD558\uBA74 \uACF5\uAC1C\uD560\uAE4C\uC694?"));
|
|
2235
2305
|
runtime2.action(content.command("withdraw <id> <version-id>").description("\uC9C4\uD589 \uC911\uC778 \uC2EC\uC0AC \uCDE8\uC18C (creator-content:write)"), async (ctx, [id2, versionId]) => mutate(ctx, "POST", `/v4/creator/contents/${uuid(id2)}/versions/${uuid(versionId)}/review/cancel`, void 0, "\uC774 \uBC84\uC804\uC758 \uC2EC\uC0AC\uB97C \uCDE8\uC18C\uD560\uAE4C\uC694?"));
|
|
2306
|
+
runtime2.action(content.command("info-version <id> <version-id>").description("\uBC14\uB010 \uCF58\uD150\uCE20 \uC815\uBCF4\uB9CC \uB2F4\uC740 \uC0C8 \uBC84\uC804 \uC0DD\uC131 (creator-content:write)"), async (ctx, [id2, versionId]) => mutate(
|
|
2307
|
+
ctx,
|
|
2308
|
+
"POST",
|
|
2309
|
+
`/v4/creator/contents/${uuid(id2)}/versions/${uuid(versionId)}/info-version`,
|
|
2310
|
+
void 0,
|
|
2311
|
+
"\uD604\uC7AC \uCF58\uD150\uCE20 \uC815\uBCF4\uB85C \uC774 \uBC84\uC804\uC744 \uBCF5\uC81C\uD55C \uC0C8 \uCD08\uC548\uC744 \uB9CC\uB4E4\uAE4C\uC694? \uC0C8 \uBC84\uC804\uC740 \uC571 \uD655\uC778\uACFC \uC2EC\uC0AC\uB97C \uB2E4\uC2DC \uBC1B\uC544\uC57C \uD569\uB2C8\uB2E4."
|
|
2312
|
+
));
|
|
2236
2313
|
runtime2.action(content.command("publish <id> <version-id>").description("\uC2B9\uC778\uB410\uC9C0\uB9CC \uACF5\uAC1C\uD558\uC9C0 \uC54A\uC740 \uBC84\uC804 \uACF5\uAC1C (creator-content:publish)"), async (ctx, [id2, versionId]) => mutate(ctx, "POST", `/v4/creator/contents/${uuid(id2)}/versions/${uuid(versionId)}/publish`, void 0, "\uC2B9\uC778\uB41C \uC774 \uBC84\uC804\uC744 \uACF5\uAC1C\uD560\uAE4C\uC694?"));
|
|
2237
2314
|
runtime2.action(content.command("unpublish <id>").description("\uCF58\uD150\uCE20 \uAC8C\uC2DC \uC911\uB2E8 (creator-content:publish)"), async (ctx, [id2]) => mutate(ctx, "POST", `/v4/creator/contents/${uuid(id2)}/unpublish`, void 0, "\uCF58\uD150\uCE20 \uAC8C\uC2DC\uB97C \uC911\uB2E8\uD560\uAE4C\uC694?"));
|
|
2238
2315
|
const serverKeys = content.command("server-keys").description("\uCF58\uD150\uCE20 \uC11C\uBC84 \uD0A4 \uC870\uD68C\xB7\uD3D0\uAE30 (platform:read\xB7platform:write, \uBC1C\uAE09\xB7\uD68C\uC804\uC740 \uD06C\uB9AC\uC5D0\uC774\uD130 \uC13C\uD130)");
|
|
@@ -2271,36 +2348,161 @@ function registerContentCommands(program2, runtime2) {
|
|
|
2271
2348
|
}
|
|
2272
2349
|
|
|
2273
2350
|
// src/vendor/clack-api-v4-contracts/authoring-semantics.mjs
|
|
2274
|
-
function
|
|
2351
|
+
function walkDataSchema(schema, visit) {
|
|
2352
|
+
const defs = schema?.$defs ?? {};
|
|
2353
|
+
const step = (node, info) => {
|
|
2354
|
+
if (!node || typeof node !== "object" || Array.isArray(node)) return;
|
|
2355
|
+
if (typeof node.$ref === "string") {
|
|
2356
|
+
const key = node.$ref.startsWith("#/$defs/") ? node.$ref.slice("#/$defs/".length) : null;
|
|
2357
|
+
if (key === null || !Object.hasOwn(defs, key) || info.refs.includes(key)) {
|
|
2358
|
+
visit(node, { ...info, cycle: key !== null && info.refs.includes(key), unresolved: key === null || !Object.hasOwn(defs, key) });
|
|
2359
|
+
return;
|
|
2360
|
+
}
|
|
2361
|
+
visit(node, info);
|
|
2362
|
+
step(defs[key], { ...info, refs: [...info.refs, key] });
|
|
2363
|
+
return;
|
|
2364
|
+
}
|
|
2365
|
+
visit(node, info);
|
|
2366
|
+
for (const branch of Array.isArray(node.oneOf) ? node.oneOf : []) step(branch, { ...info, branch: true });
|
|
2367
|
+
for (const [name2, child] of Object.entries(node.properties && typeof node.properties === "object" ? node.properties : {})) {
|
|
2368
|
+
step(child, {
|
|
2369
|
+
...info,
|
|
2370
|
+
pointer: `${info.pointer}/${name2}`,
|
|
2371
|
+
depth: info.depth + 1,
|
|
2372
|
+
root: info.depth === 0 ? name2 : info.root,
|
|
2373
|
+
visibility: info.depth === 0 ? child?.["x-clack-visibility"] : info.visibility,
|
|
2374
|
+
branch: false
|
|
2375
|
+
});
|
|
2376
|
+
}
|
|
2377
|
+
if (node.items && typeof node.items === "object") step(node.items, { ...info, pointer: `${info.pointer}/*`, depth: info.depth + 1, branch: false });
|
|
2378
|
+
};
|
|
2379
|
+
step(schema, { pointer: "", depth: 0, root: null, visibility: null, refs: [], branch: false });
|
|
2380
|
+
}
|
|
2381
|
+
function dataRootField(schema, name2) {
|
|
2382
|
+
const field2 = schema?.properties?.[name2];
|
|
2383
|
+
if (!field2 || typeof field2 !== "object") return null;
|
|
2384
|
+
if (typeof field2.$ref === "string" && field2.$ref.startsWith("#/$defs/")) {
|
|
2385
|
+
const target = schema.$defs?.[field2.$ref.slice("#/$defs/".length)];
|
|
2386
|
+
return target && typeof target === "object" ? { ...target, "x-clack-visibility": field2["x-clack-visibility"] } : null;
|
|
2387
|
+
}
|
|
2388
|
+
return field2;
|
|
2389
|
+
}
|
|
2390
|
+
function inspectEditorSemantics(skill, data, fail2) {
|
|
2391
|
+
const editor = skill.authoring?.editor;
|
|
2392
|
+
if (!editor) return;
|
|
2393
|
+
const permissions = new Set(editor.permissions ?? []);
|
|
2394
|
+
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.");
|
|
2395
|
+
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.");
|
|
2396
|
+
const imageTool = (skill.authoring?.tools ?? []).find((tool) => tool.tool === "image.generate");
|
|
2397
|
+
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.");
|
|
2398
|
+
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.");
|
|
2399
|
+
if (!data) return;
|
|
2400
|
+
let assets = 0;
|
|
2401
|
+
let suggest = 0;
|
|
2402
|
+
walkDataSchema(data, (node) => {
|
|
2403
|
+
if (node["x-clack-asset"]) assets++;
|
|
2404
|
+
if (node["x-clack-ai"]?.suggest === true) suggest++;
|
|
2405
|
+
});
|
|
2406
|
+
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.");
|
|
2407
|
+
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.");
|
|
2408
|
+
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.");
|
|
2409
|
+
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.");
|
|
2410
|
+
const metadata = skill.output?.metadata ?? {};
|
|
2411
|
+
for (const key of ["title", "description"]) {
|
|
2412
|
+
if (metadata[key] === void 0) continue;
|
|
2413
|
+
const field2 = dataRootField(data, metadata[key]);
|
|
2414
|
+
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.");
|
|
2415
|
+
}
|
|
2416
|
+
for (const key of ["thumbnail", "thumbnail_fallback"]) {
|
|
2417
|
+
if (metadata[key] === void 0) continue;
|
|
2418
|
+
const field2 = dataRootField(data, metadata[key]);
|
|
2419
|
+
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).");
|
|
2420
|
+
}
|
|
2421
|
+
}
|
|
2422
|
+
var INTRO_MAX_IMAGES = 8;
|
|
2423
|
+
var INTRO_CREATOR_COMMENT_MAX = 500;
|
|
2424
|
+
var INTRO_DESCRIPTION_MAX = 5e3;
|
|
2425
|
+
var INTRO_TEXT_WIDGETS = /* @__PURE__ */ new Set(["text", "textarea"]);
|
|
2426
|
+
var labelRecord = (value2) => value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : null;
|
|
2427
|
+
var introLabelText = (value2) => value2.normalize("NFC").trim();
|
|
2428
|
+
function introFieldLabelMaxLength(name2, field2) {
|
|
2429
|
+
const title = typeof field2?.title === "string" ? introLabelText(field2.title) : "";
|
|
2430
|
+
const lengths = [(title || name2).length];
|
|
2431
|
+
for (const value2 of Object.values(labelRecord(field2?.["x-clack-i18n"]) ?? {})) {
|
|
2432
|
+
if (typeof value2 === "string") lengths.push(introLabelText(value2).length);
|
|
2433
|
+
}
|
|
2434
|
+
return Math.max(...lengths);
|
|
2435
|
+
}
|
|
2436
|
+
function inspectIntroSemantics(skill, form, slots, fail2) {
|
|
2437
|
+
const intro = skill.output?.intro;
|
|
2438
|
+
if (!intro) return;
|
|
2439
|
+
let images = 0;
|
|
2440
|
+
for (const name2 of intro.images ?? []) {
|
|
2441
|
+
const slot = slots.find((entry) => entry.field === name2);
|
|
2442
|
+
if (!slot) {
|
|
2443
|
+
fail2("/output/intro/images", "\uC778\uD2B8\uB85C \uC774\uBBF8\uC9C0\uB294 output.asset_slots\uC758 \uD544\uB4DC\uB9CC \uC4F8 \uC218 \uC788\uC2B5\uB2C8\uB2E4.");
|
|
2444
|
+
continue;
|
|
2445
|
+
}
|
|
2446
|
+
images += slot.multiple ? slot.multiple.max : 1;
|
|
2447
|
+
}
|
|
2448
|
+
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.`);
|
|
2449
|
+
if (!form) return;
|
|
2450
|
+
const properties = form.properties ?? {};
|
|
2451
|
+
const textField = (name2) => {
|
|
2452
|
+
const field2 = Object.hasOwn(properties, name2) ? properties[name2] : null;
|
|
2453
|
+
return field2 && field2.type === "string" && INTRO_TEXT_WIDGETS.has(field2["x-clack-widget"]) && Number.isSafeInteger(field2.maxLength) ? field2 : null;
|
|
2454
|
+
};
|
|
2455
|
+
for (const [key, max] of [["creator_comment", INTRO_CREATOR_COMMENT_MAX], ["description", INTRO_DESCRIPTION_MAX]]) {
|
|
2456
|
+
if (intro[key] === void 0) continue;
|
|
2457
|
+
const field2 = textField(intro[key]);
|
|
2458
|
+
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.");
|
|
2459
|
+
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.`);
|
|
2460
|
+
}
|
|
2461
|
+
const fallback = intro.description_fallback ?? [];
|
|
2462
|
+
let composed = 0;
|
|
2463
|
+
for (const name2 of fallback) {
|
|
2464
|
+
const field2 = textField(name2);
|
|
2465
|
+
if (!field2) {
|
|
2466
|
+
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.");
|
|
2467
|
+
continue;
|
|
2468
|
+
}
|
|
2469
|
+
composed += introFieldLabelMaxLength(name2, field2) + 1 + field2.maxLength;
|
|
2470
|
+
}
|
|
2471
|
+
composed += Math.max(0, fallback.length - 1) * 2;
|
|
2472
|
+
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.`);
|
|
2473
|
+
}
|
|
2474
|
+
function inspectAuthoringSemantics(skill, form, data) {
|
|
2275
2475
|
const errors = [];
|
|
2276
|
-
const
|
|
2476
|
+
const fail2 = (path, message) => errors.push({ path, message });
|
|
2477
|
+
inspectEditorSemantics(skill, data, fail2);
|
|
2277
2478
|
const tools = skill.authoring?.tools ?? [];
|
|
2278
|
-
if (new Set(tools.map((tool) => tool.tool)).size !== tools.length)
|
|
2279
|
-
for (const tool of tools) if (tool.batch_max > tool.max_calls)
|
|
2479
|
+
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.");
|
|
2480
|
+
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.");
|
|
2280
2481
|
const slots = skill.output?.asset_slots ?? [];
|
|
2281
2482
|
const paths = /* @__PURE__ */ new Set();
|
|
2282
2483
|
const fields3 = /* @__PURE__ */ new Set();
|
|
2283
2484
|
for (const slot of slots) {
|
|
2284
|
-
if (fields3.has(slot.field))
|
|
2485
|
+
if (fields3.has(slot.field)) fail2("/output/asset_slots", "\uC2AC\uB86F \uD544\uB4DC\uB294 \uC911\uBCF5\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
2285
2486
|
fields3.add(slot.field);
|
|
2286
|
-
if (slot.multiple && slot.multiple.min > slot.multiple.max)
|
|
2487
|
+
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.");
|
|
2287
2488
|
for (let index = 0; index < (slot.multiple?.max ?? 1); index++) {
|
|
2288
2489
|
const path = slot.multiple ? slot.multiple.path.replace("{n}", String(index)) : slot.path;
|
|
2289
|
-
if (paths.has(path))
|
|
2490
|
+
if (paths.has(path)) fail2("/output/asset_slots", "\uC2AC\uB86F \uD30C\uC77C \uACBD\uB85C\uB294 \uC911\uBCF5\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
2290
2491
|
paths.add(path);
|
|
2291
2492
|
}
|
|
2292
2493
|
}
|
|
2494
|
+
inspectIntroSemantics(skill, form, slots, fail2);
|
|
2293
2495
|
if (!form) return errors;
|
|
2294
2496
|
const properties = form.properties ?? {};
|
|
2295
2497
|
const exists = (expression, path) => {
|
|
2296
2498
|
const match = /^([a-z][a-z0-9_-]*)(?:\[(\d+)\])?$/.exec(expression);
|
|
2297
2499
|
if (!match || !Object.hasOwn(properties, match[1])) {
|
|
2298
|
-
|
|
2500
|
+
fail2(path, "\uC120\uC5B8\uD55C \uD3FC \uD544\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
2299
2501
|
return;
|
|
2300
2502
|
}
|
|
2301
2503
|
if (match[2] !== void 0) {
|
|
2302
2504
|
const slot = slots.find((value2) => value2.field === match[1]);
|
|
2303
|
-
if (!slot?.multiple || Number(match[2]) >= slot.multiple.max)
|
|
2505
|
+
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.");
|
|
2304
2506
|
}
|
|
2305
2507
|
};
|
|
2306
2508
|
const entity = skill.output?.entity_map;
|
|
@@ -2310,30 +2512,539 @@ function inspectAuthoringSemantics(skill, form) {
|
|
|
2310
2512
|
}
|
|
2311
2513
|
for (const slot of slots) {
|
|
2312
2514
|
const node = properties[slot.field];
|
|
2313
|
-
if (!node || node["x-clack-widget"] !== (slot.multiple ? "image_list" : "image"))
|
|
2515
|
+
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.");
|
|
2314
2516
|
}
|
|
2315
2517
|
for (const [name2, node] of Object.entries(properties)) {
|
|
2316
2518
|
if (node["x-clack-widget"] === "content_ref") {
|
|
2317
|
-
if (node.type !== "string" || !node["x-clack-source"])
|
|
2519
|
+
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.");
|
|
2318
2520
|
for (const target of Object.keys(node["x-clack-source"]?.import ?? {})) {
|
|
2319
2521
|
exists(target, `/properties/${name2}/x-clack-source/import`);
|
|
2320
|
-
if (properties[target]?.["x-clack-widget"] === "content_ref")
|
|
2522
|
+
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.");
|
|
2321
2523
|
}
|
|
2322
|
-
} else if (node["x-clack-source"])
|
|
2323
|
-
if (node["x-clack-widget"] === "image_list" && (node.type !== "array" || node.items?.type !== "object" || !node["x-clack-asset"]))
|
|
2524
|
+
} else if (node["x-clack-source"]) fail2(`/properties/${name2}`, "x-clack-source\uB294 content_ref\uC5D0\uB9CC \uC120\uC5B8\uD569\uB2C8\uB2E4.");
|
|
2525
|
+
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.");
|
|
2324
2526
|
}
|
|
2325
2527
|
return errors;
|
|
2326
2528
|
}
|
|
2327
2529
|
|
|
2328
2530
|
// src/lib/skill-package.ts
|
|
2329
|
-
import { createHash as
|
|
2531
|
+
import { createHash as createHash4 } from "crypto";
|
|
2330
2532
|
import { lstat as lstat4, open as open3, readdir } from "fs/promises";
|
|
2331
2533
|
import { constants } from "fs";
|
|
2332
2534
|
import { basename as basename4, extname as extname2, join as join2, resolve as resolve7 } from "path";
|
|
2333
|
-
import { Ajv2020 } from "ajv/dist/2020.js";
|
|
2535
|
+
import { Ajv2020 as Ajv20202 } from "ajv/dist/2020.js";
|
|
2334
2536
|
import { unzipSync as unzipSync2, zipSync } from "fflate";
|
|
2335
2537
|
import { parseDocument } from "yaml";
|
|
2336
2538
|
|
|
2539
|
+
// src/lib/skill-editor.ts
|
|
2540
|
+
import { createHash as createHash3 } from "crypto";
|
|
2541
|
+
import { Ajv2020 } from "ajv/dist/2020.js";
|
|
2542
|
+
|
|
2543
|
+
// src/vendor/clack-api-v4-contracts/output-data-inspect.mjs
|
|
2544
|
+
var bytes = (value2) => Buffer.byteLength(JSON.stringify(value2), "utf8");
|
|
2545
|
+
var fail = (path, message) => ({ path, message });
|
|
2546
|
+
var OUTPUT_DATA_LIMITS = { schema_bytes: 65536, depth: 6, properties: 400, public_bytes: 262144, private_bytes: 65536, ui_state_bytes: 262144 };
|
|
2547
|
+
var SCALAR_TYPES = /* @__PURE__ */ new Set(["string", "integer", "number", "boolean"]);
|
|
2548
|
+
var SCALAR_KEYS = {
|
|
2549
|
+
string: ["minLength", "maxLength", "format"],
|
|
2550
|
+
number: ["minimum", "maximum"],
|
|
2551
|
+
integer: ["minimum", "maximum"],
|
|
2552
|
+
array: ["items", "minItems", "maxItems", "uniqueItems"],
|
|
2553
|
+
object: ["properties", "required", "additionalProperties"],
|
|
2554
|
+
boolean: []
|
|
2555
|
+
};
|
|
2556
|
+
var TYPED_KEYS = new Set(Object.values(SCALAR_KEYS).flat());
|
|
2557
|
+
function inspectOutputData(schema) {
|
|
2558
|
+
const errors = [];
|
|
2559
|
+
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."));
|
|
2560
|
+
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.`));
|
|
2561
|
+
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."));
|
|
2562
|
+
const defs = schema.$defs ?? {};
|
|
2563
|
+
const resolve9 = (node) => typeof node?.$ref === "string" ? defs[node.$ref.slice("#/$defs/".length)] : node;
|
|
2564
|
+
let properties = 0;
|
|
2565
|
+
const suggestRoots = [];
|
|
2566
|
+
const assetPointers = [];
|
|
2567
|
+
walkDataSchema(schema, (node, info) => {
|
|
2568
|
+
const at = info.pointer || "/";
|
|
2569
|
+
if (info.cycle) {
|
|
2570
|
+
errors.push(fail(at, "\uC21C\uD658 \uCC38\uC870\uB294 \uD5C8\uC6A9\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4."));
|
|
2571
|
+
return;
|
|
2572
|
+
}
|
|
2573
|
+
if (info.unresolved) {
|
|
2574
|
+
errors.push(fail(at, "\uB85C\uCEEC $defs \uCC38\uC870\uB9CC \uD5C8\uC6A9\uD569\uB2C8\uB2E4."));
|
|
2575
|
+
return;
|
|
2576
|
+
}
|
|
2577
|
+
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."));
|
|
2578
|
+
if (info.depth === 0) return;
|
|
2579
|
+
if (typeof node.$ref === "string") {
|
|
2580
|
+
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."));
|
|
2581
|
+
return;
|
|
2582
|
+
}
|
|
2583
|
+
if (!info.branch) properties++;
|
|
2584
|
+
if (Array.isArray(node.oneOf)) {
|
|
2585
|
+
const discriminator = node["x-clack-discriminator"];
|
|
2586
|
+
if (!discriminator) errors.push(fail(at, "oneOf\uC5D0\uB294 x-clack-discriminator\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4."));
|
|
2587
|
+
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."));
|
|
2588
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2589
|
+
for (const raw of node.oneOf) {
|
|
2590
|
+
const branch = resolve9(raw);
|
|
2591
|
+
const tag = branch?.properties?.[discriminator];
|
|
2592
|
+
if (branch?.type !== "object" || typeof tag?.const !== "string" || tag.type !== "string" || !(branch.required ?? []).includes(discriminator)) {
|
|
2593
|
+
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."));
|
|
2594
|
+
} else if (seen.has(tag.const)) errors.push(fail(at, `\uD310\uBCC4\uC790 \uAC12 ${tag.const}\uC774 \uC911\uBCF5\uB429\uB2C8\uB2E4.`));
|
|
2595
|
+
else seen.add(tag.const);
|
|
2596
|
+
}
|
|
2597
|
+
return;
|
|
2598
|
+
}
|
|
2599
|
+
if (node["x-clack-discriminator"] !== void 0) errors.push(fail(at, "x-clack-discriminator\uB294 oneOf\uC5D0\uB9CC \uB461\uB2C8\uB2E4."));
|
|
2600
|
+
if (!node.type) {
|
|
2601
|
+
errors.push(fail(at, "type\uC774 \uD544\uC694\uD569\uB2C8\uB2E4."));
|
|
2602
|
+
return;
|
|
2603
|
+
}
|
|
2604
|
+
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.`));
|
|
2605
|
+
const range = (low, high, label) => {
|
|
2606
|
+
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.`));
|
|
2607
|
+
};
|
|
2608
|
+
range("minLength", "maxLength", "\uAE38\uC774");
|
|
2609
|
+
range("minItems", "maxItems", "\uD56D\uBAA9 \uC218");
|
|
2610
|
+
range("minimum", "maximum", "\uAC12");
|
|
2611
|
+
if (node.type === "string" && typeof node.maxLength !== "number") errors.push(fail(at, "\uBB38\uC790\uC5F4 maxLength\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4."));
|
|
2612
|
+
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."));
|
|
2613
|
+
if (node.type === "array" && node.uniqueItems === true) {
|
|
2614
|
+
let items = node.items;
|
|
2615
|
+
for (let hops = 0; typeof items?.$ref === "string" && hops < 64; hops++) items = resolve9(items);
|
|
2616
|
+
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."));
|
|
2617
|
+
}
|
|
2618
|
+
if (node.type === "object") {
|
|
2619
|
+
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."));
|
|
2620
|
+
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.`));
|
|
2621
|
+
}
|
|
2622
|
+
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."));
|
|
2623
|
+
if (node["x-clack-asset"]) {
|
|
2624
|
+
assetPointers.push(at);
|
|
2625
|
+
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."));
|
|
2626
|
+
if (info.visibility === "private") errors.push(fail(at, "\uBE44\uACF5\uAC1C \uD0A4\uC5D0\uB294 \uC790\uC0B0\uC744 \uB458 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."));
|
|
2627
|
+
} else if (node.format === "uuid" && node.type !== "string") errors.push(fail(at, "format\uC740 \uBB38\uC790\uC5F4\uC5D0\uB9CC \uC501\uB2C8\uB2E4."));
|
|
2628
|
+
if (node["x-clack-ai"]?.suggest === true) {
|
|
2629
|
+
if (info.visibility !== "public") errors.push(fail(at, "AI \uC81C\uC548\uC740 \uACF5\uAC1C \uD0A4\uC5D0\uB9CC \uCF24 \uC218 \uC788\uC2B5\uB2C8\uB2E4."));
|
|
2630
|
+
suggestRoots.push(at);
|
|
2631
|
+
}
|
|
2632
|
+
});
|
|
2633
|
+
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."));
|
|
2634
|
+
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."));
|
|
2635
|
+
return errors;
|
|
2636
|
+
}
|
|
2637
|
+
var ZWJ_AFTER = new RegExp("\\p{Extended_Pictographic}", "u");
|
|
2638
|
+
|
|
2639
|
+
// src/vendor/clack-api-v4-contracts/schemas/output-data.v1.schema.json
|
|
2640
|
+
var output_data_v1_schema_default = {
|
|
2641
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
2642
|
+
$id: "https://clack.kr/schemas/platform/output-data.v1.schema.json",
|
|
2643
|
+
$comment: "\uBAA8\uB178\uB808\uD3EC clack-api-v4/contracts/platform/schemas/output-data.v1.schema.json\uC5D0\uC11C \uBCF5\uC0AC\uD55C \uD30C\uC77C\uC774\uB2E4. \uC774 \uD30C\uC77C\uC744 \uC9C1\uC811 \uC218\uC815\uD558\uC9C0 \uB9D0\uACE0 scripts/sync-vendor-types.mjs\uB85C \uAC31\uC2E0\uD55C\uB2E4.",
|
|
2644
|
+
title: "\uC2A4\uD0AC \uC5D0\uB514\uD130 \uB370\uC774\uD130 \uBB38\uC11C \uC2A4\uD0A4\uB9C8 \uADDC\uCE59(output-data.v1)",
|
|
2645
|
+
description: "\uC2A4\uD0AC \uB9E4\uB2C8\uD398\uC2A4\uD2B8 output.data.schema\uAC00 \uAC00\uB9AC\uD0A4\uB294 \uB370\uC774\uD130 \uBB38\uC11C \uC2A4\uD0A4\uB9C8 \uD30C\uC77C\uC774 \uB530\uB77C\uC57C \uD558\uB294 \uBA54\uD0C0 \uADDC\uCE59\uC774\uB2E4. v1\uC740 pattern\uC744 \uD5C8\uC6A9\uD558\uC9C0 \uC54A\uACE0(\uBC31\uD2B8\uB798\uD0B9 \uBE44\uC6A9), uniqueItems\uB294 \uC2A4\uCE7C\uB77C \uBC30\uC5F4\uC5D0\uB9CC \uC4F4\uB2E4. \uAE4A\uC774 6\xB7\uBB38\uC790\uC5F4 maxLength \uD544\uC218\xB7\uBC30\uC5F4 maxItems \uD544\uC218\xB7\uD310\uBCC4\uC790 oneOf\xB7\uB85C\uCEEC $ref\uB9CC\xB7\uC21C\uD658 \uAE08\uC9C0\xB7\uBE44\uACF5\uAC1C\uB294 \uCD5C\uC0C1\uC704 \uD0A4\uB9CC\xB7uniqueItems \uB300\uC0C1\uCC98\uB7FC \uAC19\uC740 \uC2A4\uD0A4\uB9C8\uB85C \uD45C\uD604\uD560 \uC218 \uC5C6\uB294 \uADDC\uCE59\uC740 validators.mjs\uC758 inspectOutputData\uAC00 \uCD94\uAC00\uB85C \uAC80\uC0AC\uD55C\uB2E4.",
|
|
2646
|
+
type: "object",
|
|
2647
|
+
additionalProperties: false,
|
|
2648
|
+
properties: {
|
|
2649
|
+
$schema: {
|
|
2650
|
+
const: "https://json-schema.org/draft/2020-12/schema"
|
|
2651
|
+
},
|
|
2652
|
+
title: {
|
|
2653
|
+
type: "string",
|
|
2654
|
+
maxLength: 120
|
|
2655
|
+
},
|
|
2656
|
+
description: {
|
|
2657
|
+
type: "string",
|
|
2658
|
+
maxLength: 1e3
|
|
2659
|
+
},
|
|
2660
|
+
type: {
|
|
2661
|
+
const: "object"
|
|
2662
|
+
},
|
|
2663
|
+
additionalProperties: {
|
|
2664
|
+
const: false
|
|
2665
|
+
},
|
|
2666
|
+
properties: {
|
|
2667
|
+
type: "object",
|
|
2668
|
+
propertyNames: {
|
|
2669
|
+
$ref: "#/$defs/key"
|
|
2670
|
+
},
|
|
2671
|
+
additionalProperties: {
|
|
2672
|
+
$ref: "#/$defs/rootField"
|
|
2673
|
+
},
|
|
2674
|
+
minProperties: 1,
|
|
2675
|
+
maxProperties: 60
|
|
2676
|
+
},
|
|
2677
|
+
required: {
|
|
2678
|
+
$ref: "#/$defs/keyList"
|
|
2679
|
+
},
|
|
2680
|
+
$defs: {
|
|
2681
|
+
type: "object",
|
|
2682
|
+
propertyNames: {
|
|
2683
|
+
type: "string",
|
|
2684
|
+
pattern: "^[A-Za-z][A-Za-z0-9_-]{0,63}$"
|
|
2685
|
+
},
|
|
2686
|
+
additionalProperties: {
|
|
2687
|
+
$ref: "#/$defs/node"
|
|
2688
|
+
},
|
|
2689
|
+
maxProperties: 64
|
|
2690
|
+
}
|
|
2691
|
+
},
|
|
2692
|
+
required: [
|
|
2693
|
+
"type",
|
|
2694
|
+
"properties",
|
|
2695
|
+
"additionalProperties"
|
|
2696
|
+
],
|
|
2697
|
+
$defs: {
|
|
2698
|
+
key: {
|
|
2699
|
+
type: "string",
|
|
2700
|
+
pattern: "^[a-z][a-z0-9_]{0,31}$"
|
|
2701
|
+
},
|
|
2702
|
+
keyList: {
|
|
2703
|
+
type: "array",
|
|
2704
|
+
items: {
|
|
2705
|
+
$ref: "#/$defs/key"
|
|
2706
|
+
},
|
|
2707
|
+
uniqueItems: true
|
|
2708
|
+
},
|
|
2709
|
+
label: {
|
|
2710
|
+
type: "object",
|
|
2711
|
+
additionalProperties: false,
|
|
2712
|
+
properties: {
|
|
2713
|
+
ko: {
|
|
2714
|
+
type: "string",
|
|
2715
|
+
minLength: 1,
|
|
2716
|
+
maxLength: 200
|
|
2717
|
+
},
|
|
2718
|
+
en: {
|
|
2719
|
+
type: "string",
|
|
2720
|
+
minLength: 1,
|
|
2721
|
+
maxLength: 200
|
|
2722
|
+
}
|
|
2723
|
+
},
|
|
2724
|
+
required: [
|
|
2725
|
+
"ko"
|
|
2726
|
+
]
|
|
2727
|
+
},
|
|
2728
|
+
asset: {
|
|
2729
|
+
type: "object",
|
|
2730
|
+
additionalProperties: false,
|
|
2731
|
+
properties: {
|
|
2732
|
+
accept: {
|
|
2733
|
+
type: "array",
|
|
2734
|
+
items: {
|
|
2735
|
+
type: "string",
|
|
2736
|
+
enum: [
|
|
2737
|
+
"image/png",
|
|
2738
|
+
"image/jpeg",
|
|
2739
|
+
"image/webp"
|
|
2740
|
+
]
|
|
2741
|
+
},
|
|
2742
|
+
minItems: 1,
|
|
2743
|
+
uniqueItems: true
|
|
2744
|
+
},
|
|
2745
|
+
max_bytes: {
|
|
2746
|
+
type: "integer",
|
|
2747
|
+
minimum: 1,
|
|
2748
|
+
maximum: 4194304
|
|
2749
|
+
},
|
|
2750
|
+
aspect: {
|
|
2751
|
+
type: "string",
|
|
2752
|
+
pattern: "^[1-9][0-9]*:[1-9][0-9]*$"
|
|
2753
|
+
},
|
|
2754
|
+
generate: {
|
|
2755
|
+
type: "boolean"
|
|
2756
|
+
}
|
|
2757
|
+
},
|
|
2758
|
+
required: [
|
|
2759
|
+
"accept",
|
|
2760
|
+
"max_bytes"
|
|
2761
|
+
]
|
|
2762
|
+
},
|
|
2763
|
+
ai: {
|
|
2764
|
+
type: "object",
|
|
2765
|
+
additionalProperties: false,
|
|
2766
|
+
properties: {
|
|
2767
|
+
suggest: {
|
|
2768
|
+
type: "boolean"
|
|
2769
|
+
},
|
|
2770
|
+
hint: {
|
|
2771
|
+
type: "string",
|
|
2772
|
+
maxLength: 300
|
|
2773
|
+
}
|
|
2774
|
+
},
|
|
2775
|
+
required: [
|
|
2776
|
+
"suggest"
|
|
2777
|
+
]
|
|
2778
|
+
},
|
|
2779
|
+
scalar: {
|
|
2780
|
+
type: [
|
|
2781
|
+
"string",
|
|
2782
|
+
"number",
|
|
2783
|
+
"boolean"
|
|
2784
|
+
]
|
|
2785
|
+
},
|
|
2786
|
+
nodeKeywords: {
|
|
2787
|
+
type: "object",
|
|
2788
|
+
properties: {
|
|
2789
|
+
type: {
|
|
2790
|
+
type: "string",
|
|
2791
|
+
enum: [
|
|
2792
|
+
"object",
|
|
2793
|
+
"array",
|
|
2794
|
+
"string",
|
|
2795
|
+
"integer",
|
|
2796
|
+
"number",
|
|
2797
|
+
"boolean"
|
|
2798
|
+
]
|
|
2799
|
+
},
|
|
2800
|
+
title: {
|
|
2801
|
+
type: "string",
|
|
2802
|
+
maxLength: 120
|
|
2803
|
+
},
|
|
2804
|
+
description: {
|
|
2805
|
+
type: "string",
|
|
2806
|
+
maxLength: 1e3
|
|
2807
|
+
},
|
|
2808
|
+
"x-clack-i18n": {
|
|
2809
|
+
$ref: "#/$defs/label"
|
|
2810
|
+
},
|
|
2811
|
+
enum: {
|
|
2812
|
+
type: "array",
|
|
2813
|
+
items: {
|
|
2814
|
+
$ref: "#/$defs/scalar"
|
|
2815
|
+
},
|
|
2816
|
+
minItems: 1,
|
|
2817
|
+
maxItems: 100,
|
|
2818
|
+
uniqueItems: true
|
|
2819
|
+
},
|
|
2820
|
+
const: {
|
|
2821
|
+
$ref: "#/$defs/scalar"
|
|
2822
|
+
},
|
|
2823
|
+
minLength: {
|
|
2824
|
+
type: "integer",
|
|
2825
|
+
minimum: 0
|
|
2826
|
+
},
|
|
2827
|
+
maxLength: {
|
|
2828
|
+
type: "integer",
|
|
2829
|
+
minimum: 1,
|
|
2830
|
+
maximum: 1e5
|
|
2831
|
+
},
|
|
2832
|
+
format: {
|
|
2833
|
+
const: "uuid"
|
|
2834
|
+
},
|
|
2835
|
+
minimum: {
|
|
2836
|
+
type: "number"
|
|
2837
|
+
},
|
|
2838
|
+
maximum: {
|
|
2839
|
+
type: "number"
|
|
2840
|
+
},
|
|
2841
|
+
minItems: {
|
|
2842
|
+
type: "integer",
|
|
2843
|
+
minimum: 0
|
|
2844
|
+
},
|
|
2845
|
+
maxItems: {
|
|
2846
|
+
type: "integer",
|
|
2847
|
+
minimum: 1,
|
|
2848
|
+
maximum: 1e3
|
|
2849
|
+
},
|
|
2850
|
+
uniqueItems: {
|
|
2851
|
+
type: "boolean",
|
|
2852
|
+
description: "\uC2A4\uCE7C\uB77C(string\xB7integer\xB7number\xB7boolean) items \uBC30\uC5F4\uC5D0\uB9CC \uC4F4\uB2E4(validators.mjs \uAC80\uC0AC)."
|
|
2853
|
+
},
|
|
2854
|
+
items: {
|
|
2855
|
+
$ref: "#/$defs/node"
|
|
2856
|
+
},
|
|
2857
|
+
properties: {
|
|
2858
|
+
type: "object",
|
|
2859
|
+
propertyNames: {
|
|
2860
|
+
$ref: "#/$defs/key"
|
|
2861
|
+
},
|
|
2862
|
+
additionalProperties: {
|
|
2863
|
+
$ref: "#/$defs/node"
|
|
2864
|
+
},
|
|
2865
|
+
maxProperties: 60
|
|
2866
|
+
},
|
|
2867
|
+
required: {
|
|
2868
|
+
$ref: "#/$defs/keyList"
|
|
2869
|
+
},
|
|
2870
|
+
additionalProperties: {
|
|
2871
|
+
const: false
|
|
2872
|
+
},
|
|
2873
|
+
$ref: {
|
|
2874
|
+
type: "string",
|
|
2875
|
+
pattern: "^#/\\$defs/[A-Za-z][A-Za-z0-9_-]{0,63}$"
|
|
2876
|
+
},
|
|
2877
|
+
oneOf: {
|
|
2878
|
+
type: "array",
|
|
2879
|
+
items: {
|
|
2880
|
+
$ref: "#/$defs/node"
|
|
2881
|
+
},
|
|
2882
|
+
minItems: 2,
|
|
2883
|
+
maxItems: 16
|
|
2884
|
+
},
|
|
2885
|
+
"x-clack-discriminator": {
|
|
2886
|
+
$ref: "#/$defs/key"
|
|
2887
|
+
},
|
|
2888
|
+
"x-clack-asset": {
|
|
2889
|
+
$ref: "#/$defs/asset"
|
|
2890
|
+
},
|
|
2891
|
+
"x-clack-ai": {
|
|
2892
|
+
$ref: "#/$defs/ai"
|
|
2893
|
+
}
|
|
2894
|
+
}
|
|
2895
|
+
},
|
|
2896
|
+
node: {
|
|
2897
|
+
allOf: [
|
|
2898
|
+
{
|
|
2899
|
+
$ref: "#/$defs/nodeKeywords"
|
|
2900
|
+
}
|
|
2901
|
+
],
|
|
2902
|
+
unevaluatedProperties: false
|
|
2903
|
+
},
|
|
2904
|
+
rootField: {
|
|
2905
|
+
allOf: [
|
|
2906
|
+
{
|
|
2907
|
+
$ref: "#/$defs/nodeKeywords"
|
|
2908
|
+
}
|
|
2909
|
+
],
|
|
2910
|
+
properties: {
|
|
2911
|
+
"x-clack-visibility": {
|
|
2912
|
+
type: "string",
|
|
2913
|
+
enum: [
|
|
2914
|
+
"public",
|
|
2915
|
+
"private"
|
|
2916
|
+
]
|
|
2917
|
+
}
|
|
2918
|
+
},
|
|
2919
|
+
required: [
|
|
2920
|
+
"x-clack-visibility"
|
|
2921
|
+
],
|
|
2922
|
+
unevaluatedProperties: false
|
|
2923
|
+
}
|
|
2924
|
+
}
|
|
2925
|
+
};
|
|
2926
|
+
|
|
2927
|
+
// src/lib/skill-editor.ts
|
|
2928
|
+
var EDITOR_BUNDLE_LIMITS = { maxFiles: 50, maxFileBytes: 512 * 1024, maxTotalBytes: 2 * 1024 * 1024, maxCodeBytes: 512 * 1024 };
|
|
2929
|
+
var DATA_SCHEMA_MAX_BYTES = 64 * 1024;
|
|
2930
|
+
var ALLOWED = /\.(?:html?|js|mjs|css|json|png|jpe?g|webp|gif|woff2?)$/i;
|
|
2931
|
+
var CODE = /\.(?:html?|js|mjs|css)$/i;
|
|
2932
|
+
var RESERVED = /^(?:runtime|form|examples|output|data)(?:\/|$)/;
|
|
2933
|
+
var textDecoder = new TextDecoder("utf-8", { fatal: true });
|
|
2934
|
+
var validateMeta = new Ajv2020({ allErrors: true, strict: false }).compile(output_data_v1_schema_default);
|
|
2935
|
+
var invalid = (message) => {
|
|
2936
|
+
throw new CliError(message, "SKILL_EDITOR_INVALID");
|
|
2937
|
+
};
|
|
2938
|
+
var isObject2 = (value2) => value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
|
|
2939
|
+
var sha = (value2) => createHash3("sha256").update(value2).digest("hex");
|
|
2940
|
+
function selectEditorBundle(files, bundle) {
|
|
2941
|
+
const single = Object.hasOwn(files, bundle);
|
|
2942
|
+
const prefix = `${bundle}/`;
|
|
2943
|
+
const selected = single ? [[bundle, files[bundle]]] : Object.entries(files).filter(([path]) => path.startsWith(prefix));
|
|
2944
|
+
const list2 = selected.map(([path, data]) => ({ path: single ? path.split("/").at(-1) : path.slice(prefix.length), bytes: data.length, sha256: sha(data) })).sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
2945
|
+
const computed = single ? list2[0]?.sha256 ?? "" : sha(JSON.stringify(list2));
|
|
2946
|
+
return { single, selected, list: list2, computed };
|
|
2947
|
+
}
|
|
2948
|
+
function computeEditorHash(manifest, files) {
|
|
2949
|
+
const editor = manifest.authoring?.editor;
|
|
2950
|
+
if (!isObject2(editor) || typeof editor.bundle !== "string") return null;
|
|
2951
|
+
const { list: list2, computed } = selectEditorBundle(files, editor.bundle.replace(/\/+$/, ""));
|
|
2952
|
+
return list2.length ? computed : null;
|
|
2953
|
+
}
|
|
2954
|
+
function parseJson(bytes2, label) {
|
|
2955
|
+
try {
|
|
2956
|
+
const value2 = JSON.parse(textDecoder.decode(bytes2));
|
|
2957
|
+
if (isObject2(value2)) return value2;
|
|
2958
|
+
} catch {
|
|
2959
|
+
}
|
|
2960
|
+
return invalid(`${label}\uC740 UTF-8 JSON \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4.`);
|
|
2961
|
+
}
|
|
2962
|
+
function scanBundle(list2, warnings) {
|
|
2963
|
+
for (const [path, data] of list2) {
|
|
2964
|
+
if (!/\.(?:html?|js|mjs|css)$/i.test(path)) continue;
|
|
2965
|
+
let text;
|
|
2966
|
+
try {
|
|
2967
|
+
text = textDecoder.decode(data);
|
|
2968
|
+
} catch {
|
|
2969
|
+
invalid(`${path}\uB294 UTF-8 \uD14D\uC2A4\uD2B8\uC5EC\uC57C \uD569\uB2C8\uB2E4.`);
|
|
2970
|
+
return;
|
|
2971
|
+
}
|
|
2972
|
+
if (/loading\s*=\s*["']?lazy/i.test(text)) warnings.push(`EDITOR_LAZY_LOAD: ${path}\uC5D0 loading="lazy"\uAC00 \uC788\uC2B5\uB2C8\uB2E4. \uC5D0\uB514\uD130\uB294 \uC2DC\uC791\uD560 \uB54C \uBAA8\uB4E0 \uC790\uC6D0\uC744 \uBD88\uB7EC\uC640\uC57C \uD569\uB2C8\uB2E4.`);
|
|
2973
|
+
if (/\bimport\s*\(/.test(text)) warnings.push(`EDITOR_LAZY_LOAD: ${path}\uC5D0 \uB3D9\uC801 import()\uAC00 \uC788\uC2B5\uB2C8\uB2E4. \uC815\uC801 import\uB9CC \uD5C8\uC6A9\uB429\uB2C8\uB2E4.`);
|
|
2974
|
+
if (/\.css$/i.test(path) && /url\(\s*(?!["']?data:)/i.test(text)) {
|
|
2975
|
+
warnings.push(`EDITOR_CSS_URL_NOT_INLINE: ${path}\uC758 CSS url()\uC740 data:\uB9CC \uD5C8\uC6A9\uB429\uB2C8\uB2E4. \uAE00\uAF34\xB7\uBC30\uACBD \uC774\uBBF8\uC9C0\uB294 \uC778\uB77C\uC778\uD558\uC138\uC694.`);
|
|
2976
|
+
}
|
|
2977
|
+
}
|
|
2978
|
+
}
|
|
2979
|
+
function inspectEditorSkill(manifest, files, options2) {
|
|
2980
|
+
const authoring = manifest.authoring;
|
|
2981
|
+
const output2 = manifest.output;
|
|
2982
|
+
const editor = authoring.editor;
|
|
2983
|
+
const warnings = [];
|
|
2984
|
+
const bundle = String(editor.bundle).replace(/\/+$/, "");
|
|
2985
|
+
if (RESERVED.test(bundle)) invalid("editor \uBC88\uB4E4 \uACBD\uB85C\uAC00 \uC608\uC57D \uACBD\uB85C(runtime\xB7form\xB7examples\xB7output\xB7data)\uC785\uB2C8\uB2E4.");
|
|
2986
|
+
const { single, selected, list: list2, computed } = selectEditorBundle(files, bundle);
|
|
2987
|
+
if (!list2.length) invalid(`editor \uBC88\uB4E4(${bundle})\uC5D0 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.`);
|
|
2988
|
+
const entry = single ? bundle : `${bundle}/index.html`;
|
|
2989
|
+
if (single ? !/\.html?$/i.test(bundle) : !list2.some((file) => file.path === "index.html")) invalid(`editor \uC9C4\uC785\uC810(${entry})\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.`);
|
|
2990
|
+
if (list2.length > EDITOR_BUNDLE_LIMITS.maxFiles) invalid(`editor \uBC88\uB4E4 \uD30C\uC77C \uC218\uAC00 ${EDITOR_BUNDLE_LIMITS.maxFiles}\uAC1C\uB97C \uB118\uC2B5\uB2C8\uB2E4.`);
|
|
2991
|
+
const total = list2.reduce((sum, file) => sum + file.bytes, 0);
|
|
2992
|
+
if (total > EDITOR_BUNDLE_LIMITS.maxTotalBytes) invalid("editor \uBC88\uB4E4 \uC804\uCCB4 \uD06C\uAE30\uAC00 2 MiB\uB97C \uB118\uC2B5\uB2C8\uB2E4.");
|
|
2993
|
+
let code = 0;
|
|
2994
|
+
for (const [path, data2] of selected) {
|
|
2995
|
+
if (!ALLOWED.test(path)) invalid(`editor \uBC88\uB4E4\uC5D0 \uD5C8\uC6A9\uB418\uC9C0 \uC54A\uB294 \uD30C\uC77C \uD615\uC2DD\uC785\uB2C8\uB2E4: ${path}`);
|
|
2996
|
+
if (data2.length > EDITOR_BUNDLE_LIMITS.maxFileBytes) invalid(`editor \uBC88\uB4E4 \uD30C\uC77C\uC774 512 KiB\uB97C \uB118\uC2B5\uB2C8\uB2E4: ${path}`);
|
|
2997
|
+
if (CODE.test(path)) code += data2.length;
|
|
2998
|
+
}
|
|
2999
|
+
if (code > EDITOR_BUNDLE_LIMITS.maxCodeBytes) invalid("editor \uBC88\uB4E4 \uCF54\uB4DC(HTML\xB7JS\xB7CSS) \uD569\uACC4\uAC00 512 KiB\uB97C \uB118\uC2B5\uB2C8\uB2E4.");
|
|
3000
|
+
scanBundle(selected, warnings);
|
|
3001
|
+
const declared = String(editor.sha256);
|
|
3002
|
+
const match = declared === computed;
|
|
3003
|
+
if (!match) {
|
|
3004
|
+
const message = `authoring.editor.sha256\uC774 \uBC88\uB4E4 \uD574\uC2DC\uC640 \uB2E4\uB985\uB2C8\uB2E4(\uACC4\uC0B0\uAC12 ${computed}). \`clack skill pack\`\uC774 \uAC12\uC744 \uCC44\uC6C1\uB2C8\uB2E4.`;
|
|
3005
|
+
if (options2.hashMismatch === "error") invalid(message);
|
|
3006
|
+
warnings.push(`PLUGIN_BUNDLE_HASH_MISMATCH: ${message}`);
|
|
3007
|
+
}
|
|
3008
|
+
const data = output2.data;
|
|
3009
|
+
const schemaPath = data?.schema;
|
|
3010
|
+
if (typeof schemaPath !== "string" || !files[schemaPath]) return invalid("output.data.schema \uD30C\uC77C\uC774 \uD328\uD0A4\uC9C0\uC5D0 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
3011
|
+
if (files[schemaPath].length > DATA_SCHEMA_MAX_BYTES) invalid("\uB370\uC774\uD130 \uC2A4\uD0A4\uB9C8 \uD30C\uC77C\uC740 64 KiB \uC774\uD558\uC5EC\uC57C \uD569\uB2C8\uB2E4.");
|
|
3012
|
+
const schema = parseJson(files[schemaPath], "\uB370\uC774\uD130 \uC2A4\uD0A4\uB9C8");
|
|
3013
|
+
if (!validateMeta(schema)) {
|
|
3014
|
+
invalid(`\uB370\uC774\uD130 \uC2A4\uD0A4\uB9C8\uAC00 output-data.v1\uACFC \uC77C\uCE58\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4: ${(validateMeta.errors ?? []).slice(0, 5).map((error2) => `${error2.instancePath || "/"} ${error2.message ?? ""}`).join(" / ")}`);
|
|
3015
|
+
}
|
|
3016
|
+
const dataErrors = [...inspectOutputData(schema), ...inspectAuthoringSemantics(manifest, void 0, schema)];
|
|
3017
|
+
if (dataErrors.length) invalid(dataErrors.map((error2) => `${error2.path}: ${error2.message}`).join(" / "));
|
|
3018
|
+
const template = output2.template;
|
|
3019
|
+
if (!template || template.root !== "runtime" || typeof template.entry !== "string") invalid("runtime \uD15C\uD50C\uB9BF \uACBD\uB85C\uB97C \uD655\uC778\uD558\uC138\uC694.");
|
|
3020
|
+
if (Object.keys(files).some((path) => path.startsWith("runtime/data/"))) invalid("runtime/data/ \uACBD\uB85C\uB294 \uC5D0\uB514\uD130 \uC2A4\uD0AC\uC5D0\uC11C \uC608\uC57D\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4(data/document.json\uACFC \uCDA9\uB3CC).");
|
|
3021
|
+
const examples = Object.keys(files).filter((path) => /^examples\/[^/]+\.json$/.test(path));
|
|
3022
|
+
if (!examples.length || examples.length > 10) invalid("\uD15C\uD50C\uB9BF \uC608\uC2DC\uB294 1~10\uAC1C \uD544\uC694\uD569\uB2C8\uB2E4.");
|
|
3023
|
+
let validateExample;
|
|
3024
|
+
try {
|
|
3025
|
+
const ajv2 = new Ajv2020({ allErrors: true, strict: false });
|
|
3026
|
+
ajv2.addFormat("uuid", /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i);
|
|
3027
|
+
validateExample = ajv2.compile(schema);
|
|
3028
|
+
} catch {
|
|
3029
|
+
return invalid("\uB370\uC774\uD130 \uC2A4\uD0A4\uB9C8\uB97C \uCEF4\uD30C\uC77C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
3030
|
+
}
|
|
3031
|
+
for (const path of examples) {
|
|
3032
|
+
if (files[path].length > 256 * 1024) invalid(`\uC608\uC2DC\uAC00 \uB108\uBB34 \uD07D\uB2C8\uB2E4: ${path}`);
|
|
3033
|
+
if (!validateExample(parseJson(files[path], "\uC608\uC2DC"))) invalid(`\uB370\uC774\uD130 \uC2A4\uD0A4\uB9C8\uC640 \uC608\uC2DC\uAC00 \uC77C\uCE58\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4: ${path}`);
|
|
3034
|
+
}
|
|
3035
|
+
return { computedHash: computed, warnings, summary: {
|
|
3036
|
+
bundle,
|
|
3037
|
+
entry,
|
|
3038
|
+
file_count: list2.length,
|
|
3039
|
+
total_bytes: total,
|
|
3040
|
+
declared_sha256: declared,
|
|
3041
|
+
computed_sha256: computed,
|
|
3042
|
+
hash_match: match,
|
|
3043
|
+
permissions: editor.permissions,
|
|
3044
|
+
center_only: true
|
|
3045
|
+
} };
|
|
3046
|
+
}
|
|
3047
|
+
|
|
2337
3048
|
// src/vendor/clack-api-v4-contracts/schemas/clack-skill.v1.schema.json
|
|
2338
3049
|
var clack_skill_v1_schema_default = {
|
|
2339
3050
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
@@ -2435,13 +3146,74 @@ var clack_skill_v1_schema_default = {
|
|
|
2435
3146
|
},
|
|
2436
3147
|
maxItems: 10,
|
|
2437
3148
|
uniqueItems: true
|
|
3149
|
+
},
|
|
3150
|
+
description: {
|
|
3151
|
+
type: "object",
|
|
3152
|
+
properties: {
|
|
3153
|
+
ko: {
|
|
3154
|
+
type: "string",
|
|
3155
|
+
minLength: 1,
|
|
3156
|
+
maxLength: 1e3
|
|
3157
|
+
},
|
|
3158
|
+
en: {
|
|
3159
|
+
type: "string",
|
|
3160
|
+
minLength: 1,
|
|
3161
|
+
maxLength: 1e3
|
|
3162
|
+
}
|
|
3163
|
+
},
|
|
3164
|
+
required: [
|
|
3165
|
+
"ko"
|
|
3166
|
+
],
|
|
3167
|
+
additionalProperties: false
|
|
3168
|
+
},
|
|
3169
|
+
tags_i18n: {
|
|
3170
|
+
type: "object",
|
|
3171
|
+
properties: {
|
|
3172
|
+
en: {
|
|
3173
|
+
type: "array",
|
|
3174
|
+
items: {
|
|
3175
|
+
type: "string",
|
|
3176
|
+
minLength: 1,
|
|
3177
|
+
maxLength: 40
|
|
3178
|
+
},
|
|
3179
|
+
minItems: 1,
|
|
3180
|
+
maxItems: 10,
|
|
3181
|
+
uniqueItems: true
|
|
3182
|
+
}
|
|
3183
|
+
},
|
|
3184
|
+
minProperties: 1,
|
|
3185
|
+
additionalProperties: false
|
|
3186
|
+
},
|
|
3187
|
+
release_notes: {
|
|
3188
|
+
type: "object",
|
|
3189
|
+
properties: {
|
|
3190
|
+
ko: {
|
|
3191
|
+
type: "string",
|
|
3192
|
+
minLength: 1,
|
|
3193
|
+
maxLength: 1e3
|
|
3194
|
+
},
|
|
3195
|
+
en: {
|
|
3196
|
+
type: "string",
|
|
3197
|
+
minLength: 1,
|
|
3198
|
+
maxLength: 1e3
|
|
3199
|
+
}
|
|
3200
|
+
},
|
|
3201
|
+
required: [
|
|
3202
|
+
"ko"
|
|
3203
|
+
],
|
|
3204
|
+
additionalProperties: false
|
|
2438
3205
|
}
|
|
2439
3206
|
},
|
|
2440
3207
|
required: [
|
|
2441
3208
|
"title",
|
|
2442
3209
|
"summary",
|
|
2443
3210
|
"category"
|
|
2444
|
-
]
|
|
3211
|
+
],
|
|
3212
|
+
dependentRequired: {
|
|
3213
|
+
tags_i18n: [
|
|
3214
|
+
"tags"
|
|
3215
|
+
]
|
|
3216
|
+
}
|
|
2445
3217
|
},
|
|
2446
3218
|
authoring: {
|
|
2447
3219
|
type: "object",
|
|
@@ -2624,6 +3396,64 @@ var clack_skill_v1_schema_default = {
|
|
|
2624
3396
|
"permissions",
|
|
2625
3397
|
"purpose"
|
|
2626
3398
|
]
|
|
3399
|
+
},
|
|
3400
|
+
editor: {
|
|
3401
|
+
type: "object",
|
|
3402
|
+
additionalProperties: false,
|
|
3403
|
+
description: "\uC2A4\uD0AC \uC5D0\uB514\uD130(\uC804\uCCB4 \uD654\uBA74 \uC81C\uC791 UI) \uC120\uC5B8. v1\uC740 template \uC2A4\uD0AC \uC804\uC6A9\uC774\uBA70 \uC81C\uC791 \uD3FC\xB7\uD50C\uB7EC\uADF8\uC778 \uD328\uB110\uACFC \uD568\uAED8 \uC4F8 \uC218 \uC5C6\uB2E4. \uBC88\uB4E4 \uADDC\uCE59(\uB514\uB809\uD130\uB9AC\uBA74 {bundle}/index.html \uC9C4\uC785, sha256 \uACC4\uC0B0\uBC95)\uC740 authoring.plugin\uACFC \uAC19\uB2E4.",
|
|
3404
|
+
properties: {
|
|
3405
|
+
bundle: {
|
|
3406
|
+
type: "string",
|
|
3407
|
+
pattern: "^(?!/)(?!.*\\.\\.)[A-Za-z0-9_./-]+$"
|
|
3408
|
+
},
|
|
3409
|
+
sha256: {
|
|
3410
|
+
type: "string",
|
|
3411
|
+
pattern: "^[a-f0-9]{64}$"
|
|
3412
|
+
},
|
|
3413
|
+
permissions: {
|
|
3414
|
+
type: "array",
|
|
3415
|
+
items: {
|
|
3416
|
+
type: "string",
|
|
3417
|
+
enum: [
|
|
3418
|
+
"document.read",
|
|
3419
|
+
"document.write",
|
|
3420
|
+
"assets.read",
|
|
3421
|
+
"assets.pick",
|
|
3422
|
+
"ai.suggest",
|
|
3423
|
+
"tools.image.generate",
|
|
3424
|
+
"preview",
|
|
3425
|
+
"package"
|
|
3426
|
+
]
|
|
3427
|
+
},
|
|
3428
|
+
minItems: 2,
|
|
3429
|
+
uniqueItems: true
|
|
3430
|
+
},
|
|
3431
|
+
purpose: {
|
|
3432
|
+
type: "object",
|
|
3433
|
+
additionalProperties: false,
|
|
3434
|
+
properties: {
|
|
3435
|
+
ko: {
|
|
3436
|
+
type: "string",
|
|
3437
|
+
minLength: 1,
|
|
3438
|
+
maxLength: 200
|
|
3439
|
+
},
|
|
3440
|
+
en: {
|
|
3441
|
+
type: "string",
|
|
3442
|
+
minLength: 1,
|
|
3443
|
+
maxLength: 200
|
|
3444
|
+
}
|
|
3445
|
+
},
|
|
3446
|
+
required: [
|
|
3447
|
+
"ko"
|
|
3448
|
+
]
|
|
3449
|
+
}
|
|
3450
|
+
},
|
|
3451
|
+
required: [
|
|
3452
|
+
"bundle",
|
|
3453
|
+
"sha256",
|
|
3454
|
+
"permissions",
|
|
3455
|
+
"purpose"
|
|
3456
|
+
]
|
|
2627
3457
|
}
|
|
2628
3458
|
},
|
|
2629
3459
|
required: [
|
|
@@ -2885,21 +3715,105 @@ var clack_skill_v1_schema_default = {
|
|
|
2885
3715
|
"fixed",
|
|
2886
3716
|
"scroll_hide",
|
|
2887
3717
|
"translucent_scroll_hide",
|
|
2888
|
-
"floating_close"
|
|
3718
|
+
"floating_close",
|
|
3719
|
+
"floating_close_safe_area"
|
|
2889
3720
|
]
|
|
2890
3721
|
},
|
|
2891
|
-
color_scheme: {
|
|
2892
|
-
enum: [
|
|
2893
|
-
"light",
|
|
2894
|
-
"dark"
|
|
2895
|
-
]
|
|
3722
|
+
color_scheme: {
|
|
3723
|
+
enum: [
|
|
3724
|
+
"light",
|
|
3725
|
+
"dark"
|
|
3726
|
+
]
|
|
3727
|
+
}
|
|
3728
|
+
},
|
|
3729
|
+
required: [
|
|
3730
|
+
"schema_version",
|
|
3731
|
+
"header_mode",
|
|
3732
|
+
"color_scheme"
|
|
3733
|
+
]
|
|
3734
|
+
},
|
|
3735
|
+
data: {
|
|
3736
|
+
type: "object",
|
|
3737
|
+
additionalProperties: false,
|
|
3738
|
+
description: "\uC2A4\uD0AC \uC5D0\uB514\uD130\uAC00 \uB9CC\uB4DC\uB294 \uB370\uC774\uD130 \uBB38\uC11C \uC120\uC5B8. schema \uD30C\uC77C\uC740 output-data.v1 \uADDC\uCE59\uC744 \uB530\uB978\uB2E4. \uBC88\uB4E4\uC5D0\uB294 \uACF5\uAC1C \uBD80\uBD84\uC774 data/document.json, \uBE44\uACF5\uAC1C \uCD5C\uC0C1\uC704 \uD0A4\uAC00 .clack/config.private.json, \uC790\uC0B0\uC774 data/assets/\uB85C \uB4E4\uC5B4\uAC04\uB2E4.",
|
|
3739
|
+
properties: {
|
|
3740
|
+
schema: {
|
|
3741
|
+
type: "string",
|
|
3742
|
+
pattern: "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$))[A-Za-z0-9_./-]+$"
|
|
3743
|
+
},
|
|
3744
|
+
max_bytes: {
|
|
3745
|
+
type: "integer",
|
|
3746
|
+
minimum: 1024,
|
|
3747
|
+
maximum: 262144
|
|
3748
|
+
},
|
|
3749
|
+
private_max_bytes: {
|
|
3750
|
+
type: "integer",
|
|
3751
|
+
minimum: 1,
|
|
3752
|
+
maximum: 65536
|
|
3753
|
+
},
|
|
3754
|
+
assets: {
|
|
3755
|
+
type: "object",
|
|
3756
|
+
additionalProperties: false,
|
|
3757
|
+
properties: {
|
|
3758
|
+
max_count: {
|
|
3759
|
+
type: "integer",
|
|
3760
|
+
minimum: 1,
|
|
3761
|
+
maximum: 48
|
|
3762
|
+
},
|
|
3763
|
+
max_total_bytes: {
|
|
3764
|
+
type: "integer",
|
|
3765
|
+
minimum: 1,
|
|
3766
|
+
maximum: 25165824
|
|
3767
|
+
}
|
|
3768
|
+
},
|
|
3769
|
+
required: [
|
|
3770
|
+
"max_count",
|
|
3771
|
+
"max_total_bytes"
|
|
3772
|
+
]
|
|
3773
|
+
}
|
|
3774
|
+
},
|
|
3775
|
+
required: [
|
|
3776
|
+
"schema"
|
|
3777
|
+
]
|
|
3778
|
+
},
|
|
3779
|
+
intro: {
|
|
3780
|
+
type: "object",
|
|
3781
|
+
additionalProperties: false,
|
|
3782
|
+
description: "\uCF58\uD150\uCE20 \uC778\uD2B8\uB85C(\uC2E4\uD589 \uC804 \uAE30\uBCF8 \uC815\uBCF4 \uD654\uBA74) \uCD9C\uB825 \uC120\uC5B8. \uBAA8\uB4E0 \uD0A4\uB294 \uC120\uD0DD\uC774\uB2E4. creator_comment\xB7description\uC740 \uACF5\uAC1C \uBB38\uC790\uC5F4 \uD3FC \uD544\uB4DC(\uCD5C\uB300 \uAE38\uC774 500\xB75000 \uC774\uD558), description_fallback\uC740 description \uAC12\uC774 \uBE44\uC5C8\uC744 \uB54C \uC21C\uC11C\uB300\uB85C \uAC12\uC774 \uC788\uB294 \uD544\uB4DC\uB9CC '{\uD3FC \uD544\uB4DC title}\\n{\uAC12}'\uC744 \uBE48 \uC904\uB85C \uC774\uC5B4 \uC0C1\uC138 \uC124\uBA85\uC744 \uC790\uB3D9 \uAD6C\uC131\uD560 \uBB38\uC790\uC5F4 \uD3FC \uD544\uB4DC\uB2E4(\uBE44\uACF5\uAC1C \uD544\uB4DC\uB97C \uACF5\uAC1C\uD558\uB294 \uC720\uC77C\uD55C \uBA85\uC2DC \uC608\uC678, \uCD5C\uB300 \uAE38\uC774 \uD569\uACFC \uB77C\uBCA8\uC774 5000\uC790 \uC774\uD558). images\uB294 output.asset_slots\uC758 field\uC774\uBA70 \uC2AC\uB86F \uC21C\uC11C\uB300\uB85C \uD3BC\uCE5C \uC81C\uC791 \uC790\uC0B0\uC744 \uC4F4\uB2E4(\uB2E8\uC77C 1\uC7A5\xB7multiple\uC740 max\uC7A5\uC73C\uB85C \uC138\uC5B4 \uD569\uACC4 8 \uC774\uD558). \uD3FC \uC2A4\uD0AC \uC804\uC6A9\uC774\uBA70 \uC5D0\uB514\uD130 \uC2A4\uD0AC\xB7\uC9C0\uCE68\uD615(instruction) \uC2A4\uD0AC\uC740 \uC4F8 \uC218 \uC5C6\uB2E4.",
|
|
3783
|
+
properties: {
|
|
3784
|
+
default_enabled: {
|
|
3785
|
+
type: "boolean",
|
|
3786
|
+
default: false
|
|
3787
|
+
},
|
|
3788
|
+
creator_comment: {
|
|
3789
|
+
type: "string",
|
|
3790
|
+
pattern: "^[a-z][a-z0-9_-]{0,31}$"
|
|
3791
|
+
},
|
|
3792
|
+
description: {
|
|
3793
|
+
type: "string",
|
|
3794
|
+
pattern: "^[a-z][a-z0-9_-]{0,31}$"
|
|
3795
|
+
},
|
|
3796
|
+
description_fallback: {
|
|
3797
|
+
type: "array",
|
|
3798
|
+
items: {
|
|
3799
|
+
type: "string",
|
|
3800
|
+
pattern: "^[a-z][a-z0-9_-]{0,31}$"
|
|
3801
|
+
},
|
|
3802
|
+
minItems: 1,
|
|
3803
|
+
maxItems: 20,
|
|
3804
|
+
uniqueItems: true
|
|
3805
|
+
},
|
|
3806
|
+
images: {
|
|
3807
|
+
type: "array",
|
|
3808
|
+
items: {
|
|
3809
|
+
type: "string",
|
|
3810
|
+
pattern: "^[a-z][a-z0-9_-]{0,31}$"
|
|
3811
|
+
},
|
|
3812
|
+
minItems: 1,
|
|
3813
|
+
maxItems: 8,
|
|
3814
|
+
uniqueItems: true
|
|
2896
3815
|
}
|
|
2897
|
-
}
|
|
2898
|
-
required: [
|
|
2899
|
-
"schema_version",
|
|
2900
|
-
"header_mode",
|
|
2901
|
-
"color_scheme"
|
|
2902
|
-
]
|
|
3816
|
+
}
|
|
2903
3817
|
}
|
|
2904
3818
|
},
|
|
2905
3819
|
required: [
|
|
@@ -3042,8 +3956,27 @@ var clack_skill_v1_schema_default = {
|
|
|
3042
3956
|
],
|
|
3043
3957
|
properties: {
|
|
3044
3958
|
authoring: {
|
|
3045
|
-
|
|
3046
|
-
|
|
3959
|
+
oneOf: [
|
|
3960
|
+
{
|
|
3961
|
+
required: [
|
|
3962
|
+
"form"
|
|
3963
|
+
],
|
|
3964
|
+
not: {
|
|
3965
|
+
required: [
|
|
3966
|
+
"editor"
|
|
3967
|
+
]
|
|
3968
|
+
}
|
|
3969
|
+
},
|
|
3970
|
+
{
|
|
3971
|
+
required: [
|
|
3972
|
+
"editor"
|
|
3973
|
+
],
|
|
3974
|
+
not: {
|
|
3975
|
+
required: [
|
|
3976
|
+
"form"
|
|
3977
|
+
]
|
|
3978
|
+
}
|
|
3979
|
+
}
|
|
3047
3980
|
],
|
|
3048
3981
|
properties: {
|
|
3049
3982
|
modes: {
|
|
@@ -3094,6 +4027,136 @@ var clack_skill_v1_schema_default = {
|
|
|
3094
4027
|
"runtime"
|
|
3095
4028
|
]
|
|
3096
4029
|
}
|
|
4030
|
+
},
|
|
4031
|
+
{
|
|
4032
|
+
if: {
|
|
4033
|
+
properties: {
|
|
4034
|
+
authoring: {
|
|
4035
|
+
required: [
|
|
4036
|
+
"editor"
|
|
4037
|
+
]
|
|
4038
|
+
}
|
|
4039
|
+
},
|
|
4040
|
+
required: [
|
|
4041
|
+
"authoring"
|
|
4042
|
+
]
|
|
4043
|
+
},
|
|
4044
|
+
then: {
|
|
4045
|
+
properties: {
|
|
4046
|
+
type: {
|
|
4047
|
+
const: "template"
|
|
4048
|
+
},
|
|
4049
|
+
authoring: {
|
|
4050
|
+
properties: {
|
|
4051
|
+
surfaces: {
|
|
4052
|
+
items: {
|
|
4053
|
+
const: "center"
|
|
4054
|
+
},
|
|
4055
|
+
maxItems: 1
|
|
4056
|
+
}
|
|
4057
|
+
},
|
|
4058
|
+
not: {
|
|
4059
|
+
anyOf: [
|
|
4060
|
+
{
|
|
4061
|
+
required: [
|
|
4062
|
+
"form"
|
|
4063
|
+
]
|
|
4064
|
+
},
|
|
4065
|
+
{
|
|
4066
|
+
required: [
|
|
4067
|
+
"plugin"
|
|
4068
|
+
]
|
|
4069
|
+
}
|
|
4070
|
+
]
|
|
4071
|
+
}
|
|
4072
|
+
},
|
|
4073
|
+
output: {
|
|
4074
|
+
required: [
|
|
4075
|
+
"data"
|
|
4076
|
+
],
|
|
4077
|
+
not: {
|
|
4078
|
+
anyOf: [
|
|
4079
|
+
{
|
|
4080
|
+
required: [
|
|
4081
|
+
"asset_slots"
|
|
4082
|
+
]
|
|
4083
|
+
},
|
|
4084
|
+
{
|
|
4085
|
+
required: [
|
|
4086
|
+
"derive"
|
|
4087
|
+
]
|
|
4088
|
+
},
|
|
4089
|
+
{
|
|
4090
|
+
required: [
|
|
4091
|
+
"profile_select"
|
|
4092
|
+
]
|
|
4093
|
+
},
|
|
4094
|
+
{
|
|
4095
|
+
required: [
|
|
4096
|
+
"entity"
|
|
4097
|
+
]
|
|
4098
|
+
},
|
|
4099
|
+
{
|
|
4100
|
+
required: [
|
|
4101
|
+
"entity_map"
|
|
4102
|
+
]
|
|
4103
|
+
},
|
|
4104
|
+
{
|
|
4105
|
+
required: [
|
|
4106
|
+
"intro"
|
|
4107
|
+
]
|
|
4108
|
+
}
|
|
4109
|
+
]
|
|
4110
|
+
}
|
|
4111
|
+
}
|
|
4112
|
+
}
|
|
4113
|
+
}
|
|
4114
|
+
},
|
|
4115
|
+
{
|
|
4116
|
+
if: {
|
|
4117
|
+
properties: {
|
|
4118
|
+
output: {
|
|
4119
|
+
required: [
|
|
4120
|
+
"data"
|
|
4121
|
+
]
|
|
4122
|
+
}
|
|
4123
|
+
},
|
|
4124
|
+
required: [
|
|
4125
|
+
"output"
|
|
4126
|
+
]
|
|
4127
|
+
},
|
|
4128
|
+
then: {
|
|
4129
|
+
properties: {
|
|
4130
|
+
authoring: {
|
|
4131
|
+
required: [
|
|
4132
|
+
"editor"
|
|
4133
|
+
]
|
|
4134
|
+
}
|
|
4135
|
+
}
|
|
4136
|
+
}
|
|
4137
|
+
},
|
|
4138
|
+
{
|
|
4139
|
+
if: {
|
|
4140
|
+
properties: {
|
|
4141
|
+
type: {
|
|
4142
|
+
const: "instruction"
|
|
4143
|
+
}
|
|
4144
|
+
},
|
|
4145
|
+
required: [
|
|
4146
|
+
"type"
|
|
4147
|
+
]
|
|
4148
|
+
},
|
|
4149
|
+
then: {
|
|
4150
|
+
properties: {
|
|
4151
|
+
output: {
|
|
4152
|
+
not: {
|
|
4153
|
+
required: [
|
|
4154
|
+
"intro"
|
|
4155
|
+
]
|
|
4156
|
+
}
|
|
4157
|
+
}
|
|
4158
|
+
}
|
|
4159
|
+
}
|
|
3097
4160
|
}
|
|
3098
4161
|
]
|
|
3099
4162
|
};
|
|
@@ -3312,6 +4375,44 @@ var authoring_form_v1_schema_default = {
|
|
|
3312
4375
|
],
|
|
3313
4376
|
additionalProperties: false
|
|
3314
4377
|
},
|
|
4378
|
+
"x-clack-help-i18n": {
|
|
4379
|
+
type: "object",
|
|
4380
|
+
properties: {
|
|
4381
|
+
ko: {
|
|
4382
|
+
type: "string",
|
|
4383
|
+
minLength: 1,
|
|
4384
|
+
maxLength: 1e3
|
|
4385
|
+
},
|
|
4386
|
+
en: {
|
|
4387
|
+
type: "string",
|
|
4388
|
+
minLength: 1,
|
|
4389
|
+
maxLength: 1e3
|
|
4390
|
+
}
|
|
4391
|
+
},
|
|
4392
|
+
required: [
|
|
4393
|
+
"ko"
|
|
4394
|
+
],
|
|
4395
|
+
additionalProperties: false
|
|
4396
|
+
},
|
|
4397
|
+
"x-clack-placeholder-i18n": {
|
|
4398
|
+
type: "object",
|
|
4399
|
+
properties: {
|
|
4400
|
+
ko: {
|
|
4401
|
+
type: "string",
|
|
4402
|
+
minLength: 1,
|
|
4403
|
+
maxLength: 300
|
|
4404
|
+
},
|
|
4405
|
+
en: {
|
|
4406
|
+
type: "string",
|
|
4407
|
+
minLength: 1,
|
|
4408
|
+
maxLength: 300
|
|
4409
|
+
}
|
|
4410
|
+
},
|
|
4411
|
+
required: [
|
|
4412
|
+
"ko"
|
|
4413
|
+
],
|
|
4414
|
+
additionalProperties: false
|
|
4415
|
+
},
|
|
3315
4416
|
"x-clack-sections": {
|
|
3316
4417
|
type: "array",
|
|
3317
4418
|
items: {
|
|
@@ -3583,6 +4684,44 @@ var authoring_form_v1_schema_default = {
|
|
|
3583
4684
|
],
|
|
3584
4685
|
additionalProperties: false
|
|
3585
4686
|
},
|
|
4687
|
+
"x-clack-help-i18n": {
|
|
4688
|
+
type: "object",
|
|
4689
|
+
properties: {
|
|
4690
|
+
ko: {
|
|
4691
|
+
type: "string",
|
|
4692
|
+
minLength: 1,
|
|
4693
|
+
maxLength: 1e3
|
|
4694
|
+
},
|
|
4695
|
+
en: {
|
|
4696
|
+
type: "string",
|
|
4697
|
+
minLength: 1,
|
|
4698
|
+
maxLength: 1e3
|
|
4699
|
+
}
|
|
4700
|
+
},
|
|
4701
|
+
required: [
|
|
4702
|
+
"ko"
|
|
4703
|
+
],
|
|
4704
|
+
additionalProperties: false
|
|
4705
|
+
},
|
|
4706
|
+
"x-clack-placeholder-i18n": {
|
|
4707
|
+
type: "object",
|
|
4708
|
+
properties: {
|
|
4709
|
+
ko: {
|
|
4710
|
+
type: "string",
|
|
4711
|
+
minLength: 1,
|
|
4712
|
+
maxLength: 300
|
|
4713
|
+
},
|
|
4714
|
+
en: {
|
|
4715
|
+
type: "string",
|
|
4716
|
+
minLength: 1,
|
|
4717
|
+
maxLength: 300
|
|
4718
|
+
}
|
|
4719
|
+
},
|
|
4720
|
+
required: [
|
|
4721
|
+
"ko"
|
|
4722
|
+
],
|
|
4723
|
+
additionalProperties: false
|
|
4724
|
+
},
|
|
3586
4725
|
"x-clack-source": {
|
|
3587
4726
|
type: "object",
|
|
3588
4727
|
additionalProperties: false,
|
|
@@ -3977,39 +5116,39 @@ var clack_content_v1_schema_default = {
|
|
|
3977
5116
|
var MAX_ZIP = 10 * 1024 * 1024;
|
|
3978
5117
|
var MAX_EXPANDED = 30 * 1024 * 1024;
|
|
3979
5118
|
var MAX_FILES2 = 500;
|
|
3980
|
-
var
|
|
3981
|
-
var ajv = new
|
|
5119
|
+
var textDecoder2 = new TextDecoder("utf-8", { fatal: true });
|
|
5120
|
+
var ajv = new Ajv20202({ allErrors: true, strict: false });
|
|
3982
5121
|
ajv.addSchema(clack_content_v1_schema_default);
|
|
3983
5122
|
var validateManifest = ajv.compile(clack_skill_v1_schema_default);
|
|
3984
|
-
var validateForm = new
|
|
3985
|
-
function
|
|
5123
|
+
var validateForm = new Ajv20202({ allErrors: true, strict: false }).compile(authoring_form_v1_schema_default);
|
|
5124
|
+
function invalid2(message) {
|
|
3986
5125
|
throw new CliError(message, "SKILL_PACKAGE_INVALID");
|
|
3987
5126
|
}
|
|
3988
|
-
function hash(
|
|
3989
|
-
return
|
|
5127
|
+
function hash(bytes2) {
|
|
5128
|
+
return createHash4("sha256").update(bytes2).digest("hex");
|
|
3990
5129
|
}
|
|
3991
5130
|
function object(value2) {
|
|
3992
5131
|
return value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
|
|
3993
5132
|
}
|
|
3994
|
-
function json(
|
|
3995
|
-
if (
|
|
5133
|
+
function json(bytes2, max, label) {
|
|
5134
|
+
if (bytes2.length > max) invalid2(`${label} \uD06C\uAE30 \uC81C\uD55C\uC744 \uCD08\uACFC\uD588\uC2B5\uB2C8\uB2E4.`);
|
|
3996
5135
|
try {
|
|
3997
|
-
const parsed = JSON.parse(
|
|
5136
|
+
const parsed = JSON.parse(textDecoder2.decode(bytes2));
|
|
3998
5137
|
if (object(parsed)) return parsed;
|
|
3999
5138
|
} catch {
|
|
4000
5139
|
}
|
|
4001
|
-
return
|
|
5140
|
+
return invalid2(`${label}\uC740 UTF-8 JSON \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4.`);
|
|
4002
5141
|
}
|
|
4003
5142
|
async function readRegular(path) {
|
|
4004
5143
|
let handle;
|
|
4005
5144
|
try {
|
|
4006
5145
|
handle = await open3(path, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
4007
5146
|
const info = await handle.stat();
|
|
4008
|
-
if (!info.isFile())
|
|
5147
|
+
if (!info.isFile()) invalid2("\uC2A4\uD0AC \uD328\uD0A4\uC9C0\uC5D0\uB294 \uC77C\uBC18 \uD30C\uC77C\uB9CC \uD5C8\uC6A9\uB429\uB2C8\uB2E4.");
|
|
4009
5148
|
return await handle.readFile();
|
|
4010
5149
|
} catch (error2) {
|
|
4011
5150
|
if (error2 instanceof CliError) throw error2;
|
|
4012
|
-
return
|
|
5151
|
+
return invalid2("\uC2A4\uD0AC \uD30C\uC77C\uC744 \uC548\uC804\uD558\uAC8C \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
4013
5152
|
} finally {
|
|
4014
5153
|
await handle?.close();
|
|
4015
5154
|
}
|
|
@@ -4028,76 +5167,76 @@ async function collectFiles(directory) {
|
|
|
4028
5167
|
const entries = await readdir(join2(directory, relative), { withFileTypes: true });
|
|
4029
5168
|
for (const entry of entries.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0)) {
|
|
4030
5169
|
const path = relative ? `${relative}/${entry.name}` : entry.name;
|
|
4031
|
-
if (!skillPathAllowed(path))
|
|
4032
|
-
if (entry.isSymbolicLink() || !(entry.isDirectory() || entry.isFile()))
|
|
5170
|
+
if (!skillPathAllowed(path)) invalid2(`\uD5C8\uC6A9\uB418\uC9C0 \uC54A\uB294 \uD328\uD0A4\uC9C0 \uACBD\uB85C: ${path}`);
|
|
5171
|
+
if (entry.isSymbolicLink() || !(entry.isDirectory() || entry.isFile())) invalid2(`\uC77C\uBC18 \uD30C\uC77C\xB7\uD3F4\uB354\uB9CC \uD5C8\uC6A9\uB429\uB2C8\uB2E4: ${path}`);
|
|
4033
5172
|
if (entry.isDirectory()) {
|
|
4034
5173
|
await walk(path);
|
|
4035
5174
|
continue;
|
|
4036
5175
|
}
|
|
4037
|
-
if (Object.keys(files).length >= MAX_FILES2)
|
|
5176
|
+
if (Object.keys(files).length >= MAX_FILES2) invalid2("\uC2A4\uD0AC \uD30C\uC77C \uC218\uAC00 500\uAC1C\uB97C \uCD08\uACFC\uD588\uC2B5\uB2C8\uB2E4.");
|
|
4038
5177
|
const data = await readRegular(join2(directory, path));
|
|
4039
5178
|
totalBytes += data.length;
|
|
4040
|
-
if (totalBytes > MAX_EXPANDED)
|
|
5179
|
+
if (totalBytes > MAX_EXPANDED) invalid2("\uC2A4\uD0AC \uC555\uCD95 \uD574\uC81C \uC6A9\uB7C9\uC774 30 MiB\uB97C \uCD08\uACFC\uD588\uC2B5\uB2C8\uB2E4.");
|
|
4041
5180
|
files[path] = data;
|
|
4042
5181
|
}
|
|
4043
5182
|
}
|
|
4044
5183
|
await walk("");
|
|
4045
5184
|
return files;
|
|
4046
5185
|
}
|
|
4047
|
-
function inspectZip(
|
|
4048
|
-
if (!
|
|
5186
|
+
function inspectZip(bytes2, expectedDirectory, hashMismatch) {
|
|
5187
|
+
if (!bytes2.length || bytes2.length > MAX_ZIP) invalid2("\uC2A4\uD0AC ZIP\uC740 \uBE44\uC5B4 \uC788\uC9C0 \uC54A\uC740 10 MiB \uC774\uD558 \uD30C\uC77C\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4.");
|
|
4049
5188
|
const seen = /* @__PURE__ */ new Set();
|
|
4050
5189
|
let entries = 0;
|
|
4051
5190
|
let totalBytes = 0;
|
|
4052
5191
|
let extracted;
|
|
4053
5192
|
try {
|
|
4054
|
-
extracted = unzipSync2(
|
|
5193
|
+
extracted = unzipSync2(bytes2, { filter(entry) {
|
|
4055
5194
|
entries++;
|
|
4056
|
-
if (entries > MAX_FILES2)
|
|
5195
|
+
if (entries > MAX_FILES2) invalid2("\uC2A4\uD0AC \uD30C\uC77C \uC218\uAC00 500\uAC1C\uB97C \uCD08\uACFC\uD588\uC2B5\uB2C8\uB2E4.");
|
|
4057
5196
|
const path = entry.name.endsWith("/") ? entry.name.slice(0, -1) : entry.name;
|
|
4058
|
-
if (!skillPathAllowed(path) || seen.has(path.toLowerCase()))
|
|
5197
|
+
if (!skillPathAllowed(path) || seen.has(path.toLowerCase())) invalid2(`\uD5C8\uC6A9\uB418\uC9C0 \uC54A\uB294 ZIP \uACBD\uB85C: ${path}`);
|
|
4059
5198
|
seen.add(path.toLowerCase());
|
|
4060
5199
|
if (entry.name.endsWith("/")) return false;
|
|
4061
5200
|
totalBytes += entry.originalSize;
|
|
4062
|
-
if (totalBytes > MAX_EXPANDED)
|
|
5201
|
+
if (totalBytes > MAX_EXPANDED) invalid2("\uC2A4\uD0AC \uC555\uCD95 \uD574\uC81C \uC6A9\uB7C9\uC774 30 MiB\uB97C \uCD08\uACFC\uD588\uC2B5\uB2C8\uB2E4.");
|
|
4063
5202
|
return true;
|
|
4064
5203
|
} });
|
|
4065
5204
|
} catch (error2) {
|
|
4066
5205
|
if (error2 instanceof CliError) throw error2;
|
|
4067
|
-
return
|
|
5206
|
+
return invalid2("ZIP \uAD6C\uC870\uB97C \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
4068
5207
|
}
|
|
4069
5208
|
const markdown = extracted["SKILL.md"];
|
|
4070
5209
|
const manifestBytes = extracted["clack.skill.json"];
|
|
4071
|
-
if (!markdown || !manifestBytes)
|
|
4072
|
-
if (markdown.length > 20 * 1024)
|
|
5210
|
+
if (!markdown || !manifestBytes) invalid2("\uB8E8\uD2B8 SKILL.md\uC640 clack.skill.json\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.");
|
|
5211
|
+
if (markdown.length > 20 * 1024) invalid2("SKILL.md\uB294 20 KiB \uC774\uD558\uC5EC\uC57C \uD569\uB2C8\uB2E4.");
|
|
4073
5212
|
let frontmatter;
|
|
4074
5213
|
try {
|
|
4075
|
-
const match = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/.exec(
|
|
4076
|
-
if (!match)
|
|
5214
|
+
const match = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/.exec(textDecoder2.decode(markdown));
|
|
5215
|
+
if (!match) invalid2("SKILL.md\uC758 YAML frontmatter\uB97C \uD655\uC778\uD558\uC138\uC694.");
|
|
4077
5216
|
frontmatter = match[1];
|
|
4078
5217
|
} catch {
|
|
4079
|
-
return
|
|
5218
|
+
return invalid2("SKILL.md\uB294 UTF-8 \uD14D\uC2A4\uD2B8\uC5EC\uC57C \uD569\uB2C8\uB2E4.");
|
|
4080
5219
|
}
|
|
4081
5220
|
const document = parseDocument(frontmatter, { version: "1.2", uniqueKeys: true, merge: false });
|
|
4082
5221
|
if (document.errors.length || document.warnings.length || /(?:^|\n)\s*allowed-tools\s*:/i.test(frontmatter)) {
|
|
4083
|
-
|
|
5222
|
+
invalid2("SKILL.md frontmatter\uC5D0 \uC798\uBABB\uB41C \uAC12 \uB610\uB294 allowed-tools\uAC00 \uC788\uC2B5\uB2C8\uB2E4.");
|
|
4084
5223
|
}
|
|
4085
5224
|
let front;
|
|
4086
5225
|
try {
|
|
4087
5226
|
front = document.toJS({ maxAliasCount: 0 });
|
|
4088
5227
|
} catch {
|
|
4089
|
-
return
|
|
5228
|
+
return invalid2("SKILL.md frontmatter\uB97C \uD655\uC778\uD558\uC138\uC694.");
|
|
4090
5229
|
}
|
|
4091
|
-
if (!object(front) || typeof front.name !== "string" || typeof front.description !== "string" || !front.description.length || front.description.length > 1024)
|
|
5230
|
+
if (!object(front) || typeof front.name !== "string" || typeof front.description !== "string" || !front.description.length || front.description.length > 1024) invalid2("SKILL.md\uC758 name\xB7description\uC744 \uD655\uC778\uD558\uC138\uC694.");
|
|
4092
5231
|
const manifest = json(manifestBytes, 64 * 1024, "clack.skill.json");
|
|
4093
|
-
if (!validateManifest(manifest))
|
|
5232
|
+
if (!validateManifest(manifest)) invalid2("clack.skill.json\uC774 \uACF5\uAC1C \uC2A4\uD0AC \uC2A4\uD0A4\uB9C8\uC640 \uC77C\uCE58\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.");
|
|
4094
5233
|
if (manifest.name !== front.name || expectedDirectory && manifest.name !== expectedDirectory && manifest.version !== expectedDirectory || object(front.metadata) && front.metadata.version !== void 0 && front.metadata.version !== manifest.version) {
|
|
4095
|
-
|
|
5234
|
+
invalid2("\uC2A4\uD0AC \uB514\uB809\uD130\uB9AC\xB7SKILL.md\xB7\uB9E4\uB2C8\uD398\uC2A4\uD2B8\uC758 \uC774\uB984 \uB610\uB294 \uBC84\uC804\uC774 \uB2E4\uB985\uB2C8\uB2E4.");
|
|
4096
5235
|
}
|
|
4097
5236
|
const file = (path, max) => {
|
|
4098
|
-
if (typeof path !== "string" || !skillPathAllowed(path) || !extracted[path])
|
|
5237
|
+
if (typeof path !== "string" || !skillPathAllowed(path) || !extracted[path]) invalid2("\uB9E4\uB2C8\uD398\uC2A4\uD2B8\uC5D0 \uC120\uC5B8\uD55C \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
4099
5238
|
const data = extracted[path];
|
|
4100
|
-
if (max && data.length > max)
|
|
5239
|
+
if (max && data.length > max) invalid2("\uC120\uC5B8\uD55C \uD30C\uC77C\uC758 \uD06C\uAE30 \uC81C\uD55C\uC744 \uCD08\uACFC\uD588\uC2B5\uB2C8\uB2E4.");
|
|
4101
5240
|
return data;
|
|
4102
5241
|
};
|
|
4103
5242
|
const display = manifest.display;
|
|
@@ -4106,50 +5245,93 @@ function inspectZip(bytes, expectedDirectory) {
|
|
|
4106
5245
|
const authoring = manifest.authoring;
|
|
4107
5246
|
const output2 = manifest.output;
|
|
4108
5247
|
for (const reference of authoring.references ?? []) file(reference, 64 * 1024);
|
|
4109
|
-
|
|
5248
|
+
let editor;
|
|
5249
|
+
if (manifest.type === "template" && authoring.editor) {
|
|
5250
|
+
editor = inspectEditorSkill(manifest, extracted, { hashMismatch });
|
|
5251
|
+
file(`runtime/${output2.template.entry}`);
|
|
5252
|
+
const runtime2 = manifest.runtime;
|
|
5253
|
+
for (const profile of runtime2.profiles) file(`runtime/.clack/profiles/${profile}.yaml`, 16 * 1024);
|
|
5254
|
+
} else if (manifest.type === "template" || manifest.type === "tool") {
|
|
4110
5255
|
const form = json(file(authoring.form, 64 * 1024), 64 * 1024, "\uC81C\uC791 \uD3FC");
|
|
4111
|
-
if (!validateForm(form))
|
|
5256
|
+
if (!validateForm(form)) invalid2("\uC81C\uC791 \uD3FC\uC774 \uACF5\uAC1C \uC2A4\uD0A4\uB9C8\uC640 \uC77C\uCE58\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.");
|
|
4112
5257
|
const semanticErrors = inspectAuthoringSemantics(manifest, form);
|
|
4113
|
-
if (semanticErrors.length)
|
|
5258
|
+
if (semanticErrors.length) invalid2(semanticErrors.map((error2) => `${error2.path}: ${error2.message}`).join(" / "));
|
|
4114
5259
|
const template = output2.template;
|
|
4115
|
-
if (template.root !== "runtime" || typeof template.entry !== "string")
|
|
5260
|
+
if (template.root !== "runtime" || typeof template.entry !== "string") invalid2("runtime \uD15C\uD50C\uB9BF \uACBD\uB85C\uB97C \uD655\uC778\uD558\uC138\uC694.");
|
|
4116
5261
|
file(`runtime/${template.entry}`);
|
|
4117
5262
|
const runtime2 = manifest.runtime;
|
|
4118
5263
|
for (const profile of runtime2.profiles) file(`runtime/.clack/profiles/${profile}.yaml`, 16 * 1024);
|
|
4119
5264
|
const examples = Object.keys(extracted).filter((path) => /^examples\/[^/]+\.json$/.test(path));
|
|
4120
|
-
if (!examples.length || examples.length > 10)
|
|
5265
|
+
if (!examples.length || examples.length > 10) invalid2("\uD15C\uD50C\uB9BF \uC608\uC2DC\uB294 1~10\uAC1C \uD544\uC694\uD569\uB2C8\uB2E4.");
|
|
4121
5266
|
let validateExample;
|
|
4122
5267
|
try {
|
|
4123
|
-
const examplesAjv = new
|
|
5268
|
+
const examplesAjv = new Ajv20202({ allErrors: true, strict: false });
|
|
4124
5269
|
examplesAjv.addFormat("uuid", /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i);
|
|
4125
5270
|
validateExample = examplesAjv.compile(form);
|
|
4126
5271
|
} catch {
|
|
4127
|
-
return
|
|
5272
|
+
return invalid2("\uC81C\uC791 \uD3FC JSON Schema\uB97C \uD655\uC778\uD558\uC138\uC694.");
|
|
4128
5273
|
}
|
|
4129
5274
|
for (const path of examples) if (!validateExample(json(file(path, 64 * 1024), 64 * 1024, "\uC608\uC2DC"))) {
|
|
4130
|
-
|
|
5275
|
+
invalid2("\uC81C\uC791 \uD3FC\uACFC \uC608\uC2DC \uB370\uC774\uD130\uAC00 \uC77C\uCE58\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.");
|
|
4131
5276
|
}
|
|
4132
5277
|
}
|
|
4133
|
-
return {
|
|
5278
|
+
return {
|
|
5279
|
+
manifest,
|
|
5280
|
+
file_count: Object.keys(extracted).length,
|
|
5281
|
+
total_bytes: totalBytes,
|
|
5282
|
+
...editor ? { editor: editor.summary, warnings: editor.warnings } : {}
|
|
5283
|
+
};
|
|
5284
|
+
}
|
|
5285
|
+
function fillEditorHash(files) {
|
|
5286
|
+
const manifestBytes = files["clack.skill.json"];
|
|
5287
|
+
if (!manifestBytes) return null;
|
|
5288
|
+
let text;
|
|
5289
|
+
let manifest;
|
|
5290
|
+
try {
|
|
5291
|
+
text = textDecoder2.decode(manifestBytes);
|
|
5292
|
+
manifest = JSON.parse(text);
|
|
5293
|
+
} catch {
|
|
5294
|
+
return null;
|
|
5295
|
+
}
|
|
5296
|
+
if (!object(manifest)) return null;
|
|
5297
|
+
const computed = computeEditorHash(manifest, files);
|
|
5298
|
+
const editor = object(manifest.authoring) ? manifest.authoring.editor : void 0;
|
|
5299
|
+
if (!computed || !object(editor) || typeof editor.sha256 !== "string" || editor.sha256 === computed) return null;
|
|
5300
|
+
const declared = editor.sha256;
|
|
5301
|
+
const next = text.split(declared).length === 2 ? text.replace(declared, computed) : `${JSON.stringify({ ...manifest, authoring: { ...manifest.authoring, editor: { ...editor, sha256: computed } } }, null, 2)}
|
|
5302
|
+
`;
|
|
5303
|
+
return new TextEncoder().encode(next);
|
|
4134
5304
|
}
|
|
4135
|
-
async function prepareSkillPackage(source) {
|
|
5305
|
+
async function prepareSkillPackage(source, options2 = {}) {
|
|
4136
5306
|
const path = resolve7(source);
|
|
4137
5307
|
let info;
|
|
4138
5308
|
try {
|
|
4139
5309
|
info = await lstat4(path);
|
|
4140
5310
|
} catch {
|
|
4141
|
-
return
|
|
5311
|
+
return invalid2("\uC2A4\uD0AC \uB514\uB809\uD130\uB9AC \uB610\uB294 ZIP \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
4142
5312
|
}
|
|
4143
|
-
if (info.isSymbolicLink())
|
|
4144
|
-
let
|
|
5313
|
+
if (info.isSymbolicLink()) invalid2("\uC2A4\uD0AC \uACBD\uB85C\uB294 \uC2EC\uBCFC\uB9AD \uB9C1\uD06C\uC77C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
5314
|
+
let bytes2;
|
|
5315
|
+
let filledManifest;
|
|
4145
5316
|
if (info.isDirectory()) {
|
|
4146
5317
|
const files = await collectFiles(path);
|
|
4147
|
-
if (!Object.keys(files).length)
|
|
4148
|
-
|
|
4149
|
-
|
|
4150
|
-
|
|
4151
|
-
|
|
4152
|
-
|
|
5318
|
+
if (!Object.keys(files).length) invalid2("\uC2A4\uD0AC \uB514\uB809\uD130\uB9AC\uAC00 \uBE44\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.");
|
|
5319
|
+
if (options2.fillEditorHash) {
|
|
5320
|
+
const filled = fillEditorHash(files);
|
|
5321
|
+
if (filled) {
|
|
5322
|
+
files["clack.skill.json"] = filled;
|
|
5323
|
+
filledManifest = filled;
|
|
5324
|
+
}
|
|
5325
|
+
}
|
|
5326
|
+
bytes2 = Buffer.from(zipSync(files, { level: 6, mtime: new Date(2e3, 0, 1) }));
|
|
5327
|
+
} else if (info.isFile() && extname2(path).toLowerCase() === ".zip") bytes2 = await readRegular(path);
|
|
5328
|
+
else return invalid2("\uC2A4\uD0AC \uB514\uB809\uD130\uB9AC \uB610\uB294 ZIP \uD30C\uC77C\uC744 \uC9C0\uC815\uD558\uC138\uC694.");
|
|
5329
|
+
const inspected = inspectZip(
|
|
5330
|
+
bytes2,
|
|
5331
|
+
info.isDirectory() ? basename4(path) : void 0,
|
|
5332
|
+
info.isDirectory() && !options2.fillEditorHash ? "warn" : "error"
|
|
5333
|
+
);
|
|
5334
|
+
return { bytes: bytes2, sha256: hash(bytes2), ...inspected, ...filledManifest ? { filled_manifest: filledManifest } : {} };
|
|
4153
5335
|
}
|
|
4154
5336
|
|
|
4155
5337
|
// src/commands/skill.ts
|
|
@@ -4192,7 +5374,7 @@ function ensurePat(client, message = PAT_FOR_WRITE) {
|
|
|
4192
5374
|
}
|
|
4193
5375
|
return token;
|
|
4194
5376
|
}
|
|
4195
|
-
async function validateSkillRemotely(client,
|
|
5377
|
+
async function validateSkillRemotely(client, bytes2) {
|
|
4196
5378
|
const token = ensurePat(client);
|
|
4197
5379
|
const url = new URL("/v4/creator/skills/validate", client.settings.baseUrl);
|
|
4198
5380
|
let response;
|
|
@@ -4210,7 +5392,7 @@ async function validateSkillRemotely(client, bytes) {
|
|
|
4210
5392
|
"X-CLACK-Time-Contract": "utc-v1",
|
|
4211
5393
|
"Cache-Control": "no-store"
|
|
4212
5394
|
},
|
|
4213
|
-
body: new Uint8Array(
|
|
5395
|
+
body: new Uint8Array(bytes2),
|
|
4214
5396
|
redirect: "error",
|
|
4215
5397
|
signal: AbortSignal.timeout(3e4)
|
|
4216
5398
|
});
|
|
@@ -4296,13 +5478,13 @@ function registerSkillCommands(program2, runtime2) {
|
|
|
4296
5478
|
}, contract });
|
|
4297
5479
|
if (!Array.isArray(result.data)) throw new CliError("\uC2A4\uD0AC \uBAA9\uB85D \uC751\uB2F5 \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.", "INVALID_RESPONSE", 502);
|
|
4298
5480
|
if (!opts.all) {
|
|
4299
|
-
ctx.output(result);
|
|
5481
|
+
ctx.output({ ...result, data: markCenterOnly(result.data) });
|
|
4300
5482
|
return;
|
|
4301
5483
|
}
|
|
4302
5484
|
contract = result.time_contract;
|
|
4303
5485
|
all.push(...result.data);
|
|
4304
5486
|
if (!result.pagination?.has_more) {
|
|
4305
|
-
ctx.output({ data: all, time_contract: result.time_contract });
|
|
5487
|
+
ctx.output({ data: markCenterOnly(all), time_contract: result.time_contract });
|
|
4306
5488
|
return;
|
|
4307
5489
|
}
|
|
4308
5490
|
const next = result.pagination.next_cursor;
|
|
@@ -4316,7 +5498,8 @@ function registerSkillCommands(program2, runtime2) {
|
|
|
4316
5498
|
runtime2.action(skill.command("get <slug>").description("\uC2A4\uD0AC \uC0C1\uC138 \uC870\uD68C (skill:read)"), async (ctx, [value2]) => {
|
|
4317
5499
|
const parsed = skillSlug.safeParse(value2);
|
|
4318
5500
|
if (!parsed.success) throw new CliError("\uC2A4\uD0AC \uC774\uB984\uC744 \uD655\uC778\uD558\uC138\uC694.");
|
|
4319
|
-
|
|
5501
|
+
const detail = await ctx.api.request("GET", `/v4/skills/${parsed.data}`);
|
|
5502
|
+
ctx.output({ ...detail, data: markCenterOnly(detail.data) });
|
|
4320
5503
|
});
|
|
4321
5504
|
runtime2.action(skill.command("form <slug> <version>").description("\uC2A4\uD0AC \uBC84\uC804 \uC785\uB825 \uC591\uC2DD \uC870\uD68C (skill:read)"), async (ctx, [value2, version]) => {
|
|
4322
5505
|
const parsed = skillSlug.safeParse(value2);
|
|
@@ -4330,27 +5513,73 @@ function registerSkillCommands(program2, runtime2) {
|
|
|
4330
5513
|
});
|
|
4331
5514
|
runtime2.action(skill.command("validate [dir]").description("\uC2A4\uD0AC \uD328\uD0A4\uC9C0 \uB85C\uCEEC \uAC80\uC0AC (\uB85C\uADF8\uC778 \uBD88\uD544\uC694, --remote\uB294 skill:write)").option("--remote", "\uC11C\uBC84\uC5D0\uC11C\uB3C4 \uD328\uD0A4\uC9C0\uB97C \uB4DC\uB77C\uC774\uB7F0 \uAC80\uC99D (skill:write)"), async (ctx, [dir], opts) => {
|
|
4332
5515
|
const prepared = await prepareSkillPackage(dir ?? ".");
|
|
4333
|
-
|
|
4334
|
-
const remote = await validateSkillRemotely(ctx.api, prepared.bytes);
|
|
4335
|
-
ctx.output({
|
|
4336
|
-
local_valid: true,
|
|
4337
|
-
sha256: prepared.sha256,
|
|
4338
|
-
byte_size: prepared.bytes.length,
|
|
4339
|
-
file_count: prepared.file_count,
|
|
4340
|
-
manifest: prepared.manifest,
|
|
4341
|
-
remote
|
|
4342
|
-
});
|
|
4343
|
-
} else ctx.output({
|
|
5516
|
+
const local = {
|
|
4344
5517
|
local_valid: true,
|
|
4345
5518
|
sha256: prepared.sha256,
|
|
4346
5519
|
byte_size: prepared.bytes.length,
|
|
4347
5520
|
file_count: prepared.file_count,
|
|
4348
5521
|
manifest: prepared.manifest,
|
|
4349
|
-
...
|
|
5522
|
+
...editorReport(prepared)
|
|
5523
|
+
};
|
|
5524
|
+
if (opts.remote && !ctx.options.dryRun) {
|
|
5525
|
+
const remote = await validateSkillRemotely(ctx.api, prepared.bytes);
|
|
5526
|
+
ctx.output({ ...local, remote });
|
|
5527
|
+
} else ctx.output({ ...local, ...opts.remote ? { remote_skipped: true } : {} });
|
|
5528
|
+
});
|
|
5529
|
+
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) => {
|
|
5530
|
+
const source = dir ?? ".";
|
|
5531
|
+
const prepared = await prepareSkillPackage(source, { fillEditorHash: true });
|
|
5532
|
+
const out = resolve8(opts.out ?? `${prepared.manifest.name}-${prepared.manifest.version}.zip`);
|
|
5533
|
+
const writeManifest = Boolean(opts.writeManifest && prepared.filled_manifest);
|
|
5534
|
+
if (!ctx.options.dryRun) {
|
|
5535
|
+
await writeFile2(out, prepared.bytes);
|
|
5536
|
+
if (writeManifest) await writeFile2(join3(resolve8(source), "clack.skill.json"), prepared.filled_manifest);
|
|
5537
|
+
}
|
|
5538
|
+
ctx.output({
|
|
5539
|
+
...ctx.options.dryRun ? { dry_run: true } : {},
|
|
5540
|
+
out,
|
|
5541
|
+
sha256: prepared.sha256,
|
|
5542
|
+
byte_size: prepared.bytes.length,
|
|
5543
|
+
file_count: prepared.file_count,
|
|
5544
|
+
slug: prepared.manifest.name,
|
|
5545
|
+
version: prepared.manifest.version,
|
|
5546
|
+
...prepared.filled_manifest ? { editor_hash_filled: true, manifest_written: writeManifest && !ctx.options.dryRun } : {},
|
|
5547
|
+
...editorReport(prepared)
|
|
4350
5548
|
});
|
|
4351
5549
|
});
|
|
5550
|
+
const editor = skill.command("editor").description("\uC2A4\uD0AC \uC5D0\uB514\uD130(authoring.editor) \uAC1C\uBC1C \uB3C4\uAD6C \u2014 \uC5D0\uB514\uD130 \uC2A4\uD0AC\uC740 \uD06C\uB9AC\uC5D0\uC774\uD130 \uC13C\uD130 \uC804\uC6A9");
|
|
5551
|
+
runtime2.action(
|
|
5552
|
+
editor.command("dev").description("\uB85C\uCEEC \uBAA8\uC758 \uD638\uC2A4\uD2B8\uC5D0\uC11C \uC5D0\uB514\uD130\uB97C \uB744\uC6B4\uB2E4 (http://localhost:5170, \uB85C\uADF8\uC778 \uBD88\uD544\uC694)").option("--editor <dir>", "\uC5D0\uB514\uD130 \uBC88\uB4E4 \uB514\uB809\uD130\uB9AC (\uAE30\uBCF8: SDK \uC2A4\uD0C0\uD130)").option("--document <file>", "\uCD08\uAE30 \uB370\uC774\uD130 \uBB38\uC11C JSON (\uAE30\uBCF8: SDK \uC2A4\uD0C0\uD130 \uC608\uC2DC)").option("--schema <file>", "\uB370\uC774\uD130 \uC2A4\uD0A4\uB9C8 output/data.schema.json (\uAE30\uBCF8: SDK \uC2A4\uD0C0\uD130)").option("--host-port <port>", "\uD638\uC2A4\uD2B8 \uD398\uC774\uC9C0 \uD3EC\uD2B8", "5170").option("--editor-port <port>", "\uC5D0\uB514\uD130 \uD3EC\uD2B8", "5171").option("--sdk-dir <dir>", "@clack/skill-editor-sdk \uC704\uCE58 (\uAE30\uBCF8: CLACK_SKILL_EDITOR_SDK_DIR, \uC124\uCE58\uB41C \uD328\uD0A4\uC9C0, CLACK_MONOREPO_DIR/clack-skill-editor-sdk \uC21C)").addHelpText("after", `
|
|
5553
|
+
${SDK_LOCATION_HELP}
|
|
5554
|
+
`),
|
|
5555
|
+
async (ctx, _args, opts) => {
|
|
5556
|
+
const args = [];
|
|
5557
|
+
for (const [flag, value2] of [
|
|
5558
|
+
["editor", opts.editor],
|
|
5559
|
+
["document", opts.document],
|
|
5560
|
+
["schema", opts.schema],
|
|
5561
|
+
["host-port", opts.hostPort],
|
|
5562
|
+
["editor-port", opts.editorPort]
|
|
5563
|
+
]) {
|
|
5564
|
+
if (value2 === void 0) continue;
|
|
5565
|
+
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.");
|
|
5566
|
+
args.push(`--${flag}`, flag.endsWith("port") ? value2 : resolve8(value2));
|
|
5567
|
+
}
|
|
5568
|
+
const sdkDir = resolveSdkDir(opts.sdkDir);
|
|
5569
|
+
const serve = join3(sdkDir, "mock-host/serve.mjs");
|
|
5570
|
+
if (ctx.options.dryRun) {
|
|
5571
|
+
ctx.output({ dry_run: true, sdk_dir: sdkDir, command: ["node", serve, ...args] });
|
|
5572
|
+
return;
|
|
5573
|
+
}
|
|
5574
|
+
await new Promise((done, fail2) => {
|
|
5575
|
+
const child = spawn(process.execPath, [serve, ...args], { stdio: "inherit" });
|
|
5576
|
+
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")));
|
|
5577
|
+
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());
|
|
5578
|
+
});
|
|
5579
|
+
}
|
|
5580
|
+
);
|
|
4352
5581
|
runtime2.action(skill.command("push [dir]").description("\uC2A4\uD0AC \uD328\uD0A4\uC9C0 \uAC80\uC99D \uD6C4 \uC0C8 \uBC84\uC804 \uC5C5\uB85C\uB4DC (skill:write)").option("--skill-id <id>", "\uAE30\uC874 \uC2A4\uD0AC ID\uC5D0 \uC0C8 \uBC84\uC804 \uCD94\uAC00"), async (ctx, [dir], opts) => {
|
|
4353
|
-
const prepared = await prepareSkillPackage(dir ?? ".");
|
|
5582
|
+
const prepared = await prepareSkillPackage(dir ?? ".", { fillEditorHash: true });
|
|
4354
5583
|
if (prepared.manifest.type !== "instruction" && prepared.manifest.type !== "template") {
|
|
4355
5584
|
throw new CliError("\uD604\uC7AC \uC2A4\uD0AC \uC5C5\uB85C\uB4DC\uB294 instruction\xB7template \uC720\uD615\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4.", "SKILL_TYPE_UNSUPPORTED");
|
|
4356
5585
|
}
|
|
@@ -4454,6 +5683,39 @@ function registerSkillCommands(program2, runtime2) {
|
|
|
4454
5683
|
}
|
|
4455
5684
|
);
|
|
4456
5685
|
}
|
|
5686
|
+
function markCenterOnly(value2) {
|
|
5687
|
+
const mark2 = (item) => {
|
|
5688
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) return item;
|
|
5689
|
+
const row = item;
|
|
5690
|
+
return row.center_only === true || row.editor === true ? { ...row, center_only: true, availability: "\uC13C\uD130 \uC804\uC6A9" } : row;
|
|
5691
|
+
};
|
|
5692
|
+
return Array.isArray(value2) ? value2.map(mark2) : mark2(value2);
|
|
5693
|
+
}
|
|
5694
|
+
function editorReport(prepared) {
|
|
5695
|
+
if (!prepared.editor) return {};
|
|
5696
|
+
return {
|
|
5697
|
+
editor: prepared.editor,
|
|
5698
|
+
center_only: true,
|
|
5699
|
+
...prepared.warnings?.length ? { warnings: prepared.warnings } : {},
|
|
5700
|
+
notice: "\uB85C\uCEEC \uAC80\uC0AC\uB294 \uAD6C\uC870\xB7\uD55C\uB3C4\xB7\uD574\uC2DC\xB7\uB370\uC774\uD130 \uC2A4\uD0A4\uB9C8\xB7\uC608\uC2DC\uAE4C\uC9C0\uC785\uB2C8\uB2E4. \uC5D0\uB514\uD130 \uCF54\uB4DC \uC815\uC801 \uAC80\uC0AC(editor-static-v1)\uC640 \uC2EC\uC0AC\uB294 \uC11C\uBC84 \uD310\uC815\uC774 \uCD5C\uC885\uC785\uB2C8\uB2E4."
|
|
5701
|
+
};
|
|
5702
|
+
}
|
|
5703
|
+
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.";
|
|
5704
|
+
function resolveSdkDir(option) {
|
|
5705
|
+
const candidates = [];
|
|
5706
|
+
if (option) candidates.push(resolve8(option));
|
|
5707
|
+
else {
|
|
5708
|
+
if (process.env.CLACK_SKILL_EDITOR_SDK_DIR) candidates.push(resolve8(process.env.CLACK_SKILL_EDITOR_SDK_DIR));
|
|
5709
|
+
try {
|
|
5710
|
+
candidates.push(resolve8(createRequire(join3(process.cwd(), "noop.js")).resolve("@clack/skill-editor-sdk/package.json"), ".."));
|
|
5711
|
+
} catch {
|
|
5712
|
+
}
|
|
5713
|
+
if (process.env.CLACK_MONOREPO_DIR) candidates.push(resolve8(process.env.CLACK_MONOREPO_DIR, "clack-skill-editor-sdk"));
|
|
5714
|
+
}
|
|
5715
|
+
const found = candidates.find((dir) => existsSync(join3(dir, "mock-host/serve.mjs")));
|
|
5716
|
+
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");
|
|
5717
|
+
return found;
|
|
5718
|
+
}
|
|
4457
5719
|
function skillVersionIds(skillId, versionId) {
|
|
4458
5720
|
const parsedSkillId = uuid2.safeParse(skillId);
|
|
4459
5721
|
const parsedVersionId = uuid2.safeParse(versionId);
|
|
@@ -4631,7 +5893,7 @@ var toolDefinitions = [
|
|
|
4631
5893
|
function error(id2, code, message) {
|
|
4632
5894
|
return { jsonrpc: "2.0", id: id2, error: { code, message } };
|
|
4633
5895
|
}
|
|
4634
|
-
function
|
|
5896
|
+
function isObject3(value2) {
|
|
4635
5897
|
return value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
|
|
4636
5898
|
}
|
|
4637
5899
|
function toolResult(value2, isError = false) {
|
|
@@ -4650,7 +5912,7 @@ var PlatformMcpServer = class {
|
|
|
4650
5912
|
env;
|
|
4651
5913
|
initialized = false;
|
|
4652
5914
|
async handle(message) {
|
|
4653
|
-
if (!
|
|
5915
|
+
if (!isObject3(message) || message.jsonrpc !== "2.0" || typeof message.method !== "string" || message.id !== void 0 && typeof message.id !== "string" && typeof message.id !== "number" && message.id !== null) {
|
|
4654
5916
|
return error(null, -32600, "\uC62C\uBC14\uB978 JSON-RPC \uC694\uCCAD\uC774 \uC544\uB2D9\uB2C8\uB2E4.");
|
|
4655
5917
|
}
|
|
4656
5918
|
const request = message;
|
|
@@ -4659,7 +5921,7 @@ var PlatformMcpServer = class {
|
|
|
4659
5921
|
return void 0;
|
|
4660
5922
|
}
|
|
4661
5923
|
if (request.method === "initialize") {
|
|
4662
|
-
const requested =
|
|
5924
|
+
const requested = isObject3(request.params) && typeof request.params.protocolVersion === "string" ? request.params.protocolVersion : "";
|
|
4663
5925
|
if (!requested) return error(request.id, -32602, "\uD504\uB85C\uD1A0\uCF5C \uBC84\uC804\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.");
|
|
4664
5926
|
this.initialized = false;
|
|
4665
5927
|
return { jsonrpc: "2.0", id: request.id, result: {
|
|
@@ -4672,7 +5934,7 @@ var PlatformMcpServer = class {
|
|
|
4672
5934
|
if (!this.initialized) return error(request.id, -32e3, "MCP \uCD08\uAE30\uD654\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.");
|
|
4673
5935
|
if (request.method === "tools/list") return { jsonrpc: "2.0", id: request.id, result: { tools: toolDefinitions } };
|
|
4674
5936
|
if (request.method !== "tools/call") return error(request.id, -32601, "\uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 MCP \uBA54\uC11C\uB4DC\uC785\uB2C8\uB2E4.");
|
|
4675
|
-
if (!
|
|
5937
|
+
if (!isObject3(request.params) || typeof request.params.name !== "string" || request.params.arguments !== void 0 && !isObject3(request.params.arguments)) {
|
|
4676
5938
|
return error(request.id, -32602, "\uB3C4\uAD6C \uC774\uB984\uACFC \uC778\uC790\uB97C \uD655\uC778\uD558\uC138\uC694.");
|
|
4677
5939
|
}
|
|
4678
5940
|
const name2 = request.params.name;
|
|
@@ -4682,7 +5944,7 @@ var PlatformMcpServer = class {
|
|
|
4682
5944
|
const writeKind = name2 === "platform_server_data_put" ? "put" : name2 === "platform_server_data_patch" ? "patch" : name2 === "platform_server_data_delete" ? "delete" : null;
|
|
4683
5945
|
if (writeKind) {
|
|
4684
5946
|
const preview = platformWritePreview(writeKind, args);
|
|
4685
|
-
if (!
|
|
5947
|
+
if (!isObject3(args) || args.confirm !== true) {
|
|
4686
5948
|
return { jsonrpc: "2.0", id: request.id, result: toolResult({ preview: true, confirmation_required: true, ...preview }) };
|
|
4687
5949
|
}
|
|
4688
5950
|
}
|
|
@@ -4700,7 +5962,7 @@ var PlatformMcpServer = class {
|
|
|
4700
5962
|
}
|
|
4701
5963
|
/** 개인 액세스 토큰(platform:read·platform:write, skill:*, creator-content:write·publish 스코프) 기반 도구. */
|
|
4702
5964
|
async callPatTool(name2, args) {
|
|
4703
|
-
if (patDestructiveToolNames.has(name2) && (!
|
|
5965
|
+
if (patDestructiveToolNames.has(name2) && (!isObject3(args) || args.confirm !== true)) {
|
|
4704
5966
|
return { preview: true, confirmation_required: true, tool: name2, arguments: redact(args) };
|
|
4705
5967
|
}
|
|
4706
5968
|
const client = platformPatClient(this.settings, this.env);
|
|
@@ -4715,7 +5977,7 @@ var PlatformMcpServer = class {
|
|
|
4715
5977
|
}
|
|
4716
5978
|
if (name2 === "platform_skill_push") {
|
|
4717
5979
|
const { dir, skill_id } = parsePat(patSchemas.skillPush, args, "\uC2A4\uD0AC \uC5C5\uB85C\uB4DC \uC785\uB825");
|
|
4718
|
-
const prepared = await prepareSkillPackage(dir ?? ".");
|
|
5980
|
+
const prepared = await prepareSkillPackage(dir ?? ".", { fillEditorHash: true });
|
|
4719
5981
|
if (prepared.manifest.type !== "instruction" && prepared.manifest.type !== "template") {
|
|
4720
5982
|
throw new CliError("\uD604\uC7AC \uC2A4\uD0AC \uC5C5\uB85C\uB4DC\uB294 instruction\xB7template \uC720\uD615\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4.", "SKILL_TYPE_UNSUPPORTED");
|
|
4721
5983
|
}
|
|
@@ -4865,7 +6127,7 @@ var revision3 = (value2) => parse(z11.coerce.number().int().nonnegative().max(42
|
|
|
4865
6127
|
var base = "/v4/creator/custom-pages";
|
|
4866
6128
|
var presentationSchema = z11.object({
|
|
4867
6129
|
schema_version: z11.literal(1),
|
|
4868
|
-
header_mode: z11.enum(["fixed", "scroll_hide", "translucent_scroll_hide", "floating_close"]),
|
|
6130
|
+
header_mode: z11.enum(["fixed", "scroll_hide", "translucent_scroll_hide", "floating_close", "floating_close_safe_area"]),
|
|
4869
6131
|
color_scheme: z11.enum(["light", "dark"])
|
|
4870
6132
|
}).strict();
|
|
4871
6133
|
function registerPageCommands(program2, runtime2) {
|
|
@@ -4877,7 +6139,7 @@ function registerPageCommands(program2, runtime2) {
|
|
|
4877
6139
|
const policy = parse(z11.literal("2026-09-22"), opts.policyVersion);
|
|
4878
6140
|
await mutate(ctx, "POST", base, { target_type: opts.target, ...opts.target === "space" ? { space_id: uuid3(opts.spaceId) } : {}, policy_version: policy });
|
|
4879
6141
|
});
|
|
4880
|
-
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"]).default("fixed")).addOption(new Option3("--color <scheme>", "\uD5E4\uB354 \uC0C9\uC0C1").choices(["light", "dark"]).default("light")), async (ctx, [id2, file], opts) => {
|
|
6142
|
+
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) => {
|
|
4881
6143
|
const path = `${base}/${uuid3(id2)}`;
|
|
4882
6144
|
const prepared = await prepareContentFile(file);
|
|
4883
6145
|
const presentation = parse(presentationSchema, { schema_version: 1, header_mode: opts.header, color_scheme: opts.color });
|
|
@@ -4895,7 +6157,7 @@ function registerPageCommands(program2, runtime2) {
|
|
|
4895
6157
|
}
|
|
4896
6158
|
});
|
|
4897
6159
|
runtime2.action(page.command("complete <id> <upload-id>").description("\uC5C5\uB85C\uB4DC \uC644\uB8CC \uCC98\uB9AC \uC7AC\uC2DC\uB3C4 (custom-page:write)"), async (ctx, [id2, uploadId]) => mutate(ctx, "POST", `${base}/${uuid3(id2)}/uploads/${uuid3(uploadId)}/complete`, {}));
|
|
4898
|
-
runtime2.action(page.command("presentation <id> <version-id>").description("\uD30C\uC77C\uC740 \uB450\uACE0 \uD5E4\uB354 \uC124\uC815\uB9CC \uBC14\uAFBC \uC0C8 \uBC84\uC804 \uC0DD\uC131 (custom-page:write)").addOption(new Option3("--header <mode>", "\uC0C8 \uBC84\uC804\uC758 \uD5E4\uB354 \uD45C\uC2DC").choices(["fixed", "scroll_hide", "translucent_scroll_hide", "floating_close"]).makeOptionMandatory()).addOption(new Option3("--color <scheme>", "\uC0C8 \uBC84\uC804\uC758 \uD5E4\uB354 \uC0C9\uC0C1").choices(["light", "dark"]).makeOptionMandatory()), async (ctx, [id2, versionId], opts) => mutate(ctx, "POST", `${base}/${uuid3(id2)}/versions/${uuid3(versionId)}/presentation`, {
|
|
6160
|
+
runtime2.action(page.command("presentation <id> <version-id>").description("\uD30C\uC77C\uC740 \uB450\uACE0 \uD5E4\uB354 \uC124\uC815\uB9CC \uBC14\uAFBC \uC0C8 \uBC84\uC804 \uC0DD\uC131 (custom-page:write)").addOption(new Option3("--header <mode>", "\uC0C8 \uBC84\uC804\uC758 \uD5E4\uB354 \uD45C\uC2DC").choices(["fixed", "scroll_hide", "translucent_scroll_hide", "floating_close", "floating_close_safe_area"]).makeOptionMandatory()).addOption(new Option3("--color <scheme>", "\uC0C8 \uBC84\uC804\uC758 \uD5E4\uB354 \uC0C9\uC0C1").choices(["light", "dark"]).makeOptionMandatory()), async (ctx, [id2, versionId], opts) => mutate(ctx, "POST", `${base}/${uuid3(id2)}/versions/${uuid3(versionId)}/presentation`, {
|
|
4899
6161
|
presentation: parse(presentationSchema, { schema_version: 1, header_mode: opts.header, color_scheme: opts.color })
|
|
4900
6162
|
}));
|
|
4901
6163
|
runtime2.action(page.command("preview <id> <version-id>").description("\uC571 \uD655\uC778\uC6A9 \uBBF8\uB9AC\uBCF4\uAE30 \uB9C1\uD06C \uC870\uD68C (custom-page:read)"), async (ctx, [id2, versionId]) => ctx.output(await ctx.api.request("GET", `${base}/${uuid3(id2)}/versions/${uuid3(versionId)}/preview`)));
|