@zalkera/cli 0.28.0 → 0.29.1

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 +128 -75
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -103,9 +103,9 @@ zalkera logout
103
103
  `y`/`버립니다` 를 그대로 이어 붙인다 — 크레딧이 안 걸렸으니 다시 묻지 않는다.
104
104
  - 다만 확인하시는 사이 사이트 쪽 편집이 달라졌으면 **아무것도 안 버리고** 멈춘다.
105
105
  보여 드린 것과 버리는 것이 갈리면 안 되기 때문이다.
106
- - ⚠ **되돌리기는 원장을 되감지 않는다.** 되돌릴 대상이 마지막 버전이 아니면 사이트는 그 내용으로
107
- **새 버전을 세워** 켠다 — 그래서 「5판으로 되돌렸습니다」 뒤에 켜진 번호가 10일 수 있다. 그때
108
- 그 사실을 함께 말한다.
106
+ - ⚠ **되돌리기는 버전 목록을 되감지 않는다.** 되돌릴 대상이 마지막 버전이 아니면 사이트는 그
107
+ 내용으로 **새 버전을 세워** 켠다 — 그래서 「버전 5로 되돌렸습니다」 뒤에 켜진 번호가 10일 수
108
+ 있다. 그때 그 사실을 함께 말한다.
109
109
  - ⚠ **편집이 걸려 있으면 되돌리기가 막힌다.** 사이트가 그것을 거절한다 — 먼저 `zalkera publish`
110
110
  로 새 버전을 만들거나 `zalkera discard` 로 버려야 한다.
111
111
  - 그 편집이 **옛 버전 위**에 있으면(그 사이 라이브가 옮겨진 경우) `publish` 쪽도 막힌다.
package/dist/main.js CHANGED
@@ -25,11 +25,11 @@ function plainNotice(text, limit = 300) {
25
25
  return "";
26
26
  const cap = Math.min(limit, MAX_CAP);
27
27
  const head = text.length > cap * 4 ? text.slice(0, cap * 4) : text;
28
- const stripped = head.replace(/[\u0000-\u001f\u007f-\u009f\u200b-\u200f\u202a-\u202e\u2066-\u2069\u2028\u2029]+/g, " ");
28
+ const stripped = head.replace(new RegExp("[\\u0000-\\u001f\\u007f-\\u009f\\u2028\\u2029]|\\p{Cf}", "gu"), " ");
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, count, JOSA_PAIRS, countJosa;
33
33
  var init_notice = __esm({
34
34
  "../core/dist/notice.js"() {
35
35
  "use strict";
@@ -38,6 +38,28 @@ 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
+ JOSA_PAIRS = {
48
+ "\uC774/\uAC00": ["\uC774", "\uAC00"],
49
+ "\uC744/\uB97C": ["\uC744", "\uB97C"],
50
+ "\uC740/\uB294": ["\uC740", "\uB294"],
51
+ "\uC73C\uB85C/\uB85C": ["\uC73C\uB85C", "\uB85C"]
52
+ };
53
+ countJosa = (value, josa) => {
54
+ const drawn = count(value);
55
+ const pair = JOSA_PAIRS[josa];
56
+ if (!pair)
57
+ return drawn;
58
+ const last = drawn.slice(-1);
59
+ const kind = /[2459]/.test(last) ? "none" : /[178]/.test(last) ? "rieul" : /[036]/.test(last) ? "other" : "none";
60
+ const withoutFinal = kind === "none" || kind === "rieul" && josa === "\uC73C\uB85C/\uB85C";
61
+ return `${drawn}${withoutFinal ? pair[1] : pair[0]}`;
62
+ };
41
63
  }
42
64
  });
43
65
 
@@ -1109,7 +1131,7 @@ async function ensureEnvIgnored(dir) {
1109
1131
  return "created";
1110
1132
  }
1111
1133
  const content = await readFile2(path, "utf8");
1112
- const ignored = content.split("\n").map((line) => line.trim()).some((line) => line === ".env.local" || line === ".env*" || line === ".env*.local");
1134
+ const ignored = content.split("\n").map((line) => line.trim()).some((line) => line === ".env.local" || line === ".env*" || line === ".env.*" || line === ".env*.local");
1113
1135
  if (ignored)
1114
1136
  return "already";
1115
1137
  const suffix = content.endsWith("\n") || content.length === 0 ? "" : "\n";
@@ -1341,8 +1363,12 @@ async function createSink(targetDir, options) {
1341
1363
  if (options.rejectVendored === true)
1342
1364
  assertNotVendored(segments, name);
1343
1365
  if (entry.type === "5") {
1344
- if (options.decide)
1345
- return;
1366
+ if (options.decide) {
1367
+ if (options.emptyDirs !== true)
1368
+ return;
1369
+ if (options.decide(`${segments.join("/")}/`) === "skip")
1370
+ return;
1371
+ }
1346
1372
  await descend(root, segments, verified);
1347
1373
  return;
1348
1374
  }
@@ -1918,7 +1944,39 @@ function serializeSyncLedger(ledger) {
1918
1944
  }
1919
1945
 
1920
1946
  // ../core/dist/localMark.js
1947
+ import { join as join10 } from "node:path";
1948
+
1949
+ // ../core/dist/gitExclude.js
1950
+ import { chmod as chmod3, lstat as lstat3, readFile as readFile5 } from "node:fs/promises";
1921
1951
  import { join as join9 } from "node:path";
1952
+ async function excludeFromGit(root, path) {
1953
+ try {
1954
+ const info = await lstat3(join9(root, ".git")).catch(() => null);
1955
+ if (!info || !info.isDirectory())
1956
+ return "no-git";
1957
+ const dir = await ensureOwnDir(root, ".git", "info");
1958
+ const file = join9(dir, "exclude");
1959
+ const leaf = await lstat3(file).catch((error) => {
1960
+ if (error?.code === "ENOENT")
1961
+ return null;
1962
+ throw error;
1963
+ });
1964
+ if (leaf !== null && !leaf.isFile())
1965
+ return "failed";
1966
+ const current = leaf === null ? "" : await readFile5(file, "utf8");
1967
+ if (current.split(/\r?\n/).some((line) => line.trim() === path))
1968
+ return "already";
1969
+ const prefix = current === "" || current.endsWith("\n") ? "" : "\n";
1970
+ const mode = leaf === null ? 420 : leaf.mode & 511;
1971
+ await writeOwnFile(file, `${current}${prefix}${path}
1972
+ `, mode);
1973
+ if (leaf !== null)
1974
+ await chmod3(file, mode);
1975
+ return "written";
1976
+ } catch {
1977
+ return "failed";
1978
+ }
1979
+ }
1922
1980
 
1923
1981
  // ../core/dist/sourceVersion.js
1924
1982
  var VERSION_DIGEST_PATTERN = /^[0-9a-f]{64}$/;
@@ -2005,11 +2063,12 @@ async function writeSourceMarkTo(root, mark) {
2005
2063
  async function writeMarkText(root, text) {
2006
2064
  try {
2007
2065
  await ensureOwnDir(root, ".zalkera");
2008
- await writeOwnFile(join9(root, SOURCE_MARK_PATH), text);
2009
- return { ok: true, text: "" };
2066
+ await writeOwnFile(join10(root, SOURCE_MARK_PATH), text);
2010
2067
  } catch (error) {
2011
2068
  return { ok: false, reason: error instanceof Error ? error.message : String(error) };
2012
2069
  }
2070
+ await excludeFromGit(root, SOURCE_MARK_PATH);
2071
+ return { ok: true, text: "" };
2013
2072
  }
2014
2073
 
2015
2074
  // ../core/dist/provenance.js
@@ -2017,7 +2076,7 @@ var PROVENANCE_PATH = ".zalkera/provenance.json";
2017
2076
 
2018
2077
  // ../core/dist/zip.js
2019
2078
  init_errors();
2020
- import { join as join10, relative as relative3, sep as sep3 } from "node:path";
2079
+ import { join as join11, relative as relative3, sep as sep3 } from "node:path";
2021
2080
  import { deflateRaw } from "node:zlib";
2022
2081
  import { promisify } from "node:util";
2023
2082
  var deflate = promisify(deflateRaw);
@@ -2162,8 +2221,9 @@ var SECRET_NAMES = /* @__PURE__ */ new Set([
2162
2221
  var SECRET_SUFFIXES = [".pem", ".key", ".p12", ".pfx", ".p8"];
2163
2222
  var EXCLUDED_PATHS = /* @__PURE__ */ new Set([".zalkera/source.json", SYNC_LEDGER_PATH, PROVENANCE_PATH]);
2164
2223
  var EXCLUDED_PREFIXES = [".zalkera/saved/"];
2224
+ var OWN_TMP = /\.zalkera-[0-9a-f]{12}\.tmp$/;
2165
2225
  function isExcludedPath(path) {
2166
- return EXCLUDED_PATHS.has(path) || EXCLUDED_PREFIXES.some((prefix) => path.startsWith(prefix));
2226
+ return EXCLUDED_PATHS.has(path) || EXCLUDED_PREFIXES.some((prefix) => path.startsWith(prefix)) || OWN_TMP.test(path);
2167
2227
  }
2168
2228
  function isExcludedEntry(entryPath) {
2169
2229
  const path = entryPath.split(sep3).join("/").toLowerCase();
@@ -2246,8 +2306,8 @@ var SERVER_NORMALIZATION_SETS = {
2246
2306
  init_errors();
2247
2307
  import { createReadStream as createReadStream2 } from "node:fs";
2248
2308
  import { createHash as createHash5 } from "node:crypto";
2249
- import { lstat as lstat3, readdir as readdir3 } from "node:fs/promises";
2250
- import { join as join11, sep as sep4 } from "node:path";
2309
+ import { lstat as lstat4, readdir as readdir3 } from "node:fs/promises";
2310
+ import { join as join12, sep as sep4 } from "node:path";
2251
2311
  async function hashWorkdir(root, options = {}) {
2252
2312
  const manifest = {};
2253
2313
  const cap = Math.min(options.maxEntries ?? MAX_ENTRIES, MAX_ENTRIES);
@@ -2265,7 +2325,7 @@ async function hashWorkdir(root, options = {}) {
2265
2325
  if (item.isSymbolicLink())
2266
2326
  continue;
2267
2327
  if (item.isDirectory()) {
2268
- await walk(join11(dir, item.name), relative5);
2328
+ await walk(join12(dir, item.name), relative5);
2269
2329
  continue;
2270
2330
  }
2271
2331
  if (!item.isFile())
@@ -2274,7 +2334,7 @@ async function hashWorkdir(root, options = {}) {
2274
2334
  if (seen > cap) {
2275
2335
  throw new DevtoolsError("LOCAL_TOO_LARGE", `\uC774 \uD3F4\uB354\uC758 \uD30C\uC77C\uC774 \uB108\uBB34 \uB9CE\uC2B5\uB2C8\uB2E4(${cap}\uAC1C \uCD08\uACFC).`, "\uC0AC\uC774\uD2B8 \uD3F4\uB354\uAC00 \uC544\uB2CC \uACF3\uC744 \uAC00\uB9AC\uD0A4\uACE0 \uC788\uC9C0 \uC54A\uC740\uC9C0 \uD655\uC778\uD574 \uC8FC\uC138\uC694.");
2276
2336
  }
2277
- manifest[relative5] = { sha256: await hashFile(join11(dir, item.name)) };
2337
+ manifest[relative5] = { sha256: await hashFile(join12(dir, item.name)) };
2278
2338
  }
2279
2339
  }
2280
2340
  await walk(root, "");
@@ -2296,8 +2356,8 @@ async function resolveExisting(root, path) {
2296
2356
  return null;
2297
2357
  let current = root;
2298
2358
  for (const part of segments) {
2299
- const next = join11(current, part);
2300
- const info = await lstat3(next).catch(() => null);
2359
+ const next = join12(current, part);
2360
+ const info = await lstat4(next).catch(() => null);
2301
2361
  if (!info || info.isSymbolicLink())
2302
2362
  return null;
2303
2363
  current = next;
@@ -2474,10 +2534,13 @@ var DEFAULT_TIMEOUT_MS2 = 10 * 60 * 1e3;
2474
2534
  // ../core/dist/tenantScope.js
2475
2535
  init_notice();
2476
2536
 
2537
+ // ../core/dist/git.js
2538
+ init_notice();
2539
+
2477
2540
  // ../core/dist/npmChoice.js
2478
2541
  import { execFileSync } from "node:child_process";
2479
2542
  import { existsSync as existsSync7, realpathSync } from "node:fs";
2480
- import { delimiter as delimiter2, isAbsolute as isAbsolute2, join as join12, normalize as normalize3, sep as sep5 } from "node:path";
2543
+ import { delimiter as delimiter2, isAbsolute as isAbsolute2, join as join13, normalize as normalize3, sep as sep5 } from "node:path";
2481
2544
  var MIN_SYSTEM_NPM_MAJOR = 9;
2482
2545
  function majorOf(version2) {
2483
2546
  const m = /^(\d+)\./.exec((version2 ?? "").trim());
@@ -2540,7 +2603,7 @@ function npmArgvOf(choice, nodePath) {
2540
2603
  return null;
2541
2604
  }
2542
2605
  }
2543
- var PATH_OPS = { join: join12, isAbsolute: isAbsolute2, normalize: normalize3, sep: sep5 };
2606
+ var PATH_OPS = { join: join13, isAbsolute: isAbsolute2, normalize: normalize3, sep: sep5 };
2544
2607
  function probeSystemNpm(nodePath, excludeUnder = [], env = process.env) {
2545
2608
  const entries = (env.PATH ?? "").split(delimiter2);
2546
2609
  for (const step of systemNpmSearchSteps(entries, PATH_OPS, excludeUnder)) {
@@ -2619,12 +2682,12 @@ var UPGRADE_NOTICE_INTERVAL_MS = 24 * 60 * 60 * 1e3;
2619
2682
 
2620
2683
  // ../core/dist/tokenPath.js
2621
2684
  import { homedir as homedir2 } from "node:os";
2622
- import { isAbsolute as isAbsolute3, join as join13, resolve as resolve5, sep as sep6 } from "node:path";
2685
+ import { isAbsolute as isAbsolute3, join as join14, resolve as resolve5, sep as sep6 } from "node:path";
2623
2686
  function tokenPath(env = process.env, home = homedir2()) {
2624
- const fallback = join13(home, ".config");
2687
+ const fallback = join14(home, ".config");
2625
2688
  const base = env.XDG_CONFIG_HOME;
2626
2689
  const inside = base !== void 0 && isAbsolute3(base) && (resolve5(base) === resolve5(home) || resolve5(base).startsWith(resolve5(home) + sep6));
2627
- return join13(inside ? resolve5(base) : fallback, "zalkera", "auth.json");
2690
+ return join14(inside ? resolve5(base) : fallback, "zalkera", "auth.json");
2628
2691
  }
2629
2692
 
2630
2693
  // ../core/dist/index.js
@@ -2744,9 +2807,9 @@ function trimPaths(paths, cap = PATH_LIST_CAP, all = false) {
2744
2807
  }
2745
2808
 
2746
2809
  // ../core/dist/pull.js
2747
- import { lstat as lstat4, mkdir as mkdir5, readFile as readFile5, realpath as realpath2, rename as rename2, rmdir, stat as stat3, unlink, writeFile as writeFile7 } from "node:fs/promises";
2810
+ import { lstat as lstat5, mkdir as mkdir5, readFile as readFile6, realpath as realpath2, rename as rename2, rmdir, unlink } from "node:fs/promises";
2748
2811
  init_errors();
2749
- import { basename as basename2, dirname as dirname2, join as join14, relative as relative4, resolve as resolve6, sep as sep7 } from "node:path";
2812
+ import { basename as basename2, dirname as dirname2, join as join15, relative as relative4, resolve as resolve6, sep as sep7 } from "node:path";
2750
2813
  var SAVED_SUFFIX = ".zalkera-saved";
2751
2814
  async function pullSiteSource(options) {
2752
2815
  const report = options.onProgress ?? (() => {
@@ -2845,11 +2908,11 @@ async function requireNoLinkedTargets(root, paths) {
2845
2908
  const segments = path.split("/").filter((s) => s !== "");
2846
2909
  let current = root;
2847
2910
  for (const part of segments) {
2848
- current = join14(current, part);
2911
+ current = join15(current, part);
2849
2912
  if (checked.has(current))
2850
2913
  continue;
2851
2914
  checked.add(current);
2852
- const info = await lstat4(current).catch(() => null);
2915
+ const info = await lstat5(current).catch(() => null);
2853
2916
  if (!info?.isSymbolicLink())
2854
2917
  continue;
2855
2918
  throw new DevtoolsError("PULL_WOULD_OVERWRITE", `\uC774 \uD3F4\uB354\uC758 ${relative4(root, current).split(sep7).join("/")} \uAC00 \uB2E4\uB978 \uACF3\uC744 \uAC00\uB9AC\uD0A4\uB294 \uBC14\uB85C\uAC00\uAE30\uC785\uB2C8\uB2E4. \uC544\uBB34\uAC83\uB3C4 \uBC1B\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.`, "\uADF8 \uBC14\uB85C\uAC00\uAE30\uB97C \uC9C0\uC6B0\uACE0 \uB2E4\uC2DC \uC2E4\uD589\uD574 \uC8FC\uC138\uC694. \uADF8\uB300\uB85C \uB450\uBA74 \uC774 \uD3F4\uB354 \uBC16\uC758 \uAC83\uC744 \uB36E\uC5B4\uC4F0\uAC8C \uB429\uB2C8\uB2E4.");
@@ -2868,13 +2931,13 @@ function withoutExcluded(declared) {
2868
2931
  return { incoming, dropped: dropped.sort() };
2869
2932
  }
2870
2933
  async function readLedger(root) {
2871
- const text = await readFile5(join14(root, SYNC_LEDGER_PATH), "utf8").catch(() => null);
2934
+ const text = await readFile6(join15(root, SYNC_LEDGER_PATH), "utf8").catch(() => null);
2872
2935
  return text === null ? null : parseSyncLedger(text);
2873
2936
  }
2874
2937
  async function writeLedger(root, ledger) {
2875
2938
  try {
2876
2939
  const dir = await ensureOwnDir(root, ...SYNC_LEDGER_PATH.split("/").slice(0, -1));
2877
- await writeOwnFile(join14(dir, basename2(SYNC_LEDGER_PATH)), serializeSyncLedger(ledger));
2940
+ await writeOwnFile(join15(dir, basename2(SYNC_LEDGER_PATH)), serializeSyncLedger(ledger));
2878
2941
  } catch {
2879
2942
  return false;
2880
2943
  }
@@ -2883,18 +2946,7 @@ async function writeLedger(root, ledger) {
2883
2946
  return true;
2884
2947
  }
2885
2948
  async function ignoreLedger(root) {
2886
- const info = join14(root, ".git", "info");
2887
- const exists = await stat3(join14(root, ".git")).then((s) => s.isDirectory()).catch(() => false);
2888
- if (!exists)
2889
- return;
2890
- const path = join14(info, "exclude");
2891
- const current = await readFile5(path, "utf8").catch(() => "");
2892
- if (current.split(/\r?\n/).some((line) => line.trim() === SYNC_LEDGER_PATH))
2893
- return;
2894
- await mkdir5(info, { recursive: true });
2895
- const prefix = current === "" || current.endsWith("\n") ? "" : "\n";
2896
- await writeFile7(path, `${current}${prefix}${SYNC_LEDGER_PATH}
2897
- `);
2949
+ await excludeFromGit(root, SYNC_LEDGER_PATH);
2898
2950
  }
2899
2951
  async function currentGeneration(api) {
2900
2952
  return await api.draftFiles().then((state) => state.generation ?? null).catch(() => null);
@@ -2924,13 +2976,13 @@ async function applyDeletes(root, paths) {
2924
2976
  async function setAside(root, plan) {
2925
2977
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
2926
2978
  const real = await realpath2(root).catch(() => root);
2927
- const saved = join14(dirname2(real), `${basename2(real)}${SAVED_SUFFIX}`, stamp);
2979
+ const saved = join15(dirname2(real), `${basename2(real)}${SAVED_SUFFIX}`, stamp);
2928
2980
  const moved = [];
2929
2981
  for (const conflict of plan.conflicts) {
2930
2982
  const from = await resolveExisting(root, conflict.path);
2931
2983
  if (!from)
2932
2984
  continue;
2933
- const to = join14(saved, conflict.path);
2985
+ const to = join15(saved, conflict.path);
2934
2986
  await mkdir5(dirname2(to), { recursive: true });
2935
2987
  await rename2(from, to);
2936
2988
  moved.push(conflict.path);
@@ -2952,13 +3004,13 @@ ${list}`,
2952
3004
 
2953
3005
  // ../core/dist/baseline.js
2954
3006
  init_errors();
2955
- import { readFile as readFile6 } from "node:fs/promises";
2956
- import { join as join15, resolve as resolve7 } from "node:path";
3007
+ import { readFile as readFile7 } from "node:fs/promises";
3008
+ import { join as join16, resolve as resolve7 } from "node:path";
2957
3009
  async function rebuildBaseline(options) {
2958
3010
  const report = options.onProgress ?? (() => {
2959
3011
  });
2960
3012
  const root = resolve7(options.folder);
2961
- const previousText = await readFile6(join15(root, SYNC_LEDGER_PATH), "utf8").catch(() => null);
3013
+ const previousText = await readFile7(join16(root, SYNC_LEDGER_PATH), "utf8").catch(() => null);
2962
3014
  const previous = previousText === null ? null : parseSyncLedger(previousText);
2963
3015
  report("\uD310\uC758 \uD30C\uC77C \uBAA9\uB85D\uC744 \uB2E4\uC2DC \uC77D\uB294 \uC911\uC785\uB2C8\uB2E4\u2026");
2964
3016
  const tar = await fetchVerifiedSourceTar({
@@ -3058,8 +3110,8 @@ function ledgerCorrection(ledger, draft) {
3058
3110
  // ../core/dist/push.js
3059
3111
  init_errors();
3060
3112
  init_notice();
3061
- import { readFile as readFile7, stat as stat4 } from "node:fs/promises";
3062
- import { basename as basename3, join as join16, resolve as resolve8 } from "node:path";
3113
+ import { readFile as readFile8, stat as stat3 } from "node:fs/promises";
3114
+ import { basename as basename3, join as join17, resolve as resolve8 } from "node:path";
3063
3115
 
3064
3116
  // ../core/dist/pushPlan.js
3065
3117
  function effectiveSha(base, draft, path) {
@@ -3136,10 +3188,10 @@ async function pushSiteSource(options) {
3136
3188
  const notYetLive = active < ledger.base.revisionNo;
3137
3189
  const mineRow = notYetLive ? rows.find((row) => row.revisionNo === ledger.base.revisionNo) : void 0;
3138
3190
  if (mineRow?.status === "BUILDING") {
3139
- throw new DevtoolsError("PUSH_BASE_BUILDING", `${ledger.base.revisionNo}\uD310\uC744 \uC9D3\uB294 \uC911\uC774\uB77C \uC544\uC9C1 \uC62C\uB9B4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`, "\uBE4C\uB4DC\uAC00 \uB05D\uB098\uBA74 \uADF8 \uBC84\uC804\uC774 \uCF1C\uC9D1\uB2C8\uB2E4 \u2014 `zalkera status` \uB85C \uD655\uC778\uD55C \uB4A4 \uB2E4\uC2DC \uC62C\uB824 \uC8FC\uC138\uC694. \uC774 \uD3F4\uB354\uB294 \uADF8\uB300\uB85C \uB450\uC154\uB3C4 \uB429\uB2C8\uB2E4.");
3191
+ throw new DevtoolsError("PUSH_BASE_BUILDING", `\uBC84\uC804 ${countJosa(ledger.base.revisionNo, "\uC744/\uB97C")} \uC9D3\uB294 \uC911\uC774\uB77C \uC544\uC9C1 \uC62C\uB9B4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`, "\uBE4C\uB4DC\uAC00 \uB05D\uB098\uBA74 \uADF8 \uBC84\uC804\uC774 \uCF1C\uC9D1\uB2C8\uB2E4 \u2014 `zalkera status` \uB85C \uD655\uC778\uD55C \uB4A4 \uB2E4\uC2DC \uC62C\uB824 \uC8FC\uC138\uC694. \uC774 \uD3F4\uB354\uB294 \uADF8\uB300\uB85C \uB450\uC154\uB3C4 \uB429\uB2C8\uB2E4.");
3140
3192
  }
3141
3193
  if (mineRow?.status === "FAILED") {
3142
- throw new DevtoolsError("PUSH_BASE_BUILD_FAILED", `${ledger.base.revisionNo}\uD310\uC740 \uC9D3\uB2E4\uAC00 \uC2E4\uD328\uD574 \uCF1C\uC9C0\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4 \u2014 \uC9C0\uAE08 \uCF1C\uC838 \uC788\uB294 \uAC83\uC740 ${active}\uD310\uC785\uB2C8\uB2E4.`, "`zalkera status` \uB85C \uC2E4\uD328 \uC0AC\uC720\uB97C \uD655\uC778\uD574 \uC8FC\uC138\uC694. \uACE0\uCCD0\uC11C \uB2E4\uC2DC \uC62C\uB9AC\uB824\uBA74 `zalkera baseline` \uC73C\uB85C \uAE30\uC900\uC744 \uC9C0\uAE08 \uCF1C\uC9C4 \uBC84\uC804\uC73C\uB85C \uB9DE\uCD98 \uB4A4 \uC791\uC5C5\uD574 \uC8FC\uC138\uC694.");
3194
+ throw new DevtoolsError("PUSH_BASE_BUILD_FAILED", `\uBC84\uC804 ${countJosa(ledger.base.revisionNo, "\uC740/\uB294")} \uC9D3\uB2E4\uAC00 \uC2E4\uD328\uD574 \uCF1C\uC9C0\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4 \u2014 \uC9C0\uAE08 \uCF1C\uC838 \uC788\uB294 \uAC83\uC740 \uBC84\uC804 ${count(active)} \uC785\uB2C8\uB2E4.`, "`zalkera status` \uB85C \uC2E4\uD328 \uC0AC\uC720\uB97C \uD655\uC778\uD574 \uC8FC\uC138\uC694. \uACE0\uCCD0\uC11C \uB2E4\uC2DC \uC62C\uB9AC\uB824\uBA74 `zalkera baseline` \uC73C\uB85C \uAE30\uC900\uC744 \uC9C0\uAE08 \uCF1C\uC9C4 \uBC84\uC804\uC73C\uB85C \uB9DE\uCD98 \uB4A4 \uC791\uC5C5\uD574 \uC8FC\uC138\uC694.");
3143
3195
  }
3144
3196
  const dirty = Object.keys(ledger.files).filter((path) => local[path]?.sha256 !== ledger.files[path]?.sha256).length;
3145
3197
  throw baseMoved(ledger.base.revisionNo, active, dirty);
@@ -3336,13 +3388,13 @@ async function bodyOf(root, edits) {
3336
3388
  body.push({ path: edit.path, remove: true, ...edit.baseSha256 === null ? {} : { baseSha256: edit.baseSha256 } });
3337
3389
  continue;
3338
3390
  }
3339
- const size = (await stat4(join16(root, edit.path))).size;
3391
+ const size = (await stat3(join17(root, edit.path))).size;
3340
3392
  if (size > MAX_PUSH_FILE_BYTES) {
3341
3393
  throw new DevtoolsError("PUSH_TOO_LARGE", `\uD30C\uC77C \uD558\uB098\uAC00 \uB108\uBB34 \uD07D\uB2C8\uB2E4: ${edit.path} (${Math.round(size / 1024)}KB \xB7 \uC0C1\uD55C ${MAX_PUSH_FILE_BYTES / 1024}KB).`, "\uB3D9\uC601\uC0C1\xB7\uC6D0\uBCF8 \uC774\uBBF8\uC9C0\xB7\uBE4C\uB4DC \uC0B0\uCD9C\uBB3C\uC774 \uC18C\uC2A4 \uD3F4\uB354\uC5D0 \uB4E4\uC5B4 \uC788\uC9C0 \uC54A\uC740\uC9C0 \uD655\uC778\uD574 \uC8FC\uC138\uC694.");
3342
3394
  }
3343
3395
  if (total + size > MAX_PUSH_BYTES)
3344
3396
  throw tooLarge();
3345
- const raw = await readFile7(join16(root, edit.path));
3397
+ const raw = await readFile8(join17(root, edit.path));
3346
3398
  const breach = templateBreach(basename3(edit.path), raw);
3347
3399
  if (breach !== null) {
3348
3400
  throw new DevtoolsError("PUSH_SECRET_TEMPLATE", `${edit.path} \uC5D0 \uC2E4\uC81C \uAC12\uC774 \uB4E4\uC5B4 \uC788\uC5B4 \uC62C\uB9AC\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4(${breach}).`, "`.env.example` \uAC19\uC740 \uC11C\uC2DD \uD30C\uC77C\uC740 \uAC12\uC744 \uBE44\uC6CC \uB450\uC5B4\uC57C \uC62C\uB77C\uAC11\uB2C8\uB2E4. \uC2E4\uC81C \uAC12\uC740 \uCF58\uC194\uC758 \uD658\uACBD\uBCC0\uC218 \uD654\uBA74\uC5D0\uC11C \uAD00\uB9AC\uD574 \uC8FC\uC138\uC694.");
@@ -3373,13 +3425,14 @@ function unreachable() {
3373
3425
  return new DevtoolsError("SERVER_REJECTED", "\uC62C\uB9AC\uAE30\uAC00 \uB05D\uB098\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.", "\uB2E4\uC2DC \uC2DC\uB3C4\uD574 \uC8FC\uC138\uC694.");
3374
3426
  }
3375
3427
  function baseMoved(mine, now, dirty) {
3376
- const way = dirty === 0 ? `\uC774 \uD3F4\uB354\uB97C ${now}\uD310\uC5D0 \uB9DE\uCD94\uB824\uBA74 \`zalkera pull\` \uC744 \uC2E4\uD589\uD558\uC138\uC694.` : `\uC774 \uD3F4\uB354\uC5D0\uC11C \uACE0\uCE5C \uAC83\uC774 ${dirty}\uAC1C \uC788\uC5B4 \`zalkera pull\` \uB3C4 \uADF8\uB300\uB85C\uB294 \uB9C9\uD799\uB2C8\uB2E4. \`zalkera pull --discard-local\` \uC744 \uC2E4\uD589\uD558\uBA74 \uACE0\uCE5C \uD30C\uC77C\uC744 \uC606 \uD3F4\uB354\uC5D0 \uC62E\uACA8 \uB450\uACE0 \uC0C8 \uD310\uC744 \uBC1B\uC2B5\uB2C8\uB2E4 \u2014 \uADF8 \uB4A4 \uC62E\uACA8 \uB454 \uD30C\uC77C\uC744 \uBCF4\uACE0 \uB2E4\uC2DC \uACE0\uCCD0 \uC62C\uB9AC\uC2DC\uBA74 \uB429\uB2C8\uB2E4.`;
3377
- return new DevtoolsError("PUSH_BASE_MOVED", `\uAE30\uC900\uC774 ${mine}\uD310\uC5D0\uC11C ${now}\uD310\uC73C\uB85C \uC6C0\uC9C1\uC5EC \uC544\uBB34\uAC83\uB3C4 \uC62C\uB9AC\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.`, `${way} \uC9C0\uAE08 \uC62C\uB9AC\uBA74 ${mine}\uD310\uC744 \uBCF4\uACE0 \uACE0\uCE5C \uB0B4\uC6A9\uC774 ${now}\uD310 \uC704\uC5D0 \uC5B9\uD799\uB2C8\uB2E4.`);
3428
+ const way = dirty === 0 ? `\uC774 \uD3F4\uB354\uB97C \uBC84\uC804 ${count(now)} \uC5D0 \uB9DE\uCD94\uB824\uBA74 \`zalkera pull\` \uC744 \uC2E4\uD589\uD558\uC138\uC694.` : `\uC774 \uD3F4\uB354\uC5D0\uC11C \uACE0\uCE5C \uAC83\uC774 ${dirty}\uAC1C \uC788\uC5B4 \`zalkera pull\` \uB3C4 \uADF8\uB300\uB85C\uB294 \uB9C9\uD799\uB2C8\uB2E4. \`zalkera pull --discard-local\` \uC744 \uC2E4\uD589\uD558\uBA74 \uACE0\uCE5C \uD30C\uC77C\uC744 \uC606 \uD3F4\uB354\uC5D0 \uC62E\uACA8 \uB450\uACE0 \uC0C8 \uD310\uC744 \uBC1B\uC2B5\uB2C8\uB2E4 \u2014 \uADF8 \uB4A4 \uC62E\uACA8 \uB454 \uD30C\uC77C\uC744 \uBCF4\uACE0 \uB2E4\uC2DC \uACE0\uCCD0 \uC62C\uB9AC\uC2DC\uBA74 \uB429\uB2C8\uB2E4.`;
3429
+ return new DevtoolsError("PUSH_BASE_MOVED", `\uAE30\uC900\uC774 \uBC84\uC804 ${count(mine)} \uC5D0\uC11C \uBC84\uC804 ${countJosa(now, "\uC73C\uB85C/\uB85C")} \uC6C0\uC9C1\uC5EC \uC544\uBB34\uAC83\uB3C4 \uC62C\uB9AC\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.`, `${way} \uC9C0\uAE08 \uC62C\uB9AC\uBA74 \uBC84\uC804 ${countJosa(mine, "\uC744/\uB97C")} \uBCF4\uACE0 \uACE0\uCE5C \uB0B4\uC6A9\uC774 \uBC84\uC804 ${count(now)} \uC704\uC5D0 \uC5B9\uD799\uB2C8\uB2E4.`);
3378
3430
  }
3379
3431
 
3380
3432
  // ../core/dist/draftLifecycle.js
3381
3433
  import { resolve as resolve9 } from "node:path";
3382
3434
  init_errors();
3435
+ init_notice();
3383
3436
  import { rm as rm5 } from "node:fs/promises";
3384
3437
  async function publishDraft(options) {
3385
3438
  const report = options.onProgress ?? (() => {
@@ -3412,9 +3465,9 @@ async function rollbackRevision(options) {
3412
3465
  const root = resolve9(options.folder);
3413
3466
  const active = await activeRevisionNo(options.api);
3414
3467
  if (options.revisionNo === active) {
3415
- throw new DevtoolsError("ROLLBACK_IS_DISCARD", `${options.revisionNo}\uD310\uC740 \uC9C0\uAE08 \uCF1C\uC838 \uC788\uB294 \uBC84\uC804\uC785\uB2C8\uB2E4 \u2014 \uB418\uB3CC\uB9B4 \uAC83\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.`, "\uD3B8\uC9D1 \uC911\uC778 \uAC83\uC744 \uBC84\uB9AC\uB824\uB294 \uAC83\uC774\uB77C\uBA74 `zalkera discard` \uB97C \uC4F0\uC138\uC694. \uADF8\uCABD\uC740 \uD310\uC744 \uC62E\uAE30\uC9C0 \uC54A\uACE0 \uD3B8\uC9D1\uB9CC \uBC84\uB9BD\uB2C8\uB2E4.");
3468
+ throw new DevtoolsError("ROLLBACK_IS_DISCARD", `\uBC84\uC804 ${countJosa(options.revisionNo, "\uC740/\uB294")} \uC9C0\uAE08 \uCF1C\uC838 \uC788\uC2B5\uB2C8\uB2E4 \u2014 \uB418\uB3CC\uB9B4 \uAC83\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.`, "\uD3B8\uC9D1 \uC911\uC778 \uAC83\uC744 \uBC84\uB9AC\uB824\uB294 \uAC83\uC774\uB77C\uBA74 `zalkera discard` \uB97C \uC4F0\uC138\uC694. \uADF8\uCABD\uC740 \uD310\uC744 \uC62E\uAE30\uC9C0 \uC54A\uACE0 \uD3B8\uC9D1\uB9CC \uBC84\uB9BD\uB2C8\uB2E4.");
3416
3469
  }
3417
- report(`${options.revisionNo}\uD310\uC73C\uB85C \uB418\uB3CC\uB9AC\uB294 \uC911\uC785\uB2C8\uB2E4\u2026`);
3470
+ report(`\uBC84\uC804 ${countJosa(options.revisionNo, "\uC73C\uB85C/\uB85C")} \uB418\uB3CC\uB9AC\uB294 \uC911\uC785\uB2C8\uB2E4\u2026`);
3418
3471
  const result = await requireConsent(() => options.api.activateRevision(options.revisionNo, options.discardPending === true)).catch((error) => {
3419
3472
  if (isDraftInProgress(error)) {
3420
3473
  throw new DevtoolsError("ROLLBACK_BLOCKED_BY_DRAFT", "\uC0AC\uC774\uD2B8 \uCABD\uC5D0 \uD3B8\uC9D1 \uC911\uC778 \uAC83\uC774 \uC788\uC5B4 \uBC84\uC804\uC744 \uB418\uB3CC\uB9B4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.", "`zalkera publish` \uB85C \uADF8 \uD3B8\uC9D1\uC744 \uC0C8 \uBC84\uC804\uC73C\uB85C \uB9CC\uB4E4\uAC70\uB098, `zalkera discard` \uB85C \uBC84\uB9B0 \uB4A4 \uB2E4\uC2DC \uC2DC\uB3C4\uD574 \uC8FC\uC138\uC694. \uBC84\uB9AC\uAE30\uB294 \uBB34\uC5C7\uC774 \uAC78\uB824 \uC788\uB294\uC9C0 \uBA3C\uC800 \uBCF4\uC5EC \uC90D\uB2C8\uB2E4.", error);
@@ -3608,12 +3661,12 @@ function flagValue(flags, name) {
3608
3661
 
3609
3662
  // src/context.ts
3610
3663
  import { readFileSync } from "node:fs";
3611
- import { readFile as readFile9 } from "node:fs/promises";
3664
+ import { readFile as readFile10 } from "node:fs/promises";
3612
3665
  import { fileURLToPath } from "node:url";
3613
- import { join as join18, resolve as resolve11 } from "node:path";
3666
+ import { join as join19, resolve as resolve11 } from "node:path";
3614
3667
 
3615
3668
  // src/tokenStore.ts
3616
- import { chmod as chmod3, readFile as readFile8, rm as rm6 } from "node:fs/promises";
3669
+ import { chmod as chmod4, readFile as readFile9, rm as rm6 } from "node:fs/promises";
3617
3670
  import { basename as basename4, dirname as dirname3 } from "node:path";
3618
3671
  var FileTokenStore = class {
3619
3672
  // ⚠ 매개변수 프로퍼티(`constructor(private path)`)를 안 쓴다 — Node 의 타입 제거 실행기가
@@ -3623,7 +3676,7 @@ var FileTokenStore = class {
3623
3676
  this.path = path;
3624
3677
  }
3625
3678
  async read() {
3626
- const text = await readFile8(this.path, "utf8").catch(() => null);
3679
+ const text = await readFile9(this.path, "utf8").catch(() => null);
3627
3680
  if (text === null) return null;
3628
3681
  try {
3629
3682
  const raw = JSON.parse(text);
@@ -3644,10 +3697,10 @@ var FileTokenStore = class {
3644
3697
  }
3645
3698
  async write(tokens) {
3646
3699
  const dir = await ensureOwnDir(dirname3(dirname3(this.path)), basename4(dirname3(this.path)));
3647
- await chmod3(dir, 448).catch(() => {
3700
+ await chmod4(dir, 448).catch(() => {
3648
3701
  });
3649
3702
  await writeOwnFile(this.path, JSON.stringify(tokens), 384);
3650
- await chmod3(this.path, 384);
3703
+ await chmod4(this.path, 384);
3651
3704
  }
3652
3705
  async clear() {
3653
3706
  await rm6(this.path, { force: true });
@@ -3670,9 +3723,9 @@ function serverUrlOf(env = process.env) {
3670
3723
  }
3671
3724
  async function tenantOf(folder, explicit) {
3672
3725
  if (explicit) return TENANT_CODE.test(explicit) ? explicit : null;
3673
- const mark = parseSourceMark(await readFile9(join18(folder, SOURCE_MARK_PATH), "utf8").catch(() => null));
3726
+ const mark = parseSourceMark(await readFile10(join19(folder, SOURCE_MARK_PATH), "utf8").catch(() => null));
3674
3727
  if (mark) return mark.tenant;
3675
- const text = await readFile9(join18(folder, SYNC_LEDGER_PATH), "utf8").catch(() => null);
3728
+ const text = await readFile10(join19(folder, SYNC_LEDGER_PATH), "utf8").catch(() => null);
3676
3729
  return text === null ? null : parseSyncLedger(text)?.tenant ?? null;
3677
3730
  }
3678
3731
  async function openAuth(options = {}) {
@@ -3782,18 +3835,18 @@ function describeStatus(status, verbose = false) {
3782
3835
  const lines = [];
3783
3836
  lines.push(`\uC0AC\uC774\uD2B8: ${status.tenant ?? "(\uBAA8\uB984)"}`);
3784
3837
  lines.push(
3785
- status.baseRevisionNo === null ? "\uC774 \uD3F4\uB354\uAC00 \uC120 \uD310: \uBAA8\uB984" : `\uC774 \uD3F4\uB354\uAC00 \uC120 \uD310: ${status.baseRevisionNo}\uD310`
3838
+ status.baseRevisionNo === null ? "\uC774 \uD3F4\uB354\uAC00 \uC120 \uD310: \uBAA8\uB984" : `\uC774 \uD3F4\uB354\uAC00 \uC120 \uD310: \uBC84\uC804 ${count(status.baseRevisionNo)}`
3786
3839
  );
3787
- if (status.activeRevisionNo !== null) lines.push(`\uC9C0\uAE08 \uC0AC\uC774\uD2B8\uC5D0 \uCF1C\uC9C4 \uD310: ${status.activeRevisionNo}\uD310`);
3840
+ if (status.activeRevisionNo !== null) lines.push(`\uC9C0\uAE08 \uC0AC\uC774\uD2B8\uC5D0 \uCF1C\uC9C4 \uD310: \uBC84\uC804 ${count(status.activeRevisionNo)}`);
3788
3841
  if (status.behind) {
3789
3842
  lines.push("");
3790
- lines.push(`\uAE30\uC900\uC774 ${status.baseRevisionNo}\uD310\uC5D0\uC11C ${status.activeRevisionNo}\uD310\uC73C\uB85C \uC6C0\uC9C1\uC600\uC2B5\uB2C8\uB2E4.`);
3843
+ lines.push(`\uAE30\uC900\uC774 \uBC84\uC804 ${count(status.baseRevisionNo)} \uC5D0\uC11C \uBC84\uC804 ${countJosa(status.activeRevisionNo, "\uC73C\uB85C/\uB85C")} \uC6C0\uC9C1\uC600\uC2B5\uB2C8\uB2E4.`);
3791
3844
  const dirty = status.changed.length + status.removed.length;
3792
3845
  lines.push(
3793
- dirty === 0 ? `\uC774 \uD3F4\uB354\uB97C ${status.activeRevisionNo}\uD310\uC5D0 \uB9DE\uCD94\uB824\uBA74 \`zalkera pull\` \uC744 \uC2E4\uD589\uD558\uC138\uC694.` : `\uC774 \uD3F4\uB354\uC5D0\uC11C \uACE0\uCE5C \uAC83\uC774 ${dirty}\uAC1C \uC788\uC5B4 \`zalkera pull\` \uC740 \uADF8\uB300\uB85C\uB294 \uB9C9\uD799\uB2C8\uB2E4. \`zalkera pull --discard-local\` \uC744 \uC2E4\uD589\uD558\uBA74 \uACE0\uCE5C \uD30C\uC77C\uC744 \uC606 \uD3F4\uB354\uC5D0 \uC62E\uACA8 \uB450\uACE0 \uC0C8 \uD310\uC744 \uBC1B\uC2B5\uB2C8\uB2E4.`
3846
+ dirty === 0 ? `\uC774 \uD3F4\uB354\uB97C \uBC84\uC804 ${count(status.activeRevisionNo)} \uC5D0 \uB9DE\uCD94\uB824\uBA74 \`zalkera pull\` \uC744 \uC2E4\uD589\uD558\uC138\uC694.` : `\uC774 \uD3F4\uB354\uC5D0\uC11C \uACE0\uCE5C \uAC83\uC774 ${dirty}\uAC1C \uC788\uC5B4 \`zalkera pull\` \uC740 \uADF8\uB300\uB85C\uB294 \uB9C9\uD799\uB2C8\uB2E4. \`zalkera pull --discard-local\` \uC744 \uC2E4\uD589\uD558\uBA74 \uACE0\uCE5C \uD30C\uC77C\uC744 \uC606 \uD3F4\uB354\uC5D0 \uC62E\uACA8 \uB450\uACE0 \uC0C8 \uD310\uC744 \uBC1B\uC2B5\uB2C8\uB2E4.`
3794
3847
  );
3795
3848
  lines.push(
3796
- `\uC9C0\uAE08 \uC62C\uB9AC\uBA74 ${status.baseRevisionNo}\uD310\uC744 \uBCF4\uACE0 \uACE0\uCE5C \uB0B4\uC6A9\uC774 ${status.activeRevisionNo}\uD310 \uC704\uC5D0 \uC5B9\uD799\uB2C8\uB2E4.`
3849
+ `\uC9C0\uAE08 \uC62C\uB9AC\uBA74 \uBC84\uC804 ${countJosa(status.baseRevisionNo, "\uC744/\uB97C")} \uBCF4\uACE0 \uACE0\uCE5C \uB0B4\uC6A9\uC774 \uBC84\uC804 ${count(status.activeRevisionNo)} \uC704\uC5D0 \uC5B9\uD799\uB2C8\uB2E4.`
3797
3850
  );
3798
3851
  }
3799
3852
  const changes = [
@@ -3965,7 +4018,7 @@ async function main(argv) {
3965
4018
  `)
3966
4019
  });
3967
4020
  const parts = [
3968
- `${result.revisionNo}\uD310\uC744 \uBC1B\uC558\uC2B5\uB2C8\uB2E4.`,
4021
+ `\uBC84\uC804 ${countJosa(result.revisionNo, "\uC744/\uB97C")} \uBC1B\uC558\uC2B5\uB2C8\uB2E4.`,
3969
4022
  `\uC0C8\uB85C \uC4F4 \uAC83 ${result.written}\uAC1C \xB7 \uC9C0\uC6B4 \uAC83 ${result.deleted}\uAC1C \xB7 \uADF8\uB300\uB85C \uB454 \uAC83 ${result.unchanged}\uAC1C`
3970
4023
  ];
3971
4024
  if (result.untracked > 0) parts.push(`\uC774 \uD3F4\uB354\uC5D0\uB9CC \uC788\uB294 \uD30C\uC77C ${result.untracked}\uAC1C\uB294 \uAC74\uB4DC\uB9AC\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.`);
@@ -4022,7 +4075,7 @@ async function main(argv) {
4022
4075
  `)
4023
4076
  });
4024
4077
  const lines = [
4025
- `${result.revisionNo}\uD310\uC73C\uB85C \uC62C\uB838\uC2B5\uB2C8\uB2E4.`,
4078
+ `\uBC84\uC804 ${countJosa(result.revisionNo, "\uC73C\uB85C/\uB85C")} \uC62C\uB838\uC2B5\uB2C8\uB2E4.`,
4026
4079
  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 \uC790\uB3D9\uC73C\uB85C \uC190\uB2D8\uC5D0\uAC8C \uBCF4\uC785\uB2C8\uB2E4."
4027
4080
  ];
4028
4081
  if (result.capabilityNote) lines.push(result.capabilityNote);
@@ -4051,14 +4104,14 @@ async function main(argv) {
4051
4104
  `)
4052
4105
  });
4053
4106
  const lines = [
4054
- result.revisionNo === null ? "\uB418\uB3CC\uB838\uC2B5\uB2C8\uB2E4 \u2014 \uB2E4\uB9CC \uC9C0\uAE08 \uCF1C\uC9C4 \uBC84\uC804\uC758 \uBC88\uD638\uB97C \uD655\uC778\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4." : result.pointerMoved ? `${result.revisionNo}\uD310\uC73C\uB85C \uB418\uB3CC\uB838\uC2B5\uB2C8\uB2E4.` : `\uC774\uBBF8 ${result.revisionNo}\uD310\uC774\uC5C8\uC2B5\uB2C8\uB2E4.`
4107
+ result.revisionNo === null ? "\uB418\uB3CC\uB838\uC2B5\uB2C8\uB2E4 \u2014 \uB2E4\uB9CC \uC9C0\uAE08 \uCF1C\uC9C4 \uBC84\uC804\uC758 \uBC88\uD638\uB97C \uD655\uC778\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4." : result.pointerMoved ? `\uBC84\uC804 ${countJosa(result.revisionNo, "\uC73C\uB85C/\uB85C")} \uB418\uB3CC\uB838\uC2B5\uB2C8\uB2E4.` : `\uC774\uBBF8 \uBC84\uC804 ${count(result.revisionNo)} \uC774\uC5C8\uC2B5\uB2C8\uB2E4.`
4055
4108
  ];
4056
4109
  if (result.revisionNo === null) {
4057
4110
  lines.push("`zalkera status` \uB85C \uD655\uC778\uD574 \uC8FC\uC138\uC694. **\uB2E4\uC2DC \uB418\uB3CC\uB9AC\uC9C0 \uB9C8\uC138\uC694** \u2014 \uAC19\uC740 \uB0B4\uC6A9\uC758 \uBC84\uC804\uC774 \uD558\uB098 \uB354 \uC0DD\uAE41\uB2C8\uB2E4.");
4058
4111
  }
4059
4112
  if (result.revisionNo !== null && result.revisionNo !== result.requested) {
4060
4113
  lines.push(
4061
- `(${result.requested}\uD310\uC758 \uB0B4\uC6A9\uC73C\uB85C **\uC0C8 ${result.revisionNo}\uD310**\uC744 \uC138\uC6CC \uCF30\uC2B5\uB2C8\uB2E4 \u2014 \uC6D0\uC7A5\uC740 \uB418\uAC10\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.)`
4114
+ `(\uBC84\uC804 ${count(result.requested)} \uC758 \uB0B4\uC6A9\uC73C\uB85C **\uC0C8 \uBC84\uC804 ${count(result.revisionNo)}** \uC744 \uC138\uC6CC \uCF30\uC2B5\uB2C8\uB2E4 \u2014 \uC6D0\uC7A5\uC740 \uB418\uAC10\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.)`
4062
4115
  );
4063
4116
  }
4064
4117
  if (result.discardedPendingChanges > 0) {
@@ -4067,9 +4120,9 @@ async function main(argv) {
4067
4120
  lines.push("", "\uC774 \uD3F4\uB354\uC758 \uD30C\uC77C\uC740 \uAC74\uB4DC\uB9AC\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4 \u2014 \uB9DE\uCD94\uB824\uBA74 `zalkera pull` \uC744 \uC2E4\uD589\uD558\uC138\uC694.");
4068
4121
  if (result.differing.length > 0) {
4069
4122
  lines.push(
4070
- `\u26A0 \uC774 \uD3F4\uB354\uC758 ${result.differing.length}\uAC1C \uD30C\uC77C\uC774 ${result.revisionNo}\uD310\uACFC \uB2E4\uB985\uB2C8\uB2E4.`,
4123
+ `\u26A0 \uC774 \uD3F4\uB354\uC758 ${result.differing.length}\uAC1C \uD30C\uC77C\uC774 \uBC84\uC804 ${count(result.revisionNo)} \uC758 \uB0B4\uC6A9\uACFC \uB2E4\uB985\uB2C8\uB2E4.`,
4071
4124
  ...pathLines(result.differing, verbose),
4072
- `\uC9C0\uAE08 \`zalkera push\` \uB97C \uD558\uBA74 \uC774 ${result.differing.length}\uAC1C\uAC00 **\uC804\uBD80** \uC62C\uB77C\uAC00 ${result.revisionNo}\uD310\uC758 \uB0B4\uC6A9\uC744 \uB36E\uC2B5\uB2C8\uB2E4.`
4125
+ `\uC9C0\uAE08 \`zalkera push\` \uB97C \uD558\uBA74 \uC774 ${result.differing.length}\uAC1C\uAC00 **\uC804\uBD80** \uC62C\uB77C\uAC00 \uBC84\uC804 ${count(result.revisionNo)} \uC758 \uB0B4\uC6A9\uC744 \uB36E\uC2B5\uB2C8\uB2E4.`
4073
4126
  );
4074
4127
  }
4075
4128
  if (!result.ledgerRebuilt) {
@@ -4204,15 +4257,15 @@ async function main(argv) {
4204
4257
  `)
4205
4258
  });
4206
4259
  const lines = [
4207
- `${result.revisionNo}\uD310(\uD30C\uC77C ${result.files}\uAC1C)\uC744 \uAE30\uC900\uC73C\uB85C ${result.replaced ? "\uB2E4\uC2DC " : ""}\uC138\uC6E0\uC2B5\uB2C8\uB2E4.`,
4260
+ `\uBC84\uC804 ${count(result.revisionNo)} (\uD30C\uC77C ${count(result.files)}\uAC1C)\uC744 \uAE30\uC900\uC73C\uB85C ${result.replaced ? "\uB2E4\uC2DC " : ""}\uC138\uC6E0\uC2B5\uB2C8\uB2E4.`,
4208
4261
  "\uD3F4\uB354\uC758 \uD30C\uC77C\uC740 \uAC74\uB4DC\uB9AC\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4."
4209
4262
  ];
4210
4263
  if (result.differing.length > 0) {
4211
4264
  lines.push(
4212
4265
  "",
4213
- `\u26A0 \uC774 \uD3F4\uB354\uC758 ${result.differing.length}\uAC1C \uD30C\uC77C\uC774 ${result.revisionNo}\uD310\uACFC \uB2E4\uB985\uB2C8\uB2E4.`,
4266
+ `\u26A0 \uC774 \uD3F4\uB354\uC758 ${result.differing.length}\uAC1C \uD30C\uC77C\uC774 \uBC84\uC804 ${count(result.revisionNo)} \uC758 \uB0B4\uC6A9\uACFC \uB2E4\uB985\uB2C8\uB2E4.`,
4214
4267
  ...pathLines(result.differing, verbose),
4215
- `\uC9C0\uAE08 \`zalkera push\` \uB97C \uD558\uBA74 \uC774 ${result.differing.length}\uAC1C\uAC00 **\uC804\uBD80** \uC62C\uB77C\uAC00 ${result.revisionNo}\uD310\uC758 \uB0B4\uC6A9\uC744 \uB36E\uC2B5\uB2C8\uB2E4.`,
4268
+ `\uC9C0\uAE08 \`zalkera push\` \uB97C \uD558\uBA74 \uC774 ${result.differing.length}\uAC1C\uAC00 **\uC804\uBD80** \uC62C\uB77C\uAC00 \uBC84\uC804 ${count(result.revisionNo)} \uC758 \uB0B4\uC6A9\uC744 \uB36E\uC2B5\uB2C8\uB2E4.`,
4216
4269
  "\uACE0\uCE5C \uAC83\uC774 \uADF8\uC911 \uC77C\uBD80\uBFD0\uC774\uB77C\uBA74 `zalkera pull` \uB85C \uBC1B\uB294 \uCABD\uC774 \uB9DE\uC2B5\uB2C8\uB2E4."
4217
4270
  );
4218
4271
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zalkera/cli",
3
- "version": "0.28.0",
3
+ "version": "0.29.1",
4
4
  "description": "잘커라 사이트 소스를 터미널에서 받고·올리고·발행합니다. 편집기 없이, 스크립트에서도.",
5
5
  "license": "MIT",
6
6
  "type": "module",