@clack-platform/cli 0.1.0 → 0.2.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 +68 -13
- package/dist/cli.js +1419 -232
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -86,6 +86,20 @@ function requestTimeContract(method, path) {
|
|
|
86
86
|
].includes(path) || /^\/v4\/posts\/\d+(?:\/comments|\/merchandises)?$/.test(path) || /^\/v4\/merchandises\/\d+$/.test(path) || /^\/v4\/channels\/[^/]+(?:\/posts|\/series)?$/.test(path) || /^\/v4\/(?:channel-posts|channel-series)\/\d+$/.test(path) || /^\/v4\/search\/(?:posts|merchandises-with-post|merchandises-without-post|channel-posts|channels)$/.test(path)) return "utc-v1";
|
|
87
87
|
return "legacy-kst";
|
|
88
88
|
}
|
|
89
|
+
var KST_PARTS = new Intl.DateTimeFormat("en-CA", {
|
|
90
|
+
timeZone: "Asia/Seoul",
|
|
91
|
+
year: "numeric",
|
|
92
|
+
month: "2-digit",
|
|
93
|
+
day: "2-digit",
|
|
94
|
+
hour: "2-digit",
|
|
95
|
+
minute: "2-digit",
|
|
96
|
+
hour12: false
|
|
97
|
+
});
|
|
98
|
+
function formatKst(date) {
|
|
99
|
+
const parts = Object.fromEntries(KST_PARTS.formatToParts(date).map((part) => [part.type, part.value]));
|
|
100
|
+
const hour = parts.hour === "24" ? "00" : parts.hour;
|
|
101
|
+
return `${parts.year}-${parts.month}-${parts.day} ${hour}:${parts.minute} KST`;
|
|
102
|
+
}
|
|
89
103
|
function formatInstant(value2, contract, mode) {
|
|
90
104
|
const iso = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/.test(value2);
|
|
91
105
|
const legacyWallClock = contract === "legacy-kst" && /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?:\.\d{3})?$/.test(value2);
|
|
@@ -93,10 +107,7 @@ function formatInstant(value2, contract, mode) {
|
|
|
93
107
|
const source = contract === "legacy-kst" ? (iso ? value2.slice(0, -1) : value2.replace(" ", "T")) + "+09:00" : value2;
|
|
94
108
|
const date = new Date(source);
|
|
95
109
|
if (!Number.isFinite(date.getTime())) return value2;
|
|
96
|
-
return mode === "utc" ? date.toISOString() :
|
|
97
|
-
dateStyle: "medium",
|
|
98
|
-
timeStyle: "long"
|
|
99
|
-
}).format(date);
|
|
110
|
+
return mode === "utc" ? date.toISOString() : formatKst(date);
|
|
100
111
|
}
|
|
101
112
|
var instantKeys = /* @__PURE__ */ new Set(["created_at", "updated_at", "published_at", "hidden_at", "owner_hidden_at", "delete_available_at", "lastBulkBumpedAt", "deleted_at", "expires_at", "revoked_at", "suspended_at", "last_used_at"]);
|
|
102
113
|
function displayData(value2, contract, mode) {
|
|
@@ -139,7 +150,7 @@ function redact(value2) {
|
|
|
139
150
|
// package.json
|
|
140
151
|
var package_default = {
|
|
141
152
|
name: "@clack-platform/cli",
|
|
142
|
-
version: "0.
|
|
153
|
+
version: "0.2.0",
|
|
143
154
|
description: "\uD074\uB799 \uACC4\uC815\uC758 \uC0C1\uD488\xB7\uCF58\uD150\uCE20\xB7\uD504\uB85C\uD544\uC744 \uAD00\uB9AC\uD558\uB294 CLI",
|
|
144
155
|
type: "module",
|
|
145
156
|
bin: {
|
|
@@ -388,9 +399,9 @@ function documentPath(collection2, key) {
|
|
|
388
399
|
return `/platform/v1/data/${collection2}/docs/${key}`;
|
|
389
400
|
}
|
|
390
401
|
function bodySize(body) {
|
|
391
|
-
const
|
|
392
|
-
if (
|
|
393
|
-
return
|
|
402
|
+
const bytes2 = Buffer.byteLength(JSON.stringify(body), "utf8");
|
|
403
|
+
if (bytes2 > maxBodyBytes) throw new CliError("\uBB38\uC11C \uBCF8\uBB38\uC740 64 KiB \uC774\uD558\uC5EC\uC57C \uD569\uB2C8\uB2E4.", "VALIDATION_ERROR");
|
|
404
|
+
return bytes2;
|
|
394
405
|
}
|
|
395
406
|
async function readPlatformBodyFile(file) {
|
|
396
407
|
let raw;
|
|
@@ -587,11 +598,11 @@ async function authoringRequest(client, raw, surface = "cli") {
|
|
|
587
598
|
if (input.action === "upload") {
|
|
588
599
|
const stat3 = await lstat2(input.file);
|
|
589
600
|
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.");
|
|
590
|
-
const
|
|
601
|
+
const bytes2 = await readFile2(input.file);
|
|
591
602
|
const body2 = new FormData();
|
|
592
603
|
body2.append("field", input.field);
|
|
593
604
|
if (input.item_index !== void 0) body2.append("item_index", String(input.item_index));
|
|
594
|
-
body2.append("file", new Blob([
|
|
605
|
+
body2.append("file", new Blob([bytes2]), basename(input.file));
|
|
595
606
|
return client.request("POST", `${path}/assets`, { body: body2 });
|
|
596
607
|
}
|
|
597
608
|
if (input.action === "image" || input.action === "image-batch") {
|
|
@@ -606,7 +617,7 @@ async function authoringRequest(client, raw, surface = "cli") {
|
|
|
606
617
|
return client.request(action === "save" ? "PATCH" : "POST", `${path}/${action === "save" ? "form" : action}`, { body });
|
|
607
618
|
}
|
|
608
619
|
function registerAuthoringCommands(program2, runtime2) {
|
|
609
|
-
runtime2.action(program2.command("authoring").description("\uACF5\uAC1C \uC2A4\uD0AC\uB85C \uC81C\uC791\xB7\uC790\uC0B0 \uC5C5\uB85C\uB4DC\xB7\uC774\uBBF8\uC9C0 \uC0DD\uC131\xB7\uD328\uD0A4\uC9D5").requiredOption("--input <file>", "action\uACFC \uC694\uCCAD \uAC12\uC774 \uC788\uB294 JSON \uD30C\uC77C"), async (ctx, _args, opts) => {
|
|
620
|
+
runtime2.action(program2.command("authoring").description("\uACF5\uAC1C \uC2A4\uD0AC\uB85C \uC81C\uC791\xB7\uC790\uC0B0 \uC5C5\uB85C\uB4DC\xB7\uC774\uBBF8\uC9C0 \uC0DD\uC131\xB7\uD328\uD0A4\uC9D5 (creator-content:write)").requiredOption("--input <file>", "action\uACFC \uC694\uCCAD \uAC12\uC774 \uC788\uB294 JSON \uD30C\uC77C"), async (ctx, _args, opts) => {
|
|
610
621
|
const input = await readPlatformBodyFile(opts.input);
|
|
611
622
|
if (ctx.options.dryRun) {
|
|
612
623
|
const parsed = authoringInput.safeParse(input);
|
|
@@ -667,7 +678,7 @@ var ConfigStore = class {
|
|
|
667
678
|
try {
|
|
668
679
|
const stat3 = await lstat3(path);
|
|
669
680
|
if (!stat3.isFile() || stat3.isSymbolicLink()) throw new CliError("\uC124\uC815 \uD30C\uC77C\uC740 \uC77C\uBC18 \uD30C\uC77C\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4.", "CONFIG_UNSAFE");
|
|
670
|
-
if (name2 === "credentials.json" && process.platform !== "win32") await chmod(path, 384);
|
|
681
|
+
if ((name2 === "credentials.json" || name2 === "device-request.json") && process.platform !== "win32") await chmod(path, 384);
|
|
671
682
|
const body = JSON.parse(await readFile3(path, "utf8"));
|
|
672
683
|
if (!body || typeof body !== "object" || !("profiles" in body) || !body.profiles || typeof body.profiles !== "object" || Array.isArray(body.profiles)) throw new Error("\uD615\uC2DD");
|
|
673
684
|
return body;
|
|
@@ -737,6 +748,21 @@ var ConfigStore = class {
|
|
|
737
748
|
delete all.profiles[validateProfile(profile)];
|
|
738
749
|
await this.write("credentials.json", all);
|
|
739
750
|
}
|
|
751
|
+
/** 발급한 디바이스 요청을 저장해 `login`이 같은 턴에서 끝나도 `login --resume`으로 이어받을 수 있게 한다(E1). */
|
|
752
|
+
async savePendingDeviceRequest(profile, request) {
|
|
753
|
+
const all = await this.read("device-request.json");
|
|
754
|
+
all.profiles[validateProfile(profile)] = request;
|
|
755
|
+
await this.write("device-request.json", all);
|
|
756
|
+
}
|
|
757
|
+
async getPendingDeviceRequest(profile) {
|
|
758
|
+
return (await this.read("device-request.json")).profiles[validateProfile(profile)];
|
|
759
|
+
}
|
|
760
|
+
/** 만료·거부·소비 등으로 더 이상 재개할 수 없는 요청을 정리한다. 네트워크 오류처럼 일시적인 실패에는 호출하지 않는다. */
|
|
761
|
+
async removePendingDeviceRequest(profile) {
|
|
762
|
+
const all = await this.read("device-request.json");
|
|
763
|
+
delete all.profiles[validateProfile(profile)];
|
|
764
|
+
await this.write("device-request.json", all);
|
|
765
|
+
}
|
|
740
766
|
async getConfig(profile) {
|
|
741
767
|
return (await this.read("config.json")).profiles[validateProfile(profile)] ?? {};
|
|
742
768
|
}
|
|
@@ -846,6 +872,57 @@ async function pollDevice(api, request, deps = {
|
|
|
846
872
|
}
|
|
847
873
|
throw new CliError("\uC2B9\uC778 \uCF54\uB4DC\uAC00 \uB9CC\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4. clack login\uC744 \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694.", "EXPIRED_TOKEN", 410);
|
|
848
874
|
}
|
|
875
|
+
var TERMINAL_DEVICE_FAILURE_CODES = ["EXPIRED_TOKEN", "ACCESS_DENIED", "DEVICE_CODE_CONSUMED", "DEVICE_AUTHORIZATION_NOT_FOUND"];
|
|
876
|
+
function validateVerificationUrl(href2) {
|
|
877
|
+
let verification;
|
|
878
|
+
try {
|
|
879
|
+
verification = new URL(href2);
|
|
880
|
+
} catch {
|
|
881
|
+
throw new CliError("\uC2B9\uC778 \uD398\uC774\uC9C0 \uC8FC\uC18C\uAC00 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.", "INVALID_RESPONSE", 0);
|
|
882
|
+
}
|
|
883
|
+
if (verification.protocol !== "https:" || !["clack.kr", "dev.clack.kr", "www.clack.kr"].includes(verification.hostname) || verification.username || verification.password || verification.port) throw new CliError("\uC2B9\uC778 \uD398\uC774\uC9C0 \uC8FC\uC18C\uAC00 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.", "INVALID_RESPONSE", 0);
|
|
884
|
+
return verification;
|
|
885
|
+
}
|
|
886
|
+
function validateFreshDeviceRequest(request) {
|
|
887
|
+
if (!/^dvc_[a-f0-9]{64}$/.test(request.device_code) || !/^[A-Z2-9]{4}-[A-Z2-9]{4}$/.test(request.user_code) || !Number.isFinite(request.expires_in) || request.expires_in <= 0 || request.expires_in > 600 || !Number.isFinite(request.interval) || request.interval < 1) throw new CliError("\uC2B9\uC778 \uC694\uCCAD \uC751\uB2F5\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.", "INVALID_RESPONSE", 0);
|
|
888
|
+
return validateVerificationUrl(request.verification_uri_complete);
|
|
889
|
+
}
|
|
890
|
+
function validatePendingRequest(pending) {
|
|
891
|
+
if (!/^dvc_[a-f0-9]{64}$/.test(pending.device_code) || !/^[A-Z2-9]{4}-[A-Z2-9]{4}$/.test(pending.user_code) || !Number.isFinite(pending.interval) || pending.interval < 1 || !Number.isFinite(Date.parse(pending.expires_at))) {
|
|
892
|
+
throw new CliError("\uC800\uC7A5\uB41C \uB85C\uADF8\uC778 \uC694\uCCAD \uC815\uBCF4\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. clack login\uC73C\uB85C \uC0C8\uB85C \uC2DC\uC791\uD558\uC138\uC694.", "CONFIG_UNSAFE");
|
|
893
|
+
}
|
|
894
|
+
return validateVerificationUrl(pending.verification_uri_complete);
|
|
895
|
+
}
|
|
896
|
+
function newPendingRequest(request, scopes, baseUrl) {
|
|
897
|
+
return {
|
|
898
|
+
device_code: request.device_code,
|
|
899
|
+
user_code: request.user_code,
|
|
900
|
+
verification_uri: request.verification_uri,
|
|
901
|
+
verification_uri_complete: request.verification_uri_complete,
|
|
902
|
+
interval: request.interval,
|
|
903
|
+
scopes,
|
|
904
|
+
base_url: baseUrl,
|
|
905
|
+
expires_at: new Date(Date.now() + request.expires_in * 1e3).toISOString(),
|
|
906
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
function remainingDeviceRequest(pending) {
|
|
910
|
+
return {
|
|
911
|
+
device_code: pending.device_code,
|
|
912
|
+
user_code: pending.user_code,
|
|
913
|
+
verification_uri: pending.verification_uri,
|
|
914
|
+
verification_uri_complete: pending.verification_uri_complete,
|
|
915
|
+
interval: pending.interval,
|
|
916
|
+
expires_in: Math.max(0, Math.floor((Date.parse(pending.expires_at) - Date.now()) / 1e3))
|
|
917
|
+
};
|
|
918
|
+
}
|
|
919
|
+
async function announceDeviceCode(verification, userCode, opts, json2) {
|
|
920
|
+
process.stderr.write(redactText(`\uC571 \uB9C8\uC774\uD398\uC774\uC9C0 \u2192 \uACC4\uC815 \u2192 \uB0B4 \uC815\uBCF4 \uC218\uC815\uD558\uAE30 \u2192 \uC678\uBD80 \uB3C4\uAD6C \uC5F0\uACB0 \u2192 \uCF54\uB4DC\uB85C \uC2B9\uC778\uC5D0\uC11C ${userCode} \uC785\uB825
|
|
921
|
+
${verification.href}
|
|
922
|
+
`));
|
|
923
|
+
if (opts.qr !== false && process.stderr.isTTY && !json2) qr.generate(verification.href, { small: true }, (value2) => process.stderr.write(value2 + "\n"));
|
|
924
|
+
if (opts.browser && process.stdin.isTTY && !json2) await open2(verification.href).catch(() => process.stderr.write("\uBE0C\uB77C\uC6B0\uC800\uB97C \uC5F4\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uC704 \uC8FC\uC18C\uB098 \uC571\uC5D0\uC11C \uC2B9\uC778\uD558\uC138\uC694.\n"));
|
|
925
|
+
}
|
|
849
926
|
async function tokenInput(option) {
|
|
850
927
|
if (typeof option === "string") return validateToken(option);
|
|
851
928
|
if (process.env.CLACK_TOKEN) return validateToken(process.env.CLACK_TOKEN);
|
|
@@ -858,11 +935,12 @@ async function tokenInput(option) {
|
|
|
858
935
|
return validateToken(value2);
|
|
859
936
|
}
|
|
860
937
|
function registerAuthCommands(program2, runtime2, store2) {
|
|
861
|
-
runtime2.action(program2.command("login").description("\uC571\uC5D0\uC11C \uC2B9\uC778\uD558\uAC70\uB098 \uAC1C\uC778 \uC561\uC138\uC2A4 \uD1A0\uD070\uC73C\uB85C \uC5F0\uACB0").option("--token [pat]", "\uD1A0\uD070 \uC785\uB825. \uAC12\uC744 \uC0DD\uB7B5\uD558\uBA74 \uC228\uAE40 \uD504\uB86C\uD504\uD2B8 \uB610\uB294 \uD45C\uC900 \uC785\uB825 \uC0AC\uC6A9").option("--scopes <scopes>", "\uC694\uCCAD \uAD8C\uD55C (\uC27C\uD45C \uAD6C\uBD84)", "profile:read").option("--no-browser", "\uBE0C\uB77C\uC6B0\uC800 \uC790\uB3D9 \uC5F4\uAE30 \uC0DD\uB7B5").option("--no-qr", "QR \uCF54\uB4DC \uCD9C\uB825 \uC0DD\uB7B5 (\uC5D0\uC774\uC804\uD2B8\xB7\uBE44\uB300\uD654\uD615 \uD658\uACBD\uC5D0 \uC801\uD569)").option("--label <name>", "\uAE30\uAE30 \uC774\uB984", hostname()), async (ctx, _args, opts) => {
|
|
938
|
+
runtime2.action(program2.command("login").description("\uC571\uC5D0\uC11C \uC2B9\uC778\uD558\uAC70\uB098 \uAC1C\uC778 \uC561\uC138\uC2A4 \uD1A0\uD070\uC73C\uB85C \uC5F0\uACB0").option("--token [pat]", "\uD1A0\uD070 \uC785\uB825. \uAC12\uC744 \uC0DD\uB7B5\uD558\uBA74 \uC228\uAE40 \uD504\uB86C\uD504\uD2B8 \uB610\uB294 \uD45C\uC900 \uC785\uB825 \uC0AC\uC6A9").option("--scopes <scopes>", "\uC694\uCCAD \uAD8C\uD55C (\uC27C\uD45C \uAD6C\uBD84). product \uAC19\uC740 \uBB36\uC74C \uC774\uB984\uC740 :read\xB7:write\uB9CC \uC694\uCCAD\uD558\uBA70, \uC81C\uCD9C\xB7\uAC8C\uC2DC\uC5D0 \uD544\uC694\uD55C *:publish\uB294 \uB530\uB85C \uC801\uB294\uB2E4", "profile:read").option("--no-browser", "\uBE0C\uB77C\uC6B0\uC800 \uC790\uB3D9 \uC5F4\uAE30 \uC0DD\uB7B5").option("--no-qr", "QR \uCF54\uB4DC \uCD9C\uB825 \uC0DD\uB7B5 (\uC5D0\uC774\uC804\uD2B8\xB7\uBE44\uB300\uD654\uD615 \uD658\uACBD\uC5D0 \uC801\uD569)").option("--label <name>", "\uAE30\uAE30 \uC774\uB984", hostname()).option("--no-wait", "\uCF54\uB4DC\uB9CC \uBC1C\uAE09\uD558\uACE0 \uC2B9\uC778\uC744 \uAE30\uB2E4\uB9AC\uC9C0 \uC54A\uC74C (\uAC19\uC740 \uD134\uC744 \uB05D\uB0B4\uC57C \uD558\uB294 \uC5D0\uC774\uC804\uD2B8\uC5D0 \uC801\uD569, \uC774\uD6C4 clack login --resume\uC73C\uB85C \uC774\uC5B4\uBC1B\uC74C)").option("--resume", "\uC774\uC804\uC5D0 \uBC1C\uAE09\uD588\uC9C0\uB9CC \uC544\uC9C1 \uB9CC\uB8CC\uB418\uC9C0 \uC54A\uC740 \uC694\uCCAD\uC744 \uC774\uC5B4\uC11C \uAE30\uB2E4\uB9BC (\uC0C8 \uCF54\uB4DC\uB97C \uBC1C\uAE09\uD558\uC9C0 \uC54A\uC74C)"), async (ctx, _args, opts) => {
|
|
862
939
|
if (ctx.options.dryRun) throw new CliError("login\uC740 --dry-run\uC744 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.");
|
|
863
940
|
const resolved = await store2.resolve(ctx.options, true);
|
|
864
941
|
let credential;
|
|
865
942
|
if (opts.token !== void 0 || process.env.CLACK_TOKEN) {
|
|
943
|
+
if (opts.resume) throw new CliError("--resume\uC740 --token\uACFC \uD568\uAED8 \uC4F8 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
866
944
|
const token = await tokenInput(opts.token);
|
|
867
945
|
const api = new ApiClient({ baseUrl: resolved.baseUrl, token, verbose: ctx.options.verbose });
|
|
868
946
|
const row = await currentToken(api, token);
|
|
@@ -873,36 +951,77 @@ function registerAuthCommands(program2, runtime2, store2) {
|
|
|
873
951
|
credential.user = { id: result.data.id, name: result.data.name, avatar: result.data.avatar };
|
|
874
952
|
}
|
|
875
953
|
} else {
|
|
876
|
-
|
|
877
|
-
if (
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
954
|
+
let pending;
|
|
955
|
+
if (opts.resume) {
|
|
956
|
+
const stored = await store2.getPendingDeviceRequest(resolved.profile);
|
|
957
|
+
if (!stored) throw new CliError("\uB300\uAE30 \uC911\uC778 \uB85C\uADF8\uC778 \uC694\uCCAD\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. clack login\uC73C\uB85C \uC0C8\uB85C \uC2DC\uC791\uD558\uC138\uC694.", "DEVICE_REQUEST_NOT_FOUND", 404);
|
|
958
|
+
if ((ctx.options.env || ctx.options.baseUrl) && resolved.baseUrl !== stored.base_url) {
|
|
959
|
+
throw new CliError(`\uB300\uAE30 \uC911\uC778 \uC694\uCCAD\uC740 ${stored.base_url}\uC5D0 \uBC1C\uAE09\uB418\uC5C8\uC2B5\uB2C8\uB2E4. --env\xB7--base-url \uC5C6\uC774 clack login --resume\uC744 \uC2E4\uD589\uD558\uC138\uC694.`, "DEVICE_REQUEST_ORIGIN_MISMATCH", 401);
|
|
960
|
+
}
|
|
961
|
+
pending = stored;
|
|
962
|
+
} else {
|
|
963
|
+
const scopes = parseScopes(opts.scopes);
|
|
964
|
+
if (typeof opts.label !== "string" || !opts.label.trim() || opts.label.length > 120) throw new CliError("\uAE30\uAE30 \uC774\uB984\uC740 1~120\uC790\uC5EC\uC57C \uD569\uB2C8\uB2E4.");
|
|
965
|
+
const { data: request } = await ctx.api.request("POST", "/v4/auth/device/code", { auth: false, body: {
|
|
966
|
+
client_name: "cli",
|
|
967
|
+
client_version: VERSION,
|
|
968
|
+
device_name: opts.label,
|
|
969
|
+
scopes
|
|
970
|
+
} });
|
|
971
|
+
rememberSecret(request.device_code);
|
|
972
|
+
validateFreshDeviceRequest(request);
|
|
973
|
+
pending = newPendingRequest(request, scopes, resolved.baseUrl);
|
|
974
|
+
await store2.savePendingDeviceRequest(resolved.profile, pending);
|
|
975
|
+
}
|
|
976
|
+
rememberSecret(pending.device_code);
|
|
977
|
+
const verification = validatePendingRequest(pending);
|
|
978
|
+
await announceDeviceCode(verification, pending.user_code, opts, Boolean(ctx.options.json));
|
|
979
|
+
if (opts.wait === false) {
|
|
980
|
+
const expiry = formatInstant(pending.expires_at, "utc-v1", ctx.options.time ?? "local");
|
|
981
|
+
if (ctx.options.json) {
|
|
982
|
+
ctx.output({
|
|
983
|
+
status: "pending",
|
|
984
|
+
user_code: pending.user_code,
|
|
985
|
+
verification_uri: pending.verification_uri,
|
|
986
|
+
verification_uri_complete: pending.verification_uri_complete,
|
|
987
|
+
expires_at: pending.expires_at,
|
|
988
|
+
interval: pending.interval,
|
|
989
|
+
message: "\uC571\uC5D0\uC11C \uC2B9\uC778\uD55C \uB4A4 \uC774\uC5B4\uC11C \uBC1B\uC73C\uB824\uBA74 clack login --resume\uC744 \uC2E4\uD589\uD558\uC138\uC694."
|
|
990
|
+
});
|
|
991
|
+
} else {
|
|
992
|
+
process.stdout.write(redactText([
|
|
993
|
+
`\uC2B9\uC778\uC744 \uAE30\uB2E4\uB9AC\uC9C0 \uC54A\uACE0 \uBC18\uD658\uD569\uB2C8\uB2E4. (\uD504\uB85C\uD544: ${resolved.profile}, \uC8FC\uC18C: ${pending.base_url})`,
|
|
994
|
+
`\uCF54\uB4DC\uB294 ${expiry}\uAE4C\uC9C0 \uC720\uD6A8\uD569\uB2C8\uB2E4.`,
|
|
995
|
+
"\uC571\uC5D0\uC11C \uC2B9\uC778\uD55C \uB4A4 \uC774\uC5B4\uC11C \uBC1B\uC73C\uB824\uBA74: clack login --resume"
|
|
996
|
+
].join("\n") + "\n"));
|
|
997
|
+
}
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
1000
|
+
const api = opts.resume ? new ApiClient({ baseUrl: pending.base_url, verbose: ctx.options.verbose }) : ctx.api;
|
|
1001
|
+
let grant;
|
|
1002
|
+
try {
|
|
1003
|
+
grant = await pollDevice(api, remainingDeviceRequest(pending));
|
|
1004
|
+
} catch (error2) {
|
|
1005
|
+
if (error2 instanceof CliError && TERMINAL_DEVICE_FAILURE_CODES.includes(error2.code)) {
|
|
1006
|
+
await store2.removePendingDeviceRequest(resolved.profile).catch(() => {
|
|
1007
|
+
});
|
|
1008
|
+
}
|
|
1009
|
+
throw error2;
|
|
1010
|
+
}
|
|
894
1011
|
const token = validateToken(grant.token);
|
|
895
|
-
if (!Array.isArray(grant.scopes) || grant.scopes.some((scope) => !scopes.includes(scope)) || !Number.isFinite(Date.parse(grant.expires_at)) || !grant.user || !Number.isSafeInteger(grant.user.id)) throw new CliError("\uAD50\uBD80 \uC751\uB2F5\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uC571\uC5D0\uC11C \uC5F0\uACB0 \uBAA9\uB85D\uC744 \uD655\uC778\uD558\uC138\uC694.", "INVALID_RESPONSE", 0);
|
|
896
|
-
credential = { token, base_url:
|
|
1012
|
+
if (!Array.isArray(grant.scopes) || grant.scopes.some((scope) => !pending.scopes.includes(scope)) || !Number.isFinite(Date.parse(grant.expires_at)) || !grant.user || !Number.isSafeInteger(grant.user.id)) throw new CliError("\uAD50\uBD80 \uC751\uB2F5\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uC571\uC5D0\uC11C \uC5F0\uACB0 \uBAA9\uB85D\uC744 \uD655\uC778\uD558\uC138\uC694.", "INVALID_RESPONSE", 0);
|
|
1013
|
+
credential = { token, base_url: pending.base_url, scopes: grant.scopes, expires_at: grant.expires_at, user: grant.user };
|
|
1014
|
+
await store2.removePendingDeviceRequest(resolved.profile).catch(() => {
|
|
1015
|
+
});
|
|
897
1016
|
}
|
|
898
1017
|
await store2.saveCredential(resolved.profile, credential);
|
|
899
|
-
await store2.setConfig(resolved.profile, "base_url",
|
|
1018
|
+
await store2.setConfig(resolved.profile, "base_url", credential.base_url);
|
|
900
1019
|
if (ctx.options.json) {
|
|
901
1020
|
ctx.output({ profile: resolved.profile, user: credential.user ?? null, scopes: credential.scopes, expires_at: credential.expires_at, message: "\uC5F0\uACB0\uB418\uC5C8\uC2B5\uB2C8\uB2E4." });
|
|
902
1021
|
} else {
|
|
903
1022
|
const expiry = formatInstant(credential.expires_at, "utc-v1", ctx.options.time ?? "local");
|
|
904
1023
|
process.stdout.write(redactText([
|
|
905
|
-
`\uC5F0\uACB0\uB418\uC5C8\uC2B5\uB2C8\uB2E4. (\uD504\uB85C\uD544: ${resolved.profile}, \uC8FC\uC18C: ${
|
|
1024
|
+
`\uC5F0\uACB0\uB418\uC5C8\uC2B5\uB2C8\uB2E4. (\uD504\uB85C\uD544: ${resolved.profile}, \uC8FC\uC18C: ${credential.base_url})`,
|
|
906
1025
|
credential.user ? `\uC0AC\uC6A9\uC790: ${credential.user.name ?? "\uC774\uB984 \uC5C6\uC74C"} (#${credential.user.id})` : "\uC0AC\uC6A9\uC790: profile:read \uAD8C\uD55C\uC774 \uC5C6\uC5B4 \uC870\uD68C\uD558\uC9C0 \uC54A\uC74C",
|
|
907
1026
|
`\uAD8C\uD55C: ${credential.scopes.join(", ")}`,
|
|
908
1027
|
`\uB9CC\uB8CC: ${expiry}`
|
|
@@ -1143,9 +1262,9 @@ function isRemoteImage(value2) {
|
|
|
1143
1262
|
return false;
|
|
1144
1263
|
}
|
|
1145
1264
|
}
|
|
1146
|
-
function dimensions(
|
|
1265
|
+
function dimensions(bytes2) {
|
|
1147
1266
|
try {
|
|
1148
|
-
const result = imageSize(
|
|
1267
|
+
const result = imageSize(bytes2);
|
|
1149
1268
|
if (!result.width || !result.height || result.width > 2e4 || result.height > 2e4 || !result.type || !MIME[result.type]) throw new Error();
|
|
1150
1269
|
return { width: result.width, height: result.height, mime: MIME[result.type] };
|
|
1151
1270
|
} catch {
|
|
@@ -1172,26 +1291,26 @@ async function prepareImage(source, baseDir = process.cwd(), measureRemote = fal
|
|
|
1172
1291
|
} finally {
|
|
1173
1292
|
await reader.cancel();
|
|
1174
1293
|
}
|
|
1175
|
-
const
|
|
1294
|
+
const bytes3 = new Uint8Array(size);
|
|
1176
1295
|
let offset = 0;
|
|
1177
1296
|
for (const chunk of chunks) {
|
|
1178
|
-
|
|
1297
|
+
bytes3.set(chunk, offset);
|
|
1179
1298
|
offset += chunk.length;
|
|
1180
1299
|
}
|
|
1181
|
-
return { source, ...dimensions(
|
|
1300
|
+
return { source, ...dimensions(bytes3) };
|
|
1182
1301
|
}
|
|
1183
1302
|
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.");
|
|
1184
1303
|
const path = resolve(baseDir, source);
|
|
1185
|
-
let
|
|
1304
|
+
let bytes2;
|
|
1186
1305
|
try {
|
|
1187
1306
|
const info = await stat(path);
|
|
1188
1307
|
if (!info.isFile() || info.size === 0 || info.size > MAX_IMAGE_BYTES) throw new Error();
|
|
1189
|
-
|
|
1190
|
-
if (
|
|
1308
|
+
bytes2 = await readFile5(path);
|
|
1309
|
+
if (bytes2.length > MAX_IMAGE_BYTES) throw new Error();
|
|
1191
1310
|
} catch {
|
|
1192
1311
|
throw new CliError(`10MB \uC774\uD558 \uC774\uBBF8\uC9C0 \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${path}`);
|
|
1193
1312
|
}
|
|
1194
|
-
return { source: path, bytes, ...dimensions(
|
|
1313
|
+
return { source: path, bytes: bytes2, ...dimensions(bytes2) };
|
|
1195
1314
|
}
|
|
1196
1315
|
async function uploadPrepared(ctx, image2) {
|
|
1197
1316
|
if (!image2.bytes) return { originalUrl: image2.source };
|
|
@@ -1879,6 +1998,11 @@ import { createInterface } from "readline";
|
|
|
1879
1998
|
import { z as z10 } from "zod";
|
|
1880
1999
|
|
|
1881
2000
|
// src/commands/skill.ts
|
|
2001
|
+
import { spawn } from "child_process";
|
|
2002
|
+
import { existsSync } from "fs";
|
|
2003
|
+
import { writeFile as writeFile2 } from "fs/promises";
|
|
2004
|
+
import { createRequire } from "module";
|
|
2005
|
+
import { join as join3, resolve as resolve8 } from "path";
|
|
1882
2006
|
import { z as z9 } from "zod";
|
|
1883
2007
|
|
|
1884
2008
|
// src/commands/content.ts
|
|
@@ -1915,15 +2039,15 @@ var CAPABILITIES = /* @__PURE__ */ new Set([
|
|
|
1915
2039
|
function isObject(value2) {
|
|
1916
2040
|
return typeof value2 === "object" && value2 !== null && !Array.isArray(value2);
|
|
1917
2041
|
}
|
|
1918
|
-
function parseObject(
|
|
2042
|
+
function parseObject(bytes2) {
|
|
1919
2043
|
try {
|
|
1920
|
-
const value2 = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(
|
|
2044
|
+
const value2 = JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes2));
|
|
1921
2045
|
return isObject(value2) ? value2 : null;
|
|
1922
2046
|
} catch {
|
|
1923
2047
|
return null;
|
|
1924
2048
|
}
|
|
1925
2049
|
}
|
|
1926
|
-
function inspectContentPackage(
|
|
2050
|
+
function inspectContentPackage(bytes2) {
|
|
1927
2051
|
const warnings = [];
|
|
1928
2052
|
const paths = /* @__PURE__ */ new Set();
|
|
1929
2053
|
const profileFiles = /* @__PURE__ */ new Set();
|
|
@@ -1942,7 +2066,7 @@ function inspectContentPackage(bytes) {
|
|
|
1942
2066
|
let tooLargeExpanded = false;
|
|
1943
2067
|
let files;
|
|
1944
2068
|
try {
|
|
1945
|
-
files = unzipSync(
|
|
2069
|
+
files = unzipSync(bytes2, { filter(entry) {
|
|
1946
2070
|
entries++;
|
|
1947
2071
|
if (entries > MAX_FILES) {
|
|
1948
2072
|
tooManyFiles = true;
|
|
@@ -2029,27 +2153,27 @@ async function prepareContentFile(source) {
|
|
|
2029
2153
|
const file = resolve6(source);
|
|
2030
2154
|
const extension = extname(file).toLowerCase();
|
|
2031
2155
|
if (![".html", ".htm", ".zip"].includes(extension)) throw new CliError("HTML \uB610\uB294 ZIP \uD30C\uC77C\uC744 \uC9C0\uC815\uD558\uC138\uC694.");
|
|
2032
|
-
let
|
|
2156
|
+
let bytes2;
|
|
2033
2157
|
try {
|
|
2034
2158
|
const info = await stat2(file);
|
|
2035
2159
|
if (!info.isFile() || info.size === 0 || info.size > MAX_BYTES) throw new Error();
|
|
2036
|
-
|
|
2037
|
-
if (!
|
|
2160
|
+
bytes2 = await readFile8(file);
|
|
2161
|
+
if (!bytes2.length || bytes2.length > MAX_BYTES) throw new Error();
|
|
2038
2162
|
} catch {
|
|
2039
2163
|
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.");
|
|
2040
2164
|
}
|
|
2041
2165
|
return {
|
|
2042
|
-
bytes,
|
|
2043
|
-
warnings: extension === ".zip" ? inspectContentPackage(
|
|
2166
|
+
bytes: bytes2,
|
|
2167
|
+
warnings: extension === ".zip" ? inspectContentPackage(bytes2) : [],
|
|
2044
2168
|
metadata: {
|
|
2045
2169
|
filename: basename3(file),
|
|
2046
2170
|
content_type: extension === ".zip" ? "application/zip" : "text/html",
|
|
2047
|
-
byte_size:
|
|
2048
|
-
sha256: createHash2("sha256").update(
|
|
2171
|
+
byte_size: bytes2.length,
|
|
2172
|
+
sha256: createHash2("sha256").update(bytes2).digest("hex")
|
|
2049
2173
|
}
|
|
2050
2174
|
};
|
|
2051
2175
|
}
|
|
2052
|
-
async function putContentFile(session2,
|
|
2176
|
+
async function putContentFile(session2, bytes2, fetcher = fetch) {
|
|
2053
2177
|
uuid(session2.upload_id);
|
|
2054
2178
|
let url;
|
|
2055
2179
|
try {
|
|
@@ -2060,7 +2184,7 @@ async function putContentFile(session2, bytes, fetcher = fetch) {
|
|
|
2060
2184
|
}
|
|
2061
2185
|
const contentType = session2.headers?.["Content-Type"];
|
|
2062
2186
|
const checksum = session2.headers?.["x-amz-checksum-sha256"];
|
|
2063
|
-
if (!["text/html", "application/zip"].includes(contentType ?? "") || checksum !== createHash2("sha256").update(
|
|
2187
|
+
if (!["text/html", "application/zip"].includes(contentType ?? "") || checksum !== createHash2("sha256").update(bytes2).digest("base64")) {
|
|
2064
2188
|
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);
|
|
2065
2189
|
}
|
|
2066
2190
|
rememberSecret(session2.upload_url);
|
|
@@ -2068,7 +2192,7 @@ async function putContentFile(session2, bytes, fetcher = fetch) {
|
|
|
2068
2192
|
const result = await fetcher(url, {
|
|
2069
2193
|
method: "PUT",
|
|
2070
2194
|
headers: { "Content-Type": contentType, "x-amz-checksum-sha256": checksum },
|
|
2071
|
-
body: new Uint8Array(
|
|
2195
|
+
body: new Uint8Array(bytes2),
|
|
2072
2196
|
redirect: "error",
|
|
2073
2197
|
signal: AbortSignal.timeout(12e4)
|
|
2074
2198
|
});
|
|
@@ -2086,15 +2210,15 @@ var keyId = (value2) => {
|
|
|
2086
2210
|
return id2;
|
|
2087
2211
|
};
|
|
2088
2212
|
function registerContentCommands(program2, runtime2) {
|
|
2089
|
-
const content = program2.command("content").description("HTML \uCF58\uD150\uCE20 \uB4F1\uB85D\xB7\uC571 \uD655\uC778\xB7\uC2EC\uC0AC \uAD00\uB9AC");
|
|
2090
|
-
runtime2.action(content.command("config"), async (ctx) => ctx.output(await ctx.api.request("GET", "/v4/creator/config", { auth: false })));
|
|
2091
|
-
runtime2.action(content.command("list"), async (ctx) => ctx.output(await ctx.api.request("GET", "/v4/creator/contents")));
|
|
2092
|
-
runtime2.action(content.command("status <id>"), async (ctx, [id2]) => ctx.output(await ctx.api.request("GET", `/v4/creator/contents/${uuid(id2)}`)));
|
|
2093
|
-
runtime2.action(content.command("create").requiredOption("--title <text>", "\uC81C\uBAA9").option("--description <text>", "\uC124\uBA85", "").option("--kind <kind>", "html|gallery|slideshow|video", "html").requiredOption("--policy-version <version>", "\uB3D9\uC758\uD55C \uCF58\uD150\uCE20 \uC81C\uACF5 \uC815\uCC45 \uBC84\uC804"), async (ctx, _args, opts) => {
|
|
2213
|
+
const content = program2.command("content").description("HTML \uCF58\uD150\uCE20 \uB4F1\uB85D\xB7\uC571 \uD655\uC778\xB7\uC2EC\uC0AC \uAD00\uB9AC (creator-content:read\xB7write\xB7publish)");
|
|
2214
|
+
runtime2.action(content.command("config").description("\uCF58\uD150\uCE20 \uAE30\uB2A5 \uC81C\uACF5 \uC5EC\uBD80\xB7\uC124\uC815 \uC870\uD68C (\uB85C\uADF8\uC778 \uBD88\uD544\uC694)"), async (ctx) => ctx.output(await ctx.api.request("GET", "/v4/creator/config", { auth: false })));
|
|
2215
|
+
runtime2.action(content.command("list").description("\uB0B4 \uCF58\uD150\uCE20 \uBAA9\uB85D (creator-content:read)"), async (ctx) => ctx.output(await ctx.api.request("GET", "/v4/creator/contents")));
|
|
2216
|
+
runtime2.action(content.command("status <id>").description("\uCF58\uD150\uCE20\uC640 \uBC84\uC804\uBCC4 \uC5C5\uB85C\uB4DC\xB7\uC2EC\uC0AC\xB7\uACF5\uAC1C \uC0C1\uD0DC (creator-content:read)"), async (ctx, [id2]) => ctx.output(await ctx.api.request("GET", `/v4/creator/contents/${uuid(id2)}`)));
|
|
2217
|
+
runtime2.action(content.command("create").description("\uCF58\uD150\uCE20 \uCD08\uC548 \uB4F1\uB85D (creator-content:write)").requiredOption("--title <text>", "\uC81C\uBAA9").option("--description <text>", "\uC124\uBA85", "").option("--kind <kind>", "html|gallery|slideshow|video", "html").requiredOption("--policy-version <version>", "\uB3D9\uC758\uD55C \uCF58\uD150\uCE20 \uC81C\uACF5 \uC815\uCC45 \uBC84\uC804"), async (ctx, _args, opts) => {
|
|
2094
2218
|
const body = parse(createSchema, { title: opts.title, description: opts.description, kind: opts.kind, policy_version: opts.policyVersion });
|
|
2095
2219
|
await mutate(ctx, "POST", "/v4/creator/contents", body);
|
|
2096
2220
|
});
|
|
2097
|
-
runtime2.action(content.command("upload <id> <file>").addOption(new Option("--header <mode>", "\uD5E4\uB354 \uD45C\uC2DC").choices(["fixed", "scroll_hide", "translucent_scroll_hide", "floating_close"])).addOption(new Option("--color <scheme>", "\uD5E4\uB354 \uC0C9\uC0C1").choices(["light", "dark"])), async (ctx, [id2, file], opts) => {
|
|
2221
|
+
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) => {
|
|
2098
2222
|
const path = `/v4/creator/contents/${uuid(id2)}`;
|
|
2099
2223
|
const prepared = await prepareContentFile(file);
|
|
2100
2224
|
for (const warning of prepared.warnings) process.stderr.write(`\uD328\uD0A4\uC9C0 \uACBD\uACE0: ${warning}
|
|
@@ -2110,39 +2234,39 @@ function registerContentCommands(program2, runtime2) {
|
|
|
2110
2234
|
throw error2;
|
|
2111
2235
|
}
|
|
2112
2236
|
});
|
|
2113
|
-
runtime2.action(content.command("complete <id> <upload-id>"), async (ctx, [id2, uploadId]) => mutate(ctx, "POST", `/v4/creator/contents/${uuid(id2)}/uploads/${uuid(uploadId)}/complete`));
|
|
2114
|
-
runtime2.action(content.command("preview <id> <version-id>"), async (ctx, [id2, versionId]) => ctx.output(await ctx.api.request("GET", `/v4/creator/contents/${uuid(id2)}/versions/${uuid(versionId)}/preview`)));
|
|
2115
|
-
runtime2.action(content.command("submit <id> <version-id>"), 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?"));
|
|
2116
|
-
runtime2.action(content.command("withdraw <id> <version-id>"), 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?"));
|
|
2117
|
-
runtime2.action(content.command("publish <id> <version-id>"), 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?"));
|
|
2118
|
-
runtime2.action(content.command("unpublish <id>"), 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?"));
|
|
2237
|
+
runtime2.action(content.command("complete <id> <upload-id>").description("\uC5C5\uB85C\uB4DC \uC644\uB8CC \uCC98\uB9AC \uC7AC\uC2DC\uB3C4 (creator-content:write)"), async (ctx, [id2, uploadId]) => mutate(ctx, "POST", `/v4/creator/contents/${uuid(id2)}/uploads/${uuid(uploadId)}/complete`));
|
|
2238
|
+
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`)));
|
|
2239
|
+
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?"));
|
|
2240
|
+
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?"));
|
|
2241
|
+
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?"));
|
|
2242
|
+
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?"));
|
|
2119
2243
|
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)");
|
|
2120
|
-
runtime2.action(serverKeys.command("list <id>"), async (ctx, [id2]) => ctx.output(await ctx.api.request("GET", `/v4/creator/contents/${uuid(id2)}/server-keys`)));
|
|
2244
|
+
runtime2.action(serverKeys.command("list <id>").description("\uC11C\uBC84 \uD0A4 \uBAA9\uB85D (platform:read)"), async (ctx, [id2]) => ctx.output(await ctx.api.request("GET", `/v4/creator/contents/${uuid(id2)}/server-keys`)));
|
|
2121
2245
|
for (const name2 of ["issue", "rotate"]) {
|
|
2122
2246
|
runtime2.action(serverKeys.command(name2).description("\uD06C\uB9AC\uC5D0\uC774\uD130 \uC13C\uD130 \uC804\uC6A9 \uC548\uB0B4").argument("[args...]").allowUnknownOption(), async () => {
|
|
2123
2247
|
throw new CliError(SERVER_KEY_CENTER_ONLY, "SERVER_KEY_CENTER_ONLY", 403);
|
|
2124
2248
|
});
|
|
2125
2249
|
}
|
|
2126
|
-
runtime2.action(serverKeys.command("revoke <id> <key-id>"), async (ctx, [id2, keyIdValue]) => mutate(ctx, "DELETE", `/v4/creator/contents/${uuid(id2)}/server-keys/${keyId(keyIdValue)}`, void 0, "\uC774 \uC11C\uBC84 \uD0A4\uB97C \uC989\uC2DC \uD3D0\uAE30\uD560\uAE4C\uC694?"));
|
|
2250
|
+
runtime2.action(serverKeys.command("revoke <id> <key-id>").description("\uC11C\uBC84 \uD0A4 \uC989\uC2DC \uD3D0\uAE30 (platform:write)"), async (ctx, [id2, keyIdValue]) => mutate(ctx, "DELETE", `/v4/creator/contents/${uuid(id2)}/server-keys/${keyId(keyIdValue)}`, void 0, "\uC774 \uC11C\uBC84 \uD0A4\uB97C \uC989\uC2DC \uD3D0\uAE30\uD560\uAE4C\uC694?"));
|
|
2127
2251
|
const shared = content.command("shared").description("\uACF5\uC720 \uBB38\uC11C \uC870\uD68C\xB7\uC228\uAE40\xB7\uC0AD\uC81C (platform:read\xB7platform:write)");
|
|
2128
|
-
runtime2.action(shared.command("collections <id>"), async (ctx, [id2]) => ctx.output(await ctx.api.request("GET", `/v4/creator/contents/${uuid(id2)}/shared-document-collections`)));
|
|
2129
|
-
runtime2.action(shared.command("list <id> <collection>").option("--limit <count>", "1~100\uAC1C, \uAE30\uBCF8 50"), async (ctx, [id2, collectionValue], opts) => ctx.output(await ctx.api.request(
|
|
2252
|
+
runtime2.action(shared.command("collections <id>").description("\uACF5\uC720 \uBB38\uC11C \uCEEC\uB809\uC158 \uC774\uB984 (platform:read)"), async (ctx, [id2]) => ctx.output(await ctx.api.request("GET", `/v4/creator/contents/${uuid(id2)}/shared-document-collections`)));
|
|
2253
|
+
runtime2.action(shared.command("list <id> <collection>").description("\uACF5\uC720 \uBB38\uC11C \uBAA9\uB85D (platform:read)").option("--limit <count>", "1~100\uAC1C, \uAE30\uBCF8 50"), async (ctx, [id2, collectionValue], opts) => ctx.output(await ctx.api.request(
|
|
2130
2254
|
"GET",
|
|
2131
2255
|
`/v4/creator/contents/${uuid(id2)}/shared-documents/${parse(collection, collectionValue)}`,
|
|
2132
2256
|
{ query: { limit: opts.limit } }
|
|
2133
2257
|
)));
|
|
2134
|
-
runtime2.action(shared.command("get <id> <collection> <key>"), async (ctx, [id2, collectionValue, keyValue]) => ctx.output(await ctx.api.request(
|
|
2258
|
+
runtime2.action(shared.command("get <id> <collection> <key>").description("\uACF5\uC720 \uBB38\uC11C \uD558\uB098 \uC870\uD68C (platform:read)"), async (ctx, [id2, collectionValue, keyValue]) => ctx.output(await ctx.api.request(
|
|
2135
2259
|
"GET",
|
|
2136
2260
|
`/v4/creator/contents/${uuid(id2)}/shared-documents/${parse(collection, collectionValue)}/${parse(documentKey, keyValue)}`
|
|
2137
2261
|
)));
|
|
2138
|
-
runtime2.action(shared.command("hide <id> <collection> <key>"), async (ctx, [id2, collectionValue, keyValue]) => mutate(
|
|
2262
|
+
runtime2.action(shared.command("hide <id> <collection> <key>").description("\uACF5\uC720 \uBB38\uC11C \uC228\uAE40 (platform:write)"), async (ctx, [id2, collectionValue, keyValue]) => mutate(
|
|
2139
2263
|
ctx,
|
|
2140
2264
|
"POST",
|
|
2141
2265
|
`/v4/creator/contents/${uuid(id2)}/shared-documents/${parse(collection, collectionValue)}/${parse(documentKey, keyValue)}/hide`,
|
|
2142
2266
|
void 0,
|
|
2143
2267
|
"\uC774 \uACF5\uC720 \uBB38\uC11C\uB97C \uC228\uAE38\uAE4C\uC694? \uBAA9\uB85D\xB7\uC21C\uC704\uC5D0\uC11C \uC81C\uC678\uB418\uBA70 \uBB38\uC11C \uC790\uCCB4\uB294 \uB0A8\uC2B5\uB2C8\uB2E4."
|
|
2144
2268
|
));
|
|
2145
|
-
runtime2.action(shared.command("delete <id> <collection> <key>"), async (ctx, [id2, collectionValue, keyValue]) => mutate(
|
|
2269
|
+
runtime2.action(shared.command("delete <id> <collection> <key>").description("\uACF5\uC720 \uBB38\uC11C \uC601\uAD6C \uC0AD\uC81C (platform:write)"), async (ctx, [id2, collectionValue, keyValue]) => mutate(
|
|
2146
2270
|
ctx,
|
|
2147
2271
|
"DELETE",
|
|
2148
2272
|
`/v4/creator/contents/${uuid(id2)}/shared-documents/${parse(collection, collectionValue)}/${parse(documentKey, keyValue)}`,
|
|
@@ -2152,36 +2276,155 @@ function registerContentCommands(program2, runtime2) {
|
|
|
2152
2276
|
}
|
|
2153
2277
|
|
|
2154
2278
|
// src/vendor/clack-api-v4-contracts/authoring-semantics.mjs
|
|
2155
|
-
function
|
|
2279
|
+
function walkDataSchema(schema, visit) {
|
|
2280
|
+
const defs = schema?.$defs ?? {};
|
|
2281
|
+
const step = (node, info) => {
|
|
2282
|
+
if (!node || typeof node !== "object" || Array.isArray(node)) return;
|
|
2283
|
+
if (typeof node.$ref === "string") {
|
|
2284
|
+
const key = node.$ref.startsWith("#/$defs/") ? node.$ref.slice("#/$defs/".length) : null;
|
|
2285
|
+
if (key === null || !Object.hasOwn(defs, key) || info.refs.includes(key)) {
|
|
2286
|
+
visit(node, { ...info, cycle: key !== null && info.refs.includes(key), unresolved: key === null || !Object.hasOwn(defs, key) });
|
|
2287
|
+
return;
|
|
2288
|
+
}
|
|
2289
|
+
visit(node, info);
|
|
2290
|
+
step(defs[key], { ...info, refs: [...info.refs, key] });
|
|
2291
|
+
return;
|
|
2292
|
+
}
|
|
2293
|
+
visit(node, info);
|
|
2294
|
+
for (const branch of Array.isArray(node.oneOf) ? node.oneOf : []) step(branch, { ...info, branch: true });
|
|
2295
|
+
for (const [name2, child] of Object.entries(node.properties && typeof node.properties === "object" ? node.properties : {})) {
|
|
2296
|
+
step(child, {
|
|
2297
|
+
...info,
|
|
2298
|
+
pointer: `${info.pointer}/${name2}`,
|
|
2299
|
+
depth: info.depth + 1,
|
|
2300
|
+
root: info.depth === 0 ? name2 : info.root,
|
|
2301
|
+
visibility: info.depth === 0 ? child?.["x-clack-visibility"] : info.visibility,
|
|
2302
|
+
branch: false
|
|
2303
|
+
});
|
|
2304
|
+
}
|
|
2305
|
+
if (node.items && typeof node.items === "object") step(node.items, { ...info, pointer: `${info.pointer}/*`, depth: info.depth + 1, branch: false });
|
|
2306
|
+
};
|
|
2307
|
+
step(schema, { pointer: "", depth: 0, root: null, visibility: null, refs: [], branch: false });
|
|
2308
|
+
}
|
|
2309
|
+
function dataRootField(schema, name2) {
|
|
2310
|
+
const field2 = schema?.properties?.[name2];
|
|
2311
|
+
if (!field2 || typeof field2 !== "object") return null;
|
|
2312
|
+
if (typeof field2.$ref === "string" && field2.$ref.startsWith("#/$defs/")) {
|
|
2313
|
+
const target = schema.$defs?.[field2.$ref.slice("#/$defs/".length)];
|
|
2314
|
+
return target && typeof target === "object" ? { ...target, "x-clack-visibility": field2["x-clack-visibility"] } : null;
|
|
2315
|
+
}
|
|
2316
|
+
return field2;
|
|
2317
|
+
}
|
|
2318
|
+
function inspectEditorSemantics(skill, data, fail2) {
|
|
2319
|
+
const editor = skill.authoring?.editor;
|
|
2320
|
+
if (!editor) return;
|
|
2321
|
+
const permissions = new Set(editor.permissions ?? []);
|
|
2322
|
+
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.");
|
|
2323
|
+
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.");
|
|
2324
|
+
const imageTool = (skill.authoring?.tools ?? []).find((tool) => tool.tool === "image.generate");
|
|
2325
|
+
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.");
|
|
2326
|
+
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.");
|
|
2327
|
+
if (!data) return;
|
|
2328
|
+
let assets = 0;
|
|
2329
|
+
let suggest = 0;
|
|
2330
|
+
walkDataSchema(data, (node) => {
|
|
2331
|
+
if (node["x-clack-asset"]) assets++;
|
|
2332
|
+
if (node["x-clack-ai"]?.suggest === true) suggest++;
|
|
2333
|
+
});
|
|
2334
|
+
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.");
|
|
2335
|
+
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.");
|
|
2336
|
+
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.");
|
|
2337
|
+
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.");
|
|
2338
|
+
const metadata = skill.output?.metadata ?? {};
|
|
2339
|
+
for (const key of ["title", "description"]) {
|
|
2340
|
+
if (metadata[key] === void 0) continue;
|
|
2341
|
+
const field2 = dataRootField(data, metadata[key]);
|
|
2342
|
+
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.");
|
|
2343
|
+
}
|
|
2344
|
+
for (const key of ["thumbnail", "thumbnail_fallback"]) {
|
|
2345
|
+
if (metadata[key] === void 0) continue;
|
|
2346
|
+
const field2 = dataRootField(data, metadata[key]);
|
|
2347
|
+
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).");
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
var INTRO_MAX_IMAGES = 8;
|
|
2351
|
+
var INTRO_CREATOR_COMMENT_MAX = 500;
|
|
2352
|
+
var INTRO_DESCRIPTION_MAX = 5e3;
|
|
2353
|
+
var INTRO_TEXT_WIDGETS = /* @__PURE__ */ new Set(["text", "textarea"]);
|
|
2354
|
+
function introFieldLabel(name2, field2) {
|
|
2355
|
+
const title = typeof field2?.title === "string" ? field2.title.normalize("NFC").trim() : "";
|
|
2356
|
+
return title || name2;
|
|
2357
|
+
}
|
|
2358
|
+
function inspectIntroSemantics(skill, form, slots, fail2) {
|
|
2359
|
+
const intro = skill.output?.intro;
|
|
2360
|
+
if (!intro) return;
|
|
2361
|
+
let images = 0;
|
|
2362
|
+
for (const name2 of intro.images ?? []) {
|
|
2363
|
+
const slot = slots.find((entry) => entry.field === name2);
|
|
2364
|
+
if (!slot) {
|
|
2365
|
+
fail2("/output/intro/images", "\uC778\uD2B8\uB85C \uC774\uBBF8\uC9C0\uB294 output.asset_slots\uC758 \uD544\uB4DC\uB9CC \uC4F8 \uC218 \uC788\uC2B5\uB2C8\uB2E4.");
|
|
2366
|
+
continue;
|
|
2367
|
+
}
|
|
2368
|
+
images += slot.multiple ? slot.multiple.max : 1;
|
|
2369
|
+
}
|
|
2370
|
+
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.`);
|
|
2371
|
+
if (!form) return;
|
|
2372
|
+
const properties = form.properties ?? {};
|
|
2373
|
+
const textField = (name2) => {
|
|
2374
|
+
const field2 = Object.hasOwn(properties, name2) ? properties[name2] : null;
|
|
2375
|
+
return field2 && field2.type === "string" && INTRO_TEXT_WIDGETS.has(field2["x-clack-widget"]) && Number.isSafeInteger(field2.maxLength) ? field2 : null;
|
|
2376
|
+
};
|
|
2377
|
+
for (const [key, max] of [["creator_comment", INTRO_CREATOR_COMMENT_MAX], ["description", INTRO_DESCRIPTION_MAX]]) {
|
|
2378
|
+
if (intro[key] === void 0) continue;
|
|
2379
|
+
const field2 = textField(intro[key]);
|
|
2380
|
+
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.");
|
|
2381
|
+
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.`);
|
|
2382
|
+
}
|
|
2383
|
+
const fallback = intro.description_fallback ?? [];
|
|
2384
|
+
let composed = 0;
|
|
2385
|
+
for (const name2 of fallback) {
|
|
2386
|
+
const field2 = textField(name2);
|
|
2387
|
+
if (!field2) {
|
|
2388
|
+
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.");
|
|
2389
|
+
continue;
|
|
2390
|
+
}
|
|
2391
|
+
composed += introFieldLabel(name2, field2).length + 1 + field2.maxLength;
|
|
2392
|
+
}
|
|
2393
|
+
composed += Math.max(0, fallback.length - 1) * 2;
|
|
2394
|
+
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.`);
|
|
2395
|
+
}
|
|
2396
|
+
function inspectAuthoringSemantics(skill, form, data) {
|
|
2156
2397
|
const errors = [];
|
|
2157
|
-
const
|
|
2398
|
+
const fail2 = (path, message) => errors.push({ path, message });
|
|
2399
|
+
inspectEditorSemantics(skill, data, fail2);
|
|
2158
2400
|
const tools = skill.authoring?.tools ?? [];
|
|
2159
|
-
if (new Set(tools.map((tool) => tool.tool)).size !== tools.length)
|
|
2160
|
-
for (const tool of tools) if (tool.batch_max > tool.max_calls)
|
|
2401
|
+
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.");
|
|
2402
|
+
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.");
|
|
2161
2403
|
const slots = skill.output?.asset_slots ?? [];
|
|
2162
2404
|
const paths = /* @__PURE__ */ new Set();
|
|
2163
2405
|
const fields3 = /* @__PURE__ */ new Set();
|
|
2164
2406
|
for (const slot of slots) {
|
|
2165
|
-
if (fields3.has(slot.field))
|
|
2407
|
+
if (fields3.has(slot.field)) fail2("/output/asset_slots", "\uC2AC\uB86F \uD544\uB4DC\uB294 \uC911\uBCF5\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
2166
2408
|
fields3.add(slot.field);
|
|
2167
|
-
if (slot.multiple && slot.multiple.min > slot.multiple.max)
|
|
2409
|
+
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.");
|
|
2168
2410
|
for (let index = 0; index < (slot.multiple?.max ?? 1); index++) {
|
|
2169
2411
|
const path = slot.multiple ? slot.multiple.path.replace("{n}", String(index)) : slot.path;
|
|
2170
|
-
if (paths.has(path))
|
|
2412
|
+
if (paths.has(path)) fail2("/output/asset_slots", "\uC2AC\uB86F \uD30C\uC77C \uACBD\uB85C\uB294 \uC911\uBCF5\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
2171
2413
|
paths.add(path);
|
|
2172
2414
|
}
|
|
2173
2415
|
}
|
|
2416
|
+
inspectIntroSemantics(skill, form, slots, fail2);
|
|
2174
2417
|
if (!form) return errors;
|
|
2175
2418
|
const properties = form.properties ?? {};
|
|
2176
2419
|
const exists = (expression, path) => {
|
|
2177
2420
|
const match = /^([a-z][a-z0-9_-]*)(?:\[(\d+)\])?$/.exec(expression);
|
|
2178
2421
|
if (!match || !Object.hasOwn(properties, match[1])) {
|
|
2179
|
-
|
|
2422
|
+
fail2(path, "\uC120\uC5B8\uD55C \uD3FC \uD544\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
2180
2423
|
return;
|
|
2181
2424
|
}
|
|
2182
2425
|
if (match[2] !== void 0) {
|
|
2183
2426
|
const slot = slots.find((value2) => value2.field === match[1]);
|
|
2184
|
-
if (!slot?.multiple || Number(match[2]) >= slot.multiple.max)
|
|
2427
|
+
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.");
|
|
2185
2428
|
}
|
|
2186
2429
|
};
|
|
2187
2430
|
const entity = skill.output?.entity_map;
|
|
@@ -2191,30 +2434,539 @@ function inspectAuthoringSemantics(skill, form) {
|
|
|
2191
2434
|
}
|
|
2192
2435
|
for (const slot of slots) {
|
|
2193
2436
|
const node = properties[slot.field];
|
|
2194
|
-
if (!node || node["x-clack-widget"] !== (slot.multiple ? "image_list" : "image"))
|
|
2437
|
+
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.");
|
|
2195
2438
|
}
|
|
2196
2439
|
for (const [name2, node] of Object.entries(properties)) {
|
|
2197
2440
|
if (node["x-clack-widget"] === "content_ref") {
|
|
2198
|
-
if (node.type !== "string" || !node["x-clack-source"])
|
|
2441
|
+
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.");
|
|
2199
2442
|
for (const target of Object.keys(node["x-clack-source"]?.import ?? {})) {
|
|
2200
2443
|
exists(target, `/properties/${name2}/x-clack-source/import`);
|
|
2201
|
-
if (properties[target]?.["x-clack-widget"] === "content_ref")
|
|
2444
|
+
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.");
|
|
2202
2445
|
}
|
|
2203
|
-
} else if (node["x-clack-source"])
|
|
2204
|
-
if (node["x-clack-widget"] === "image_list" && (node.type !== "array" || node.items?.type !== "object" || !node["x-clack-asset"]))
|
|
2446
|
+
} else if (node["x-clack-source"]) fail2(`/properties/${name2}`, "x-clack-source\uB294 content_ref\uC5D0\uB9CC \uC120\uC5B8\uD569\uB2C8\uB2E4.");
|
|
2447
|
+
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.");
|
|
2205
2448
|
}
|
|
2206
2449
|
return errors;
|
|
2207
2450
|
}
|
|
2208
2451
|
|
|
2209
2452
|
// src/lib/skill-package.ts
|
|
2210
|
-
import { createHash as
|
|
2453
|
+
import { createHash as createHash4 } from "crypto";
|
|
2211
2454
|
import { lstat as lstat4, open as open3, readdir } from "fs/promises";
|
|
2212
2455
|
import { constants } from "fs";
|
|
2213
2456
|
import { basename as basename4, extname as extname2, join as join2, resolve as resolve7 } from "path";
|
|
2214
|
-
import { Ajv2020 } from "ajv/dist/2020.js";
|
|
2457
|
+
import { Ajv2020 as Ajv20202 } from "ajv/dist/2020.js";
|
|
2215
2458
|
import { unzipSync as unzipSync2, zipSync } from "fflate";
|
|
2216
2459
|
import { parseDocument } from "yaml";
|
|
2217
2460
|
|
|
2461
|
+
// src/lib/skill-editor.ts
|
|
2462
|
+
import { createHash as createHash3 } from "crypto";
|
|
2463
|
+
import { Ajv2020 } from "ajv/dist/2020.js";
|
|
2464
|
+
|
|
2465
|
+
// src/vendor/clack-api-v4-contracts/output-data-inspect.mjs
|
|
2466
|
+
var bytes = (value2) => Buffer.byteLength(JSON.stringify(value2), "utf8");
|
|
2467
|
+
var fail = (path, message) => ({ path, message });
|
|
2468
|
+
var OUTPUT_DATA_LIMITS = { schema_bytes: 65536, depth: 6, properties: 400, public_bytes: 262144, private_bytes: 65536, ui_state_bytes: 262144 };
|
|
2469
|
+
var SCALAR_TYPES = /* @__PURE__ */ new Set(["string", "integer", "number", "boolean"]);
|
|
2470
|
+
var SCALAR_KEYS = {
|
|
2471
|
+
string: ["minLength", "maxLength", "format"],
|
|
2472
|
+
number: ["minimum", "maximum"],
|
|
2473
|
+
integer: ["minimum", "maximum"],
|
|
2474
|
+
array: ["items", "minItems", "maxItems", "uniqueItems"],
|
|
2475
|
+
object: ["properties", "required", "additionalProperties"],
|
|
2476
|
+
boolean: []
|
|
2477
|
+
};
|
|
2478
|
+
var TYPED_KEYS = new Set(Object.values(SCALAR_KEYS).flat());
|
|
2479
|
+
function inspectOutputData(schema) {
|
|
2480
|
+
const errors = [];
|
|
2481
|
+
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."));
|
|
2482
|
+
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.`));
|
|
2483
|
+
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."));
|
|
2484
|
+
const defs = schema.$defs ?? {};
|
|
2485
|
+
const resolve9 = (node) => typeof node?.$ref === "string" ? defs[node.$ref.slice("#/$defs/".length)] : node;
|
|
2486
|
+
let properties = 0;
|
|
2487
|
+
const suggestRoots = [];
|
|
2488
|
+
const assetPointers = [];
|
|
2489
|
+
walkDataSchema(schema, (node, info) => {
|
|
2490
|
+
const at = info.pointer || "/";
|
|
2491
|
+
if (info.cycle) {
|
|
2492
|
+
errors.push(fail(at, "\uC21C\uD658 \uCC38\uC870\uB294 \uD5C8\uC6A9\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4."));
|
|
2493
|
+
return;
|
|
2494
|
+
}
|
|
2495
|
+
if (info.unresolved) {
|
|
2496
|
+
errors.push(fail(at, "\uB85C\uCEEC $defs \uCC38\uC870\uB9CC \uD5C8\uC6A9\uD569\uB2C8\uB2E4."));
|
|
2497
|
+
return;
|
|
2498
|
+
}
|
|
2499
|
+
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."));
|
|
2500
|
+
if (info.depth === 0) return;
|
|
2501
|
+
if (typeof node.$ref === "string") {
|
|
2502
|
+
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."));
|
|
2503
|
+
return;
|
|
2504
|
+
}
|
|
2505
|
+
if (!info.branch) properties++;
|
|
2506
|
+
if (Array.isArray(node.oneOf)) {
|
|
2507
|
+
const discriminator = node["x-clack-discriminator"];
|
|
2508
|
+
if (!discriminator) errors.push(fail(at, "oneOf\uC5D0\uB294 x-clack-discriminator\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4."));
|
|
2509
|
+
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."));
|
|
2510
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2511
|
+
for (const raw of node.oneOf) {
|
|
2512
|
+
const branch = resolve9(raw);
|
|
2513
|
+
const tag = branch?.properties?.[discriminator];
|
|
2514
|
+
if (branch?.type !== "object" || typeof tag?.const !== "string" || tag.type !== "string" || !(branch.required ?? []).includes(discriminator)) {
|
|
2515
|
+
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."));
|
|
2516
|
+
} else if (seen.has(tag.const)) errors.push(fail(at, `\uD310\uBCC4\uC790 \uAC12 ${tag.const}\uC774 \uC911\uBCF5\uB429\uB2C8\uB2E4.`));
|
|
2517
|
+
else seen.add(tag.const);
|
|
2518
|
+
}
|
|
2519
|
+
return;
|
|
2520
|
+
}
|
|
2521
|
+
if (node["x-clack-discriminator"] !== void 0) errors.push(fail(at, "x-clack-discriminator\uB294 oneOf\uC5D0\uB9CC \uB461\uB2C8\uB2E4."));
|
|
2522
|
+
if (!node.type) {
|
|
2523
|
+
errors.push(fail(at, "type\uC774 \uD544\uC694\uD569\uB2C8\uB2E4."));
|
|
2524
|
+
return;
|
|
2525
|
+
}
|
|
2526
|
+
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.`));
|
|
2527
|
+
const range = (low, high, label) => {
|
|
2528
|
+
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.`));
|
|
2529
|
+
};
|
|
2530
|
+
range("minLength", "maxLength", "\uAE38\uC774");
|
|
2531
|
+
range("minItems", "maxItems", "\uD56D\uBAA9 \uC218");
|
|
2532
|
+
range("minimum", "maximum", "\uAC12");
|
|
2533
|
+
if (node.type === "string" && typeof node.maxLength !== "number") errors.push(fail(at, "\uBB38\uC790\uC5F4 maxLength\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4."));
|
|
2534
|
+
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."));
|
|
2535
|
+
if (node.type === "array" && node.uniqueItems === true) {
|
|
2536
|
+
let items = node.items;
|
|
2537
|
+
for (let hops = 0; typeof items?.$ref === "string" && hops < 64; hops++) items = resolve9(items);
|
|
2538
|
+
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."));
|
|
2539
|
+
}
|
|
2540
|
+
if (node.type === "object") {
|
|
2541
|
+
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."));
|
|
2542
|
+
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.`));
|
|
2543
|
+
}
|
|
2544
|
+
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."));
|
|
2545
|
+
if (node["x-clack-asset"]) {
|
|
2546
|
+
assetPointers.push(at);
|
|
2547
|
+
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."));
|
|
2548
|
+
if (info.visibility === "private") errors.push(fail(at, "\uBE44\uACF5\uAC1C \uD0A4\uC5D0\uB294 \uC790\uC0B0\uC744 \uB458 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4."));
|
|
2549
|
+
} else if (node.format === "uuid" && node.type !== "string") errors.push(fail(at, "format\uC740 \uBB38\uC790\uC5F4\uC5D0\uB9CC \uC501\uB2C8\uB2E4."));
|
|
2550
|
+
if (node["x-clack-ai"]?.suggest === true) {
|
|
2551
|
+
if (info.visibility !== "public") errors.push(fail(at, "AI \uC81C\uC548\uC740 \uACF5\uAC1C \uD0A4\uC5D0\uB9CC \uCF24 \uC218 \uC788\uC2B5\uB2C8\uB2E4."));
|
|
2552
|
+
suggestRoots.push(at);
|
|
2553
|
+
}
|
|
2554
|
+
});
|
|
2555
|
+
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."));
|
|
2556
|
+
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."));
|
|
2557
|
+
return errors;
|
|
2558
|
+
}
|
|
2559
|
+
var ZWJ_AFTER = new RegExp("\\p{Extended_Pictographic}", "u");
|
|
2560
|
+
|
|
2561
|
+
// src/vendor/clack-api-v4-contracts/schemas/output-data.v1.schema.json
|
|
2562
|
+
var output_data_v1_schema_default = {
|
|
2563
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
2564
|
+
$id: "https://clack.kr/schemas/platform/output-data.v1.schema.json",
|
|
2565
|
+
$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.",
|
|
2566
|
+
title: "\uC2A4\uD0AC \uC5D0\uB514\uD130 \uB370\uC774\uD130 \uBB38\uC11C \uC2A4\uD0A4\uB9C8 \uADDC\uCE59(output-data.v1)",
|
|
2567
|
+
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.",
|
|
2568
|
+
type: "object",
|
|
2569
|
+
additionalProperties: false,
|
|
2570
|
+
properties: {
|
|
2571
|
+
$schema: {
|
|
2572
|
+
const: "https://json-schema.org/draft/2020-12/schema"
|
|
2573
|
+
},
|
|
2574
|
+
title: {
|
|
2575
|
+
type: "string",
|
|
2576
|
+
maxLength: 120
|
|
2577
|
+
},
|
|
2578
|
+
description: {
|
|
2579
|
+
type: "string",
|
|
2580
|
+
maxLength: 1e3
|
|
2581
|
+
},
|
|
2582
|
+
type: {
|
|
2583
|
+
const: "object"
|
|
2584
|
+
},
|
|
2585
|
+
additionalProperties: {
|
|
2586
|
+
const: false
|
|
2587
|
+
},
|
|
2588
|
+
properties: {
|
|
2589
|
+
type: "object",
|
|
2590
|
+
propertyNames: {
|
|
2591
|
+
$ref: "#/$defs/key"
|
|
2592
|
+
},
|
|
2593
|
+
additionalProperties: {
|
|
2594
|
+
$ref: "#/$defs/rootField"
|
|
2595
|
+
},
|
|
2596
|
+
minProperties: 1,
|
|
2597
|
+
maxProperties: 60
|
|
2598
|
+
},
|
|
2599
|
+
required: {
|
|
2600
|
+
$ref: "#/$defs/keyList"
|
|
2601
|
+
},
|
|
2602
|
+
$defs: {
|
|
2603
|
+
type: "object",
|
|
2604
|
+
propertyNames: {
|
|
2605
|
+
type: "string",
|
|
2606
|
+
pattern: "^[A-Za-z][A-Za-z0-9_-]{0,63}$"
|
|
2607
|
+
},
|
|
2608
|
+
additionalProperties: {
|
|
2609
|
+
$ref: "#/$defs/node"
|
|
2610
|
+
},
|
|
2611
|
+
maxProperties: 64
|
|
2612
|
+
}
|
|
2613
|
+
},
|
|
2614
|
+
required: [
|
|
2615
|
+
"type",
|
|
2616
|
+
"properties",
|
|
2617
|
+
"additionalProperties"
|
|
2618
|
+
],
|
|
2619
|
+
$defs: {
|
|
2620
|
+
key: {
|
|
2621
|
+
type: "string",
|
|
2622
|
+
pattern: "^[a-z][a-z0-9_]{0,31}$"
|
|
2623
|
+
},
|
|
2624
|
+
keyList: {
|
|
2625
|
+
type: "array",
|
|
2626
|
+
items: {
|
|
2627
|
+
$ref: "#/$defs/key"
|
|
2628
|
+
},
|
|
2629
|
+
uniqueItems: true
|
|
2630
|
+
},
|
|
2631
|
+
label: {
|
|
2632
|
+
type: "object",
|
|
2633
|
+
additionalProperties: false,
|
|
2634
|
+
properties: {
|
|
2635
|
+
ko: {
|
|
2636
|
+
type: "string",
|
|
2637
|
+
minLength: 1,
|
|
2638
|
+
maxLength: 200
|
|
2639
|
+
},
|
|
2640
|
+
en: {
|
|
2641
|
+
type: "string",
|
|
2642
|
+
minLength: 1,
|
|
2643
|
+
maxLength: 200
|
|
2644
|
+
}
|
|
2645
|
+
},
|
|
2646
|
+
required: [
|
|
2647
|
+
"ko"
|
|
2648
|
+
]
|
|
2649
|
+
},
|
|
2650
|
+
asset: {
|
|
2651
|
+
type: "object",
|
|
2652
|
+
additionalProperties: false,
|
|
2653
|
+
properties: {
|
|
2654
|
+
accept: {
|
|
2655
|
+
type: "array",
|
|
2656
|
+
items: {
|
|
2657
|
+
type: "string",
|
|
2658
|
+
enum: [
|
|
2659
|
+
"image/png",
|
|
2660
|
+
"image/jpeg",
|
|
2661
|
+
"image/webp"
|
|
2662
|
+
]
|
|
2663
|
+
},
|
|
2664
|
+
minItems: 1,
|
|
2665
|
+
uniqueItems: true
|
|
2666
|
+
},
|
|
2667
|
+
max_bytes: {
|
|
2668
|
+
type: "integer",
|
|
2669
|
+
minimum: 1,
|
|
2670
|
+
maximum: 4194304
|
|
2671
|
+
},
|
|
2672
|
+
aspect: {
|
|
2673
|
+
type: "string",
|
|
2674
|
+
pattern: "^[1-9][0-9]*:[1-9][0-9]*$"
|
|
2675
|
+
},
|
|
2676
|
+
generate: {
|
|
2677
|
+
type: "boolean"
|
|
2678
|
+
}
|
|
2679
|
+
},
|
|
2680
|
+
required: [
|
|
2681
|
+
"accept",
|
|
2682
|
+
"max_bytes"
|
|
2683
|
+
]
|
|
2684
|
+
},
|
|
2685
|
+
ai: {
|
|
2686
|
+
type: "object",
|
|
2687
|
+
additionalProperties: false,
|
|
2688
|
+
properties: {
|
|
2689
|
+
suggest: {
|
|
2690
|
+
type: "boolean"
|
|
2691
|
+
},
|
|
2692
|
+
hint: {
|
|
2693
|
+
type: "string",
|
|
2694
|
+
maxLength: 300
|
|
2695
|
+
}
|
|
2696
|
+
},
|
|
2697
|
+
required: [
|
|
2698
|
+
"suggest"
|
|
2699
|
+
]
|
|
2700
|
+
},
|
|
2701
|
+
scalar: {
|
|
2702
|
+
type: [
|
|
2703
|
+
"string",
|
|
2704
|
+
"number",
|
|
2705
|
+
"boolean"
|
|
2706
|
+
]
|
|
2707
|
+
},
|
|
2708
|
+
nodeKeywords: {
|
|
2709
|
+
type: "object",
|
|
2710
|
+
properties: {
|
|
2711
|
+
type: {
|
|
2712
|
+
type: "string",
|
|
2713
|
+
enum: [
|
|
2714
|
+
"object",
|
|
2715
|
+
"array",
|
|
2716
|
+
"string",
|
|
2717
|
+
"integer",
|
|
2718
|
+
"number",
|
|
2719
|
+
"boolean"
|
|
2720
|
+
]
|
|
2721
|
+
},
|
|
2722
|
+
title: {
|
|
2723
|
+
type: "string",
|
|
2724
|
+
maxLength: 120
|
|
2725
|
+
},
|
|
2726
|
+
description: {
|
|
2727
|
+
type: "string",
|
|
2728
|
+
maxLength: 1e3
|
|
2729
|
+
},
|
|
2730
|
+
"x-clack-i18n": {
|
|
2731
|
+
$ref: "#/$defs/label"
|
|
2732
|
+
},
|
|
2733
|
+
enum: {
|
|
2734
|
+
type: "array",
|
|
2735
|
+
items: {
|
|
2736
|
+
$ref: "#/$defs/scalar"
|
|
2737
|
+
},
|
|
2738
|
+
minItems: 1,
|
|
2739
|
+
maxItems: 100,
|
|
2740
|
+
uniqueItems: true
|
|
2741
|
+
},
|
|
2742
|
+
const: {
|
|
2743
|
+
$ref: "#/$defs/scalar"
|
|
2744
|
+
},
|
|
2745
|
+
minLength: {
|
|
2746
|
+
type: "integer",
|
|
2747
|
+
minimum: 0
|
|
2748
|
+
},
|
|
2749
|
+
maxLength: {
|
|
2750
|
+
type: "integer",
|
|
2751
|
+
minimum: 1,
|
|
2752
|
+
maximum: 1e5
|
|
2753
|
+
},
|
|
2754
|
+
format: {
|
|
2755
|
+
const: "uuid"
|
|
2756
|
+
},
|
|
2757
|
+
minimum: {
|
|
2758
|
+
type: "number"
|
|
2759
|
+
},
|
|
2760
|
+
maximum: {
|
|
2761
|
+
type: "number"
|
|
2762
|
+
},
|
|
2763
|
+
minItems: {
|
|
2764
|
+
type: "integer",
|
|
2765
|
+
minimum: 0
|
|
2766
|
+
},
|
|
2767
|
+
maxItems: {
|
|
2768
|
+
type: "integer",
|
|
2769
|
+
minimum: 1,
|
|
2770
|
+
maximum: 1e3
|
|
2771
|
+
},
|
|
2772
|
+
uniqueItems: {
|
|
2773
|
+
type: "boolean",
|
|
2774
|
+
description: "\uC2A4\uCE7C\uB77C(string\xB7integer\xB7number\xB7boolean) items \uBC30\uC5F4\uC5D0\uB9CC \uC4F4\uB2E4(validators.mjs \uAC80\uC0AC)."
|
|
2775
|
+
},
|
|
2776
|
+
items: {
|
|
2777
|
+
$ref: "#/$defs/node"
|
|
2778
|
+
},
|
|
2779
|
+
properties: {
|
|
2780
|
+
type: "object",
|
|
2781
|
+
propertyNames: {
|
|
2782
|
+
$ref: "#/$defs/key"
|
|
2783
|
+
},
|
|
2784
|
+
additionalProperties: {
|
|
2785
|
+
$ref: "#/$defs/node"
|
|
2786
|
+
},
|
|
2787
|
+
maxProperties: 60
|
|
2788
|
+
},
|
|
2789
|
+
required: {
|
|
2790
|
+
$ref: "#/$defs/keyList"
|
|
2791
|
+
},
|
|
2792
|
+
additionalProperties: {
|
|
2793
|
+
const: false
|
|
2794
|
+
},
|
|
2795
|
+
$ref: {
|
|
2796
|
+
type: "string",
|
|
2797
|
+
pattern: "^#/\\$defs/[A-Za-z][A-Za-z0-9_-]{0,63}$"
|
|
2798
|
+
},
|
|
2799
|
+
oneOf: {
|
|
2800
|
+
type: "array",
|
|
2801
|
+
items: {
|
|
2802
|
+
$ref: "#/$defs/node"
|
|
2803
|
+
},
|
|
2804
|
+
minItems: 2,
|
|
2805
|
+
maxItems: 16
|
|
2806
|
+
},
|
|
2807
|
+
"x-clack-discriminator": {
|
|
2808
|
+
$ref: "#/$defs/key"
|
|
2809
|
+
},
|
|
2810
|
+
"x-clack-asset": {
|
|
2811
|
+
$ref: "#/$defs/asset"
|
|
2812
|
+
},
|
|
2813
|
+
"x-clack-ai": {
|
|
2814
|
+
$ref: "#/$defs/ai"
|
|
2815
|
+
}
|
|
2816
|
+
}
|
|
2817
|
+
},
|
|
2818
|
+
node: {
|
|
2819
|
+
allOf: [
|
|
2820
|
+
{
|
|
2821
|
+
$ref: "#/$defs/nodeKeywords"
|
|
2822
|
+
}
|
|
2823
|
+
],
|
|
2824
|
+
unevaluatedProperties: false
|
|
2825
|
+
},
|
|
2826
|
+
rootField: {
|
|
2827
|
+
allOf: [
|
|
2828
|
+
{
|
|
2829
|
+
$ref: "#/$defs/nodeKeywords"
|
|
2830
|
+
}
|
|
2831
|
+
],
|
|
2832
|
+
properties: {
|
|
2833
|
+
"x-clack-visibility": {
|
|
2834
|
+
type: "string",
|
|
2835
|
+
enum: [
|
|
2836
|
+
"public",
|
|
2837
|
+
"private"
|
|
2838
|
+
]
|
|
2839
|
+
}
|
|
2840
|
+
},
|
|
2841
|
+
required: [
|
|
2842
|
+
"x-clack-visibility"
|
|
2843
|
+
],
|
|
2844
|
+
unevaluatedProperties: false
|
|
2845
|
+
}
|
|
2846
|
+
}
|
|
2847
|
+
};
|
|
2848
|
+
|
|
2849
|
+
// src/lib/skill-editor.ts
|
|
2850
|
+
var EDITOR_BUNDLE_LIMITS = { maxFiles: 50, maxFileBytes: 512 * 1024, maxTotalBytes: 2 * 1024 * 1024, maxCodeBytes: 512 * 1024 };
|
|
2851
|
+
var DATA_SCHEMA_MAX_BYTES = 64 * 1024;
|
|
2852
|
+
var ALLOWED = /\.(?:html?|js|mjs|css|json|png|jpe?g|webp|gif|woff2?)$/i;
|
|
2853
|
+
var CODE = /\.(?:html?|js|mjs|css)$/i;
|
|
2854
|
+
var RESERVED = /^(?:runtime|form|examples|output|data)(?:\/|$)/;
|
|
2855
|
+
var textDecoder = new TextDecoder("utf-8", { fatal: true });
|
|
2856
|
+
var validateMeta = new Ajv2020({ allErrors: true, strict: false }).compile(output_data_v1_schema_default);
|
|
2857
|
+
var invalid = (message) => {
|
|
2858
|
+
throw new CliError(message, "SKILL_EDITOR_INVALID");
|
|
2859
|
+
};
|
|
2860
|
+
var isObject2 = (value2) => value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
|
|
2861
|
+
var sha = (value2) => createHash3("sha256").update(value2).digest("hex");
|
|
2862
|
+
function selectEditorBundle(files, bundle) {
|
|
2863
|
+
const single = Object.hasOwn(files, bundle);
|
|
2864
|
+
const prefix = `${bundle}/`;
|
|
2865
|
+
const selected = single ? [[bundle, files[bundle]]] : Object.entries(files).filter(([path]) => path.startsWith(prefix));
|
|
2866
|
+
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);
|
|
2867
|
+
const computed = single ? list2[0]?.sha256 ?? "" : sha(JSON.stringify(list2));
|
|
2868
|
+
return { single, selected, list: list2, computed };
|
|
2869
|
+
}
|
|
2870
|
+
function computeEditorHash(manifest, files) {
|
|
2871
|
+
const editor = manifest.authoring?.editor;
|
|
2872
|
+
if (!isObject2(editor) || typeof editor.bundle !== "string") return null;
|
|
2873
|
+
const { list: list2, computed } = selectEditorBundle(files, editor.bundle.replace(/\/+$/, ""));
|
|
2874
|
+
return list2.length ? computed : null;
|
|
2875
|
+
}
|
|
2876
|
+
function parseJson(bytes2, label) {
|
|
2877
|
+
try {
|
|
2878
|
+
const value2 = JSON.parse(textDecoder.decode(bytes2));
|
|
2879
|
+
if (isObject2(value2)) return value2;
|
|
2880
|
+
} catch {
|
|
2881
|
+
}
|
|
2882
|
+
return invalid(`${label}\uC740 UTF-8 JSON \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4.`);
|
|
2883
|
+
}
|
|
2884
|
+
function scanBundle(list2, warnings) {
|
|
2885
|
+
for (const [path, data] of list2) {
|
|
2886
|
+
if (!/\.(?:html?|js|mjs|css)$/i.test(path)) continue;
|
|
2887
|
+
let text;
|
|
2888
|
+
try {
|
|
2889
|
+
text = textDecoder.decode(data);
|
|
2890
|
+
} catch {
|
|
2891
|
+
invalid(`${path}\uB294 UTF-8 \uD14D\uC2A4\uD2B8\uC5EC\uC57C \uD569\uB2C8\uB2E4.`);
|
|
2892
|
+
return;
|
|
2893
|
+
}
|
|
2894
|
+
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.`);
|
|
2895
|
+
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.`);
|
|
2896
|
+
if (/\.css$/i.test(path) && /url\(\s*(?!["']?data:)/i.test(text)) {
|
|
2897
|
+
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.`);
|
|
2898
|
+
}
|
|
2899
|
+
}
|
|
2900
|
+
}
|
|
2901
|
+
function inspectEditorSkill(manifest, files, options2) {
|
|
2902
|
+
const authoring = manifest.authoring;
|
|
2903
|
+
const output2 = manifest.output;
|
|
2904
|
+
const editor = authoring.editor;
|
|
2905
|
+
const warnings = [];
|
|
2906
|
+
const bundle = String(editor.bundle).replace(/\/+$/, "");
|
|
2907
|
+
if (RESERVED.test(bundle)) invalid("editor \uBC88\uB4E4 \uACBD\uB85C\uAC00 \uC608\uC57D \uACBD\uB85C(runtime\xB7form\xB7examples\xB7output\xB7data)\uC785\uB2C8\uB2E4.");
|
|
2908
|
+
const { single, selected, list: list2, computed } = selectEditorBundle(files, bundle);
|
|
2909
|
+
if (!list2.length) invalid(`editor \uBC88\uB4E4(${bundle})\uC5D0 \uD30C\uC77C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.`);
|
|
2910
|
+
const entry = single ? bundle : `${bundle}/index.html`;
|
|
2911
|
+
if (single ? !/\.html?$/i.test(bundle) : !list2.some((file) => file.path === "index.html")) invalid(`editor \uC9C4\uC785\uC810(${entry})\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.`);
|
|
2912
|
+
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.`);
|
|
2913
|
+
const total = list2.reduce((sum, file) => sum + file.bytes, 0);
|
|
2914
|
+
if (total > EDITOR_BUNDLE_LIMITS.maxTotalBytes) invalid("editor \uBC88\uB4E4 \uC804\uCCB4 \uD06C\uAE30\uAC00 2 MiB\uB97C \uB118\uC2B5\uB2C8\uB2E4.");
|
|
2915
|
+
let code = 0;
|
|
2916
|
+
for (const [path, data2] of selected) {
|
|
2917
|
+
if (!ALLOWED.test(path)) invalid(`editor \uBC88\uB4E4\uC5D0 \uD5C8\uC6A9\uB418\uC9C0 \uC54A\uB294 \uD30C\uC77C \uD615\uC2DD\uC785\uB2C8\uB2E4: ${path}`);
|
|
2918
|
+
if (data2.length > EDITOR_BUNDLE_LIMITS.maxFileBytes) invalid(`editor \uBC88\uB4E4 \uD30C\uC77C\uC774 512 KiB\uB97C \uB118\uC2B5\uB2C8\uB2E4: ${path}`);
|
|
2919
|
+
if (CODE.test(path)) code += data2.length;
|
|
2920
|
+
}
|
|
2921
|
+
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.");
|
|
2922
|
+
scanBundle(selected, warnings);
|
|
2923
|
+
const declared = String(editor.sha256);
|
|
2924
|
+
const match = declared === computed;
|
|
2925
|
+
if (!match) {
|
|
2926
|
+
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.`;
|
|
2927
|
+
if (options2.hashMismatch === "error") invalid(message);
|
|
2928
|
+
warnings.push(`PLUGIN_BUNDLE_HASH_MISMATCH: ${message}`);
|
|
2929
|
+
}
|
|
2930
|
+
const data = output2.data;
|
|
2931
|
+
const schemaPath = data?.schema;
|
|
2932
|
+
if (typeof schemaPath !== "string" || !files[schemaPath]) return invalid("output.data.schema \uD30C\uC77C\uC774 \uD328\uD0A4\uC9C0\uC5D0 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
2933
|
+
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.");
|
|
2934
|
+
const schema = parseJson(files[schemaPath], "\uB370\uC774\uD130 \uC2A4\uD0A4\uB9C8");
|
|
2935
|
+
if (!validateMeta(schema)) {
|
|
2936
|
+
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(" / ")}`);
|
|
2937
|
+
}
|
|
2938
|
+
const dataErrors = [...inspectOutputData(schema), ...inspectAuthoringSemantics(manifest, void 0, schema)];
|
|
2939
|
+
if (dataErrors.length) invalid(dataErrors.map((error2) => `${error2.path}: ${error2.message}`).join(" / "));
|
|
2940
|
+
const template = output2.template;
|
|
2941
|
+
if (!template || template.root !== "runtime" || typeof template.entry !== "string") invalid("runtime \uD15C\uD50C\uB9BF \uACBD\uB85C\uB97C \uD655\uC778\uD558\uC138\uC694.");
|
|
2942
|
+
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).");
|
|
2943
|
+
const examples = Object.keys(files).filter((path) => /^examples\/[^/]+\.json$/.test(path));
|
|
2944
|
+
if (!examples.length || examples.length > 10) invalid("\uD15C\uD50C\uB9BF \uC608\uC2DC\uB294 1~10\uAC1C \uD544\uC694\uD569\uB2C8\uB2E4.");
|
|
2945
|
+
let validateExample;
|
|
2946
|
+
try {
|
|
2947
|
+
const ajv2 = new Ajv2020({ allErrors: true, strict: false });
|
|
2948
|
+
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);
|
|
2949
|
+
validateExample = ajv2.compile(schema);
|
|
2950
|
+
} catch {
|
|
2951
|
+
return invalid("\uB370\uC774\uD130 \uC2A4\uD0A4\uB9C8\uB97C \uCEF4\uD30C\uC77C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
2952
|
+
}
|
|
2953
|
+
for (const path of examples) {
|
|
2954
|
+
if (files[path].length > 256 * 1024) invalid(`\uC608\uC2DC\uAC00 \uB108\uBB34 \uD07D\uB2C8\uB2E4: ${path}`);
|
|
2955
|
+
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}`);
|
|
2956
|
+
}
|
|
2957
|
+
return { computedHash: computed, warnings, summary: {
|
|
2958
|
+
bundle,
|
|
2959
|
+
entry,
|
|
2960
|
+
file_count: list2.length,
|
|
2961
|
+
total_bytes: total,
|
|
2962
|
+
declared_sha256: declared,
|
|
2963
|
+
computed_sha256: computed,
|
|
2964
|
+
hash_match: match,
|
|
2965
|
+
permissions: editor.permissions,
|
|
2966
|
+
center_only: true
|
|
2967
|
+
} };
|
|
2968
|
+
}
|
|
2969
|
+
|
|
2218
2970
|
// src/vendor/clack-api-v4-contracts/schemas/clack-skill.v1.schema.json
|
|
2219
2971
|
var clack_skill_v1_schema_default = {
|
|
2220
2972
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
@@ -2454,11 +3206,62 @@ var clack_skill_v1_schema_default = {
|
|
|
2454
3206
|
"purpose"
|
|
2455
3207
|
]
|
|
2456
3208
|
},
|
|
2457
|
-
maxItems: 8
|
|
3209
|
+
maxItems: 8
|
|
3210
|
+
},
|
|
3211
|
+
plugin: {
|
|
3212
|
+
type: "object",
|
|
3213
|
+
additionalProperties: false,
|
|
3214
|
+
properties: {
|
|
3215
|
+
bundle: {
|
|
3216
|
+
type: "string",
|
|
3217
|
+
pattern: "^(?!/)(?!.*\\.\\.)[A-Za-z0-9_./-]+$"
|
|
3218
|
+
},
|
|
3219
|
+
sha256: {
|
|
3220
|
+
type: "string",
|
|
3221
|
+
pattern: "^[a-f0-9]{64}$"
|
|
3222
|
+
},
|
|
3223
|
+
fields: {
|
|
3224
|
+
type: "array",
|
|
3225
|
+
items: {
|
|
3226
|
+
type: "string",
|
|
3227
|
+
pattern: "^[a-z][a-z0-9_-]{0,31}$"
|
|
3228
|
+
},
|
|
3229
|
+
minItems: 1,
|
|
3230
|
+
uniqueItems: true
|
|
3231
|
+
},
|
|
3232
|
+
permissions: {
|
|
3233
|
+
type: "array",
|
|
3234
|
+
items: {
|
|
3235
|
+
type: "string",
|
|
3236
|
+
enum: [
|
|
3237
|
+
"getFormSchema",
|
|
3238
|
+
"getFormValues",
|
|
3239
|
+
"setFormValues",
|
|
3240
|
+
"listApprovedAssets",
|
|
3241
|
+
"requestAssetUpload"
|
|
3242
|
+
]
|
|
3243
|
+
},
|
|
3244
|
+
minItems: 1,
|
|
3245
|
+
uniqueItems: true
|
|
3246
|
+
},
|
|
3247
|
+
purpose: {
|
|
3248
|
+
type: "string",
|
|
3249
|
+
minLength: 1,
|
|
3250
|
+
maxLength: 200
|
|
3251
|
+
}
|
|
3252
|
+
},
|
|
3253
|
+
required: [
|
|
3254
|
+
"bundle",
|
|
3255
|
+
"sha256",
|
|
3256
|
+
"fields",
|
|
3257
|
+
"permissions",
|
|
3258
|
+
"purpose"
|
|
3259
|
+
]
|
|
2458
3260
|
},
|
|
2459
|
-
|
|
3261
|
+
editor: {
|
|
2460
3262
|
type: "object",
|
|
2461
3263
|
additionalProperties: false,
|
|
3264
|
+
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.",
|
|
2462
3265
|
properties: {
|
|
2463
3266
|
bundle: {
|
|
2464
3267
|
type: "string",
|
|
@@ -2468,40 +3271,47 @@ var clack_skill_v1_schema_default = {
|
|
|
2468
3271
|
type: "string",
|
|
2469
3272
|
pattern: "^[a-f0-9]{64}$"
|
|
2470
3273
|
},
|
|
2471
|
-
fields: {
|
|
2472
|
-
type: "array",
|
|
2473
|
-
items: {
|
|
2474
|
-
type: "string",
|
|
2475
|
-
pattern: "^[a-z][a-z0-9_-]{0,31}$"
|
|
2476
|
-
},
|
|
2477
|
-
minItems: 1,
|
|
2478
|
-
uniqueItems: true
|
|
2479
|
-
},
|
|
2480
3274
|
permissions: {
|
|
2481
3275
|
type: "array",
|
|
2482
3276
|
items: {
|
|
2483
3277
|
type: "string",
|
|
2484
3278
|
enum: [
|
|
2485
|
-
"
|
|
2486
|
-
"
|
|
2487
|
-
"
|
|
2488
|
-
"
|
|
2489
|
-
"
|
|
3279
|
+
"document.read",
|
|
3280
|
+
"document.write",
|
|
3281
|
+
"assets.read",
|
|
3282
|
+
"assets.pick",
|
|
3283
|
+
"ai.suggest",
|
|
3284
|
+
"tools.image.generate",
|
|
3285
|
+
"preview",
|
|
3286
|
+
"package"
|
|
2490
3287
|
]
|
|
2491
3288
|
},
|
|
2492
|
-
minItems:
|
|
3289
|
+
minItems: 2,
|
|
2493
3290
|
uniqueItems: true
|
|
2494
3291
|
},
|
|
2495
3292
|
purpose: {
|
|
2496
|
-
type: "
|
|
2497
|
-
|
|
2498
|
-
|
|
3293
|
+
type: "object",
|
|
3294
|
+
additionalProperties: false,
|
|
3295
|
+
properties: {
|
|
3296
|
+
ko: {
|
|
3297
|
+
type: "string",
|
|
3298
|
+
minLength: 1,
|
|
3299
|
+
maxLength: 200
|
|
3300
|
+
},
|
|
3301
|
+
en: {
|
|
3302
|
+
type: "string",
|
|
3303
|
+
minLength: 1,
|
|
3304
|
+
maxLength: 200
|
|
3305
|
+
}
|
|
3306
|
+
},
|
|
3307
|
+
required: [
|
|
3308
|
+
"ko"
|
|
3309
|
+
]
|
|
2499
3310
|
}
|
|
2500
3311
|
},
|
|
2501
3312
|
required: [
|
|
2502
3313
|
"bundle",
|
|
2503
3314
|
"sha256",
|
|
2504
|
-
"fields",
|
|
2505
3315
|
"permissions",
|
|
2506
3316
|
"purpose"
|
|
2507
3317
|
]
|
|
@@ -2766,7 +3576,8 @@ var clack_skill_v1_schema_default = {
|
|
|
2766
3576
|
"fixed",
|
|
2767
3577
|
"scroll_hide",
|
|
2768
3578
|
"translucent_scroll_hide",
|
|
2769
|
-
"floating_close"
|
|
3579
|
+
"floating_close",
|
|
3580
|
+
"floating_close_safe_area"
|
|
2770
3581
|
]
|
|
2771
3582
|
},
|
|
2772
3583
|
color_scheme: {
|
|
@@ -2781,6 +3592,89 @@ var clack_skill_v1_schema_default = {
|
|
|
2781
3592
|
"header_mode",
|
|
2782
3593
|
"color_scheme"
|
|
2783
3594
|
]
|
|
3595
|
+
},
|
|
3596
|
+
data: {
|
|
3597
|
+
type: "object",
|
|
3598
|
+
additionalProperties: false,
|
|
3599
|
+
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.",
|
|
3600
|
+
properties: {
|
|
3601
|
+
schema: {
|
|
3602
|
+
type: "string",
|
|
3603
|
+
pattern: "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$))[A-Za-z0-9_./-]+$"
|
|
3604
|
+
},
|
|
3605
|
+
max_bytes: {
|
|
3606
|
+
type: "integer",
|
|
3607
|
+
minimum: 1024,
|
|
3608
|
+
maximum: 262144
|
|
3609
|
+
},
|
|
3610
|
+
private_max_bytes: {
|
|
3611
|
+
type: "integer",
|
|
3612
|
+
minimum: 1,
|
|
3613
|
+
maximum: 65536
|
|
3614
|
+
},
|
|
3615
|
+
assets: {
|
|
3616
|
+
type: "object",
|
|
3617
|
+
additionalProperties: false,
|
|
3618
|
+
properties: {
|
|
3619
|
+
max_count: {
|
|
3620
|
+
type: "integer",
|
|
3621
|
+
minimum: 1,
|
|
3622
|
+
maximum: 48
|
|
3623
|
+
},
|
|
3624
|
+
max_total_bytes: {
|
|
3625
|
+
type: "integer",
|
|
3626
|
+
minimum: 1,
|
|
3627
|
+
maximum: 25165824
|
|
3628
|
+
}
|
|
3629
|
+
},
|
|
3630
|
+
required: [
|
|
3631
|
+
"max_count",
|
|
3632
|
+
"max_total_bytes"
|
|
3633
|
+
]
|
|
3634
|
+
}
|
|
3635
|
+
},
|
|
3636
|
+
required: [
|
|
3637
|
+
"schema"
|
|
3638
|
+
]
|
|
3639
|
+
},
|
|
3640
|
+
intro: {
|
|
3641
|
+
type: "object",
|
|
3642
|
+
additionalProperties: false,
|
|
3643
|
+
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.",
|
|
3644
|
+
properties: {
|
|
3645
|
+
default_enabled: {
|
|
3646
|
+
type: "boolean",
|
|
3647
|
+
default: false
|
|
3648
|
+
},
|
|
3649
|
+
creator_comment: {
|
|
3650
|
+
type: "string",
|
|
3651
|
+
pattern: "^[a-z][a-z0-9_-]{0,31}$"
|
|
3652
|
+
},
|
|
3653
|
+
description: {
|
|
3654
|
+
type: "string",
|
|
3655
|
+
pattern: "^[a-z][a-z0-9_-]{0,31}$"
|
|
3656
|
+
},
|
|
3657
|
+
description_fallback: {
|
|
3658
|
+
type: "array",
|
|
3659
|
+
items: {
|
|
3660
|
+
type: "string",
|
|
3661
|
+
pattern: "^[a-z][a-z0-9_-]{0,31}$"
|
|
3662
|
+
},
|
|
3663
|
+
minItems: 1,
|
|
3664
|
+
maxItems: 20,
|
|
3665
|
+
uniqueItems: true
|
|
3666
|
+
},
|
|
3667
|
+
images: {
|
|
3668
|
+
type: "array",
|
|
3669
|
+
items: {
|
|
3670
|
+
type: "string",
|
|
3671
|
+
pattern: "^[a-z][a-z0-9_-]{0,31}$"
|
|
3672
|
+
},
|
|
3673
|
+
minItems: 1,
|
|
3674
|
+
maxItems: 8,
|
|
3675
|
+
uniqueItems: true
|
|
3676
|
+
}
|
|
3677
|
+
}
|
|
2784
3678
|
}
|
|
2785
3679
|
},
|
|
2786
3680
|
required: [
|
|
@@ -2923,8 +3817,27 @@ var clack_skill_v1_schema_default = {
|
|
|
2923
3817
|
],
|
|
2924
3818
|
properties: {
|
|
2925
3819
|
authoring: {
|
|
2926
|
-
|
|
2927
|
-
|
|
3820
|
+
oneOf: [
|
|
3821
|
+
{
|
|
3822
|
+
required: [
|
|
3823
|
+
"form"
|
|
3824
|
+
],
|
|
3825
|
+
not: {
|
|
3826
|
+
required: [
|
|
3827
|
+
"editor"
|
|
3828
|
+
]
|
|
3829
|
+
}
|
|
3830
|
+
},
|
|
3831
|
+
{
|
|
3832
|
+
required: [
|
|
3833
|
+
"editor"
|
|
3834
|
+
],
|
|
3835
|
+
not: {
|
|
3836
|
+
required: [
|
|
3837
|
+
"form"
|
|
3838
|
+
]
|
|
3839
|
+
}
|
|
3840
|
+
}
|
|
2928
3841
|
],
|
|
2929
3842
|
properties: {
|
|
2930
3843
|
modes: {
|
|
@@ -2975,6 +3888,136 @@ var clack_skill_v1_schema_default = {
|
|
|
2975
3888
|
"runtime"
|
|
2976
3889
|
]
|
|
2977
3890
|
}
|
|
3891
|
+
},
|
|
3892
|
+
{
|
|
3893
|
+
if: {
|
|
3894
|
+
properties: {
|
|
3895
|
+
authoring: {
|
|
3896
|
+
required: [
|
|
3897
|
+
"editor"
|
|
3898
|
+
]
|
|
3899
|
+
}
|
|
3900
|
+
},
|
|
3901
|
+
required: [
|
|
3902
|
+
"authoring"
|
|
3903
|
+
]
|
|
3904
|
+
},
|
|
3905
|
+
then: {
|
|
3906
|
+
properties: {
|
|
3907
|
+
type: {
|
|
3908
|
+
const: "template"
|
|
3909
|
+
},
|
|
3910
|
+
authoring: {
|
|
3911
|
+
properties: {
|
|
3912
|
+
surfaces: {
|
|
3913
|
+
items: {
|
|
3914
|
+
const: "center"
|
|
3915
|
+
},
|
|
3916
|
+
maxItems: 1
|
|
3917
|
+
}
|
|
3918
|
+
},
|
|
3919
|
+
not: {
|
|
3920
|
+
anyOf: [
|
|
3921
|
+
{
|
|
3922
|
+
required: [
|
|
3923
|
+
"form"
|
|
3924
|
+
]
|
|
3925
|
+
},
|
|
3926
|
+
{
|
|
3927
|
+
required: [
|
|
3928
|
+
"plugin"
|
|
3929
|
+
]
|
|
3930
|
+
}
|
|
3931
|
+
]
|
|
3932
|
+
}
|
|
3933
|
+
},
|
|
3934
|
+
output: {
|
|
3935
|
+
required: [
|
|
3936
|
+
"data"
|
|
3937
|
+
],
|
|
3938
|
+
not: {
|
|
3939
|
+
anyOf: [
|
|
3940
|
+
{
|
|
3941
|
+
required: [
|
|
3942
|
+
"asset_slots"
|
|
3943
|
+
]
|
|
3944
|
+
},
|
|
3945
|
+
{
|
|
3946
|
+
required: [
|
|
3947
|
+
"derive"
|
|
3948
|
+
]
|
|
3949
|
+
},
|
|
3950
|
+
{
|
|
3951
|
+
required: [
|
|
3952
|
+
"profile_select"
|
|
3953
|
+
]
|
|
3954
|
+
},
|
|
3955
|
+
{
|
|
3956
|
+
required: [
|
|
3957
|
+
"entity"
|
|
3958
|
+
]
|
|
3959
|
+
},
|
|
3960
|
+
{
|
|
3961
|
+
required: [
|
|
3962
|
+
"entity_map"
|
|
3963
|
+
]
|
|
3964
|
+
},
|
|
3965
|
+
{
|
|
3966
|
+
required: [
|
|
3967
|
+
"intro"
|
|
3968
|
+
]
|
|
3969
|
+
}
|
|
3970
|
+
]
|
|
3971
|
+
}
|
|
3972
|
+
}
|
|
3973
|
+
}
|
|
3974
|
+
}
|
|
3975
|
+
},
|
|
3976
|
+
{
|
|
3977
|
+
if: {
|
|
3978
|
+
properties: {
|
|
3979
|
+
output: {
|
|
3980
|
+
required: [
|
|
3981
|
+
"data"
|
|
3982
|
+
]
|
|
3983
|
+
}
|
|
3984
|
+
},
|
|
3985
|
+
required: [
|
|
3986
|
+
"output"
|
|
3987
|
+
]
|
|
3988
|
+
},
|
|
3989
|
+
then: {
|
|
3990
|
+
properties: {
|
|
3991
|
+
authoring: {
|
|
3992
|
+
required: [
|
|
3993
|
+
"editor"
|
|
3994
|
+
]
|
|
3995
|
+
}
|
|
3996
|
+
}
|
|
3997
|
+
}
|
|
3998
|
+
},
|
|
3999
|
+
{
|
|
4000
|
+
if: {
|
|
4001
|
+
properties: {
|
|
4002
|
+
type: {
|
|
4003
|
+
const: "instruction"
|
|
4004
|
+
}
|
|
4005
|
+
},
|
|
4006
|
+
required: [
|
|
4007
|
+
"type"
|
|
4008
|
+
]
|
|
4009
|
+
},
|
|
4010
|
+
then: {
|
|
4011
|
+
properties: {
|
|
4012
|
+
output: {
|
|
4013
|
+
not: {
|
|
4014
|
+
required: [
|
|
4015
|
+
"intro"
|
|
4016
|
+
]
|
|
4017
|
+
}
|
|
4018
|
+
}
|
|
4019
|
+
}
|
|
4020
|
+
}
|
|
2978
4021
|
}
|
|
2979
4022
|
]
|
|
2980
4023
|
};
|
|
@@ -3858,39 +4901,39 @@ var clack_content_v1_schema_default = {
|
|
|
3858
4901
|
var MAX_ZIP = 10 * 1024 * 1024;
|
|
3859
4902
|
var MAX_EXPANDED = 30 * 1024 * 1024;
|
|
3860
4903
|
var MAX_FILES2 = 500;
|
|
3861
|
-
var
|
|
3862
|
-
var ajv = new
|
|
4904
|
+
var textDecoder2 = new TextDecoder("utf-8", { fatal: true });
|
|
4905
|
+
var ajv = new Ajv20202({ allErrors: true, strict: false });
|
|
3863
4906
|
ajv.addSchema(clack_content_v1_schema_default);
|
|
3864
4907
|
var validateManifest = ajv.compile(clack_skill_v1_schema_default);
|
|
3865
|
-
var validateForm = new
|
|
3866
|
-
function
|
|
4908
|
+
var validateForm = new Ajv20202({ allErrors: true, strict: false }).compile(authoring_form_v1_schema_default);
|
|
4909
|
+
function invalid2(message) {
|
|
3867
4910
|
throw new CliError(message, "SKILL_PACKAGE_INVALID");
|
|
3868
4911
|
}
|
|
3869
|
-
function hash(
|
|
3870
|
-
return
|
|
4912
|
+
function hash(bytes2) {
|
|
4913
|
+
return createHash4("sha256").update(bytes2).digest("hex");
|
|
3871
4914
|
}
|
|
3872
4915
|
function object(value2) {
|
|
3873
4916
|
return value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
|
|
3874
4917
|
}
|
|
3875
|
-
function json(
|
|
3876
|
-
if (
|
|
4918
|
+
function json(bytes2, max, label) {
|
|
4919
|
+
if (bytes2.length > max) invalid2(`${label} \uD06C\uAE30 \uC81C\uD55C\uC744 \uCD08\uACFC\uD588\uC2B5\uB2C8\uB2E4.`);
|
|
3877
4920
|
try {
|
|
3878
|
-
const parsed = JSON.parse(
|
|
4921
|
+
const parsed = JSON.parse(textDecoder2.decode(bytes2));
|
|
3879
4922
|
if (object(parsed)) return parsed;
|
|
3880
4923
|
} catch {
|
|
3881
4924
|
}
|
|
3882
|
-
return
|
|
4925
|
+
return invalid2(`${label}\uC740 UTF-8 JSON \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4.`);
|
|
3883
4926
|
}
|
|
3884
4927
|
async function readRegular(path) {
|
|
3885
4928
|
let handle;
|
|
3886
4929
|
try {
|
|
3887
4930
|
handle = await open3(path, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
3888
4931
|
const info = await handle.stat();
|
|
3889
|
-
if (!info.isFile())
|
|
4932
|
+
if (!info.isFile()) invalid2("\uC2A4\uD0AC \uD328\uD0A4\uC9C0\uC5D0\uB294 \uC77C\uBC18 \uD30C\uC77C\uB9CC \uD5C8\uC6A9\uB429\uB2C8\uB2E4.");
|
|
3890
4933
|
return await handle.readFile();
|
|
3891
4934
|
} catch (error2) {
|
|
3892
4935
|
if (error2 instanceof CliError) throw error2;
|
|
3893
|
-
return
|
|
4936
|
+
return invalid2("\uC2A4\uD0AC \uD30C\uC77C\uC744 \uC548\uC804\uD558\uAC8C \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
3894
4937
|
} finally {
|
|
3895
4938
|
await handle?.close();
|
|
3896
4939
|
}
|
|
@@ -3909,76 +4952,76 @@ async function collectFiles(directory) {
|
|
|
3909
4952
|
const entries = await readdir(join2(directory, relative), { withFileTypes: true });
|
|
3910
4953
|
for (const entry of entries.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0)) {
|
|
3911
4954
|
const path = relative ? `${relative}/${entry.name}` : entry.name;
|
|
3912
|
-
if (!skillPathAllowed(path))
|
|
3913
|
-
if (entry.isSymbolicLink() || !(entry.isDirectory() || entry.isFile()))
|
|
4955
|
+
if (!skillPathAllowed(path)) invalid2(`\uD5C8\uC6A9\uB418\uC9C0 \uC54A\uB294 \uD328\uD0A4\uC9C0 \uACBD\uB85C: ${path}`);
|
|
4956
|
+
if (entry.isSymbolicLink() || !(entry.isDirectory() || entry.isFile())) invalid2(`\uC77C\uBC18 \uD30C\uC77C\xB7\uD3F4\uB354\uB9CC \uD5C8\uC6A9\uB429\uB2C8\uB2E4: ${path}`);
|
|
3914
4957
|
if (entry.isDirectory()) {
|
|
3915
4958
|
await walk(path);
|
|
3916
4959
|
continue;
|
|
3917
4960
|
}
|
|
3918
|
-
if (Object.keys(files).length >= MAX_FILES2)
|
|
4961
|
+
if (Object.keys(files).length >= MAX_FILES2) invalid2("\uC2A4\uD0AC \uD30C\uC77C \uC218\uAC00 500\uAC1C\uB97C \uCD08\uACFC\uD588\uC2B5\uB2C8\uB2E4.");
|
|
3919
4962
|
const data = await readRegular(join2(directory, path));
|
|
3920
4963
|
totalBytes += data.length;
|
|
3921
|
-
if (totalBytes > MAX_EXPANDED)
|
|
4964
|
+
if (totalBytes > MAX_EXPANDED) invalid2("\uC2A4\uD0AC \uC555\uCD95 \uD574\uC81C \uC6A9\uB7C9\uC774 30 MiB\uB97C \uCD08\uACFC\uD588\uC2B5\uB2C8\uB2E4.");
|
|
3922
4965
|
files[path] = data;
|
|
3923
4966
|
}
|
|
3924
4967
|
}
|
|
3925
4968
|
await walk("");
|
|
3926
4969
|
return files;
|
|
3927
4970
|
}
|
|
3928
|
-
function inspectZip(
|
|
3929
|
-
if (!
|
|
4971
|
+
function inspectZip(bytes2, expectedDirectory, hashMismatch) {
|
|
4972
|
+
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.");
|
|
3930
4973
|
const seen = /* @__PURE__ */ new Set();
|
|
3931
4974
|
let entries = 0;
|
|
3932
4975
|
let totalBytes = 0;
|
|
3933
4976
|
let extracted;
|
|
3934
4977
|
try {
|
|
3935
|
-
extracted = unzipSync2(
|
|
4978
|
+
extracted = unzipSync2(bytes2, { filter(entry) {
|
|
3936
4979
|
entries++;
|
|
3937
|
-
if (entries > MAX_FILES2)
|
|
4980
|
+
if (entries > MAX_FILES2) invalid2("\uC2A4\uD0AC \uD30C\uC77C \uC218\uAC00 500\uAC1C\uB97C \uCD08\uACFC\uD588\uC2B5\uB2C8\uB2E4.");
|
|
3938
4981
|
const path = entry.name.endsWith("/") ? entry.name.slice(0, -1) : entry.name;
|
|
3939
|
-
if (!skillPathAllowed(path) || seen.has(path.toLowerCase()))
|
|
4982
|
+
if (!skillPathAllowed(path) || seen.has(path.toLowerCase())) invalid2(`\uD5C8\uC6A9\uB418\uC9C0 \uC54A\uB294 ZIP \uACBD\uB85C: ${path}`);
|
|
3940
4983
|
seen.add(path.toLowerCase());
|
|
3941
4984
|
if (entry.name.endsWith("/")) return false;
|
|
3942
4985
|
totalBytes += entry.originalSize;
|
|
3943
|
-
if (totalBytes > MAX_EXPANDED)
|
|
4986
|
+
if (totalBytes > MAX_EXPANDED) invalid2("\uC2A4\uD0AC \uC555\uCD95 \uD574\uC81C \uC6A9\uB7C9\uC774 30 MiB\uB97C \uCD08\uACFC\uD588\uC2B5\uB2C8\uB2E4.");
|
|
3944
4987
|
return true;
|
|
3945
4988
|
} });
|
|
3946
4989
|
} catch (error2) {
|
|
3947
4990
|
if (error2 instanceof CliError) throw error2;
|
|
3948
|
-
return
|
|
4991
|
+
return invalid2("ZIP \uAD6C\uC870\uB97C \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
3949
4992
|
}
|
|
3950
4993
|
const markdown = extracted["SKILL.md"];
|
|
3951
4994
|
const manifestBytes = extracted["clack.skill.json"];
|
|
3952
|
-
if (!markdown || !manifestBytes)
|
|
3953
|
-
if (markdown.length > 20 * 1024)
|
|
4995
|
+
if (!markdown || !manifestBytes) invalid2("\uB8E8\uD2B8 SKILL.md\uC640 clack.skill.json\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.");
|
|
4996
|
+
if (markdown.length > 20 * 1024) invalid2("SKILL.md\uB294 20 KiB \uC774\uD558\uC5EC\uC57C \uD569\uB2C8\uB2E4.");
|
|
3954
4997
|
let frontmatter;
|
|
3955
4998
|
try {
|
|
3956
|
-
const match = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/.exec(
|
|
3957
|
-
if (!match)
|
|
4999
|
+
const match = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/.exec(textDecoder2.decode(markdown));
|
|
5000
|
+
if (!match) invalid2("SKILL.md\uC758 YAML frontmatter\uB97C \uD655\uC778\uD558\uC138\uC694.");
|
|
3958
5001
|
frontmatter = match[1];
|
|
3959
5002
|
} catch {
|
|
3960
|
-
return
|
|
5003
|
+
return invalid2("SKILL.md\uB294 UTF-8 \uD14D\uC2A4\uD2B8\uC5EC\uC57C \uD569\uB2C8\uB2E4.");
|
|
3961
5004
|
}
|
|
3962
5005
|
const document = parseDocument(frontmatter, { version: "1.2", uniqueKeys: true, merge: false });
|
|
3963
5006
|
if (document.errors.length || document.warnings.length || /(?:^|\n)\s*allowed-tools\s*:/i.test(frontmatter)) {
|
|
3964
|
-
|
|
5007
|
+
invalid2("SKILL.md frontmatter\uC5D0 \uC798\uBABB\uB41C \uAC12 \uB610\uB294 allowed-tools\uAC00 \uC788\uC2B5\uB2C8\uB2E4.");
|
|
3965
5008
|
}
|
|
3966
5009
|
let front;
|
|
3967
5010
|
try {
|
|
3968
5011
|
front = document.toJS({ maxAliasCount: 0 });
|
|
3969
5012
|
} catch {
|
|
3970
|
-
return
|
|
5013
|
+
return invalid2("SKILL.md frontmatter\uB97C \uD655\uC778\uD558\uC138\uC694.");
|
|
3971
5014
|
}
|
|
3972
|
-
if (!object(front) || typeof front.name !== "string" || typeof front.description !== "string" || !front.description.length || front.description.length > 1024)
|
|
5015
|
+
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.");
|
|
3973
5016
|
const manifest = json(manifestBytes, 64 * 1024, "clack.skill.json");
|
|
3974
|
-
if (!validateManifest(manifest))
|
|
5017
|
+
if (!validateManifest(manifest)) invalid2("clack.skill.json\uC774 \uACF5\uAC1C \uC2A4\uD0AC \uC2A4\uD0A4\uB9C8\uC640 \uC77C\uCE58\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.");
|
|
3975
5018
|
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) {
|
|
3976
|
-
|
|
5019
|
+
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.");
|
|
3977
5020
|
}
|
|
3978
5021
|
const file = (path, max) => {
|
|
3979
|
-
if (typeof path !== "string" || !skillPathAllowed(path) || !extracted[path])
|
|
5022
|
+
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.");
|
|
3980
5023
|
const data = extracted[path];
|
|
3981
|
-
if (max && data.length > max)
|
|
5024
|
+
if (max && data.length > max) invalid2("\uC120\uC5B8\uD55C \uD30C\uC77C\uC758 \uD06C\uAE30 \uC81C\uD55C\uC744 \uCD08\uACFC\uD588\uC2B5\uB2C8\uB2E4.");
|
|
3982
5025
|
return data;
|
|
3983
5026
|
};
|
|
3984
5027
|
const display = manifest.display;
|
|
@@ -3987,50 +5030,93 @@ function inspectZip(bytes, expectedDirectory) {
|
|
|
3987
5030
|
const authoring = manifest.authoring;
|
|
3988
5031
|
const output2 = manifest.output;
|
|
3989
5032
|
for (const reference of authoring.references ?? []) file(reference, 64 * 1024);
|
|
3990
|
-
|
|
5033
|
+
let editor;
|
|
5034
|
+
if (manifest.type === "template" && authoring.editor) {
|
|
5035
|
+
editor = inspectEditorSkill(manifest, extracted, { hashMismatch });
|
|
5036
|
+
file(`runtime/${output2.template.entry}`);
|
|
5037
|
+
const runtime2 = manifest.runtime;
|
|
5038
|
+
for (const profile of runtime2.profiles) file(`runtime/.clack/profiles/${profile}.yaml`, 16 * 1024);
|
|
5039
|
+
} else if (manifest.type === "template" || manifest.type === "tool") {
|
|
3991
5040
|
const form = json(file(authoring.form, 64 * 1024), 64 * 1024, "\uC81C\uC791 \uD3FC");
|
|
3992
|
-
if (!validateForm(form))
|
|
5041
|
+
if (!validateForm(form)) invalid2("\uC81C\uC791 \uD3FC\uC774 \uACF5\uAC1C \uC2A4\uD0A4\uB9C8\uC640 \uC77C\uCE58\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.");
|
|
3993
5042
|
const semanticErrors = inspectAuthoringSemantics(manifest, form);
|
|
3994
|
-
if (semanticErrors.length)
|
|
5043
|
+
if (semanticErrors.length) invalid2(semanticErrors.map((error2) => `${error2.path}: ${error2.message}`).join(" / "));
|
|
3995
5044
|
const template = output2.template;
|
|
3996
|
-
if (template.root !== "runtime" || typeof template.entry !== "string")
|
|
5045
|
+
if (template.root !== "runtime" || typeof template.entry !== "string") invalid2("runtime \uD15C\uD50C\uB9BF \uACBD\uB85C\uB97C \uD655\uC778\uD558\uC138\uC694.");
|
|
3997
5046
|
file(`runtime/${template.entry}`);
|
|
3998
5047
|
const runtime2 = manifest.runtime;
|
|
3999
5048
|
for (const profile of runtime2.profiles) file(`runtime/.clack/profiles/${profile}.yaml`, 16 * 1024);
|
|
4000
5049
|
const examples = Object.keys(extracted).filter((path) => /^examples\/[^/]+\.json$/.test(path));
|
|
4001
|
-
if (!examples.length || examples.length > 10)
|
|
5050
|
+
if (!examples.length || examples.length > 10) invalid2("\uD15C\uD50C\uB9BF \uC608\uC2DC\uB294 1~10\uAC1C \uD544\uC694\uD569\uB2C8\uB2E4.");
|
|
4002
5051
|
let validateExample;
|
|
4003
5052
|
try {
|
|
4004
|
-
const examplesAjv = new
|
|
5053
|
+
const examplesAjv = new Ajv20202({ allErrors: true, strict: false });
|
|
4005
5054
|
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);
|
|
4006
5055
|
validateExample = examplesAjv.compile(form);
|
|
4007
5056
|
} catch {
|
|
4008
|
-
return
|
|
5057
|
+
return invalid2("\uC81C\uC791 \uD3FC JSON Schema\uB97C \uD655\uC778\uD558\uC138\uC694.");
|
|
4009
5058
|
}
|
|
4010
5059
|
for (const path of examples) if (!validateExample(json(file(path, 64 * 1024), 64 * 1024, "\uC608\uC2DC"))) {
|
|
4011
|
-
|
|
5060
|
+
invalid2("\uC81C\uC791 \uD3FC\uACFC \uC608\uC2DC \uB370\uC774\uD130\uAC00 \uC77C\uCE58\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.");
|
|
4012
5061
|
}
|
|
4013
5062
|
}
|
|
4014
|
-
return {
|
|
5063
|
+
return {
|
|
5064
|
+
manifest,
|
|
5065
|
+
file_count: Object.keys(extracted).length,
|
|
5066
|
+
total_bytes: totalBytes,
|
|
5067
|
+
...editor ? { editor: editor.summary, warnings: editor.warnings } : {}
|
|
5068
|
+
};
|
|
5069
|
+
}
|
|
5070
|
+
function fillEditorHash(files) {
|
|
5071
|
+
const manifestBytes = files["clack.skill.json"];
|
|
5072
|
+
if (!manifestBytes) return null;
|
|
5073
|
+
let text;
|
|
5074
|
+
let manifest;
|
|
5075
|
+
try {
|
|
5076
|
+
text = textDecoder2.decode(manifestBytes);
|
|
5077
|
+
manifest = JSON.parse(text);
|
|
5078
|
+
} catch {
|
|
5079
|
+
return null;
|
|
5080
|
+
}
|
|
5081
|
+
if (!object(manifest)) return null;
|
|
5082
|
+
const computed = computeEditorHash(manifest, files);
|
|
5083
|
+
const editor = object(manifest.authoring) ? manifest.authoring.editor : void 0;
|
|
5084
|
+
if (!computed || !object(editor) || typeof editor.sha256 !== "string" || editor.sha256 === computed) return null;
|
|
5085
|
+
const declared = editor.sha256;
|
|
5086
|
+
const next = text.split(declared).length === 2 ? text.replace(declared, computed) : `${JSON.stringify({ ...manifest, authoring: { ...manifest.authoring, editor: { ...editor, sha256: computed } } }, null, 2)}
|
|
5087
|
+
`;
|
|
5088
|
+
return new TextEncoder().encode(next);
|
|
4015
5089
|
}
|
|
4016
|
-
async function prepareSkillPackage(source) {
|
|
5090
|
+
async function prepareSkillPackage(source, options2 = {}) {
|
|
4017
5091
|
const path = resolve7(source);
|
|
4018
5092
|
let info;
|
|
4019
5093
|
try {
|
|
4020
5094
|
info = await lstat4(path);
|
|
4021
5095
|
} catch {
|
|
4022
|
-
return
|
|
5096
|
+
return invalid2("\uC2A4\uD0AC \uB514\uB809\uD130\uB9AC \uB610\uB294 ZIP \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
4023
5097
|
}
|
|
4024
|
-
if (info.isSymbolicLink())
|
|
4025
|
-
let
|
|
5098
|
+
if (info.isSymbolicLink()) invalid2("\uC2A4\uD0AC \uACBD\uB85C\uB294 \uC2EC\uBCFC\uB9AD \uB9C1\uD06C\uC77C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
5099
|
+
let bytes2;
|
|
5100
|
+
let filledManifest;
|
|
4026
5101
|
if (info.isDirectory()) {
|
|
4027
5102
|
const files = await collectFiles(path);
|
|
4028
|
-
if (!Object.keys(files).length)
|
|
4029
|
-
|
|
4030
|
-
|
|
4031
|
-
|
|
4032
|
-
|
|
4033
|
-
|
|
5103
|
+
if (!Object.keys(files).length) invalid2("\uC2A4\uD0AC \uB514\uB809\uD130\uB9AC\uAC00 \uBE44\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.");
|
|
5104
|
+
if (options2.fillEditorHash) {
|
|
5105
|
+
const filled = fillEditorHash(files);
|
|
5106
|
+
if (filled) {
|
|
5107
|
+
files["clack.skill.json"] = filled;
|
|
5108
|
+
filledManifest = filled;
|
|
5109
|
+
}
|
|
5110
|
+
}
|
|
5111
|
+
bytes2 = Buffer.from(zipSync(files, { level: 6, mtime: new Date(2e3, 0, 1) }));
|
|
5112
|
+
} else if (info.isFile() && extname2(path).toLowerCase() === ".zip") bytes2 = await readRegular(path);
|
|
5113
|
+
else return invalid2("\uC2A4\uD0AC \uB514\uB809\uD130\uB9AC \uB610\uB294 ZIP \uD30C\uC77C\uC744 \uC9C0\uC815\uD558\uC138\uC694.");
|
|
5114
|
+
const inspected = inspectZip(
|
|
5115
|
+
bytes2,
|
|
5116
|
+
info.isDirectory() ? basename4(path) : void 0,
|
|
5117
|
+
info.isDirectory() && !options2.fillEditorHash ? "warn" : "error"
|
|
5118
|
+
);
|
|
5119
|
+
return { bytes: bytes2, sha256: hash(bytes2), ...inspected, ...filledManifest ? { filled_manifest: filledManifest } : {} };
|
|
4034
5120
|
}
|
|
4035
5121
|
|
|
4036
5122
|
// src/commands/skill.ts
|
|
@@ -4061,14 +5147,19 @@ function serverData(schema, value2) {
|
|
|
4061
5147
|
if (!parsed.success) throw new CliError("\uC2A4\uD0AC API \uC751\uB2F5 \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.", "INVALID_RESPONSE", 502);
|
|
4062
5148
|
return parsed.data;
|
|
4063
5149
|
}
|
|
4064
|
-
|
|
5150
|
+
var PAT_FOR_WRITE = "\uC2A4\uD0AC \uC11C\uBC84 \uAC80\uC99D\xB7\uC5C5\uB85C\uB4DC\xB7\uC644\uB8CC \uCC98\uB9AC\uC5D0\uB294 skill:write \uAD8C\uD55C\uC758 \uAC1C\uC778 \uC561\uC138\uC2A4 \uD1A0\uD070(pat_)\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.";
|
|
5151
|
+
var PAT_FOR_SUBMIT = "\uC2A4\uD0AC \uC2EC\uC0AC \uC81C\uCD9C\uC5D0\uB294 skill:write\uC640 skill:publish \uAD8C\uD55C(\uB458 \uB2E4)\uC758 \uAC1C\uC778 \uC561\uC138\uC2A4 \uD1A0\uD070(pat_)\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.";
|
|
5152
|
+
var PAT_FOR_CANCEL = "\uC2A4\uD0AC \uC5C5\uB85C\uB4DC\xB7\uC2EC\uC0AC \uCDE8\uC18C\uC5D0\uB294 skill:write \uAD8C\uD55C\uC758 \uAC1C\uC778 \uC561\uC138\uC2A4 \uD1A0\uD070(pat_)\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.";
|
|
5153
|
+
var PAT_FOR_RELEASE = "\uC2A4\uD0AC \uAC8C\uC2DC\uC5D0\uB294 skill:publish \uAD8C\uD55C\uC758 \uAC1C\uC778 \uC561\uC138\uC2A4 \uD1A0\uD070(pat_)\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.";
|
|
5154
|
+
var PAT_FOR_DEPRECATE = "\uC2A4\uD0AC \uC9C0\uC6D0 \uC885\uB8CC\uC5D0\uB294 skill:publish \uAD8C\uD55C\uC758 \uAC1C\uC778 \uC561\uC138\uC2A4 \uD1A0\uD070(pat_)\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.";
|
|
5155
|
+
function ensurePat(client, message = PAT_FOR_WRITE) {
|
|
4065
5156
|
const token = client.settings.token;
|
|
4066
5157
|
if (!token || !/^pat_[a-f0-9]{64}$/.test(token)) {
|
|
4067
5158
|
throw new CliError(message, "SKILL_PAT_REQUIRED", 401);
|
|
4068
5159
|
}
|
|
4069
5160
|
return token;
|
|
4070
5161
|
}
|
|
4071
|
-
async function validateSkillRemotely(client,
|
|
5162
|
+
async function validateSkillRemotely(client, bytes2) {
|
|
4072
5163
|
const token = ensurePat(client);
|
|
4073
5164
|
const url = new URL("/v4/creator/skills/validate", client.settings.baseUrl);
|
|
4074
5165
|
let response;
|
|
@@ -4086,7 +5177,7 @@ async function validateSkillRemotely(client, bytes) {
|
|
|
4086
5177
|
"X-CLACK-Time-Contract": "utc-v1",
|
|
4087
5178
|
"Cache-Control": "no-store"
|
|
4088
5179
|
},
|
|
4089
|
-
body: new Uint8Array(
|
|
5180
|
+
body: new Uint8Array(bytes2),
|
|
4090
5181
|
redirect: "error",
|
|
4091
5182
|
signal: AbortSignal.timeout(3e4)
|
|
4092
5183
|
});
|
|
@@ -4146,8 +5237,8 @@ async function pushSkillVersion(client, prepared, skillId) {
|
|
|
4146
5237
|
}
|
|
4147
5238
|
}
|
|
4148
5239
|
function registerSkillCommands(program2, runtime2) {
|
|
4149
|
-
const skill = program2.command("skill").description("\uC2A4\uD0AC \uD328\uD0A4\uC9C0 \uAC80\uC0AC\xB7\uC5C5\uB85C\uB4DC");
|
|
4150
|
-
runtime2.action(skill.command("list").option("-q, --query <text>", "\uC2A4\uD0AC \uC774\uB984 \uAC80\uC0C9").option("--category <category>", "\uBD84\uB958").option("--entity <entity>", "\uC0B0\uCD9C\uBB3C \uC5D4\uD2F0\uD2F0(character)").option("--type <type>", "\uC2A4\uD0AC \uC720\uD615(template|instruction|tool)").option("--official <value>", "\uACF5\uC2DD \uC2A4\uD0AC\uB9CC true \uB610\uB294 false").option("--cursor <id>", "\uB2E4\uC74C \uD398\uC774\uC9C0 UUID").option("--all", "\uBAA8\uB4E0 \uD398\uC774\uC9C0 \uC870\uD68C"), async (ctx, _args, opts) => {
|
|
5240
|
+
const skill = program2.command("skill").description("\uC2A4\uD0AC \uD328\uD0A4\uC9C0 \uAC80\uC0AC\xB7\uC5C5\uB85C\uB4DC\xB7\uC2EC\uC0AC\xB7\uAC8C\uC2DC (skill:read\xB7write\xB7publish)");
|
|
5241
|
+
runtime2.action(skill.command("list").description("\uC2A4\uD0AC \uBAA9\uB85D \uAC80\uC0C9 (skill:read)").option("-q, --query <text>", "\uC2A4\uD0AC \uC774\uB984 \uAC80\uC0C9").option("--category <category>", "\uBD84\uB958").option("--entity <entity>", "\uC0B0\uCD9C\uBB3C \uC5D4\uD2F0\uD2F0(character)").option("--type <type>", "\uC2A4\uD0AC \uC720\uD615(template|instruction|tool)").option("--official <value>", "\uACF5\uC2DD \uC2A4\uD0AC\uB9CC true \uB610\uB294 false").option("--cursor <id>", "\uB2E4\uC74C \uD398\uC774\uC9C0 UUID").option("--all", "\uBAA8\uB4E0 \uD398\uC774\uC9C0 \uC870\uD68C"), async (ctx, _args, opts) => {
|
|
4151
5242
|
const query = opts.query === void 0 ? void 0 : z9.string().trim().min(1).max(100).safeParse(opts.query);
|
|
4152
5243
|
if (query && !query.success) throw new CliError("\uAC80\uC0C9\uC5B4\uB294 1~100\uC790\uC5EC\uC57C \uD569\uB2C8\uB2E4.");
|
|
4153
5244
|
const selectedCategory = opts.category === void 0 ? void 0 : category.safeParse(opts.category);
|
|
@@ -4172,13 +5263,13 @@ function registerSkillCommands(program2, runtime2) {
|
|
|
4172
5263
|
}, contract });
|
|
4173
5264
|
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);
|
|
4174
5265
|
if (!opts.all) {
|
|
4175
|
-
ctx.output(result);
|
|
5266
|
+
ctx.output({ ...result, data: markCenterOnly(result.data) });
|
|
4176
5267
|
return;
|
|
4177
5268
|
}
|
|
4178
5269
|
contract = result.time_contract;
|
|
4179
5270
|
all.push(...result.data);
|
|
4180
5271
|
if (!result.pagination?.has_more) {
|
|
4181
|
-
ctx.output({ data: all, time_contract: result.time_contract });
|
|
5272
|
+
ctx.output({ data: markCenterOnly(all), time_contract: result.time_contract });
|
|
4182
5273
|
return;
|
|
4183
5274
|
}
|
|
4184
5275
|
const next = result.pagination.next_cursor;
|
|
@@ -4189,44 +5280,91 @@ function registerSkillCommands(program2, runtime2) {
|
|
|
4189
5280
|
cursor = next;
|
|
4190
5281
|
}
|
|
4191
5282
|
});
|
|
4192
|
-
runtime2.action(skill.command("get <slug>"), async (ctx, [value2]) => {
|
|
5283
|
+
runtime2.action(skill.command("get <slug>").description("\uC2A4\uD0AC \uC0C1\uC138 \uC870\uD68C (skill:read)"), async (ctx, [value2]) => {
|
|
4193
5284
|
const parsed = skillSlug.safeParse(value2);
|
|
4194
5285
|
if (!parsed.success) throw new CliError("\uC2A4\uD0AC \uC774\uB984\uC744 \uD655\uC778\uD558\uC138\uC694.");
|
|
4195
|
-
|
|
5286
|
+
const detail = await ctx.api.request("GET", `/v4/skills/${parsed.data}`);
|
|
5287
|
+
ctx.output({ ...detail, data: markCenterOnly(detail.data) });
|
|
4196
5288
|
});
|
|
4197
|
-
runtime2.action(skill.command("form <slug> <version>"), async (ctx, [value2, version]) => {
|
|
5289
|
+
runtime2.action(skill.command("form <slug> <version>").description("\uC2A4\uD0AC \uBC84\uC804 \uC785\uB825 \uC591\uC2DD \uC870\uD68C (skill:read)"), async (ctx, [value2, version]) => {
|
|
4198
5290
|
const parsed = skillSlug.safeParse(value2);
|
|
4199
5291
|
const parsedVersion = semver.safeParse(version);
|
|
4200
5292
|
if (!parsed.success || !parsedVersion.success) throw new CliError("\uC2A4\uD0AC \uC774\uB984\uACFC \uBC84\uC804\uC744 \uD655\uC778\uD558\uC138\uC694.");
|
|
4201
5293
|
ctx.output(await ctx.api.request("GET", `/v4/skills/${parsed.data}/versions/${parsedVersion.data}/form`));
|
|
4202
5294
|
});
|
|
4203
|
-
runtime2.action(skill.command("status <skill-id> <version-id>"), async (ctx, [skillId, versionId]) => {
|
|
5295
|
+
runtime2.action(skill.command("status <skill-id> <version-id>").description("\uB0B4 \uC2A4\uD0AC \uBC84\uC804\uC758 \uC2EC\uC0AC\xB7\uAC8C\uC2DC \uC0C1\uD0DC \uC870\uD68C (skill:read)"), async (ctx, [skillId, versionId]) => {
|
|
4204
5296
|
const ids = skillVersionIds(skillId, versionId);
|
|
4205
5297
|
ctx.output(await ctx.api.request("GET", `/v4/creator/skills/${ids.skillId}/versions/${ids.versionId}`));
|
|
4206
5298
|
});
|
|
4207
|
-
runtime2.action(skill.command("validate [dir]").option("--remote", "\uC11C\uBC84\uC5D0\uC11C\uB3C4 \uD328\uD0A4\uC9C0\uB97C \uB4DC\uB77C\uC774\uB7F0 \uAC80\uC99D"), async (ctx, [dir], opts) => {
|
|
5299
|
+
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) => {
|
|
4208
5300
|
const prepared = await prepareSkillPackage(dir ?? ".");
|
|
4209
|
-
|
|
4210
|
-
const remote = await validateSkillRemotely(ctx.api, prepared.bytes);
|
|
4211
|
-
ctx.output({
|
|
4212
|
-
local_valid: true,
|
|
4213
|
-
sha256: prepared.sha256,
|
|
4214
|
-
byte_size: prepared.bytes.length,
|
|
4215
|
-
file_count: prepared.file_count,
|
|
4216
|
-
manifest: prepared.manifest,
|
|
4217
|
-
remote
|
|
4218
|
-
});
|
|
4219
|
-
} else ctx.output({
|
|
5301
|
+
const local = {
|
|
4220
5302
|
local_valid: true,
|
|
4221
5303
|
sha256: prepared.sha256,
|
|
4222
5304
|
byte_size: prepared.bytes.length,
|
|
4223
5305
|
file_count: prepared.file_count,
|
|
4224
5306
|
manifest: prepared.manifest,
|
|
4225
|
-
...
|
|
5307
|
+
...editorReport(prepared)
|
|
5308
|
+
};
|
|
5309
|
+
if (opts.remote && !ctx.options.dryRun) {
|
|
5310
|
+
const remote = await validateSkillRemotely(ctx.api, prepared.bytes);
|
|
5311
|
+
ctx.output({ ...local, remote });
|
|
5312
|
+
} else ctx.output({ ...local, ...opts.remote ? { remote_skipped: true } : {} });
|
|
5313
|
+
});
|
|
5314
|
+
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) => {
|
|
5315
|
+
const source = dir ?? ".";
|
|
5316
|
+
const prepared = await prepareSkillPackage(source, { fillEditorHash: true });
|
|
5317
|
+
const out = resolve8(opts.out ?? `${prepared.manifest.name}-${prepared.manifest.version}.zip`);
|
|
5318
|
+
const writeManifest = Boolean(opts.writeManifest && prepared.filled_manifest);
|
|
5319
|
+
if (!ctx.options.dryRun) {
|
|
5320
|
+
await writeFile2(out, prepared.bytes);
|
|
5321
|
+
if (writeManifest) await writeFile2(join3(resolve8(source), "clack.skill.json"), prepared.filled_manifest);
|
|
5322
|
+
}
|
|
5323
|
+
ctx.output({
|
|
5324
|
+
...ctx.options.dryRun ? { dry_run: true } : {},
|
|
5325
|
+
out,
|
|
5326
|
+
sha256: prepared.sha256,
|
|
5327
|
+
byte_size: prepared.bytes.length,
|
|
5328
|
+
file_count: prepared.file_count,
|
|
5329
|
+
slug: prepared.manifest.name,
|
|
5330
|
+
version: prepared.manifest.version,
|
|
5331
|
+
...prepared.filled_manifest ? { editor_hash_filled: true, manifest_written: writeManifest && !ctx.options.dryRun } : {},
|
|
5332
|
+
...editorReport(prepared)
|
|
4226
5333
|
});
|
|
4227
5334
|
});
|
|
4228
|
-
|
|
4229
|
-
|
|
5335
|
+
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");
|
|
5336
|
+
runtime2.action(
|
|
5337
|
+
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", `
|
|
5338
|
+
${SDK_LOCATION_HELP}
|
|
5339
|
+
`),
|
|
5340
|
+
async (ctx, _args, opts) => {
|
|
5341
|
+
const args = [];
|
|
5342
|
+
for (const [flag, value2] of [
|
|
5343
|
+
["editor", opts.editor],
|
|
5344
|
+
["document", opts.document],
|
|
5345
|
+
["schema", opts.schema],
|
|
5346
|
+
["host-port", opts.hostPort],
|
|
5347
|
+
["editor-port", opts.editorPort]
|
|
5348
|
+
]) {
|
|
5349
|
+
if (value2 === void 0) continue;
|
|
5350
|
+
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.");
|
|
5351
|
+
args.push(`--${flag}`, flag.endsWith("port") ? value2 : resolve8(value2));
|
|
5352
|
+
}
|
|
5353
|
+
const sdkDir = resolveSdkDir(opts.sdkDir);
|
|
5354
|
+
const serve = join3(sdkDir, "mock-host/serve.mjs");
|
|
5355
|
+
if (ctx.options.dryRun) {
|
|
5356
|
+
ctx.output({ dry_run: true, sdk_dir: sdkDir, command: ["node", serve, ...args] });
|
|
5357
|
+
return;
|
|
5358
|
+
}
|
|
5359
|
+
await new Promise((done, fail2) => {
|
|
5360
|
+
const child = spawn(process.execPath, [serve, ...args], { stdio: "inherit" });
|
|
5361
|
+
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")));
|
|
5362
|
+
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());
|
|
5363
|
+
});
|
|
5364
|
+
}
|
|
5365
|
+
);
|
|
5366
|
+
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) => {
|
|
5367
|
+
const prepared = await prepareSkillPackage(dir ?? ".", { fillEditorHash: true });
|
|
4230
5368
|
if (prepared.manifest.type !== "instruction" && prepared.manifest.type !== "template") {
|
|
4231
5369
|
throw new CliError("\uD604\uC7AC \uC2A4\uD0AC \uC5C5\uB85C\uB4DC\uB294 instruction\xB7template \uC720\uD615\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4.", "SKILL_TYPE_UNSUPPORTED");
|
|
4232
5370
|
}
|
|
@@ -4247,7 +5385,7 @@ function registerSkillCommands(program2, runtime2) {
|
|
|
4247
5385
|
}
|
|
4248
5386
|
ctx.output(await pushSkillVersion(ctx.api, prepared, skillIdOption?.data));
|
|
4249
5387
|
});
|
|
4250
|
-
runtime2.action(skill.command("complete <skill-id> <version-id>"), async (ctx, [skillId, versionId]) => {
|
|
5388
|
+
runtime2.action(skill.command("complete <skill-id> <version-id>").description("\uC5C5\uB85C\uB4DC\uD55C \uC2A4\uD0AC \uBC84\uC804 \uC644\uB8CC \uCC98\uB9AC \uC7AC\uC2DC\uB3C4 (skill:write)"), async (ctx, [skillId, versionId]) => {
|
|
4251
5389
|
ensurePat(ctx.api);
|
|
4252
5390
|
const parsedSkillId = uuid2.safeParse(skillId);
|
|
4253
5391
|
const parsedVersionId = uuid2.safeParse(versionId);
|
|
@@ -4258,8 +5396,8 @@ function registerSkillCommands(program2, runtime2) {
|
|
|
4258
5396
|
}
|
|
4259
5397
|
ctx.output(await ctx.api.request("POST", `/v4/creator/skills/${parsedSkillId.data}/versions/${parsedVersionId.data}/complete`));
|
|
4260
5398
|
});
|
|
4261
|
-
runtime2.action(skill.command("submit <skill-id> <version-id>"), async (ctx, [skillId, versionId]) => {
|
|
4262
|
-
ensurePat(ctx.api,
|
|
5399
|
+
runtime2.action(skill.command("submit <skill-id> <version-id>").description("\uAC80\uC99D \uC644\uB8CC\uD55C \uC2A4\uD0AC \uBC84\uC804 \uC2EC\uC0AC \uC81C\uCD9C (skill:write\uC640 skill:publish \uB458 \uB2E4 \uD544\uC694)"), async (ctx, [skillId, versionId]) => {
|
|
5400
|
+
ensurePat(ctx.api, PAT_FOR_SUBMIT);
|
|
4263
5401
|
const ids = skillVersionIds(skillId, versionId);
|
|
4264
5402
|
const path = `/v4/creator/skills/${ids.skillId}/versions/${ids.versionId}`;
|
|
4265
5403
|
const current = serverData(managedVersionSchema, (await ctx.api.request("GET", path)).data);
|
|
@@ -4279,8 +5417,8 @@ function registerSkillCommands(program2, runtime2) {
|
|
|
4279
5417
|
await ctx.confirm(`${current.version} \uBC84\uC804(\uD328\uD0A4\uC9C0 ${current.package_hash})\uC744 \uC2EC\uC0AC\uC5D0 \uC81C\uCD9C\uD560\uAE4C\uC694?`);
|
|
4280
5418
|
ctx.output(await ctx.api.request("POST", `${path}/submit`));
|
|
4281
5419
|
});
|
|
4282
|
-
runtime2.action(skill.command("deprecate <skill-id>").description("\uC2A4\uD0AC\uC744 \uC9C0\uC6D0 \uC885\uB8CC\uD55C\uB2E4 (\uB418\uB3CC\uB9B4 \uC218 \uC5C6\uC74C, \uC0C8 \uC124\uCE58\xB7\uC0C8 \uC81C\uC791 \uBD88\uAC00)"), async (ctx, [skillId]) => {
|
|
4283
|
-
ensurePat(ctx.api);
|
|
5420
|
+
runtime2.action(skill.command("deprecate <skill-id>").description("\uC2A4\uD0AC\uC744 \uC9C0\uC6D0 \uC885\uB8CC\uD55C\uB2E4 (\uB418\uB3CC\uB9B4 \uC218 \uC5C6\uC74C, \uC0C8 \uC124\uCE58\xB7\uC0C8 \uC81C\uC791 \uBD88\uAC00, skill:publish)"), async (ctx, [skillId]) => {
|
|
5421
|
+
ensurePat(ctx.api, PAT_FOR_DEPRECATE);
|
|
4284
5422
|
const parsedSkillId = uuid2.safeParse(skillId);
|
|
4285
5423
|
if (!parsedSkillId.success) throw new CliError("\uC2A4\uD0AC ID\uB294 UUID\uC5EC\uC57C \uD569\uB2C8\uB2E4.");
|
|
4286
5424
|
const path = `/v4/creator/skills/${parsedSkillId.data}/deprecate`;
|
|
@@ -4291,8 +5429,8 @@ function registerSkillCommands(program2, runtime2) {
|
|
|
4291
5429
|
await ctx.confirm("\uC774 \uC2A4\uD0AC\uC744 \uC9C0\uC6D0 \uC885\uB8CC\uD560\uAE4C\uC694? \uB418\uB3CC\uB9B4 \uC218 \uC5C6\uC73C\uBA70 \uC0C8 \uC124\uCE58\xB7\uC0C8 \uC81C\uC791\uC774 \uBD88\uAC00\uB2A5\uD569\uB2C8\uB2E4(\uAE30\uC874 \uCF58\uD150\uCE20\uC758 \uACE0\uC815 \uBC84\uC804\uC740 \uC720\uC9C0\uB429\uB2C8\uB2E4).");
|
|
4292
5430
|
ctx.output(await ctx.api.request("POST", path));
|
|
4293
5431
|
});
|
|
4294
|
-
runtime2.action(skill.command("cancel <skill-id> <version-id>"), async (ctx, [skillId, versionId]) => {
|
|
4295
|
-
ensurePat(ctx.api);
|
|
5432
|
+
runtime2.action(skill.command("cancel <skill-id> <version-id>").description("\uC2A4\uD0AC \uBC84\uC804\uC758 \uC5C5\uB85C\uB4DC \uB610\uB294 \uC2EC\uC0AC \uCDE8\uC18C (skill:write)"), async (ctx, [skillId, versionId]) => {
|
|
5433
|
+
ensurePat(ctx.api, PAT_FOR_CANCEL);
|
|
4296
5434
|
const ids = skillVersionIds(skillId, versionId);
|
|
4297
5435
|
const path = `/v4/creator/skills/${ids.skillId}/versions/${ids.versionId}`;
|
|
4298
5436
|
if (ctx.options.dryRun) {
|
|
@@ -4303,9 +5441,9 @@ function registerSkillCommands(program2, runtime2) {
|
|
|
4303
5441
|
ctx.output(await ctx.api.request("POST", `${path}/cancel`));
|
|
4304
5442
|
});
|
|
4305
5443
|
runtime2.action(
|
|
4306
|
-
skill.command("release <skill-id> <version-id>").requiredOption("--visibility <value>", "private|unlisted|public"),
|
|
5444
|
+
skill.command("release <skill-id> <version-id>").description("\uC2B9\uC778\uB41C \uC2A4\uD0AC \uBC84\uC804\uC744 \uACF5\uAC1C \uBC94\uC704\uB85C \uAC8C\uC2DC (skill:publish)").requiredOption("--visibility <value>", "private|unlisted|public"),
|
|
4307
5445
|
async (ctx, [skillId, versionId], opts) => {
|
|
4308
|
-
ensurePat(ctx.api);
|
|
5446
|
+
ensurePat(ctx.api, PAT_FOR_RELEASE);
|
|
4309
5447
|
const ids = skillVersionIds(skillId, versionId);
|
|
4310
5448
|
const selected = visibility.safeParse(opts.visibility);
|
|
4311
5449
|
if (!selected.success) throw new CliError("\uACF5\uAC1C \uBC94\uC704\uB294 private, unlisted, public \uC911 \uD558\uB098\uC5EC\uC57C \uD569\uB2C8\uB2E4.");
|
|
@@ -4330,6 +5468,39 @@ function registerSkillCommands(program2, runtime2) {
|
|
|
4330
5468
|
}
|
|
4331
5469
|
);
|
|
4332
5470
|
}
|
|
5471
|
+
function markCenterOnly(value2) {
|
|
5472
|
+
const mark2 = (item) => {
|
|
5473
|
+
if (!item || typeof item !== "object" || Array.isArray(item)) return item;
|
|
5474
|
+
const row = item;
|
|
5475
|
+
return row.center_only === true || row.editor === true ? { ...row, center_only: true, availability: "\uC13C\uD130 \uC804\uC6A9" } : row;
|
|
5476
|
+
};
|
|
5477
|
+
return Array.isArray(value2) ? value2.map(mark2) : mark2(value2);
|
|
5478
|
+
}
|
|
5479
|
+
function editorReport(prepared) {
|
|
5480
|
+
if (!prepared.editor) return {};
|
|
5481
|
+
return {
|
|
5482
|
+
editor: prepared.editor,
|
|
5483
|
+
center_only: true,
|
|
5484
|
+
...prepared.warnings?.length ? { warnings: prepared.warnings } : {},
|
|
5485
|
+
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."
|
|
5486
|
+
};
|
|
5487
|
+
}
|
|
5488
|
+
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.";
|
|
5489
|
+
function resolveSdkDir(option) {
|
|
5490
|
+
const candidates = [];
|
|
5491
|
+
if (option) candidates.push(resolve8(option));
|
|
5492
|
+
else {
|
|
5493
|
+
if (process.env.CLACK_SKILL_EDITOR_SDK_DIR) candidates.push(resolve8(process.env.CLACK_SKILL_EDITOR_SDK_DIR));
|
|
5494
|
+
try {
|
|
5495
|
+
candidates.push(resolve8(createRequire(join3(process.cwd(), "noop.js")).resolve("@clack/skill-editor-sdk/package.json"), ".."));
|
|
5496
|
+
} catch {
|
|
5497
|
+
}
|
|
5498
|
+
if (process.env.CLACK_MONOREPO_DIR) candidates.push(resolve8(process.env.CLACK_MONOREPO_DIR, "clack-skill-editor-sdk"));
|
|
5499
|
+
}
|
|
5500
|
+
const found = candidates.find((dir) => existsSync(join3(dir, "mock-host/serve.mjs")));
|
|
5501
|
+
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");
|
|
5502
|
+
return found;
|
|
5503
|
+
}
|
|
4333
5504
|
function skillVersionIds(skillId, versionId) {
|
|
4334
5505
|
const parsedSkillId = uuid2.safeParse(skillId);
|
|
4335
5506
|
const parsedVersionId = uuid2.safeParse(versionId);
|
|
@@ -4387,7 +5558,7 @@ function parsePat(schema, input, label) {
|
|
|
4387
5558
|
return parsed.data;
|
|
4388
5559
|
}
|
|
4389
5560
|
var toolDefinitions = [
|
|
4390
|
-
{ name: "platform_authoring", description: "\uACF5\uAC1C \uC2A4\uD0AC \uC81C\uC791 \uC138\uC158\xB7\uD3FC\xB7\uC790\uC0B0\xB7\uC774\uBBF8\uC9C0 \uC0DD\uC131\xB7\uD328\uD0A4\uC9D5\uC744 \uCC98\uB9AC\uD569\uB2C8\uB2E4. \uC774\uBBF8\uC9C0 \uC0DD\uC131\uC5D0\uB294 \uACAC\uC801 \uC2B9\uC778 \uAC00\uACA9\uACFC \uBA71\uB4F1\uC131 \uD0A4\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.", inputSchema: { type: "object", ...z10.toJSONSchema(authoringInput) }, annotations: { readOnlyHint: false, destructiveHint: false } },
|
|
5561
|
+
{ name: "platform_authoring", description: "\uACF5\uAC1C \uC2A4\uD0AC \uC81C\uC791 \uC138\uC158\xB7\uD3FC\xB7\uC790\uC0B0\xB7\uC774\uBBF8\uC9C0 \uC0DD\uC131\xB7\uD328\uD0A4\uC9D5\uC744 \uCC98\uB9AC\uD569\uB2C8\uB2E4. \uC774\uBBF8\uC9C0 \uC0DD\uC131\uC5D0\uB294 \uACAC\uC801 \uC2B9\uC778 \uAC00\uACA9\uACFC \uBA71\uB4F1\uC131 \uD0A4\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. creator-content:write \uAD8C\uD55C\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.", inputSchema: { type: "object", ...z10.toJSONSchema(authoringInput) }, annotations: { readOnlyHint: false, destructiveHint: false } },
|
|
4391
5562
|
{
|
|
4392
5563
|
name: "platform_usage_get",
|
|
4393
5564
|
description: "\uC11C\uBC84 \uD0A4\uAC00 \uC18D\uD55C \uCF58\uD150\uCE20\uC758 \uD50C\uB7AB\uD3FC \uC0AC\uC6A9\uB7C9\uC744 \uC870\uD68C\uD569\uB2C8\uB2E4.",
|
|
@@ -4433,13 +5604,13 @@ var toolDefinitions = [
|
|
|
4433
5604
|
// 아래는 개인 액세스 토큰(CLACK_TOKEN, pat_)으로 동작하는 개인 도구다. 서버 키 도구와 인증 평면이 다르다.
|
|
4434
5605
|
{
|
|
4435
5606
|
name: "platform_skill_list",
|
|
4436
|
-
description: "\uACF5\uAC1C \uC2A4\uD0AC \uBAA9\uB85D\uC744 \uC774\uB984\uC73C\uB85C \uAC80\uC0C9\uD569\uB2C8\uB2E4.",
|
|
5607
|
+
description: "\uACF5\uAC1C \uC2A4\uD0AC \uBAA9\uB85D\uC744 \uC774\uB984\uC73C\uB85C \uAC80\uC0C9\uD569\uB2C8\uB2E4. skill:read \uAD8C\uD55C\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.",
|
|
4437
5608
|
inputSchema: z10.toJSONSchema(patSchemas.skillList),
|
|
4438
5609
|
annotations: { readOnlyHint: true, destructiveHint: false }
|
|
4439
5610
|
},
|
|
4440
5611
|
{
|
|
4441
5612
|
name: "platform_skill_get",
|
|
4442
|
-
description: "\uC2A4\uD0AC \uC0C1\uC138\uB97C \uC774\uB984(slug)\uC73C\uB85C \uC870\uD68C\uD569\uB2C8\uB2E4.",
|
|
5613
|
+
description: "\uC2A4\uD0AC \uC0C1\uC138\uB97C \uC774\uB984(slug)\uC73C\uB85C \uC870\uD68C\uD569\uB2C8\uB2E4. skill:read \uAD8C\uD55C\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.",
|
|
4443
5614
|
inputSchema: z10.toJSONSchema(patSchemas.skillGet),
|
|
4444
5615
|
annotations: { readOnlyHint: true, destructiveHint: false }
|
|
4445
5616
|
},
|
|
@@ -4451,13 +5622,13 @@ var toolDefinitions = [
|
|
|
4451
5622
|
},
|
|
4452
5623
|
{
|
|
4453
5624
|
name: "platform_skill_status",
|
|
4454
|
-
description: "\uB0B4 \uC2A4\uD0AC \uBC84\uC804\uC758 \uC2EC\uC0AC\xB7\uAC8C\uC2DC \uC0C1\uD0DC\uB97C \uC870\uD68C\uD569\uB2C8\uB2E4.",
|
|
5625
|
+
description: "\uB0B4 \uC2A4\uD0AC \uBC84\uC804\uC758 \uC2EC\uC0AC\xB7\uAC8C\uC2DC \uC0C1\uD0DC\uB97C \uC870\uD68C\uD569\uB2C8\uB2E4. skill:read \uAD8C\uD55C\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.",
|
|
4455
5626
|
inputSchema: z10.toJSONSchema(patSchemas.skillVersionRef),
|
|
4456
5627
|
annotations: { readOnlyHint: true, destructiveHint: false }
|
|
4457
5628
|
},
|
|
4458
5629
|
{
|
|
4459
5630
|
name: "platform_skill_submit",
|
|
4460
|
-
description: "\uAC80\uC99D \uC644\uB8CC\uD55C \uC2A4\uD0AC \uBC84\uC804\uC744 \uC2EC\uC0AC\uC5D0 \uC81C\uCD9C\uD569\uB2C8\uB2E4. skill:write\
|
|
5631
|
+
description: "\uAC80\uC99D \uC644\uB8CC\uD55C \uC2A4\uD0AC \uBC84\uC804\uC744 \uC2EC\uC0AC\uC5D0 \uC81C\uCD9C\uD569\uB2C8\uB2E4. skill:write\uC640 skill:publish \uAD8C\uD55C(\uB458 \uB2E4)\uACFC confirm:true\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.",
|
|
4461
5632
|
inputSchema: z10.toJSONSchema(patSchemas.skillSubmit),
|
|
4462
5633
|
annotations: { readOnlyHint: false, destructiveHint: true }
|
|
4463
5634
|
},
|
|
@@ -4507,7 +5678,7 @@ var toolDefinitions = [
|
|
|
4507
5678
|
function error(id2, code, message) {
|
|
4508
5679
|
return { jsonrpc: "2.0", id: id2, error: { code, message } };
|
|
4509
5680
|
}
|
|
4510
|
-
function
|
|
5681
|
+
function isObject3(value2) {
|
|
4511
5682
|
return value2 !== null && typeof value2 === "object" && !Array.isArray(value2);
|
|
4512
5683
|
}
|
|
4513
5684
|
function toolResult(value2, isError = false) {
|
|
@@ -4526,7 +5697,7 @@ var PlatformMcpServer = class {
|
|
|
4526
5697
|
env;
|
|
4527
5698
|
initialized = false;
|
|
4528
5699
|
async handle(message) {
|
|
4529
|
-
if (!
|
|
5700
|
+
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) {
|
|
4530
5701
|
return error(null, -32600, "\uC62C\uBC14\uB978 JSON-RPC \uC694\uCCAD\uC774 \uC544\uB2D9\uB2C8\uB2E4.");
|
|
4531
5702
|
}
|
|
4532
5703
|
const request = message;
|
|
@@ -4535,7 +5706,7 @@ var PlatformMcpServer = class {
|
|
|
4535
5706
|
return void 0;
|
|
4536
5707
|
}
|
|
4537
5708
|
if (request.method === "initialize") {
|
|
4538
|
-
const requested =
|
|
5709
|
+
const requested = isObject3(request.params) && typeof request.params.protocolVersion === "string" ? request.params.protocolVersion : "";
|
|
4539
5710
|
if (!requested) return error(request.id, -32602, "\uD504\uB85C\uD1A0\uCF5C \uBC84\uC804\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.");
|
|
4540
5711
|
this.initialized = false;
|
|
4541
5712
|
return { jsonrpc: "2.0", id: request.id, result: {
|
|
@@ -4548,7 +5719,7 @@ var PlatformMcpServer = class {
|
|
|
4548
5719
|
if (!this.initialized) return error(request.id, -32e3, "MCP \uCD08\uAE30\uD654\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.");
|
|
4549
5720
|
if (request.method === "tools/list") return { jsonrpc: "2.0", id: request.id, result: { tools: toolDefinitions } };
|
|
4550
5721
|
if (request.method !== "tools/call") return error(request.id, -32601, "\uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 MCP \uBA54\uC11C\uB4DC\uC785\uB2C8\uB2E4.");
|
|
4551
|
-
if (!
|
|
5722
|
+
if (!isObject3(request.params) || typeof request.params.name !== "string" || request.params.arguments !== void 0 && !isObject3(request.params.arguments)) {
|
|
4552
5723
|
return error(request.id, -32602, "\uB3C4\uAD6C \uC774\uB984\uACFC \uC778\uC790\uB97C \uD655\uC778\uD558\uC138\uC694.");
|
|
4553
5724
|
}
|
|
4554
5725
|
const name2 = request.params.name;
|
|
@@ -4558,7 +5729,7 @@ var PlatformMcpServer = class {
|
|
|
4558
5729
|
const writeKind = name2 === "platform_server_data_put" ? "put" : name2 === "platform_server_data_patch" ? "patch" : name2 === "platform_server_data_delete" ? "delete" : null;
|
|
4559
5730
|
if (writeKind) {
|
|
4560
5731
|
const preview = platformWritePreview(writeKind, args);
|
|
4561
|
-
if (!
|
|
5732
|
+
if (!isObject3(args) || args.confirm !== true) {
|
|
4562
5733
|
return { jsonrpc: "2.0", id: request.id, result: toolResult({ preview: true, confirmation_required: true, ...preview }) };
|
|
4563
5734
|
}
|
|
4564
5735
|
}
|
|
@@ -4574,9 +5745,9 @@ var PlatformMcpServer = class {
|
|
|
4574
5745
|
return { jsonrpc: "2.0", id: request.id, result: toolResult({ error: failure }, true) };
|
|
4575
5746
|
}
|
|
4576
5747
|
}
|
|
4577
|
-
/** 개인 액세스 토큰(platform:read·platform:write
|
|
5748
|
+
/** 개인 액세스 토큰(platform:read·platform:write, skill:*, creator-content:write·publish 스코프) 기반 도구. */
|
|
4578
5749
|
async callPatTool(name2, args) {
|
|
4579
|
-
if (patDestructiveToolNames.has(name2) && (!
|
|
5750
|
+
if (patDestructiveToolNames.has(name2) && (!isObject3(args) || args.confirm !== true)) {
|
|
4580
5751
|
return { preview: true, confirmation_required: true, tool: name2, arguments: redact(args) };
|
|
4581
5752
|
}
|
|
4582
5753
|
const client = platformPatClient(this.settings, this.env);
|
|
@@ -4591,7 +5762,7 @@ var PlatformMcpServer = class {
|
|
|
4591
5762
|
}
|
|
4592
5763
|
if (name2 === "platform_skill_push") {
|
|
4593
5764
|
const { dir, skill_id } = parsePat(patSchemas.skillPush, args, "\uC2A4\uD0AC \uC5C5\uB85C\uB4DC \uC785\uB825");
|
|
4594
|
-
const prepared = await prepareSkillPackage(dir ?? ".");
|
|
5765
|
+
const prepared = await prepareSkillPackage(dir ?? ".", { fillEditorHash: true });
|
|
4595
5766
|
if (prepared.manifest.type !== "instruction" && prepared.manifest.type !== "template") {
|
|
4596
5767
|
throw new CliError("\uD604\uC7AC \uC2A4\uD0AC \uC5C5\uB85C\uB4DC\uB294 instruction\xB7template \uC720\uD615\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4.", "SKILL_TYPE_UNSUPPORTED");
|
|
4597
5768
|
}
|
|
@@ -4655,6 +5826,19 @@ async function servePlatformMcp(settings, env = process.env) {
|
|
|
4655
5826
|
|
|
4656
5827
|
// src/commands/integrations.ts
|
|
4657
5828
|
var SKILLS_REPOSITORY = "https://github.com/clack-project/skills";
|
|
5829
|
+
var AGENT_SKILLS = [
|
|
5830
|
+
{ name: "clack-setup", audience: "general", minimum_cli_version: "0.1.1", cli_required: true },
|
|
5831
|
+
{ name: "clack-products", audience: "general", minimum_cli_version: "0.1.1", cli_required: true },
|
|
5832
|
+
{ name: "clack-content", audience: "general", minimum_cli_version: "0.1.1", cli_required: true },
|
|
5833
|
+
{ name: "clack-channel", audience: "general", minimum_cli_version: "0.1.1", cli_required: true },
|
|
5834
|
+
{ name: "clack-profile", audience: "general", minimum_cli_version: "0.1.1", cli_required: true },
|
|
5835
|
+
{ name: "clack-mcp", audience: "general", minimum_cli_version: "0.1.0", cli_required: false },
|
|
5836
|
+
{ name: "clack-creator-content", audience: "creator", minimum_cli_version: "0.1.1", cli_required: true },
|
|
5837
|
+
{ name: "clack-page", audience: "creator", minimum_cli_version: "0.1.1", cli_required: true },
|
|
5838
|
+
{ name: "clack-skill-package", audience: "creator", minimum_cli_version: "0.1.1", cli_required: true },
|
|
5839
|
+
{ name: "clack-platform-data", audience: "creator", minimum_cli_version: "0.1.1", cli_required: true }
|
|
5840
|
+
];
|
|
5841
|
+
var SKILLS_MINIMUM_CLI_VERSION = "0.1.1";
|
|
4658
5842
|
function quoteArgument(value2) {
|
|
4659
5843
|
return `'${value2.replaceAll("'", "'\\''")}'`;
|
|
4660
5844
|
}
|
|
@@ -4687,7 +5871,7 @@ function registerIntegrationCommands(program2, runtime2) {
|
|
|
4687
5871
|
const mcp = program2.command("mcp").description("MCP \uD074\uB77C\uC774\uC5B8\uD2B8 \uC5F0\uACB0\xB7\uD50C\uB7AB\uD3FC \uC870\uD68C \uB3C4\uAD6C");
|
|
4688
5872
|
runtime2.action(mcp.command("config").description("\uD1A0\uD070 \uC5C6\uB294 MCP \uC124\uC815 \uCD9C\uB825 (\uD30C\uC77C \uBCC0\uACBD\xB7\uC11C\uBC84 \uC694\uCCAD \uC5C6\uC74C)").option("--platform", "CLACK_SERVER_KEY\uB97C \uC0AC\uC6A9\uD558\uB294 \uB85C\uCEEC \uD50C\uB7AB\uD3FC \uC870\uD68C \uC11C\uBC84 \uC124\uC815").addOption(new Option2("--print", "Claude Code .mcp.json \uC870\uAC01 \uCD9C\uB825 (\uAE30\uBCF8)").conflicts(["claude", "codex"])).addOption(new Option2("--claude", "Claude Code \uB4F1\uB85D \uBA85\uB839 \uCD9C\uB825").conflicts(["print", "codex"])).addOption(new Option2("--codex", "Codex config.toml \uC870\uAC01 \uCD9C\uB825").conflicts(["print", "claude"])), async (ctx, _args, opts) => {
|
|
4689
5873
|
const config = opts.platform ? platformMcpConfiguration(ctx.api.settings.baseUrl) : mcpConfiguration(ctx.api.settings.baseUrl);
|
|
4690
|
-
const instructions = opts.platform ? "\uC5D0\uC774\uC804\uD2B8 \uC2E4\uD589 \uD658\uACBD\uC5D0 \uCF58\uD150\uCE20 \uC11C\uBC84 \uD0A4 \uB3C4\uAD6C\uB294 \uBC1C\uAE09\uBC1B\uC740 \uC11C\uBC84 \uD0A4\uB97C CLACK_SERVER_KEY\uB85C, \uC2A4\uD0AC\xB7\uCF58\uD150\uCE20 \uACF5\uAC1C\xB7\uACF5\uC720 \uBB38\uC11C \uB3C4\uAD6C\uB294 platform:read\xB7platform:write(\uD544\uC694 \uC2DC skill
|
|
5874
|
+
const instructions = opts.platform ? "\uC5D0\uC774\uC804\uD2B8 \uC2E4\uD589 \uD658\uACBD\uC5D0 \uCF58\uD150\uCE20 \uC11C\uBC84 \uD0A4 \uB3C4\uAD6C\uB294 \uBC1C\uAE09\uBC1B\uC740 \uC11C\uBC84 \uD0A4\uB97C CLACK_SERVER_KEY\uB85C, \uC2A4\uD0AC\xB7\uC81C\uC791\xB7\uCF58\uD150\uCE20 \uACF5\uAC1C\xB7\uACF5\uC720 \uBB38\uC11C \uB3C4\uAD6C\uB294 platform:read\xB7platform:write(\uD544\uC694 \uC2DC skill:*, \uC81C\uC791\uC740 creator-content:write, \uCF58\uD150\uCE20 \uACF5\uAC1C\uB294 creator-content:publish) \uAD8C\uD55C\uC758 \uAC1C\uC778 \uC561\uC138\uC2A4 \uD1A0\uD070\uC744 CLACK_TOKEN\uC73C\uB85C \uC124\uC815\uD558\uC138\uC694. \uD544\uC694\uD55C \uB3C4\uAD6C\uB9CC\uD07C\uB9CC \uC124\uC815\uD558\uBA74 \uB429\uB2C8\uB2E4. \uC124\uC815 \uCD9C\uB825\uC5D0\uB294 \uD0A4\xB7\uD1A0\uD070 \uC6D0\uBB38\uC744 \uB123\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uD658\uACBD\uACFC \uC124\uC815 \uC6D0\uC810\uC774 \uC77C\uCE58\uD558\uB294\uC9C0 \uD655\uC778\uD558\uC138\uC694." : "\uC571\uC5D0\uC11C \uBC1C\uAE09\uD55C \uAC1C\uC778 \uC561\uC138\uC2A4 \uD1A0\uD070\uC744 \uC5D0\uC774\uC804\uD2B8 \uC2E4\uD589 \uD658\uACBD\uC758 CLACK_TOKEN\uC5D0 \uC548\uC804\uD558\uAC8C \uC124\uC815\uD558\uC138\uC694. CLI\uC5D0 \uC800\uC7A5\uB41C \uD1A0\uD070\uC740 \uC790\uB3D9 \uC804\uB2EC\uB418\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uD1A0\uD070\uC744 \uB300\uD654\xB7\uBA85\uB839 \uAE30\uB85D\xB7\uC124\uC815 \uD30C\uC77C\uC5D0 \uBD99\uC5EC \uB123\uC9C0 \uB9C8\uC138\uC694. CLI \uD504\uB85C\uD544\uC744 \uBC14\uAFB8\uC5B4\uB3C4 \uAE30\uC874 MCP \uC124\uC815\uC740 \uAC31\uC2E0\uB418\uC9C0 \uC54A\uC73C\uBBC0\uB85C URL\uACFC \uD1A0\uD070\uC758 \uACC4\uC815\uC744 \uD655\uC778\uD558\uC138\uC694.";
|
|
4691
5875
|
if (ctx.options.json) process.stdout.write(JSON.stringify({ ok: true, data: {
|
|
4692
5876
|
...config,
|
|
4693
5877
|
token_env: opts.platform ? "CLACK_SERVER_KEY" : "CLACK_TOKEN",
|
|
@@ -4705,11 +5889,14 @@ function registerIntegrationCommands(program2, runtime2) {
|
|
|
4705
5889
|
runtime2.action(program2.command("skills").description("\uC5D0\uC774\uC804\uD2B8 \uC2A4\uD0AC \uC124\uCE58 \uBC29\uBC95 \uC548\uB0B4"), async (ctx) => {
|
|
4706
5890
|
ctx.output({
|
|
4707
5891
|
repository: SKILLS_REPOSITORY,
|
|
4708
|
-
minimum_cli_version:
|
|
5892
|
+
minimum_cli_version: SKILLS_MINIMUM_CLI_VERSION,
|
|
4709
5893
|
cli_version: VERSION,
|
|
5894
|
+
skill_count: AGENT_SKILLS.length,
|
|
5895
|
+
skills: AGENT_SKILLS,
|
|
4710
5896
|
install: "npx skills add clack-project/skills",
|
|
4711
5897
|
install_global: "npx skills add clack-project/skills -g",
|
|
4712
5898
|
install_selected: "npx skills add clack-project/skills -s clack-products",
|
|
5899
|
+
install_selected_multiple: "npx skills add clack-project/skills --skill clack-setup clack-creator-content clack-page",
|
|
4713
5900
|
update: "npx skills update",
|
|
4714
5901
|
remove: "npx skills remove",
|
|
4715
5902
|
message: `\uACF5\uAC1C \uC800\uC7A5\uC18C ${SKILLS_REPOSITORY}\uC5D0\uC11C npx skills\uB85C \uC124\uCE58\uD569\uB2C8\uB2E4. \uC774 \uBA85\uB839\uC740 \uC548\uB0B4\uB9CC \uCD9C\uB825\uD558\uBA70 \uC124\uCE58\uB098 \uB85C\uADF8\uC778\uC744 \uC2E4\uD589\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.`
|
|
@@ -4725,19 +5912,19 @@ var revision3 = (value2) => parse(z11.coerce.number().int().nonnegative().max(42
|
|
|
4725
5912
|
var base = "/v4/creator/custom-pages";
|
|
4726
5913
|
var presentationSchema = z11.object({
|
|
4727
5914
|
schema_version: z11.literal(1),
|
|
4728
|
-
header_mode: z11.enum(["fixed", "scroll_hide", "translucent_scroll_hide", "floating_close"]),
|
|
5915
|
+
header_mode: z11.enum(["fixed", "scroll_hide", "translucent_scroll_hide", "floating_close", "floating_close_safe_area"]),
|
|
4729
5916
|
color_scheme: z11.enum(["light", "dark"])
|
|
4730
5917
|
}).strict();
|
|
4731
5918
|
function registerPageCommands(program2, runtime2) {
|
|
4732
5919
|
const page = program2.command("page").description("\uC138\uACC4\uAD00\xB7\uB0B4 \uACF5\uAC1C \uD648 HTML \uAFB8\uBBF8\uAE30 (custom-page:read\xB7write\xB7publish)");
|
|
4733
|
-
runtime2.action(page.command("list"), async (ctx) => ctx.output(await ctx.api.request("GET", base)));
|
|
4734
|
-
runtime2.action(page.command("status <id>"), async (ctx, [id2]) => ctx.output(await ctx.api.request("GET", `${base}/${uuid3(id2)}`)));
|
|
4735
|
-
runtime2.action(page.command("create").addOption(new Option3("--target <type>", "\uAFB8\uBC00 \uB300\uC0C1").choices(["profile", "space"]).makeOptionMandatory()).option("--space-id <uuid>", "\uC138\uACC4\uAD00 ID").requiredOption("--policy-version <version>", "\uB3D9\uC758\uD55C \uCF58\uD150\uCE20 \uC81C\uACF5 \uC815\uCC45 \uBC84\uC804"), async (ctx, _args, opts) => {
|
|
5920
|
+
runtime2.action(page.command("list").description("\uB0B4 \uD398\uC774\uC9C0 \uBAA9\uB85D (custom-page:read)"), async (ctx) => ctx.output(await ctx.api.request("GET", base)));
|
|
5921
|
+
runtime2.action(page.command("status <id>").description("\uD398\uC774\uC9C0\uC640 \uBC84\uC804\xB7\uC218\uC815 \uBC88\uD638 \uC0C1\uD0DC (custom-page:read)"), async (ctx, [id2]) => ctx.output(await ctx.api.request("GET", `${base}/${uuid3(id2)}`)));
|
|
5922
|
+
runtime2.action(page.command("create").description("\uAFB8\uBC00 \uD398\uC774\uC9C0 \uB4F1\uB85D (custom-page:write)").addOption(new Option3("--target <type>", "\uAFB8\uBC00 \uB300\uC0C1").choices(["profile", "space"]).makeOptionMandatory()).option("--space-id <uuid>", "\uC138\uACC4\uAD00 ID").requiredOption("--policy-version <version>", "\uB3D9\uC758\uD55C \uCF58\uD150\uCE20 \uC81C\uACF5 \uC815\uCC45 \uBC84\uC804"), async (ctx, _args, opts) => {
|
|
4736
5923
|
if (opts.target === "profile" && opts.spaceId) throw new CliError("\uB0B4 \uACF5\uAC1C \uD648\uC5D0\uB294 \uC138\uACC4\uAD00 ID\uB97C \uC9C0\uC815\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.");
|
|
4737
5924
|
const policy = parse(z11.literal("2026-09-22"), opts.policyVersion);
|
|
4738
5925
|
await mutate(ctx, "POST", base, { target_type: opts.target, ...opts.target === "space" ? { space_id: uuid3(opts.spaceId) } : {}, policy_version: policy });
|
|
4739
5926
|
});
|
|
4740
|
-
runtime2.action(page.command("upload <id> <file>").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) => {
|
|
5927
|
+
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) => {
|
|
4741
5928
|
const path = `${base}/${uuid3(id2)}`;
|
|
4742
5929
|
const prepared = await prepareContentFile(file);
|
|
4743
5930
|
const presentation = parse(presentationSchema, { schema_version: 1, header_mode: opts.header, color_scheme: opts.color });
|
|
@@ -4754,16 +5941,16 @@ function registerPageCommands(program2, runtime2) {
|
|
|
4754
5941
|
throw failure;
|
|
4755
5942
|
}
|
|
4756
5943
|
});
|
|
4757
|
-
runtime2.action(page.command("complete <id> <upload-id>"), async (ctx, [id2, uploadId]) => mutate(ctx, "POST", `${base}/${uuid3(id2)}/uploads/${uuid3(uploadId)}/complete`, {}));
|
|
4758
|
-
runtime2.action(page.command("presentation <id> <version-id>").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`, {
|
|
5944
|
+
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`, {}));
|
|
5945
|
+
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`, {
|
|
4759
5946
|
presentation: parse(presentationSchema, { schema_version: 1, header_mode: opts.header, color_scheme: opts.color })
|
|
4760
5947
|
}));
|
|
4761
|
-
runtime2.action(page.command("preview <id> <version-id>"), async (ctx, [id2, versionId]) => ctx.output(await ctx.api.request("GET", `${base}/${uuid3(id2)}/versions/${uuid3(versionId)}/preview`)));
|
|
4762
|
-
runtime2.action(page.command("submit <id> <version-id>"), async (ctx, [id2, versionId]) => mutate(ctx, "POST", `${base}/${uuid3(id2)}/versions/${uuid3(versionId)}/submit`, {}, "\uC571\uC5D0\uC11C \uD655\uC778\uD55C \uC774 \uBC84\uC804\uC758 \uC2EC\uC0AC\uB97C \uC694\uCCAD\uD560\uAE4C\uC694? \uC2B9\uC778 \uB4A4 \uC9C1\uC811 \uC801\uC6A9\uD574\uC57C \uD569\uB2C8\uB2E4."));
|
|
5948
|
+
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`)));
|
|
5949
|
+
runtime2.action(page.command("submit <id> <version-id>").description("\uC571\uC5D0\uC11C \uD655\uC778 \uC644\uB8CC\uD55C \uBC84\uC804\uC758 \uC2EC\uC0AC \uC694\uCCAD (custom-page:write\uC640 custom-page:publish \uB458 \uB2E4 \uD544\uC694)"), async (ctx, [id2, versionId]) => mutate(ctx, "POST", `${base}/${uuid3(id2)}/versions/${uuid3(versionId)}/submit`, {}, "\uC571\uC5D0\uC11C \uD655\uC778\uD55C \uC774 \uBC84\uC804\uC758 \uC2EC\uC0AC\uB97C \uC694\uCCAD\uD560\uAE4C\uC694? \uC2B9\uC778 \uB4A4 \uC9C1\uC811 \uC801\uC6A9\uD574\uC57C \uD569\uB2C8\uB2E4."));
|
|
4763
5950
|
for (const name2 of ["apply", "restore"]) {
|
|
4764
|
-
runtime2.action(page.command(`${name2} <id> <version-id>`).requiredOption("--revision <number>", "status\uC5D0\uC11C \uD655\uC778\uD55C \uD604\uC7AC \uC218\uC815 \uBC88\uD638"), async (ctx, [id2, versionId], opts) => mutate(ctx, "POST", `${base}/${uuid3(id2)}/apply`, { version_id: uuid3(versionId), revision: revision3(opts.revision) }, "\uC2B9\uC778\uB41C \uC774 \uBC84\uC804\uC758 \uD30C\uC77C\uACFC \uD5E4\uB354 \uC124\uC815\uC744 \uC801\uC6A9\uD560\uAE4C\uC694?"));
|
|
5951
|
+
runtime2.action(page.command(`${name2} <id> <version-id>`).description(`${name2 === "apply" ? "\uC2B9\uC778\uB41C \uBC84\uC804 \uC801\uC6A9" : "\uC774\uC804 \uC2B9\uC778 \uBC84\uC804\uC73C\uB85C \uBCF5\uC6D0"} (custom-page:publish)`).requiredOption("--revision <number>", "status\uC5D0\uC11C \uD655\uC778\uD55C \uD604\uC7AC \uC218\uC815 \uBC88\uD638"), async (ctx, [id2, versionId], opts) => mutate(ctx, "POST", `${base}/${uuid3(id2)}/apply`, { version_id: uuid3(versionId), revision: revision3(opts.revision) }, "\uC2B9\uC778\uB41C \uC774 \uBC84\uC804\uC758 \uD30C\uC77C\uACFC \uD5E4\uB354 \uC124\uC815\uC744 \uC801\uC6A9\uD560\uAE4C\uC694?"));
|
|
4765
5952
|
}
|
|
4766
|
-
runtime2.action(page.command("disable <id>").requiredOption("--revision <number>", "status\uC5D0\uC11C \uD655\uC778\uD55C \uD604\uC7AC \uC218\uC815 \uBC88\uD638"), async (ctx, [id2], opts) => mutate(ctx, "POST", `${base}/${uuid3(id2)}/disable`, { revision: revision3(opts.revision) }, "\uAE30\uBCF8 \uD654\uBA74\uC73C\uB85C \uC804\uD658\uD560\uAE4C\uC694? \uC5C5\uB85C\uB4DC\uD55C \uBC84\uC804\uC740 \uBCF4\uC874\uB429\uB2C8\uB2E4."));
|
|
5953
|
+
runtime2.action(page.command("disable <id>").description("\uAE30\uBCF8 \uD654\uBA74\uC73C\uB85C \uC804\uD658 (custom-page:publish)").requiredOption("--revision <number>", "status\uC5D0\uC11C \uD655\uC778\uD55C \uD604\uC7AC \uC218\uC815 \uBC88\uD638"), async (ctx, [id2], opts) => mutate(ctx, "POST", `${base}/${uuid3(id2)}/disable`, { revision: revision3(opts.revision) }, "\uAE30\uBCF8 \uD654\uBA74\uC73C\uB85C \uC804\uD658\uD560\uAE4C\uC694? \uC5C5\uB85C\uB4DC\uD55C \uBC84\uC804\uC740 \uBCF4\uC874\uB429\uB2C8\uB2E4."));
|
|
4767
5954
|
}
|
|
4768
5955
|
|
|
4769
5956
|
// src/cli.ts
|