@bifos/nhncloud-cli 0.13.0 → 0.14.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/dist/index.js CHANGED
@@ -66,6 +66,14 @@ var NhnCloudCliError = class extends Error {
66
66
  exitCode;
67
67
  };
68
68
 
69
+ // src/utils/terminal.ts
70
+ function sanitizeForTerminal(value) {
71
+ return value.replace(/[\x00-\x1F\x7F]/g, "?");
72
+ }
73
+ function sanitizeMultilineForTerminal(value) {
74
+ return value.replace(/[\x00-\x09\x0B-\x1F\x7F]/g, "?");
75
+ }
76
+
69
77
  // src/api/timeout.ts
70
78
  var SYNC_FLOOR_MS = 6e5;
71
79
  var DEFAULT_TIMEOUT_MS = 3e4;
@@ -737,15 +745,20 @@ async function getIaasToken(profile, iaas, forceRefresh = false) {
737
745
  }
738
746
 
739
747
  // src/services/logncrash/client.ts
740
- var import_ky4 = __toESM(require("ky"));
748
+ var import_ky5 = __toESM(require("ky"));
741
749
 
742
750
  // src/api/envelope.ts
751
+ var NhnEnvelopeError = class extends NhnCloudCliError {
752
+ resultCode;
753
+ constructor(resultCode, resultMessage) {
754
+ super(`API \uC624\uB958: ${resultMessage}`, EXIT_API_ERROR);
755
+ this.name = "NhnEnvelopeError";
756
+ this.resultCode = resultCode;
757
+ }
758
+ };
743
759
  function unwrapHeader(res) {
744
760
  if (!res.header.isSuccessful) {
745
- throw new NhnCloudCliError(
746
- `API \uC624\uB958: ${res.header.resultMessage}`,
747
- EXIT_API_ERROR
748
- );
761
+ throw new NhnEnvelopeError(res.header.resultCode, res.header.resultMessage);
749
762
  }
750
763
  }
751
764
  function unwrap(res) {
@@ -756,6 +769,45 @@ function unwrap(res) {
756
769
  return res.body;
757
770
  }
758
771
 
772
+ // src/services/logncrash/errors.ts
773
+ var import_ky4 = require("ky");
774
+ var RATE_LIMIT_RESULT_CODE = 429;
775
+ function isRateLimitError(err) {
776
+ return err instanceof NhnEnvelopeError && Number(err.resultCode) === RATE_LIMIT_RESULT_CODE;
777
+ }
778
+ function withRateLimitHint(err) {
779
+ return new NhnCloudCliError(
780
+ `${err.message}
781
+ \uC870\uD68C \uD69F\uC218 \uC81C\uD55C\uC5D0 \uAC78\uB838\uC2B5\uB2C8\uB2E4. \uC2DC\uAC04\uC744 \uB450\uACE0 \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694. \uAC80\uC0C9 \uAE30\uAC04\uC744 \uC881\uD600\uB3C4 \uD480\uB9AC\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.`,
782
+ err.exitCode
783
+ );
784
+ }
785
+ var LogncrashServerError = class extends NhnCloudCliError {
786
+ requestId;
787
+ constructor(message, requestId) {
788
+ super(message, EXIT_API_ERROR);
789
+ this.name = "LogncrashServerError";
790
+ this.requestId = requestId === null ? null : sanitizeForTerminal(requestId);
791
+ }
792
+ };
793
+ async function toLogncrashError(err) {
794
+ if (!(err instanceof import_ky4.HTTPError) || err.response.status !== 500) {
795
+ return toNhnCloudCliError(err);
796
+ }
797
+ let requestId = null;
798
+ try {
799
+ const body = await err.response.json();
800
+ if (typeof body === "object" && body !== null && "requestId" in body && typeof body.requestId === "string") {
801
+ requestId = body.requestId;
802
+ }
803
+ } catch {
804
+ }
805
+ return new LogncrashServerError(
806
+ `API \uD638\uCD9C \uC2E4\uD328 (500): ${err.message}`,
807
+ requestId
808
+ );
809
+ }
810
+
759
811
  // src/services/logncrash/client.ts
760
812
  var LogncrashClient = class {
761
813
  appkey;
@@ -770,7 +822,7 @@ var LogncrashClient = class {
770
822
  const endpoint = endpointFor("logncrash");
771
823
  const url = `${endpoint}/v3/${encodeURIComponent(this.appkey)}/logs/cursor`;
772
824
  try {
773
- const res = await import_ky4.default.post(url, {
825
+ const res = await import_ky5.default.post(url, {
774
826
  headers,
775
827
  json: {
776
828
  query: params.query,
@@ -785,7 +837,7 @@ var LogncrashClient = class {
785
837
  }).json();
786
838
  return unwrap(res);
787
839
  } catch (err) {
788
- throw toNhnCloudCliError(err);
840
+ throw await toLogncrashError(err);
789
841
  }
790
842
  }
791
843
  /**
@@ -796,7 +848,7 @@ var LogncrashClient = class {
796
848
  const endpoint = endpointFor("logncrash");
797
849
  const url = `${endpoint}/v3/${encodeURIComponent(this.appkey)}/logs/scroll`;
798
850
  try {
799
- const res = await import_ky4.default.post(url, {
851
+ const res = await import_ky5.default.post(url, {
800
852
  headers,
801
853
  json: {
802
854
  query: params.query,
@@ -808,7 +860,7 @@ var LogncrashClient = class {
808
860
  }).json();
809
861
  return unwrap(res);
810
862
  } catch (err) {
811
- throw toNhnCloudCliError(err);
863
+ throw await toLogncrashError(err);
812
864
  }
813
865
  }
814
866
  /**
@@ -819,14 +871,14 @@ var LogncrashClient = class {
819
871
  const endpoint = endpointFor("logncrash");
820
872
  const url = `${endpoint}/v3/${encodeURIComponent(this.appkey)}/logs/scroll/${encodeURIComponent(scrollKey)}`;
821
873
  try {
822
- const res = await import_ky4.default.post(url, {
874
+ const res = await import_ky5.default.post(url, {
823
875
  headers,
824
876
  retry: 0,
825
877
  timeout: DEFAULT_TIMEOUT_MS
826
878
  }).json();
827
879
  return unwrap(res);
828
880
  } catch (err) {
829
- throw toNhnCloudCliError(err);
881
+ throw await toLogncrashError(err);
830
882
  }
831
883
  }
832
884
  readHeaders() {
@@ -858,7 +910,7 @@ var LogncrashClient = class {
858
910
  if (params.logType !== void 0) payload["logType"] = params.logType;
859
911
  if (params.host !== void 0) payload["host"] = params.host;
860
912
  try {
861
- const res = await import_ky4.default.post(url, {
913
+ const res = await import_ky5.default.post(url, {
862
914
  headers: { "Content-Type": "application/json" },
863
915
  json: payload,
864
916
  retry: 0,
@@ -872,7 +924,7 @@ var LogncrashClient = class {
872
924
  };
873
925
 
874
926
  // src/services/ncr/client.ts
875
- var import_ky5 = __toESM(require("ky"));
927
+ var import_ky6 = __toESM(require("ky"));
876
928
 
877
929
  // src/services/ncr/types.ts
878
930
  function isRegistry(val) {
@@ -913,7 +965,7 @@ var NcrClient = class {
913
965
  async listRegistries(appKey) {
914
966
  const url = `${this.baseUrl}/ncr/v2.0/appkeys/${encodeURIComponent(appKey)}/registries`;
915
967
  try {
916
- const res = await import_ky5.default.get(url, {
968
+ const res = await import_ky6.default.get(url, {
917
969
  headers: this.authHeaders(),
918
970
  retry: 0,
919
971
  timeout: DEFAULT_TIMEOUT_MS
@@ -940,7 +992,7 @@ var NcrClient = class {
940
992
  async getRegistry(appKey, registry) {
941
993
  const url = `${this.baseUrl}/ncr/v2.0/appkeys/${encodeURIComponent(appKey)}/registries/${encodeURIComponent(registry)}`;
942
994
  try {
943
- const res = await import_ky5.default.get(url, {
995
+ const res = await import_ky6.default.get(url, {
944
996
  headers: this.authHeaders(),
945
997
  retry: 0,
946
998
  timeout: DEFAULT_TIMEOUT_MS
@@ -961,7 +1013,7 @@ var NcrClient = class {
961
1013
  };
962
1014
 
963
1015
  // src/services/ncs/client.ts
964
- var import_ky6 = __toESM(require("ky"));
1016
+ var import_ky7 = __toESM(require("ky"));
965
1017
 
966
1018
  // src/services/ncs/types.ts
967
1019
  function isNcsTemplateSummary(val) {
@@ -1053,7 +1105,7 @@ var NcsClient = class {
1053
1105
  searchParams["disable_containers"] = query.disableContainers;
1054
1106
  }
1055
1107
  try {
1056
- const res = await import_ky6.default.get(url, {
1108
+ const res = await import_ky7.default.get(url, {
1057
1109
  headers: this.authHeaders(),
1058
1110
  searchParams,
1059
1111
  retry: 0,
@@ -1085,7 +1137,7 @@ var NcsClient = class {
1085
1137
  async getTemplate(id) {
1086
1138
  const url = `${this.baseUrl}/templates/${encodeURIComponent(id)}`;
1087
1139
  try {
1088
- const res = await import_ky6.default.get(url, {
1140
+ const res = await import_ky7.default.get(url, {
1089
1141
  headers: this.authHeaders(),
1090
1142
  retry: 0,
1091
1143
  timeout: DEFAULT_TIMEOUT_MS
@@ -1118,7 +1170,7 @@ var NcsClient = class {
1118
1170
  if (query.page !== void 0) searchParams["page"] = query.page;
1119
1171
  if (query.size !== void 0) searchParams["size"] = query.size;
1120
1172
  try {
1121
- const res = await import_ky6.default.get(url, {
1173
+ const res = await import_ky7.default.get(url, {
1122
1174
  headers: this.authHeaders(),
1123
1175
  searchParams,
1124
1176
  retry: 0,
@@ -1150,7 +1202,7 @@ var NcsClient = class {
1150
1202
  async getTemplateVersion(id, version) {
1151
1203
  const url = `${this.baseUrl}/templates/${encodeURIComponent(id)}/versions/${encodeURIComponent(version)}`;
1152
1204
  try {
1153
- const res = await import_ky6.default.get(url, {
1205
+ const res = await import_ky7.default.get(url, {
1154
1206
  headers: this.authHeaders(),
1155
1207
  retry: 0,
1156
1208
  timeout: DEFAULT_TIMEOUT_MS
@@ -1182,7 +1234,7 @@ var NcsClient = class {
1182
1234
  if (query.page !== void 0) searchParams["page"] = query.page;
1183
1235
  if (query.size !== void 0) searchParams["size"] = query.size;
1184
1236
  try {
1185
- const res = await import_ky6.default.get(url, {
1237
+ const res = await import_ky7.default.get(url, {
1186
1238
  headers: this.authHeaders(),
1187
1239
  searchParams,
1188
1240
  retry: 0,
@@ -1214,7 +1266,7 @@ var NcsClient = class {
1214
1266
  async getWorkload(id) {
1215
1267
  const url = `${this.baseUrl}/workloads/${encodeURIComponent(id)}`;
1216
1268
  try {
1217
- const res = await import_ky6.default.get(url, {
1269
+ const res = await import_ky7.default.get(url, {
1218
1270
  headers: this.authHeaders(),
1219
1271
  retry: 0,
1220
1272
  timeout: DEFAULT_TIMEOUT_MS
@@ -1254,7 +1306,7 @@ var NcsClient = class {
1254
1306
  if (query.page !== void 0) searchParams["page"] = query.page;
1255
1307
  if (query.size !== void 0) searchParams["size"] = query.size;
1256
1308
  try {
1257
- const res = await import_ky6.default.get(url, {
1309
+ const res = await import_ky7.default.get(url, {
1258
1310
  headers: this.authHeaders(),
1259
1311
  searchParams,
1260
1312
  retry: 0,
@@ -1292,7 +1344,7 @@ var NcsClient = class {
1292
1344
  if (query.page !== void 0) searchParams["page"] = query.page;
1293
1345
  if (query.size !== void 0) searchParams["size"] = query.size;
1294
1346
  try {
1295
- const res = await import_ky6.default.get(url, {
1347
+ const res = await import_ky7.default.get(url, {
1296
1348
  headers: this.authHeaders(),
1297
1349
  searchParams,
1298
1350
  retry: 0,
@@ -1329,7 +1381,7 @@ var NcsClient = class {
1329
1381
  if (query.size !== void 0) searchParams["size"] = query.size;
1330
1382
  if (query.sort !== void 0) searchParams["sort"] = query.sort;
1331
1383
  try {
1332
- const res = await import_ky6.default.get(url, {
1384
+ const res = await import_ky7.default.get(url, {
1333
1385
  headers: this.authHeaders(),
1334
1386
  searchParams,
1335
1387
  retry: 0,
@@ -1361,7 +1413,7 @@ var NcsClient = class {
1361
1413
  async getWorkloadHistory(id, historyId) {
1362
1414
  const url = `${this.baseUrl}/workloads/${encodeURIComponent(id)}/history/${encodeURIComponent(historyId)}`;
1363
1415
  try {
1364
- const res = await import_ky6.default.get(url, {
1416
+ const res = await import_ky7.default.get(url, {
1365
1417
  headers: this.authHeaders(),
1366
1418
  retry: 0,
1367
1419
  timeout: DEFAULT_TIMEOUT_MS
@@ -1389,7 +1441,7 @@ var NcsClient = class {
1389
1441
  async getWorkloadScheduleHistory(id) {
1390
1442
  const url = `${this.baseUrl}/workloads/${encodeURIComponent(id)}/schedulehistory`;
1391
1443
  try {
1392
- const res = await import_ky6.default.get(url, {
1444
+ const res = await import_ky7.default.get(url, {
1393
1445
  headers: this.authHeaders(),
1394
1446
  retry: 0,
1395
1447
  timeout: DEFAULT_TIMEOUT_MS
@@ -1418,7 +1470,7 @@ var NcsClient = class {
1418
1470
  async createTemplate(body) {
1419
1471
  const url = `${this.baseUrl}/templates`;
1420
1472
  try {
1421
- const res = await import_ky6.default.post(url, {
1473
+ const res = await import_ky7.default.post(url, {
1422
1474
  headers: this.authHeaders(),
1423
1475
  json: body,
1424
1476
  retry: 0,
@@ -1445,7 +1497,7 @@ var NcsClient = class {
1445
1497
  async deleteTemplate(id) {
1446
1498
  const url = `${this.baseUrl}/templates/${encodeURIComponent(id)}`;
1447
1499
  try {
1448
- const res = await import_ky6.default.delete(url, {
1500
+ const res = await import_ky7.default.delete(url, {
1449
1501
  headers: this.authHeaders(),
1450
1502
  retry: 0,
1451
1503
  timeout: DEFAULT_TIMEOUT_MS
@@ -1466,7 +1518,7 @@ var NcsClient = class {
1466
1518
  async createTemplateVersion(id, body) {
1467
1519
  const url = `${this.baseUrl}/templates/${encodeURIComponent(id)}/versions`;
1468
1520
  try {
1469
- const res = await import_ky6.default.post(url, {
1521
+ const res = await import_ky7.default.post(url, {
1470
1522
  headers: this.authHeaders(),
1471
1523
  json: body,
1472
1524
  retry: 0,
@@ -1493,7 +1545,7 @@ var NcsClient = class {
1493
1545
  async deleteTemplateVersion(id, version) {
1494
1546
  const url = `${this.baseUrl}/templates/${encodeURIComponent(id)}/versions/${encodeURIComponent(version)}`;
1495
1547
  try {
1496
- const res = await import_ky6.default.delete(url, {
1548
+ const res = await import_ky7.default.delete(url, {
1497
1549
  headers: this.authHeaders(),
1498
1550
  retry: 0,
1499
1551
  timeout: DEFAULT_TIMEOUT_MS
@@ -1511,7 +1563,7 @@ var NcsClient = class {
1511
1563
  async pauseWorkload(id) {
1512
1564
  const url = `${this.baseUrl}/workloads/${encodeURIComponent(id)}/pause`;
1513
1565
  try {
1514
- const res = await import_ky6.default.post(url, {
1566
+ const res = await import_ky7.default.post(url, {
1515
1567
  headers: this.authHeaders(),
1516
1568
  retry: 0,
1517
1569
  timeout: DEFAULT_TIMEOUT_MS
@@ -1529,7 +1581,7 @@ var NcsClient = class {
1529
1581
  async resumeWorkload(id) {
1530
1582
  const url = `${this.baseUrl}/workloads/${encodeURIComponent(id)}/resume`;
1531
1583
  try {
1532
- const res = await import_ky6.default.post(url, {
1584
+ const res = await import_ky7.default.post(url, {
1533
1585
  headers: this.authHeaders(),
1534
1586
  retry: 0,
1535
1587
  timeout: DEFAULT_TIMEOUT_MS
@@ -1547,7 +1599,7 @@ var NcsClient = class {
1547
1599
  async restartWorkloadTask(id, taskId) {
1548
1600
  const url = `${this.baseUrl}/workloads/${encodeURIComponent(id)}/tasks/${encodeURIComponent(taskId)}/restart`;
1549
1601
  try {
1550
- const res = await import_ky6.default.post(url, {
1602
+ const res = await import_ky7.default.post(url, {
1551
1603
  headers: this.authHeaders(),
1552
1604
  retry: 0,
1553
1605
  timeout: DEFAULT_TIMEOUT_MS
@@ -1567,7 +1619,7 @@ var NcsClient = class {
1567
1619
  async createWorkload(body) {
1568
1620
  const url = `${this.baseUrl}/workloads`;
1569
1621
  try {
1570
- const res = await import_ky6.default.post(url, {
1622
+ const res = await import_ky7.default.post(url, {
1571
1623
  headers: this.authHeaders(),
1572
1624
  json: body,
1573
1625
  retry: 0,
@@ -1595,7 +1647,7 @@ var NcsClient = class {
1595
1647
  async updateWorkload(id, body) {
1596
1648
  const url = `${this.baseUrl}/workloads/${encodeURIComponent(id)}`;
1597
1649
  try {
1598
- const res = await import_ky6.default.put(url, {
1650
+ const res = await import_ky7.default.put(url, {
1599
1651
  headers: this.authHeaders(),
1600
1652
  json: body,
1601
1653
  retry: 0,
@@ -1625,7 +1677,7 @@ var NcsClient = class {
1625
1677
  async patchWorkload(id, patch) {
1626
1678
  const url = `${this.baseUrl}/workloads/${encodeURIComponent(id)}`;
1627
1679
  try {
1628
- const res = await import_ky6.default.patch(url, {
1680
+ const res = await import_ky7.default.patch(url, {
1629
1681
  headers: { ...this.authHeaders(), "Content-Type": "application/json-patch+json" },
1630
1682
  json: patch,
1631
1683
  retry: 0,
@@ -1677,7 +1729,7 @@ var NcsClient = class {
1677
1729
  async deleteWorkload(id) {
1678
1730
  const url = `${this.baseUrl}/workloads/${encodeURIComponent(id)}`;
1679
1731
  try {
1680
- const res = await import_ky6.default.delete(url, {
1732
+ const res = await import_ky7.default.delete(url, {
1681
1733
  headers: this.authHeaders(),
1682
1734
  retry: 0,
1683
1735
  timeout: DEFAULT_TIMEOUT_MS
@@ -1696,7 +1748,7 @@ var NcsClient = class {
1696
1748
  async getMalwareConfig() {
1697
1749
  const url = `${this.baseUrl}/malware/config`;
1698
1750
  try {
1699
- const res = await import_ky6.default.get(url, {
1751
+ const res = await import_ky7.default.get(url, {
1700
1752
  headers: this.authHeaders(),
1701
1753
  retry: 0,
1702
1754
  timeout: DEFAULT_TIMEOUT_MS
@@ -1725,7 +1777,7 @@ var NcsClient = class {
1725
1777
  async setMalwareConfig(enabled) {
1726
1778
  const url = `${this.baseUrl}/malware/config`;
1727
1779
  try {
1728
- const res = await import_ky6.default.post(url, {
1780
+ const res = await import_ky7.default.post(url, {
1729
1781
  headers: this.authHeaders(),
1730
1782
  json: { enabled },
1731
1783
  retry: 0,
@@ -1755,7 +1807,7 @@ var NcsClient = class {
1755
1807
  async getMalwareResult(workloadId, historyId) {
1756
1808
  const url = `${this.baseUrl}/workloads/${encodeURIComponent(workloadId)}/history/${encodeURIComponent(historyId)}/malware`;
1757
1809
  try {
1758
- const res = await import_ky6.default.get(url, {
1810
+ const res = await import_ky7.default.get(url, {
1759
1811
  headers: this.authHeaders(),
1760
1812
  retry: 0,
1761
1813
  timeout: DEFAULT_TIMEOUT_MS
@@ -2071,7 +2123,7 @@ async function runInteractive(opts) {
2071
2123
  await saveAndVerify(profileName, uak, logncrash, iaas, ncr, ncs, void 0, true, uak);
2072
2124
  } catch (err) {
2073
2125
  if (err instanceof NhnCloudCliError && err.exitCode === EXIT_AUTH_ERROR) {
2074
- process.stderr.write(import_chalk.default.red(` \u2717 ${err.message}
2126
+ process.stderr.write(import_chalk.default.red(` \u2717 ${sanitizeMultilineForTerminal(err.message)}
2075
2127
  `));
2076
2128
  const saveDespite = await confirm({
2077
2129
  message: "\uAC80\uC99D \uC2E4\uD328\uC5D0\uB3C4 \uC800\uC7A5\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C?",
@@ -2317,14 +2369,14 @@ function compareByCodePoint(left, right) {
2317
2369
  return leftPoints.length - rightPoints.length;
2318
2370
  }
2319
2371
  function ensureRegularFile(filePath) {
2320
- let stat2;
2372
+ let stat3;
2321
2373
  try {
2322
- stat2 = (0, import_node_fs2.lstatSync)(filePath);
2374
+ stat3 = (0, import_node_fs2.lstatSync)(filePath);
2323
2375
  } catch (error) {
2324
2376
  const reason = error instanceof Error ? error.message : String(error);
2325
2377
  throw new NhnCloudCliError(`\uC2A4\uD0AC \uD30C\uC77C\uC744 \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${filePath} (${reason})`, EXIT_PARAM_ERROR);
2326
2378
  }
2327
- if (stat2.isSymbolicLink() || !stat2.isFile()) {
2379
+ if (stat3.isSymbolicLink() || !stat3.isFile()) {
2328
2380
  throw new NhnCloudCliError(`\uC2A4\uD0AC \uCF58\uD150\uCE20\uB294 \uC815\uADDC \uD30C\uC77C\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4: ${filePath}`, EXIT_PARAM_ERROR);
2329
2381
  }
2330
2382
  }
@@ -2341,21 +2393,21 @@ function collectReferenceFiles(referencesRoot, currentDir, files) {
2341
2393
  }
2342
2394
  for (const entry of entries) {
2343
2395
  const entryPath = import_node_path4.default.join(currentDir, entry.name);
2344
- let stat2;
2396
+ let stat3;
2345
2397
  try {
2346
- stat2 = (0, import_node_fs2.lstatSync)(entryPath);
2398
+ stat3 = (0, import_node_fs2.lstatSync)(entryPath);
2347
2399
  } catch (error) {
2348
2400
  const reason = error instanceof Error ? error.message : String(error);
2349
2401
  throw new NhnCloudCliError(`\uC2A4\uD0AC \uCF58\uD150\uCE20\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${entryPath} (${reason})`, EXIT_PARAM_ERROR);
2350
2402
  }
2351
- if (stat2.isSymbolicLink()) {
2403
+ if (stat3.isSymbolicLink()) {
2352
2404
  throw new NhnCloudCliError(`\uC2A4\uD0AC \uCF58\uD150\uCE20\uC5D0 \uC2EC\uBCFC\uB9AD \uB9C1\uD06C\uB97C \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${entryPath}`, EXIT_PARAM_ERROR);
2353
2405
  }
2354
- if (stat2.isDirectory()) {
2406
+ if (stat3.isDirectory()) {
2355
2407
  collectReferenceFiles(referencesRoot, entryPath, files);
2356
2408
  continue;
2357
2409
  }
2358
- if (!stat2.isFile()) {
2410
+ if (!stat3.isFile()) {
2359
2411
  throw new NhnCloudCliError(`\uC2A4\uD0AC \uCF58\uD150\uCE20\uB294 \uC815\uADDC \uD30C\uC77C \uB610\uB294 \uB514\uB809\uD130\uB9AC\uC5EC\uC57C \uD569\uB2C8\uB2E4: ${entryPath}`, EXIT_PARAM_ERROR);
2360
2412
  }
2361
2413
  files.push(`references/${import_node_path4.default.relative(referencesRoot, entryPath).split(import_node_path4.default.sep).join("/")}`);
@@ -3133,10 +3185,17 @@ function resolveTime(input) {
3133
3185
  }
3134
3186
  return toLocalISOString(now);
3135
3187
  }
3136
- const isoPattern = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}(:\d{2})?([+-]\d{2}:\d{2}|Z)?)?$/;
3188
+ const isoPattern = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}([+-]\d{2}:\d{2}|Z)$/;
3137
3189
  if (isoPattern.test(trimmed)) {
3138
3190
  return trimmed;
3139
3191
  }
3192
+ const partialIso = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}(:\d{2})?)?$/.test(trimmed);
3193
+ if (partialIso) {
3194
+ throw new NhnCloudCliError(
3195
+ `\uC2DC\uAC04 \uD615\uC2DD \uC624\uB958: "${input}" \u2014 \uCD08\uC640 \uC2DC\uAC04\uB300\uAE4C\uC9C0 \uC9C0\uC815\uD574\uC57C \uD569\uB2C8\uB2E4 (\uC608: 2026-08-03T00:00:00+09:00 \uB610\uB294 2026-08-03T00:00:00Z).`,
3196
+ EXIT_PARAM_ERROR
3197
+ );
3198
+ }
3140
3199
  throw new NhnCloudCliError(
3141
3200
  `\uC2DC\uAC04 \uD615\uC2DD \uC624\uB958: "${input}" \u2014 ISO8601 (\uC608: 2024-01-01T00:00:00+09:00) \uB610\uB294 \uC0C1\uB300\uC2DC\uAC04 (\uC608: 1h, 30m, 2d, now) \uC744 \uC0AC\uC6A9\uD558\uC138\uC694.`,
3142
3201
  EXIT_PARAM_ERROR
@@ -3144,6 +3203,49 @@ function resolveTime(input) {
3144
3203
  }
3145
3204
  var MAX_RANGE_DAYS = 31;
3146
3205
  var MAX_LOOKBACK_DAYS = 90;
3206
+ var MIN_SPLIT_WINDOW_MS = 10 * 60 * 1e3;
3207
+ function splitTimeRange(fromIso, toIso, windowMs) {
3208
+ if (windowMs < MIN_SPLIT_WINDOW_MS) {
3209
+ throw new NhnCloudCliError(
3210
+ `\uAC80\uC0C9 \uBD84\uD560 \uCC3D\uC740 \uCD5C\uC18C ${MIN_SPLIT_WINDOW_MS / 6e4}\uBD84\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4.`,
3211
+ EXIT_PARAM_ERROR
3212
+ );
3213
+ }
3214
+ const normalizedWindowMs = Math.floor(windowMs / 1e3) * 1e3;
3215
+ const fromMs = floorToSecond(new Date(fromIso).getTime());
3216
+ const toMs = floorToSecond(new Date(toIso).getTime());
3217
+ if (normalizedWindowMs >= toMs - fromMs) {
3218
+ return [{ from: fromIso, to: toIso }];
3219
+ }
3220
+ const windows = [];
3221
+ let windowFromMs = fromMs;
3222
+ let windowFrom = fromIso;
3223
+ while (windowFromMs < toMs) {
3224
+ const windowToMs = Math.min(windowFromMs + normalizedWindowMs, toMs);
3225
+ const windowTo = windowToMs === toMs ? toIso : formatSplitBoundary(windowToMs, fromIso);
3226
+ windows.push({ from: windowFrom, to: windowTo });
3227
+ windowFromMs = windowToMs;
3228
+ windowFrom = windowTo;
3229
+ }
3230
+ return windows;
3231
+ }
3232
+ function floorToSecond(timestampMs) {
3233
+ return Math.floor(timestampMs / 1e3) * 1e3;
3234
+ }
3235
+ function formatSplitBoundary(timestampMs, referenceIso) {
3236
+ const secondTimestampMs = floorToSecond(timestampMs);
3237
+ if (referenceIso.endsWith("Z")) {
3238
+ return `${new Date(secondTimestampMs).toISOString().slice(0, 19)}Z`;
3239
+ }
3240
+ const offsetMatch = referenceIso.match(/([+-])(\d{2}):(\d{2})$/);
3241
+ if (!offsetMatch) {
3242
+ return toLocalISOString(new Date(secondTimestampMs));
3243
+ }
3244
+ const direction = offsetMatch[1] === "+" ? 1 : -1;
3245
+ const offsetMinutes = direction * (Number(offsetMatch[2]) * 60 + Number(offsetMatch[3]));
3246
+ const shifted = new Date(secondTimestampMs + offsetMinutes * 6e4).toISOString().slice(0, 19);
3247
+ return `${shifted}${offsetMatch[0]}`;
3248
+ }
3147
3249
  function assertSearchRange(fromIso, toIso) {
3148
3250
  const from = new Date(fromIso);
3149
3251
  const to = new Date(toIso);
@@ -3186,7 +3288,7 @@ function formatLogRow(log) {
3186
3288
  const body = truncate(getString(log["logBody"] ?? log["body"] ?? log["message"] ?? ""), 60);
3187
3289
  return [logTime, logType, body];
3188
3290
  }
3189
- var searchCommand = new import_commander4.Command("search").description("Log & Crash \uB85C\uADF8 \uAC80\uC0C9").option("--query <lucene>", "Lucene \uC9C8\uC758 \uBB38\uC790\uC5F4 (\uD544\uC218)").option("--from <time>", "\uAC80\uC0C9 \uC2DC\uC791: ISO8601 \uB610\uB294 \uC0C1\uB300\uC2DC\uAC04 (1h/30m/2d/now) (\uD544\uC218)").option("--to <time>", "\uAC80\uC0C9 \uB05D: ISO8601 \uB610\uB294 \uC0C1\uB300\uC2DC\uAC04 (\uD544\uC218)").option("--page <n>", "\uC804\uD658 \uD638\uD658\uC6A9 pageNumber. 0\uB9CC \uD5C8\uC6A9 (\uB2E4\uC74C \uD398\uC774\uC9C0\uB294 --cursor)", "0").option("--size <n>", "pageSize (\uAE30\uBCF8 10, \uCD5C\uB300 100)", "10").option("--cursor <value>", "\uB2E4\uC74C \uD398\uC774\uC9C0 \uC870\uD68C\uC6A9 opaque cursor").option("--profile <name>", "\uC0AC\uC6A9\uD560 profile \uC774\uB984").action(async (_opts, cmd) => {
3291
+ var searchCommand = new import_commander4.Command("search").description("Log & Crash \uB85C\uADF8 \uAC80\uC0C9").option("--query <lucene>", "Lucene \uC9C8\uC758 \uBB38\uC790\uC5F4 (\uD544\uC218)").option("--from <time>", "\uAC80\uC0C9 \uC2DC\uC791: \uCD08\xB7\uC2DC\uAC04\uB300 \uD3EC\uD568 ISO8601 \uB610\uB294 \uC0C1\uB300\uC2DC\uAC04 (1h/30m/2d/now) (\uD544\uC218)").option("--to <time>", "\uAC80\uC0C9 \uB05D: \uCD08\xB7\uC2DC\uAC04\uB300 \uD3EC\uD568 ISO8601 \uB610\uB294 \uC0C1\uB300\uC2DC\uAC04 (\uD544\uC218)").option("--page <n>", "\uC804\uD658 \uD638\uD658\uC6A9 pageNumber. 0\uB9CC \uD5C8\uC6A9 (\uB2E4\uC74C \uD398\uC774\uC9C0\uB294 --cursor)", "0").option("--size <n>", "pageSize (\uAE30\uBCF8 10, \uCD5C\uB300 100)", "10").option("--cursor <value>", "\uB2E4\uC74C \uD398\uC774\uC9C0 \uC870\uD68C\uC6A9 opaque cursor").option("--profile <name>", "\uC0AC\uC6A9\uD560 profile \uC774\uB984").action(async (_opts, cmd) => {
3190
3292
  const opts = cmd.optsWithGlobals();
3191
3293
  if (!opts.query) {
3192
3294
  throw new NhnCloudCliError(`--query \uC635\uC158\uC740 \uD544\uC218\uC785\uB2C8\uB2E4. \uC608: --query 'logType:"NORMAL"'`, EXIT_PARAM_ERROR);
@@ -3224,6 +3326,17 @@ var searchCommand = new import_commander4.Command("search").description("Log & C
3224
3326
  });
3225
3327
  } catch (err) {
3226
3328
  stopSpinner(false);
3329
+ if (isRateLimitError(err)) {
3330
+ throw withRateLimitHint(err);
3331
+ }
3332
+ if (err instanceof LogncrashServerError) {
3333
+ const requestId = err.requestId === null ? "" : ` (requestId: ${err.requestId})`;
3334
+ throw new LogncrashServerError(
3335
+ `${err.message}
3336
+ \uAC80\uC0C9 \uAE30\uAC04\uC774 \uB113\uC5B4 \uC11C\uBC84\uAC00 \uCC98\uB9AC\uD558\uC9C0 \uBABB\uD588\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uAE30\uAC04\uC744 \uC904\uC5EC \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uAC70\uB098 logncrash export\uB97C \uC0AC\uC6A9\uD558\uC138\uC694.${requestId}`,
3337
+ err.requestId
3338
+ );
3339
+ }
3227
3340
  throw err;
3228
3341
  }
3229
3342
  stopSpinner(true);
@@ -3251,19 +3364,19 @@ var VALID_LEVELS = ["DEBUG", "INFO", "WARN", "ERROR", "FATAL"];
3251
3364
  function resolveBody(opts) {
3252
3365
  if (opts.body !== void 0) return opts.body;
3253
3366
  if (opts.file !== void 0) {
3254
- let stat2;
3367
+ let stat3;
3255
3368
  try {
3256
- stat2 = (0, import_node_fs3.statSync)(opts.file);
3369
+ stat3 = (0, import_node_fs3.statSync)(opts.file);
3257
3370
  } catch (e) {
3258
3371
  const reason = e.code ?? (e instanceof Error ? e.message : String(e));
3259
3372
  throw new NhnCloudCliError(`\uB85C\uADF8 \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${opts.file} (${reason})`, EXIT_PARAM_ERROR);
3260
3373
  }
3261
- if (!stat2.isFile()) {
3374
+ if (!stat3.isFile()) {
3262
3375
  throw new NhnCloudCliError(`\uB85C\uADF8 \uD30C\uC77C\uC774 \uC77C\uBC18 \uD30C\uC77C\uC774 \uC544\uB2D9\uB2C8\uB2E4: ${opts.file}`, EXIT_PARAM_ERROR);
3263
3376
  }
3264
- if (stat2.size > MAX_LOG_BYTES) {
3377
+ if (stat3.size > MAX_LOG_BYTES) {
3265
3378
  throw new NhnCloudCliError(
3266
- `\uB85C\uADF8 \uD30C\uC77C\uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4: ${stat2.size} \uBC14\uC774\uD2B8 (\uD55C\uB3C4 ${MAX_LOG_BYTES} \uBC14\uC774\uD2B8).`,
3379
+ `\uB85C\uADF8 \uD30C\uC77C\uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4: ${stat3.size} \uBC14\uC774\uD2B8 (\uD55C\uB3C4 ${MAX_LOG_BYTES} \uBC14\uC774\uD2B8).`,
3267
3380
  EXIT_PARAM_ERROR
3268
3381
  );
3269
3382
  }
@@ -3351,7 +3464,7 @@ function assertWritable(path5, force) {
3351
3464
  EXIT_PARAM_ERROR
3352
3465
  );
3353
3466
  }
3354
- var exportCommand = new import_commander6.Command("export").description("Log & Crash \uB85C\uADF8\uB97C scroll \uB85C \uC804\uCCB4 \uCD94\uCD9C\uD574 \uD30C\uC77C\uB85C \uC800\uC7A5 (\uB300\uB7C9 \uCD94\uCD9C)").option("--query <lucene>", "Lucene \uC9C8\uC758 \uBB38\uC790\uC5F4 (\uD544\uC218)").option("--from <time>", "\uAC80\uC0C9 \uC2DC\uC791: ISO8601 \uB610\uB294 \uC0C1\uB300\uC2DC\uAC04 (1h/30m/2d/now) (\uD544\uC218)").option("--to <time>", "\uAC80\uC0C9 \uB05D: ISO8601 \uB610\uB294 \uC0C1\uB300\uC2DC\uAC04 (\uD544\uC218)").option("--output <file>", "\uCD9C\uB825 \uD30C\uC77C \uACBD\uB85C (\uD544\uC218)").option("--format <fmt>", "\uCD9C\uB825 \uD615\uC2DD: jsonl(\uAE30\uBCF8, \uD55C \uC904\uB2F9 \uD55C \uB85C\uADF8) \uB610\uB294 json(\uBC30\uC5F4)", "jsonl").option("--size <n>", "\uD3D0\uAE30 \uC608\uC815: Search v3\uC5D0\uC11C\uB294 \uBB34\uC2DC\uB428 (\uD638\uD658 \uAC80\uC99D \uBC94\uC704 10~100)").option("--force", "\uCD9C\uB825 \uD30C\uC77C\uC774 \uC788\uC73C\uBA74 \uB36E\uC5B4\uC4F4\uB2E4").option("--profile <name>", "\uC0AC\uC6A9\uD560 profile \uC774\uB984").action(async (_opts, cmd) => {
3467
+ var exportCommand = new import_commander6.Command("export").description("Log & Crash \uB85C\uADF8\uB97C scroll \uB85C \uC804\uCCB4 \uCD94\uCD9C\uD574 \uD30C\uC77C\uB85C \uC800\uC7A5 (\uB300\uB7C9 \uCD94\uCD9C)").option("--query <lucene>", "Lucene \uC9C8\uC758 \uBB38\uC790\uC5F4 (\uD544\uC218)").option("--from <time>", "\uAC80\uC0C9 \uC2DC\uC791: \uCD08\xB7\uC2DC\uAC04\uB300 \uD3EC\uD568 ISO8601 \uB610\uB294 \uC0C1\uB300\uC2DC\uAC04 (1h/30m/2d/now) (\uD544\uC218)").option("--to <time>", "\uAC80\uC0C9 \uB05D: \uCD08\xB7\uC2DC\uAC04\uB300 \uD3EC\uD568 ISO8601 \uB610\uB294 \uC0C1\uB300\uC2DC\uAC04 (\uD544\uC218)").option("--output <file>", "\uCD9C\uB825 \uD30C\uC77C \uACBD\uB85C (\uD544\uC218)").option("--format <fmt>", "\uCD9C\uB825 \uD615\uC2DD: jsonl(\uAE30\uBCF8, \uD55C \uC904\uB2F9 \uD55C \uB85C\uADF8) \uB610\uB294 json(\uBC30\uC5F4)", "jsonl").option("--size <n>", "\uD3D0\uAE30 \uC608\uC815: Search v3\uC5D0\uC11C\uB294 \uBB34\uC2DC\uB428 (\uD638\uD658 \uAC80\uC99D \uBC94\uC704 10~100)").option("--force", "\uCD9C\uB825 \uD30C\uC77C\uC774 \uC788\uC73C\uBA74 \uB36E\uC5B4\uC4F4\uB2E4").option("--profile <name>", "\uC0AC\uC6A9\uD560 profile \uC774\uB984").action(async (_opts, cmd) => {
3355
3468
  const opts = cmd.optsWithGlobals();
3356
3469
  if (!opts.query) {
3357
3470
  throw new NhnCloudCliError(`--query \uC635\uC158\uC740 \uD544\uC218\uC785\uB2C8\uB2E4. \uC608: --query 'logType:"NORMAL"'`, EXIT_PARAM_ERROR);
@@ -3387,53 +3500,125 @@ var exportCommand = new import_commander6.Command("export").description("Log & C
3387
3500
  assertWritable(opts.output, opts.force ?? false);
3388
3501
  const client = await resolveLogncrashClient(opts.profile);
3389
3502
  const tmp = opts.output + "." + (0, import_node_crypto4.randomBytes)(4).toString("hex") + ".tmp";
3390
- const stream = (0, import_node_fs4.createWriteStream)(tmp, { encoding: "utf-8" });
3503
+ let stream = (0, import_node_fs4.createWriteStream)(tmp, { encoding: "utf-8" });
3391
3504
  const spinner = startSpinner("\uB85C\uADF8 \uCD94\uCD9C \uC911...");
3392
3505
  let count = 0;
3393
3506
  let total = 0;
3507
+ let hasUnqueriedWindows = false;
3394
3508
  let first = true;
3509
+ let bytePosition = 0;
3510
+ let resumeFrom = "";
3511
+ const write = (chunk) => {
3512
+ stream.write(chunk);
3513
+ bytePosition += Buffer.byteLength(chunk, "utf-8");
3514
+ };
3395
3515
  const writePage = (data) => {
3396
3516
  for (const log of data) {
3397
3517
  if (count >= MAX_TOTAL) break;
3398
3518
  const json = JSON.stringify(log);
3399
- stream.write(format === "json" ? first ? json : "," + json : json + "\n");
3519
+ write(format === "json" ? first ? json : "," + json : json + "\n");
3400
3520
  first = false;
3401
3521
  count++;
3402
3522
  }
3403
3523
  };
3404
3524
  try {
3405
- if (format === "json") stream.write("[");
3406
- let res = await client.scrollStart({
3407
- query: opts.query,
3408
- from: fromIso,
3409
- to: toIso
3410
- });
3411
- total = res.totalItems;
3412
- writePage(res.data);
3413
- spinner.text = `\uB85C\uADF8 \uCD94\uCD9C \uC911... ${count}/${total}`;
3414
- while (res.data.length > 0 && res.scrollKey && count < Math.min(total, MAX_TOTAL)) {
3415
- res = await scrollNextWithHint(client, res.scrollKey);
3416
- writePage(res.data);
3417
- spinner.text = `\uB85C\uADF8 \uCD94\uCD9C \uC911... ${count}/${total}`;
3418
- }
3419
- if (format === "json") stream.write("]\n");
3420
- await new Promise((resolve, reject) => {
3421
- stream.once("error", reject);
3422
- stream.end(resolve);
3423
- });
3525
+ if (format === "json") write("[");
3526
+ let pendingWindows = [{ from: fromIso, to: toIso }];
3527
+ let completedWindows = 0;
3528
+ const progressText = () => {
3529
+ const totalWindows = completedWindows + pendingWindows.length + 1;
3530
+ const windowPart = totalWindows > 1 ? `\uCC3D ${completedWindows + 1}/${totalWindows} ` : "";
3531
+ return `\uB85C\uADF8 \uCD94\uCD9C \uC911... ${windowPart}${count}/${total}`;
3532
+ };
3533
+ while (pendingWindows.length > 0) {
3534
+ const window = pendingWindows.shift();
3535
+ if (!window) break;
3536
+ resumeFrom = window.from;
3537
+ const checkpoint = {
3538
+ bytesWritten: bytePosition,
3539
+ count,
3540
+ first,
3541
+ total
3542
+ };
3543
+ try {
3544
+ let res = await client.scrollStart({
3545
+ query: opts.query,
3546
+ from: window.from,
3547
+ to: window.to
3548
+ });
3549
+ total += res.totalItems;
3550
+ writePage(res.data);
3551
+ spinner.text = progressText();
3552
+ while (res.data.length > 0 && res.scrollKey && count < Math.min(total, MAX_TOTAL)) {
3553
+ res = await scrollNextWithHint(client, res.scrollKey);
3554
+ writePage(res.data);
3555
+ spinner.text = progressText();
3556
+ }
3557
+ completedWindows++;
3558
+ if (count >= MAX_TOTAL && pendingWindows.length > 0) {
3559
+ hasUnqueriedWindows = true;
3560
+ break;
3561
+ }
3562
+ } catch (err) {
3563
+ if (!(err instanceof LogncrashServerError)) throw err;
3564
+ await endAndClose(stream);
3565
+ await (0, import_promises5.truncate)(tmp, checkpoint.bytesWritten);
3566
+ stream = (0, import_node_fs4.createWriteStream)(tmp, { encoding: "utf-8", flags: "a" });
3567
+ bytePosition = checkpoint.bytesWritten;
3568
+ count = checkpoint.count;
3569
+ first = checkpoint.first;
3570
+ total = checkpoint.total;
3571
+ const currentWindowMs = new Date(window.to).getTime() - new Date(window.from).getTime();
3572
+ const smallerWindowMs = Math.ceil(currentWindowMs / 2);
3573
+ if (smallerWindowMs < MIN_SPLIT_WINDOW_MS) throw err;
3574
+ pendingWindows = splitTimeRange(window.from, toIso, smallerWindowMs);
3575
+ }
3576
+ }
3577
+ await endAndClose(stream);
3578
+ if (format === "json") await (0, import_promises5.appendFile)(tmp, "]\n");
3424
3579
  } catch (err) {
3425
3580
  stopSpinner(false);
3581
+ const failure = isRateLimitError(err) ? withRateLimitHint(err) : err;
3426
3582
  await new Promise((resolve) => {
3427
- if (stream.destroyed && stream.closed) {
3583
+ if (stream.closed) {
3428
3584
  resolve();
3429
3585
  return;
3430
3586
  }
3431
3587
  stream.once("close", () => resolve());
3432
- stream.destroy();
3588
+ stream.once("error", () => resolve());
3589
+ if (stream.destroyed) return;
3590
+ stream.end();
3433
3591
  });
3434
- await (0, import_promises5.rm)(tmp, { force: true }).catch(() => {
3435
- });
3436
- throw err;
3592
+ if (count === 0) {
3593
+ await (0, import_promises5.rm)(tmp, { force: true }).catch(() => {
3594
+ });
3595
+ throw failure;
3596
+ }
3597
+ const partial = `${opts.output}.partial`;
3598
+ try {
3599
+ if (format === "json") await (0, import_promises5.appendFile)(tmp, "]\n");
3600
+ await (0, import_promises5.rename)(tmp, partial);
3601
+ } catch {
3602
+ process.stderr.write(
3603
+ `\uACBD\uACE0: \uBD80\uBD84 \uACB0\uACFC\uB97C ${partial} \uC5D0 \uB0A8\uAE30\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uC784\uC2DC \uD30C\uC77C: ${tmp}
3604
+ `
3605
+ );
3606
+ throw failure;
3607
+ }
3608
+ process.stderr.write(`\uC548\uB0B4: \uC5EC\uAE30\uAE4C\uC9C0 \uBC1B\uC740 ${count}\uAC74\uC744 ${partial} \uC5D0 \uB0A8\uACBC\uC2B5\uB2C8\uB2E4.
3609
+ `);
3610
+ if (resumeFrom === fromIso) {
3611
+ process.stderr.write(
3612
+ `\uC548\uB0B4: \uC774\uC5B4\uBC1B\uC744 \uC9C0\uC810\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uAC19\uC740 \uBA85\uB839\uC744 \uB2E4\uC2DC \uC2E4\uD589\uD558\uBA74 ${count}\uAC74\uB3C4 \uB2E4\uC2DC \uC870\uD68C\uB429\uB2C8\uB2E4.
3613
+ `
3614
+ );
3615
+ } else {
3616
+ process.stderr.write(
3617
+ `\uC548\uB0B4: \uC774\uC5B4\uBC1B\uC73C\uB824\uBA74 --from "${resumeFrom}" --to "${toIso}" \uB85C \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694. \uACBD\uACC4 \uAD6C\uAC04\uC758 \uB85C\uADF8\uAC00 \uC911\uBCF5\uB420 \uC218 \uC788\uC2B5\uB2C8\uB2E4.
3618
+ `
3619
+ );
3620
+ }
3621
+ throw failure;
3437
3622
  }
3438
3623
  stopSpinner(true, `${count}\uAC74 \uCD94\uCD9C \uC644\uB8CC`);
3439
3624
  try {
@@ -3444,9 +3629,12 @@ var exportCommand = new import_commander6.Command("export").description("Log & C
3444
3629
  const reason = err.code ?? (err instanceof Error ? err.message : String(err));
3445
3630
  throw new NhnCloudCliError(`\uCD9C\uB825 \uD30C\uC77C\uC744 \uC4F8 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${opts.output} (${reason})`, EXIT_PARAM_ERROR);
3446
3631
  }
3447
- if (count >= MAX_TOTAL && total > MAX_TOTAL) {
3632
+ await (0, import_promises5.rm)(`${opts.output}.partial`, { force: true }).catch(() => {
3633
+ });
3634
+ if (count >= MAX_TOTAL && (total > MAX_TOTAL || hasUnqueriedWindows)) {
3635
+ const totalDescription = hasUnqueriedWindows ? `\uC870\uD68C\uD55C \uCC3D\uC5D0\uC11C ${total}\uAC74\uC744 \uD655\uC778\uD588\uACE0 \uB0A8\uC740 \uCC3D\uC744 \uC870\uD68C\uD558\uC9C0 \uC54A\uC740 \uCC44` : `\uC804\uCCB4 ${total}\uAC74 \uC911`;
3448
3636
  process.stderr.write(
3449
- `\uACBD\uACE0: \uC804\uCCB4 ${total}\uAC74 \uC911 \uC0C1\uD55C ${MAX_TOTAL}\uAC74\uAE4C\uC9C0\uB9CC \uCD94\uCD9C\uD588\uC2B5\uB2C8\uB2E4. \uAC80\uC0C9 \uBC94\uC704\uB97C \uC881\uD600 \uB098\uB220 \uCD94\uCD9C\uD558\uC138\uC694.
3637
+ `\uACBD\uACE0: ${totalDescription} \uC0C1\uD55C ${MAX_TOTAL}\uAC74\uAE4C\uC9C0\uB9CC \uCD94\uCD9C\uD588\uC2B5\uB2C8\uB2E4. \uAC80\uC0C9 \uBC94\uC704\uB97C \uC881\uD600 \uB098\uB220 \uCD94\uCD9C\uD558\uC138\uC694.
3450
3638
  `
3451
3639
  );
3452
3640
  }
@@ -3457,6 +3645,8 @@ async function scrollNextWithHint(client, scrollKey) {
3457
3645
  try {
3458
3646
  return await client.scrollNext(scrollKey);
3459
3647
  } catch (err) {
3648
+ if (isRateLimitError(err)) throw err;
3649
+ if (err instanceof LogncrashServerError) throw err;
3460
3650
  if (err instanceof NhnCloudCliError && err.exitCode === EXIT_API_ERROR) {
3461
3651
  throw new NhnCloudCliError(
3462
3652
  `scroll \uB2E4\uC74C \uD398\uC774\uC9C0 \uC694\uCCAD\uC774 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4 (\uC6D0\uC778: ${err.message}). \uAC80\uC0C9 \uBC94\uC704\uB97C \uC881\uD600 \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694.`,
@@ -3466,12 +3656,19 @@ async function scrollNextWithHint(client, scrollKey) {
3466
3656
  throw err;
3467
3657
  }
3468
3658
  }
3659
+ async function endAndClose(stream) {
3660
+ await new Promise((resolve, reject) => {
3661
+ stream.once("error", reject);
3662
+ stream.once("close", resolve);
3663
+ stream.end();
3664
+ });
3665
+ }
3469
3666
 
3470
3667
  // src/commands/deploy/run.ts
3471
3668
  var import_commander7 = require("commander");
3472
3669
 
3473
3670
  // src/services/deploy/client.ts
3474
- var import_ky7 = __toESM(require("ky"));
3671
+ var import_ky8 = __toESM(require("ky"));
3475
3672
  function isBinaryGroup(val) {
3476
3673
  if (typeof val !== "object" || val === null) return false;
3477
3674
  const obj = val;
@@ -3517,7 +3714,7 @@ var DeployClient = class {
3517
3714
  payload["targetServerHostnames"] = params.targetHosts;
3518
3715
  }
3519
3716
  try {
3520
- const res = await import_ky7.default.post(url, {
3717
+ const res = await import_ky8.default.post(url, {
3521
3718
  headers: {
3522
3719
  ...this.authHeaders(),
3523
3720
  "Content-Type": "application/json"
@@ -3537,7 +3734,7 @@ var DeployClient = class {
3537
3734
  async artifacts(appKey) {
3538
3735
  const url = `${this.baseUrl}/api/v2.1/projects/${encodeURIComponent(appKey)}/artifacts`;
3539
3736
  try {
3540
- const res = await import_ky7.default.get(url, {
3737
+ const res = await import_ky8.default.get(url, {
3541
3738
  headers: this.authHeaders(),
3542
3739
  retry: 0,
3543
3740
  timeout: DEFAULT_TIMEOUT_MS
@@ -3553,7 +3750,7 @@ var DeployClient = class {
3553
3750
  async serverGroups(appKey, artifactId) {
3554
3751
  const url = `${this.baseUrl}/api/v2.1/projects/${encodeURIComponent(appKey)}/artifacts/${encodeURIComponent(artifactId)}/server-groups`;
3555
3752
  try {
3556
- const res = await import_ky7.default.get(url, {
3753
+ const res = await import_ky8.default.get(url, {
3557
3754
  headers: this.authHeaders(),
3558
3755
  retry: 0,
3559
3756
  timeout: DEFAULT_TIMEOUT_MS
@@ -3569,7 +3766,7 @@ var DeployClient = class {
3569
3766
  async histories(appKey, artifactId) {
3570
3767
  const url = `${this.baseUrl}/api/v2.1/projects/${encodeURIComponent(appKey)}/artifacts/${encodeURIComponent(artifactId)}/deploy-histories`;
3571
3768
  try {
3572
- const res = await import_ky7.default.get(url, {
3769
+ const res = await import_ky8.default.get(url, {
3573
3770
  headers: this.authHeaders(),
3574
3771
  retry: 0,
3575
3772
  timeout: DEFAULT_TIMEOUT_MS
@@ -3585,7 +3782,7 @@ var DeployClient = class {
3585
3782
  async binaryGroups(appKey, artifactId) {
3586
3783
  const url = `${this.baseUrl}/api/v2.1/projects/${encodeURIComponent(appKey)}/artifacts/${encodeURIComponent(artifactId)}/binary-groups`;
3587
3784
  try {
3588
- const res = await import_ky7.default.get(url, {
3785
+ const res = await import_ky8.default.get(url, {
3589
3786
  headers: this.authHeaders(),
3590
3787
  retry: 0,
3591
3788
  timeout: DEFAULT_TIMEOUT_MS
@@ -3615,7 +3812,7 @@ var DeployClient = class {
3615
3812
  if (params.sortKey !== void 0) searchParams["sortKey"] = params.sortKey;
3616
3813
  if (params.sortDirection !== void 0) searchParams["sortDirection"] = params.sortDirection;
3617
3814
  try {
3618
- const res = await import_ky7.default.get(url, {
3815
+ const res = await import_ky8.default.get(url, {
3619
3816
  headers: this.authHeaders(),
3620
3817
  searchParams,
3621
3818
  retry: 0,
@@ -3657,7 +3854,7 @@ var DeployClient = class {
3657
3854
  form.append("description", params.description);
3658
3855
  }
3659
3856
  try {
3660
- const res = await import_ky7.default.post(url, {
3857
+ const res = await import_ky8.default.post(url, {
3661
3858
  headers: this.authHeaders(),
3662
3859
  // 인증 헤더만 — multipart boundary 는 ky 가 자동 설정
3663
3860
  body: form,
@@ -3693,7 +3890,7 @@ var DeployClient = class {
3693
3890
  async downloadBinary(appKey, artifactId, binaryGroupKey, binaryKey) {
3694
3891
  const url = `${this.baseUrl}/api/v2.1/projects/${encodeURIComponent(appKey)}/artifacts/${encodeURIComponent(artifactId)}/binary-group/${binaryGroupKey}/binaries/${binaryKey}`;
3695
3892
  try {
3696
- const ab = await import_ky7.default.get(url, {
3893
+ const ab = await import_ky8.default.get(url, {
3697
3894
  headers: this.authHeaders(),
3698
3895
  retry: 0,
3699
3896
  timeout: SYNC_TIMEOUT_MS
@@ -3953,9 +4150,9 @@ var uploadCommand = new import_commander13.Command("upload").description("\uB85C
3953
4150
  throw new NhnCloudCliError("--binary-group \uC774 \uD544\uC694\uD569\uB2C8\uB2E4.", EXIT_PARAM_ERROR);
3954
4151
  }
3955
4152
  const filePath = opts.file;
3956
- let stat2;
4153
+ let stat3;
3957
4154
  try {
3958
- stat2 = (0, import_node_fs5.statSync)(filePath);
4155
+ stat3 = (0, import_node_fs5.statSync)(filePath);
3959
4156
  } catch (e) {
3960
4157
  const reason = e.code ?? (e instanceof Error ? e.message : String(e));
3961
4158
  throw new NhnCloudCliError(
@@ -3963,12 +4160,12 @@ var uploadCommand = new import_commander13.Command("upload").description("\uB85C
3963
4160
  EXIT_PARAM_ERROR
3964
4161
  );
3965
4162
  }
3966
- if (!stat2.isFile()) {
4163
+ if (!stat3.isFile()) {
3967
4164
  throw new NhnCloudCliError(`--file \uC774 \uC77C\uBC18 \uD30C\uC77C\uC774 \uC544\uB2D9\uB2C8\uB2E4: ${filePath}`, EXIT_PARAM_ERROR);
3968
4165
  }
3969
- if (stat2.size > MAX_UPLOAD_BYTES) {
4166
+ if (stat3.size > MAX_UPLOAD_BYTES) {
3970
4167
  throw new NhnCloudCliError(
3971
- `--file \uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4 (${stat2.size} \uBC14\uC774\uD2B8). \uC5C5\uB85C\uB4DC \uD55C\uB3C4 ${MAX_UPLOAD_BYTES} \uBC14\uC774\uD2B8.`,
4168
+ `--file \uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4 (${stat3.size} \uBC14\uC774\uD2B8). \uC5C5\uB85C\uB4DC \uD55C\uB3C4 ${MAX_UPLOAD_BYTES} \uBC14\uC774\uD2B8.`,
3972
4169
  EXIT_PARAM_ERROR
3973
4170
  );
3974
4171
  }
@@ -4079,7 +4276,7 @@ async function resolveIaasTokenContext(opts) {
4079
4276
  }
4080
4277
 
4081
4278
  // src/services/instance/client.ts
4082
- var import_ky8 = __toESM(require("ky"));
4279
+ var import_ky9 = __toESM(require("ky"));
4083
4280
  var DEFAULT_POLL_INTERVAL_MS = 5e3;
4084
4281
  function isServer(val) {
4085
4282
  if (typeof val !== "object" || val === null) return false;
@@ -4208,7 +4405,7 @@ var InstanceClient = class {
4208
4405
  async list() {
4209
4406
  const url = `${this.computeEndpoint}/servers/detail`;
4210
4407
  try {
4211
- const raw = await import_ky8.default.get(url, {
4408
+ const raw = await import_ky9.default.get(url, {
4212
4409
  headers: this.authHeaders(),
4213
4410
  retry: 0,
4214
4411
  timeout: DEFAULT_TIMEOUT_MS
@@ -4230,7 +4427,7 @@ var InstanceClient = class {
4230
4427
  async get(id) {
4231
4428
  const url = `${this.computeEndpoint}/servers/${encodeURIComponent(id)}`;
4232
4429
  try {
4233
- const raw = await import_ky8.default.get(url, {
4430
+ const raw = await import_ky9.default.get(url, {
4234
4431
  headers: this.authHeaders(),
4235
4432
  retry: 0,
4236
4433
  timeout: DEFAULT_TIMEOUT_MS
@@ -4289,7 +4486,7 @@ var InstanceClient = class {
4289
4486
  }
4290
4487
  let raw;
4291
4488
  try {
4292
- raw = await import_ky8.default.post(url, {
4489
+ raw = await import_ky9.default.post(url, {
4293
4490
  headers: this.authHeaders(),
4294
4491
  json: { server: serverBody },
4295
4492
  retry: 0,
@@ -4312,7 +4509,7 @@ var InstanceClient = class {
4312
4509
  async delete(id) {
4313
4510
  const url = `${this.computeEndpoint}/servers/${encodeURIComponent(id)}`;
4314
4511
  try {
4315
- await import_ky8.default.delete(url, {
4512
+ await import_ky9.default.delete(url, {
4316
4513
  headers: this.authHeaders(),
4317
4514
  retry: 0,
4318
4515
  timeout: DEFAULT_TIMEOUT_MS
@@ -4330,7 +4527,7 @@ var InstanceClient = class {
4330
4527
  async serverAction(id, payload) {
4331
4528
  const url = `${this.computeEndpoint}/servers/${encodeURIComponent(id)}/action`;
4332
4529
  try {
4333
- await import_ky8.default.post(url, {
4530
+ await import_ky9.default.post(url, {
4334
4531
  headers: this.authHeaders(),
4335
4532
  json: payload,
4336
4533
  retry: 0,
@@ -4375,7 +4572,7 @@ var InstanceClient = class {
4375
4572
  if (params.minDisk !== void 0) searchParams["minDisk"] = params.minDisk;
4376
4573
  if (params.minRam !== void 0) searchParams["minRam"] = params.minRam;
4377
4574
  try {
4378
- const raw = await import_ky8.default.get(url, {
4575
+ const raw = await import_ky9.default.get(url, {
4379
4576
  headers: this.authHeaders(),
4380
4577
  searchParams,
4381
4578
  retry: 0,
@@ -4408,7 +4605,7 @@ var InstanceClient = class {
4408
4605
  async listAvailabilityZones() {
4409
4606
  const url = `${this.computeEndpoint}/os-availability-zone`;
4410
4607
  try {
4411
- const raw = await import_ky8.default.get(url, {
4608
+ const raw = await import_ky9.default.get(url, {
4412
4609
  headers: this.authHeaders(),
4413
4610
  retry: 0,
4414
4611
  timeout: DEFAULT_TIMEOUT_MS
@@ -4428,7 +4625,7 @@ var InstanceClient = class {
4428
4625
  async listKeypairs() {
4429
4626
  const url = `${this.computeEndpoint}/os-keypairs`;
4430
4627
  try {
4431
- const raw = await import_ky8.default.get(url, { headers: this.authHeaders(), retry: 0, timeout: DEFAULT_TIMEOUT_MS }).json();
4628
+ const raw = await import_ky9.default.get(url, { headers: this.authHeaders(), retry: 0, timeout: DEFAULT_TIMEOUT_MS }).json();
4432
4629
  if (!isKeypairsResponse(raw)) {
4433
4630
  throw new NhnCloudCliError(
4434
4631
  "instance keypairs \uC751\uB2F5 \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2014 keypairs \uBC30\uC5F4\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
@@ -4444,7 +4641,7 @@ var InstanceClient = class {
4444
4641
  async getKeypair(name) {
4445
4642
  const url = `${this.computeEndpoint}/os-keypairs/${encodeURIComponent(name)}`;
4446
4643
  try {
4447
- const raw = await import_ky8.default.get(url, { headers: this.authHeaders(), retry: 0, timeout: DEFAULT_TIMEOUT_MS }).json();
4644
+ const raw = await import_ky9.default.get(url, { headers: this.authHeaders(), retry: 0, timeout: DEFAULT_TIMEOUT_MS }).json();
4448
4645
  if (!isKeypairDetailResponse(raw)) {
4449
4646
  throw new NhnCloudCliError(
4450
4647
  `instance keypair get(${name}) \uC751\uB2F5 \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2014 keypair \uC0C1\uC138 \uD544\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.`,
@@ -4469,7 +4666,7 @@ var InstanceClient = class {
4469
4666
  }
4470
4667
  let raw;
4471
4668
  try {
4472
- raw = await import_ky8.default.post(url, {
4669
+ raw = await import_ky9.default.post(url, {
4473
4670
  headers: this.authHeaders(),
4474
4671
  json: { keypair: keypairBody },
4475
4672
  retry: 0,
@@ -4498,7 +4695,7 @@ var InstanceClient = class {
4498
4695
  async deleteKeypair(name) {
4499
4696
  const url = `${this.computeEndpoint}/os-keypairs/${encodeURIComponent(name)}`;
4500
4697
  try {
4501
- await import_ky8.default.delete(url, { headers: this.authHeaders(), retry: 0, timeout: DEFAULT_TIMEOUT_MS });
4698
+ await import_ky9.default.delete(url, { headers: this.authHeaders(), retry: 0, timeout: DEFAULT_TIMEOUT_MS });
4502
4699
  } catch (err) {
4503
4700
  throw toNhnCloudCliError(err);
4504
4701
  }
@@ -4518,7 +4715,7 @@ var InstanceClient = class {
4518
4715
  if (params.owner !== void 0) searchParams["owner"] = params.owner;
4519
4716
  if (params.status !== void 0) searchParams["status"] = params.status;
4520
4717
  try {
4521
- const raw = await import_ky8.default.get(url, {
4718
+ const raw = await import_ky9.default.get(url, {
4522
4719
  headers: this.authHeaders(),
4523
4720
  searchParams,
4524
4721
  retry: 0,
@@ -4542,7 +4739,7 @@ var InstanceClient = class {
4542
4739
  async listVolumeAttachments(serverId) {
4543
4740
  const url = `${this.computeEndpoint}/servers/${encodeURIComponent(serverId)}/os-volume_attachments`;
4544
4741
  try {
4545
- const raw = await import_ky8.default.get(url, { headers: this.authHeaders(), retry: 0, timeout: DEFAULT_TIMEOUT_MS }).json();
4742
+ const raw = await import_ky9.default.get(url, { headers: this.authHeaders(), retry: 0, timeout: DEFAULT_TIMEOUT_MS }).json();
4546
4743
  if (!isVolumeAttachmentsResponse(raw)) {
4547
4744
  throw new NhnCloudCliError(
4548
4745
  "instance volumes \uC751\uB2F5 \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2014 volumeAttachments \uBC30\uC5F4\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
@@ -4561,7 +4758,7 @@ var InstanceClient = class {
4561
4758
  async attachVolume(serverId, volumeId) {
4562
4759
  const url = `${this.computeEndpoint}/servers/${encodeURIComponent(serverId)}/os-volume_attachments`;
4563
4760
  try {
4564
- const res = await import_ky8.default.post(url, {
4761
+ const res = await import_ky9.default.post(url, {
4565
4762
  headers: this.authHeaders(),
4566
4763
  json: { volumeAttachment: { volumeId } },
4567
4764
  retry: 0,
@@ -4589,7 +4786,7 @@ var InstanceClient = class {
4589
4786
  async detachVolume(serverId, volumeId) {
4590
4787
  const url = `${this.computeEndpoint}/servers/${encodeURIComponent(serverId)}/os-volume_attachments/${encodeURIComponent(volumeId)}`;
4591
4788
  try {
4592
- await import_ky8.default.delete(url, { headers: this.authHeaders(), retry: 0, timeout: DEFAULT_TIMEOUT_MS });
4789
+ await import_ky9.default.delete(url, { headers: this.authHeaders(), retry: 0, timeout: DEFAULT_TIMEOUT_MS });
4593
4790
  } catch (err) {
4594
4791
  throw toNhnCloudCliError(err);
4595
4792
  }
@@ -4656,7 +4853,7 @@ var listCommand = new import_commander15.Command("list").description("\uC778\uC2
4656
4853
  var import_commander16 = require("commander");
4657
4854
 
4658
4855
  // src/services/blockstorage/client.ts
4659
- var import_ky9 = __toESM(require("ky"));
4856
+ var import_ky10 = __toESM(require("ky"));
4660
4857
  function isVolume(val) {
4661
4858
  if (typeof val !== "object" || val === null) return false;
4662
4859
  const obj = val;
@@ -4687,7 +4884,7 @@ var BlockStorageClient = class {
4687
4884
  if (params?.offset !== void 0) searchParams["offset"] = params.offset;
4688
4885
  if (params?.marker !== void 0) searchParams["marker"] = params.marker;
4689
4886
  try {
4690
- const raw = await import_ky9.default.get(url, { headers: this.authHeaders(), searchParams, retry: 0, timeout: DEFAULT_TIMEOUT_MS }).json();
4887
+ const raw = await import_ky10.default.get(url, { headers: this.authHeaders(), searchParams, retry: 0, timeout: DEFAULT_TIMEOUT_MS }).json();
4691
4888
  if (typeof raw !== "object" || raw === null) {
4692
4889
  throw new NhnCloudCliError(
4693
4890
  "volume list \uC751\uB2F5 \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2014 volumes \uBC30\uC5F4\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
@@ -4715,7 +4912,7 @@ var BlockStorageClient = class {
4715
4912
  async get(id) {
4716
4913
  const url = `${this.endpoint}/volumes/${encodeURIComponent(id)}`;
4717
4914
  try {
4718
- const raw = await import_ky9.default.get(url, { headers: this.authHeaders(), retry: 0, timeout: DEFAULT_TIMEOUT_MS }).json();
4915
+ const raw = await import_ky10.default.get(url, { headers: this.authHeaders(), retry: 0, timeout: DEFAULT_TIMEOUT_MS }).json();
4719
4916
  if (!isVolumeResponse(raw)) {
4720
4917
  throw new NhnCloudCliError(
4721
4918
  "volume get \uC751\uB2F5 \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2014 volume \uAC1D\uCCB4\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
@@ -4736,7 +4933,7 @@ var BlockStorageClient = class {
4736
4933
  if (params.availability_zone !== void 0) volumeBody["availability_zone"] = params.availability_zone;
4737
4934
  if (params.snapshot_id !== void 0) volumeBody["snapshot_id"] = params.snapshot_id;
4738
4935
  try {
4739
- const raw = await import_ky9.default.post(url, {
4936
+ const raw = await import_ky10.default.post(url, {
4740
4937
  headers: this.authHeaders(),
4741
4938
  json: { volume: volumeBody },
4742
4939
  retry: 0,
@@ -4895,7 +5092,7 @@ var createCommand = new import_commander18.Command("create").description("\uBCFC
4895
5092
  var import_commander19 = require("commander");
4896
5093
 
4897
5094
  // src/services/network/client.ts
4898
- var import_ky10 = __toESM(require("ky"));
5095
+ var import_ky11 = __toESM(require("ky"));
4899
5096
  function isVpc(val) {
4900
5097
  if (typeof val !== "object" || val === null) return false;
4901
5098
  const obj = val;
@@ -4948,7 +5145,7 @@ var NetworkClient = class {
4948
5145
  async listVpcs() {
4949
5146
  const url = `${this.networkEndpoint}/vpcs`;
4950
5147
  try {
4951
- const raw = await import_ky10.default.get(url, {
5148
+ const raw = await import_ky11.default.get(url, {
4952
5149
  headers: this.authHeaders(),
4953
5150
  retry: 0,
4954
5151
  timeout: DEFAULT_TIMEOUT_MS
@@ -4970,7 +5167,7 @@ var NetworkClient = class {
4970
5167
  async listSubnets() {
4971
5168
  const url = `${this.networkEndpoint}/vpcsubnets`;
4972
5169
  try {
4973
- const raw = await import_ky10.default.get(url, {
5170
+ const raw = await import_ky11.default.get(url, {
4974
5171
  headers: this.authHeaders(),
4975
5172
  retry: 0,
4976
5173
  timeout: DEFAULT_TIMEOUT_MS
@@ -4990,7 +5187,7 @@ var NetworkClient = class {
4990
5187
  async listFloatingIps() {
4991
5188
  const url = `${this.networkEndpoint}/floatingips`;
4992
5189
  try {
4993
- const raw = await import_ky10.default.get(url, { headers: this.authHeaders(), retry: 0, timeout: DEFAULT_TIMEOUT_MS }).json();
5190
+ const raw = await import_ky11.default.get(url, { headers: this.authHeaders(), retry: 0, timeout: DEFAULT_TIMEOUT_MS }).json();
4994
5191
  if (!isFloatingIpsResponse(raw)) {
4995
5192
  throw new NhnCloudCliError(
4996
5193
  "floatingip list \uC751\uB2F5 \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2014 floatingips \uBC30\uC5F4\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.",
@@ -5006,7 +5203,7 @@ var NetworkClient = class {
5006
5203
  async createFloatingIp(params) {
5007
5204
  const url = `${this.networkEndpoint}/floatingips`;
5008
5205
  try {
5009
- const raw = await import_ky10.default.post(url, {
5206
+ const raw = await import_ky11.default.post(url, {
5010
5207
  headers: this.authHeaders(),
5011
5208
  json: { floatingip: { floating_network_id: params.floating_network_id } },
5012
5209
  retry: 0,
@@ -5027,7 +5224,7 @@ var NetworkClient = class {
5027
5224
  async deleteFloatingIp(id) {
5028
5225
  const url = `${this.networkEndpoint}/floatingips/${encodeURIComponent(id)}`;
5029
5226
  try {
5030
- await import_ky10.default.delete(url, { headers: this.authHeaders(), retry: 0, timeout: DEFAULT_TIMEOUT_MS });
5227
+ await import_ky11.default.delete(url, { headers: this.authHeaders(), retry: 0, timeout: DEFAULT_TIMEOUT_MS });
5031
5228
  } catch (err) {
5032
5229
  throw toNhnCloudCliError(err);
5033
5230
  }
@@ -5041,7 +5238,7 @@ var NetworkClient = class {
5041
5238
  async findExternalNetworkId() {
5042
5239
  const url = `${this.networkEndpoint}/vpcs`;
5043
5240
  try {
5044
- const raw = await import_ky10.default.get(url, {
5241
+ const raw = await import_ky11.default.get(url, {
5045
5242
  headers: this.authHeaders(),
5046
5243
  searchParams: { "router:external": "true" },
5047
5244
  retry: 0,
@@ -5360,9 +5557,9 @@ var createCommand3 = new import_commander27.Command("create").description("\uC77
5360
5557
  }
5361
5558
  let userDataBase64;
5362
5559
  if (opts.userData !== void 0) {
5363
- let stat2;
5560
+ let stat3;
5364
5561
  try {
5365
- stat2 = (0, import_node_fs7.statSync)(opts.userData);
5562
+ stat3 = (0, import_node_fs7.statSync)(opts.userData);
5366
5563
  } catch (e) {
5367
5564
  const reason = e.code ?? (e instanceof Error ? e.message : String(e));
5368
5565
  throw new NhnCloudCliError(
@@ -5370,15 +5567,15 @@ var createCommand3 = new import_commander27.Command("create").description("\uC77
5370
5567
  EXIT_PARAM_ERROR
5371
5568
  );
5372
5569
  }
5373
- if (!stat2.isFile()) {
5570
+ if (!stat3.isFile()) {
5374
5571
  throw new NhnCloudCliError(
5375
5572
  `--user-data \uAC00 \uC77C\uBC18 \uD30C\uC77C\uC774 \uC544\uB2D9\uB2C8\uB2E4: ${opts.userData}`,
5376
5573
  EXIT_PARAM_ERROR
5377
5574
  );
5378
5575
  }
5379
- if (stat2.size > 49149) {
5576
+ if (stat3.size > 49149) {
5380
5577
  throw new NhnCloudCliError(
5381
- `--user-data \uAC00 \uB108\uBB34 \uD07D\uB2C8\uB2E4 (${stat2.size} \uBC14\uC774\uD2B8). base64 \uC778\uCF54\uB529 \uD6C4 65535 \uBC14\uC774\uD2B8 \uD55C\uB3C4\uB77C raw 49149 \uBC14\uC774\uD2B8\uAE4C\uC9C0\uB9CC \uD5C8\uC6A9\uB429\uB2C8\uB2E4.`,
5578
+ `--user-data \uAC00 \uB108\uBB34 \uD07D\uB2C8\uB2E4 (${stat3.size} \uBC14\uC774\uD2B8). base64 \uC778\uCF54\uB529 \uD6C4 65535 \uBC14\uC774\uD2B8 \uD55C\uB3C4\uB77C raw 49149 \uBC14\uC774\uD2B8\uAE4C\uC9C0\uB9CC \uD5C8\uC6A9\uB429\uB2C8\uB2E4.`,
5382
5579
  EXIT_PARAM_ERROR
5383
5580
  );
5384
5581
  }
@@ -5663,9 +5860,9 @@ var import_node_fs8 = require("fs");
5663
5860
  var import_node_crypto5 = require("crypto");
5664
5861
  var import_chalk11 = __toESM(require("chalk"));
5665
5862
  function resolvePublicKey(value) {
5666
- let stat2;
5863
+ let stat3;
5667
5864
  try {
5668
- stat2 = (0, import_node_fs8.statSync)(value);
5865
+ stat3 = (0, import_node_fs8.statSync)(value);
5669
5866
  } catch (e) {
5670
5867
  const err = e;
5671
5868
  if (err.code && err.code !== "ENOENT") {
@@ -5676,7 +5873,7 @@ function resolvePublicKey(value) {
5676
5873
  }
5677
5874
  return value;
5678
5875
  }
5679
- if (!stat2.isFile()) {
5876
+ if (!stat3.isFile()) {
5680
5877
  throw new NhnCloudCliError(`--public-key \uAC00 \uC77C\uBC18 \uD30C\uC77C\uC774 \uC544\uB2D9\uB2C8\uB2E4: ${value}`, EXIT_PARAM_ERROR);
5681
5878
  }
5682
5879
  return (0, import_node_fs8.readFileSync)(value, "utf-8").trim();
@@ -5880,7 +6077,7 @@ var volumesCommand = new import_commander35.Command("volumes").description("\uC7
5880
6077
  var import_commander36 = require("commander");
5881
6078
 
5882
6079
  // src/services/ncr/harbor-client.ts
5883
- var import_ky11 = __toESM(require("ky"));
6080
+ var import_ky12 = __toESM(require("ky"));
5884
6081
  var PAGE_SIZE = 100;
5885
6082
  var MAX_PAGES = 1e3;
5886
6083
  var HarborClient = class {
@@ -5909,7 +6106,7 @@ var HarborClient = class {
5909
6106
  try {
5910
6107
  for (; ; ) {
5911
6108
  const url = `https://${this.host}${path5}?page=${page}&page_size=${PAGE_SIZE}`;
5912
- const res = await import_ky11.default.get(url, {
6109
+ const res = await import_ky12.default.get(url, {
5913
6110
  headers: this.basicAuthHeaders(),
5914
6111
  retry: 0,
5915
6112
  timeout: DEFAULT_TIMEOUT_MS
@@ -6208,7 +6405,7 @@ var import_commander41 = require("commander");
6208
6405
  var import_promises6 = require("fs/promises");
6209
6406
 
6210
6407
  // src/services/nks/client.ts
6211
- var import_ky12 = __toESM(require("ky"));
6408
+ var import_ky13 = __toESM(require("ky"));
6212
6409
 
6213
6410
  // src/services/nks/types.ts
6214
6411
  function isNksClusterSummary(val) {
@@ -6266,6 +6463,11 @@ function isBooleanLike(val) {
6266
6463
  function isNksUuidResponse(val) {
6267
6464
  return isPlainObject(val) && typeof val["uuid"] === "string";
6268
6465
  }
6466
+ function isNksClusterEvent(val) {
6467
+ if (!isPlainObject(val)) return false;
6468
+ if ("header" in val || "body" in val) return false;
6469
+ return typeof val["id"] === "number" && typeof val["uuid"] === "string";
6470
+ }
6269
6471
 
6270
6472
  // src/services/nks/client.ts
6271
6473
  function isArrayFieldResponse(key, val, itemGuard) {
@@ -6276,8 +6478,8 @@ function isArrayFieldResponse(key, val, itemGuard) {
6276
6478
  function isClustersResponse(val) {
6277
6479
  return isArrayFieldResponse("clusters", val, isNksClusterSummary);
6278
6480
  }
6279
- function isNamedResourceArrayResponse(key, val) {
6280
- return isArrayFieldResponse(key, val, isNksNamedResource);
6481
+ function isClusterEventsResponse(val) {
6482
+ return isArrayFieldResponse("events", val, isNksClusterEvent);
6281
6483
  }
6282
6484
  function isConfigResponse(val) {
6283
6485
  if (typeof val !== "object" || val === null) return false;
@@ -6310,7 +6512,7 @@ var NksClient = class {
6310
6512
  async supports() {
6311
6513
  const url = `${this.nksEndpoint}/supports`;
6312
6514
  try {
6313
- const raw = await import_ky12.default.get(url, {
6515
+ const raw = await import_ky13.default.get(url, {
6314
6516
  headers: this.authHeaders(),
6315
6517
  retry: 0,
6316
6518
  timeout: DEFAULT_TIMEOUT_MS
@@ -6330,7 +6532,7 @@ var NksClient = class {
6330
6532
  async listClusters() {
6331
6533
  const url = `${this.nksEndpoint}/clusters`;
6332
6534
  try {
6333
- const raw = await import_ky12.default.get(url, {
6535
+ const raw = await import_ky13.default.get(url, {
6334
6536
  headers: this.authHeaders(),
6335
6537
  retry: 0,
6336
6538
  timeout: DEFAULT_TIMEOUT_MS
@@ -6405,18 +6607,29 @@ var NksClient = class {
6405
6607
  "nks cluster set-control-plane-log \uC751\uB2F5 \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2014 uuid \uD544\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4."
6406
6608
  );
6407
6609
  }
6408
- async listClusterEvents(cluster) {
6409
- return this.getNamedResourceArray(
6410
- "events",
6411
- `/clusters/${encodeURIComponent(cluster)}/events`,
6412
- "nks cluster events \uC751\uB2F5 \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2014 events \uBC30\uC5F4\uC774 \uC5C6\uC2B5\uB2C8\uB2E4."
6413
- );
6610
+ /** 이벤트 엔드포인트는 클러스터 이름을 받지 않는다. 반드시 UUID 를 넘긴다. */
6611
+ async listClusterEvents(clusterUuid) {
6612
+ const raw = await this.getJson(`/clusters/${encodeURIComponent(clusterUuid)}/events`);
6613
+ if (!isClusterEventsResponse(raw)) {
6614
+ throw new NhnCloudCliError(
6615
+ "nks cluster events \uC751\uB2F5 \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2014 events \uBC30\uC5F4\uC774 \uC5C6\uAC70\uB098 \uD56D\uBAA9\uC5D0 id / uuid \uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
6616
+ EXIT_API_ERROR
6617
+ );
6618
+ }
6619
+ return raw.events;
6414
6620
  }
6415
- async getClusterEvent(cluster, event) {
6416
- return this.getFlatNamedResource(
6417
- `/clusters/${encodeURIComponent(cluster)}/events/${encodeURIComponent(event)}`,
6418
- "nks cluster event \uC751\uB2F5 \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2014 event \uAC1D\uCCB4\uAC00 \uC544\uB2D9\uB2C8\uB2E4."
6621
+ /** event 는 정수 `id` 가 아니라 `uuid` 로 조회한다. `id` 를 넘기면 서버가 400 을 준다. */
6622
+ async getClusterEvent(clusterUuid, eventUuid) {
6623
+ const raw = await this.getJson(
6624
+ `/clusters/${encodeURIComponent(clusterUuid)}/events/${encodeURIComponent(eventUuid)}`
6419
6625
  );
6626
+ if (!isNksClusterEvent(raw)) {
6627
+ throw new NhnCloudCliError(
6628
+ "nks cluster event \uC751\uB2F5 \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2014 id / uuid \uAC00 \uC788\uB294 event \uAC1D\uCCB4\uAC00 \uC544\uB2D9\uB2C8\uB2E4.",
6629
+ EXIT_API_ERROR
6630
+ );
6631
+ }
6632
+ return raw;
6420
6633
  }
6421
6634
  async getClusterKubeconfig(cluster) {
6422
6635
  const raw = await this.getJson(`/clusters/${encodeURIComponent(cluster)}/config`);
@@ -6627,7 +6840,7 @@ var NksClient = class {
6627
6840
  async removeClusterAddon(cluster, addon) {
6628
6841
  const url = `${this.nksEndpoint}/clusters/${encodeURIComponent(cluster)}/addons/${encodeURIComponent(addon)}`;
6629
6842
  try {
6630
- const raw = await import_ky12.default.delete(url, { headers: this.authHeaders(), retry: 0, timeout: DEFAULT_TIMEOUT_MS }).json();
6843
+ const raw = await import_ky13.default.delete(url, { headers: this.authHeaders(), retry: 0, timeout: DEFAULT_TIMEOUT_MS }).json();
6631
6844
  if (!isNksUuidResponse(raw)) {
6632
6845
  throw new NhnCloudCliError(
6633
6846
  "nks cluster addon remove \uC751\uB2F5 \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 \u2014 uuid \uD544\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.",
@@ -6639,13 +6852,6 @@ var NksClient = class {
6639
6852
  throw toNhnCloudCliError(err);
6640
6853
  }
6641
6854
  }
6642
- async getNamedResourceArray(key, path5, errorMessage) {
6643
- const raw = await this.getJson(path5);
6644
- if (!isNamedResourceArrayResponse(key, raw)) {
6645
- throw new NhnCloudCliError(errorMessage, EXIT_API_ERROR);
6646
- }
6647
- return raw[key];
6648
- }
6649
6855
  async getFlatNamedResource(path5, errorMessage) {
6650
6856
  const raw = await this.getJson(path5);
6651
6857
  if (!isNksNamedResource(raw)) {
@@ -6656,7 +6862,7 @@ var NksClient = class {
6656
6862
  async getJson(path5, searchParams) {
6657
6863
  const url = `${this.nksEndpoint}${path5}`;
6658
6864
  try {
6659
- return await import_ky12.default.get(url, {
6865
+ return await import_ky13.default.get(url, {
6660
6866
  headers: this.authHeaders(),
6661
6867
  searchParams,
6662
6868
  retry: 0,
@@ -6677,10 +6883,10 @@ var NksClient = class {
6677
6883
  const url = `${this.nksEndpoint}${path5}`;
6678
6884
  try {
6679
6885
  if (method === "delete") {
6680
- await import_ky12.default.delete(url, { headers: this.authHeaders(), retry: 0, timeout: DEFAULT_TIMEOUT_MS });
6886
+ await import_ky13.default.delete(url, { headers: this.authHeaders(), retry: 0, timeout: DEFAULT_TIMEOUT_MS });
6681
6887
  return;
6682
6888
  }
6683
- await import_ky12.default.post(url, { headers: this.authHeaders(), json: payload, retry: 0, timeout: DEFAULT_TIMEOUT_MS });
6889
+ await import_ky13.default.post(url, { headers: this.authHeaders(), json: payload, retry: 0, timeout: DEFAULT_TIMEOUT_MS });
6684
6890
  } catch (err) {
6685
6891
  throw toNhnCloudCliError(err);
6686
6892
  }
@@ -6689,7 +6895,7 @@ var NksClient = class {
6689
6895
  const url = `${this.nksEndpoint}${path5}`;
6690
6896
  try {
6691
6897
  const options = { headers: this.authHeaders(), json: payload, retry: 0, timeout: DEFAULT_TIMEOUT_MS };
6692
- return method === "post" ? await import_ky12.default.post(url, options).json() : await import_ky12.default.patch(url, options).json();
6898
+ return method === "post" ? await import_ky13.default.post(url, options).json() : await import_ky13.default.patch(url, options).json();
6693
6899
  } catch (err) {
6694
6900
  throw toNhnCloudCliError(err);
6695
6901
  }
@@ -6709,6 +6915,22 @@ async function resolveNksClient(opts) {
6709
6915
  const { profileName, tokenId, nksEndpoint } = await resolveIaasTokenContext(opts);
6710
6916
  return { client: new NksClient(tokenId, nksEndpoint), profileName };
6711
6917
  }
6918
+ var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
6919
+ function isUuid(value) {
6920
+ return UUID_PATTERN.test(value);
6921
+ }
6922
+ async function resolveClusterUuid(client, cluster) {
6923
+ if (isUuid(cluster)) return cluster;
6924
+ const detail = await client.getCluster(cluster);
6925
+ const uuid = detail.uuid;
6926
+ if (typeof uuid !== "string" || uuid === "") {
6927
+ throw new NhnCloudCliError(
6928
+ `\uD074\uB7EC\uC2A4\uD130 "${cluster}" \uC758 UUID \uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \uC751\uB2F5\uC5D0 uuid \uD544\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.`,
6929
+ EXIT_API_ERROR
6930
+ );
6931
+ }
6932
+ return uuid;
6933
+ }
6712
6934
  async function readJsonFile(path5) {
6713
6935
  let raw;
6714
6936
  try {
@@ -6813,6 +7035,16 @@ function resourceRow(resource) {
6813
7035
  resource.status ?? ""
6814
7036
  ];
6815
7037
  }
7038
+ function eventRow(event) {
7039
+ return [
7040
+ event.uuid,
7041
+ String(event.id),
7042
+ event.type ?? "",
7043
+ event.state ?? "",
7044
+ event.resource_name ?? "",
7045
+ event.created_at ?? ""
7046
+ ];
7047
+ }
6816
7048
  function addonRow(addon) {
6817
7049
  return [
6818
7050
  addon.uuid ?? addon.id ?? addon.name,
@@ -7015,36 +7247,48 @@ var eventsCommand = new import_commander42.Command("events").description("NKS \u
7015
7247
  startSpinner("NKS \uD074\uB7EC\uC2A4\uD130 \uC791\uC5C5 \uC774\uB825 \uC870\uD68C \uC911...");
7016
7248
  let events;
7017
7249
  try {
7018
- events = await client.listClusterEvents(cluster);
7250
+ const clusterUuid = await resolveClusterUuid(client, cluster);
7251
+ events = await client.listClusterEvents(clusterUuid);
7019
7252
  } catch (err) {
7020
7253
  stopSpinner(false);
7021
7254
  throw err;
7022
7255
  }
7023
7256
  stopSpinner(true);
7024
7257
  output(opts, {
7025
- headers: ["id", "name", "status"],
7026
- rows: events.map(resourceRow),
7258
+ headers: ["uuid", "id", "type", "state", "resource_name", "created_at"],
7259
+ rows: events.map(eventRow),
7027
7260
  raw: events,
7028
- ids: events.map(resourceId)
7261
+ // 단건 조회가 uuid 로만 되므로 --quiet 식별자도 uuid 를 쓴다.
7262
+ ids: events.map((event) => event.uuid)
7029
7263
  });
7030
7264
  });
7031
- var eventCommand = new import_commander42.Command("event").description("NKS \uD074\uB7EC\uC2A4\uD130 \uC791\uC5C5 \uC774\uB825 \uB2E8\uAC74\uC744 \uC870\uD68C\uD55C\uB2E4").argument("<cluster>", "\uD074\uB7EC\uC2A4\uD130 UUID \uB610\uB294 \uC774\uB984").argument("<event>", "\uC791\uC5C5 \uC774\uB825 UUID \uB610\uB294 ID").option("--region <region>", "region override (\uAE30\uBCF8: iaas \uC790\uACA9\uC99D\uBA85\uC758 region)").option("--profile <name>", "\uC0AC\uC6A9\uD560 profile \uC774\uB984").action(async (cluster, event, _opts, cmd) => {
7265
+ var eventCommand = new import_commander42.Command("event").description("NKS \uD074\uB7EC\uC2A4\uD130 \uC791\uC5C5 \uC774\uB825 \uB2E8\uAC74\uC744 \uC870\uD68C\uD55C\uB2E4").argument("<cluster>", "\uD074\uB7EC\uC2A4\uD130 UUID \uB610\uB294 \uC774\uB984").argument("<event>", "\uC791\uC5C5 \uC774\uB825 UUID (\uC815\uC218 id \uAC00 \uC544\uB2C8\uB2E4)").option("--region <region>", "region override (\uAE30\uBCF8: iaas \uC790\uACA9\uC99D\uBA85\uC758 region)").option("--profile <name>", "\uC0AC\uC6A9\uD560 profile \uC774\uB984").action(async (cluster, event, _opts, cmd) => {
7032
7266
  const opts = cmd.optsWithGlobals();
7267
+ if (!isUuid(event)) {
7268
+ throw new NhnCloudCliError(
7269
+ `\uC791\uC5C5 \uC774\uB825\uC740 UUID \uB85C \uC870\uD68C\uD55C\uB2E4. events \uCD9C\uB825\uC758 uuid \uC5F4 \uAC12\uC744 \uB118\uAE34\uB2E4 (\uC785\uB825: ${JSON.stringify(event)}).`,
7270
+ EXIT_PARAM_ERROR
7271
+ );
7272
+ }
7033
7273
  const { client } = await resolveNksClient(opts);
7034
7274
  startSpinner("NKS \uD074\uB7EC\uC2A4\uD130 \uC791\uC5C5 \uC774\uB825 \uC870\uD68C \uC911...");
7035
7275
  let result;
7036
7276
  try {
7037
- result = await client.getClusterEvent(cluster, event);
7277
+ const clusterUuid = await resolveClusterUuid(client, cluster);
7278
+ result = await client.getClusterEvent(clusterUuid, event);
7038
7279
  } catch (err) {
7039
7280
  stopSpinner(false);
7040
7281
  throw err;
7041
7282
  }
7042
7283
  stopSpinner(true);
7043
7284
  output(opts, {
7044
- headers: ["id", "name", "status"],
7045
- rows: [resourceRow(result)],
7285
+ headers: ["field", "value"],
7286
+ rows: Object.entries(result).map(([key, value]) => [
7287
+ key,
7288
+ truncate(typeof value === "object" && value !== null ? JSON.stringify(value) : String(value), 80)
7289
+ ]),
7046
7290
  raw: result,
7047
- ids: [resourceId(result)]
7291
+ ids: [result.uuid]
7048
7292
  });
7049
7293
  });
7050
7294
  var kubeconfigCommand = new import_commander42.Command("kubeconfig").description("NKS \uD074\uB7EC\uC2A4\uD130 kubeconfig \uB97C \uCD9C\uB825\uD558\uAC70\uB098 \uC800\uC7A5\uD55C\uB2E4").argument("<cluster>", "\uD074\uB7EC\uC2A4\uD130 UUID \uB610\uB294 \uC774\uB984").option("--output <file>", "kubeconfig \uC800\uC7A5 \uACBD\uB85C").option("--force", "\uAE30\uC874 \uD30C\uC77C \uB36E\uC5B4\uC4F0\uAE30").option("--region <region>", "region override (\uAE30\uBCF8: iaas \uC790\uACA9\uC99D\uBA85\uC758 region)").option("--profile <name>", "\uC0AC\uC6A9\uD560 profile \uC774\uB984").action(async (cluster, _opts, cmd) => {
@@ -7133,7 +7377,7 @@ function parseResolveConflicts(value) {
7133
7377
  EXIT_PARAM_ERROR
7134
7378
  );
7135
7379
  }
7136
- var clusterAddonInstallCommand = new import_commander42.Command("install").description("NKS \uD074\uB7EC\uC2A4\uD130 \uC560\uB4DC\uC628\uC744 \uC124\uCE58\uD55C\uB2E4").argument("<cluster>", "\uD074\uB7EC\uC2A4\uD130 UUID \uB610\uB294 \uC774\uB984").requiredOption("--name <name>", "\uC560\uB4DC\uC628 \uC774\uB984").requiredOption("--version <version>", "\uC560\uB4DC\uC628 \uBC84\uC804").requiredOption("--resolve-conflicts <none|overwrite|preserve>", "\uCDA9\uB3CC \uD574\uACB0 \uC815\uCC45").option("--region <region>", "region override (\uAE30\uBCF8: iaas \uC790\uACA9\uC99D\uBA85\uC758 region)").option("--profile <name>", "\uC0AC\uC6A9\uD560 profile \uC774\uB984").action(async (cluster, _opts, cmd) => {
7380
+ var clusterAddonInstallCommand = new import_commander42.Command("install").description("NKS \uD074\uB7EC\uC2A4\uD130 \uC560\uB4DC\uC628\uC744 \uC124\uCE58\uD55C\uB2E4").argument("<cluster>", "\uD074\uB7EC\uC2A4\uD130 UUID \uB610\uB294 \uC774\uB984").requiredOption("--name <name>", "\uC560\uB4DC\uC628 \uC774\uB984").requiredOption("--addon-version <version>", "\uC560\uB4DC\uC628 \uBC84\uC804").requiredOption("--resolve-conflicts <none|overwrite|preserve>", "\uCDA9\uB3CC \uD574\uACB0 \uC815\uCC45").option("--region <region>", "region override (\uAE30\uBCF8: iaas \uC790\uACA9\uC99D\uBA85\uC758 region)").option("--profile <name>", "\uC0AC\uC6A9\uD560 profile \uC774\uB984").action(async (cluster, _opts, cmd) => {
7137
7381
  const opts = cmd.optsWithGlobals();
7138
7382
  const resolveConflicts = parseResolveConflicts(opts.resolveConflicts);
7139
7383
  const { client } = await resolveNksClient(opts);
@@ -7142,7 +7386,7 @@ var clusterAddonInstallCommand = new import_commander42.Command("install").descr
7142
7386
  try {
7143
7387
  result = await client.installClusterAddon(cluster, {
7144
7388
  name: opts.name,
7145
- version: opts.version,
7389
+ version: opts.addonVersion,
7146
7390
  resolveConflicts
7147
7391
  });
7148
7392
  } catch (err) {
@@ -7152,7 +7396,7 @@ var clusterAddonInstallCommand = new import_commander42.Command("install").descr
7152
7396
  stopSpinner(true);
7153
7397
  uuidOutput(opts, result, "\uD074\uB7EC\uC2A4\uD130 \uC560\uB4DC\uC628 \uC124\uCE58");
7154
7398
  });
7155
- var clusterAddonUpdateCommand = new import_commander42.Command("update").description("NKS \uD074\uB7EC\uC2A4\uD130 \uC560\uB4DC\uC628\uC744 \uC5C5\uB370\uC774\uD2B8\uD55C\uB2E4").argument("<cluster>", "\uD074\uB7EC\uC2A4\uD130 UUID \uB610\uB294 \uC774\uB984").argument("<addon>", "\uC560\uB4DC\uC628 UUID \uB610\uB294 \uC774\uB984").requiredOption("--version <version>", "\uC560\uB4DC\uC628 \uBC84\uC804").requiredOption("--resolve-conflicts <none|overwrite|preserve>", "\uCDA9\uB3CC \uD574\uACB0 \uC815\uCC45").option("--region <region>", "region override (\uAE30\uBCF8: iaas \uC790\uACA9\uC99D\uBA85\uC758 region)").option("--profile <name>", "\uC0AC\uC6A9\uD560 profile \uC774\uB984").action(async (cluster, addon, _opts, cmd) => {
7399
+ var clusterAddonUpdateCommand = new import_commander42.Command("update").description("NKS \uD074\uB7EC\uC2A4\uD130 \uC560\uB4DC\uC628\uC744 \uC5C5\uB370\uC774\uD2B8\uD55C\uB2E4").argument("<cluster>", "\uD074\uB7EC\uC2A4\uD130 UUID \uB610\uB294 \uC774\uB984").argument("<addon>", "\uC560\uB4DC\uC628 UUID \uB610\uB294 \uC774\uB984").requiredOption("--addon-version <version>", "\uC560\uB4DC\uC628 \uBC84\uC804").requiredOption("--resolve-conflicts <none|overwrite|preserve>", "\uCDA9\uB3CC \uD574\uACB0 \uC815\uCC45").option("--region <region>", "region override (\uAE30\uBCF8: iaas \uC790\uACA9\uC99D\uBA85\uC758 region)").option("--profile <name>", "\uC0AC\uC6A9\uD560 profile \uC774\uB984").action(async (cluster, addon, _opts, cmd) => {
7156
7400
  const opts = cmd.optsWithGlobals();
7157
7401
  const resolveConflicts = parseResolveConflicts(opts.resolveConflicts);
7158
7402
  const { client } = await resolveNksClient(opts);
@@ -7160,7 +7404,7 @@ var clusterAddonUpdateCommand = new import_commander42.Command("update").descrip
7160
7404
  let result;
7161
7405
  try {
7162
7406
  result = await client.updateClusterAddon(cluster, addon, {
7163
- version: opts.version,
7407
+ version: opts.addonVersion,
7164
7408
  resolveConflicts
7165
7409
  });
7166
7410
  } catch (err) {
@@ -7379,14 +7623,14 @@ var setMetricAutoscaleCommand = new import_commander43.Command("set-metric-autos
7379
7623
  stopSpinner(true);
7380
7624
  uuidOutput2(opts, result, "metric autoscale \uBCC0\uACBD");
7381
7625
  });
7382
- var upgradeCommand = new import_commander43.Command("upgrade").description("NKS \uB178\uB4DC \uADF8\uB8F9 Kubernetes version \uC744 \uC5C5\uADF8\uB808\uC774\uB4DC\uD55C\uB2E4").argument("<cluster>", "\uD074\uB7EC\uC2A4\uD130 UUID \uB610\uB294 \uC774\uB984").argument("<nodegroup>", "\uB178\uB4DC \uADF8\uB8F9 UUID \uB610\uB294 \uC774\uB984").requiredOption("--version <v>", "\uB300\uC0C1 Kubernetes version").option("--num-buffer-nodes <n>", "buffer node \uC218").option("--num-max-unavailable-nodes <n>", "\uCD5C\uB300 unavailable node \uC218").option("--region <region>", "region override (\uAE30\uBCF8: iaas \uC790\uACA9\uC99D\uBA85\uC758 region)").option("--profile <name>", "\uC0AC\uC6A9\uD560 profile \uC774\uB984").action(async (cluster, nodegroup, _opts, cmd) => {
7626
+ var upgradeCommand = new import_commander43.Command("upgrade").description("NKS \uB178\uB4DC \uADF8\uB8F9 Kubernetes version \uC744 \uC5C5\uADF8\uB808\uC774\uB4DC\uD55C\uB2E4 (default-master \uB85C control plane \uC5C5\uADF8\uB808\uC774\uB4DC)").argument("<cluster>", "\uD074\uB7EC\uC2A4\uD130 UUID \uB610\uB294 \uC774\uB984").argument("<nodegroup>", "\uB178\uB4DC \uADF8\uB8F9 UUID \uB610\uB294 \uC774\uB984. control plane \uC740 default-master \uB85C \uC9C0\uC815\uD55C\uB2E4").requiredOption("--kube-version <v>", "\uB300\uC0C1 Kubernetes version").option("--num-buffer-nodes <n>", "\uCD95\uCD9C\uB41C \uD30C\uB4DC\uB97C \uBC1B\uC744 \uC5EC\uC720 \uB178\uB4DC \uC218 (\uAE30\uBCF8 1, \uC791\uC5C5 \uD6C4 \uC790\uB3D9 \uC0AD\uC81C, \uC778\uC2A4\uD134\uC2A4 \uC694\uAE08 \uBC1C\uC0DD)").option("--num-max-unavailable-nodes <n>", "\uD55C \uBC88\uC5D0 \uC11C\uBE44\uC2A4 \uBD88\uAC00 \uC0C1\uD0DC\uB85C \uB458 \uB178\uB4DC \uC218 (\uAE30\uBCF8 1, \uD06C\uBA74 \uBE60\uB974\uACE0 \uAC00\uC6A9\uC131 \uB0AE\uC74C)").option("--region <region>", "region override (\uAE30\uBCF8: iaas \uC790\uACA9\uC99D\uBA85\uC758 region)").option("--profile <name>", "\uC0AC\uC6A9\uD560 profile \uC774\uB984").action(async (cluster, nodegroup, _opts, cmd) => {
7383
7627
  const opts = cmd.optsWithGlobals();
7384
7628
  const { client } = await resolveNksClient(opts);
7385
7629
  startSpinner("NKS \uB178\uB4DC \uADF8\uB8F9 upgrade \uC694\uCCAD \uC911...");
7386
7630
  let result;
7387
7631
  try {
7388
7632
  result = await client.upgradeNodeGroup(cluster, nodegroup, {
7389
- version: opts.version,
7633
+ version: opts.kubeVersion,
7390
7634
  numBufferNodes: optionalNonNegativeInteger(opts.numBufferNodes, "--num-buffer-nodes"),
7391
7635
  numMaxUnavailableNodes: optionalPositiveInteger(opts.numMaxUnavailableNodes, "--num-max-unavailable-nodes")
7392
7636
  });
@@ -7412,7 +7656,7 @@ var setUserscriptCommand = new import_commander43.Command("set-userscript").desc
7412
7656
  stopSpinner(true);
7413
7657
  uuidOutput2(opts, result, "user script \uBCC0\uACBD");
7414
7658
  });
7415
- var updateFlavorCommand = new import_commander43.Command("update-flavor").description("NKS \uB178\uB4DC \uADF8\uB8F9 flavor \uB97C \uBCC0\uACBD\uD55C\uB2E4").argument("<cluster>", "\uD074\uB7EC\uC2A4\uD130 UUID \uB610\uB294 \uC774\uB984").argument("<nodegroup>", "\uB178\uB4DC \uADF8\uB8F9 UUID \uB610\uB294 \uC774\uB984").requiredOption("--flavor <uuid>", "\uBCC0\uACBD\uD560 flavor UUID").option("--num-buffer-nodes <n>", "buffer node \uC218").option("--num-max-unavailable-nodes <n>", "\uCD5C\uB300 unavailable node \uC218").option("--region <region>", "region override (\uAE30\uBCF8: iaas \uC790\uACA9\uC99D\uBA85\uC758 region)").option("--profile <name>", "\uC0AC\uC6A9\uD560 profile \uC774\uB984").action(async (cluster, nodegroup, _opts, cmd) => {
7659
+ var updateFlavorCommand = new import_commander43.Command("update-flavor").description("NKS \uB178\uB4DC \uADF8\uB8F9 flavor \uB97C \uBCC0\uACBD\uD55C\uB2E4").argument("<cluster>", "\uD074\uB7EC\uC2A4\uD130 UUID \uB610\uB294 \uC774\uB984").argument("<nodegroup>", "\uB178\uB4DC \uADF8\uB8F9 UUID \uB610\uB294 \uC774\uB984").requiredOption("--flavor <uuid>", "\uBCC0\uACBD\uD560 flavor UUID").option("--num-buffer-nodes <n>", "\uCD95\uCD9C\uB41C \uD30C\uB4DC\uB97C \uBC1B\uC744 \uC5EC\uC720 \uB178\uB4DC \uC218 (\uAE30\uBCF8 1, \uC791\uC5C5 \uD6C4 \uC790\uB3D9 \uC0AD\uC81C, \uC778\uC2A4\uD134\uC2A4 \uC694\uAE08 \uBC1C\uC0DD)").option("--num-max-unavailable-nodes <n>", "\uD55C \uBC88\uC5D0 \uC11C\uBE44\uC2A4 \uBD88\uAC00 \uC0C1\uD0DC\uB85C \uB458 \uB178\uB4DC \uC218 (\uAE30\uBCF8 1, \uD06C\uBA74 \uBE60\uB974\uACE0 \uAC00\uC6A9\uC131 \uB0AE\uC74C)").option("--region <region>", "region override (\uAE30\uBCF8: iaas \uC790\uACA9\uC99D\uBA85\uC758 region)").option("--profile <name>", "\uC0AC\uC6A9\uD560 profile \uC774\uB984").action(async (cluster, nodegroup, _opts, cmd) => {
7416
7660
  const opts = cmd.optsWithGlobals();
7417
7661
  const { client } = await resolveNksClient(opts);
7418
7662
  startSpinner("NKS \uB178\uB4DC \uADF8\uB8F9 flavor \uBCC0\uACBD \uC694\uCCAD \uC911...");
@@ -7555,7 +7799,11 @@ var import_chalk15 = __toESM(require("chalk"));
7555
7799
 
7556
7800
  // src/commands/ncs/helpers.ts
7557
7801
  var import_node_fs9 = require("fs");
7558
- var MAX_JSON_PAYLOAD_BYTES = 1e6;
7802
+
7803
+ // src/utils/limits.ts
7804
+ var MAX_JSON_INPUT_BYTES = 1e6;
7805
+
7806
+ // src/commands/ncs/helpers.ts
7559
7807
  var NCS_RFC3339_PATTERN = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?(Z|([+-])(\d{2}):(\d{2}))$/;
7560
7808
  var NCS_RELATIVE_TIME_PATTERN = /^(\d+)(m|h|d)$/;
7561
7809
  var NCS_TIME_EXAMPLES = "\uC2DC\uAC04\uB300 \uD3EC\uD568 RFC3339(\uC608: 2026-05-01T00:00:00+09:00) \uB610\uB294 \uC0C1\uB300\uC2DC\uAC04(\uC608: 30m, 1h, 2d, now)";
@@ -7679,9 +7927,9 @@ async function resolveNcsClient(opts) {
7679
7927
  return { client: new NcsClient(accessToken, region, appKey), profileName };
7680
7928
  }
7681
7929
  function readJsonPayload(filePath) {
7682
- let stat2;
7930
+ let stat3;
7683
7931
  try {
7684
- stat2 = (0, import_node_fs9.statSync)(filePath);
7932
+ stat3 = (0, import_node_fs9.statSync)(filePath);
7685
7933
  } catch (err) {
7686
7934
  const reason = err.code ?? (err instanceof Error ? err.message : String(err));
7687
7935
  throw new NhnCloudCliError(
@@ -7689,12 +7937,12 @@ function readJsonPayload(filePath) {
7689
7937
  EXIT_PARAM_ERROR
7690
7938
  );
7691
7939
  }
7692
- if (!stat2.isFile()) {
7940
+ if (!stat3.isFile()) {
7693
7941
  throw new NhnCloudCliError(`--file \uC774 \uC77C\uBC18 \uD30C\uC77C\uC774 \uC544\uB2D9\uB2C8\uB2E4: ${filePath}`, EXIT_PARAM_ERROR);
7694
7942
  }
7695
- if (stat2.size > MAX_JSON_PAYLOAD_BYTES) {
7943
+ if (stat3.size > MAX_JSON_INPUT_BYTES) {
7696
7944
  throw new NhnCloudCliError(
7697
- `--file \uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4 (${stat2.size} \uBC14\uC774\uD2B8). JSON spec \uD55C\uB3C4 ${MAX_JSON_PAYLOAD_BYTES} \uBC14\uC774\uD2B8.`,
7945
+ `--file \uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4 (${stat3.size} \uBC14\uC774\uD2B8). JSON spec \uD55C\uB3C4 ${MAX_JSON_INPUT_BYTES} \uBC14\uC774\uD2B8.`,
7698
7946
  EXIT_PARAM_ERROR
7699
7947
  );
7700
7948
  }
@@ -8424,8 +8672,11 @@ var malwareCommand = new import_commander47.Command("malware").description("NCS
8424
8672
  // src/commands/apigateway/service.ts
8425
8673
  var import_commander48 = require("commander");
8426
8674
 
8675
+ // src/commands/apigateway/helpers.ts
8676
+ var import_promises8 = require("fs/promises");
8677
+
8427
8678
  // src/services/apigateway/client.ts
8428
- var import_ky13 = __toESM(require("ky"));
8679
+ var import_ky14 = __toESM(require("ky"));
8429
8680
 
8430
8681
  // src/services/apigateway/types.ts
8431
8682
  function isApiGatewayService(value) {
@@ -8438,6 +8689,19 @@ function isApiGatewayPaging(value) {
8438
8689
  const obj = value;
8439
8690
  return typeof obj["limit"] === "number" && Number.isInteger(obj["limit"]) && obj["limit"] > 0 && typeof obj["page"] === "number" && Number.isInteger(obj["page"]) && obj["page"] > 0 && typeof obj["totalCount"] === "number" && Number.isInteger(obj["totalCount"]) && obj["totalCount"] >= 0;
8440
8691
  }
8692
+ var PATH_PLUGIN_TYPES = [
8693
+ "CORS",
8694
+ "SET_REQUEST_HEADER",
8695
+ "SET_RESPONSE_HEADER",
8696
+ "ADD_REQUEST_QUERY_PARAMETER"
8697
+ ];
8698
+ var METHOD_PLUGIN_TYPES = [
8699
+ "HTTP",
8700
+ "MOCK",
8701
+ "SET_REQUEST_HEADER",
8702
+ "SET_RESPONSE_HEADER",
8703
+ "ADD_REQUEST_QUERY_PARAMETER"
8704
+ ];
8441
8705
  function isNullableString(value) {
8442
8706
  return typeof value === "string" || value === null;
8443
8707
  }
@@ -8451,6 +8715,11 @@ function isResource(value) {
8451
8715
  const obj = value;
8452
8716
  return typeof obj["resourceId"] === "string" && typeof obj["apigwServiceId"] === "string" && isNullableString(obj["parentPath"]) && typeof obj["path"] === "string" && isNullableString(obj["methodType"]) && isNullableString(obj["methodName"]) && isNullableString(obj["methodDescription"]) && typeof obj["createdAt"] === "string" && typeof obj["updatedAt"] === "string" && Array.isArray(obj["resourcePluginList"]);
8453
8717
  }
8718
+ function isUpdatedResource(value) {
8719
+ if (typeof value !== "object" || value === null) return false;
8720
+ const obj = value;
8721
+ return typeof obj["resourceId"] === "string" && typeof obj["path"] === "string" && (obj["methodType"] === void 0 || isNullableString(obj["methodType"]));
8722
+ }
8454
8723
  function isResourceParameters(value) {
8455
8724
  if (typeof value !== "object" || value === null) return false;
8456
8725
  const obj = value;
@@ -8461,6 +8730,8 @@ function isResourceResponses(value) {
8461
8730
  const obj = value;
8462
8731
  return Array.isArray(obj["responseList"]) && Array.isArray(obj["contentTypeList"]);
8463
8732
  }
8733
+ var DEPLOY_STATUS_COMPLETE = "COMPLETE";
8734
+ var DEPLOY_STATUS_DEPLOYING = "DEPLOYING";
8464
8735
  function isRecord(value) {
8465
8736
  return typeof value === "object" && value !== null && !Array.isArray(value);
8466
8737
  }
@@ -8476,6 +8747,10 @@ function isStage(value) {
8476
8747
  if (!isRecord(value)) return false;
8477
8748
  return typeof value["stageId"] === "string" && typeof value["apigwServiceId"] === "string" && typeof value["regionCode"] === "string" && isNullableString(value["stageName"]) && typeof value["stageDescription"] === "string" && typeof value["stageUrl"] === "string" && typeof value["backendEndpointUrl"] === "string" && typeof value["resourceUpdatedAt"] === "string" && typeof value["createdAt"] === "string" && typeof value["updatedAt"] === "string" && typeof value["stageCustomUrl"] === "string" && Array.isArray(value["stageCustomDomainList"]) && value["stageCustomDomainList"].every(isStageCustomDomain) && Array.isArray(value["stageAliasDomainList"]) && value["stageAliasDomainList"].every(isStageAliasDomain);
8478
8749
  }
8750
+ function isUpdatedStage(value) {
8751
+ if (!isRecord(value)) return false;
8752
+ return typeof value["stageId"] === "string" && isNullableString(value["stageName"]) && typeof value["stageUrl"] === "string" && typeof value["backendEndpointUrl"] === "string" && typeof value["updatedAt"] === "string";
8753
+ }
8479
8754
  function isStageResourcePlugin(value) {
8480
8755
  return isRecord(value);
8481
8756
  }
@@ -8483,6 +8758,10 @@ function isStageResource(value) {
8483
8758
  if (!isRecord(value)) return false;
8484
8759
  return typeof value["stageResourceId"] === "string" && typeof value["path"] === "string" && isNullableString(value["methodType"]) && isNullableString(value["methodName"]) && isNullableString(value["methodDescription"]) && isNullableString(value["customBackendEndpointUrl"]) && typeof value["updatedAt"] === "string" && Array.isArray(value["stageResourcePluginList"]) && value["stageResourcePluginList"].every(isStageResourcePlugin);
8485
8760
  }
8761
+ function isWrittenStageResource(value) {
8762
+ if (!isRecord(value)) return false;
8763
+ return typeof value["stageResourceId"] === "string" && typeof value["path"] === "string" && (value["methodType"] === void 0 || isNullableString(value["methodType"])) && (value["methodName"] === void 0 || isNullableString(value["methodName"])) && (value["stageResourcePluginList"] === void 0 || Array.isArray(value["stageResourcePluginList"]) && value["stageResourcePluginList"].every(isStageResourcePlugin));
8764
+ }
8486
8765
  function isDeployHistory(value) {
8487
8766
  if (!isRecord(value)) return false;
8488
8767
  return typeof value["deployId"] === "string" && typeof value["stageId"] === "string" && typeof value["deployedAt"] === "string" && isNullableString(value["rollbackAt"]) && typeof value["deployDescription"] === "string" && typeof value["isBase"] === "boolean";
@@ -8496,6 +8775,7 @@ function isSwaggerData(value) {
8496
8775
  }
8497
8776
 
8498
8777
  // src/services/apigateway/client.ts
8778
+ var DEFAULT_DEPLOY_POLL_INTERVAL_MS = 3e3;
8499
8779
  var ApiGatewayClient = class {
8500
8780
  accessToken;
8501
8781
  baseUrl;
@@ -8513,7 +8793,7 @@ var ApiGatewayClient = class {
8513
8793
  let page = params.page ?? 1;
8514
8794
  try {
8515
8795
  while (true) {
8516
- const response = await import_ky13.default.get(`${this.baseUrl}/services`, {
8796
+ const response = await import_ky14.default.get(`${this.baseUrl}/services`, {
8517
8797
  headers: this.authHeaders(),
8518
8798
  searchParams: { page, limit },
8519
8799
  retry: 0,
@@ -8553,7 +8833,7 @@ var ApiGatewayClient = class {
8553
8833
  /** API Gateway service 한 건을 조회한다. */
8554
8834
  async getService(apigwServiceId) {
8555
8835
  try {
8556
- const response = await import_ky13.default.get(`${this.baseUrl}/services/${encodeURIComponent(apigwServiceId)}`, {
8836
+ const response = await import_ky14.default.get(`${this.baseUrl}/services/${encodeURIComponent(apigwServiceId)}`, {
8557
8837
  headers: this.authHeaders(),
8558
8838
  retry: 0,
8559
8839
  timeout: DEFAULT_TIMEOUT_MS
@@ -8574,7 +8854,7 @@ var ApiGatewayClient = class {
8574
8854
  /** paging 없이 API Gateway resource 전체를 한 번에 조회한다. */
8575
8855
  async listResources(apigwServiceId) {
8576
8856
  try {
8577
- const response = await import_ky13.default.get(
8857
+ const response = await import_ky14.default.get(
8578
8858
  `${this.baseUrl}/services/${encodeURIComponent(apigwServiceId)}/resources`,
8579
8859
  {
8580
8860
  headers: this.authHeaders(),
@@ -8595,10 +8875,60 @@ var ApiGatewayClient = class {
8595
8875
  throw toNhnCloudCliError(err);
8596
8876
  }
8597
8877
  }
8878
+ /** API Gateway resource 경로 플러그인을 upsert한다. */
8879
+ async setPathPlugins(apigwServiceId, resourceId2, pathPluginList) {
8880
+ try {
8881
+ const response = await import_ky14.default.put(
8882
+ `${this.baseUrl}/services/${encodeURIComponent(apigwServiceId)}/resource-paths/${encodeURIComponent(resourceId2)}`,
8883
+ {
8884
+ headers: this.authHeaders(),
8885
+ json: { pathPluginList },
8886
+ retry: 0,
8887
+ timeout: DEFAULT_TIMEOUT_MS
8888
+ }
8889
+ ).json();
8890
+ unwrapHeader(response);
8891
+ if (!Array.isArray(response.resourceList) || !response.resourceList.every(isUpdatedResource)) {
8892
+ throw new NhnCloudCliError(
8893
+ "API Gateway \uC751\uB2F5 \uD615\uC2DD \uC624\uB958: resourceList \uAC00 \uC62C\uBC14\uB978 \uC218\uC815 \uC751\uB2F5 \uBC30\uC5F4\uC774 \uC544\uB2D9\uB2C8\uB2E4.",
8894
+ EXIT_API_ERROR
8895
+ );
8896
+ }
8897
+ return response.resourceList;
8898
+ } catch (error) {
8899
+ if (error instanceof NhnCloudCliError) throw error;
8900
+ throw toNhnCloudCliError(error);
8901
+ }
8902
+ }
8903
+ /** API Gateway resource 메서드 플러그인을 upsert한다. */
8904
+ async setMethodPlugins(apigwServiceId, resourceId2, body) {
8905
+ try {
8906
+ const response = await import_ky14.default.put(
8907
+ `${this.baseUrl}/services/${encodeURIComponent(apigwServiceId)}/resource-methods/${encodeURIComponent(resourceId2)}`,
8908
+ {
8909
+ headers: this.authHeaders(),
8910
+ json: body,
8911
+ retry: 0,
8912
+ timeout: DEFAULT_TIMEOUT_MS
8913
+ }
8914
+ ).json();
8915
+ unwrapHeader(response);
8916
+ if (!Array.isArray(response.resourceList) || !response.resourceList.every(isUpdatedResource)) {
8917
+ throw new NhnCloudCliError(
8918
+ "API Gateway \uC751\uB2F5 \uD615\uC2DD \uC624\uB958: resourceList \uAC00 \uC62C\uBC14\uB978 \uC218\uC815 \uC751\uB2F5 \uBC30\uC5F4\uC774 \uC544\uB2D9\uB2C8\uB2E4.",
8919
+ EXIT_API_ERROR
8920
+ );
8921
+ }
8922
+ return response.resourceList;
8923
+ } catch (error) {
8924
+ if (error instanceof NhnCloudCliError) throw error;
8925
+ throw toNhnCloudCliError(error);
8926
+ }
8927
+ }
8598
8928
  /** API Gateway resource 요청 parameter를 조회한다. */
8599
8929
  async getResourceParameters(apigwServiceId, resourceId2) {
8600
8930
  try {
8601
- const response = await import_ky13.default.get(
8931
+ const response = await import_ky14.default.get(
8602
8932
  `${this.baseUrl}/services/${encodeURIComponent(apigwServiceId)}/resources/${encodeURIComponent(resourceId2)}/parameters`,
8603
8933
  {
8604
8934
  headers: this.authHeaders(),
@@ -8628,7 +8958,7 @@ var ApiGatewayClient = class {
8628
8958
  /** API Gateway resource 응답 정의를 조회한다. */
8629
8959
  async getResourceResponses(apigwServiceId, resourceId2) {
8630
8960
  try {
8631
- const response = await import_ky13.default.get(
8961
+ const response = await import_ky14.default.get(
8632
8962
  `${this.baseUrl}/services/${encodeURIComponent(apigwServiceId)}/resources/${encodeURIComponent(resourceId2)}/responses`,
8633
8963
  {
8634
8964
  headers: this.authHeaders(),
@@ -8659,7 +8989,7 @@ var ApiGatewayClient = class {
8659
8989
  let page = 1;
8660
8990
  try {
8661
8991
  while (true) {
8662
- const response = await import_ky13.default.get(
8992
+ const response = await import_ky14.default.get(
8663
8993
  `${this.baseUrl}/services/${encodeURIComponent(apigwServiceId)}/stages`,
8664
8994
  {
8665
8995
  headers: this.authHeaders(),
@@ -8699,10 +9029,35 @@ var ApiGatewayClient = class {
8699
9029
  throw toNhnCloudCliError(err);
8700
9030
  }
8701
9031
  }
9032
+ /** API Gateway stage의 backend endpoint와 설명을 수정한다. */
9033
+ async updateStage(apigwServiceId, stageId, body) {
9034
+ try {
9035
+ const response = await import_ky14.default.put(
9036
+ `${this.baseUrl}/services/${encodeURIComponent(apigwServiceId)}/stages/${encodeURIComponent(stageId)}`,
9037
+ {
9038
+ headers: this.authHeaders(),
9039
+ json: body,
9040
+ retry: 0,
9041
+ timeout: DEFAULT_TIMEOUT_MS
9042
+ }
9043
+ ).json();
9044
+ unwrapHeader(response);
9045
+ if (!isUpdatedStage(response.stage)) {
9046
+ throw new NhnCloudCliError(
9047
+ "API Gateway \uC751\uB2F5 \uD615\uC2DD \uC624\uB958: stage \uD544\uB4DC\uAC00 \uC5C6\uAC70\uB098 \uC62C\uBC14\uB978 \uC218\uC815 \uC751\uB2F5\uC774 \uC544\uB2D9\uB2C8\uB2E4.",
9048
+ EXIT_API_ERROR
9049
+ );
9050
+ }
9051
+ return response.stage;
9052
+ } catch (error) {
9053
+ if (error instanceof NhnCloudCliError) throw error;
9054
+ throw toNhnCloudCliError(error);
9055
+ }
9056
+ }
8702
9057
  /** 사용자 정의 Swagger 객체를 내부 해석 없이 반환한다. */
8703
9058
  async getStageSwagger(apigwServiceId, stageId) {
8704
9059
  try {
8705
- const response = await import_ky13.default.get(
9060
+ const response = await import_ky14.default.get(
8706
9061
  `${this.baseUrl}/services/${encodeURIComponent(apigwServiceId)}/stages/${encodeURIComponent(stageId)}/swagger`,
8707
9062
  {
8708
9063
  headers: this.authHeaders(),
@@ -8726,7 +9081,7 @@ var ApiGatewayClient = class {
8726
9081
  /** paging 없이 배포된 API Gateway stage resource 전체를 한 번에 조회한다. */
8727
9082
  async listStageResources(apigwServiceId, stageId) {
8728
9083
  try {
8729
- const response = await import_ky13.default.get(
9084
+ const response = await import_ky14.default.get(
8730
9085
  `${this.baseUrl}/services/${encodeURIComponent(apigwServiceId)}/stages/${encodeURIComponent(stageId)}/resources`,
8731
9086
  {
8732
9087
  headers: this.authHeaders(),
@@ -8747,16 +9102,83 @@ var ApiGatewayClient = class {
8747
9102
  throw toNhnCloudCliError(err);
8748
9103
  }
8749
9104
  }
8750
- /** paging.totalCount 를 기준으로 API Gateway stage 배포 이력 전체를 수집한다. */
8751
- async listDeploys(apigwServiceId, stageId) {
8752
- const deploys = [];
8753
- const limit = 1e3;
8754
- let page = 1;
9105
+ /** API Gateway service resource를 stage에 반영한다. */
9106
+ async importStageResources(apigwServiceId, stageId) {
8755
9107
  try {
8756
- while (true) {
8757
- const response = await import_ky13.default.get(
8758
- `${this.baseUrl}/services/${encodeURIComponent(apigwServiceId)}/stages/${encodeURIComponent(stageId)}/deploys`,
8759
- {
9108
+ const response = await import_ky14.default.put(
9109
+ `${this.baseUrl}/services/${encodeURIComponent(apigwServiceId)}/stages/${encodeURIComponent(stageId)}/resources`,
9110
+ {
9111
+ headers: this.authHeaders(),
9112
+ retry: 0,
9113
+ timeout: DEFAULT_TIMEOUT_MS
9114
+ }
9115
+ ).json();
9116
+ unwrapHeader(response);
9117
+ if (!Array.isArray(response.stageResourceList) || !response.stageResourceList.every(isWrittenStageResource)) {
9118
+ throw new NhnCloudCliError(
9119
+ "API Gateway \uC751\uB2F5 \uD615\uC2DD \uC624\uB958: stageResourceList \uAC00 \uC62C\uBC14\uB978 \uBC30\uC5F4\uC774 \uC544\uB2D9\uB2C8\uB2E4.",
9120
+ EXIT_API_ERROR
9121
+ );
9122
+ }
9123
+ return response.stageResourceList;
9124
+ } catch (err) {
9125
+ if (err instanceof NhnCloudCliError) throw err;
9126
+ throw toNhnCloudCliError(err);
9127
+ }
9128
+ }
9129
+ /** API Gateway stage 배포를 요청한다. */
9130
+ async createDeploy(apigwServiceId, stageId, body) {
9131
+ const json = body.deployDescription === void 0 ? {} : { deployDescription: body.deployDescription };
9132
+ try {
9133
+ const response = await import_ky14.default.post(
9134
+ `${this.baseUrl}/services/${encodeURIComponent(apigwServiceId)}/stages/${encodeURIComponent(stageId)}/deploys`,
9135
+ {
9136
+ headers: this.authHeaders(),
9137
+ json,
9138
+ retry: 0,
9139
+ timeout: DEFAULT_TIMEOUT_MS
9140
+ }
9141
+ ).json();
9142
+ unwrapHeader(response);
9143
+ } catch (err) {
9144
+ if (err instanceof NhnCloudCliError) throw err;
9145
+ throw toNhnCloudCliError(err);
9146
+ }
9147
+ }
9148
+ /** 배포 이력의 설정으로 API Gateway stage를 롤백한다. */
9149
+ async rollbackDeploy(apigwServiceId, stageId, deployId) {
9150
+ try {
9151
+ const response = await import_ky14.default.post(
9152
+ `${this.baseUrl}/services/${encodeURIComponent(apigwServiceId)}/stages/${encodeURIComponent(stageId)}/deploys/${encodeURIComponent(deployId)}/rollback`,
9153
+ {
9154
+ headers: this.authHeaders(),
9155
+ retry: 0,
9156
+ timeout: DEFAULT_TIMEOUT_MS
9157
+ }
9158
+ ).json();
9159
+ unwrapHeader(response);
9160
+ if (!Array.isArray(response.stageResourceList) || !response.stageResourceList.every(isWrittenStageResource)) {
9161
+ throw new NhnCloudCliError(
9162
+ "API Gateway \uC751\uB2F5 \uD615\uC2DD \uC624\uB958: stageResourceList \uAC00 \uC62C\uBC14\uB978 \uBC30\uC5F4\uC774 \uC544\uB2D9\uB2C8\uB2E4.",
9163
+ EXIT_API_ERROR
9164
+ );
9165
+ }
9166
+ return response.stageResourceList;
9167
+ } catch (err) {
9168
+ if (err instanceof NhnCloudCliError) throw err;
9169
+ throw toNhnCloudCliError(err);
9170
+ }
9171
+ }
9172
+ /** paging.totalCount 를 기준으로 API Gateway stage 배포 이력 전체를 수집한다. */
9173
+ async listDeploys(apigwServiceId, stageId) {
9174
+ const deploys = [];
9175
+ const limit = 1e3;
9176
+ let page = 1;
9177
+ try {
9178
+ while (true) {
9179
+ const response = await import_ky14.default.get(
9180
+ `${this.baseUrl}/services/${encodeURIComponent(apigwServiceId)}/stages/${encodeURIComponent(stageId)}/deploys`,
9181
+ {
8760
9182
  headers: this.authHeaders(),
8761
9183
  searchParams: { page, limit },
8762
9184
  retry: 0,
@@ -8797,7 +9219,7 @@ var ApiGatewayClient = class {
8797
9219
  /** API Gateway stage 최신 배포 결과를 조회한다. */
8798
9220
  async getLatestDeploy(apigwServiceId, stageId) {
8799
9221
  try {
8800
- const response = await import_ky13.default.get(
9222
+ const response = await import_ky14.default.get(
8801
9223
  `${this.baseUrl}/services/${encodeURIComponent(apigwServiceId)}/stages/${encodeURIComponent(stageId)}/deploys/latest`,
8802
9224
  {
8803
9225
  headers: this.authHeaders(),
@@ -8818,14 +9240,118 @@ var ApiGatewayClient = class {
8818
9240
  throw toNhnCloudCliError(err);
8819
9241
  }
8820
9242
  }
9243
+ /** 직전 배포 ID와 상태를 비교해 이번 stage 배포가 종료될 때까지 대기한다. */
9244
+ async waitForDeploy(apigwServiceId, stageId, opts) {
9245
+ const intervalMs = opts.intervalMs ?? DEFAULT_DEPLOY_POLL_INTERVAL_MS;
9246
+ const deadline = Date.now() + opts.timeoutMs;
9247
+ let lastDeploy = null;
9248
+ while (Date.now() < deadline) {
9249
+ const deploy = await this.getLatestDeploy(apigwServiceId, stageId);
9250
+ lastDeploy = deploy;
9251
+ const isNewDeploy = opts.baselineDeployId === null || deploy.deployId !== opts.baselineDeployId;
9252
+ if (isNewDeploy && deploy.deployStatus !== DEPLOY_STATUS_DEPLOYING) {
9253
+ return deploy;
9254
+ }
9255
+ const remaining = deadline - Date.now();
9256
+ if (remaining <= 0) break;
9257
+ await new Promise(
9258
+ (resolve) => setTimeout(resolve, Math.min(intervalMs, remaining))
9259
+ );
9260
+ }
9261
+ const lastDeploySummary = lastDeploy ? `\uB9C8\uC9C0\uB9C9 \uC0C1\uD0DC: ${lastDeploy.deployStatus}, \uB9C8\uC9C0\uB9C9 \uBC30\uD3EC ID: ${lastDeploy.deployId}` : "\uB9C8\uC9C0\uB9C9 \uC870\uD68C \uC5C6\uC74C";
9262
+ const baselineDeployId = opts.baselineDeployId ?? "\uC5C6\uC74C";
9263
+ throw new NhnCloudCliError(
9264
+ `API Gateway stage \uBC30\uD3EC\uAC00 \uC644\uB8CC\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4 (${lastDeploySummary}, \uAE30\uC900 \uBC30\uD3EC ID: ${baselineDeployId}). \uB300\uAE30 \uC0C1\uD55C(${Math.round(opts.timeoutMs / 1e3)}\uCD08)\uC744 \uB118\uACBC\uC2B5\uB2C8\uB2E4. --timeout \uC73C\uB85C \uB298\uB9B4 \uC218 \uC788\uC2B5\uB2C8\uB2E4.`,
9265
+ EXIT_API_ERROR
9266
+ );
9267
+ }
8821
9268
  };
8822
9269
 
8823
9270
  // src/commands/apigateway/helpers.ts
8824
- function sanitizeForTerminal(value) {
8825
- return value.replace(/[\x00-\x1F\x7F]/g, "?");
9271
+ function fileErrorReason(error) {
9272
+ if (typeof error === "object" && error !== null && "code" in error) {
9273
+ const code = error.code;
9274
+ if (typeof code === "string") return code;
9275
+ }
9276
+ return error instanceof Error ? error.message : String(error);
9277
+ }
9278
+ function requireYes(yes, operation) {
9279
+ if (!yes) {
9280
+ throw new NhnCloudCliError(
9281
+ `${operation}\uC5D0\uB294 --yes \uD50C\uB798\uADF8\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.`,
9282
+ EXIT_PARAM_ERROR
9283
+ );
9284
+ }
9285
+ return true;
9286
+ }
9287
+ async function readPluginConfigFile(path5) {
9288
+ let fileStat;
9289
+ try {
9290
+ fileStat = await (0, import_promises8.stat)(path5);
9291
+ } catch (error) {
9292
+ const reason = fileErrorReason(error);
9293
+ throw new NhnCloudCliError(
9294
+ `\uD50C\uB7EC\uADF8\uC778 \uC124\uC815 \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${JSON.stringify(path5)} (${JSON.stringify(reason)}).`,
9295
+ EXIT_PARAM_ERROR
9296
+ );
9297
+ }
9298
+ if (!fileStat.isFile()) {
9299
+ const reason = fileStat.isDirectory() ? "EISDIR" : "EINVAL";
9300
+ throw new NhnCloudCliError(
9301
+ `\uD50C\uB7EC\uADF8\uC778 \uC124\uC815 \uACBD\uB85C\uAC00 \uC77C\uBC18 \uD30C\uC77C\uC774 \uC544\uB2D9\uB2C8\uB2E4: ${JSON.stringify(path5)} (${reason}).`,
9302
+ EXIT_PARAM_ERROR
9303
+ );
9304
+ }
9305
+ if (fileStat.size > MAX_JSON_INPUT_BYTES) {
9306
+ throw new NhnCloudCliError(
9307
+ `\uD50C\uB7EC\uADF8\uC778 \uC124\uC815 \uD30C\uC77C\uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4 (${fileStat.size} \uBC14\uC774\uD2B8). \uD5C8\uC6A9 \uC0C1\uD55C\uC740 ${MAX_JSON_INPUT_BYTES} \uBC14\uC774\uD2B8\uC785\uB2C8\uB2E4.`,
9308
+ EXIT_PARAM_ERROR
9309
+ );
9310
+ }
9311
+ let raw;
9312
+ try {
9313
+ raw = await (0, import_promises8.readFile)(path5, "utf-8");
9314
+ } catch (error) {
9315
+ const reason = fileErrorReason(error);
9316
+ throw new NhnCloudCliError(
9317
+ `\uD50C\uB7EC\uADF8\uC778 \uC124\uC815 \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${JSON.stringify(path5)} (${JSON.stringify(reason)}).`,
9318
+ EXIT_PARAM_ERROR
9319
+ );
9320
+ }
9321
+ try {
9322
+ return JSON.parse(raw);
9323
+ } catch (error) {
9324
+ const reason = sanitizeForTerminal(
9325
+ error instanceof Error ? error.message : String(error)
9326
+ );
9327
+ throw new NhnCloudCliError(
9328
+ `\uD50C\uB7EC\uADF8\uC778 \uC124\uC815 JSON \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4: ${JSON.stringify(path5)} (${reason}).`,
9329
+ EXIT_PARAM_ERROR
9330
+ );
9331
+ }
8826
9332
  }
8827
- async function resolveApiGatewayAppKey(profileName, appKeyOption) {
8828
- if (appKeyOption?.trim()) return appKeyOption.trim();
9333
+ function collectAffectedPaths(resources, targetPath) {
9334
+ if (targetPath === "/") return resources;
9335
+ const childPrefix = `${targetPath}/`;
9336
+ return resources.filter(
9337
+ (resource) => resource.path === targetPath || resource.path.startsWith(childPrefix)
9338
+ );
9339
+ }
9340
+ function writtenStageResourceOutput(resources) {
9341
+ return {
9342
+ headers: ["stageResourceId", "path", "methodType", "methodName", "plugins"],
9343
+ rows: resources.map((resource) => [
9344
+ sanitizeForTerminal(resource.stageResourceId),
9345
+ sanitizeForTerminal(resource.path),
9346
+ resource.methodType == null ? "-" : sanitizeForTerminal(resource.methodType),
9347
+ resource.methodName == null ? "-" : sanitizeForTerminal(resource.methodName),
9348
+ String(resource.stageResourcePluginList?.length ?? 0)
9349
+ ]),
9350
+ raw: resources,
9351
+ ids: resources.map((resource) => sanitizeForTerminal(resource.stageResourceId))
9352
+ };
9353
+ }
9354
+ async function resolveApiGatewayAppKey(profileName) {
8829
9355
  let credential;
8830
9356
  try {
8831
9357
  credential = await getServiceCredential("apigateway", profileName);
@@ -8836,7 +9362,7 @@ async function resolveApiGatewayAppKey(profileName, appKeyOption) {
8836
9362
  }
8837
9363
  if (!credential?.appkey) {
8838
9364
  throw new NhnCloudCliError(
8839
- "API Gateway appKey\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. nhncloud configure --apigateway-appkey <key>\uB85C \uC124\uC815\uD558\uAC70\uB098\n--app-key\uB85C \uC9C1\uC811 \uB118\uAE30\uC138\uC694.",
9365
+ "API Gateway appKey\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. nhncloud configure --apigateway-appkey <key>\uB85C \uC124\uC815\uD558\uC138\uC694.",
8840
9366
  EXIT_CONFIG_ERROR
8841
9367
  );
8842
9368
  }
@@ -8846,7 +9372,7 @@ async function resolveApiGatewayClient(opts) {
8846
9372
  const profileName = await resolveProfileName(opts.profile);
8847
9373
  const uak = await getUserAccessKey(profileName);
8848
9374
  const accessToken = await getAccessToken(profileName, uak.id, uak.secret);
8849
- const appKey = await resolveApiGatewayAppKey(profileName, opts.appKey);
9375
+ const appKey = await resolveApiGatewayAppKey(profileName);
8850
9376
  return {
8851
9377
  client: new ApiGatewayClient(accessToken, opts.region ?? "kr1", appKey),
8852
9378
  profileName
@@ -8854,7 +9380,7 @@ async function resolveApiGatewayClient(opts) {
8854
9380
  }
8855
9381
 
8856
9382
  // src/commands/apigateway/service.ts
8857
- var listCommand10 = new import_commander48.Command("list").description("API Gateway service \uBAA9\uB85D\uC744 \uC870\uD68C\uD55C\uB2E4 (\uC804\uCCB4 \uD544\uB4DC\uB294 --json)").option("--region <region>", "API Gateway region (\uAE30\uBCF8: kr1)", "kr1").option("--app-key <key>", "API Gateway appKey (profile \uC758 apigateway.appkey \uBCF4\uB2E4 \uC6B0\uC120)").option("--profile <name>", "\uC0AC\uC6A9\uD560 profile \uC774\uB984").action(async (_opts, command) => {
9383
+ var listCommand10 = new import_commander48.Command("list").description("API Gateway service \uBAA9\uB85D\uC744 \uC870\uD68C\uD55C\uB2E4 (\uC804\uCCB4 \uD544\uB4DC\uB294 --json)").option("--region <region>", "API Gateway region (\uAE30\uBCF8: kr1)", "kr1").option("--profile <name>", "\uC0AC\uC6A9\uD560 profile \uC774\uB984").action(async (_opts, command) => {
8858
9384
  const opts = command.optsWithGlobals();
8859
9385
  const { client } = await resolveApiGatewayClient(opts);
8860
9386
  startSpinner("API Gateway service \uBAA9\uB85D \uC870\uD68C \uC911...");
@@ -8885,7 +9411,7 @@ var listCommand10 = new import_commander48.Command("list").description("API Gate
8885
9411
  ids: services.map((service) => sanitizeForTerminal(service.apigwServiceId))
8886
9412
  });
8887
9413
  });
8888
- var getCommand8 = new import_commander48.Command("get").description("API Gateway service \uD55C \uAC74\uC744 \uC870\uD68C\uD55C\uB2E4 (\uC804\uCCB4 \uD544\uB4DC\uB294 --json)").argument("<service-id>", "API Gateway service ID").option("--region <region>", "API Gateway region (\uAE30\uBCF8: kr1)", "kr1").option("--app-key <key>", "API Gateway appKey (profile \uC758 apigateway.appkey \uBCF4\uB2E4 \uC6B0\uC120)").option("--profile <name>", "\uC0AC\uC6A9\uD560 profile \uC774\uB984").action(async (serviceId, _opts, command) => {
9414
+ var getCommand8 = new import_commander48.Command("get").description("API Gateway service \uD55C \uAC74\uC744 \uC870\uD68C\uD55C\uB2E4 (\uC804\uCCB4 \uD544\uB4DC\uB294 --json)").argument("<service-id>", "API Gateway service ID").option("--region <region>", "API Gateway region (\uAE30\uBCF8: kr1)", "kr1").option("--profile <name>", "\uC0AC\uC6A9\uD560 profile \uC774\uB984").action(async (serviceId, _opts, command) => {
8889
9415
  const opts = command.optsWithGlobals();
8890
9416
  const parsedServiceId = parseRequiredArgument(serviceId, "service-id");
8891
9417
  const displayServiceId = sanitizeForTerminal(parsedServiceId);
@@ -8923,12 +9449,85 @@ var serviceCommand = new import_commander48.Command("service").description("API
8923
9449
  // src/commands/apigateway/resource.ts
8924
9450
  var import_commander49 = require("commander");
8925
9451
  function addApiGatewayOptions(command) {
8926
- return command.option("--region <region>", "API Gateway region (\uAE30\uBCF8: kr1)", "kr1").option("--app-key <key>", "API Gateway appKey (profile \uC758 apigateway.appkey \uBCF4\uB2E4 \uC6B0\uC120)").option("--profile <name>", "\uC0AC\uC6A9\uD560 profile \uC774\uB984");
9452
+ return command.option("--region <region>", "API Gateway region (\uAE30\uBCF8: kr1)", "kr1").option("--profile <name>", "\uC0AC\uC6A9\uD560 profile \uC774\uB984");
8927
9453
  }
8928
9454
  function displayUnknown(value) {
8929
9455
  const serialized = JSON.stringify(value);
8930
9456
  return serialized ?? String(value);
8931
9457
  }
9458
+ function isRecord2(value) {
9459
+ return typeof value === "object" && value !== null && !Array.isArray(value);
9460
+ }
9461
+ function configError(message) {
9462
+ throw new NhnCloudCliError(message, EXIT_PARAM_ERROR);
9463
+ }
9464
+ function parsePluginInput(value, allowedTypes, label, rejectApplyChildPath) {
9465
+ if (!isRecord2(value)) {
9466
+ return configError(`${label} \uD56D\uBAA9\uC740 JSON \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4.`);
9467
+ }
9468
+ if (typeof value["pluginType"] !== "string" || !allowedTypes.includes(value["pluginType"])) {
9469
+ return configError(`${label}\uC758 pluginType\uC774 \uD5C8\uC6A9\uB41C \uD0C0\uC785\uC774 \uC544\uB2D9\uB2C8\uB2E4.`);
9470
+ }
9471
+ if (rejectApplyChildPath && "applyChildPath" in value) {
9472
+ return configError(`${label}\uC5D0\uB294 applyChildPath\uB97C \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`);
9473
+ }
9474
+ if ("delete" in value && typeof value["delete"] !== "boolean") {
9475
+ return configError(`${label}\uC758 delete\uB294 boolean\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4.`);
9476
+ }
9477
+ if ("applyChildPath" in value && typeof value["applyChildPath"] !== "boolean") {
9478
+ return configError(`${label}\uC758 applyChildPath\uB294 boolean\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4.`);
9479
+ }
9480
+ if (value["delete"] !== true && value["pluginConfigJson"] === void 0) {
9481
+ return configError(`${label}\uC5D0\uC11C delete\uAC00 true\uAC00 \uC544\uB2C8\uBA74 pluginConfigJson\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.`);
9482
+ }
9483
+ if (value["pluginConfigJson"] !== void 0 && !isRecord2(value["pluginConfigJson"])) {
9484
+ return configError(`${label}\uC758 pluginConfigJson\uC740 JSON \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4.`);
9485
+ }
9486
+ return {
9487
+ pluginType: value["pluginType"],
9488
+ ...isRecord2(value["pluginConfigJson"]) ? { pluginConfigJson: value["pluginConfigJson"] } : {},
9489
+ ...typeof value["delete"] === "boolean" ? { delete: value["delete"] } : {},
9490
+ ...typeof value["applyChildPath"] === "boolean" ? { applyChildPath: value["applyChildPath"] } : {}
9491
+ };
9492
+ }
9493
+ function parsePathPluginConfig(value) {
9494
+ if (!isRecord2(value) || !Array.isArray(value["pathPluginList"]) || value["pathPluginList"].length === 0) {
9495
+ return configError("\uC124\uC815 \uD30C\uC77C\uC758 pathPluginList\uB294 \uBE44\uC5B4 \uC788\uC9C0 \uC54A\uC740 \uBC30\uC5F4\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4.");
9496
+ }
9497
+ return value["pathPluginList"].map(
9498
+ (item, index) => parsePluginInput(item, PATH_PLUGIN_TYPES, `pathPluginList[${index}]`, false)
9499
+ );
9500
+ }
9501
+ function parseMethodPluginConfig(value) {
9502
+ if (!isRecord2(value) || !Array.isArray(value["methodPluginList"]) || value["methodPluginList"].length === 0) {
9503
+ return configError("\uC124\uC815 \uD30C\uC77C\uC758 methodPluginList\uB294 \uBE44\uC5B4 \uC788\uC9C0 \uC54A\uC740 \uBC30\uC5F4\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4.");
9504
+ }
9505
+ if (value["methodName"] !== void 0 && typeof value["methodName"] !== "string") {
9506
+ return configError("methodName\uC740 \uBB38\uC790\uC5F4\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4.");
9507
+ }
9508
+ if (value["methodDescription"] !== void 0 && typeof value["methodDescription"] !== "string") {
9509
+ return configError("methodDescription\uC740 \uBB38\uC790\uC5F4\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4.");
9510
+ }
9511
+ return {
9512
+ methodName: value["methodName"],
9513
+ methodDescription: value["methodDescription"],
9514
+ methodPluginList: value["methodPluginList"].map(
9515
+ (item, index) => parsePluginInput(item, METHOD_PLUGIN_TYPES, `methodPluginList[${index}]`, true)
9516
+ )
9517
+ };
9518
+ }
9519
+ function updatedResourceOutput(opts, resources) {
9520
+ output(opts, {
9521
+ headers: ["resourceId", "path", "methodType"],
9522
+ rows: resources.map((resource) => [
9523
+ sanitizeForTerminal(resource.resourceId),
9524
+ sanitizeForTerminal(resource.path),
9525
+ resource.methodType == null ? "-" : sanitizeForTerminal(resource.methodType)
9526
+ ]),
9527
+ raw: resources,
9528
+ ids: resources.map((resource) => sanitizeForTerminal(resource.resourceId))
9529
+ });
9530
+ }
8932
9531
  var listCommand11 = addApiGatewayOptions(
8933
9532
  new import_commander49.Command("list").description("API Gateway resource \uBAA9\uB85D\uC744 \uC870\uD68C\uD55C\uB2E4 (\uC804\uCCB4 \uD544\uB4DC\uB294 --json)").argument("<service-id>", "API Gateway service ID")
8934
9533
  ).action(async (serviceId, _opts, command) => {
@@ -9013,16 +9612,179 @@ var responsesCommand = addApiGatewayOptions(
9013
9612
  ids: []
9014
9613
  });
9015
9614
  });
9016
- var resourceCommand = new import_commander49.Command("resource").description("API Gateway resource \uC870\uD68C \uBA85\uB839").addCommand(listCommand11).addCommand(parametersCommand).addCommand(responsesCommand);
9615
+ var setPathPluginCommand = addApiGatewayOptions(
9616
+ new import_commander49.Command("set-path-plugin").description("API Gateway resource \uACBD\uB85C \uD50C\uB7EC\uADF8\uC778\uC744 \uC124\uC815\uD55C\uB2E4").argument("<service-id>", "API Gateway service ID").argument("<resource-id>", "API Gateway resource ID").requiredOption("--config-file <path>", "\uD50C\uB7EC\uADF8\uC778 \uC124\uC815 JSON \uD30C\uC77C").option("--dry-run", "\uC4F0\uAE30 \uC5C6\uC774 \uC608\uC0C1 \uC601\uD5A5 \uBC94\uC704\uB97C \uCD9C\uB825\uD55C\uB2E4").option("--yes", "\uB9AC\uC18C\uC2A4 \uACBD\uB85C \uD50C\uB7EC\uADF8\uC778 \uC124\uC815\uC744 \uD655\uC778\uD55C\uB2E4")
9617
+ ).action(async (serviceId, resourceId2, _opts, command) => {
9618
+ const opts = command.optsWithGlobals();
9619
+ const parsedServiceId = parseRequiredArgument(serviceId, "service-id");
9620
+ const parsedResourceId = parseRequiredArgument(resourceId2, "resource-id");
9621
+ const plugins = parsePathPluginConfig(await readPluginConfigFile(opts.configFile));
9622
+ if (plugins.some((plugin) => plugin.pluginType === "CORS")) {
9623
+ process.stderr.write(
9624
+ "\uACBD\uACE0: CORS \uD50C\uB7EC\uADF8\uC778\uC740 \uD558\uC704\uC5D0 OPTIONS \uBA54\uC11C\uB4DC\uB97C \uC790\uB3D9 \uC0DD\uC131\uD558\uBA70 \uAE30\uC874 OPTIONS\uB97C \uC0AD\uC81C\xB7\uB300\uCCB4\uD569\uB2C8\uB2E4.\n"
9625
+ );
9626
+ }
9627
+ if (plugins.some((plugin) => plugin.applyChildPath === true && plugin.delete === true)) {
9628
+ process.stderr.write(
9629
+ "\uACBD\uACE0: applyChildPath\uC640 delete\uAC00 \uD568\uAED8 true\uC774\uBA74 \uD558\uC704 \uC804\uCCB4\uC5D0\uC11C \uD574\uB2F9 \uD50C\uB7EC\uADF8\uC778\uC774 \uC0AD\uC81C\uB429\uB2C8\uB2E4.\n"
9630
+ );
9631
+ }
9632
+ if (!opts.dryRun) requireYes(opts.yes, "\uB9AC\uC18C\uC2A4 \uACBD\uB85C \uD50C\uB7EC\uADF8\uC778 \uC124\uC815");
9633
+ const { client } = await resolveApiGatewayClient(opts);
9634
+ const displayResourceId = sanitizeForTerminal(parsedResourceId);
9635
+ const spinnerAction = opts.dryRun ? "\uACBD\uB85C \uD50C\uB7EC\uADF8\uC778 \uC601\uD5A5 \uBC94\uC704 \uD655\uC778" : "\uACBD\uB85C \uD50C\uB7EC\uADF8\uC778 \uC124\uC815";
9636
+ startSpinner(`API Gateway resource "${displayResourceId}" ${spinnerAction} \uC911...`);
9637
+ let target;
9638
+ let applyChildPath;
9639
+ let affected;
9640
+ let updatedResources = [];
9641
+ try {
9642
+ const resources = await client.listResources(parsedServiceId);
9643
+ const matchedResource = resources.find(
9644
+ (resource) => resource.resourceId === parsedResourceId
9645
+ );
9646
+ if (matchedResource === void 0) {
9647
+ throw new NhnCloudCliError(
9648
+ `API Gateway resource "${displayResourceId}"\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`,
9649
+ EXIT_PARAM_ERROR
9650
+ );
9651
+ }
9652
+ target = matchedResource;
9653
+ applyChildPath = plugins.some((plugin) => plugin.applyChildPath === true);
9654
+ affected = applyChildPath ? collectAffectedPaths(resources, target.path) : [target];
9655
+ if (!opts.dryRun) {
9656
+ updatedResources = await client.setPathPlugins(
9657
+ parsedServiceId,
9658
+ parsedResourceId,
9659
+ plugins
9660
+ );
9661
+ }
9662
+ } catch (error) {
9663
+ stopSpinner(false);
9664
+ throw error;
9665
+ }
9666
+ stopSpinner(true);
9667
+ if (opts.dryRun) {
9668
+ const appliedPluginTypes = plugins.map((plugin) => plugin.pluginType).join(",");
9669
+ output(opts, {
9670
+ headers: ["resourceId", "path", "methodType", "appliedPluginTypes"],
9671
+ rows: affected.map((resource) => [
9672
+ sanitizeForTerminal(resource.resourceId),
9673
+ sanitizeForTerminal(resource.path),
9674
+ resource.methodType === null ? "-" : sanitizeForTerminal(resource.methodType),
9675
+ appliedPluginTypes
9676
+ ]),
9677
+ raw: { targetPath: target.path, applyChildPath, plugins, affected },
9678
+ ids: affected.map((resource) => sanitizeForTerminal(resource.resourceId))
9679
+ });
9680
+ if (applyChildPath) {
9681
+ process.stderr.write(
9682
+ "\uACBD\uACE0: dry-run \uC601\uD5A5 \uBC94\uC704\uB294 path \uC811\uB450 \uBE44\uAD50\uB85C \uACC4\uC0B0\uD55C \uCD94\uC815\uAC12\uC774\uBA70 \uC11C\uBC84 \uD310\uC815\uACFC \uB2E4\uB97C \uC218 \uC788\uC2B5\uB2C8\uB2E4.\n"
9683
+ );
9684
+ }
9685
+ return;
9686
+ }
9687
+ updatedResourceOutput(opts, updatedResources);
9688
+ });
9689
+ var setMethodPluginCommand = addApiGatewayOptions(
9690
+ new import_commander49.Command("set-method-plugin").description("API Gateway resource \uBA54\uC11C\uB4DC \uD50C\uB7EC\uADF8\uC778\uC744 \uC124\uC815\uD55C\uB2E4").argument("<service-id>", "API Gateway service ID").argument("<resource-id>", "API Gateway resource ID").requiredOption("--config-file <path>", "\uD50C\uB7EC\uADF8\uC778 \uC124\uC815 JSON \uD30C\uC77C").option("--dry-run", "\uC4F0\uAE30 \uC5C6\uC774 \uC608\uC0C1 \uBCC0\uACBD \uB0B4\uC6A9\uC744 \uCD9C\uB825\uD55C\uB2E4").option("--yes", "\uB9AC\uC18C\uC2A4 \uBA54\uC11C\uB4DC \uD50C\uB7EC\uADF8\uC778 \uC124\uC815\uC744 \uD655\uC778\uD55C\uB2E4")
9691
+ ).action(async (serviceId, resourceId2, _opts, command) => {
9692
+ const opts = command.optsWithGlobals();
9693
+ const parsedServiceId = parseRequiredArgument(serviceId, "service-id");
9694
+ const parsedResourceId = parseRequiredArgument(resourceId2, "resource-id");
9695
+ const config = parseMethodPluginConfig(await readPluginConfigFile(opts.configFile));
9696
+ if (!opts.dryRun) requireYes(opts.yes, "\uB9AC\uC18C\uC2A4 \uBA54\uC11C\uB4DC \uD50C\uB7EC\uADF8\uC778 \uC124\uC815");
9697
+ const { client } = await resolveApiGatewayClient(opts);
9698
+ const displayResourceId = sanitizeForTerminal(parsedResourceId);
9699
+ const spinnerAction = opts.dryRun ? "\uBA54\uC11C\uB4DC \uD50C\uB7EC\uADF8\uC778 \uC601\uD5A5 \uBC94\uC704 \uD655\uC778" : "\uBA54\uC11C\uB4DC \uD50C\uB7EC\uADF8\uC778 \uC124\uC815";
9700
+ startSpinner(`API Gateway resource "${displayResourceId}" ${spinnerAction} \uC911...`);
9701
+ let target;
9702
+ let methodName;
9703
+ let methodDescription;
9704
+ let updatedResources = [];
9705
+ try {
9706
+ const resources = await client.listResources(parsedServiceId);
9707
+ const matchedResource = resources.find(
9708
+ (resource) => resource.resourceId === parsedResourceId
9709
+ );
9710
+ if (matchedResource === void 0) {
9711
+ throw new NhnCloudCliError(
9712
+ `API Gateway resource "${displayResourceId}"\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`,
9713
+ EXIT_PARAM_ERROR
9714
+ );
9715
+ }
9716
+ target = matchedResource;
9717
+ if (target.methodType === null) {
9718
+ throw new NhnCloudCliError(
9719
+ "\uB300\uC0C1 resource\uB294 \uBA54\uC11C\uB4DC\uAC00 \uC544\uB2CC \uACBD\uB85C\uC785\uB2C8\uB2E4. set-path-plugin\uC744 \uC0AC\uC6A9\uD558\uC138\uC694.",
9720
+ EXIT_PARAM_ERROR
9721
+ );
9722
+ }
9723
+ const resolvedMethodName = config.methodName ?? target.methodName;
9724
+ if (resolvedMethodName === null) {
9725
+ throw new NhnCloudCliError(
9726
+ "\uAE30\uC874 methodName\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uC124\uC815 \uD30C\uC77C\uC5D0 methodName\uC744 \uB123\uC73C\uC138\uC694.",
9727
+ EXIT_PARAM_ERROR
9728
+ );
9729
+ }
9730
+ methodName = resolvedMethodName;
9731
+ methodDescription = config.methodDescription ?? target.methodDescription ?? void 0;
9732
+ if (!opts.dryRun) {
9733
+ const body = {
9734
+ methodName,
9735
+ ...methodDescription === void 0 ? {} : { methodDescription },
9736
+ methodPluginList: config.methodPluginList
9737
+ };
9738
+ updatedResources = await client.setMethodPlugins(
9739
+ parsedServiceId,
9740
+ parsedResourceId,
9741
+ body
9742
+ );
9743
+ }
9744
+ } catch (error) {
9745
+ stopSpinner(false);
9746
+ throw error;
9747
+ }
9748
+ stopSpinner(true);
9749
+ if (opts.dryRun) {
9750
+ const appliedPluginTypes = config.methodPluginList.map((plugin) => plugin.pluginType).join(",");
9751
+ output(opts, {
9752
+ headers: [
9753
+ "resourceId",
9754
+ "path",
9755
+ "methodType",
9756
+ "methodName",
9757
+ "appliedPluginTypes"
9758
+ ],
9759
+ rows: [[
9760
+ sanitizeForTerminal(target.resourceId),
9761
+ sanitizeForTerminal(target.path),
9762
+ sanitizeForTerminal(target.methodType),
9763
+ sanitizeForTerminal(methodName),
9764
+ appliedPluginTypes
9765
+ ]],
9766
+ raw: {
9767
+ target,
9768
+ methodName,
9769
+ methodDescription,
9770
+ plugins: config.methodPluginList
9771
+ },
9772
+ ids: [sanitizeForTerminal(target.resourceId)]
9773
+ });
9774
+ return;
9775
+ }
9776
+ updatedResourceOutput(opts, updatedResources);
9777
+ });
9778
+ var resourceCommand = new import_commander49.Command("resource").description("API Gateway resource \uC870\uD68C \uBC0F \uBCC0\uACBD \uBA85\uB839").addCommand(listCommand11).addCommand(parametersCommand).addCommand(responsesCommand).addCommand(setPathPluginCommand).addCommand(setMethodPluginCommand);
9017
9779
 
9018
9780
  // src/commands/apigateway/stage.ts
9019
- var import_promises8 = require("fs/promises");
9781
+ var import_promises9 = require("fs/promises");
9020
9782
  var import_commander51 = require("commander");
9021
9783
 
9022
9784
  // src/commands/apigateway/deploy.ts
9023
9785
  var import_commander50 = require("commander");
9024
9786
  function addApiGatewayOptions2(command) {
9025
- return command.option("--region <region>", "API Gateway region (\uAE30\uBCF8: kr1)", "kr1").option("--app-key <key>", "API Gateway appKey (profile \uC758 apigateway.appkey \uBCF4\uB2E4 \uC6B0\uC120)").option("--profile <name>", "\uC0AC\uC6A9\uD560 profile \uC774\uB984");
9787
+ return command.option("--region <region>", "API Gateway region (\uAE30\uBCF8: kr1)", "kr1").option("--profile <name>", "\uC0AC\uC6A9\uD560 profile \uC774\uB984");
9026
9788
  }
9027
9789
  var listCommand12 = addApiGatewayOptions2(
9028
9790
  new import_commander50.Command("list").description("API Gateway stage \uBC30\uD3EC \uC774\uB825 \uBAA9\uB85D\uC744 \uC870\uD68C\uD55C\uB2E4 (\uC804\uCCB4 \uD544\uB4DC\uB294 --json)").argument("<service-id>", "API Gateway service ID").argument("<stage-id>", "API Gateway stage ID")
@@ -9083,13 +9845,140 @@ var latestCommand = addApiGatewayOptions2(
9083
9845
  ids: [sanitizeForTerminal(deploy.deployId)]
9084
9846
  });
9085
9847
  });
9086
- var deployCommand = new import_commander50.Command("deploy").description("API Gateway stage \uBC30\uD3EC \uC774\uB825 \uC870\uD68C \uBA85\uB839").addCommand(listCommand12).addCommand(latestCommand);
9848
+ var createCommand8 = addApiGatewayOptions2(
9849
+ new import_commander50.Command("create").description("stage \uC124\uC815\uC744 API Gateway service \uC5D0 \uBC30\uD3EC\uD55C\uB2E4").argument("<service-id>", "API Gateway service ID").argument("<stage-id>", "API Gateway stage ID").option("--description <text>", "\uBC30\uD3EC \uC124\uBA85 (\uCD5C\uB300 200\uC790)").option("--no-wait", "\uBC30\uD3EC \uACB0\uACFC\uB97C \uAE30\uB2E4\uB9AC\uC9C0 \uC54A\uACE0 \uC694\uCCAD\uB9CC \uD55C\uB2E4").option("--timeout <sec>", "\uBC30\uD3EC \uB300\uAE30 \uC0C1\uD55C (\uCD08, \uAE30\uBCF8 300)", "300").option("--yes", "\uBC30\uD3EC\uB97C \uD655\uC778\uD55C\uB2E4")
9850
+ ).action(async (serviceId, stageId, _opts, command) => {
9851
+ const opts = command.optsWithGlobals();
9852
+ const parsedServiceId = parseRequiredArgument(serviceId, "service-id");
9853
+ const parsedStageId = parseRequiredArgument(stageId, "stage-id");
9854
+ if (opts.description !== void 0 && opts.description.trim().length === 0) {
9855
+ throw new NhnCloudCliError(
9856
+ "--description \uC740 \uBE44\uC5B4 \uC788\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
9857
+ EXIT_PARAM_ERROR
9858
+ );
9859
+ }
9860
+ if (opts.description !== void 0 && [...opts.description].length > 200) {
9861
+ throw new NhnCloudCliError(
9862
+ "--description \uC740 \uCD5C\uB300 200\uC790\uAE4C\uC9C0 \uC9C0\uC815\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.",
9863
+ EXIT_PARAM_ERROR
9864
+ );
9865
+ }
9866
+ const timeoutMs = parsePositiveIntegerOption(opts.timeout ?? "300", "--timeout") * 1e3;
9867
+ requireYes(opts.yes, "\uC2A4\uD14C\uC774\uC9C0 \uBC30\uD3EC");
9868
+ const displayStageId = sanitizeForTerminal(parsedStageId);
9869
+ const { client } = await resolveApiGatewayClient(opts);
9870
+ let baselineDeployId = null;
9871
+ let baselineLookupFailed = false;
9872
+ let deploy;
9873
+ if (opts.wait) {
9874
+ startSpinner("\uC9C1\uC804 \uBC30\uD3EC \uD655\uC778 \uC911...");
9875
+ try {
9876
+ const baseline = await client.getLatestDeploy(parsedServiceId, parsedStageId);
9877
+ baselineDeployId = baseline.deployId;
9878
+ stopSpinner(true);
9879
+ } catch {
9880
+ baselineLookupFailed = true;
9881
+ stopSpinner(false);
9882
+ }
9883
+ }
9884
+ if (baselineLookupFailed) {
9885
+ process.stderr.write("\uC548\uB0B4: \uC9C1\uC804 \uBC30\uD3EC ID \uB97C \uC77D\uC9C0 \uBABB\uD574 \uC0C1\uD0DC\uB9CC\uC73C\uB85C \uC644\uB8CC\uB97C \uD310\uC815\uD569\uB2C8\uB2E4.\n");
9886
+ }
9887
+ startSpinner(`API Gateway stage "${displayStageId}" \uBC30\uD3EC \uC911...`);
9888
+ let deployAccepted = false;
9889
+ let deployResultKnown = false;
9890
+ try {
9891
+ await client.createDeploy(parsedServiceId, parsedStageId, {
9892
+ deployDescription: opts.description
9893
+ });
9894
+ deployAccepted = true;
9895
+ if (opts.wait) {
9896
+ deploy = await client.waitForDeploy(parsedServiceId, parsedStageId, {
9897
+ timeoutMs,
9898
+ baselineDeployId
9899
+ });
9900
+ if (deploy.deployStatus !== DEPLOY_STATUS_COMPLETE) {
9901
+ deployResultKnown = true;
9902
+ throw new NhnCloudCliError(
9903
+ `API Gateway stage \uBC30\uD3EC \uC2E4\uD328: deployId=${deploy.deployId}, deployStatus=${deploy.deployStatus}`,
9904
+ EXIT_API_ERROR
9905
+ );
9906
+ }
9907
+ }
9908
+ } catch (error) {
9909
+ stopSpinner(false);
9910
+ if (deployAccepted && !deployResultKnown) {
9911
+ process.stderr.write(
9912
+ "\uC8FC\uC758: \uBC30\uD3EC \uC694\uCCAD\uC740 \uC774\uBBF8 \uC811\uC218\uB410\uC2B5\uB2C8\uB2E4. \uC7AC\uC2E4\uD589\uD558\uC9C0 \uB9D0\uACE0 apigateway stage deploy latest \uB85C \uACB0\uACFC\uB97C \uD655\uC778\uD558\uC138\uC694.\n"
9913
+ );
9914
+ }
9915
+ throw error;
9916
+ }
9917
+ stopSpinner(true);
9918
+ if (!opts.wait) {
9919
+ if (opts.json) {
9920
+ printJson({ requested: true });
9921
+ } else if (!opts.quiet) {
9922
+ process.stderr.write(
9923
+ "\uC548\uB0B4: \uBC30\uD3EC\uB97C \uC694\uCCAD\uD588\uC2B5\uB2C8\uB2E4. \uACB0\uACFC\uB294 apigateway stage deploy latest \uB85C \uD655\uC778\uD558\uC138\uC694.\n"
9924
+ );
9925
+ }
9926
+ return;
9927
+ }
9928
+ if (deploy === void 0) {
9929
+ throw new NhnCloudCliError(
9930
+ "API Gateway stage \uBC30\uD3EC \uACB0\uACFC\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
9931
+ EXIT_API_ERROR
9932
+ );
9933
+ }
9934
+ output(opts, {
9935
+ headers: ["deployId", "deployStatus", "deployedAt", "deployDescription"],
9936
+ rows: [[
9937
+ sanitizeForTerminal(deploy.deployId),
9938
+ sanitizeForTerminal(deploy.deployStatus),
9939
+ sanitizeForTerminal(deploy.deployedAt),
9940
+ sanitizeForTerminal(deploy.deployDescription)
9941
+ ]],
9942
+ raw: deploy,
9943
+ ids: [sanitizeForTerminal(deploy.deployId)]
9944
+ });
9945
+ });
9946
+ var rollbackCommand = addApiGatewayOptions2(
9947
+ new import_commander50.Command("rollback").description("\uBC30\uD3EC \uC774\uB825\uC73C\uB85C stage \uC124\uC815\uC744 \uB418\uB3CC\uB9B0\uB2E4").argument("<service-id>", "API Gateway service ID").argument("<stage-id>", "API Gateway stage ID").argument("<deploy-id>", "\uB418\uB3CC\uB9B4 \uBC30\uD3EC ID").option("--yes", "\uB418\uB3CC\uB9AC\uAE30\uB97C \uD655\uC778\uD55C\uB2E4")
9948
+ ).action(async (serviceId, stageId, deployId, _opts, command) => {
9949
+ const opts = command.optsWithGlobals();
9950
+ const parsedServiceId = parseRequiredArgument(serviceId, "service-id");
9951
+ const parsedStageId = parseRequiredArgument(stageId, "stage-id");
9952
+ const parsedDeployId = parseRequiredArgument(deployId, "deploy-id");
9953
+ requireYes(opts.yes, "\uC2A4\uD14C\uC774\uC9C0 \uB418\uB3CC\uB9AC\uAE30");
9954
+ const displayStageId = sanitizeForTerminal(parsedStageId);
9955
+ const { client } = await resolveApiGatewayClient(opts);
9956
+ startSpinner(`API Gateway stage "${displayStageId}" \uB418\uB3CC\uB9AC\uB294 \uC911...`);
9957
+ let resources;
9958
+ try {
9959
+ resources = await client.rollbackDeploy(
9960
+ parsedServiceId,
9961
+ parsedStageId,
9962
+ parsedDeployId
9963
+ );
9964
+ } catch (error) {
9965
+ stopSpinner(false);
9966
+ throw error;
9967
+ }
9968
+ stopSpinner(true);
9969
+ output(opts, writtenStageResourceOutput(resources));
9970
+ process.stderr.write(
9971
+ "\uC548\uB0B4: \uB418\uB3CC\uB9AC\uAE30\uB294 \uC2A4\uD14C\uC774\uC9C0 \uC124\uC815\uB9CC \uBC14\uAFC9\uB2C8\uB2E4. \uC11C\uBE44\uC2A4\uC5D0 \uC801\uC6A9\uD558\uB824\uBA74 apigateway stage deploy create \uB97C \uC2E4\uD589\uD558\uC138\uC694.\n"
9972
+ );
9973
+ process.stderr.write("\uC644\uB8CC: \uC9C1\uC804\uAE4C\uC9C0\uC758 \uC2A4\uD14C\uC774\uC9C0 \uC124\uC815\uC740 \uC774 \uBC30\uD3EC \uC774\uB825\uC758 \uB0B4\uC6A9\uC73C\uB85C \uB300\uCCB4\uB410\uC2B5\uB2C8\uB2E4.\n");
9974
+ });
9975
+ var deployCommand = new import_commander50.Command("deploy").description("API Gateway stage \uBC30\uD3EC \uC870\uD68C\uC640 \uC2E4\uD589 \uBA85\uB839").addCommand(listCommand12).addCommand(latestCommand).addCommand(createCommand8).addCommand(rollbackCommand);
9087
9976
 
9088
9977
  // src/commands/apigateway/stage.ts
9089
9978
  function addApiGatewayOptions3(command) {
9090
- return command.option("--region <region>", "API Gateway region (\uAE30\uBCF8: kr1)", "kr1").option("--app-key <key>", "API Gateway appKey (profile \uC758 apigateway.appkey \uBCF4\uB2E4 \uC6B0\uC120)").option("--profile <name>", "\uC0AC\uC6A9\uD560 profile \uC774\uB984");
9979
+ return command.option("--region <region>", "API Gateway region (\uAE30\uBCF8: kr1)", "kr1").option("--profile <name>", "\uC0AC\uC6A9\uD560 profile \uC774\uB984");
9091
9980
  }
9092
- function fileErrorReason(error) {
9981
+ function fileErrorReason2(error) {
9093
9982
  if (typeof error === "object" && error !== null && "code" in error) {
9094
9983
  const code = error.code;
9095
9984
  if (typeof code === "string") return code;
@@ -9098,12 +9987,12 @@ function fileErrorReason(error) {
9098
9987
  }
9099
9988
  async function writeStageSwaggerFile(path5, swagger, force) {
9100
9989
  try {
9101
- await (0, import_promises8.writeFile)(path5, JSON.stringify(swagger, null, 2) + "\n", {
9990
+ await (0, import_promises9.writeFile)(path5, JSON.stringify(swagger, null, 2) + "\n", {
9102
9991
  encoding: "utf-8",
9103
9992
  flag: force ? "w" : "wx"
9104
9993
  });
9105
9994
  } catch (error) {
9106
- const reason = fileErrorReason(error);
9995
+ const reason = fileErrorReason2(error);
9107
9996
  const guidance = reason === "EEXIST" ? " \uB36E\uC5B4\uC4F0\uB824\uBA74 --force \uB97C \uC9C0\uC815\uD558\uC138\uC694." : "";
9108
9997
  throw new NhnCloudCliError(
9109
9998
  `Swagger \uD30C\uC77C \uC800\uC7A5 \uC2E4\uD328: ${JSON.stringify(path5)} (${JSON.stringify(reason)}).${guidance}`,
@@ -9194,7 +10083,79 @@ var resourcesCommand = addApiGatewayOptions3(
9194
10083
  ids: resources.map((resource) => sanitizeForTerminal(resource.stageResourceId))
9195
10084
  });
9196
10085
  });
9197
- var stageCommand = new import_commander51.Command("stage").description("API Gateway stage \uC870\uD68C \uBA85\uB839").addCommand(listCommand13).addCommand(swaggerCommand).addCommand(resourcesCommand).addCommand(deployCommand);
10086
+ var updateCommand2 = addApiGatewayOptions3(
10087
+ new import_commander51.Command("update").description("API Gateway stage\uC758 backend endpoint URL\uACFC \uC124\uBA85\uC744 \uC218\uC815\uD55C\uB2E4").argument("<service-id>", "API Gateway service ID").argument("<stage-id>", "API Gateway stage ID").option("--backend-endpoint-url <url>", "\uBCC0\uACBD\uD560 backend endpoint URL").option("--description <text>", "\uBCC0\uACBD\uD560 stage \uC124\uBA85").option("--yes", "\uC2A4\uD14C\uC774\uC9C0 \uC218\uC815\uC744 \uD655\uC778\uD55C\uB2E4")
10088
+ ).action(async (serviceId, stageId, _opts, command) => {
10089
+ const opts = command.optsWithGlobals();
10090
+ const parsedServiceId = parseRequiredArgument(serviceId, "service-id");
10091
+ const parsedStageId = parseRequiredArgument(stageId, "stage-id");
10092
+ if (opts.backendEndpointUrl === void 0 && opts.description === void 0) {
10093
+ throw new NhnCloudCliError(
10094
+ "\uC2A4\uD14C\uC774\uC9C0 \uC218\uC815\uC5D0\uC11C \uBC14\uAFC0 \uAC12\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. --backend-endpoint-url \uB610\uB294 --description\uC744 \uC9C0\uC815\uD558\uC138\uC694.",
10095
+ EXIT_PARAM_ERROR
10096
+ );
10097
+ }
10098
+ requireYes(opts.yes, "\uC2A4\uD14C\uC774\uC9C0 \uC218\uC815");
10099
+ const displayStageId = sanitizeForTerminal(parsedStageId);
10100
+ const { client } = await resolveApiGatewayClient(opts);
10101
+ startSpinner(`API Gateway stage "${displayStageId}" \uC218\uC815 \uC911...`);
10102
+ let updatedStage;
10103
+ try {
10104
+ const stages = await client.listStages(parsedServiceId);
10105
+ const existingStage = stages.find((stage) => stage.stageId === parsedStageId);
10106
+ if (existingStage === void 0) {
10107
+ throw new NhnCloudCliError(
10108
+ `API Gateway stage "${displayStageId}"\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`,
10109
+ EXIT_PARAM_ERROR
10110
+ );
10111
+ }
10112
+ updatedStage = await client.updateStage(parsedServiceId, parsedStageId, {
10113
+ backendEndpointUrl: opts.backendEndpointUrl ?? existingStage.backendEndpointUrl,
10114
+ stageDescription: opts.description ?? existingStage.stageDescription
10115
+ });
10116
+ } catch (error) {
10117
+ stopSpinner(false);
10118
+ throw error;
10119
+ }
10120
+ stopSpinner(true);
10121
+ output(opts, {
10122
+ headers: ["stageId", "stageName", "stageUrl", "backendEndpointUrl", "updatedAt"],
10123
+ rows: [[
10124
+ sanitizeForTerminal(updatedStage.stageId),
10125
+ updatedStage.stageName === null ? "-" : sanitizeForTerminal(updatedStage.stageName),
10126
+ sanitizeForTerminal(updatedStage.stageUrl),
10127
+ sanitizeForTerminal(updatedStage.backendEndpointUrl),
10128
+ sanitizeForTerminal(updatedStage.updatedAt)
10129
+ ]],
10130
+ raw: updatedStage,
10131
+ ids: [sanitizeForTerminal(updatedStage.stageId)]
10132
+ });
10133
+ process.stderr.write("\uC548\uB0B4: \uC2A4\uD14C\uC774\uC9C0 \uBCC0\uACBD \uD6C4 \uC2E4\uC81C \uD2B8\uB798\uD53D \uBC18\uC601 \uC0C1\uD0DC\uB97C \uD655\uC778\uD558\uC138\uC694.\n");
10134
+ });
10135
+ var importResourcesCommand = addApiGatewayOptions3(
10136
+ new import_commander51.Command("import-resources").description("API Gateway service \uC758 \uB9AC\uC18C\uC2A4\uB97C stage \uB85C \uAC00\uC838\uC628\uB2E4").argument("<service-id>", "API Gateway service ID").argument("<stage-id>", "API Gateway stage ID").option("--yes", "\uC2A4\uD14C\uC774\uC9C0 \uBC18\uC601\uC744 \uD655\uC778\uD55C\uB2E4")
10137
+ ).action(async (serviceId, stageId, _opts, command) => {
10138
+ const opts = command.optsWithGlobals();
10139
+ const parsedServiceId = parseRequiredArgument(serviceId, "service-id");
10140
+ const parsedStageId = parseRequiredArgument(stageId, "stage-id");
10141
+ requireYes(opts.yes, "\uC2A4\uD14C\uC774\uC9C0 \uBC18\uC601");
10142
+ const displayStageId = sanitizeForTerminal(parsedStageId);
10143
+ const { client } = await resolveApiGatewayClient(opts);
10144
+ startSpinner(`API Gateway stage "${displayStageId}" \uB9AC\uC18C\uC2A4 \uBC18\uC601 \uC911...`);
10145
+ let resources;
10146
+ try {
10147
+ resources = await client.importStageResources(parsedServiceId, parsedStageId);
10148
+ } catch (error) {
10149
+ stopSpinner(false);
10150
+ throw error;
10151
+ }
10152
+ stopSpinner(true);
10153
+ output(opts, writtenStageResourceOutput(resources));
10154
+ process.stderr.write(
10155
+ "\uC548\uB0B4: \uBC18\uC601\uC740 \uC2A4\uD14C\uC774\uC9C0 \uC124\uC815\uB9CC \uBC14\uAFC9\uB2C8\uB2E4. \uC11C\uBE44\uC2A4\uC5D0 \uC801\uC6A9\uD558\uB824\uBA74 apigateway stage deploy create \uB97C \uC2E4\uD589\uD558\uC138\uC694.\n"
10156
+ );
10157
+ });
10158
+ var stageCommand = new import_commander51.Command("stage").description("API Gateway stage \uC870\uD68C \uBC0F \uBCC0\uACBD \uBA85\uB839").addCommand(listCommand13).addCommand(swaggerCommand).addCommand(resourcesCommand).addCommand(updateCommand2).addCommand(importResourcesCommand).addCommand(deployCommand);
9198
10159
 
9199
10160
  // src/commands/loadbalancer/list.ts
9200
10161
  var import_commander52 = require("commander");
@@ -9203,10 +10164,10 @@ var import_commander52 = require("commander");
9203
10164
  var import_node_net = require("net");
9204
10165
 
9205
10166
  // src/services/loadbalancer/client.ts
9206
- var import_ky14 = __toESM(require("ky"));
10167
+ var import_ky15 = __toESM(require("ky"));
9207
10168
 
9208
10169
  // src/services/loadbalancer/types.ts
9209
- function isRecord2(value) {
10170
+ function isRecord3(value) {
9210
10171
  return typeof value === "object" && value !== null;
9211
10172
  }
9212
10173
  function isNonEmptyString(value) {
@@ -9216,29 +10177,29 @@ function isIpAclAction(value) {
9216
10177
  return value === "ALLOW" || value === "DENY";
9217
10178
  }
9218
10179
  function isLoadBalancerIpAclGroup(value) {
9219
- return isRecord2(value) && isNonEmptyString(value["ipacl_group_id"]);
10180
+ return isRecord3(value) && isNonEmptyString(value["ipacl_group_id"]);
9220
10181
  }
9221
10182
  function isIpAclGroupLoadBalancer(value) {
9222
- return isRecord2(value) && isNonEmptyString(value["loadbalancer_id"]);
10183
+ return isRecord3(value) && isNonEmptyString(value["loadbalancer_id"]);
9223
10184
  }
9224
10185
  function isLoadBalancer(value) {
9225
- if (!isRecord2(value)) return false;
10186
+ if (!isRecord3(value)) return false;
9226
10187
  return isNonEmptyString(value["id"]) && typeof value["name"] === "string" && typeof value["vip_address"] === "string" && typeof value["provisioning_status"] === "string" && typeof value["operating_status"] === "string" && (value["ipacl_group_action"] === null || isIpAclAction(value["ipacl_group_action"])) && Array.isArray(value["ipacl_groups"]) && value["ipacl_groups"].every(isLoadBalancerIpAclGroup);
9227
10188
  }
9228
10189
  function isIpAclGroup(value) {
9229
- if (!isRecord2(value)) return false;
10190
+ if (!isRecord3(value)) return false;
9230
10191
  return isNonEmptyString(value["id"]) && typeof value["name"] === "string" && isIpAclAction(value["action"]) && typeof value["ipacl_target_count"] === "string" && Array.isArray(value["loadbalancers"]) && value["loadbalancers"].every(isIpAclGroupLoadBalancer);
9231
10192
  }
9232
10193
  function isIpAclTarget(value) {
9233
- if (!isRecord2(value)) return false;
10194
+ if (!isRecord3(value)) return false;
9234
10195
  return isNonEmptyString(value["id"]) && typeof value["cidr_address"] === "string" && typeof value["description"] === "string" && isNonEmptyString(value["ipacl_group_id"]);
9235
10196
  }
9236
10197
  function isIpAclBinding(value) {
9237
- return isRecord2(value) && isNonEmptyString(value["loadbalancer_id"]) && isNonEmptyString(value["ipacl_group_id"]);
10198
+ return isRecord3(value) && isNonEmptyString(value["loadbalancer_id"]) && isNonEmptyString(value["ipacl_group_id"]);
9238
10199
  }
9239
10200
 
9240
10201
  // src/services/loadbalancer/client.ts
9241
- function isRecord3(value) {
10202
+ function isRecord4(value) {
9242
10203
  return typeof value === "object" && value !== null;
9243
10204
  }
9244
10205
  function requireNonEmptyId(id, label) {
@@ -9249,7 +10210,7 @@ function requireNonEmptyId(id, label) {
9249
10210
  return normalized;
9250
10211
  }
9251
10212
  function readArrayField(response, key, itemGuard, missingKeyMessage, invalidItemMessage) {
9252
- if (!isRecord3(response) || !Array.isArray(response[key])) {
10213
+ if (!isRecord4(response) || !Array.isArray(response[key])) {
9253
10214
  throw new NhnCloudCliError(missingKeyMessage, EXIT_API_ERROR);
9254
10215
  }
9255
10216
  const items = response[key];
@@ -9259,7 +10220,7 @@ function readArrayField(response, key, itemGuard, missingKeyMessage, invalidItem
9259
10220
  return items;
9260
10221
  }
9261
10222
  function readObjectField(response, key, itemGuard, missingKeyMessage, invalidItemMessage) {
9262
- if (!isRecord3(response) || !(key in response)) {
10223
+ if (!isRecord4(response) || !(key in response)) {
9263
10224
  throw new NhnCloudCliError(missingKeyMessage, EXIT_API_ERROR);
9264
10225
  }
9265
10226
  const item = response[key];
@@ -9400,7 +10361,7 @@ var LoadBalancerClient = class {
9400
10361
  async getJson(path5, searchParams) {
9401
10362
  const url = `${this.endpoint}${path5}`;
9402
10363
  try {
9403
- return await import_ky14.default.get(url, {
10364
+ return await import_ky15.default.get(url, {
9404
10365
  headers: this.authHeaders(),
9405
10366
  searchParams,
9406
10367
  retry: 0,
@@ -9413,7 +10374,7 @@ var LoadBalancerClient = class {
9413
10374
  async postJson(path5, json) {
9414
10375
  const url = `${this.endpoint}${path5}`;
9415
10376
  try {
9416
- return await import_ky14.default.post(url, {
10377
+ return await import_ky15.default.post(url, {
9417
10378
  headers: this.authHeaders(),
9418
10379
  json,
9419
10380
  retry: 0,
@@ -9426,7 +10387,7 @@ var LoadBalancerClient = class {
9426
10387
  async putJson(path5, json) {
9427
10388
  const url = `${this.endpoint}${path5}`;
9428
10389
  try {
9429
- return await import_ky14.default.put(url, {
10390
+ return await import_ky15.default.put(url, {
9430
10391
  headers: this.authHeaders(),
9431
10392
  json,
9432
10393
  retry: 0,
@@ -9439,7 +10400,7 @@ var LoadBalancerClient = class {
9439
10400
  async deleteNoContent(path5) {
9440
10401
  const url = `${this.endpoint}${path5}`;
9441
10402
  try {
9442
- await import_ky14.default.delete(url, {
10403
+ await import_ky15.default.delete(url, {
9443
10404
  headers: this.authHeaders(),
9444
10405
  retry: 0,
9445
10406
  timeout: DEFAULT_TIMEOUT_MS
@@ -9462,7 +10423,7 @@ function optionalTrimmed(value) {
9462
10423
  if (value === void 0) return void 0;
9463
10424
  return value.trim();
9464
10425
  }
9465
- function requireYes(yes, operation) {
10426
+ function requireYes2(yes, operation) {
9466
10427
  if (!yes) {
9467
10428
  throw new NhnCloudCliError(
9468
10429
  `${operation}\uC5D0\uB294 --yes \uD50C\uB798\uADF8\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.`,
@@ -9652,9 +10613,6 @@ var import_commander55 = require("commander");
9652
10613
  var import_commander54 = require("commander");
9653
10614
 
9654
10615
  // src/commands/loadbalancer/rebind.ts
9655
- function sanitizeForTerminal2(value) {
9656
- return value.replace(/[\x00-\x1F\x7F]/g, "?");
9657
- }
9658
10616
  function shellQuote(value) {
9659
10617
  return `'${value.replace(/'/g, `'"'"'`)}'`;
9660
10618
  }
@@ -9765,9 +10723,9 @@ function writeTargetWarnings(group, result) {
9765
10723
  `
9766
10724
  );
9767
10725
  for (const failure of result.rebind.failed) {
9768
- process.stderr.write(`${sanitizeForTerminal2(failure.retry_command)}
10726
+ process.stderr.write(`${sanitizeForTerminal(failure.retry_command)}
9769
10727
  `);
9770
- process.stderr.write(` \uC624\uB958: ${sanitizeForTerminal2(failure.error)}
10728
+ process.stderr.write(` \uC624\uB958: ${sanitizeForTerminal(failure.error)}
9771
10729
  `);
9772
10730
  }
9773
10731
  }
@@ -9814,7 +10772,7 @@ var listCommand15 = new import_commander54.Command("list").description("IP ACL \
9814
10772
  });
9815
10773
  var addCommand = new import_commander54.Command("add").description("IP ACL \uB300\uC0C1\uC744 \uCD94\uAC00\uD558\uACE0 \uC5F0\uACB0\uB41C Load Balancer\uB97C \uC7AC\uBC14\uC778\uB529\uD55C\uB2E4").argument("<group>", "IP ACL \uADF8\uB8F9 \uC774\uB984 \uB610\uB294 UUID").requiredOption("--cidr <ip-or-cidr>", "\uCD94\uAC00\uD560 IP \uC8FC\uC18C \uB610\uB294 CIDR").option("--description <text>", "IP ACL \uB300\uC0C1 \uC124\uBA85").option("--no-rebind", "\uB300\uC0C1 \uCD94\uAC00 \uD6C4 Load Balancer \uC7AC\uBC14\uC778\uB529 \uC0DD\uB7B5").option("--yes", "\uB300\uC0C1 \uCD94\uAC00 \uD655\uC778").option("--region <region>", "region override (\uAE30\uBCF8: iaas \uC790\uACA9\uC99D\uBA85\uC758 region)").option("--profile <name>", "\uC0AC\uC6A9\uD560 profile \uC774\uB984").action(async (groupInput, _opts, cmd) => {
9816
10774
  const opts = cmd.optsWithGlobals();
9817
- requireYes(opts.yes, "IP ACL \uB300\uC0C1 \uCD94\uAC00");
10775
+ requireYes2(opts.yes, "IP ACL \uB300\uC0C1 \uCD94\uAC00");
9818
10776
  const parsedGroup = requireResourceInput(groupInput, "IP ACL \uADF8\uB8F9");
9819
10777
  const parsedCidr = parseIpOrCidr(opts.cidr ?? "");
9820
10778
  const parsedDescription = optionalTrimmed(opts.description);
@@ -9851,7 +10809,7 @@ var addCommand = new import_commander54.Command("add").description("IP ACL \uB30
9851
10809
  });
9852
10810
  var removeCommand = new import_commander54.Command("remove").description("IP ACL \uB300\uC0C1\uC744 \uC0AD\uC81C\uD558\uACE0 \uC5F0\uACB0\uB41C Load Balancer\uB97C \uC7AC\uBC14\uC778\uB529\uD55C\uB2E4").argument("<target-id>", "\uC0AD\uC81C\uD560 IP ACL \uB300\uC0C1 UUID").option("--no-rebind", "\uB300\uC0C1 \uC0AD\uC81C \uD6C4 Load Balancer \uC7AC\uBC14\uC778\uB529 \uC0DD\uB7B5").option("--yes", "\uB300\uC0C1 \uC0AD\uC81C \uD655\uC778").option("--region <region>", "region override (\uAE30\uBCF8: iaas \uC790\uACA9\uC99D\uBA85\uC758 region)").option("--profile <name>", "\uC0AC\uC6A9\uD560 profile \uC774\uB984").action(async (targetInput, _opts, cmd) => {
9853
10811
  const opts = cmd.optsWithGlobals();
9854
- requireYes(opts.yes, "IP ACL \uB300\uC0C1 \uC0AD\uC81C");
10812
+ requireYes2(opts.yes, "IP ACL \uB300\uC0C1 \uC0AD\uC81C");
9855
10813
  const parsedTargetId = requireResourceInput(targetInput, "IP ACL \uB300\uC0C1");
9856
10814
  const { client } = await resolveLoadBalancerClient(opts);
9857
10815
  startSpinner("IP ACL \uB300\uC0C1 \uC0AD\uC81C\uC640 Load Balancer \uC7AC\uBC14\uC778\uB529 \uC911...");
@@ -9947,7 +10905,7 @@ var getCommand10 = new import_commander55.Command("get").description("IP ACL \uA
9947
10905
  ids: [group.id]
9948
10906
  });
9949
10907
  });
9950
- var createCommand8 = new import_commander55.Command("create").description("IP ACL \uADF8\uB8F9\uC744 \uC0DD\uC131\uD55C\uB2E4").requiredOption("--name <name>", "IP ACL \uADF8\uB8F9 \uC774\uB984").requiredOption("--action <ALLOW|DENY>", "IP \uC811\uADFC \uC81C\uC5B4 \uB3D9\uC791").option("--description <text>", "IP ACL \uADF8\uB8F9 \uC124\uBA85").option("--region <region>", "region override (\uAE30\uBCF8: iaas \uC790\uACA9\uC99D\uBA85\uC758 region)").option("--profile <name>", "\uC0AC\uC6A9\uD560 profile \uC774\uB984").action(async (_opts, cmd) => {
10908
+ var createCommand9 = new import_commander55.Command("create").description("IP ACL \uADF8\uB8F9\uC744 \uC0DD\uC131\uD55C\uB2E4").requiredOption("--name <name>", "IP ACL \uADF8\uB8F9 \uC774\uB984").requiredOption("--action <ALLOW|DENY>", "IP \uC811\uADFC \uC81C\uC5B4 \uB3D9\uC791").option("--description <text>", "IP ACL \uADF8\uB8F9 \uC124\uBA85").option("--region <region>", "region override (\uAE30\uBCF8: iaas \uC790\uACA9\uC99D\uBA85\uC758 region)").option("--profile <name>", "\uC0AC\uC6A9\uD560 profile \uC774\uB984").action(async (_opts, cmd) => {
9951
10909
  const opts = cmd.optsWithGlobals();
9952
10910
  const parsedName = requireResourceInput(opts.name ?? "", "IP ACL \uADF8\uB8F9");
9953
10911
  const parsedAction = parseIpAclAction(opts.action ?? "");
@@ -9980,7 +10938,7 @@ var createCommand8 = new import_commander55.Command("create").description("IP AC
9980
10938
  });
9981
10939
  var deleteCommand7 = new import_commander55.Command("delete").description("IP ACL \uADF8\uB8F9\uACFC \uD558\uC704 \uB300\uC0C1\uC744 \uC0AD\uC81C\uD55C\uB2E4").argument("<group>", "IP ACL \uADF8\uB8F9 \uC774\uB984 \uB610\uB294 UUID").option("--yes", "\uC0AD\uC81C\uC640 \uC5F0\uACB0 \uADDC\uCE59 \uC81C\uAC70 \uD655\uC778").option("--region <region>", "region override (\uAE30\uBCF8: iaas \uC790\uACA9\uC99D\uBA85\uC758 region)").option("--profile <name>", "\uC0AC\uC6A9\uD560 profile \uC774\uB984").action(async (groupInput, _opts, cmd) => {
9982
10940
  const opts = cmd.optsWithGlobals();
9983
- const confirmedYes = requireYes(opts.yes, "IP ACL \uADF8\uB8F9 \uC0AD\uC81C");
10941
+ const confirmedYes = requireYes2(opts.yes, "IP ACL \uADF8\uB8F9 \uC0AD\uC81C");
9984
10942
  const parsedGroup = requireResourceInput(groupInput, "IP ACL \uADF8\uB8F9");
9985
10943
  if (confirmedYes) {
9986
10944
  process.stderr.write(
@@ -10013,7 +10971,7 @@ var deleteCommand7 = new import_commander55.Command("delete").description("IP AC
10013
10971
  var ipaclCommand = new import_commander55.Command("ipacl").description("IP ACL \uADF8\uB8F9\xB7\uB300\uC0C1 \uC870\uD68C");
10014
10972
  ipaclCommand.addCommand(listCommand16);
10015
10973
  ipaclCommand.addCommand(getCommand10);
10016
- ipaclCommand.addCommand(createCommand8);
10974
+ ipaclCommand.addCommand(createCommand9);
10017
10975
  ipaclCommand.addCommand(deleteCommand7);
10018
10976
  ipaclCommand.addCommand(targetCommand);
10019
10977
 
@@ -10054,7 +11012,7 @@ var setIpAclCommand2 = new import_commander56.Command("set-ipacl").description("
10054
11012
  collectOption
10055
11013
  ).option("--yes", "\uAE30\uC874 \uC5F0\uACB0 \uC804\uCCB4 \uAD50\uCCB4 \uD655\uC778").option("--region <region>", "region override (\uAE30\uBCF8: iaas \uC790\uACA9\uC99D\uBA85\uC758 region)").option("--profile <name>", "\uC0AC\uC6A9\uD560 profile \uC774\uB984").action(async (loadBalancerInput, _opts, cmd) => {
10056
11014
  const opts = cmd.optsWithGlobals();
10057
- const confirmedYes = requireYes(opts.yes, "Load Balancer IP ACL \uC5F0\uACB0 \uAD50\uCCB4");
11015
+ const confirmedYes = requireYes2(opts.yes, "Load Balancer IP ACL \uC5F0\uACB0 \uAD50\uCCB4");
10058
11016
  const parsedLoadBalancer = requireResourceInput(loadBalancerInput, "Load Balancer");
10059
11017
  const parsedGroups = requireResourceInputs(opts.group ?? [], "IP ACL \uADF8\uB8F9");
10060
11018
  const { client } = await resolveLoadBalancerClient(opts);
@@ -10090,7 +11048,7 @@ var setIpAclCommand2 = new import_commander56.Command("set-ipacl").description("
10090
11048
  });
10091
11049
  var clearIpAclCommand = new import_commander56.Command("clear-ipacl").description("Load Balancer\uC758 \uBAA8\uB4E0 IP ACL \uADF8\uB8F9 \uC5F0\uACB0\uC744 \uD574\uC81C\uD55C\uB2E4").argument("<loadbalancer>", "Load Balancer \uC774\uB984 \uB610\uB294 UUID").option("--yes", "\uBAA8\uB4E0 IP ACL \uC5F0\uACB0 \uD574\uC81C \uD655\uC778").option("--region <region>", "region override (\uAE30\uBCF8: iaas \uC790\uACA9\uC99D\uBA85\uC758 region)").option("--profile <name>", "\uC0AC\uC6A9\uD560 profile \uC774\uB984").action(async (loadBalancerInput, _opts, cmd) => {
10092
11050
  const opts = cmd.optsWithGlobals();
10093
- const confirmedYes = requireYes(opts.yes, "Load Balancer IP ACL \uC5F0\uACB0 \uD574\uC81C");
11051
+ const confirmedYes = requireYes2(opts.yes, "Load Balancer IP ACL \uC5F0\uACB0 \uD574\uC81C");
10094
11052
  const parsedLoadBalancer = requireResourceInput(loadBalancerInput, "Load Balancer");
10095
11053
  const { client } = await resolveLoadBalancerClient(opts);
10096
11054
  startSpinner("Load Balancer IP ACL \uC5F0\uACB0 \uD574\uC81C \uC911...");
@@ -10177,7 +11135,7 @@ Agent workflow:
10177
11135
  3. nhncloud ncs workload get <id> --json
10178
11136
  `;
10179
11137
  var program = new import_commander57.Command();
10180
- program.name("nhncloud").description("NHN Cloud CLI \u2014 AI agent & terminal friendly").version("0.13.0").option("--json", "JSON \uD615\uC2DD\uC73C\uB85C \uCD9C\uB825").option("--quiet", "\uCD5C\uC18C \uCD9C\uB825 (\uC790\uB3D9\uD654\uC6A9)").option("--no-color", "\uC0C9\uC0C1 \uBE44\uD65C\uC131\uD654").option("--request-timeout <sec>", "HTTP \uC694\uCCAD \uD0C0\uC784\uC544\uC6C3 (\uCD08, \uAE30\uBCF8 30, \uBC94\uC704 1~3600). NHNCLOUD_REQUEST_TIMEOUT \uB85C\uB3C4 \uC9C0\uC815").addHelpText("after", rootAgentHints);
11138
+ program.name("nhncloud").description("NHN Cloud CLI \u2014 AI agent & terminal friendly").version("0.14.0").option("--json", "JSON \uD615\uC2DD\uC73C\uB85C \uCD9C\uB825").option("--quiet", "\uCD5C\uC18C \uCD9C\uB825 (\uC790\uB3D9\uD654\uC6A9)").option("--no-color", "\uC0C9\uC0C1 \uBE44\uD65C\uC131\uD654").option("--request-timeout <sec>", "HTTP \uC694\uCCAD \uD0C0\uC784\uC544\uC6C3 (\uCD08, \uAE30\uBCF8 30, \uBC94\uC704 1~3600). NHNCLOUD_REQUEST_TIMEOUT \uB85C\uB3C4 \uC9C0\uC815").addHelpText("after", rootAgentHints);
10181
11139
  program.hook("preAction", () => {
10182
11140
  const opts = program.opts();
10183
11141
  const rawTimeout = opts.requestTimeout ?? process.env["NHNCLOUD_REQUEST_TIMEOUT"];
@@ -10283,7 +11241,9 @@ program.addCommand(skillsCommand);
10283
11241
  program.addCommand(doctorCommand);
10284
11242
  program.addCommand(createCommandsCommand(program));
10285
11243
  program.parseAsync().catch((err) => {
10286
- const message = err instanceof Error ? err.message : String(err);
11244
+ const message = sanitizeMultilineForTerminal(
11245
+ err instanceof Error ? err.message : String(err)
11246
+ );
10287
11247
  const exitCode = err instanceof NhnCloudCliError ? err.exitCode : 1;
10288
11248
  process.stderr.write(import_chalk17.default.red(`\uC624\uB958: ${message}`) + "\n");
10289
11249
  process.exit(exitCode);