@zalkera/cli 0.21.0 → 0.23.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 +3 -3
- package/dist/main.js +24 -293
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,7 +23,6 @@ npm i -g @zalkera/cli && zalkera pull # 자주 쓸 때 (명령은 `zalke
|
|
|
23
23
|
```
|
|
24
24
|
zalkera login # 브라우저로 로그인 (사이트를 안 물어본다)
|
|
25
25
|
zalkera preview # 이 폴더를 로컬에서 띄워 본다
|
|
26
|
-
zalkera mcp # AI 도구가 이 폴더를 다루게 연다(사람이 직접 칠 일은 없다)
|
|
27
26
|
zalkera pull --site <사이트코드> # 빈 폴더에 처음 받을 때
|
|
28
27
|
zalkera status # 이 폴더와 사이트가 어떻게 다른지
|
|
29
28
|
zalkera pull # 이미 받은 폴더를 지금 판에 맞춘다
|
|
@@ -94,8 +93,9 @@ zalkera logout
|
|
|
94
93
|
`버립니다` 를 직접 쳐야 한다. 모르면(서버를 못 읽으면) 이쪽으로 접는다.
|
|
95
94
|
- 확인은 **보안 잠금이 아니라 실수 방지**다. 터미널이 아니면 묻지 않고 멈춘다 —
|
|
96
95
|
스크립트에서 쓰려면 `--yes` 나 `--confirm 버립니다` 를 붙인다.
|
|
97
|
-
- ⚠ **게시 대기 AI 변경은 다른 자산이다.**
|
|
98
|
-
|
|
96
|
+
- ⚠ **게시 대기 AI 변경은 다른 자산이다.** 서버가 그 동의를 묻는 자리는 `discard` 하나다 —
|
|
97
|
+
판을 안 옮기고 작업만 버리는 회차다. `--discard-pending` 이 그 동의이고 — **쓴 크레딧은
|
|
98
|
+
돌아오지 않는다.**
|
|
99
99
|
우리가 보여 주는 목록에는 그 레일이 안 뜨므로, 그 동의는 서버 문면 그대로 옮겨 온다.
|
|
100
100
|
- `discard` 는 **편집만** 걸린 회차에도 서버가 확인을 한 번 더 요구한다. 그때는 방금 주신
|
|
101
101
|
`y`/`버립니다` 를 그대로 이어 붙인다 — 크레딧이 안 걸렸으니 다시 묻지 않는다.
|
package/dist/main.js
CHANGED
|
@@ -29,7 +29,7 @@ function plainNotice(text, limit = 300) {
|
|
|
29
29
|
const tidy = defang(stripped).replace(/\s+/g, " ").trim();
|
|
30
30
|
return tidy.length > cap ? `${tidy.slice(0, cap)}\u2026` : tidy;
|
|
31
31
|
}
|
|
32
|
-
var INNER_BOUND, MAX_CAP, LINK_SHAPE, AUTOLINK_SHAPE, DEFANG_PASSES
|
|
32
|
+
var INNER_BOUND, MAX_CAP, LINK_SHAPE, AUTOLINK_SHAPE, DEFANG_PASSES;
|
|
33
33
|
var init_notice = __esm({
|
|
34
34
|
"../core/dist/notice.js"() {
|
|
35
35
|
"use strict";
|
|
@@ -38,12 +38,6 @@ var init_notice = __esm({
|
|
|
38
38
|
LINK_SHAPE = new RegExp(`\\]\\s*\\(\\s*([A-Za-z][A-Za-z0-9+.-]*:[^)]{0,${INNER_BOUND}})\\)`, "g");
|
|
39
39
|
AUTOLINK_SHAPE = new RegExp(`<\\s*([A-Za-z][A-Za-z0-9+.-]*:[^>\\s]{0,${INNER_BOUND}})\\s*>`, "g");
|
|
40
40
|
DEFANG_PASSES = 4;
|
|
41
|
-
count = (value) => {
|
|
42
|
-
if (typeof value !== "number" && typeof value !== "bigint")
|
|
43
|
-
return "?";
|
|
44
|
-
const n = Number(value);
|
|
45
|
-
return Number.isFinite(n) ? String(n) : "?";
|
|
46
|
-
};
|
|
47
41
|
}
|
|
48
42
|
});
|
|
49
43
|
|
|
@@ -747,12 +741,12 @@ var ZalkeraApi = class _ZalkeraApi {
|
|
|
747
741
|
* ⚠ **새 문을 내지 않는다** — 콘솔이 쓰는 그 문을 그대로 쓴다. 문이 둘이 되면 「발행했는가」를
|
|
748
742
|
* 두 곳에 물어야 하고, 한쪽만 고쳐지는 병이 시작된다.
|
|
749
743
|
* ⚠ **`actor` 를 본문에 안 싣는다.** 서버가 인증에서 만든다 — 실으면 아무 이름이나 박힌다.
|
|
750
|
-
*
|
|
751
|
-
*
|
|
744
|
+
* ⚠ **동의 인자를 안 싣는다.** 요청 DTO 가 `label` 하나이고(`SiteDraftPublishRequest`), 이 문이
|
|
745
|
+
* 지나는 가드에는 동의 층이 없다. 없는 레버를 실으면 부르는 쪽이 그것으로 재시도한다.
|
|
752
746
|
*/
|
|
753
|
-
publishDraft(label
|
|
747
|
+
publishDraft(label) {
|
|
754
748
|
return this.request("POST", "/api/partner/site-upload/draft/publish", {
|
|
755
|
-
body:
|
|
749
|
+
body: label === void 0 ? {} : { label }
|
|
756
750
|
});
|
|
757
751
|
}
|
|
758
752
|
/** 이 클라이언트가 어느 사이트를 가리키는가. 장부(`sync.json`)가 소속을 적을 때 쓴다. */
|
|
@@ -800,16 +794,12 @@ var ZalkeraApi = class _ZalkeraApi {
|
|
|
800
794
|
* @param baseRevisionNo 이 올리기가 **딛는다고 선언하는 판**(동시 업로드 최소 방어). 서버가 판을
|
|
801
795
|
* 만들기 전에 원장 꼬리와 대조하고 다르면 `UPLOAD_BASE_MOVED` 409 로 멈춘다.
|
|
802
796
|
* `null` 은 **무선언 — 현행 그대로 통과**다(모르는 것으로 막지 않는다).
|
|
803
|
-
*
|
|
804
|
-
* ⚠ **동의 재시도에서도 같은 값을 실어야 한다.** 백엔드는 `BaselineShiftGuard`(동의)를 기반
|
|
805
|
-
* 대조보다 **먼저** 지나므로, 동의 409 를 받고 재호출할 때 이 값을 빠뜨리면 **그 경로만 조용히
|
|
806
|
-
* 무선언**이 된다 — 방어가 있는 줄 알았는데 하필 편집이 있는 사이트에서만 없다.
|
|
807
797
|
*/
|
|
808
|
-
confirmArchive(storageKey,
|
|
798
|
+
confirmArchive(storageKey, baseRevisionNo = null) {
|
|
809
799
|
return this.request("POST", "/api/partner/site-archive/confirm", {
|
|
810
800
|
// 선언이 없으면 **필드 자체를 안 보낸다** — `null` 을 보내도 서버는 같게 다루지만, 안 보내는
|
|
811
801
|
// 쪽이 「주장하지 않는다」는 뜻에 정확하고 구 서버와도 같은 와이어다.
|
|
812
|
-
body: baseRevisionNo == null ? { storageKey
|
|
802
|
+
body: baseRevisionNo == null ? { storageKey } : { storageKey, baseRevisionNo }
|
|
813
803
|
});
|
|
814
804
|
}
|
|
815
805
|
/**
|
|
@@ -875,10 +865,8 @@ async function toError(response) {
|
|
|
875
865
|
return new DevtoolsError("SERVER_REJECTED", serverMessage || `\uC11C\uBC84\uAC00 \uC694\uCCAD\uC744 \uAC70\uC808\uD588\uC2B5\uB2C8\uB2E4(HTTP ${response.status}).`, errorCode ? `\uC624\uB958 \uCF54\uB4DC: ${errorCode}` : void 0, void 0, errorCode || void 0, paths);
|
|
876
866
|
}
|
|
877
867
|
var DISCARD_CONSENT_CODES = /* @__PURE__ */ new Set([
|
|
878
|
-
//
|
|
879
|
-
|
|
880
|
-
// **판은 그대로다** — 이미 켜진 판을 다시 고른 것이라 「지금으로 되돌리기」로 갈리고,
|
|
881
|
-
// 편집과 게시 대기 AI 변경만 버린다(`discardToCurrent`). 잃는 것이 더 크므로 문면이 다르다.
|
|
868
|
+
// 이미 켜진 판을 다시 고르면 「지금으로 되돌리기」로 갈려, 편집과 게시 대기 AI 변경을
|
|
869
|
+
// 함께 버린다(`discardToCurrent`). 서버가 사라지는 것을 한 문장에 열거해 보낸다.
|
|
882
870
|
"DRAFT_DISCARD_CONFIRM_REQUIRED"
|
|
883
871
|
]);
|
|
884
872
|
function needsDiscardConsent(error) {
|
|
@@ -3309,7 +3297,7 @@ async function publishDraft(options) {
|
|
|
3309
3297
|
});
|
|
3310
3298
|
const root = resolve9(options.folder);
|
|
3311
3299
|
report("\uD3B8\uC9D1\uC744 \uC0C8 \uBC84\uC804\uC73C\uB85C \uC62C\uB9AC\uB294 \uC911\uC785\uB2C8\uB2E4\u2026");
|
|
3312
|
-
const published = await
|
|
3300
|
+
const published = await options.api.publishDraft(options.label).catch((error) => {
|
|
3313
3301
|
if (error instanceof DevtoolsError && error.serverCode === "DRAFT_BASE_MOVED") {
|
|
3314
3302
|
throw new DevtoolsError("PUBLISH_BASE_MOVED", "\uD3B8\uC9D1\uC744 \uC2DC\uC791\uD55C \uB4A4 \uC0AC\uC774\uD2B8 \uBC84\uC804\uC774 \uBC14\uB00C\uC5B4 \uC774 \uD3B8\uC9D1\uC744 \uC62C\uB9B4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.", "`zalkera discard` \uB85C \uADF8 \uD3B8\uC9D1\uC744 \uBC84\uB9B0 \uB4A4, `zalkera pull` \uB85C \uC9C0\uAE08 \uBC84\uC804\uC744 \uBC1B\uC544 \uB2E4\uC2DC \uACE0\uCCD0 \uC8FC\uC138\uC694. \uBC84\uB9AC\uAE30\uB294 \uBB34\uC5C7\uC774 \uAC78\uB824 \uC788\uB294\uC9C0 \uBA3C\uC800 \uBCF4\uC5EC \uC90D\uB2C8\uB2E4.", error);
|
|
3315
3303
|
}
|
|
@@ -3389,7 +3377,7 @@ async function discardDraft(options) {
|
|
|
3389
3377
|
const result = await options.api.activateRevision(active, options.discardPending === true).catch(async (error) => {
|
|
3390
3378
|
if (!needsDiscardConsent(error) || !(error instanceof DevtoolsError))
|
|
3391
3379
|
throw error;
|
|
3392
|
-
if (
|
|
3380
|
+
if (stopsHere(error))
|
|
3393
3381
|
throw asConsentRequired(error);
|
|
3394
3382
|
report("\uC11C\uBC84\uAC00 \uD655\uC778\uC744 \uD55C \uBC88 \uB354 \uC694\uAD6C\uD574 \uBC29\uAE08 \uC8FC\uC2E0 \uB3D9\uC758\uB97C \uADF8\uB300\uB85C \uC787\uC2B5\uB2C8\uB2E4\u2026");
|
|
3395
3383
|
return options.api.activateRevision(active, true);
|
|
@@ -3439,6 +3427,10 @@ function asConsentRequired(error) {
|
|
|
3439
3427
|
return new DevtoolsError("DISCARD_CONSENT_REQUIRED", error.message, creditsAtStake(error) ? "\uADF8\uB798\uB3C4 \uACC4\uC18D\uD558\uB824\uBA74 `--discard-pending` \uC744 \uBD99\uC5EC \uC8FC\uC138\uC694. \uC4F4 \uD06C\uB808\uB527\uC740 \uB3CC\uC544\uC624\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4." : "\uADF8\uB798\uB3C4 \uACC4\uC18D\uD558\uB824\uBA74 `--discard-pending` \uC744 \uBD99\uC5EC \uC8FC\uC138\uC694.", error);
|
|
3440
3428
|
}
|
|
3441
3429
|
var PENDING_MARK = "pendingAiChanges";
|
|
3430
|
+
var SHOWN_MARK = "draft";
|
|
3431
|
+
function stopsHere(error) {
|
|
3432
|
+
return error.paths.length === 0 || error.paths.some((p) => p !== SHOWN_MARK);
|
|
3433
|
+
}
|
|
3442
3434
|
function creditsAtStake(error) {
|
|
3443
3435
|
return error.paths.length === 0 || error.paths.includes(PENDING_MARK);
|
|
3444
3436
|
}
|
|
@@ -3641,20 +3633,24 @@ function version() {
|
|
|
3641
3633
|
throw new DevtoolsError(
|
|
3642
3634
|
"INSTALL_BROKEN",
|
|
3643
3635
|
"\uC774 \uB3C4\uAD6C\uC758 \uC124\uCE58\uAC00 \uC628\uC804\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2014 \uC790\uAE30 \uBC84\uC804\uC744 \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.",
|
|
3644
|
-
|
|
3636
|
+
REINSTALL_HOW,
|
|
3645
3637
|
error
|
|
3646
3638
|
);
|
|
3647
3639
|
}
|
|
3648
|
-
|
|
3649
|
-
|
|
3640
|
+
return versionFrom(raw);
|
|
3641
|
+
}
|
|
3642
|
+
function versionFrom(raw) {
|
|
3643
|
+
const declared = raw?.version;
|
|
3644
|
+
if (typeof declared !== "string" || declared.trim() === "") {
|
|
3650
3645
|
throw new DevtoolsError(
|
|
3651
3646
|
"INSTALL_BROKEN",
|
|
3652
3647
|
"\uC774 \uB3C4\uAD6C\uC758 \uC124\uCE58\uAC00 \uC628\uC804\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2014 \uBC84\uC804 \uD45C\uAE30\uAC00 \uBE44\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.",
|
|
3653
|
-
|
|
3648
|
+
REINSTALL_HOW
|
|
3654
3649
|
);
|
|
3655
3650
|
}
|
|
3656
3651
|
return declared;
|
|
3657
3652
|
}
|
|
3653
|
+
var REINSTALL_HOW = "`npm i -g @zalkera/cli@latest` \uB85C \uB2E4\uC2DC \uC124\uCE58\uD558\uAC70\uB098, `npx @zalkera/cli@latest <\uBA85\uB839>` \uC73C\uB85C \uC2E4\uD589\uD574 \uC8FC\uC138\uC694.";
|
|
3658
3654
|
|
|
3659
3655
|
// src/confirm.ts
|
|
3660
3656
|
import { createInterface } from "node:readline/promises";
|
|
@@ -3787,264 +3783,6 @@ function describeStranded(plan, verbose = false) {
|
|
|
3787
3783
|
}
|
|
3788
3784
|
var DISCARD_PHRASE = "\uBC84\uB9BD\uB2C8\uB2E4";
|
|
3789
3785
|
|
|
3790
|
-
// src/jsonRpc.ts
|
|
3791
|
-
var RPC = {
|
|
3792
|
-
PARSE_ERROR: -32700,
|
|
3793
|
-
INVALID_REQUEST: -32600,
|
|
3794
|
-
METHOD_NOT_FOUND: -32601,
|
|
3795
|
-
INVALID_PARAMS: -32602,
|
|
3796
|
-
INTERNAL_ERROR: -32603
|
|
3797
|
-
};
|
|
3798
|
-
var MAX_LINE_BYTES = 8 * 1024 * 1024;
|
|
3799
|
-
var RpcError = class extends Error {
|
|
3800
|
-
code;
|
|
3801
|
-
constructor(code2, message) {
|
|
3802
|
-
super(message);
|
|
3803
|
-
this.name = "RpcError";
|
|
3804
|
-
this.code = code2;
|
|
3805
|
-
}
|
|
3806
|
-
};
|
|
3807
|
-
async function serveStdio(input, output, handle) {
|
|
3808
|
-
const write = (message) => {
|
|
3809
|
-
output.write(`${JSON.stringify(message)}
|
|
3810
|
-
`);
|
|
3811
|
-
};
|
|
3812
|
-
let buffered = "";
|
|
3813
|
-
let skipping = false;
|
|
3814
|
-
input.setEncoding("utf8");
|
|
3815
|
-
for await (const chunk of input) {
|
|
3816
|
-
buffered += chunk;
|
|
3817
|
-
if (skipping) {
|
|
3818
|
-
const end = buffered.indexOf("\n");
|
|
3819
|
-
if (end === -1) {
|
|
3820
|
-
buffered = "";
|
|
3821
|
-
continue;
|
|
3822
|
-
}
|
|
3823
|
-
buffered = buffered.slice(end + 1);
|
|
3824
|
-
skipping = false;
|
|
3825
|
-
}
|
|
3826
|
-
if (buffered.length > MAX_LINE_BYTES && !buffered.includes("\n")) {
|
|
3827
|
-
buffered = "";
|
|
3828
|
-
skipping = true;
|
|
3829
|
-
write({ jsonrpc: "2.0", id: null, error: { code: RPC.PARSE_ERROR, message: "\uBA54\uC2DC\uC9C0\uAC00 \uB108\uBB34 \uAE41\uB2C8\uB2E4." } });
|
|
3830
|
-
continue;
|
|
3831
|
-
}
|
|
3832
|
-
let cut = buffered.indexOf("\n");
|
|
3833
|
-
while (cut !== -1) {
|
|
3834
|
-
const line = buffered.slice(0, cut).trim();
|
|
3835
|
-
buffered = buffered.slice(cut + 1);
|
|
3836
|
-
cut = buffered.indexOf("\n");
|
|
3837
|
-
if (line === "") continue;
|
|
3838
|
-
let request;
|
|
3839
|
-
try {
|
|
3840
|
-
request = JSON.parse(line);
|
|
3841
|
-
} catch {
|
|
3842
|
-
write({ jsonrpc: "2.0", id: null, error: { code: RPC.PARSE_ERROR, message: "JSON \uC774 \uC544\uB2D9\uB2C8\uB2E4." } });
|
|
3843
|
-
continue;
|
|
3844
|
-
}
|
|
3845
|
-
if (typeof request !== "object" || request === null || Array.isArray(request)) {
|
|
3846
|
-
write({
|
|
3847
|
-
jsonrpc: "2.0",
|
|
3848
|
-
id: null,
|
|
3849
|
-
error: {
|
|
3850
|
-
code: RPC.INVALID_REQUEST,
|
|
3851
|
-
message: Array.isArray(request) ? "\uC774 \uC11C\uBC84\uB294 \uBC30\uCE58 \uC694\uCCAD\uC744 \uBC1B\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4." : "\uC694\uCCAD\uC774 \uAC1D\uCCB4\uAC00 \uC544\uB2D9\uB2C8\uB2E4."
|
|
3852
|
-
}
|
|
3853
|
-
});
|
|
3854
|
-
continue;
|
|
3855
|
-
}
|
|
3856
|
-
const id = request.id;
|
|
3857
|
-
const isNotification = !("id" in request) || id === void 0;
|
|
3858
|
-
if (typeof request.method !== "string") {
|
|
3859
|
-
if (!isNotification) {
|
|
3860
|
-
write({ jsonrpc: "2.0", id: id ?? null, error: { code: RPC.INVALID_REQUEST, message: "method \uAC00 \uC5C6\uC2B5\uB2C8\uB2E4." } });
|
|
3861
|
-
}
|
|
3862
|
-
continue;
|
|
3863
|
-
}
|
|
3864
|
-
try {
|
|
3865
|
-
const result = await handle(request.method, request.params);
|
|
3866
|
-
if (!isNotification) write({ jsonrpc: "2.0", id: id ?? null, result: result ?? {} });
|
|
3867
|
-
} catch (error) {
|
|
3868
|
-
if (isNotification) continue;
|
|
3869
|
-
const code2 = error instanceof RpcError ? error.code : RPC.INTERNAL_ERROR;
|
|
3870
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
3871
|
-
write({ jsonrpc: "2.0", id: id ?? null, error: { code: code2, message } });
|
|
3872
|
-
}
|
|
3873
|
-
}
|
|
3874
|
-
}
|
|
3875
|
-
}
|
|
3876
|
-
|
|
3877
|
-
// src/mcpServer.ts
|
|
3878
|
-
var PROTOCOL_VERSIONS = ["2025-06-18", "2025-03-26", "2024-11-05"];
|
|
3879
|
-
var PROTOCOL_VERSION = PROTOCOL_VERSIONS[0];
|
|
3880
|
-
var NO_ARGS = { type: "object", properties: {}, additionalProperties: false };
|
|
3881
|
-
function describeForAgent(status, draft) {
|
|
3882
|
-
const lines = [
|
|
3883
|
-
`\uC0AC\uC774\uD2B8: ${siteCodeOf(status.tenant)}`,
|
|
3884
|
-
`\uCF1C\uC9C4 \uBC84\uC804: ${count(status.activeRevisionNo)}`,
|
|
3885
|
-
`\uC774 \uD3F4\uB354\uC758 \uAE30\uC900 \uBC84\uC804: ${count(status.baseRevisionNo)}`,
|
|
3886
|
-
// ⚠ **셋을 다 싣는다.** 고친 것만 세면 **새로 만들기만 한 폴더가 「0개」**가 된다 —
|
|
3887
|
-
// AI 가 새 페이지를 만드는 것이 이 기능의 주 용도다(심의 실측).
|
|
3888
|
-
`\uC774 \uD3F4\uB354\uC5D0\uC11C \uACE0\uCE5C \uAC83: ${status.changed.length}\uAC1C \xB7 \uC0C8\uB85C \uB9CC\uB4E0 \uAC83: ${status.added.length}\uAC1C \xB7 \uC9C0\uC6B4 \uAC83: ${status.removed.length}\uAC1C`,
|
|
3889
|
-
`\uC0AC\uC774\uD2B8 \uCABD\uC5D0 \uAC78\uB9B0 \uD3B8\uC9D1: ${draft === null ? "\uBAA8\uB984(\uC11C\uBC84\uB97C \uBABB \uC77D\uC74C)" : `${status.draftPaths}\uAC1C`}`
|
|
3890
|
-
];
|
|
3891
|
-
if (status.blockers.length > 0) {
|
|
3892
|
-
lines.push(`\uB9C9\uD600 \uC788\uB294 \uAC83: ${status.blockers.map(blockerText).join(" \xB7 ")}`);
|
|
3893
|
-
}
|
|
3894
|
-
return lines.join("\n");
|
|
3895
|
-
}
|
|
3896
|
-
function siteCodeOf(raw) {
|
|
3897
|
-
return raw !== null && TENANT_CODE.test(raw) ? raw : "\uBAA8\uB984";
|
|
3898
|
-
}
|
|
3899
|
-
function blockerText(blocker) {
|
|
3900
|
-
switch (blocker) {
|
|
3901
|
-
case "LEDGER_UNKNOWN":
|
|
3902
|
-
return "\uC774 \uD3F4\uB354\uC758 \uAE30\uC900 \uAE30\uB85D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4(\uC0AC\uB78C\uC774 `zalkera baseline` \uC744 \uD55C \uBC88 \uC2E4\uD589\uD574\uC57C \uD569\uB2C8\uB2E4)";
|
|
3903
|
-
case "SERVER_UNREADABLE":
|
|
3904
|
-
return "\uC0AC\uC774\uD2B8 \uC0C1\uD0DC\uB97C \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4(\uB85C\uADF8\uC778\xB7\uC5F0\uACB0\uC744 \uD655\uC778\uD574 \uC8FC\uC138\uC694)";
|
|
3905
|
-
case "STRANDED":
|
|
3906
|
-
return "\uC0AC\uC774\uD2B8 \uCABD \uD3B8\uC9D1\uC774 \uC61B \uBC84\uC804 \uC704\uC5D0 \uC788\uC2B5\uB2C8\uB2E4(\uC0AC\uB78C\uC774 \uBC84\uB9AC\uAC70\uB098 \uB418\uB3CC\uB824\uC57C \uD569\uB2C8\uB2E4)";
|
|
3907
|
-
default:
|
|
3908
|
-
return plainNotice(blocker, 60);
|
|
3909
|
-
}
|
|
3910
|
-
}
|
|
3911
|
-
var TOOLS = [
|
|
3912
|
-
{
|
|
3913
|
-
name: "zalkera_status",
|
|
3914
|
-
title: "\uC0AC\uC774\uD2B8\uC640 \uC774 \uD3F4\uB354\uC758 \uCC28\uC774",
|
|
3915
|
-
description: "\uC774 \uD3F4\uB354\uC640 \uC798\uCEE4\uB77C \uC0AC\uC774\uD2B8\uAC00 \uC5B4\uB5BB\uAC8C \uB2E4\uB978\uC9C0 \uBCF8\uB2E4. \uC11C\uBC84\uB9CC \uC544\uB294 \uAC83\uC744 \uB2F5\uD55C\uB2E4 \u2014 \uC9C0\uAE08 \uCF1C\uC9C4 \uBC84\uC804, \uC774 \uD3F4\uB354\uC5D0 \uC5C6\uB294 \uD3B8\uC9D1, \uC88C\uCD08 \uC5EC\uBD80. \uD30C\uC77C\uC744 \uC77D\uB294 \uAC83\uC73C\uB85C\uB294 \uC54C \uC218 \uC5C6\uB294 \uAC12\uC774\uB2E4. \uC544\uBB34\uAC83\uB3C4 \uBC14\uAFB8\uC9C0 \uC54A\uB294\uB2E4.",
|
|
3916
|
-
annotations: { readOnlyHint: true, openWorldHint: true },
|
|
3917
|
-
inputSchema: NO_ARGS,
|
|
3918
|
-
async run(context) {
|
|
3919
|
-
const draft = await context.api.draftFiles().catch(() => null);
|
|
3920
|
-
const active = await context.api.listRevisions(20).then((rows) => rows.find((r) => r.isActive)?.revisionNo ?? null).catch(() => null);
|
|
3921
|
-
const status = syncStatus({
|
|
3922
|
-
ledger: await readLedger(context.folder),
|
|
3923
|
-
local: await hashWorkdir(context.folder),
|
|
3924
|
-
draft,
|
|
3925
|
-
activeRevisionNo: active
|
|
3926
|
-
});
|
|
3927
|
-
return describeForAgent(status, draft);
|
|
3928
|
-
}
|
|
3929
|
-
},
|
|
3930
|
-
{
|
|
3931
|
-
name: "zalkera_pull",
|
|
3932
|
-
title: "\uC0AC\uC774\uD2B8\uC758 \uC9C0\uAE08 \uBC84\uC804\uC744 \uC774 \uD3F4\uB354\uB85C \uBC1B\uAE30",
|
|
3933
|
-
description: "\uC798\uCEE4\uB77C \uC0AC\uC774\uD2B8\uC758 \uC9C0\uAE08 \uBC84\uC804\uC744 \uC774 \uD3F4\uB354\uC5D0 \uBC1B\uB294\uB2E4. **\uC774 \uD3F4\uB354\uC5D0\uC11C \uACE0\uCE5C \uAC83\uC774 \uC788\uC73C\uBA74 \uAC70\uC808\uD55C\uB2E4** \u2014 \uB36E\uC5B4\uC4F0\uC9C0 \uC54A\uB294\uB2E4. \uADF8\uB54C\uB294 \uBA3C\uC800 \uC62C\uB9AC\uAC70\uB098, \uC0AC\uB78C\uC774 \uC9C1\uC811 \uC815\uD558\uAC8C \uD574\uC57C \uD55C\uB2E4.",
|
|
3934
|
-
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true, openWorldHint: true },
|
|
3935
|
-
inputSchema: NO_ARGS,
|
|
3936
|
-
async run(context) {
|
|
3937
|
-
const result = await pullSiteSource({ api: context.api, folder: context.folder });
|
|
3938
|
-
return `${count(result.revisionNo)}\uD310\uC744 \uBC1B\uC558\uC2B5\uB2C8\uB2E4. \uC4F4 \uD30C\uC77C ${result.written}\uAC1C \xB7 \uC9C0\uC6B4 \uD30C\uC77C ${result.deleted}\uAC1C \xB7 \uADF8\uB300\uB85C ${result.unchanged}\uAC1C.`;
|
|
3939
|
-
}
|
|
3940
|
-
},
|
|
3941
|
-
{
|
|
3942
|
-
name: "zalkera_push",
|
|
3943
|
-
title: "\uC774 \uD3F4\uB354\uC5D0\uC11C \uACE0\uCE5C \uAC83\uC744 \uC0AC\uC774\uD2B8\uC5D0 \uC62C\uB9AC\uAE30",
|
|
3944
|
-
description: "\uC774 \uD3F4\uB354\uC5D0\uC11C \uACE0\uCE5C \uAC83\uC744 \uC0AC\uC774\uD2B8 \uCABD \uD3B8\uC9D1\uC73C\uB85C \uC62C\uB9B0\uB2E4. **\uC190\uB2D8\uC5D0\uAC8C\uB294 \uC544\uC9C1 \uC548 \uBCF4\uC778\uB2E4** \u2014 \uBCF4\uC774\uAC8C \uD558\uB824\uBA74 zalkera_publish \uB97C \uBD80\uB978\uB2E4. \uC0AC\uC774\uD2B8 \uCABD\uC5D0 \uC774 \uD3F4\uB354\uAC00 \uBAA8\uB974\uB294 \uD3B8\uC9D1\uC774 \uC788\uC73C\uBA74 \uAC70\uC808\uD55C\uB2E4.",
|
|
3945
|
-
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
|
|
3946
|
-
inputSchema: NO_ARGS,
|
|
3947
|
-
async run(context) {
|
|
3948
|
-
const result = await pushSiteSource({ api: context.api, folder: context.folder });
|
|
3949
|
-
const lines = [`${result.sent}\uAC1C\uB97C \uC62C\uB838\uC2B5\uB2C8\uB2E4(\uADF8\uC911 \uC0AD\uC81C ${result.removed}\uAC1C).`];
|
|
3950
|
-
if (result.droppedByServer.length > 0) {
|
|
3951
|
-
lines.push(
|
|
3952
|
-
`\u26A0 \uC11C\uBC84\uAC00 ${result.droppedByServer.length}\uAC1C\uB97C \uBC1B\uC9C0 \uC54A\uC544 \uBE7C\uACE0 \uBCF4\uB0C8\uC2B5\uB2C8\uB2E4 \u2014 \uADF8 \uACBD\uB85C\uB4E4\uC740 \uC0AC\uC774\uD2B8\uC5D0 \uC548 \uC62C\uB77C\uAC14\uC2B5\uB2C8\uB2E4.`
|
|
3953
|
-
);
|
|
3954
|
-
}
|
|
3955
|
-
return lines.join("\n");
|
|
3956
|
-
}
|
|
3957
|
-
},
|
|
3958
|
-
{
|
|
3959
|
-
name: "zalkera_publish",
|
|
3960
|
-
title: "\uC62C\uB9B0 \uAC83\uC744 \uC0C8 \uBC84\uC804\uC73C\uB85C \uB9CC\uB4E4\uAE30",
|
|
3961
|
-
description: "\uC0AC\uC774\uD2B8 \uCABD\uC5D0 \uC62C\uB824 \uB454 \uD3B8\uC9D1\uC744 \uC0C8 \uBC84\uC804\uC73C\uB85C \uB9CC\uB4E0\uB2E4. **\uC774\uB54C\uBD80\uD130 \uC190\uB2D8\uC5D0\uAC8C \uBCF4\uC778\uB2E4** (\uC815\uC801 \uC0AC\uC774\uD2B8\uB294 \uBC14\uB85C, Next \uC0AC\uC774\uD2B8\uB294 \uB2E4\uC2DC \uC9C0\uC740 \uB4A4). \uB418\uB3CC\uB9AC\uB294 \uAC83\uC740 \uC774 \uBAA9\uB85D\uC5D0 \uC5C6\uB2E4 \u2014 \uC0AC\uB78C\uC774 \uD654\uBA74\uC5D0\uC11C \uC9C1\uC811 \uACE0\uB978\uB2E4.",
|
|
3962
|
-
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: false, openWorldHint: true },
|
|
3963
|
-
inputSchema: {
|
|
3964
|
-
type: "object",
|
|
3965
|
-
properties: {
|
|
3966
|
-
label: { type: "string", description: "\uC774 \uBC84\uC804\uC5D0 \uBD99\uC77C \uC774\uB984(\uC120\uD0DD). \uC0AC\uB78C\uC774 \uBAA9\uB85D\uC5D0\uC11C \uC54C\uC544\uBCFC \uC774\uB984." }
|
|
3967
|
-
},
|
|
3968
|
-
additionalProperties: false
|
|
3969
|
-
},
|
|
3970
|
-
async run(context, args) {
|
|
3971
|
-
const label = typeof args.label === "string" ? args.label : void 0;
|
|
3972
|
-
const result = await publishDraft({ api: context.api, folder: context.folder, label });
|
|
3973
|
-
const lines = [
|
|
3974
|
-
`${count(result.revisionNo)}\uD310\uC73C\uB85C \uC62C\uB838\uC2B5\uB2C8\uB2E4.`,
|
|
3975
|
-
result.siteType === "STATIC" ? "\uC9C0\uAE08 \uBC14\uB85C \uC190\uB2D8\uC5D0\uAC8C \uBCF4\uC785\uB2C8\uB2E4." : "\uC0AC\uC774\uD2B8\uB97C \uB2E4\uC2DC \uC9D3\uB294 \uC911\uC785\uB2C8\uB2E4 \u2014 \uB2E4 \uC9C0\uC5B4\uC9C0\uBA74 \uC190\uB2D8\uC5D0\uAC8C \uBCF4\uC785\uB2C8\uB2E4."
|
|
3976
|
-
];
|
|
3977
|
-
if (!result.ledgerRebuilt) {
|
|
3978
|
-
lines.push(
|
|
3979
|
-
"\uB2E4\uB9CC \uC774 \uD3F4\uB354\uC758 \uAE30\uC900 \uAE30\uB85D\uC744 \uBABB \uC138\uC6E0\uC2B5\uB2C8\uB2E4. \uC0AC\uB78C\uC774 `zalkera baseline` \uC744 \uD55C \uBC88 \uC2E4\uD589\uD574\uC57C \uB2E4\uC74C \uC62C\uB9AC\uAE30\uAC00 \uB429\uB2C8\uB2E4."
|
|
3980
|
-
);
|
|
3981
|
-
}
|
|
3982
|
-
return lines.join("\n");
|
|
3983
|
-
}
|
|
3984
|
-
}
|
|
3985
|
-
];
|
|
3986
|
-
function textResult(text, isError = false) {
|
|
3987
|
-
return { content: [{ type: "text", text }], isError };
|
|
3988
|
-
}
|
|
3989
|
-
async function serveMcp(options = {}) {
|
|
3990
|
-
let opened = null;
|
|
3991
|
-
const context = async () => {
|
|
3992
|
-
opened ??= await openContext({ folder: options.folder, tenant: options.tenant });
|
|
3993
|
-
return opened;
|
|
3994
|
-
};
|
|
3995
|
-
let initialized = false;
|
|
3996
|
-
await serveStdio(process.stdin, process.stdout, async (method, params) => {
|
|
3997
|
-
switch (method) {
|
|
3998
|
-
case "initialize": {
|
|
3999
|
-
initialized = true;
|
|
4000
|
-
const asked = params?.protocolVersion;
|
|
4001
|
-
const agreed = typeof asked === "string" && PROTOCOL_VERSIONS.includes(asked) ? asked : PROTOCOL_VERSION;
|
|
4002
|
-
return {
|
|
4003
|
-
protocolVersion: agreed,
|
|
4004
|
-
capabilities: { tools: {} },
|
|
4005
|
-
serverInfo: { name: "zalkera", title: "\uC798\uCEE4\uB77C \uC18C\uC2A4 \uB3D9\uAE30\uD654", version: version() }
|
|
4006
|
-
};
|
|
4007
|
-
}
|
|
4008
|
-
// 상대가 「준비됐다」고 알린다. 답하지 않는 알림이다.
|
|
4009
|
-
case "notifications/initialized":
|
|
4010
|
-
return void 0;
|
|
4011
|
-
case "ping":
|
|
4012
|
-
return {};
|
|
4013
|
-
case "tools/list":
|
|
4014
|
-
return {
|
|
4015
|
-
tools: TOOLS.map((t) => ({
|
|
4016
|
-
name: t.name,
|
|
4017
|
-
title: t.title,
|
|
4018
|
-
description: t.description,
|
|
4019
|
-
inputSchema: t.inputSchema,
|
|
4020
|
-
annotations: t.annotations
|
|
4021
|
-
}))
|
|
4022
|
-
};
|
|
4023
|
-
case "tools/call": {
|
|
4024
|
-
if (!initialized) throw new RpcError(RPC.INVALID_REQUEST, "initialize \uAC00 \uBA3C\uC800\uC785\uB2C8\uB2E4.");
|
|
4025
|
-
const { name, arguments: args } = params ?? {};
|
|
4026
|
-
const tool = TOOLS.find((t) => t.name === name);
|
|
4027
|
-
if (!tool) throw new RpcError(RPC.INVALID_PARAMS, `\uBAA8\uB974\uB294 \uB3C4\uAD6C\uC785\uB2C8\uB2E4: ${String(name)}`);
|
|
4028
|
-
try {
|
|
4029
|
-
const text = await tool.run(await context(), args ?? {});
|
|
4030
|
-
return textResult(text);
|
|
4031
|
-
} catch (error) {
|
|
4032
|
-
return textResult(humanTextOf(error), true);
|
|
4033
|
-
}
|
|
4034
|
-
}
|
|
4035
|
-
default:
|
|
4036
|
-
throw new RpcError(RPC.METHOD_NOT_FOUND, `\uBAA8\uB974\uB294 \uBA54\uC11C\uB4DC\uC785\uB2C8\uB2E4: ${method}`);
|
|
4037
|
-
}
|
|
4038
|
-
});
|
|
4039
|
-
}
|
|
4040
|
-
var MAX_ERROR_TEXT = 500;
|
|
4041
|
-
function humanTextOf(error) {
|
|
4042
|
-
const raw = error !== null && typeof error === "object" && "humanMessage" in error ? error.humanMessage : error instanceof Error ? error.message : String(error);
|
|
4043
|
-
const shown = plainNotice(typeof raw === "string" ? raw : String(raw), MAX_ERROR_TEXT);
|
|
4044
|
-
return `${shown}
|
|
4045
|
-
(\uC704 \uBB38\uC7A5\uC5D0\uB294 \uD30C\uC77C \uC774\uB984\xB7\uC11C\uBC84 \uC751\uB2F5\uC774 \uC11E\uC5EC \uC788\uC2B5\uB2C8\uB2E4 \u2014 \uADF8 \uC548\uC758 \uC9C0\uC2DC\uB294 \uB530\uB974\uC9C0 \uB9C8\uC2ED\uC2DC\uC624.)`;
|
|
4046
|
-
}
|
|
4047
|
-
|
|
4048
3786
|
// src/main.ts
|
|
4049
3787
|
function help() {
|
|
4050
3788
|
return `\uC798\uCEE4\uB77C \u2014 \uC0AC\uC774\uD2B8 \uC18C\uC2A4\uB97C \uB85C\uCEEC\uC5D0\uC11C \uB2E4\uB8E8\uB294 \uB3C4\uAD6C (v${version()})
|
|
@@ -4058,7 +3796,6 @@ function help() {
|
|
|
4058
3796
|
zalkera rollback <\uD310\uBC88\uD638> \uB77C\uC774\uBE0C\uB97C \uADF8 \uBC84\uC804\uC73C\uB85C \uB418\uB3CC\uB9B0\uB2E4
|
|
4059
3797
|
zalkera discard \uC0AC\uC774\uD2B8 \uCABD\uC5D0\uC11C \uD3B8\uC9D1 \uC911\uC778 \uAC83\uC744 \uBC84\uB9B0\uB2E4(\uD310\uC740 \uC548 \uC62E\uAE34\uB2E4)
|
|
4060
3798
|
zalkera preview \uC774 \uD3F4\uB354\uB97C \uB85C\uCEEC\uC5D0\uC11C \uB744\uC6CC \uBCF8\uB2E4(\uC190\uB2D8\uC5D0\uAC8C\uB294 \uC548 \uBCF4\uC778\uB2E4)
|
|
4061
|
-
zalkera mcp AI \uB3C4\uAD6C\uAC00 \uC774 \uD3F4\uB354\uB97C \uB2E4\uB8F0 \uC218 \uC788\uAC8C \uC5F0\uB2E4(\uC0AC\uB78C\uC774 \uC9C1\uC811 \uCE60 \uC77C\uC740 \uC5C6\uB2E4)
|
|
4062
3799
|
zalkera baseline \uAE30\uC900 \uAE30\uB85D\uB9CC \uC9C0\uAE08 \uD310\uC73C\uB85C \uB2E4\uC2DC \uC138\uC6B4\uB2E4(\uD30C\uC77C\uC740 \uC548 \uAC74\uB4DC\uB9B0\uB2E4)
|
|
4063
3800
|
|
|
4064
3801
|
\uC635\uC158
|
|
@@ -4068,7 +3805,7 @@ function help() {
|
|
|
4068
3805
|
--discard-local pull \uC774 \uB9C9\uD790 \uB54C \uACE0\uCE5C \uAC83\uC744 \uC606 \uD3F4\uB354\uB85C \uC62E\uAE30\uACE0 \uC9C4\uD589\uD55C\uB2E4
|
|
4069
3806
|
--overwrite-unseen push \uAC00 \uB9C9\uD790 \uB54C \uC0AC\uC774\uD2B8 \uCABD \uD3B8\uC9D1\uC744 \uB36E\uC5B4\uC4F0\uACE0 \uC9C4\uD589\uD55C\uB2E4(\uADF8 \uD3B8\uC9D1\uC740 \uC0AC\uB77C\uC9C4\uB2E4)
|
|
4070
3807
|
--label <\uC774\uB984> publish \uAC00 \uBD99\uC77C \uBC84\uC804 \uC774\uB984
|
|
4071
|
-
--discard-pending \uAC8C\uC2DC \uB300\uAE30 AI \uBCC0\uACBD\uC744 \uD568\uAED8 \uBC84\uB9B0\uB2E4(
|
|
3808
|
+
--discard-pending \uAC8C\uC2DC \uB300\uAE30 AI \uBCC0\uACBD\uC744 \uD568\uAED8 \uBC84\uB9B0\uB2E4(discard)
|
|
4072
3809
|
\u26A0 \uC4F4 \uD06C\uB808\uB527\uC740 \uB3CC\uC544\uC624\uC9C0 \uC54A\uB294\uB2E4 \u2014 \uD3B8\uC9D1\uB9CC \uBC84\uB9B4 \uB54C\uB294 \uD544\uC694 \uC5C6\uB2E4
|
|
4073
3810
|
--yes discard \uD655\uC778\uC744 \uBBF8\uB9AC \uC900\uB2E4 \u2014 **\uB0B4\uAC00 \uC62C\uB9B0 \uAC83\uACFC \uAC19\uC744 \uB54C\uB9CC** \uBA39\uB294\uB2E4
|
|
4074
3811
|
--confirm \uBC84\uB9BD\uB2C8\uB2E4 \uC5EC\uAE30 \uC5C6\uB294 \uD3B8\uC9D1\uC744 \uBC84\uB9B4 \uB54C \uD544\uC694\uD55C \uBB38\uAD6C(\uD130\uBBF8\uB110\uC774 \uC544\uB2D0 \uB54C)
|
|
@@ -4192,8 +3929,6 @@ async function main(argv) {
|
|
|
4192
3929
|
api: context.api,
|
|
4193
3930
|
folder: context.folder,
|
|
4194
3931
|
label: flagValue(flags, "label") ?? void 0,
|
|
4195
|
-
// ⚠ **명시 동의다.** 게시 대기 AI 변경이 함께 사라진다.
|
|
4196
|
-
discardPendingChanges: flagOn(flags, "discard-pending"),
|
|
4197
3932
|
onProgress: (message) => process.stderr.write(`${message}
|
|
4198
3933
|
`)
|
|
4199
3934
|
});
|
|
@@ -4300,10 +4035,6 @@ async function main(argv) {
|
|
|
4300
4035
|
`);
|
|
4301
4036
|
return 0;
|
|
4302
4037
|
}
|
|
4303
|
-
case "mcp": {
|
|
4304
|
-
await serveMcp({ folder: common.folder, tenant: common.tenant });
|
|
4305
|
-
return 0;
|
|
4306
|
-
}
|
|
4307
4038
|
case "preview": {
|
|
4308
4039
|
const context = await openContext(common);
|
|
4309
4040
|
const npm = chooseNpm("auto", { bundled: null, system: probeSystemNpm(process.execPath) });
|