@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.
Files changed (3) hide show
  1. package/README.md +3 -3
  2. package/dist/main.js +24 -293
  3. 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
- 따로 동의를 묻는다. `--discard-pending` 이 그 동의이고 — **쓴 크레딧은 돌아오지 않는다.**
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, count;
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
- * @param discardPendingChanges 게시 대기 AI 변경을 함께 버릴지. **명시 동의가 있을 때만** 참이다.
744
+ * ⚠ **동의 인자를 안 싣는다.** 요청 DTO 가 `label` 하나이고(`SiteDraftPublishRequest`), 이 문이
745
+ * 지나는 가드에는 동의 층이 없다. 없는 레버를 실으면 부르는 쪽이 그것으로 재시도한다.
752
746
  */
753
- publishDraft(label, discardPendingChanges = false) {
747
+ publishDraft(label) {
754
748
  return this.request("POST", "/api/partner/site-upload/draft/publish", {
755
- body: { ...label === void 0 ? {} : { label }, discardPendingChanges }
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, discardPendingChanges = false, baseRevisionNo = null) {
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, discardPendingChanges } : { storageKey, discardPendingChanges, baseRevisionNo }
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
- // 판이 옮겨진다 — 전환은 진행되고, 게시 안 한 AI 변경이 함께 사라진다(`BaselineShiftGuard`).
879
- "PENDING_AI_CHANGES_CONFIRM_REQUIRED",
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 requireConsent(() => options.api.publishDraft(options.label, options.discardPendingChanges === true)).catch((error) => {
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 (creditsAtStake(error))
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
- "`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.",
3636
+ REINSTALL_HOW,
3645
3637
  error
3646
3638
  );
3647
3639
  }
3648
- const declared = raw.version;
3649
- if (typeof declared !== "string" || declared === "") {
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
- "`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."
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(publish\xB7rollback\xB7discard)
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) });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zalkera/cli",
3
- "version": "0.21.0",
3
+ "version": "0.23.0",
4
4
  "description": "잘커라 사이트 소스를 터미널에서 받고·올리고·발행합니다. 편집기 없이, 스크립트에서도.",
5
5
  "license": "MIT",
6
6
  "type": "module",