@bifos/nhncloud-cli 0.18.0 → 0.20.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
@@ -24,7 +24,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  ));
25
25
 
26
26
  // src/index.ts
27
- var import_commander64 = require("commander");
27
+ var import_commander72 = require("commander");
28
28
  var import_chalk18 = __toESM(require("chalk"));
29
29
 
30
30
  // src/utils/spinner.ts
@@ -333,6 +333,69 @@ async function warnLegacyDeployTargets() {
333
333
  )
334
334
  );
335
335
  }
336
+ async function readJsonState(filePath) {
337
+ let raw;
338
+ try {
339
+ raw = await (0, import_promises.readFile)(filePath, "utf-8");
340
+ } catch (err) {
341
+ const code = err.code;
342
+ if (code === "ENOENT") return { state: "missing" };
343
+ return { state: "unreadable", reason: `\uD30C\uC77C\uC744 \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4 (${code ?? "UNKNOWN"})` };
344
+ }
345
+ try {
346
+ return { state: "ok", parsed: JSON.parse(raw) };
347
+ } catch {
348
+ return { state: "invalid", reason: "JSON \uD615\uC2DD\uC774 \uC544\uB2D9\uB2C8\uB2E4" };
349
+ }
350
+ }
351
+ async function inspectPermissions(filePath) {
352
+ try {
353
+ const { mode } = await (0, import_promises.stat)(filePath);
354
+ if (process.platform === "win32") return "unknown";
355
+ return (mode & 63) === 0 ? "ok" : "too-open";
356
+ } catch {
357
+ return void 0;
358
+ }
359
+ }
360
+ function summarizeProfile(name, value) {
361
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
362
+ return { name, environment: "invalid", blocks: [] };
363
+ }
364
+ const profile = value;
365
+ const env = profile["environment"];
366
+ const environment = env === void 0 ? "real" : env === "gov" ? "gov" : "invalid";
367
+ const blocks = Object.keys(profile).filter((key) => key !== "environment").sort();
368
+ return { name, environment, blocks };
369
+ }
370
+ async function inspectCredentialsFile() {
371
+ const result = await readJsonState(CREDENTIALS_PATH);
372
+ const permissions = result.state === "missing" ? void 0 : await inspectPermissions(CREDENTIALS_PATH);
373
+ const base = { path: CREDENTIALS_PATH, ...permissions ? { permissions } : {}, profiles: [] };
374
+ if (result.state !== "ok") {
375
+ return { ...base, state: result.state, ...result.reason ? { reason: result.reason } : {} };
376
+ }
377
+ if (!isCredentials(result.parsed)) {
378
+ return { ...base, state: "invalid", reason: "version: 1 \uACFC profiles \uD544\uB4DC\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4" };
379
+ }
380
+ const profiles = Object.entries(result.parsed.profiles).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([name, value]) => summarizeProfile(name, value));
381
+ return { ...base, state: "ok", profiles };
382
+ }
383
+ async function inspectConfigFile() {
384
+ const result = await readJsonState(CONFIG_PATH);
385
+ const base = { path: CONFIG_PATH, defaultProfile: null };
386
+ if (result.state !== "ok") {
387
+ return { ...base, state: result.state, ...result.reason ? { reason: result.reason } : {} };
388
+ }
389
+ if (!isConfig(result.parsed)) {
390
+ return { ...base, state: "invalid", reason: "version: 1 \uC774 \uD544\uC694\uD569\uB2C8\uB2E4" };
391
+ }
392
+ const defaultProfile = result.parsed.defaultProfile;
393
+ return {
394
+ ...base,
395
+ state: "ok",
396
+ defaultProfile: typeof defaultProfile === "string" && defaultProfile !== "" ? defaultProfile : null
397
+ };
398
+ }
336
399
 
337
400
  // src/utils/terminal.ts
338
401
  function sanitizeForTerminal(value) {
@@ -400,19 +463,113 @@ function parseNonNegativeIntegerOption(value, flag) {
400
463
 
401
464
  // src/commands/commander-errors.ts
402
465
  var import_commander = require("commander");
403
- function throwWithNormalizedExitCode(error) {
466
+
467
+ // src/commands/unknown-option-hint.ts
468
+ function normalizeName(name) {
469
+ return name.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
470
+ }
471
+ function parseUnknownOptionName(message) {
472
+ return /unknown option '--([^'=]+)/.exec(message)?.[1];
473
+ }
474
+ function findReferencedArgument(optionName, commandPath2, argumentNames) {
475
+ const option = normalizeName(optionName);
476
+ const normalized = argumentNames.map(normalizeName);
477
+ const exact = normalized.indexOf(option);
478
+ if (exact !== -1) {
479
+ return argumentNames[exact];
480
+ }
481
+ const prefixed = argumentNames.filter((_, i) => normalized[i].startsWith(`${option}-`));
482
+ const candidates = new Set(prefixed.length === 1 ? prefixed : []);
483
+ const segments = commandPath2.slice(1).map(normalizeName);
484
+ argumentNames.forEach((name, i) => {
485
+ const otherArguments = normalized.filter((_, j) => j !== i);
486
+ const matched = segments.some(
487
+ (segment) => !otherArguments.includes(segment) && option === `${segment}-${normalized[i]}`
488
+ );
489
+ if (matched) {
490
+ candidates.add(name);
491
+ }
492
+ });
493
+ return candidates.size === 1 ? [...candidates][0] : void 0;
494
+ }
495
+ function formatArgument(argument) {
496
+ const name = argument.variadic ? `${argument.name}...` : argument.name;
497
+ return argument.required ? `<${name}>` : `[${name}]`;
498
+ }
499
+ function buildUnknownOptionHint(input) {
500
+ if (input.message.includes("(Did you mean")) {
501
+ return "";
502
+ }
503
+ const optionName = parseUnknownOptionName(input.message);
504
+ if (optionName === void 0) {
505
+ return "";
506
+ }
507
+ const referenced = findReferencedArgument(
508
+ optionName,
509
+ input.commandPath,
510
+ input.arguments.map((argument2) => argument2.name)
511
+ );
512
+ const argument = input.arguments.find((candidate) => candidate.name === referenced);
513
+ if (argument === void 0) {
514
+ return "";
515
+ }
516
+ return `\uC548\uB0B4: --${optionName} \uB294 \uC635\uC158\uC774 \uC544\uB2D9\uB2C8\uB2E4. \uC704\uCE58 \uC778\uC218 ${formatArgument(argument)} \uB85C \uC804\uB2EC\uD558\uC138\uC694.
517
+ \uC0AC\uC6A9\uBC95: ${input.commandPath.join(" ")} ${input.usage}
518
+ `;
519
+ }
520
+
521
+ // src/commands/commander-errors.ts
522
+ function writeUnknownOptionHint(command, error) {
523
+ const commandPath2 = [];
524
+ for (let current2 = command; current2 !== null; current2 = current2.parent) {
525
+ commandPath2.unshift(current2.name());
526
+ }
527
+ const hint = buildUnknownOptionHint({
528
+ message: error.message,
529
+ commandPath: commandPath2,
530
+ usage: command.usage(),
531
+ arguments: command.registeredArguments.map((argument) => ({
532
+ name: argument.name(),
533
+ required: argument.required,
534
+ variadic: argument.variadic
535
+ }))
536
+ });
537
+ if (hint === "") {
538
+ return;
539
+ }
540
+ const writeErr = command.configureOutput().writeErr;
541
+ if (writeErr) {
542
+ writeErr(hint);
543
+ } else {
544
+ process.stderr.write(hint);
545
+ }
546
+ }
547
+ function handleCommanderError(command, error) {
404
548
  if (error.code === "commander.missingMandatoryOptionValue") {
405
549
  error.exitCode = EXIT_PARAM_ERROR;
550
+ } else if (error.code === "commander.unknownOption") {
551
+ writeUnknownOptionHint(command, error);
406
552
  }
407
553
  throw error;
408
554
  }
409
555
  function configureCommanderExitCodes(root) {
410
- root.exitOverride(throwWithNormalizedExitCode);
556
+ root.exitOverride((error) => handleCommanderError(root, error));
411
557
  for (const command of root.commands) {
412
558
  configureCommanderExitCodes(command);
413
559
  }
414
560
  }
415
561
 
562
+ // src/commands/help.ts
563
+ function configureGlobalOptionsHelp(root) {
564
+ root.configureHelp({ showGlobalOptions: true });
565
+ for (const child of root.commands) {
566
+ configureGlobalOptionsHelp(child);
567
+ }
568
+ }
569
+
570
+ // src/version.ts
571
+ var CLI_VERSION = true ? "0.20.0" : "0.0.0-dev";
572
+
416
573
  // src/commands/configure.ts
417
574
  var import_commander2 = require("commander");
418
575
  var import_chalk2 = __toESM(require("chalk"));
@@ -517,6 +674,13 @@ async function writeToken(profile, accessToken, expiresAt, credentialHash) {
517
674
 
518
675
  // src/api/httpError.ts
519
676
  var import_ky = require("ky");
677
+ async function readHttpErrorBody(err) {
678
+ try {
679
+ return await err.response.clone().json();
680
+ } catch {
681
+ return void 0;
682
+ }
683
+ }
520
684
  function toNhnCloudCliError(err) {
521
685
  if (err instanceof NhnCloudCliError) {
522
686
  return err;
@@ -592,10 +756,12 @@ var import_ky3 = __toESM(require("ky"));
592
756
  var ENDPOINTS = {
593
757
  logncrash: "https://api-lncs-search.nhncloudservice.com",
594
758
  "logncrash-collector": "https://api-logncrash.nhncloudservice.com",
595
- deploy: "https://api-deploy.nhncloudservice.com"
759
+ deploy: "https://api-deploy.nhncloudservice.com",
760
+ skm: "https://api-keymanager.nhncloudservice.com"
596
761
  };
597
762
  var GOV_ENDPOINTS = {
598
- deploy: "https://api-tcd.gov-nhncloudservice.com"
763
+ deploy: "https://api-tcd.gov-nhncloudservice.com",
764
+ skm: "https://api-keymanager.gov-nhncloudservice.com"
599
765
  };
600
766
  function endpointFor(service, environment = "real") {
601
767
  const endpoints = environment === "gov" ? GOV_ENDPOINTS : ENDPOINTS;
@@ -875,12 +1041,9 @@ async function toLogncrashError(err) {
875
1041
  return toNhnCloudCliError(err);
876
1042
  }
877
1043
  let requestId = null;
878
- try {
879
- const body = await err.response.json();
880
- if (typeof body === "object" && body !== null && "requestId" in body && typeof body.requestId === "string") {
881
- requestId = body.requestId;
882
- }
883
- } catch {
1044
+ const body = await readHttpErrorBody(err);
1045
+ if (typeof body === "object" && body !== null && "requestId" in body && typeof body.requestId === "string") {
1046
+ requestId = body.requestId;
884
1047
  }
885
1048
  return new LogncrashServerError(
886
1049
  `API \uD638\uCD9C \uC2E4\uD328 (500): ${err.message}`,
@@ -2009,7 +2172,7 @@ async function verifyLogncrash(uak, appkey) {
2009
2172
  }
2010
2173
 
2011
2174
  // src/commands/configure.ts
2012
- async function saveAndVerify(profileName, uak, logncrash, iaas, ncr, ncs, apigateway, deploy, doVerify, logncrashUak = uak) {
2175
+ async function saveAndVerify(profileName, uak, logncrash, iaas, ncr, ncs, apigateway, skm, deploy, doVerify, logncrashUak = uak) {
2013
2176
  if (doVerify) {
2014
2177
  if (await getProfileEnvironment(profileName, true) === "gov") {
2015
2178
  throw new NhnCloudCliError(
@@ -2115,6 +2278,9 @@ async function saveAndVerify(profileName, uak, logncrash, iaas, ncr, ncs, apigat
2115
2278
  if (apigateway) {
2116
2279
  await setServiceCredential(profileName, "apigateway", apigateway);
2117
2280
  }
2281
+ if (skm) {
2282
+ await setServiceCredential(profileName, "skm", skm);
2283
+ }
2118
2284
  if (deploy) {
2119
2285
  await setServiceCredential(profileName, "deploy", deploy);
2120
2286
  }
@@ -2244,7 +2410,7 @@ async function runInteractive(opts) {
2244
2410
  }
2245
2411
  if (opts.verify) {
2246
2412
  try {
2247
- await saveAndVerify(profileName, uak, logncrash, iaas, ncr, ncs, void 0, deploy, true, uak);
2413
+ await saveAndVerify(profileName, uak, logncrash, iaas, ncr, ncs, void 0, void 0, deploy, true, uak);
2248
2414
  } catch (err) {
2249
2415
  if (err instanceof NhnCloudCliError && err.exitCode === EXIT_AUTH_ERROR) {
2250
2416
  process.stderr.write(import_chalk2.default.red(` \u2717 ${sanitizeMultilineForTerminal(err.message)}
@@ -2257,13 +2423,13 @@ async function runInteractive(opts) {
2257
2423
  process.stderr.write(import_chalk2.default.yellow("\uC800\uC7A5\uC774 \uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.\n"));
2258
2424
  return;
2259
2425
  }
2260
- await saveAndVerify(profileName, uak, logncrash, iaas, ncr, ncs, void 0, deploy, false, uak);
2426
+ await saveAndVerify(profileName, uak, logncrash, iaas, ncr, ncs, void 0, void 0, deploy, false, uak);
2261
2427
  } else {
2262
2428
  throw err;
2263
2429
  }
2264
2430
  }
2265
2431
  } else {
2266
- await saveAndVerify(profileName, uak, logncrash, iaas, ncr, ncs, void 0, deploy, false, uak);
2432
+ await saveAndVerify(profileName, uak, logncrash, iaas, ncr, ncs, void 0, void 0, deploy, false, uak);
2267
2433
  }
2268
2434
  }
2269
2435
  async function runNonInteractive(opts) {
@@ -2287,6 +2453,9 @@ async function runNonInteractive(opts) {
2287
2453
  if (opts.apigatewayAppkey !== void 0 && opts.apigatewayAppkey.trim().length === 0) {
2288
2454
  throw new NhnCloudCliError("--apigateway-appkey \uAC12\uC740 \uBE44\uC5B4 \uC788\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.", EXIT_PARAM_ERROR);
2289
2455
  }
2456
+ if (opts.skmAppkey !== void 0 && opts.skmAppkey.trim().length === 0) {
2457
+ throw new NhnCloudCliError("--skm-appkey \uAC12\uC740 \uBE44\uC5B4 \uC788\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.", EXIT_PARAM_ERROR);
2458
+ }
2290
2459
  if (opts.deployAppkey !== void 0 && opts.deployAppkey.trim().length === 0) {
2291
2460
  throw new NhnCloudCliError("--deploy-appkey \uAC12\uC740 \uBE44\uC5B4 \uC788\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.", EXIT_PARAM_ERROR);
2292
2461
  }
@@ -2309,10 +2478,11 @@ async function runNonInteractive(opts) {
2309
2478
  const ncr = opts.ncrAppkey?.trim() ? { appkey: opts.ncrAppkey.trim() } : void 0;
2310
2479
  const ncs = opts.ncsAppkey?.trim() ? { appkey: opts.ncsAppkey.trim() } : void 0;
2311
2480
  const apigateway = opts.apigatewayAppkey?.trim() ? { appkey: opts.apigatewayAppkey.trim() } : void 0;
2481
+ const skm = opts.skmAppkey?.trim() ? { appkey: opts.skmAppkey.trim() } : void 0;
2312
2482
  const deploy = opts.deployAppkey?.trim() ? { appkey: opts.deployAppkey.trim() } : void 0;
2313
- if (!uak && !logncrash && !iaas && !ncr && !ncs && !apigateway && !deploy) {
2483
+ if (!uak && !logncrash && !iaas && !ncr && !ncs && !apigateway && !skm && !deploy) {
2314
2484
  throw new NhnCloudCliError(
2315
- "\uBE44\uB300\uD654\uD615 \uBAA8\uB4DC: --uak-id + UAK secret, --logncrash-appkey,\n--iaas-tenant-id + --iaas-username + iaas password,\n--ncr-appkey, --ncs-appkey, --apigateway-appkey, \uB610\uB294 --deploy-appkey\n\uC911 \uD558\uB098\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.\nsecret/password \uB294 \uB178\uCD9C \uBC29\uC9C0\uB97C \uC704\uD574 \uD658\uACBD\uBCC0\uC218 \uAD8C\uC7A5:\nNHNCLOUD_UAK_SECRET / NHNCLOUD_IAAS_PASSWORD.",
2485
+ "\uBE44\uB300\uD654\uD615 \uBAA8\uB4DC: --uak-id + UAK secret, --logncrash-appkey,\n--iaas-tenant-id + --iaas-username + iaas password,\n--ncr-appkey, --ncs-appkey, --apigateway-appkey, --skm-appkey, \uB610\uB294 --deploy-appkey\n\uC911 \uD558\uB098\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.\nsecret/password \uB294 \uB178\uCD9C \uBC29\uC9C0\uB97C \uC704\uD574 \uD658\uACBD\uBCC0\uC218 \uAD8C\uC7A5:\nNHNCLOUD_UAK_SECRET / NHNCLOUD_IAAS_PASSWORD.",
2316
2486
  EXIT_PARAM_ERROR
2317
2487
  );
2318
2488
  }
@@ -2331,6 +2501,7 @@ async function runNonInteractive(opts) {
2331
2501
  ncr,
2332
2502
  ncs,
2333
2503
  apigateway,
2504
+ skm,
2334
2505
  deploy,
2335
2506
  opts.verify,
2336
2507
  logncrashUak
@@ -2339,8 +2510,8 @@ async function runNonInteractive(opts) {
2339
2510
  var configureCommand = new import_commander2.Command("configure").description("\uC790\uACA9\uC99D\uBA85 \uC124\uC815 \uB9C8\uBC95\uC0AC (\uB300\uD654\uD615 + flag)").option("--profile <name>", "\uB300\uC0C1 profile \uC774\uB984 (\uAE30\uBCF8: default)").option("--uak-id <id>", "\uAC1C\uC778 UAK ID (\uBE44\uB300\uD654\uD615)").option("--uak-secret <secret>", "\uAC1C\uC778 UAK Secret (\uBE44\uB300\uD654\uD615, \uB178\uCD9C \uBC29\uC9C0\uB85C env NHNCLOUD_UAK_SECRET \uAD8C\uC7A5)").option("--logncrash-appkey <key>", "logncrash appkey (\uBE44\uB300\uD654\uD615)").option("--logncrash-secret <secret>", "\uD3D0\uAE30 \uC608\uC815: Search v3\uC5D0\uC11C\uB294 \uC0AC\uC6A9\xB7\uC800\uC7A5\uD558\uC9C0 \uC54A\uC74C").option("--iaas-tenant-id <id>", "iaas tenantId / \uD504\uB85C\uC81D\uD2B8 ID (\uBE44\uB300\uD654\uD615)").option("--iaas-username <user>", "iaas IAM username (\uBE44\uB300\uD654\uD615)").option(
2340
2511
  "--iaas-password <pass>",
2341
2512
  "iaas API \uBE44\uBC00\uBC88\uD638 (\uBE44\uB300\uD654\uD615, \uB178\uCD9C \uBC29\uC9C0\uB85C env NHNCLOUD_IAAS_PASSWORD \uAD8C\uC7A5)"
2342
- ).option("--iaas-region <region>", "iaas region (\uAE30\uBCF8: kr1)", "kr1").option("--ncr-appkey <key>", "ncr appkey (\uBE44\uB300\uD654\uD615)").option("--ncs-appkey <key>", "ncs appkey (\uBE44\uB300\uD654\uD615)").option("--apigateway-appkey <key>", "API Gateway appkey (\uBE44\uB300\uD654\uD615)").option("--deploy-appkey <key>", "deploy appkey (\uBE44\uB300\uD654\uD615)").option("--no-verify", "\uC5F0\uACB0 \uD14C\uC2A4\uD2B8 \uC0DD\uB7B5").action(async (opts) => {
2343
- const hasFlag = opts.uakId !== void 0 || opts.uakSecret !== void 0 || opts.logncrashAppkey !== void 0 || opts.logncrashSecret !== void 0 || opts.iaasTenantId !== void 0 || opts.iaasUsername !== void 0 || opts.iaasPassword !== void 0 || opts.ncrAppkey !== void 0 || opts.ncsAppkey !== void 0 || opts.apigatewayAppkey !== void 0 || opts.deployAppkey !== void 0;
2513
+ ).option("--iaas-region <region>", "iaas region (\uAE30\uBCF8: kr1)", "kr1").option("--ncr-appkey <key>", "ncr appkey (\uBE44\uB300\uD654\uD615)").option("--ncs-appkey <key>", "ncs appkey (\uBE44\uB300\uD654\uD615)").option("--apigateway-appkey <key>", "API Gateway appkey (\uBE44\uB300\uD654\uD615)").option("--skm-appkey <key>", "Secure Key Manager appkey (\uBE44\uB300\uD654\uD615)").option("--deploy-appkey <key>", "deploy appkey (\uBE44\uB300\uD654\uD615)").option("--no-verify", "\uC5F0\uACB0 \uD14C\uC2A4\uD2B8 \uC0DD\uB7B5").action(async (opts) => {
2514
+ const hasFlag = opts.uakId !== void 0 || opts.uakSecret !== void 0 || opts.logncrashAppkey !== void 0 || opts.logncrashSecret !== void 0 || opts.iaasTenantId !== void 0 || opts.iaasUsername !== void 0 || opts.iaasPassword !== void 0 || opts.ncrAppkey !== void 0 || opts.ncsAppkey !== void 0 || opts.apigatewayAppkey !== void 0 || opts.skmAppkey !== void 0 || opts.deployAppkey !== void 0;
2344
2515
  try {
2345
2516
  if (hasFlag) {
2346
2517
  await runNonInteractive(opts);
@@ -2498,14 +2669,14 @@ function compareByCodePoint(left, right) {
2498
2669
  return leftPoints.length - rightPoints.length;
2499
2670
  }
2500
2671
  function ensureRegularFile(filePath) {
2501
- let stat3;
2672
+ let stat4;
2502
2673
  try {
2503
- stat3 = (0, import_node_fs2.lstatSync)(filePath);
2674
+ stat4 = (0, import_node_fs2.lstatSync)(filePath);
2504
2675
  } catch (error) {
2505
2676
  const reason = error instanceof Error ? error.message : String(error);
2506
2677
  throw new NhnCloudCliError(`\uC2A4\uD0AC \uD30C\uC77C\uC744 \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${filePath} (${reason})`, EXIT_PARAM_ERROR);
2507
2678
  }
2508
- if (stat3.isSymbolicLink() || !stat3.isFile()) {
2679
+ if (stat4.isSymbolicLink() || !stat4.isFile()) {
2509
2680
  throw new NhnCloudCliError(`\uC2A4\uD0AC \uCF58\uD150\uCE20\uB294 \uC815\uADDC \uD30C\uC77C\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4: ${filePath}`, EXIT_PARAM_ERROR);
2510
2681
  }
2511
2682
  }
@@ -2522,21 +2693,21 @@ function collectReferenceFiles(referencesRoot, currentDir, files) {
2522
2693
  }
2523
2694
  for (const entry of entries) {
2524
2695
  const entryPath = import_node_path4.default.join(currentDir, entry.name);
2525
- let stat3;
2696
+ let stat4;
2526
2697
  try {
2527
- stat3 = (0, import_node_fs2.lstatSync)(entryPath);
2698
+ stat4 = (0, import_node_fs2.lstatSync)(entryPath);
2528
2699
  } catch (error) {
2529
2700
  const reason = error instanceof Error ? error.message : String(error);
2530
2701
  throw new NhnCloudCliError(`\uC2A4\uD0AC \uCF58\uD150\uCE20\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${entryPath} (${reason})`, EXIT_PARAM_ERROR);
2531
2702
  }
2532
- if (stat3.isSymbolicLink()) {
2703
+ if (stat4.isSymbolicLink()) {
2533
2704
  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);
2534
2705
  }
2535
- if (stat3.isDirectory()) {
2706
+ if (stat4.isDirectory()) {
2536
2707
  collectReferenceFiles(referencesRoot, entryPath, files);
2537
2708
  continue;
2538
2709
  }
2539
- if (!stat3.isFile()) {
2710
+ if (!stat4.isFile()) {
2540
2711
  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);
2541
2712
  }
2542
2713
  files.push(`references/${import_node_path4.default.relative(referencesRoot, entryPath).split(import_node_path4.default.sep).join("/")}`);
@@ -2584,12 +2755,24 @@ function calculateSkillContentDigest(skillRoot) {
2584
2755
  var PACKAGE_NAME3 = "@bifos/nhncloud-cli";
2585
2756
  var SKILL_NAME2 = "nhncloud-cli";
2586
2757
  var DIGEST_HEX_PATTERN = /^[0-9a-f]{64}$/;
2587
- var defaultOperations = { rename: import_promises3.rename };
2758
+ var SKILL_AGENTS = ["claude", "codex"];
2759
+ var SKILL_AGENT_NAMES = { claude: "Claude Code", codex: "Codex" };
2760
+ var STATUS_PRIORITY = [
2761
+ "corrupt",
2762
+ "modified",
2763
+ "unmanaged",
2764
+ "broken",
2765
+ "outdated",
2766
+ "missing",
2767
+ "current"
2768
+ ];
2769
+ var PROTECTED_STATUSES = ["unmanaged", "modified", "corrupt"];
2770
+ var defaultOperations = { rename: import_promises3.rename, rm: import_promises3.rm };
2588
2771
  function sourcePath(context) {
2589
2772
  return import_node_path5.default.join(context.packageRoot, "skills", SKILL_NAME2);
2590
2773
  }
2591
- function destinationPath(context) {
2592
- return import_node_path5.default.join(context.homeDir, ".claude", "skills", SKILL_NAME2);
2774
+ function destinationPath(context, agent) {
2775
+ return agent === "claude" ? import_node_path5.default.join(context.homeDir, ".claude", "skills", SKILL_NAME2) : import_node_path5.default.join(context.homeDir, ".agents", "skills", SKILL_NAME2);
2593
2776
  }
2594
2777
  function resolveLinkTarget(linkPath, rawTarget) {
2595
2778
  return import_node_path5.default.isAbsolute(rawTarget) ? import_node_path5.default.normalize(rawTarget) : import_node_path5.default.resolve(import_node_path5.default.dirname(linkPath), rawTarget);
@@ -2700,16 +2883,74 @@ async function inspectRepository(targetPath, expected) {
2700
2883
  }
2701
2884
  return { status: "valid", installedVersion: manifest.packageVersion };
2702
2885
  }
2703
- function statusBase(context) {
2886
+ function statusBase(context, agent) {
2704
2887
  return {
2705
2888
  schemaVersion: 1,
2706
- destination: destinationPath(context),
2889
+ destination: destinationPath(context, agent),
2707
2890
  source: sourcePath(context),
2708
2891
  currentVersion: context.currentVersion
2709
2892
  };
2710
2893
  }
2711
- async function inspectSkill(context) {
2712
- const base = statusBase(context);
2894
+ async function managedRepositoryStatus(context, base, linkTarget, repositoryLocation, isExpectedPath) {
2895
+ const repositoryName = parseRepositoryName(repositoryLocation);
2896
+ if (!repositoryName) {
2897
+ return { ...base, status: "corrupt", linkTarget, managed: true };
2898
+ }
2899
+ const repository = await inspectRepository(repositoryLocation, repositoryName);
2900
+ if (repository.status === "modified" || repository.status === "corrupt") {
2901
+ return {
2902
+ ...base,
2903
+ status: repository.status,
2904
+ installedVersion: repository.installedVersion ?? repositoryName.version,
2905
+ linkTarget,
2906
+ managed: true
2907
+ };
2908
+ }
2909
+ if (repository.status === "missing") {
2910
+ return {
2911
+ ...base,
2912
+ status: "broken",
2913
+ installedVersion: repositoryName.version,
2914
+ linkTarget,
2915
+ managed: true
2916
+ };
2917
+ }
2918
+ const currentDigest = calculateSkillContentDigest(base.source);
2919
+ const isCurrent = repositoryName.version === context.currentVersion && repositoryName.digest === currentDigest && await isExpectedPath(repositoryPath(context, currentDigest));
2920
+ return {
2921
+ ...base,
2922
+ status: isCurrent ? "current" : "outdated",
2923
+ installedVersion: repository.installedVersion,
2924
+ linkTarget,
2925
+ managed: true
2926
+ };
2927
+ }
2928
+ async function optionalRealpath(targetPath) {
2929
+ try {
2930
+ return await (0, import_promises3.realpath)(targetPath);
2931
+ } catch (error) {
2932
+ if (isNodeError(error, "ENOENT")) {
2933
+ return void 0;
2934
+ }
2935
+ throw managerError(`\uC2A4\uD0AC \uACBD\uB85C\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${targetPath}`, error);
2936
+ }
2937
+ }
2938
+ async function inspectIndirectManagedLink(context, base, linkTarget) {
2939
+ const realRoot = await optionalRealpath(repositoryRoot(context));
2940
+ if (!realRoot) {
2941
+ return void 0;
2942
+ }
2943
+ const realTarget = await optionalRealpath(linkTarget);
2944
+ if (!realTarget || import_node_path5.default.dirname(realTarget) !== realRoot) {
2945
+ return void 0;
2946
+ }
2947
+ return managedRepositoryStatus(context, base, linkTarget, realTarget, async (expectedPath) => {
2948
+ const realExpected = await optionalRealpath(expectedPath);
2949
+ return realExpected !== void 0 && realExpected === realTarget;
2950
+ });
2951
+ }
2952
+ async function inspectAgentSkill(context, agent) {
2953
+ const base = statusBase(context, agent);
2713
2954
  const destinationStat = await optionalLstat(base.destination);
2714
2955
  if (!destinationStat) {
2715
2956
  return { ...base, status: "missing", managed: false };
@@ -2742,38 +2983,17 @@ async function inspectSkill(context) {
2742
2983
  };
2743
2984
  }
2744
2985
  if (isManagedRepositoryLocation(context, linkTarget)) {
2745
- if (!repositoryName) {
2746
- return { ...base, status: "corrupt", linkTarget, managed: true };
2747
- }
2748
- const repository = await inspectRepository(linkTarget, repositoryName);
2749
- if (repository.status === "modified" || repository.status === "corrupt") {
2750
- return {
2751
- ...base,
2752
- status: repository.status,
2753
- installedVersion: repository.installedVersion ?? repositoryName.version,
2754
- linkTarget,
2755
- managed: true
2756
- };
2757
- }
2758
- if (repository.status === "missing") {
2759
- return {
2760
- ...base,
2761
- status: "broken",
2762
- installedVersion: repositoryName.version,
2763
- linkTarget,
2764
- managed: true
2765
- };
2766
- }
2767
- const currentDigest = calculateSkillContentDigest(base.source);
2768
- const expectedPath = repositoryPath(context, currentDigest);
2769
- const isCurrent = repositoryName.version === context.currentVersion && repositoryName.digest === currentDigest && import_node_path5.default.resolve(linkTarget) === import_node_path5.default.resolve(expectedPath);
2770
- return {
2771
- ...base,
2772
- status: isCurrent ? "current" : "outdated",
2773
- installedVersion: repository.installedVersion,
2986
+ return managedRepositoryStatus(
2987
+ context,
2988
+ base,
2774
2989
  linkTarget,
2775
- managed: true
2776
- };
2990
+ linkTarget,
2991
+ async (expectedPath) => import_node_path5.default.resolve(linkTarget) === import_node_path5.default.resolve(expectedPath)
2992
+ );
2993
+ }
2994
+ const indirect = await inspectIndirectManagedLink(context, base, linkTarget);
2995
+ if (indirect) {
2996
+ return indirect;
2777
2997
  }
2778
2998
  const legacyPackage = targetStat.isDirectory() ? await readLegacyPackageMetadata(linkTarget) : void 0;
2779
2999
  if (legacyPackage) {
@@ -2787,6 +3007,14 @@ async function inspectSkill(context) {
2787
3007
  }
2788
3008
  return { ...base, status: "unmanaged", linkTarget, managed: false };
2789
3009
  }
3010
+ function aggregateStatus(statuses) {
3011
+ return STATUS_PRIORITY.find((token) => statuses.some((status) => status.status === token)) ?? "current";
3012
+ }
3013
+ async function inspectSkill(context) {
3014
+ const claude = await inspectAgentSkill(context, "claude");
3015
+ const codex = await inspectAgentSkill(context, "codex");
3016
+ return { ...claude, status: aggregateStatus([claude, codex]), agents: { claude, codex } };
3017
+ }
2790
3018
  function utcBackupSuffix() {
2791
3019
  return `${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}-${(0, import_node_crypto3.randomUUID)()}`;
2792
3020
  }
@@ -2883,35 +3111,123 @@ async function prepareRepository(context, force, operations) {
2883
3111
  await (0, import_promises3.rm)(staging, { recursive: true, force: true });
2884
3112
  }
2885
3113
  }
2886
- async function switchActiveLink(context, repository, previousStatus, force, operations) {
2887
- const destination = destinationPath(context);
2888
- const parent = import_node_path5.default.dirname(destination);
2889
- await (0, import_promises3.mkdir)(parent, { recursive: true });
2890
- const temporaryLink = import_node_path5.default.join(parent, `.${SKILL_NAME2}.link-${(0, import_node_crypto3.randomUUID)()}`);
2891
- const backupPaths = [];
2892
- await (0, import_promises3.symlink)(repository, temporaryLink);
2893
- try {
2894
- if (previousStatus.status === "unmanaged") {
2895
- if (!force) {
2896
- throw managerError(`\uAD00\uB9AC\uB418\uC9C0 \uC54A\uC740 \uC2A4\uD0AC \uD56D\uBAA9\uC774 \uC788\uC2B5\uB2C8\uB2E4. --force\uB85C \uBC31\uC5C5 \uD6C4 \uAD50\uCCB4\uD558\uC138\uC694: ${destination}`);
2897
- }
2898
- const backup = await backupPath(destination, operations);
2899
- backupPaths.push(backup);
3114
+ function temporaryLinkPath(destination) {
3115
+ return import_node_path5.default.join(import_node_path5.default.dirname(destination), `.${SKILL_NAME2}.link-${(0, import_node_crypto3.randomUUID)()}`);
3116
+ }
3117
+ async function planLinkSwitches(context, previous) {
3118
+ const switches = [];
3119
+ for (const agent of SKILL_AGENTS) {
3120
+ const agentStatus = previous.agents[agent];
3121
+ if (agentStatus.status === "current") {
3122
+ continue;
3123
+ }
3124
+ const destination = destinationPath(context, agent);
3125
+ const parent = import_node_path5.default.dirname(destination);
3126
+ await (0, import_promises3.mkdir)(parent, { recursive: true });
3127
+ const resolvedDestination = import_node_path5.default.join(await (0, import_promises3.realpath)(parent), import_node_path5.default.basename(destination));
3128
+ const sameTarget = switches.find((entry) => entry.resolvedDestination === resolvedDestination);
3129
+ if (sameTarget) {
3130
+ sameTarget.agents.push(agent);
3131
+ continue;
3132
+ }
3133
+ let previousRawTarget;
3134
+ if (agentStatus.status !== "missing" && agentStatus.status !== "unmanaged") {
2900
3135
  try {
2901
- await operations.rename(temporaryLink, destination);
3136
+ previousRawTarget = await (0, import_promises3.readlink)(destination);
2902
3137
  } catch (error) {
2903
- return await restoreBackup(backup, destination, operations, error);
3138
+ throw managerError(`\uC2A4\uD0AC \uB9C1\uD06C\uB97C \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${destination}`, error);
3139
+ }
3140
+ }
3141
+ switches.push({
3142
+ agents: [agent],
3143
+ destination,
3144
+ resolvedDestination,
3145
+ previous: agentStatus,
3146
+ previousRawTarget,
3147
+ temporaryLink: temporaryLinkPath(destination),
3148
+ activated: false
3149
+ });
3150
+ }
3151
+ return switches;
3152
+ }
3153
+ async function rollbackLinkSwitches(switches, operations, temporaryLinks) {
3154
+ const failed = [];
3155
+ const preservedBackups = [];
3156
+ const removeEntry = operations.rm ?? defaultOperations.rm;
3157
+ let firstError;
3158
+ for (const entry of [...switches].reverse()) {
3159
+ try {
3160
+ if (entry.activated && entry.backup) {
3161
+ await removeEntry(entry.destination, { force: true });
3162
+ await operations.rename(entry.backup, entry.destination);
3163
+ } else if (entry.activated && entry.previousRawTarget !== void 0) {
3164
+ const restoreLink = temporaryLinkPath(entry.destination);
3165
+ temporaryLinks.push(restoreLink);
3166
+ await (0, import_promises3.symlink)(entry.previousRawTarget, restoreLink);
3167
+ await operations.rename(restoreLink, entry.destination);
3168
+ } else if (entry.activated && entry.previous.status === "missing") {
3169
+ await removeEntry(entry.destination, { force: true });
3170
+ } else if (!entry.activated && entry.backup) {
3171
+ await operations.rename(entry.backup, entry.destination);
3172
+ }
3173
+ } catch (error) {
3174
+ failed.push(entry.destination);
3175
+ if (entry.backup) {
3176
+ preservedBackups.push(entry.backup);
2904
3177
  }
2905
- return backupPaths;
3178
+ firstError ??= error;
2906
3179
  }
3180
+ }
3181
+ return { failed, preservedBackups, firstError };
3182
+ }
3183
+ async function switchActiveLinks(context, repository, previous, operations) {
3184
+ const switches = await planLinkSwitches(context, previous);
3185
+ const temporaryLinks = switches.map((entry) => entry.temporaryLink);
3186
+ const backupPaths = [];
3187
+ try {
3188
+ let failure;
3189
+ let failedAfterVerification = false;
2907
3190
  try {
2908
- await operations.rename(temporaryLink, destination);
3191
+ for (const entry of switches) {
3192
+ await (0, import_promises3.symlink)(repository, entry.temporaryLink);
3193
+ }
3194
+ for (const entry of switches) {
3195
+ if (entry.previous.status === "unmanaged") {
3196
+ entry.backup = await backupPath(entry.destination, operations);
3197
+ backupPaths.push(entry.backup);
3198
+ }
3199
+ await operations.rename(entry.temporaryLink, entry.destination);
3200
+ entry.activated = true;
3201
+ }
3202
+ const status = await inspectSkill(context);
3203
+ if (status.status === "current") {
3204
+ return { backupPaths, status };
3205
+ }
3206
+ failure = managerError(`\uC2A4\uD0AC \uC124\uCE58 \uD6C4 \uC0C1\uD0DC\uAC00 current\uAC00 \uC544\uB2D9\uB2C8\uB2E4: ${status.status}`);
3207
+ failedAfterVerification = true;
2909
3208
  } catch (error) {
2910
- throw managerError(`\uD65C\uC131 \uC2A4\uD0AC \uB9C1\uD06C\uB97C \uC804\uD658\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${destination}`, error);
3209
+ failure = error;
2911
3210
  }
2912
- return backupPaths;
3211
+ const rollback = await rollbackLinkSwitches(switches, operations, temporaryLinks);
3212
+ if (rollback.failed.length > 0) {
3213
+ throw managerError(
3214
+ `\uC2A4\uD0AC \uC804\uD658\uC5D0 \uC2E4\uD328\uD588\uACE0 \uC77C\uBD80 \uACBD\uB85C\uB97C \uB418\uB3CC\uB9AC\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4: ${rollback.failed.join(", ")}; \uBCF4\uC874\uD55C \uBC31\uC5C5: ${rollback.preservedBackups.join(", ") || "\uC5C6\uC74C"}; \uC804\uD658 \uC624\uB958: ${toReason(failure)}`,
3215
+ rollback.firstError
3216
+ );
3217
+ }
3218
+ if (failedAfterVerification) {
3219
+ throw failure;
3220
+ }
3221
+ const changed = switches.filter((entry) => entry.activated || entry.backup !== void 0);
3222
+ if (changed.length === 0) {
3223
+ throw managerError("\uC2A4\uD0AC \uC804\uD658\uC744 \uC2DC\uC791\uD558\uC9C0 \uBABB\uD574 \uC5B4\uB5A4 \uACBD\uB85C\uB3C4 \uBC14\uAFB8\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4", failure);
3224
+ }
3225
+ throw managerError(
3226
+ `\uC2A4\uD0AC \uC804\uD658\uC5D0 \uC2E4\uD328\uD574 \uBC14\uAFBC \uACBD\uB85C\uB97C \uC774\uC804 \uC0C1\uD0DC\uB85C \uB418\uB3CC\uB838\uC2B5\uB2C8\uB2E4: ${changed.map((entry) => entry.destination).join(", ")}`,
3227
+ failure
3228
+ );
2913
3229
  } finally {
2914
- await (0, import_promises3.rm)(temporaryLink, { force: true });
3230
+ await Promise.all(temporaryLinks.map((link) => (0, import_promises3.rm)(link, { force: true })));
2915
3231
  }
2916
3232
  }
2917
3233
  function installAction(status) {
@@ -2946,23 +3262,26 @@ async function installSkillInternal(context, options = {}, operations = defaultO
2946
3262
  backupPaths: []
2947
3263
  };
2948
3264
  }
2949
- if ((previousStatus.status === "unmanaged" || previousStatus.status === "modified" || previousStatus.status === "corrupt") && !force) {
2950
- throw managerError(`\uC2A4\uD0AC \uC0C1\uD0DC\uAC00 ${previousStatus.status}\uC785\uB2C8\uB2E4. --force\uB85C \uBC31\uC5C5 \uD6C4 \uAD50\uCCB4\uD558\uC138\uC694: ${previousStatus.destination}`);
3265
+ if (!force) {
3266
+ for (const agent of SKILL_AGENTS) {
3267
+ const agentStatus = previousStatus.agents[agent];
3268
+ if (PROTECTED_STATUSES.includes(agentStatus.status)) {
3269
+ throw managerError(
3270
+ `${SKILL_AGENT_NAMES[agent]} \uC2A4\uD0AC \uC0C1\uD0DC\uAC00 ${agentStatus.status}\uC785\uB2C8\uB2E4. --force\uB85C \uBC31\uC5C5 \uD6C4 \uAD50\uCCB4\uD558\uC138\uC694: ${agentStatus.destination}`
3271
+ );
3272
+ }
3273
+ }
2951
3274
  }
2952
3275
  const prepared = await prepareRepository(context, force, operations);
2953
- const linkBackups = await switchActiveLink(context, prepared.repository, previousStatus, force, operations);
2954
- const status = await inspectSkill(context);
2955
- if (status.status !== "current") {
2956
- throw managerError(`\uC2A4\uD0AC \uC124\uCE58 \uD6C4 \uC0C1\uD0DC\uAC00 current\uAC00 \uC544\uB2D9\uB2C8\uB2E4: ${status.status}`);
2957
- }
3276
+ const switched = await switchActiveLinks(context, prepared.repository, previousStatus, operations);
2958
3277
  return {
2959
3278
  schemaVersion: 1,
2960
3279
  action: installAction(previousStatus.status),
2961
3280
  changed: true,
2962
3281
  previousStatus,
2963
- status,
3282
+ status: switched.status,
2964
3283
  repositoryPath: prepared.repository,
2965
- backupPaths: [...prepared.backupPaths, ...linkBackups]
3284
+ backupPaths: [...prepared.backupPaths, ...switched.backupPaths]
2966
3285
  };
2967
3286
  }
2968
3287
  async function installSkill(context, options = {}, operations = defaultOperations) {
@@ -2975,73 +3294,185 @@ async function installSkill(context, options = {}, operations = defaultOperation
2975
3294
  throw managerError("\uC2A4\uD0AC\uC744 \uC124\uCE58\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.", error);
2976
3295
  }
2977
3296
  }
3297
+ async function tryRestoreCandidate(candidate, destination, operations) {
3298
+ try {
3299
+ if (await optionalLstat(destination)) {
3300
+ return { restored: false, preserved: candidate };
3301
+ }
3302
+ await operations.rename(candidate, destination);
3303
+ return { restored: true };
3304
+ } catch (error) {
3305
+ return { restored: false, error, preserved: candidate };
3306
+ }
3307
+ }
2978
3308
  async function restoreUninstallCandidate(candidate, destination, operations, originalError) {
2979
- if (await optionalLstat(destination)) {
3309
+ const result = await tryRestoreCandidate(candidate, destination, operations);
3310
+ if (result.restored) {
3311
+ throw managerError(`\uD65C\uC131 \uC2A4\uD0AC \uB9C1\uD06C\uB97C \uC81C\uAC70\uD558\uC9C0 \uC54A\uACE0 \uC6D0\uB798 \uC704\uCE58\uB85C \uBCF5\uC6D0\uD588\uC2B5\uB2C8\uB2E4: ${destination}`, originalError);
3312
+ }
3313
+ if (result.error === void 0) {
2980
3314
  throw managerError(
2981
3315
  `\uD65C\uC131 \uC2A4\uD0AC \uACBD\uB85C\uAC00 \uB3D9\uC2DC\uC5D0 \uBCC0\uACBD\uB418\uC5B4 \uC81C\uAC70\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uC774\uB3D9\uB41C \uD56D\uBAA9\uC744 \uBCF4\uC874\uD588\uC2B5\uB2C8\uB2E4: ${candidate}`,
2982
3316
  originalError
2983
3317
  );
2984
3318
  }
3319
+ throw managerError(
3320
+ `\uD65C\uC131 \uC2A4\uD0AC \uB9C1\uD06C\uB97C \uC81C\uAC70\uD558\uC9C0 \uBABB\uD588\uACE0 \uC6D0\uB798 \uC704\uCE58\uB85C \uBCF5\uC6D0\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uC774\uB3D9\uB41C \uD56D\uBAA9: ${candidate}; \uC81C\uAC70 \uC624\uB958: ${toReason(originalError)}`,
3321
+ result.error
3322
+ );
3323
+ }
3324
+ async function tryRestoreRemovedLink(destination, rawTarget, operations) {
3325
+ const restoreLink = temporaryLinkPath(destination);
2985
3326
  try {
2986
- await operations.rename(candidate, destination);
2987
- } catch (restoreError) {
2988
- throw managerError(
2989
- `\uD65C\uC131 \uC2A4\uD0AC \uB9C1\uD06C\uB97C \uC81C\uAC70\uD558\uC9C0 \uBABB\uD588\uACE0 \uC6D0\uB798 \uC704\uCE58\uB85C \uBCF5\uC6D0\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uC774\uB3D9\uB41C \uD56D\uBAA9: ${candidate}; \uC81C\uAC70 \uC624\uB958: ${toReason(originalError)}`,
2990
- restoreError
2991
- );
3327
+ if (await optionalLstat(destination)) {
3328
+ return { restored: false };
3329
+ }
3330
+ await (0, import_promises3.symlink)(rawTarget, restoreLink);
3331
+ await operations.rename(restoreLink, destination);
3332
+ return { restored: true };
3333
+ } catch (error) {
3334
+ return { restored: false, error };
3335
+ } finally {
3336
+ await (0, import_promises3.rm)(restoreLink, { force: true });
2992
3337
  }
2993
- throw managerError(`\uD65C\uC131 \uC2A4\uD0AC \uB9C1\uD06C\uB97C \uC81C\uAC70\uD558\uC9C0 \uC54A\uACE0 \uC6D0\uB798 \uC704\uCE58\uB85C \uBCF5\uC6D0\uD588\uC2B5\uB2C8\uB2E4: ${destination}`, originalError);
2994
3338
  }
2995
- async function uninstallSkill(context, operations = defaultOperations) {
2996
- const destination = destinationPath(context);
2997
- const status = await inspectSkill(context);
2998
- if (status.status === "missing") {
2999
- return "absent";
3339
+ async function planUninstallTargets(context) {
3340
+ const statuses = [];
3341
+ for (const agent of SKILL_AGENTS) {
3342
+ const status = await inspectAgentSkill(context, agent);
3343
+ if (status.status !== "missing" && (!status.managed || !status.linkTarget)) {
3344
+ throw managerError(`\uAD00\uB9AC\uB418\uC9C0 \uC54A\uC740 \uC2A4\uD0AC \uD56D\uBAA9\uC774\uBBC0\uB85C \uC5B4\uB290 \uACBD\uB85C\uB3C4 \uC81C\uAC70\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4: ${status.destination}`);
3345
+ }
3346
+ statuses.push({ agent, status });
3347
+ }
3348
+ const targets = [];
3349
+ for (const { agent, status } of statuses) {
3350
+ if (status.status === "missing" || !status.linkTarget) {
3351
+ continue;
3352
+ }
3353
+ const realParent = await optionalRealpath(import_node_path5.default.dirname(status.destination));
3354
+ if (!realParent) {
3355
+ continue;
3356
+ }
3357
+ const resolvedDestination = import_node_path5.default.join(realParent, import_node_path5.default.basename(status.destination));
3358
+ const sameTarget = targets.find((entry) => entry.resolvedDestination === resolvedDestination);
3359
+ if (sameTarget) {
3360
+ sameTarget.agents.push(agent);
3361
+ continue;
3362
+ }
3363
+ targets.push({
3364
+ agents: [agent],
3365
+ destination: status.destination,
3366
+ resolvedDestination,
3367
+ linkTarget: status.linkTarget
3368
+ });
3369
+ }
3370
+ return targets;
3371
+ }
3372
+ function uninstallRollbackError(results, originalError, handledLabel) {
3373
+ const failed = results.filter(({ result }) => !result.restored);
3374
+ if (failed.length === 0) {
3375
+ return managerError(
3376
+ `\uC2A4\uD0AC \uC81C\uAC70\uC5D0 \uC2E4\uD328\uD574 ${handledLabel}\uB97C \uC774\uC804 \uC0C1\uD0DC\uB85C \uB418\uB3CC\uB838\uC2B5\uB2C8\uB2E4: ${results.map(({ destination }) => destination).join(", ")}`,
3377
+ originalError
3378
+ );
3000
3379
  }
3001
- if (!status.managed || !status.linkTarget) {
3002
- throw managerError(`\uAD00\uB9AC\uB418\uC9C0 \uC54A\uC740 \uC2A4\uD0AC \uD56D\uBAA9\uC774\uBBC0\uB85C \uC81C\uAC70\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4: ${destination}`);
3380
+ const failedText = failed.map(
3381
+ ({ destination, result }) => result.preserved ? `${destination} (\uC774\uB3D9\uB41C \uD56D\uBAA9: ${result.preserved})` : destination
3382
+ ).join(", ");
3383
+ return managerError(
3384
+ `\uC2A4\uD0AC \uC81C\uAC70\uC5D0 \uC2E4\uD328\uD588\uACE0 \uC77C\uBD80 \uACBD\uB85C\uB97C \uB418\uB3CC\uB9AC\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4: ${failedText}; \uC81C\uAC70 \uC624\uB958: ${toReason(originalError)}`,
3385
+ failed.find(({ result }) => result.error !== void 0)?.result.error
3386
+ );
3387
+ }
3388
+ async function rollbackMovedCandidates(moved, operations, originalError) {
3389
+ const results = [];
3390
+ for (const target of [...moved].reverse()) {
3391
+ results.push({
3392
+ destination: target.destination,
3393
+ result: await tryRestoreCandidate(target.candidate, target.destination, operations)
3394
+ });
3003
3395
  }
3396
+ throw uninstallRollbackError(results, originalError, "\uC62E\uAE34 \uACBD\uB85C");
3397
+ }
3398
+ async function moveUninstallCandidate(target, moved, operations) {
3399
+ const { destination } = target;
3004
3400
  const candidate = import_node_path5.default.join(import_node_path5.default.dirname(destination), `.${SKILL_NAME2}.uninstall-${(0, import_node_crypto3.randomUUID)()}`);
3005
3401
  try {
3006
3402
  await operations.rename(destination, candidate);
3007
3403
  } catch (error) {
3008
3404
  if (isNodeError(error, "ENOENT")) {
3009
- return "absent";
3405
+ return void 0;
3406
+ }
3407
+ const moveError = managerError(`\uD65C\uC131 \uC2A4\uD0AC \uB9C1\uD06C\uB97C \uC81C\uAC70\uC6A9 \uC784\uC2DC \uACBD\uB85C\uB85C \uC774\uB3D9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${destination}`, error);
3408
+ if (moved.length === 0) {
3409
+ throw moveError;
3010
3410
  }
3011
- throw managerError(`\uD65C\uC131 \uC2A4\uD0AC \uB9C1\uD06C\uB97C \uC81C\uAC70\uC6A9 \uC784\uC2DC \uACBD\uB85C\uB85C \uC774\uB3D9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${destination}`, error);
3411
+ return await rollbackMovedCandidates(moved, operations, moveError);
3012
3412
  }
3013
- let candidateTarget;
3413
+ let rawTarget;
3414
+ let verificationError;
3014
3415
  try {
3015
3416
  const candidateStat = await (0, import_promises3.lstat)(candidate);
3016
3417
  if (!candidateStat.isSymbolicLink()) {
3017
- return await restoreUninstallCandidate(
3018
- candidate,
3019
- destination,
3020
- operations,
3021
- new Error("\uAC80\uC0AC \uD6C4 \uD65C\uC131 \uACBD\uB85C\uAC00 \uC2EC\uBCFC\uB9AD \uB9C1\uD06C\uAC00 \uC544\uB2CC \uD56D\uBAA9\uC73C\uB85C \uBCC0\uACBD\uB418\uC5C8\uC2B5\uB2C8\uB2E4.")
3022
- );
3418
+ verificationError = new Error("\uAC80\uC0AC \uD6C4 \uD65C\uC131 \uACBD\uB85C\uAC00 \uC2EC\uBCFC\uB9AD \uB9C1\uD06C\uAC00 \uC544\uB2CC \uD56D\uBAA9\uC73C\uB85C \uBCC0\uACBD\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
3419
+ } else {
3420
+ rawTarget = await (0, import_promises3.readlink)(candidate);
3421
+ if (resolveLinkTarget(destination, rawTarget) !== target.linkTarget) {
3422
+ verificationError = new Error("\uAC80\uC0AC \uD6C4 \uD65C\uC131 \uC2A4\uD0AC \uB9C1\uD06C \uB300\uC0C1\uC774 \uBCC0\uACBD\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
3423
+ }
3023
3424
  }
3024
- candidateTarget = resolveLinkTarget(destination, await (0, import_promises3.readlink)(candidate));
3025
3425
  } catch (error) {
3026
- if (error instanceof NhnCloudCliError) {
3027
- throw error;
3028
- }
3029
- return await restoreUninstallCandidate(candidate, destination, operations, error);
3426
+ verificationError = error;
3030
3427
  }
3031
- if (candidateTarget !== status.linkTarget) {
3032
- return await restoreUninstallCandidate(
3033
- candidate,
3034
- destination,
3035
- operations,
3036
- new Error("\uAC80\uC0AC \uD6C4 \uD65C\uC131 \uC2A4\uD0AC \uB9C1\uD06C \uB300\uC0C1\uC774 \uBCC0\uACBD\uB418\uC5C8\uC2B5\uB2C8\uB2E4.")
3037
- );
3428
+ if (verificationError !== void 0 || rawTarget === void 0) {
3429
+ if (moved.length === 0) {
3430
+ return await restoreUninstallCandidate(candidate, destination, operations, verificationError);
3431
+ }
3432
+ return await rollbackMovedCandidates([...moved, { destination, candidate }], operations, verificationError);
3038
3433
  }
3039
- try {
3040
- await (0, import_promises3.rm)(candidate);
3041
- } catch (error) {
3042
- return await restoreUninstallCandidate(candidate, destination, operations, error);
3434
+ return { ...target, candidate, rawTarget };
3435
+ }
3436
+ async function removeUninstallCandidates(moved, operations) {
3437
+ const removeEntry = operations.rm ?? defaultOperations.rm;
3438
+ for (const [index, target] of moved.entries()) {
3439
+ try {
3440
+ await removeEntry(target.candidate, { force: true });
3441
+ } catch (originalError) {
3442
+ if (index === 0) {
3443
+ if (moved.length === 1) {
3444
+ return await restoreUninstallCandidate(target.candidate, target.destination, operations, originalError);
3445
+ }
3446
+ return await rollbackMovedCandidates(moved, operations, originalError);
3447
+ }
3448
+ const results = [];
3449
+ for (const [restoreIndex, entry] of moved.entries()) {
3450
+ const result = restoreIndex < index ? await tryRestoreRemovedLink(entry.destination, entry.rawTarget, operations) : await tryRestoreCandidate(entry.candidate, entry.destination, operations);
3451
+ results.push({ destination: entry.destination, result });
3452
+ }
3453
+ throw uninstallRollbackError(results, originalError, "\uC9C0\uC6B4 \uACBD\uB85C");
3454
+ }
3043
3455
  }
3044
- return "removed";
3456
+ }
3457
+ async function uninstallSkill(context, operations = defaultOperations) {
3458
+ const targets = await planUninstallTargets(context);
3459
+ const moved = [];
3460
+ for (const target of targets) {
3461
+ const movedTarget = await moveUninstallCandidate(target, moved, operations);
3462
+ if (movedTarget) {
3463
+ moved.push(movedTarget);
3464
+ }
3465
+ }
3466
+ await removeUninstallCandidates(moved, operations);
3467
+ const agentResult = (agent) => ({
3468
+ action: moved.some((target) => target.agents.includes(agent)) ? "removed" : "absent",
3469
+ destination: destinationPath(context, agent)
3470
+ });
3471
+ const agents = { claude: agentResult("claude"), codex: agentResult("codex") };
3472
+ return {
3473
+ action: SKILL_AGENTS.some((agent) => agents[agent].action === "removed") ? "removed" : "absent",
3474
+ agents
3475
+ };
3045
3476
  }
3046
3477
 
3047
3478
  // src/formatters/table.ts
@@ -3117,9 +3548,16 @@ function outputSkillStatus(opts, status) {
3117
3548
  rows: [
3118
3549
  ["\uC0C1\uD0DC", status.status],
3119
3550
  ["\uD604\uC7AC \uBC84\uC804", status.currentVersion],
3120
- ["\uC124\uCE58 \uBC84\uC804", terminalText(status.installedVersion)],
3121
- ["\uC124\uCE58 \uACBD\uB85C", terminalText(status.destination)],
3122
- ["\uB9C1\uD06C \uB300\uC0C1", terminalText(status.linkTarget)],
3551
+ ...SKILL_AGENTS.flatMap((agent) => {
3552
+ const name = SKILL_AGENT_NAMES[agent];
3553
+ const agentStatus = status.agents[agent];
3554
+ return [
3555
+ [`${name} \uC0C1\uD0DC`, agentStatus.status],
3556
+ [`${name} \uC124\uCE58 \uBC84\uC804`, terminalText(agentStatus.installedVersion)],
3557
+ [`${name} \uC124\uCE58 \uACBD\uB85C`, terminalText(agentStatus.destination)],
3558
+ [`${name} \uB9C1\uD06C \uB300\uC0C1`, terminalText(agentStatus.linkTarget)]
3559
+ ];
3560
+ }),
3123
3561
  ["\uBCF5\uAD6C \uBA85\uB839", skillRecoveryCommand(status.status) ?? "\uC870\uCE58 \uC5C6\uC74C"]
3124
3562
  ],
3125
3563
  raw: status,
@@ -3134,7 +3572,10 @@ function outputSkillInstallResult(opts, result) {
3134
3572
  ["\uBCC0\uACBD \uC5EC\uBD80", result.changed ? "\uBCC0\uACBD\uB428" : "\uBCC0\uACBD \uC5C6\uC74C"],
3135
3573
  ["\uC774\uC804 \uC0C1\uD0DC", result.previousStatus.status],
3136
3574
  ["\uD604\uC7AC \uC0C1\uD0DC", result.status.status],
3137
- ["\uC124\uCE58 \uACBD\uB85C", terminalText(result.status.destination)],
3575
+ ...SKILL_AGENTS.map((agent) => [
3576
+ `${SKILL_AGENT_NAMES[agent]} \uC124\uCE58 \uACBD\uB85C`,
3577
+ terminalText(result.status.agents[agent].destination)
3578
+ ]),
3138
3579
  ["\uAD00\uB9AC \uC800\uC7A5\uC18C", terminalText(result.repositoryPath)],
3139
3580
  [
3140
3581
  "\uBC31\uC5C5 \uACBD\uB85C",
@@ -3150,7 +3591,14 @@ function outputSkillUninstallResult(opts, result) {
3150
3591
  headers: ["\uD56D\uBAA9", "\uAC12"],
3151
3592
  rows: [
3152
3593
  ["\uC791\uC5C5", result.action === "removed" ? "\uD65C\uC131 \uB9C1\uD06C \uC81C\uAC70 \uC644\uB8CC" : "\uD65C\uC131 \uB9C1\uD06C \uC5C6\uC74C"],
3153
- ["\uC124\uCE58 \uACBD\uB85C", terminalText(result.destination)],
3594
+ ...SKILL_AGENTS.flatMap((agent) => {
3595
+ const name = SKILL_AGENT_NAMES[agent];
3596
+ const agentResult = result.agents[agent];
3597
+ return [
3598
+ [`${name} \uC124\uCE58 \uACBD\uB85C`, terminalText(agentResult.destination)],
3599
+ [`${name} \uACB0\uACFC`, agentResult.action === "removed" ? "\uD65C\uC131 \uB9C1\uD06C \uC81C\uAC70" : "\uD65C\uC131 \uB9C1\uD06C \uC5C6\uC74C"]
3600
+ ];
3601
+ }),
3154
3602
  ["\uAD00\uB9AC \uC800\uC7A5\uC18C", "\uBCF4\uC874\uB428"]
3155
3603
  ],
3156
3604
  raw: result,
@@ -3173,7 +3621,7 @@ async function showStatus(cmd, dependencies) {
3173
3621
  }
3174
3622
  function createInstallCommand(name, dependencies) {
3175
3623
  return new import_commander3.Command(name).description(
3176
- name === "install" ? "Claude Code \uC2A4\uD0AC\uC744 \uAD00\uB9AC \uC800\uC7A5\uC18C\uC5D0 \uC124\uCE58\uD55C\uB2E4" : "Claude Code \uC2A4\uD0AC\uC744 \uD604\uC7AC CLI \uBC84\uC804\uC73C\uB85C \uAC31\uC2E0\uD55C\uB2E4"
3624
+ name === "install" ? "Claude Code\xB7Codex \uC2A4\uD0AC\uC744 \uAD00\uB9AC \uC800\uC7A5\uC18C\uC5D0 \uC124\uCE58\uD55C\uB2E4" : "Claude Code\xB7Codex \uC2A4\uD0AC\uC744 \uD604\uC7AC CLI \uBC84\uC804\uC73C\uB85C \uAC31\uC2E0\uD55C\uB2E4"
3177
3625
  ).option("--force", "\uC0AC\uC6A9\uC790 \uD56D\uBAA9\uC774\uB098 \uC218\uC815\xB7\uC190\uC0C1\uB41C \uAD00\uB9AC \uC800\uC7A5\uC18C\uB97C \uBC31\uC5C5 \uD6C4 \uAD50\uCCB4\uD55C\uB2E4").action(async (_localOpts, cmd) => {
3178
3626
  const opts = cmd.optsWithGlobals();
3179
3627
  const context = dependencies.createContext();
@@ -3182,98 +3630,282 @@ function createInstallCommand(name, dependencies) {
3182
3630
  });
3183
3631
  }
3184
3632
  function createSkillsCommand(dependencies = defaultDependencies) {
3185
- const statusCommand = new import_commander3.Command("status").description("Claude Code \uC2A4\uD0AC\uC758 \uAD00\uB9AC \uC0C1\uD0DC\uB97C \uC870\uD68C\uD55C\uB2E4").action(async (_opts, cmd) => showStatus(cmd, dependencies));
3186
- const uninstallCommand = new import_commander3.Command("uninstall").description("\uD65C\uC131 Claude Code \uC2A4\uD0AC \uB9C1\uD06C\uB97C \uC81C\uAC70\uD55C\uB2E4").action(async (_opts, cmd) => {
3633
+ const statusCommand = new import_commander3.Command("status").description("Claude Code\xB7Codex \uC2A4\uD0AC\uC758 \uAD00\uB9AC \uC0C1\uD0DC\uB97C \uC870\uD68C\uD55C\uB2E4").action(async (_opts, cmd) => showStatus(cmd, dependencies));
3634
+ const uninstallCommand = new import_commander3.Command("uninstall").description("\uD65C\uC131 Claude Code\xB7Codex \uC2A4\uD0AC \uB9C1\uD06C\uB97C \uC81C\uAC70\uD55C\uB2E4").action(async (_opts, cmd) => {
3187
3635
  const opts = cmd.optsWithGlobals();
3188
3636
  const context = dependencies.createContext();
3189
- const previousStatus = await dependencies.inspect(context);
3190
- const action = await dependencies.uninstall(context);
3637
+ const uninstalled = await dependencies.uninstall(context);
3191
3638
  const result = {
3192
3639
  schemaVersion: 1,
3193
- action,
3194
- changed: action === "removed",
3640
+ action: uninstalled.action,
3641
+ changed: uninstalled.action === "removed",
3195
3642
  status: "missing",
3196
- destination: previousStatus.destination,
3197
- repositoryPreserved: true
3643
+ destination: uninstalled.agents.claude.destination,
3644
+ repositoryPreserved: true,
3645
+ agents: uninstalled.agents
3198
3646
  };
3199
3647
  outputSkillUninstallResult(opts, result);
3200
3648
  });
3201
- return new import_commander3.Command("skills").description(`Claude Code \uC2A4\uD0AC(${SKILL_NAME3}) \uC124\uCE58 \uAD00\uB9AC`).addCommand(statusCommand).addCommand(createInstallCommand("install", dependencies)).addCommand(createInstallCommand("update", dependencies)).addCommand(uninstallCommand).action(async (_opts, cmd) => showStatus(cmd, dependencies));
3649
+ return new import_commander3.Command("skills").description(`Claude Code\xB7Codex \uC2A4\uD0AC(${SKILL_NAME3}) \uC124\uCE58 \uAD00\uB9AC`).addCommand(statusCommand).addCommand(createInstallCommand("install", dependencies)).addCommand(createInstallCommand("update", dependencies)).addCommand(uninstallCommand).action(async (_opts, cmd) => showStatus(cmd, dependencies));
3202
3650
  }
3203
3651
  var skillsCommand = createSkillsCommand();
3204
3652
 
3205
3653
  // src/commands/doctor.ts
3206
3654
  var import_commander4 = require("commander");
3207
3655
  var import_chalk3 = __toESM(require("chalk"));
3208
- var import_promises4 = require("fs/promises");
3209
- var import_node_os4 = require("os");
3210
- var import_node_path6 = __toESM(require("path"));
3211
- var SKILL_NAME4 = "nhncloud-cli";
3212
- var CREDENTIALS_PATH2 = import_node_path6.default.join((0, import_node_os4.homedir)(), ".nhncloud", "credentials.json");
3213
- var CONFIG_PATH2 = import_node_path6.default.join((0, import_node_os4.homedir)(), ".nhncloud", "config.json");
3214
- async function readProfiles() {
3215
- let raw;
3656
+ var SKILL_ERROR_REASON = "\uACF5\uAC1C \uC2A4\uD0AC \uC0C1\uD0DC\uB97C \uD310\uC815\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4";
3657
+ var defaultDependencies2 = {
3658
+ inspectCredentials: inspectCredentialsFile,
3659
+ inspectConfig: inspectConfigFile,
3660
+ resolveProfile: resolveProfileName,
3661
+ createSkillContext: createSkillManagerContext,
3662
+ inspectSkill: inspectAgentSkill,
3663
+ connection: {
3664
+ getUserAccessKey,
3665
+ getIaasCredential,
3666
+ getOptionalServiceCredential,
3667
+ verifyUserAccessKey,
3668
+ verifyIaas,
3669
+ verifyLogncrash,
3670
+ verifyNcr,
3671
+ verifyNcs
3672
+ }
3673
+ };
3674
+ async function resolveTargetProfile(cliProfile, dependencies) {
3216
3675
  try {
3217
- raw = await (0, import_promises4.readFile)(CREDENTIALS_PATH2, "utf-8");
3218
- } catch {
3219
- return { profiles: [], parseError: false, missing: true };
3676
+ return await dependencies.resolveProfile(cliProfile);
3677
+ } catch (err) {
3678
+ if (err instanceof NhnCloudCliError) return null;
3679
+ throw err;
3220
3680
  }
3681
+ }
3682
+ async function inspectAgentSkillForDoctor(dependencies, agent) {
3221
3683
  try {
3222
- const parsed = JSON.parse(raw);
3223
- return { profiles: Object.keys(parsed.profiles ?? {}), parseError: false, missing: false };
3224
- } catch {
3225
- return { profiles: [], parseError: true, missing: false };
3684
+ const status = await dependencies.inspectSkill(dependencies.createSkillContext(), agent);
3685
+ return { ...status, recoveryCommand: skillRecoveryCommand(status.status) ?? null };
3686
+ } catch (err) {
3687
+ const code = typeof err === "object" && err !== null && "code" in err && typeof err.code === "string" ? err.code : void 0;
3688
+ return { status: "error", reason: code ? `${SKILL_ERROR_REASON}: ${code}` : SKILL_ERROR_REASON };
3226
3689
  }
3227
3690
  }
3228
- async function readDefaultProfile() {
3691
+ function connectionHasFailure(connection) {
3692
+ return connection.checked && Object.values(connection.targets).some((target) => target.status === "failed");
3693
+ }
3694
+ var CONNECTION_TARGETS = ["userAccessKey", "iaas", "logncrash", "ncr", "ncs"];
3695
+ function skipAll(reason) {
3696
+ return Object.fromEntries(
3697
+ CONNECTION_TARGETS.map((target) => [target, { status: "skipped", reason }])
3698
+ );
3699
+ }
3700
+ async function runVerify(verify) {
3229
3701
  try {
3230
- const raw = await (0, import_promises4.readFile)(CONFIG_PATH2, "utf-8");
3231
- const parsed = JSON.parse(raw);
3232
- return parsed.defaultProfile ?? null;
3233
- } catch {
3234
- return null;
3702
+ return await verify() ? { status: "ok" } : { status: "failed", reason: "auth" };
3703
+ } catch (err) {
3704
+ const exitCode = err instanceof NhnCloudCliError ? err.exitCode : EXIT_API_ERROR;
3705
+ return { status: "failed", reason: "error", exitCode };
3235
3706
  }
3236
3707
  }
3237
- var doctorCommand = new import_commander4.Command("doctor").description("\uC790\uACA9\uC99D\uBA85\xB7\uC2A4\uD0AC \uC124\uCE58 \uC0C1\uD0DC\uB97C \uC9C4\uB2E8\uD55C\uB2E4(\uC624\uD504\uB77C\uC778 \u2014 \uC5F0\uACB0 \uD14C\uC2A4\uD2B8\uB294 configure \uB85C)").action(async () => {
3238
- console.log(import_chalk3.default.bold("\n\u{1F50D} nhncloud-cli \uC9C4\uB2E8\n"));
3239
- const creds = await readProfiles();
3240
- const defaultProfile = await readDefaultProfile();
3241
- console.log(import_chalk3.default.bold("\uC790\uACA9\uC99D\uBA85"));
3242
- if (creds.missing) {
3243
- console.log(` ${import_chalk3.default.red("\u274C \uBBF8\uC124\uC815")} \u2014 ${CREDENTIALS_PATH2} \uC5C6\uC74C. nhncloud configure \uB85C \uC124\uC815\uD558\uC138\uC694.`);
3244
- } else if (creds.parseError) {
3245
- console.log(` ${import_chalk3.default.red("\u274C \uD30C\uC2F1 \uC624\uB958")} \u2014 ${CREDENTIALS_PATH2} \uAC00 \uC62C\uBC14\uB978 JSON \uC778\uC9C0 \uD655\uC778\uD558\uC138\uC694.`);
3246
- } else if (creds.profiles.length === 0) {
3247
- console.log(` ${import_chalk3.default.yellow("\u26A0 profile \uC5C6\uC74C")} \u2014 nhncloud configure \uB85C \uC124\uC815\uD558\uC138\uC694.`);
3248
- } else {
3249
- console.log(` ${import_chalk3.default.green("\u2713 \uC124\uC815\uB428")} \u2014 profile: ${creds.profiles.join(", ")}`);
3250
- console.log(` \uAE30\uBCF8 profile: ${defaultProfile ? import_chalk3.default.green(defaultProfile) : import_chalk3.default.gray("\uBBF8\uC9C0\uC815 (default \uC0AC\uC6A9)")}`);
3708
+ async function readRequired(read) {
3709
+ try {
3710
+ return { value: await read() };
3711
+ } catch (err) {
3712
+ const reason = err instanceof NhnCloudCliError ? "not-configured" : "profile-unavailable";
3713
+ return { skipped: { status: "skipped", reason } };
3251
3714
  }
3252
- console.log(import_chalk3.default.bold("\nClaude Code \uC2A4\uD0AC"));
3253
- const skillStatus = await inspectSkill(createSkillManagerContext());
3254
- if (skillStatus.status === "current") {
3255
- console.log(
3256
- ` ${SKILL_NAME4}: ${import_chalk3.default.green(`\u2713 current (${skillStatus.installedVersion ?? skillStatus.currentVersion})`)}`
3257
- );
3258
- } else if (skillStatus.status === "missing") {
3259
- console.log(
3260
- ` ${SKILL_NAME4}: ${import_chalk3.default.gray(`\uBBF8\uC124\uCE58 \u2014 ${skillRecoveryCommand(skillStatus.status)}`)}`
3715
+ }
3716
+ async function readAppkey(service, profileName, deps) {
3717
+ try {
3718
+ const credential = await deps.getOptionalServiceCredential(service, profileName);
3719
+ if (!credential?.appkey) return { skipped: { status: "skipped", reason: "not-configured" } };
3720
+ return { appkey: credential.appkey };
3721
+ } catch {
3722
+ return { skipped: { status: "skipped", reason: "profile-unavailable" } };
3723
+ }
3724
+ }
3725
+ async function checkConnection(profileName, credentials, profileSummary, deps) {
3726
+ if (credentials.state !== "ok" || profileName === null || profileSummary === void 0 || profileSummary.environment === "invalid") {
3727
+ return skipAll("profile-unavailable");
3728
+ }
3729
+ if (profileSummary.environment === "gov") return skipAll("gov-unsupported");
3730
+ const uakRead = await readRequired(() => deps.getUserAccessKey(profileName));
3731
+ const uak = "value" in uakRead ? uakRead.value : void 0;
3732
+ const userAccessKey = "value" in uakRead ? await runVerify(() => deps.verifyUserAccessKey(uakRead.value)) : uakRead.skipped;
3733
+ const iaasRead = await readRequired(() => deps.getIaasCredential(profileName));
3734
+ const iaasResult = "value" in iaasRead ? await runVerify(() => deps.verifyIaas(iaasRead.value)) : iaasRead.skipped;
3735
+ const checkAppkeyService = async (service, verify, usesOAuth) => {
3736
+ const read = await readAppkey(service, profileName, deps);
3737
+ if ("skipped" in read) return read.skipped;
3738
+ if (uak === void 0) return { status: "skipped", reason: "uak-missing" };
3739
+ if (usesOAuth && userAccessKey.status !== "ok") return { status: "skipped", reason: "uak-failed" };
3740
+ return runVerify(() => verify(uak, read.appkey));
3741
+ };
3742
+ const logncrash = await checkAppkeyService("logncrash", deps.verifyLogncrash, true);
3743
+ const ncr = await checkAppkeyService("ncr", deps.verifyNcr, false);
3744
+ const ncs = await checkAppkeyService("ncs", deps.verifyNcs, true);
3745
+ return { userAccessKey, iaas: iaasResult, logncrash, ncr, ncs };
3746
+ }
3747
+ async function buildDoctorReport(options, dependencies) {
3748
+ const credentials = await dependencies.inspectCredentials();
3749
+ const config = await dependencies.inspectConfig();
3750
+ const profileName = await resolveTargetProfile(options.profile, dependencies);
3751
+ const profileSummary = credentials.state === "ok" && profileName !== null ? credentials.profiles.find((summary) => summary.name === profileName) : void 0;
3752
+ const connection = options.checkConnection ? {
3753
+ checked: true,
3754
+ profile: profileName,
3755
+ targets: await checkConnection(profileName, credentials, profileSummary, dependencies.connection)
3756
+ } : { checked: false };
3757
+ const claude = await inspectAgentSkillForDoctor(dependencies, "claude");
3758
+ const codex = await inspectAgentSkillForDoctor(dependencies, "codex");
3759
+ const ready = profileSummary !== void 0 && profileSummary.environment !== "invalid" && profileSummary.blocks.length > 0 && !connectionHasFailure(connection);
3760
+ return {
3761
+ schemaVersion: 1,
3762
+ ready,
3763
+ credentials,
3764
+ config,
3765
+ profile: { name: profileName, exists: profileSummary !== void 0 },
3766
+ connection,
3767
+ skills: { agents: { claude, codex } }
3768
+ };
3769
+ }
3770
+ function line(text = "") {
3771
+ process.stdout.write(`${text}
3772
+ `);
3773
+ }
3774
+ function printCredentialsSection(credentials) {
3775
+ const filePath = sanitizeForTerminal(credentials.path);
3776
+ const reason = sanitizeForTerminal(credentials.reason ?? "");
3777
+ line(import_chalk3.default.bold("\uC790\uACA9\uC99D\uBA85"));
3778
+ switch (credentials.state) {
3779
+ case "missing":
3780
+ line(` ${import_chalk3.default.red("\u274C \uBBF8\uC124\uC815")} \u2014 ${filePath} \uC5C6\uC74C. nhncloud configure \uB85C \uC124\uC815\uD558\uC138\uC694.`);
3781
+ break;
3782
+ case "invalid":
3783
+ line(` ${import_chalk3.default.red("\u274C \uD615\uC2DD \uC624\uB958")} \u2014 ${filePath}: ${reason}`);
3784
+ break;
3785
+ case "unreadable":
3786
+ line(` ${import_chalk3.default.red("\u274C \uC77D\uAE30 \uC2E4\uD328")} \u2014 ${filePath}: ${reason}`);
3787
+ break;
3788
+ case "ok":
3789
+ if (credentials.profiles.length === 0) {
3790
+ line(` ${import_chalk3.default.yellow("\u26A0 profile \uC5C6\uC74C")} \u2014 nhncloud configure \uB85C \uC124\uC815\uD558\uC138\uC694.`);
3791
+ break;
3792
+ }
3793
+ line(` ${import_chalk3.default.green("\u2713 \uC124\uC815\uB428")} \u2014 profile:`);
3794
+ for (const summary of credentials.profiles) {
3795
+ const name = sanitizeForTerminal(summary.name);
3796
+ const blocks = summary.blocks.map(sanitizeForTerminal).join(", ") || "-";
3797
+ if (summary.environment === "invalid") {
3798
+ line(` ${name} ${import_chalk3.default.yellow("\u26A0 environment \uAC12\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4")} [${blocks}]`);
3799
+ } else {
3800
+ line(` ${name}${summary.environment === "gov" ? " (gov)" : ""} [${blocks}]`);
3801
+ }
3802
+ }
3803
+ break;
3804
+ }
3805
+ if (credentials.permissions === "too-open") {
3806
+ line(` ${import_chalk3.default.yellow("\u26A0 \uD30C\uC77C \uAD8C\uD55C\uC774 \uB113\uC2B5\uB2C8\uB2E4")} \u2014 chmod 600 ${filePath}`);
3807
+ }
3808
+ }
3809
+ function printConfigSection(config) {
3810
+ line(import_chalk3.default.bold("\n\uC124\uC815 \uD30C\uC77C"));
3811
+ if (config.state === "invalid" || config.state === "unreadable") {
3812
+ line(
3813
+ ` ${import_chalk3.default.red(config.state === "invalid" ? "\u274C \uD615\uC2DD \uC624\uB958" : "\u274C \uC77D\uAE30 \uC2E4\uD328")} \u2014 ${sanitizeForTerminal(config.path)}: ${sanitizeForTerminal(config.reason ?? "")}`
3261
3814
  );
3815
+ }
3816
+ line(
3817
+ ` \uAE30\uBCF8 profile: ${config.defaultProfile ? import_chalk3.default.green(sanitizeForTerminal(config.defaultProfile)) : import_chalk3.default.gray("\uBBF8\uC9C0\uC815 (default \uC0AC\uC6A9)")}`
3818
+ );
3819
+ }
3820
+ function printProfileSection(profile) {
3821
+ line(import_chalk3.default.bold("\n\uB300\uC0C1 profile"));
3822
+ if (profile.name === null) {
3823
+ line(` ${import_chalk3.default.red("\u274C \uD574\uC11D \uC2E4\uD328")} \u2014 config.json \uC744 \uBA3C\uC800 \uACE0\uCE58\uC138\uC694.`);
3824
+ } else if (profile.exists) {
3825
+ line(` ${import_chalk3.default.green(`\u2713 ${sanitizeForTerminal(profile.name)}`)}`);
3262
3826
  } else {
3263
- console.log(
3264
- ` ${SKILL_NAME4}: ${import_chalk3.default.yellow(`\u26A0 ${skillStatus.status}`)} \u2014 ${skillRecoveryCommand(skillStatus.status)}`
3827
+ line(
3828
+ ` ${import_chalk3.default.yellow(`\u26A0 ${sanitizeForTerminal(profile.name)} \uC5C6\uC74C`)} \u2014 nhncloud configure \uB85C \uC124\uC815\uD558\uC138\uC694.`
3265
3829
  );
3266
3830
  }
3267
- console.log();
3268
- if (!creds.missing && !creds.parseError && creds.profiles.length > 0) {
3269
- console.log(import_chalk3.default.green("\u2713 \uAE30\uBCF8 \uC124\uC815\uC774 \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4."));
3831
+ }
3832
+ function printSkillLine(agent, skill) {
3833
+ const name = SKILL_AGENT_NAMES[agent];
3834
+ if (skill.status === "error") {
3835
+ line(` ${name}: ${import_chalk3.default.yellow("\u26A0 error")} \u2014 ${sanitizeForTerminal(skill.reason)}`);
3836
+ } else if (skill.status === "current") {
3837
+ const version = sanitizeForTerminal(skill.installedVersion ?? skill.currentVersion);
3838
+ line(` ${name}: ${import_chalk3.default.green(`\u2713 current (${version})`)}`);
3839
+ } else if (skill.status === "missing") {
3840
+ line(` ${name}: ${import_chalk3.default.gray(`\uBBF8\uC124\uCE58 \u2014 ${skill.recoveryCommand}`)}`);
3270
3841
  } else {
3271
- console.log(import_chalk3.default.yellow("\u26A0 \uC124\uC815\uC774 \uD544\uC694\uD569\uB2C8\uB2E4: nhncloud configure"));
3842
+ line(` ${name}: ${import_chalk3.default.yellow(`\u26A0 ${skill.status}`)} \u2014 ${skill.recoveryCommand}`);
3272
3843
  }
3273
- console.log();
3274
- });
3275
-
3276
- // src/commands/logncrash/search.ts
3844
+ }
3845
+ function printSkillSection(agents) {
3846
+ line(import_chalk3.default.bold("\n\uACF5\uAC1C \uC2A4\uD0AC"));
3847
+ for (const agent of SKILL_AGENTS) printSkillLine(agent, agents[agent]);
3848
+ }
3849
+ var SKIP_REASONS = {
3850
+ "not-configured": "\uC124\uC815 \uC5C6\uC74C",
3851
+ "uak-missing": "\uACF5\uD1B5 userAccessKey \uC5C6\uC74C",
3852
+ "uak-failed": "userAccessKey \uD655\uC778\uC774 \uC131\uACF5\uD558\uC9C0 \uC54A\uC544 \uAC74\uB108\uB700",
3853
+ "gov-unsupported": "\uACF5\uACF5\uB9DD profile \uC740 \uC5F0\uACB0 \uD655\uC778\uC744 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC74C",
3854
+ "profile-unavailable": "\uB300\uC0C1 profile \uC744 \uC77D\uC744 \uC218 \uC5C6\uC74C"
3855
+ };
3856
+ function printConnectionSection(connection) {
3857
+ if (!connection.checked) return;
3858
+ line(import_chalk3.default.bold("\n\uC5F0\uACB0 \uD655\uC778"));
3859
+ for (const target of CONNECTION_TARGETS) {
3860
+ const result = connection.targets[target];
3861
+ const label = ` ${target}:`;
3862
+ if (result.status === "ok") {
3863
+ const region = target === "ncr" || target === "ncs" ? " (kr1)" : "";
3864
+ line(`${label} ${import_chalk3.default.green(`\u2713 \uC131\uACF5${region}`)}`);
3865
+ } else if (result.status === "failed" && result.reason === "auth") {
3866
+ line(`${label} ${import_chalk3.default.red("\u274C \uC778\uC99D \uC2E4\uD328")} \u2014 \uD0A4\uB098 appkey \uB97C \uD655\uC778\uD558\uC138\uC694.`);
3867
+ } else if (result.status === "failed") {
3868
+ line(`${label} ${import_chalk3.default.red(`\u274C \uC624\uB958 (\uC885\uB8CC \uCF54\uB4DC ${result.exitCode ?? EXIT_API_ERROR})`)}`);
3869
+ } else {
3870
+ const reason = result.reason ? SKIP_REASONS[result.reason] ?? result.reason : "";
3871
+ line(`${label} ${import_chalk3.default.gray(`\uAC74\uB108\uB700 \u2014 ${reason}`)}`);
3872
+ }
3873
+ }
3874
+ }
3875
+ function printText(report) {
3876
+ line(import_chalk3.default.bold("\n\u{1F50D} nhncloud-cli \uC9C4\uB2E8\n"));
3877
+ printCredentialsSection(report.credentials);
3878
+ printConfigSection(report.config);
3879
+ printProfileSection(report.profile);
3880
+ printConnectionSection(report.connection);
3881
+ printSkillSection(report.skills.agents);
3882
+ line();
3883
+ if (report.ready) {
3884
+ line(import_chalk3.default.green("\u2713 \uAE30\uBCF8 \uC124\uC815\uC774 \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4."));
3885
+ } else {
3886
+ line(import_chalk3.default.yellow("\u26A0 \uC124\uC815\uC774 \uD544\uC694\uD569\uB2C8\uB2E4: nhncloud configure"));
3887
+ }
3888
+ line();
3889
+ }
3890
+ function createDoctorCommand(dependencies = defaultDependencies2) {
3891
+ return new import_commander4.Command("doctor").description("\uC790\uACA9\uC99D\uBA85\xB7\uC124\uC815\xB7\uC2A4\uD0AC \uC0C1\uD0DC\uB97C \uC9C4\uB2E8\uD55C\uB2E4(\uAE30\uBCF8 \uC624\uD504\uB77C\uC778, --check-connection \uC73C\uB85C \uC5F0\uACB0 \uD655\uC778)").option("--profile <name>", "\uC9C4\uB2E8\uD560 profile \uC774\uB984").option("--check-connection", "\uB300\uC0C1 profile \uC758 \uC790\uACA9\uC99D\uBA85\uC73C\uB85C \uC2E4\uC81C \uC5F0\uACB0\uC744 \uD655\uC778\uD55C\uB2E4 (\uC678\uBD80 API \uD638\uCD9C)").action(async (_opts, cmd) => {
3892
+ const opts = cmd.optsWithGlobals();
3893
+ const report = await buildDoctorReport(
3894
+ { profile: opts.profile, checkConnection: opts.checkConnection },
3895
+ dependencies
3896
+ );
3897
+ if (opts.json) {
3898
+ printJson(report);
3899
+ } else if (opts.quiet) {
3900
+ process.stdout.write(report.ready ? "ready\n" : "not-ready\n");
3901
+ } else {
3902
+ printText(report);
3903
+ }
3904
+ });
3905
+ }
3906
+ var doctorCommand = createDoctorCommand();
3907
+
3908
+ // src/commands/logncrash/search.ts
3277
3909
  var import_commander5 = require("commander");
3278
3910
 
3279
3911
  // src/utils/time.ts
@@ -3520,19 +4152,19 @@ var VALID_LEVELS = ["DEBUG", "INFO", "WARN", "ERROR", "FATAL"];
3520
4152
  function resolveBody(opts) {
3521
4153
  if (opts.body !== void 0) return opts.body;
3522
4154
  if (opts.file !== void 0) {
3523
- let stat3;
4155
+ let stat4;
3524
4156
  try {
3525
- stat3 = (0, import_node_fs3.statSync)(opts.file);
4157
+ stat4 = (0, import_node_fs3.statSync)(opts.file);
3526
4158
  } catch (e) {
3527
4159
  const reason = e.code ?? (e instanceof Error ? e.message : String(e));
3528
4160
  throw new NhnCloudCliError(`\uB85C\uADF8 \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${opts.file} (${reason})`, EXIT_PARAM_ERROR);
3529
4161
  }
3530
- if (!stat3.isFile()) {
4162
+ if (!stat4.isFile()) {
3531
4163
  throw new NhnCloudCliError(`\uB85C\uADF8 \uD30C\uC77C\uC774 \uC77C\uBC18 \uD30C\uC77C\uC774 \uC544\uB2D9\uB2C8\uB2E4: ${opts.file}`, EXIT_PARAM_ERROR);
3532
4164
  }
3533
- if (stat3.size > MAX_LOG_BYTES) {
4165
+ if (stat4.size > MAX_LOG_BYTES) {
3534
4166
  throw new NhnCloudCliError(
3535
- `\uB85C\uADF8 \uD30C\uC77C\uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4: ${stat3.size} \uBC14\uC774\uD2B8 (\uD55C\uB3C4 ${MAX_LOG_BYTES} \uBC14\uC774\uD2B8).`,
4167
+ `\uB85C\uADF8 \uD30C\uC77C\uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4: ${stat4.size} \uBC14\uC774\uD2B8 (\uD55C\uB3C4 ${MAX_LOG_BYTES} \uBC14\uC774\uD2B8).`,
3536
4168
  EXIT_PARAM_ERROR
3537
4169
  );
3538
4170
  }
@@ -3603,22 +4235,22 @@ credentials.json \uC5D0 "appkey": "<appkey>" \uB97C \uCD94\uAC00\uD558\uC138\uC6
3603
4235
 
3604
4236
  // src/commands/logncrash/export.ts
3605
4237
  var import_commander7 = require("commander");
3606
- var import_promises5 = require("fs/promises");
4238
+ var import_promises4 = require("fs/promises");
3607
4239
  var import_node_fs4 = require("fs");
3608
4240
  var import_node_crypto4 = require("crypto");
3609
4241
  var MAX_TOTAL = 1e5;
3610
- var defaultExportFileOps = { appendFile: import_promises5.appendFile, rename: import_promises5.rename };
3611
- function assertWritable(path5, force) {
4242
+ var defaultExportFileOps = { appendFile: import_promises4.appendFile, rename: import_promises4.rename };
4243
+ function assertWritable(path4, force) {
3612
4244
  if (force) return;
3613
4245
  try {
3614
- (0, import_node_fs4.statSync)(path5);
4246
+ (0, import_node_fs4.statSync)(path4);
3615
4247
  } catch (e) {
3616
4248
  if (e.code === "ENOENT") return;
3617
4249
  const reason = e.code ?? (e instanceof Error ? e.message : String(e));
3618
- throw new NhnCloudCliError(`--output \uACBD\uB85C\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${path5} (${reason})`, EXIT_PARAM_ERROR);
4250
+ throw new NhnCloudCliError(`--output \uACBD\uB85C\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${path4} (${reason})`, EXIT_PARAM_ERROR);
3619
4251
  }
3620
4252
  throw new NhnCloudCliError(
3621
- `--output \uB300\uC0C1\uC774 \uC774\uBBF8 \uC874\uC7AC\uD569\uB2C8\uB2E4: ${path5}. \uB36E\uC5B4\uC4F0\uB824\uBA74 --force \uB97C \uC4F0\uC138\uC694.`,
4253
+ `--output \uB300\uC0C1\uC774 \uC774\uBBF8 \uC874\uC7AC\uD569\uB2C8\uB2E4: ${path4}. \uB36E\uC5B4\uC4F0\uB824\uBA74 --force \uB97C \uC4F0\uC138\uC694.`,
3622
4254
  EXIT_PARAM_ERROR
3623
4255
  );
3624
4256
  }
@@ -3729,7 +4361,7 @@ function createExportCommand(finalizeOps) {
3729
4361
  } catch (err) {
3730
4362
  if (failureOrigin !== "search" || !(err instanceof LogncrashServerError)) throw err;
3731
4363
  await endAndClose(stream);
3732
- await (0, import_promises5.truncate)(tmp, checkpoint.bytesWritten);
4364
+ await (0, import_promises4.truncate)(tmp, checkpoint.bytesWritten);
3733
4365
  stream = (0, import_node_fs4.createWriteStream)(tmp, { encoding: "utf-8", flags: "a" });
3734
4366
  bytePosition = checkpoint.bytesWritten;
3735
4367
  count = checkpoint.count;
@@ -3756,14 +4388,14 @@ function createExportCommand(finalizeOps) {
3756
4388
  stream.end();
3757
4389
  });
3758
4390
  if (count === 0) {
3759
- await (0, import_promises5.rm)(tmp, { force: true }).catch(() => {
4391
+ await (0, import_promises4.rm)(tmp, { force: true }).catch(() => {
3760
4392
  });
3761
4393
  throw failure;
3762
4394
  }
3763
4395
  const partial = `${opts.output}.partial`;
3764
4396
  try {
3765
- if (format === "json") await (0, import_promises5.appendFile)(tmp, "]\n");
3766
- await (0, import_promises5.rename)(tmp, partial);
4397
+ if (format === "json") await (0, import_promises4.appendFile)(tmp, "]\n");
4398
+ await (0, import_promises4.rename)(tmp, partial);
3767
4399
  } catch {
3768
4400
  process.stderr.write(
3769
4401
  `\uACBD\uACE0: \uBD80\uBD84 \uACB0\uACFC\uB97C ${partial} \uC5D0 \uB0A8\uAE30\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uC784\uC2DC \uD30C\uC77C: ${tmp}
@@ -3805,7 +4437,7 @@ function createExportCommand(finalizeOps) {
3805
4437
  throw new NhnCloudCliError(message, EXIT_PARAM_ERROR);
3806
4438
  }
3807
4439
  stopSpinner(true, `${count}\uAC74 \uCD94\uCD9C \uC644\uB8CC`);
3808
- await (0, import_promises5.rm)(`${opts.output}.partial`, { force: true }).catch(() => {
4440
+ await (0, import_promises4.rm)(`${opts.output}.partial`, { force: true }).catch(() => {
3809
4441
  });
3810
4442
  if (count >= MAX_TOTAL && (total > MAX_TOTAL || hasUnqueriedWindows)) {
3811
4443
  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`;
@@ -4423,7 +5055,7 @@ var binariesCommand = new import_commander15.Command("binaries").description("\u
4423
5055
  // src/commands/deploy/upload.ts
4424
5056
  var import_commander16 = require("commander");
4425
5057
  var import_node_fs5 = require("fs");
4426
- var import_node_path7 = require("path");
5058
+ var import_node_path6 = require("path");
4427
5059
  var MAX_UPLOAD_BYTES = 512 * 1024 * 1024;
4428
5060
  function parsePositiveInt2(value, flag) {
4429
5061
  if (value === void 0) return void 0;
@@ -4443,9 +5075,9 @@ var uploadCommand = new import_commander16.Command("upload").description("\uB85C
4443
5075
  }
4444
5076
  const artifactId = requireCoordinate(opts.artifactId, "--artifact-id");
4445
5077
  const filePath = opts.file;
4446
- let stat3;
5078
+ let stat4;
4447
5079
  try {
4448
- stat3 = (0, import_node_fs5.statSync)(filePath);
5080
+ stat4 = (0, import_node_fs5.statSync)(filePath);
4449
5081
  } catch (e) {
4450
5082
  const reason = e.code ?? (e instanceof Error ? e.message : String(e));
4451
5083
  throw new NhnCloudCliError(
@@ -4453,17 +5085,17 @@ var uploadCommand = new import_commander16.Command("upload").description("\uB85C
4453
5085
  EXIT_PARAM_ERROR
4454
5086
  );
4455
5087
  }
4456
- if (!stat3.isFile()) {
5088
+ if (!stat4.isFile()) {
4457
5089
  throw new NhnCloudCliError(`--file \uC774 \uC77C\uBC18 \uD30C\uC77C\uC774 \uC544\uB2D9\uB2C8\uB2E4: ${filePath}`, EXIT_PARAM_ERROR);
4458
5090
  }
4459
- if (stat3.size > MAX_UPLOAD_BYTES) {
5091
+ if (stat4.size > MAX_UPLOAD_BYTES) {
4460
5092
  throw new NhnCloudCliError(
4461
- `--file \uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4 (${stat3.size} \uBC14\uC774\uD2B8). \uC5C5\uB85C\uB4DC \uD55C\uB3C4 ${MAX_UPLOAD_BYTES} \uBC14\uC774\uD2B8.`,
5093
+ `--file \uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4 (${stat4.size} \uBC14\uC774\uD2B8). \uC5C5\uB85C\uB4DC \uD55C\uB3C4 ${MAX_UPLOAD_BYTES} \uBC14\uC774\uD2B8.`,
4462
5094
  EXIT_PARAM_ERROR
4463
5095
  );
4464
5096
  }
4465
5097
  const fileBuffer = (0, import_node_fs5.readFileSync)(filePath);
4466
- const fileName = (0, import_node_path7.basename)(filePath);
5098
+ const fileName = (0, import_node_path6.basename)(filePath);
4467
5099
  const { client, profileName } = await createDeployClient(opts.profile);
4468
5100
  const appKey = await resolveDeployAppKey(profileName);
4469
5101
  startSpinner("\uBC14\uC774\uB108\uB9AC \uC5C5\uB85C\uB4DC \uC911...");
@@ -4509,20 +5141,20 @@ function parsePositiveInt3(value, flag) {
4509
5141
  }
4510
5142
  return Number(value);
4511
5143
  }
4512
- function assertWritable2(path5, force) {
5144
+ function assertWritable2(path4, force) {
4513
5145
  if (force) return;
4514
5146
  try {
4515
- (0, import_node_fs6.statSync)(path5);
5147
+ (0, import_node_fs6.statSync)(path4);
4516
5148
  } catch (e) {
4517
5149
  if (e.code === "ENOENT") return;
4518
5150
  const reason = e.code ?? (e instanceof Error ? e.message : String(e));
4519
5151
  throw new NhnCloudCliError(
4520
- `-o \uACBD\uB85C\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${path5} (${reason})`,
5152
+ `-o \uACBD\uB85C\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${path4} (${reason})`,
4521
5153
  EXIT_PARAM_ERROR
4522
5154
  );
4523
5155
  }
4524
5156
  throw new NhnCloudCliError(
4525
- `-o \uB300\uC0C1\uC774 \uC774\uBBF8 \uC874\uC7AC\uD569\uB2C8\uB2E4: ${path5}. \uB36E\uC5B4\uC4F0\uB824\uBA74 --force \uB97C \uC4F0\uC138\uC694.`,
5157
+ `-o \uB300\uC0C1\uC774 \uC774\uBBF8 \uC874\uC7AC\uD569\uB2C8\uB2E4: ${path4}. \uB36E\uC5B4\uC4F0\uB824\uBA74 --force \uB97C \uC4F0\uC138\uC694.`,
4526
5158
  EXIT_PARAM_ERROR
4527
5159
  );
4528
5160
  }
@@ -4865,8 +5497,8 @@ var InstanceClient = class {
4865
5497
  return this.serverAction(id, { revertResize: null });
4866
5498
  }
4867
5499
  async listFlavors(params = {}) {
4868
- const path5 = params.detail ? "/flavors/detail" : "/flavors";
4869
- const url = `${this.computeEndpoint}${path5}`;
5500
+ const path4 = params.detail ? "/flavors/detail" : "/flavors";
5501
+ const url = `${this.computeEndpoint}${path4}`;
4870
5502
  const searchParams = {};
4871
5503
  if (params.minDisk !== void 0) searchParams["minDisk"] = params.minDisk;
4872
5504
  if (params.minRam !== void 0) searchParams["minRam"] = params.minRam;
@@ -5405,13 +6037,10 @@ async function toNetworkWriteError(err) {
5405
6037
  return base;
5406
6038
  }
5407
6039
  let reason = null;
5408
- try {
5409
- const body = await err.response.json();
5410
- const neutron = typeof body === "object" && body !== null ? body["NeutronError"] : void 0;
5411
- const message = typeof neutron === "object" && neutron !== null ? neutron["message"] : void 0;
5412
- if (typeof message === "string") reason = message;
5413
- } catch {
5414
- }
6040
+ const body = await readHttpErrorBody(err);
6041
+ const neutron = typeof body === "object" && body !== null ? body["NeutronError"] : void 0;
6042
+ const message = typeof neutron === "object" && neutron !== null ? neutron["message"] : void 0;
6043
+ if (typeof message === "string") reason = message;
5415
6044
  if (reason === null) return base;
5416
6045
  return new NhnCloudCliError(
5417
6046
  `${base.message}
@@ -6549,9 +7178,9 @@ var createCommand5 = new import_commander33.Command("create").description("\uC77
6549
7178
  }
6550
7179
  let userDataBase64;
6551
7180
  if (opts.userData !== void 0) {
6552
- let stat3;
7181
+ let stat4;
6553
7182
  try {
6554
- stat3 = (0, import_node_fs7.statSync)(opts.userData);
7183
+ stat4 = (0, import_node_fs7.statSync)(opts.userData);
6555
7184
  } catch (e) {
6556
7185
  const reason = e.code ?? (e instanceof Error ? e.message : String(e));
6557
7186
  throw new NhnCloudCliError(
@@ -6559,15 +7188,15 @@ var createCommand5 = new import_commander33.Command("create").description("\uC77
6559
7188
  EXIT_PARAM_ERROR
6560
7189
  );
6561
7190
  }
6562
- if (!stat3.isFile()) {
7191
+ if (!stat4.isFile()) {
6563
7192
  throw new NhnCloudCliError(
6564
7193
  `--user-data \uAC00 \uC77C\uBC18 \uD30C\uC77C\uC774 \uC544\uB2D9\uB2C8\uB2E4: ${opts.userData}`,
6565
7194
  EXIT_PARAM_ERROR
6566
7195
  );
6567
7196
  }
6568
- if (stat3.size > 49149) {
7197
+ if (stat4.size > 49149) {
6569
7198
  throw new NhnCloudCliError(
6570
- `--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.`,
7199
+ `--user-data \uAC00 \uB108\uBB34 \uD07D\uB2C8\uB2E4 (${stat4.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.`,
6571
7200
  EXIT_PARAM_ERROR
6572
7201
  );
6573
7202
  }
@@ -6852,9 +7481,9 @@ var import_node_fs8 = require("fs");
6852
7481
  var import_node_crypto5 = require("crypto");
6853
7482
  var import_chalk12 = __toESM(require("chalk"));
6854
7483
  function resolvePublicKey(value) {
6855
- let stat3;
7484
+ let stat4;
6856
7485
  try {
6857
- stat3 = (0, import_node_fs8.statSync)(value);
7486
+ stat4 = (0, import_node_fs8.statSync)(value);
6858
7487
  } catch (e) {
6859
7488
  const err = e;
6860
7489
  if (err.code && err.code !== "ENOENT") {
@@ -6865,7 +7494,7 @@ function resolvePublicKey(value) {
6865
7494
  }
6866
7495
  return value;
6867
7496
  }
6868
- if (!stat3.isFile()) {
7497
+ if (!stat4.isFile()) {
6869
7498
  throw new NhnCloudCliError(`--public-key \uAC00 \uC77C\uBC18 \uD30C\uC77C\uC774 \uC544\uB2D9\uB2C8\uB2E4: ${value}`, EXIT_PARAM_ERROR);
6870
7499
  }
6871
7500
  return (0, import_node_fs8.readFileSync)(value, "utf-8").trim();
@@ -7172,12 +7801,12 @@ var HarborClient = class {
7172
7801
  * ky.get() 이 Response 를 반환하므로 .json() 과 .headers.get("link") 를 함께 사용한다
7173
7802
  * (체이닝하면 헤더를 못 본다 — 기존 NCR client 의 .json<T>() 체이닝 패턴과 다름).
7174
7803
  */
7175
- async getAllPages(path5) {
7804
+ async getAllPages(path4) {
7176
7805
  const acc = [];
7177
7806
  let page = 1;
7178
7807
  try {
7179
7808
  for (; ; ) {
7180
- const url = `https://${this.host}${path5}?page=${page}&page_size=${PAGE_SIZE}`;
7809
+ const url = `https://${this.host}${path4}?page=${page}&page_size=${PAGE_SIZE}`;
7181
7810
  const res = await import_ky13.default.get(url, {
7182
7811
  headers: this.basicAuthHeaders(),
7183
7812
  retry: 0,
@@ -7417,15 +8046,15 @@ function commandPath(command) {
7417
8046
  return names.join(" ");
7418
8047
  }
7419
8048
  function collectCommand(command) {
7420
- const path5 = commandPath(command);
7421
- if (!path5 || path5 === "help") return null;
8049
+ const path4 = commandPath(command);
8050
+ if (!path4 || path4 === "help") return null;
7422
8051
  return {
7423
- path: path5,
8052
+ path: path4,
7424
8053
  description: command.description() || command.summary() || "",
7425
8054
  arguments: command.registeredArguments.map((arg) => arg.name()),
7426
8055
  options: command.options.filter((option) => !option.hidden).map((option) => option.flags),
7427
8056
  subcommands: command.commands.map((subcommand) => subcommand.name()).filter((name) => name !== "help"),
7428
- ...path5 === "commands" ? { metadata: true } : {}
8057
+ ...path4 === "commands" ? { metadata: true } : {}
7429
8058
  };
7430
8059
  }
7431
8060
  function collectCommandCatalog(program2) {
@@ -7464,7 +8093,7 @@ function createCommandsCommand(program2) {
7464
8093
  var import_commander48 = require("commander");
7465
8094
 
7466
8095
  // src/commands/nks/helpers.ts
7467
- var import_promises6 = require("fs/promises");
8096
+ var import_promises5 = require("fs/promises");
7468
8097
 
7469
8098
  // src/services/nks/client.ts
7470
8099
  var import_ky14 = __toESM(require("ky"));
@@ -7914,15 +8543,15 @@ var NksClient = class {
7914
8543
  throw toNhnCloudCliError(err);
7915
8544
  }
7916
8545
  }
7917
- async getFlatNamedResource(path5, errorMessage) {
7918
- const raw = await this.getJson(path5);
8546
+ async getFlatNamedResource(path4, errorMessage) {
8547
+ const raw = await this.getJson(path4);
7919
8548
  if (!isNksNamedResource(raw)) {
7920
8549
  throw new NhnCloudCliError(errorMessage, EXIT_API_ERROR);
7921
8550
  }
7922
8551
  return raw;
7923
8552
  }
7924
- async getJson(path5, searchParams) {
7925
- const url = `${this.nksEndpoint}${path5}`;
8553
+ async getJson(path4, searchParams) {
8554
+ const url = `${this.nksEndpoint}${path4}`;
7926
8555
  try {
7927
8556
  return await import_ky14.default.get(url, {
7928
8557
  headers: this.authHeaders(),
@@ -7934,15 +8563,15 @@ var NksClient = class {
7934
8563
  throw toNhnCloudCliError(err);
7935
8564
  }
7936
8565
  }
7937
- async requestUuid(method, path5, payload, errorMessage) {
7938
- const raw = await this.requestJson(method, path5, payload);
8566
+ async requestUuid(method, path4, payload, errorMessage) {
8567
+ const raw = await this.requestJson(method, path4, payload);
7939
8568
  if (!isNksUuidResponse(raw)) {
7940
8569
  throw new NhnCloudCliError(errorMessage, EXIT_API_ERROR);
7941
8570
  }
7942
8571
  return raw;
7943
8572
  }
7944
- async requestNoBody(method, path5, payload) {
7945
- const url = `${this.nksEndpoint}${path5}`;
8573
+ async requestNoBody(method, path4, payload) {
8574
+ const url = `${this.nksEndpoint}${path4}`;
7946
8575
  try {
7947
8576
  if (method === "delete") {
7948
8577
  await import_ky14.default.delete(url, { headers: this.authHeaders(), retry: 0, timeout: DEFAULT_TIMEOUT_MS });
@@ -7953,8 +8582,8 @@ var NksClient = class {
7953
8582
  throw toNhnCloudCliError(err);
7954
8583
  }
7955
8584
  }
7956
- async requestJson(method, path5, payload) {
7957
- const url = `${this.nksEndpoint}${path5}`;
8585
+ async requestJson(method, path4, payload) {
8586
+ const url = `${this.nksEndpoint}${path4}`;
7958
8587
  try {
7959
8588
  const options = { headers: this.authHeaders(), json: payload, retry: 0, timeout: DEFAULT_TIMEOUT_MS };
7960
8589
  return method === "post" ? await import_ky14.default.post(url, options).json() : await import_ky14.default.patch(url, options).json();
@@ -7993,13 +8622,13 @@ async function resolveClusterUuid(client, cluster) {
7993
8622
  }
7994
8623
  return uuid;
7995
8624
  }
7996
- async function readJsonFile(path5) {
8625
+ async function readJsonFile(path4) {
7997
8626
  let raw;
7998
8627
  try {
7999
- raw = await (0, import_promises6.readFile)(path5, "utf-8");
8628
+ raw = await (0, import_promises5.readFile)(path4, "utf-8");
8000
8629
  } catch (err) {
8001
8630
  const message = err instanceof Error ? err.message : String(err);
8002
- throw new NhnCloudCliError(`JSON \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${path5} (${message})`, EXIT_PARAM_ERROR);
8631
+ throw new NhnCloudCliError(`JSON \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${path4} (${message})`, EXIT_PARAM_ERROR);
8003
8632
  }
8004
8633
  try {
8005
8634
  const parsed = JSON.parse(raw);
@@ -8009,15 +8638,15 @@ async function readJsonFile(path5) {
8009
8638
  return parsed;
8010
8639
  } catch (err) {
8011
8640
  const message = err instanceof Error ? err.message : String(err);
8012
- throw new NhnCloudCliError(`JSON \uD30C\uC77C \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4: ${path5} (${message})`, EXIT_PARAM_ERROR);
8641
+ throw new NhnCloudCliError(`JSON \uD30C\uC77C \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4: ${path4} (${message})`, EXIT_PARAM_ERROR);
8013
8642
  }
8014
8643
  }
8015
- async function readTextFile(path5) {
8644
+ async function readTextFile(path4) {
8016
8645
  try {
8017
- return await (0, import_promises6.readFile)(path5, "utf-8");
8646
+ return await (0, import_promises5.readFile)(path4, "utf-8");
8018
8647
  } catch (err) {
8019
8648
  const message = err instanceof Error ? err.message : String(err);
8020
- throw new NhnCloudCliError(`\uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${path5} (${message})`, EXIT_PARAM_ERROR);
8649
+ throw new NhnCloudCliError(`\uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${path4} (${message})`, EXIT_PARAM_ERROR);
8021
8650
  }
8022
8651
  }
8023
8652
  function parsePositiveInteger(value, optionName) {
@@ -8058,7 +8687,7 @@ var supportsCommand = new import_commander48.Command("supports").description("NK
8058
8687
  });
8059
8688
 
8060
8689
  // src/commands/nks/cluster.ts
8061
- var import_promises7 = require("fs/promises");
8690
+ var import_promises6 = require("fs/promises");
8062
8691
  var import_commander49 = require("commander");
8063
8692
  var import_chalk14 = __toESM(require("chalk"));
8064
8693
  var listCommand8 = new import_commander49.Command("list").description("NKS \uD074\uB7EC\uC2A4\uD130 \uBAA9\uB85D\uC744 \uC870\uD68C\uD55C\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 (_opts, cmd) => {
@@ -8149,18 +8778,18 @@ async function confirmDangerousAction(message, yes) {
8149
8778
  }
8150
8779
  return ok;
8151
8780
  }
8152
- async function writeKubeconfigFile(path5, contents, force) {
8781
+ async function writeKubeconfigFile(path4, contents, force) {
8153
8782
  try {
8154
- await (0, import_promises7.writeFile)(path5, contents, {
8783
+ await (0, import_promises6.writeFile)(path4, contents, {
8155
8784
  encoding: "utf-8",
8156
8785
  mode: 384,
8157
8786
  flag: force ? "w" : "wx"
8158
8787
  });
8159
- await (0, import_promises7.chmod)(path5, 384);
8788
+ await (0, import_promises6.chmod)(path4, 384);
8160
8789
  } catch (err) {
8161
8790
  if (err instanceof Error && "code" in err && err.code === "EEXIST") {
8162
8791
  throw new NhnCloudCliError(
8163
- `\uD30C\uC77C\uC774 \uC774\uBBF8 \uC874\uC7AC\uD569\uB2C8\uB2E4: ${path5}. \uB36E\uC5B4\uC4F0\uB824\uBA74 --force \uB97C \uC9C0\uC815\uD558\uC138\uC694.`,
8792
+ `\uD30C\uC77C\uC774 \uC774\uBBF8 \uC874\uC7AC\uD569\uB2C8\uB2E4: ${path4}. \uB36E\uC5B4\uC4F0\uB824\uBA74 --force \uB97C \uC9C0\uC815\uD558\uC138\uC694.`,
8164
8793
  EXIT_PARAM_ERROR
8165
8794
  );
8166
8795
  }
@@ -8979,9 +9608,9 @@ async function resolveNcsClient(opts) {
8979
9608
  return { client: new NcsClient(accessToken, region, appKey), profileName };
8980
9609
  }
8981
9610
  function readJsonPayload(filePath) {
8982
- let stat3;
9611
+ let stat4;
8983
9612
  try {
8984
- stat3 = (0, import_node_fs9.statSync)(filePath);
9613
+ stat4 = (0, import_node_fs9.statSync)(filePath);
8985
9614
  } catch (err) {
8986
9615
  const reason = err.code ?? (err instanceof Error ? err.message : String(err));
8987
9616
  throw new NhnCloudCliError(
@@ -8989,12 +9618,12 @@ function readJsonPayload(filePath) {
8989
9618
  EXIT_PARAM_ERROR
8990
9619
  );
8991
9620
  }
8992
- if (!stat3.isFile()) {
9621
+ if (!stat4.isFile()) {
8993
9622
  throw new NhnCloudCliError(`--file \uC774 \uC77C\uBC18 \uD30C\uC77C\uC774 \uC544\uB2D9\uB2C8\uB2E4: ${filePath}`, EXIT_PARAM_ERROR);
8994
9623
  }
8995
- if (stat3.size > MAX_JSON_INPUT_BYTES) {
9624
+ if (stat4.size > MAX_JSON_INPUT_BYTES) {
8996
9625
  throw new NhnCloudCliError(
8997
- `--file \uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4 (${stat3.size} \uBC14\uC774\uD2B8). JSON spec \uD55C\uB3C4 ${MAX_JSON_INPUT_BYTES} \uBC14\uC774\uD2B8.`,
9626
+ `--file \uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4 (${stat4.size} \uBC14\uC774\uD2B8). JSON spec \uD55C\uB3C4 ${MAX_JSON_INPUT_BYTES} \uBC14\uC774\uD2B8.`,
8998
9627
  EXIT_PARAM_ERROR
8999
9628
  );
9000
9629
  }
@@ -9725,7 +10354,7 @@ var malwareCommand = new import_commander54.Command("malware").description("NCS
9725
10354
  var import_commander55 = require("commander");
9726
10355
 
9727
10356
  // src/commands/apigateway/helpers.ts
9728
- var import_promises8 = require("fs/promises");
10357
+ var import_promises7 = require("fs/promises");
9729
10358
 
9730
10359
  // src/services/apigateway/client.ts
9731
10360
  var import_ky15 = __toESM(require("ky"));
@@ -10336,21 +10965,21 @@ function requireYes2(yes, operation) {
10336
10965
  }
10337
10966
  return true;
10338
10967
  }
10339
- async function readPluginConfigFile(path5) {
10968
+ async function readPluginConfigFile(path4) {
10340
10969
  let fileStat;
10341
10970
  try {
10342
- fileStat = await (0, import_promises8.stat)(path5);
10971
+ fileStat = await (0, import_promises7.stat)(path4);
10343
10972
  } catch (error) {
10344
10973
  const reason = fileErrorReason(error);
10345
10974
  throw new NhnCloudCliError(
10346
- `\uD50C\uB7EC\uADF8\uC778 \uC124\uC815 \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${JSON.stringify(path5)} (${JSON.stringify(reason)}).`,
10975
+ `\uD50C\uB7EC\uADF8\uC778 \uC124\uC815 \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${JSON.stringify(path4)} (${JSON.stringify(reason)}).`,
10347
10976
  EXIT_PARAM_ERROR
10348
10977
  );
10349
10978
  }
10350
10979
  if (!fileStat.isFile()) {
10351
10980
  const reason = fileStat.isDirectory() ? "EISDIR" : "EINVAL";
10352
10981
  throw new NhnCloudCliError(
10353
- `\uD50C\uB7EC\uADF8\uC778 \uC124\uC815 \uACBD\uB85C\uAC00 \uC77C\uBC18 \uD30C\uC77C\uC774 \uC544\uB2D9\uB2C8\uB2E4: ${JSON.stringify(path5)} (${reason}).`,
10982
+ `\uD50C\uB7EC\uADF8\uC778 \uC124\uC815 \uACBD\uB85C\uAC00 \uC77C\uBC18 \uD30C\uC77C\uC774 \uC544\uB2D9\uB2C8\uB2E4: ${JSON.stringify(path4)} (${reason}).`,
10354
10983
  EXIT_PARAM_ERROR
10355
10984
  );
10356
10985
  }
@@ -10362,11 +10991,11 @@ async function readPluginConfigFile(path5) {
10362
10991
  }
10363
10992
  let raw;
10364
10993
  try {
10365
- raw = await (0, import_promises8.readFile)(path5, "utf-8");
10994
+ raw = await (0, import_promises7.readFile)(path4, "utf-8");
10366
10995
  } catch (error) {
10367
10996
  const reason = fileErrorReason(error);
10368
10997
  throw new NhnCloudCliError(
10369
- `\uD50C\uB7EC\uADF8\uC778 \uC124\uC815 \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${JSON.stringify(path5)} (${JSON.stringify(reason)}).`,
10998
+ `\uD50C\uB7EC\uADF8\uC778 \uC124\uC815 \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${JSON.stringify(path4)} (${JSON.stringify(reason)}).`,
10370
10999
  EXIT_PARAM_ERROR
10371
11000
  );
10372
11001
  }
@@ -10377,7 +11006,7 @@ async function readPluginConfigFile(path5) {
10377
11006
  error instanceof Error ? error.message : String(error)
10378
11007
  );
10379
11008
  throw new NhnCloudCliError(
10380
- `\uD50C\uB7EC\uADF8\uC778 \uC124\uC815 JSON \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4: ${JSON.stringify(path5)} (${reason}).`,
11009
+ `\uD50C\uB7EC\uADF8\uC778 \uC124\uC815 JSON \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4: ${JSON.stringify(path4)} (${reason}).`,
10381
11010
  EXIT_PARAM_ERROR
10382
11011
  );
10383
11012
  }
@@ -10821,7 +11450,7 @@ var setMethodPluginCommand = addApiGatewayOptions(
10821
11450
  var resourceCommand = new import_commander56.Command("resource").description("API Gateway resource \uC870\uD68C \uBC0F \uBCC0\uACBD \uBA85\uB839").addCommand(listCommand13).addCommand(parametersCommand).addCommand(responsesCommand).addCommand(setPathPluginCommand).addCommand(setMethodPluginCommand);
10822
11451
 
10823
11452
  // src/commands/apigateway/stage.ts
10824
- var import_promises9 = require("fs/promises");
11453
+ var import_promises8 = require("fs/promises");
10825
11454
  var import_commander58 = require("commander");
10826
11455
 
10827
11456
  // src/commands/apigateway/deploy.ts
@@ -11028,9 +11657,9 @@ function fileErrorReason2(error) {
11028
11657
  }
11029
11658
  return error instanceof Error ? error.message : String(error);
11030
11659
  }
11031
- async function writeStageSwaggerFile(path5, swagger, force) {
11660
+ async function writeStageSwaggerFile(path4, swagger, force) {
11032
11661
  try {
11033
- await (0, import_promises9.writeFile)(path5, JSON.stringify(swagger, null, 2) + "\n", {
11662
+ await (0, import_promises8.writeFile)(path4, JSON.stringify(swagger, null, 2) + "\n", {
11034
11663
  encoding: "utf-8",
11035
11664
  flag: force ? "w" : "wx"
11036
11665
  });
@@ -11038,7 +11667,7 @@ async function writeStageSwaggerFile(path5, swagger, force) {
11038
11667
  const reason = fileErrorReason2(error);
11039
11668
  const guidance = reason === "EEXIST" ? " \uB36E\uC5B4\uC4F0\uB824\uBA74 --force \uB97C \uC9C0\uC815\uD558\uC138\uC694." : "";
11040
11669
  throw new NhnCloudCliError(
11041
- `Swagger \uD30C\uC77C \uC800\uC7A5 \uC2E4\uD328: ${JSON.stringify(path5)} (${JSON.stringify(reason)}).${guidance}`,
11670
+ `Swagger \uD30C\uC77C \uC800\uC7A5 \uC2E4\uD328: ${JSON.stringify(path4)} (${JSON.stringify(reason)}).${guidance}`,
11042
11671
  EXIT_PARAM_ERROR
11043
11672
  );
11044
11673
  }
@@ -11200,19 +11829,1343 @@ var importResourcesCommand = addApiGatewayOptions3(
11200
11829
  });
11201
11830
  var stageCommand = new import_commander58.Command("stage").description("API Gateway stage \uC870\uD68C \uBC0F \uBCC0\uACBD \uBA85\uB839").addCommand(listCommand15).addCommand(swaggerCommand).addCommand(resourcesCommand).addCommand(updateCommand3).addCommand(importResourcesCommand).addCommand(deployCommand);
11202
11831
 
11203
- // src/commands/loadbalancer/list.ts
11832
+ // src/commands/skm/confirm.ts
11204
11833
  var import_commander59 = require("commander");
11205
11834
 
11206
- // src/commands/loadbalancer/helpers.ts
11835
+ // src/commands/skm/helpers.ts
11207
11836
  var import_node_net2 = require("net");
11208
11837
 
11209
- // src/services/loadbalancer/client.ts
11838
+ // src/services/skm/client.ts
11210
11839
  var import_ky16 = __toESM(require("ky"));
11211
11840
 
11212
- // src/services/loadbalancer/types.ts
11841
+ // src/services/skm/types.ts
11213
11842
  function isRecord3(value) {
11214
11843
  return typeof value === "object" && value !== null;
11215
11844
  }
11845
+ function hasStrings(obj, keys) {
11846
+ return keys.every((key) => typeof obj[key] === "string");
11847
+ }
11848
+ function hasNumbers(obj, keys) {
11849
+ return keys.every((key) => typeof obj[key] === "number");
11850
+ }
11851
+ function hasOptional(obj, keys, type) {
11852
+ return keys.every((key) => obj[key] === void 0 || obj[key] === null || typeof obj[key] === type);
11853
+ }
11854
+ var AUDIT_FIELDS = ["creationUser", "creationDatetime", "lastChangeUser", "lastChangeDatetime"];
11855
+ function isSkmClientInfo(value) {
11856
+ if (!isRecord3(value)) return false;
11857
+ return typeof value["clientIp"] === "string" && typeof value["clientSentCertificate"] === "boolean" && hasOptional(value, ["clientMacHeader"], "string");
11858
+ }
11859
+ function isSkmKeyStore(value) {
11860
+ if (!isRecord3(value)) return false;
11861
+ return typeof value["keyStoreId"] === "number" && hasStrings(value, ["name", "ip4AuthUse", "macAuthUse", "certificateAuthUse"]) && hasOptional(value, ["description", ...AUDIT_FIELDS], "string");
11862
+ }
11863
+ function isSkmKey(value) {
11864
+ if (!isRecord3(value)) return false;
11865
+ return hasStrings(value, ["keyId", "name", "keyType"]) && typeof value["currentKeyValueVersion"] === "number" && hasOptional(value, ["autoRotationPeriod"], "number") && hasOptional(
11866
+ value,
11867
+ ["description", "nextAutoRotationDate", "lastAccessDatetime", "deletionDatetime", ...AUDIT_FIELDS],
11868
+ "string"
11869
+ );
11870
+ }
11871
+ function isSkmAuthDetail(value) {
11872
+ if (!isRecord3(value)) return false;
11873
+ return (typeof value["value"] === "string" || typeof value["name"] === "string") && hasOptional(
11874
+ value,
11875
+ [
11876
+ "value",
11877
+ "name",
11878
+ "password",
11879
+ "description",
11880
+ "expirationDate",
11881
+ "lastAccessDatetime",
11882
+ "deletionDatetime",
11883
+ ...AUDIT_FIELDS
11884
+ ],
11885
+ "string"
11886
+ );
11887
+ }
11888
+ function isSkmEncryptResult(value) {
11889
+ return isRecord3(value) && hasStrings(value, ["ciphertext"]) && hasNumbers(value, ["keyVersion"]);
11890
+ }
11891
+ function isSkmDecryptResult(value) {
11892
+ return isRecord3(value) && hasStrings(value, ["plaintext"]) && hasNumbers(value, ["keyVersion"]);
11893
+ }
11894
+ function isSkmLocalKey(value) {
11895
+ return isRecord3(value) && hasStrings(value, ["localKeyPlaintext", "localKeyCiphertext"]) && hasNumbers(value, ["keyVersion"]);
11896
+ }
11897
+ function isSkmSymmetricKey(value) {
11898
+ return isRecord3(value) && hasStrings(value, ["symmetricKey"]) && hasNumbers(value, ["keyVersion"]);
11899
+ }
11900
+ function isSkmSignResult(value) {
11901
+ return isRecord3(value) && hasStrings(value, ["signature"]) && hasNumbers(value, ["keyVersion"]);
11902
+ }
11903
+ function isSkmStandardSignResult(value) {
11904
+ return isRecord3(value) && hasStrings(value, ["signature", "algorithm", "hashAlgorithm", "mgfAlgorithm"]) && hasNumbers(value, ["keyVersion", "saltLength"]);
11905
+ }
11906
+ function isSkmVerifyResult(value) {
11907
+ return isRecord3(value) && typeof value["result"] === "boolean" && hasNumbers(value, ["keyVersion"]);
11908
+ }
11909
+ function isSkmAsymmetricKeyMaterial(value) {
11910
+ return isRecord3(value) && hasStrings(value, ["keyType", "key", "encodedKey"]) && hasOptional(value, ["standardEncodedKey"], "string") && hasNumbers(value, ["keyVersion"]);
11911
+ }
11912
+ function isSkmCreatedKey(value) {
11913
+ return isRecord3(value) && hasStrings(value, ["keyId", "keyStatus"]);
11914
+ }
11915
+ function isSkmCreatedKeyStore(value) {
11916
+ if (!isRecord3(value)) return false;
11917
+ return typeof value["keyStoreId"] === "number" && hasStrings(value, ["name", "ip4AuthUse", "macAuthUse", "certificateAuthUse"]) && hasOptional(value, ["description", "authMode"], "string");
11918
+ }
11919
+ function isSkmUpdatedSecret(value) {
11920
+ if (!isRecord3(value)) return false;
11921
+ return hasStrings(value, ["keyId", "name"]) && hasOptional(value, ["description", "secretValue", ...AUDIT_FIELDS], "string");
11922
+ }
11923
+ function isSkmDeletion(value) {
11924
+ return isRecord3(value) && hasStrings(value, ["keyId", "deletionDateTime"]);
11925
+ }
11926
+ function hasAuthIdentifier(obj) {
11927
+ return (typeof obj["value"] === "string" || typeof obj["name"] === "string") && hasOptional(obj, ["value", "name"], "string");
11928
+ }
11929
+ function isSkmAuthAdded(value) {
11930
+ return isRecord3(value) && hasAuthIdentifier(value) && hasOptional(value, ["description"], "string");
11931
+ }
11932
+ function isSkmAuthDeletion(value) {
11933
+ return isRecord3(value) && hasAuthIdentifier(value) && hasStrings(value, ["deletionDateTime"]);
11934
+ }
11935
+
11936
+ // src/services/skm/client.ts
11937
+ var KEY_PAGE_SIZE = 100;
11938
+ var STANDARD_SIGN_ALGORITHM = "RSASSA-PSS";
11939
+ var AUTH_PATHS = {
11940
+ ipv4: { path: "ips", field: "ipv4List" },
11941
+ mac: { path: "macs", field: "macList" },
11942
+ certificate: { path: "certificates", field: "certificateList" }
11943
+ };
11944
+ var AUTH_WRITE_PATH = {
11945
+ ipv4: "ipv4s",
11946
+ mac: "macs",
11947
+ certificate: "certificates"
11948
+ };
11949
+ function isRecord4(value) {
11950
+ return typeof value === "object" && value !== null;
11951
+ }
11952
+ function isEnvelope(value) {
11953
+ if (!isRecord4(value)) return false;
11954
+ const header = value["header"];
11955
+ return isRecord4(header) && typeof header["isSuccessful"] === "boolean";
11956
+ }
11957
+ function arrayOf(guard) {
11958
+ return (value) => Array.isArray(value) && value.every(guard);
11959
+ }
11960
+ function isString(value) {
11961
+ return typeof value === "string";
11962
+ }
11963
+ function withDescription(body, description) {
11964
+ return description === void 0 ? body : { ...body, description };
11965
+ }
11966
+ function authDeletionBody(type, keyStoreName, value) {
11967
+ return type === "certificate" ? { keyStoreName, name: value } : { keyStoreName, value };
11968
+ }
11969
+ function keyStoreBody(input) {
11970
+ const { name, description, ip4AuthUse, macAuthUse, certificateAuthUse, authMode } = input;
11971
+ return withDescription({ name, ip4AuthUse, macAuthUse, certificateAuthUse, authMode }, description);
11972
+ }
11973
+ function keyVersionParams(keyVersion) {
11974
+ return keyVersion === void 0 ? {} : { searchParams: { keyVersion } };
11975
+ }
11976
+ async function toSkmError(err) {
11977
+ if (!(err instanceof import_ky16.HTTPError)) return toNhnCloudCliError(err);
11978
+ let resultMessage = null;
11979
+ const body = await readHttpErrorBody(err);
11980
+ const header = isRecord4(body) ? body["header"] : void 0;
11981
+ const message = isRecord4(header) ? header["resultMessage"] : void 0;
11982
+ if (typeof message === "string") resultMessage = message;
11983
+ if (resultMessage === null) return toNhnCloudCliError(err);
11984
+ const status = err.response.status;
11985
+ return new NhnCloudCliError(
11986
+ `API \uD638\uCD9C \uC2E4\uD328 (${status}): ${sanitizeForTerminal(resultMessage)}`,
11987
+ status === 401 || status === 403 ? EXIT_AUTH_ERROR : EXIT_API_ERROR
11988
+ );
11989
+ }
11990
+ var SkmClient = class {
11991
+ baseUrl;
11992
+ headers;
11993
+ constructor(accessToken, environment, appKey, macAddress) {
11994
+ this.baseUrl = `${endpointFor("skm", environment)}/keymanager/v1.3/appkey/${encodeURIComponent(appKey)}`;
11995
+ this.headers = { "X-NHN-Authorization": `Bearer ${accessToken}` };
11996
+ if (macAddress !== void 0) {
11997
+ this.headers["X-TOAST-CLIENT-MAC-ADDR"] = macAddress;
11998
+ }
11999
+ }
12000
+ /**
12001
+ * 요청을 보내고 봉투 형태인지만 확인해 반환한다. 성공 판정은 호출부가 한다.
12002
+ * 쓰기 요청도 중복 실행을 막으려고 재시도하지 않는다.
12003
+ */
12004
+ async send(method, path4, options) {
12005
+ let response;
12006
+ try {
12007
+ response = await import_ky16.default[method](`${this.baseUrl}${path4}`, {
12008
+ headers: this.headers,
12009
+ ...options,
12010
+ retry: 0,
12011
+ timeout: DEFAULT_TIMEOUT_MS
12012
+ }).json();
12013
+ } catch (err) {
12014
+ throw await toSkmError(err);
12015
+ }
12016
+ if (!isEnvelope(response)) {
12017
+ throw new NhnCloudCliError("Secure Key Manager \uC751\uB2F5 \uD615\uC2DD \uC624\uB958: header", EXIT_API_ERROR);
12018
+ }
12019
+ return response;
12020
+ }
12021
+ /**
12022
+ * 요청을 보내고 봉투를 벗긴 뒤 body 또는 body[field] 를 가드로 검사해 반환한다.
12023
+ * field 가 null 이면 body 전체를 검사한다.
12024
+ */
12025
+ async request(method, path4, field, guard, options = {}) {
12026
+ const body = unwrap(await this.send(method, path4, options));
12027
+ const value = field === null ? body : isRecord4(body) ? body[field] : void 0;
12028
+ if (!guard(value)) {
12029
+ throw new NhnCloudCliError(`Secure Key Manager \uC751\uB2F5 \uD615\uC2DD \uC624\uB958: ${field ?? "body"}`, EXIT_API_ERROR);
12030
+ }
12031
+ return value;
12032
+ }
12033
+ /**
12034
+ * body 가 null 이거나 없는 응답(키 저장소 수정·삭제)은 검사할 필드가 없어 헤더만 본다 (ADR-040).
12035
+ * isSuccessful 이 false 면 unwrapHeader 가 NhnEnvelopeError 를 던진다.
12036
+ */
12037
+ async requestHeaderOnly(method, path4, options = {}) {
12038
+ unwrapHeader(await this.send(method, path4, options));
12039
+ }
12040
+ keyPath(prefix, keyId, action) {
12041
+ return `/${prefix}/${encodeURIComponent(keyId)}/${action}`;
12042
+ }
12043
+ /** 호출 클라이언트의 IP·MAC·인증서 전송 여부를 확인한다. */
12044
+ confirm() {
12045
+ return this.request("get", "/confirm", null, isSkmClientInfo);
12046
+ }
12047
+ listKeyStores() {
12048
+ return this.request("get", "/keystores", "keyStoreList", arrayOf(isSkmKeyStore), {
12049
+ searchParams: { detail: "true" }
12050
+ });
12051
+ }
12052
+ getKeyStore(keyStoreId) {
12053
+ return this.request("get", `/keystores/${keyStoreId}`, null, isSkmKeyStore);
12054
+ }
12055
+ /**
12056
+ * 모든 페이지의 키를 모은다.
12057
+ * 서버가 pageNumber 를 무시하면 같은 페이지가 반복되므로, 첫 keyId 가 직전 페이지와 같으면 중단한다.
12058
+ */
12059
+ async listKeys(keyStoreId, filter = {}) {
12060
+ const keys = [];
12061
+ let previousFirstKeyId;
12062
+ for (let pageNumber = 1; ; pageNumber++) {
12063
+ const searchParams = { detail: "true", pageNumber, pageSize: KEY_PAGE_SIZE };
12064
+ if (filter.type) searchParams["type"] = filter.type;
12065
+ if (filter.name) searchParams["name"] = filter.name;
12066
+ if (filter.status) searchParams["status"] = filter.status;
12067
+ const page = await this.request("get", `/keystores/${keyStoreId}/keys`, "keyList", arrayOf(isSkmKey), {
12068
+ searchParams
12069
+ });
12070
+ const firstKeyId = page[0]?.keyId;
12071
+ if (firstKeyId !== void 0 && firstKeyId === previousFirstKeyId) {
12072
+ throw new NhnCloudCliError(
12073
+ `Secure Key Manager \uC751\uB2F5 \uD615\uC2DD \uC624\uB958: ${pageNumber} \uD398\uC774\uC9C0\uAC00 \uC9C1\uC804 \uD398\uC774\uC9C0\uC640 \uAC19\uC740 \uD0A4\uB85C \uC2DC\uC791\uD569\uB2C8\uB2E4.`,
12074
+ EXIT_API_ERROR
12075
+ );
12076
+ }
12077
+ keys.push(...page);
12078
+ if (page.length < KEY_PAGE_SIZE) return keys;
12079
+ previousFirstKeyId = firstKeyId;
12080
+ }
12081
+ }
12082
+ getKey(keyStoreId, keyId) {
12083
+ return this.request("get", `/keystores/${keyStoreId}/keys/${encodeURIComponent(keyId)}`, null, isSkmKey);
12084
+ }
12085
+ listAuths(keyStoreId, type) {
12086
+ const { path: path4, field } = AUTH_PATHS[type];
12087
+ return this.request("get", `/keystores/${keyStoreId}/${path4}`, field, arrayOf(isString));
12088
+ }
12089
+ getAuth(keyStoreId, type, value) {
12090
+ const { path: path4, field } = AUTH_PATHS[type];
12091
+ return this.request("get", `/keystores/${keyStoreId}/${path4}`, field, arrayOf(isSkmAuthDetail), {
12092
+ searchParams: { value }
12093
+ });
12094
+ }
12095
+ getSecret(keyId) {
12096
+ return this.request("get", `/secrets/${encodeURIComponent(keyId)}`, "secret", isString);
12097
+ }
12098
+ encrypt(keyId, plaintext) {
12099
+ return this.request("post", this.keyPath("symmetric-keys", keyId, "encrypt"), null, isSkmEncryptResult, {
12100
+ json: { plaintext }
12101
+ });
12102
+ }
12103
+ decrypt(keyId, ciphertext) {
12104
+ return this.request("post", this.keyPath("symmetric-keys", keyId, "decrypt"), null, isSkmDecryptResult, {
12105
+ json: { ciphertext }
12106
+ });
12107
+ }
12108
+ createLocalKey(keyId) {
12109
+ return this.request("post", this.keyPath("symmetric-keys", keyId, "create-local-key"), null, isSkmLocalKey);
12110
+ }
12111
+ getSymmetricKey(keyId, keyVersion) {
12112
+ return this.request(
12113
+ "get",
12114
+ this.keyPath("symmetric-keys", keyId, "symmetric-key"),
12115
+ null,
12116
+ isSkmSymmetricKey,
12117
+ keyVersionParams(keyVersion)
12118
+ );
12119
+ }
12120
+ sign(keyId, plaintext) {
12121
+ return this.request("post", this.keyPath("asymmetric-keys", keyId, "sign"), null, isSkmSignResult, {
12122
+ json: { plaintext }
12123
+ });
12124
+ }
12125
+ verify(keyId, plaintext, signature) {
12126
+ return this.request("post", this.keyPath("asymmetric-keys", keyId, "verify"), null, isSkmVerifyResult, {
12127
+ json: { plaintext, signature }
12128
+ });
12129
+ }
12130
+ signStandard(keyId, plaintextBase64) {
12131
+ return this.request(
12132
+ "post",
12133
+ this.keyPath("asymmetric-keys", keyId, "sign-standard"),
12134
+ null,
12135
+ isSkmStandardSignResult,
12136
+ { json: { plaintext: plaintextBase64, algorithm: STANDARD_SIGN_ALGORITHM } }
12137
+ );
12138
+ }
12139
+ verifyStandard(keyId, plaintextBase64, signature, keyVersion) {
12140
+ return this.request("post", this.keyPath("asymmetric-keys", keyId, "verify-standard"), null, isSkmVerifyResult, {
12141
+ json: { plaintext: plaintextBase64, signature, algorithm: STANDARD_SIGN_ALGORITHM, keyVersion }
12142
+ });
12143
+ }
12144
+ getPrivateKey(keyId, keyVersion) {
12145
+ return this.request(
12146
+ "get",
12147
+ this.keyPath("asymmetric-keys", keyId, "privateKey"),
12148
+ null,
12149
+ isSkmAsymmetricKeyMaterial,
12150
+ keyVersionParams(keyVersion)
12151
+ );
12152
+ }
12153
+ getPublicKey(keyId, keyVersion) {
12154
+ return this.request(
12155
+ "get",
12156
+ this.keyPath("asymmetric-keys", keyId, "publicKey"),
12157
+ null,
12158
+ isSkmAsymmetricKeyMaterial,
12159
+ keyVersionParams(keyVersion)
12160
+ );
12161
+ }
12162
+ createSecret(keyStoreName, name, description, secretValue) {
12163
+ return this.request("post", "/keys/secrets/create", null, isSkmCreatedKey, {
12164
+ json: withDescription({ keyStoreName, name, secretValue }, description)
12165
+ });
12166
+ }
12167
+ /** 자동 회전 주기의 단위가 문서에 없어 항상 0(자동 회전 안 함)을 보낸다 (ADR-040). */
12168
+ createSymmetricKey(keyStoreName, name, description) {
12169
+ return this.request("post", "/keys/symmetric-keys/create", null, isSkmCreatedKey, {
12170
+ json: withDescription({ keyStoreName, name, autoRotationPeriod: 0 }, description)
12171
+ });
12172
+ }
12173
+ /** 자동 회전 주기의 단위가 문서에 없어 항상 0(자동 회전 안 함)을 보낸다 (ADR-040). */
12174
+ createAsymmetricKey(keyStoreName, name, description) {
12175
+ return this.request("post", "/keys/asymmetric-keys/create", null, isSkmCreatedKey, {
12176
+ json: withDescription({ keyStoreName, name, autoRotationPeriod: 0 }, description)
12177
+ });
12178
+ }
12179
+ updateSecret(keyId, secretValue) {
12180
+ return this.request("put", `/secrets/${encodeURIComponent(keyId)}`, null, isSkmUpdatedSecret, {
12181
+ json: { secretValue }
12182
+ });
12183
+ }
12184
+ /** 7일 뒤 삭제를 예약한다. */
12185
+ scheduleKeyDeletion(keyId) {
12186
+ return this.request("put", this.keyPath("keys", keyId, "delete"), null, isSkmDeletion);
12187
+ }
12188
+ /** 삭제 예약된 키를 즉시 삭제한다. 되돌릴 수 없다. */
12189
+ deleteKeyNow(keyId) {
12190
+ return this.request("delete", `/keys/${encodeURIComponent(keyId)}`, null, isSkmDeletion);
12191
+ }
12192
+ createKeyStore(input) {
12193
+ return this.request("post", "/keystores", null, isSkmCreatedKeyStore, { json: keyStoreBody(input) });
12194
+ }
12195
+ /** 전체 교체 API 라 주지 않은 값은 호출부가 현재 값으로 채워 넘긴다 (ADR-040). */
12196
+ updateKeyStore(keyStoreId, input) {
12197
+ return this.requestHeaderOnly("put", `/keystores/${keyStoreId}`, { json: keyStoreBody(input) });
12198
+ }
12199
+ deleteKeyStore(keyStoreId) {
12200
+ return this.requestHeaderOnly("delete", `/keystores/${keyStoreId}`);
12201
+ }
12202
+ addAuth(type, keyStoreName, value, description) {
12203
+ return this.request("post", `/auths/${AUTH_WRITE_PATH[type]}`, null, isSkmAuthAdded, {
12204
+ json: withDescription({ keyStoreName, value }, description)
12205
+ });
12206
+ }
12207
+ addCertificate(keyStoreName, name, password, lifeTime, description) {
12208
+ return this.request("post", `/auths/${AUTH_WRITE_PATH.certificate}`, null, isSkmAuthAdded, {
12209
+ json: withDescription({ keyStoreName, name, password, lifeTime }, description)
12210
+ });
12211
+ }
12212
+ /** 7일 뒤 삭제를 예약한다. */
12213
+ scheduleAuthDeletion(type, keyStoreName, value) {
12214
+ return this.request("put", `/auths/${AUTH_WRITE_PATH[type]}/delete`, null, isSkmAuthDeletion, {
12215
+ json: authDeletionBody(type, keyStoreName, value)
12216
+ });
12217
+ }
12218
+ /** 삭제 예약된 인증 정보를 즉시 삭제한다. 되돌릴 수 없다. */
12219
+ deleteAuthNow(type, keyStoreName, value) {
12220
+ return this.request("post", `/auths/${AUTH_WRITE_PATH[type]}/delete`, null, isSkmAuthDeletion, {
12221
+ json: authDeletionBody(type, keyStoreName, value)
12222
+ });
12223
+ }
12224
+ };
12225
+
12226
+ // src/commands/skm/helpers.ts
12227
+ var MAC_ADDRESS_PATTERN = /^[0-9A-Fa-f]{2}(:[0-9A-Fa-f]{2}){5}$/;
12228
+ var KEY_NAME_MAX_LENGTH = 100;
12229
+ var KEY_TYPES = {
12230
+ secret: "SECRET",
12231
+ "symmetric-key": "SYMMETRIC_KEY",
12232
+ "asymmetric-key": "ASYMMETRIC_KEY"
12233
+ };
12234
+ var AUTH_TYPES = ["ipv4", "mac", "certificate"];
12235
+ function withSkmOptions(command) {
12236
+ return command.option("--mac-address <mac>", "\uD0A4 \uC800\uC7A5\uC18C MAC \uC778\uC99D\uC5D0 \uC4F8 \uD074\uB77C\uC774\uC5B8\uD2B8 MAC \uC8FC\uC18C (X-TOAST-CLIENT-MAC-ADDR)").option("--profile <name>", "\uC0AC\uC6A9\uD560 profile \uC774\uB984");
12237
+ }
12238
+ function withTypeOption(command) {
12239
+ return command.requiredOption("--type <type>", "\uC778\uC99D \uC815\uBCF4 \uC885\uB958 (ipv4|mac|certificate)");
12240
+ }
12241
+ function parseMacAddressOption(value) {
12242
+ if (value === void 0) return void 0;
12243
+ if (!MAC_ADDRESS_PATTERN.test(value)) {
12244
+ throw new NhnCloudCliError(
12245
+ `--mac-address\uB294 aa:bb:cc:dd:ee:ff \uD615\uC2DD\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4 (\uC785\uB825: ${JSON.stringify(value)}).`,
12246
+ EXIT_PARAM_ERROR
12247
+ );
12248
+ }
12249
+ return value.toLowerCase();
12250
+ }
12251
+ function parseKeyStoreId(value) {
12252
+ return parseNonNegativeIntegerOption(parseRequiredArgument(value, "keystore-id"), "<keystore-id>");
12253
+ }
12254
+ function parseAuthTypeOption(value) {
12255
+ const found = AUTH_TYPES.find((type) => type === value);
12256
+ if (!found) {
12257
+ throw new NhnCloudCliError(
12258
+ `--type\uC740 ipv4, mac, certificate \uC911 \uD558\uB098\uC5EC\uC57C \uD569\uB2C8\uB2E4 (\uC785\uB825: ${JSON.stringify(value)}).`,
12259
+ EXIT_PARAM_ERROR
12260
+ );
12261
+ }
12262
+ return found;
12263
+ }
12264
+ function parseAuthListOption(value) {
12265
+ const items = value.split(",").map((item) => item.trim());
12266
+ if (items.some((item) => !AUTH_TYPES.some((type) => type === item))) {
12267
+ throw new NhnCloudCliError(
12268
+ `--auth\uB294 ipv4, mac, certificate\uB97C \uC27C\uD45C\uB85C \uB098\uC5F4\uD574\uC57C \uD569\uB2C8\uB2E4 (\uC785\uB825: ${JSON.stringify(value)}).`,
12269
+ EXIT_PARAM_ERROR
12270
+ );
12271
+ }
12272
+ const flag = (type) => items.includes(type) ? "Y" : "N";
12273
+ return { ip4AuthUse: flag("ipv4"), macAuthUse: flag("mac"), certificateAuthUse: flag("certificate") };
12274
+ }
12275
+ function parseAuthModeOption(value) {
12276
+ const upper = value.toUpperCase();
12277
+ if (upper !== "AND" && upper !== "OR") {
12278
+ throw new NhnCloudCliError(
12279
+ `--auth-mode\uB294 and \uB610\uB294 or\uC5EC\uC57C \uD569\uB2C8\uB2E4 (\uC785\uB825: ${JSON.stringify(value)}).`,
12280
+ EXIT_PARAM_ERROR
12281
+ );
12282
+ }
12283
+ return upper;
12284
+ }
12285
+ function parseDescriptionOption(value, maxLength) {
12286
+ const trimmed = value?.trim();
12287
+ if (!trimmed) return void 0;
12288
+ if (trimmed.length > maxLength) {
12289
+ throw new NhnCloudCliError(`--description\uC740 ${maxLength}\uC790 \uC774\uD558\uC5EC\uC57C \uD569\uB2C8\uB2E4.`, EXIT_PARAM_ERROR);
12290
+ }
12291
+ return trimmed;
12292
+ }
12293
+ function isIpv4OrCidr2(value) {
12294
+ const [address, prefix, ...rest] = value.split("/");
12295
+ if (rest.length > 0 || address === void 0 || (0, import_node_net2.isIP)(address) !== 4) return false;
12296
+ return prefix === void 0 || /^(\d|[12]\d|3[0-2])$/.test(prefix);
12297
+ }
12298
+ function parseAuthValue(type, value) {
12299
+ if (type === "ipv4") {
12300
+ if (!isIpv4OrCidr2(value)) {
12301
+ throw new NhnCloudCliError(
12302
+ `IPv4 \uC8FC\uC18C\uB098 CIDR \uB300\uC5ED \uD615\uC2DD\uC774 \uC544\uB2D9\uB2C8\uB2E4 (\uC785\uB825: ${JSON.stringify(value)}).`,
12303
+ EXIT_PARAM_ERROR
12304
+ );
12305
+ }
12306
+ return value;
12307
+ }
12308
+ if (type === "mac") {
12309
+ if (!MAC_ADDRESS_PATTERN.test(value)) {
12310
+ throw new NhnCloudCliError(
12311
+ `MAC \uC8FC\uC18C\uB294 aa:bb:cc:dd:ee:ff \uD615\uC2DD\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4 (\uC785\uB825: ${JSON.stringify(value)}).`,
12312
+ EXIT_PARAM_ERROR
12313
+ );
12314
+ }
12315
+ return value.toLowerCase();
12316
+ }
12317
+ const name = value.trim();
12318
+ if (!name) {
12319
+ throw new NhnCloudCliError("\uC778\uC99D\uC11C \uC774\uB984\uC774 \uBE44\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.", EXIT_PARAM_ERROR);
12320
+ }
12321
+ return name;
12322
+ }
12323
+ function parseKeyTypeOption(value) {
12324
+ if (value === void 0) return void 0;
12325
+ const found = Object.hasOwn(KEY_TYPES, value) ? KEY_TYPES[value] : void 0;
12326
+ if (!found) {
12327
+ throw new NhnCloudCliError(
12328
+ `--type\uC740 secret, symmetric-key, asymmetric-key \uC911 \uD558\uB098\uC5EC\uC57C \uD569\uB2C8\uB2E4 (\uC785\uB825: ${JSON.stringify(value)}).`,
12329
+ EXIT_PARAM_ERROR
12330
+ );
12331
+ }
12332
+ return found;
12333
+ }
12334
+ function parseKeyStatusOption(value) {
12335
+ if (value === void 0) return void 0;
12336
+ if (value !== "active" && value !== "inactive") {
12337
+ throw new NhnCloudCliError(
12338
+ `--status\uB294 active \uB610\uB294 inactive\uC5EC\uC57C \uD569\uB2C8\uB2E4 (\uC785\uB825: ${JSON.stringify(value)}).`,
12339
+ EXIT_PARAM_ERROR
12340
+ );
12341
+ }
12342
+ return value;
12343
+ }
12344
+ function parseKeyNameOption(value) {
12345
+ if (value === void 0) return void 0;
12346
+ const trimmed = value.trim();
12347
+ if (!trimmed) {
12348
+ throw new NhnCloudCliError("--name\uC740 \uBE44\uC5B4 \uC788\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.", EXIT_PARAM_ERROR);
12349
+ }
12350
+ if (trimmed.length > KEY_NAME_MAX_LENGTH) {
12351
+ throw new NhnCloudCliError(`--name\uC740 ${KEY_NAME_MAX_LENGTH}\uC790 \uC774\uD558\uC5EC\uC57C \uD569\uB2C8\uB2E4.`, EXIT_PARAM_ERROR);
12352
+ }
12353
+ return trimmed;
12354
+ }
12355
+ async function resolveSkmClient(opts) {
12356
+ const profileName = await resolveProfileName(opts.profile);
12357
+ const appKey = await resolveServiceAppKey(
12358
+ "skm",
12359
+ profileName,
12360
+ "Secure Key Manager appkey\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. nhncloud configure --skm-appkey <key>\uB85C \uC124\uC815\uD558\uC138\uC694."
12361
+ );
12362
+ const uak = await getUserAccessKey(profileName);
12363
+ const environment = await getProfileEnvironment(profileName);
12364
+ const accessToken = await getAccessToken(profileName, uak.id, uak.secret, false, environment);
12365
+ return { client: new SkmClient(accessToken, environment, appKey, opts.macAddress), profileName };
12366
+ }
12367
+ function maskAuthDetail(detail) {
12368
+ if (typeof detail.password !== "string") return detail;
12369
+ return { ...detail, password: "***" };
12370
+ }
12371
+ function formatCell(value) {
12372
+ if (value === null || value === void 0) return "-";
12373
+ return sanitizeForTerminal(String(value));
12374
+ }
12375
+ function printSkmValue(opts, value, raw) {
12376
+ if (opts.json) {
12377
+ printJson(raw);
12378
+ } else if (opts.quiet) {
12379
+ process.stdout.write(value + "\n");
12380
+ } else {
12381
+ process.stdout.write(sanitizeMultilineForTerminal(value) + "\n");
12382
+ }
12383
+ }
12384
+ async function resolveKeyStoreName(client, keyStoreId) {
12385
+ const keyStore = await client.getKeyStore(keyStoreId);
12386
+ return keyStore.name;
12387
+ }
12388
+ function outputSkmWriteResult(opts, result, ids) {
12389
+ output(opts, {
12390
+ headers: ["field", "value"],
12391
+ rows: Object.entries(result).map(([key, value]) => [key, formatCell(value)]),
12392
+ raw: result,
12393
+ ids
12394
+ });
12395
+ }
12396
+ function parseKeyVersionOption(value) {
12397
+ return parseNonNegativeIntegerOption(value, "--key-version");
12398
+ }
12399
+ async function withSkmSpinner(text, call) {
12400
+ startSpinner(text);
12401
+ let result;
12402
+ try {
12403
+ result = await call();
12404
+ } catch (err) {
12405
+ stopSpinner(false);
12406
+ throw err;
12407
+ }
12408
+ stopSpinner(true);
12409
+ return result;
12410
+ }
12411
+
12412
+ // src/commands/skm/confirm.ts
12413
+ var confirmCommand = withSkmOptions(
12414
+ new import_commander59.Command("confirm").description("SKM \uC11C\uBC84\uAC00 \uBCF8 \uD074\uB77C\uC774\uC5B8\uD2B8 IP, MAC \uD5E4\uB354\uC640 \uC778\uC99D\uC11C \uC0AC\uC6A9 \uC5EC\uBD80\uB97C \uC870\uD68C\uD55C\uB2E4")
12415
+ ).action(async (_opts, command) => {
12416
+ const opts = command.optsWithGlobals();
12417
+ const macAddress = parseMacAddressOption(opts.macAddress);
12418
+ const { client } = await resolveSkmClient({ profile: opts.profile, macAddress });
12419
+ const info = await withSkmSpinner("SKM \uD074\uB77C\uC774\uC5B8\uD2B8 \uC815\uBCF4 \uC870\uD68C \uC911...", () => client.confirm());
12420
+ output(opts, {
12421
+ headers: ["field", "value"],
12422
+ rows: [
12423
+ ["clientIp", formatCell(info.clientIp)],
12424
+ ["clientMacHeader", formatCell(info.clientMacHeader)],
12425
+ ["clientSentCertificate", formatCell(info.clientSentCertificate)]
12426
+ ],
12427
+ raw: info,
12428
+ ids: [formatCell(info.clientIp)]
12429
+ });
12430
+ });
12431
+
12432
+ // src/commands/skm/keystore.ts
12433
+ var import_commander61 = require("commander");
12434
+
12435
+ // src/commands/skm/keystore-manage.ts
12436
+ var import_commander60 = require("commander");
12437
+
12438
+ // src/commands/skm/input.ts
12439
+ var import_node_fs10 = require("fs");
12440
+ var processStdin = {
12441
+ get isTTY() {
12442
+ return process.stdin.isTTY;
12443
+ },
12444
+ read: () => (0, import_node_fs10.readFileSync)(0)
12445
+ };
12446
+ function assertWithinLimit(size, spec) {
12447
+ if (size > spec.maxBytes) {
12448
+ throw new NhnCloudCliError(
12449
+ `${spec.label}\uAC00 \uB108\uBB34 \uD07D\uB2C8\uB2E4: ${size}\uBC14\uC774\uD2B8 (\uD55C\uB3C4 ${spec.maxBytes}\uBC14\uC774\uD2B8).`,
12450
+ EXIT_PARAM_ERROR
12451
+ );
12452
+ }
12453
+ }
12454
+ function readInputFile(file, spec) {
12455
+ let stat4;
12456
+ try {
12457
+ stat4 = (0, import_node_fs10.statSync)(file);
12458
+ } catch (e) {
12459
+ const reason = e.code ?? (e instanceof Error ? e.message : String(e));
12460
+ throw new NhnCloudCliError(`${spec.label} \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${file} (${reason})`, EXIT_PARAM_ERROR);
12461
+ }
12462
+ if (!stat4.isFile()) {
12463
+ throw new NhnCloudCliError(`${spec.label} \uD30C\uC77C\uC774 \uC77C\uBC18 \uD30C\uC77C\uC774 \uC544\uB2D9\uB2C8\uB2E4: ${file}`, EXIT_PARAM_ERROR);
12464
+ }
12465
+ assertWithinLimit(stat4.size, spec);
12466
+ return (0, import_node_fs10.readFileSync)(file);
12467
+ }
12468
+ function readStdin(stdin, spec) {
12469
+ try {
12470
+ return stdin.read();
12471
+ } catch (e) {
12472
+ const reason = e.code ?? (e instanceof Error ? e.message : String(e));
12473
+ throw new NhnCloudCliError(`${spec.label}\uB97C \uD45C\uC900 \uC785\uB825\uC5D0\uC11C \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (${reason}).`, EXIT_PARAM_ERROR);
12474
+ }
12475
+ }
12476
+ function readSkmInput(source, spec, stdin = processStdin) {
12477
+ const fileFlag = spec.fileFlag ?? "--file";
12478
+ if (source.text !== void 0 && source.file !== void 0) {
12479
+ throw new NhnCloudCliError(`${spec.textFlag}\uC640 ${fileFlag}\uC740 \uD568\uAED8 \uC9C0\uC815\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`, EXIT_PARAM_ERROR);
12480
+ }
12481
+ let input;
12482
+ if (source.text !== void 0) {
12483
+ input = Buffer.from(source.text, "utf-8");
12484
+ } else if (source.file !== void 0) {
12485
+ input = readInputFile(source.file, spec);
12486
+ } else if (!stdin.isTTY) {
12487
+ input = readStdin(stdin, spec);
12488
+ } else {
12489
+ throw new NhnCloudCliError(
12490
+ `${spec.label}\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. ${spec.textFlag} <\uAC12>, ${fileFlag} <\uACBD\uB85C>, \uD45C\uC900 \uC785\uB825(\uD30C\uC774\uD504) \uC911 \uD558\uB098\uB85C \uC804\uB2EC\uD558\uC138\uC694.`,
12491
+ EXIT_PARAM_ERROR
12492
+ );
12493
+ }
12494
+ assertWithinLimit(input.length, spec);
12495
+ if (input.length === 0) {
12496
+ throw new NhnCloudCliError(`${spec.label}\uAC00 \uBE44\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.`, EXIT_PARAM_ERROR);
12497
+ }
12498
+ return input;
12499
+ }
12500
+ function decodeUtf8Input(input, label, hint) {
12501
+ try {
12502
+ return new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(input);
12503
+ } catch {
12504
+ throw new NhnCloudCliError(`${label}\uB294 UTF-8 \uD14D\uC2A4\uD2B8\uC5EC\uC57C \uD569\uB2C8\uB2E4. ${hint}`, EXIT_PARAM_ERROR);
12505
+ }
12506
+ }
12507
+
12508
+ // src/commands/skm/keystore-manage.ts
12509
+ var DESCRIPTION_MAX_LENGTH = 1e3;
12510
+ var PASSWORD_MAX_BYTES = 1024;
12511
+ var LOCKOUT_WARNING = "(\uC2E4\uD589 \uC704\uCE58\uAC00 \uD0A4\uB97C \uC4F0\uC9C0 \uBABB\uD558\uAC8C \uB420 \uC218 \uC788\uC74C, \uBA3C\uC800 skm confirm\uC73C\uB85C \uD655\uC778)";
12512
+ var createCommand11 = withSkmOptions(
12513
+ new import_commander60.Command("create").description("\uD0A4 \uC800\uC7A5\uC18C\uB97C \uB9CC\uB4E0\uB2E4").requiredOption("--name <name>", "\uD0A4 \uC800\uC7A5\uC18C \uC774\uB984 (100\uC790 \uC774\uD558)").requiredOption("--auth <list>", "\uC0AC\uC6A9\uD560 \uC778\uC99D (ipv4,mac,certificate \uC911 \uD558\uB098 \uC774\uC0C1\uC744 \uC27C\uD45C\uB85C)").option("--auth-mode <mode>", "\uC778\uC99D \uACB0\uD569 \uBC29\uC2DD (and|or)", "and").option("--description <text>", "\uD0A4 \uC800\uC7A5\uC18C \uC124\uBA85 (1000\uC790 \uC774\uD558)")
12514
+ ).action(async (_opts, command) => {
12515
+ const opts = command.optsWithGlobals();
12516
+ const name = parseKeyNameOption(opts.name);
12517
+ const auths = parseAuthListOption(opts.auth);
12518
+ const authMode = parseAuthModeOption(opts.authMode);
12519
+ const description = parseDescriptionOption(opts.description, DESCRIPTION_MAX_LENGTH);
12520
+ const macAddress = parseMacAddressOption(opts.macAddress);
12521
+ const { client } = await resolveSkmClient({ profile: opts.profile, macAddress });
12522
+ const input = { name, ...auths, authMode };
12523
+ if (description !== void 0) input.description = description;
12524
+ const created = await withSkmSpinner(
12525
+ `SKM \uD0A4 \uC800\uC7A5\uC18C ${formatCell(name)} \uC0DD\uC131 \uC911...`,
12526
+ () => client.createKeyStore(input)
12527
+ );
12528
+ outputSkmWriteResult(opts, { ...created }, [String(created.keyStoreId)]);
12529
+ });
12530
+ var updateCommand4 = withSkmOptions(
12531
+ new import_commander60.Command("update").description(`\uD0A4 \uC800\uC7A5\uC18C\uB97C \uC218\uC815\uD55C\uB2E4. \uC8FC\uC9C0 \uC54A\uC740 \uD56D\uBAA9\uC740 \uD604\uC7AC \uAC12\uC744 \uC720\uC9C0\uD55C\uB2E4 ${LOCKOUT_WARNING}`).argument("<keystore-id>", "\uD0A4 \uC800\uC7A5\uC18C ID").requiredOption("--auth-mode <mode>", "\uC778\uC99D \uACB0\uD569 \uBC29\uC2DD (and|or). \uC870\uD68C \uC751\uB2F5\uC5D0 \uC5C6\uC5B4 \uB9E4\uBC88 \uC9C0\uC815\uD55C\uB2E4").option("--name <name>", "\uD0A4 \uC800\uC7A5\uC18C \uC774\uB984 (100\uC790 \uC774\uD558)").option("--description <text>", "\uD0A4 \uC800\uC7A5\uC18C \uC124\uBA85 (1000\uC790 \uC774\uD558)").option("--auth <list>", "\uC0AC\uC6A9\uD560 \uC778\uC99D (ipv4,mac,certificate \uC911 \uD558\uB098 \uC774\uC0C1\uC744 \uC27C\uD45C\uB85C)")
12532
+ ).action(async (keyStoreId, _opts, command) => {
12533
+ const opts = command.optsWithGlobals();
12534
+ const parsedId = parseKeyStoreId(keyStoreId);
12535
+ const authMode = parseAuthModeOption(opts.authMode);
12536
+ const name = parseKeyNameOption(opts.name);
12537
+ const description = parseDescriptionOption(opts.description, DESCRIPTION_MAX_LENGTH);
12538
+ const auths = opts.auth === void 0 ? void 0 : parseAuthListOption(opts.auth);
12539
+ const macAddress = parseMacAddressOption(opts.macAddress);
12540
+ const { client } = await resolveSkmClient({ profile: opts.profile, macAddress });
12541
+ const current2 = await withSkmSpinner(`SKM \uD0A4 \uC800\uC7A5\uC18C ${parsedId} \uC870\uD68C \uC911...`, () => client.getKeyStore(parsedId));
12542
+ const currentDescription = typeof current2.description === "string" ? current2.description : void 0;
12543
+ const yesNo = (value) => value === "Y" ? "Y" : "N";
12544
+ const input = {
12545
+ name: name ?? current2.name,
12546
+ ...auths ?? {
12547
+ ip4AuthUse: yesNo(current2.ip4AuthUse),
12548
+ macAuthUse: yesNo(current2.macAuthUse),
12549
+ certificateAuthUse: yesNo(current2.certificateAuthUse)
12550
+ },
12551
+ authMode
12552
+ };
12553
+ const nextDescription = description ?? currentDescription;
12554
+ if (nextDescription !== void 0) input.description = nextDescription;
12555
+ if (input.ip4AuthUse === "N" && input.macAuthUse === "N" && input.certificateAuthUse === "N") {
12556
+ throw new NhnCloudCliError("\uD0A4 \uC800\uC7A5\uC18C \uC778\uC99D\uC740 \uD558\uB098 \uC774\uC0C1 \uCF1C\uC57C \uD569\uB2C8\uB2E4.", EXIT_PARAM_ERROR);
12557
+ }
12558
+ await withSkmSpinner(`SKM \uD0A4 \uC800\uC7A5\uC18C ${parsedId} \uC218\uC815 \uC911...`, () => client.updateKeyStore(parsedId, input));
12559
+ outputSkmWriteResult(
12560
+ opts,
12561
+ { operation: "keystore-update", status: "succeeded", keyStoreId: parsedId, ...input },
12562
+ [String(parsedId)]
12563
+ );
12564
+ });
12565
+ var deleteCommand9 = withSkmOptions(
12566
+ new import_commander60.Command("delete").description("\uD0A4 \uC800\uC7A5\uC18C\uB97C \uC0AD\uC81C(\uBE44\uD65C\uC131\uD654)\uD55C\uB2E4").argument("<keystore-id>", "\uD0A4 \uC800\uC7A5\uC18C ID").option("--yes", "\uC0AD\uC81C \uD655\uC778")
12567
+ ).action(async (keyStoreId, _opts, command) => {
12568
+ const opts = command.optsWithGlobals();
12569
+ requireYes(opts.yes, "\uD0A4 \uC800\uC7A5\uC18C \uC0AD\uC81C");
12570
+ const parsedId = parseKeyStoreId(keyStoreId);
12571
+ const macAddress = parseMacAddressOption(opts.macAddress);
12572
+ const { client } = await resolveSkmClient({ profile: opts.profile, macAddress });
12573
+ await withSkmSpinner(`SKM \uD0A4 \uC800\uC7A5\uC18C ${parsedId} \uC0AD\uC81C \uC911...`, () => client.deleteKeyStore(parsedId));
12574
+ outputSkmWriteResult(
12575
+ opts,
12576
+ { operation: "keystore-delete", status: "succeeded", keyStoreId: parsedId },
12577
+ [String(parsedId)]
12578
+ );
12579
+ });
12580
+ function readCertificateOptions(opts) {
12581
+ if (opts.lifeTime === void 0) {
12582
+ throw new NhnCloudCliError("\uC778\uC99D\uC11C \uCD94\uAC00\uC5D0\uB294 --life-time\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.", EXIT_PARAM_ERROR);
12583
+ }
12584
+ const lifeTime = parsePositiveIntegerOption(opts.lifeTime, "--life-time");
12585
+ const input = readSkmInput(
12586
+ { text: opts.password, file: opts.passwordFile },
12587
+ { textFlag: "--password", fileFlag: "--password-file", label: "\uC778\uC99D\uC11C \uBE44\uBC00\uBC88\uD638", maxBytes: PASSWORD_MAX_BYTES }
12588
+ );
12589
+ const password = decodeUtf8Input(input, "\uC778\uC99D\uC11C \uBE44\uBC00\uBC88\uD638", "\uD30C\uC77C \uC778\uCF54\uB529\uC744 \uD655\uC778\uD558\uC138\uC694.").replace(/\r?\n$/, "");
12590
+ if (!password) {
12591
+ throw new NhnCloudCliError("\uC778\uC99D\uC11C \uBE44\uBC00\uBC88\uD638\uAC00 \uBE44\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.", EXIT_PARAM_ERROR);
12592
+ }
12593
+ return { lifeTime, password };
12594
+ }
12595
+ function rejectCertificateOptions(opts) {
12596
+ if (opts.lifeTime !== void 0 || opts.password !== void 0 || opts.passwordFile !== void 0) {
12597
+ throw new NhnCloudCliError("--life-time\uACFC --password\uB294 --type certificate\uC5D0\uC11C\uB9CC \uC501\uB2C8\uB2E4.", EXIT_PARAM_ERROR);
12598
+ }
12599
+ }
12600
+ function authAddedFields(added) {
12601
+ return { value: added.value, name: added.name, description: added.description };
12602
+ }
12603
+ var authAddCommand = withTypeOption(
12604
+ withSkmOptions(
12605
+ new import_commander60.Command("add").description("\uD0A4 \uC800\uC7A5\uC18C\uC5D0 \uC778\uC99D \uC815\uBCF4\uB97C \uCD94\uAC00\uD55C\uB2E4 (\uC778\uC99D\uC11C \uBE44\uBC00\uBC88\uD638\uB294 --password, --password-file, \uD45C\uC900 \uC785\uB825 \uC911 \uD558\uB098)").argument("<keystore-id>", "\uD0A4 \uC800\uC7A5\uC18C ID").argument("<value>", "IPv4\xB7MAC \uAC12 \uB610\uB294 \uC778\uC99D\uC11C \uC774\uB984").option("--description <text>", "\uC778\uC99D \uC815\uBCF4 \uC124\uBA85 (1000\uC790 \uC774\uD558)").option("--life-time <days>", "\uC778\uC99D\uC11C \uC720\uD6A8 \uAE30\uAC04(\uC77C) (--type certificate \uC804\uC6A9, \uD544\uC218)").option("--password <pw>", "\uC778\uC99D\uC11C \uBE44\uBC00\uBC88\uD638 (--type certificate \uC804\uC6A9)").option("--password-file <path>", "\uC778\uC99D\uC11C \uBE44\uBC00\uBC88\uD638\uB97C \uC77D\uC744 \uD30C\uC77C (--type certificate \uC804\uC6A9)")
12606
+ )
12607
+ ).action(async (keyStoreId, value, _opts, command) => {
12608
+ const opts = command.optsWithGlobals();
12609
+ const parsedId = parseKeyStoreId(keyStoreId);
12610
+ const type = parseAuthTypeOption(opts.type);
12611
+ const parsedValue = parseAuthValue(type, value);
12612
+ const description = parseDescriptionOption(opts.description, DESCRIPTION_MAX_LENGTH);
12613
+ const macAddress = parseMacAddressOption(opts.macAddress);
12614
+ if (type !== "certificate") rejectCertificateOptions(opts);
12615
+ const target = type === "certificate" ? { type, ...readCertificateOptions(opts) } : { type };
12616
+ const { client } = await resolveSkmClient({ profile: opts.profile, macAddress });
12617
+ const added = await withSkmSpinner(`SKM ${type} \uC778\uC99D \uC815\uBCF4 ${formatCell(parsedValue)} \uCD94\uAC00 \uC911...`, async () => {
12618
+ const keyStoreName = await resolveKeyStoreName(client, parsedId);
12619
+ if (target.type === "certificate") {
12620
+ return client.addCertificate(keyStoreName, parsedValue, target.password, target.lifeTime, description);
12621
+ }
12622
+ return client.addAuth(target.type, keyStoreName, parsedValue, description);
12623
+ });
12624
+ outputSkmWriteResult(
12625
+ opts,
12626
+ { operation: "auth-add", type, keyStoreId: parsedId, ...authAddedFields(added) },
12627
+ [formatCell(parsedValue)]
12628
+ );
12629
+ });
12630
+ var authDeleteCommand = withTypeOption(
12631
+ withSkmOptions(
12632
+ new import_commander60.Command("delete").description(`\uC778\uC99D \uC815\uBCF4 \uC0AD\uC81C\uB97C \uC608\uC57D\uD55C\uB2E4 (7\uC77C \uB4A4 \uC0AD\uC81C) ${LOCKOUT_WARNING}`).argument("<keystore-id>", "\uD0A4 \uC800\uC7A5\uC18C ID").argument("<value>", "IPv4\xB7MAC \uAC12 \uB610\uB294 \uC778\uC99D\uC11C \uC774\uB984").option("--yes", "\uC0AD\uC81C \uC608\uC57D \uD655\uC778")
12633
+ )
12634
+ ).action(async (keyStoreId, value, _opts, command) => {
12635
+ const opts = command.optsWithGlobals();
12636
+ requireYes(opts.yes, "\uC778\uC99D \uC815\uBCF4 \uC0AD\uC81C \uC608\uC57D");
12637
+ const parsedId = parseKeyStoreId(keyStoreId);
12638
+ const type = parseAuthTypeOption(opts.type);
12639
+ const parsedValue = parseAuthValue(type, value);
12640
+ const macAddress = parseMacAddressOption(opts.macAddress);
12641
+ const { client } = await resolveSkmClient({ profile: opts.profile, macAddress });
12642
+ const deletion = await withSkmSpinner(`SKM ${type} \uC778\uC99D \uC815\uBCF4 ${formatCell(parsedValue)} \uC0AD\uC81C \uC608\uC57D \uC911...`, async () => {
12643
+ const keyStoreName = await resolveKeyStoreName(client, parsedId);
12644
+ return client.scheduleAuthDeletion(type, keyStoreName, parsedValue);
12645
+ });
12646
+ outputSkmWriteResult(
12647
+ opts,
12648
+ {
12649
+ operation: "auth-delete-scheduled",
12650
+ type,
12651
+ keyStoreId: parsedId,
12652
+ value: deletion.value,
12653
+ name: deletion.name,
12654
+ deletionDateTime: deletion.deletionDateTime
12655
+ },
12656
+ [formatCell(parsedValue)]
12657
+ );
12658
+ });
12659
+ var authPurgeCommand = withTypeOption(
12660
+ withSkmOptions(
12661
+ new import_commander60.Command("purge").description(`\uC778\uC99D \uC815\uBCF4\uB97C \uC989\uC2DC \uC0AD\uC81C\uD55C\uB2E4 (\uC0AD\uC81C \uC608\uC57D\uB41C \uAC83\uB9CC, \uB418\uB3CC\uB9B4 \uC218 \uC5C6\uC74C) ${LOCKOUT_WARNING}`).argument("<keystore-id>", "\uD0A4 \uC800\uC7A5\uC18C ID").argument("<value>", "IPv4\xB7MAC \uAC12 \uB610\uB294 \uC778\uC99D\uC11C \uC774\uB984").option("--yes", "\uC989\uC2DC \uC0AD\uC81C \uD655\uC778")
12662
+ )
12663
+ ).action(async (keyStoreId, value, _opts, command) => {
12664
+ const opts = command.optsWithGlobals();
12665
+ requireYes(opts.yes, "\uC778\uC99D \uC815\uBCF4 \uC989\uC2DC \uC0AD\uC81C");
12666
+ const parsedId = parseKeyStoreId(keyStoreId);
12667
+ const type = parseAuthTypeOption(opts.type);
12668
+ const parsedValue = parseAuthValue(type, value);
12669
+ const macAddress = parseMacAddressOption(opts.macAddress);
12670
+ const { client } = await resolveSkmClient({ profile: opts.profile, macAddress });
12671
+ const deletion = await withSkmSpinner(`SKM ${type} \uC778\uC99D \uC815\uBCF4 ${formatCell(parsedValue)} \uC989\uC2DC \uC0AD\uC81C \uC911...`, async () => {
12672
+ const keyStoreName = await resolveKeyStoreName(client, parsedId);
12673
+ return client.deleteAuthNow(type, keyStoreName, parsedValue);
12674
+ });
12675
+ outputSkmWriteResult(
12676
+ opts,
12677
+ {
12678
+ operation: "auth-purge",
12679
+ type,
12680
+ keyStoreId: parsedId,
12681
+ value: deletion.value,
12682
+ name: deletion.name,
12683
+ deletionDateTime: deletion.deletionDateTime
12684
+ },
12685
+ [formatCell(parsedValue)]
12686
+ );
12687
+ });
12688
+
12689
+ // src/commands/skm/keystore.ts
12690
+ var KEYSTORE_FIELDS = [
12691
+ "keyStoreId",
12692
+ "name",
12693
+ "description",
12694
+ "ip4AuthUse",
12695
+ "macAuthUse",
12696
+ "certificateAuthUse",
12697
+ "creationUser",
12698
+ "creationDatetime",
12699
+ "lastChangeUser",
12700
+ "lastChangeDatetime"
12701
+ ];
12702
+ var listCommand16 = withSkmOptions(new import_commander61.Command("list").description("\uD0A4 \uC800\uC7A5\uC18C \uBAA9\uB85D\uC744 \uC870\uD68C\uD55C\uB2E4 (\uC804\uCCB4 \uD544\uB4DC\uB294 --json)")).action(async (_opts, command) => {
12703
+ const opts = command.optsWithGlobals();
12704
+ const macAddress = parseMacAddressOption(opts.macAddress);
12705
+ const { client } = await resolveSkmClient({ profile: opts.profile, macAddress });
12706
+ const stores = await withSkmSpinner("SKM \uD0A4 \uC800\uC7A5\uC18C \uBAA9\uB85D \uC870\uD68C \uC911...", () => client.listKeyStores());
12707
+ output(opts, {
12708
+ headers: ["keyStoreId", "name", "ip4AuthUse", "macAuthUse", "certificateAuthUse", "lastChangeDatetime"],
12709
+ rows: stores.map((s) => [
12710
+ formatCell(s.keyStoreId),
12711
+ formatCell(s.name),
12712
+ formatCell(s.ip4AuthUse),
12713
+ formatCell(s.macAuthUse),
12714
+ formatCell(s.certificateAuthUse),
12715
+ formatCell(s.lastChangeDatetime)
12716
+ ]),
12717
+ raw: stores,
12718
+ ids: stores.map((s) => String(s.keyStoreId))
12719
+ });
12720
+ });
12721
+ var getCommand11 = withSkmOptions(
12722
+ new import_commander61.Command("get").description("\uD0A4 \uC800\uC7A5\uC18C \uD55C \uAC74\uC744 \uC870\uD68C\uD55C\uB2E4").argument("<keystore-id>", "\uD0A4 \uC800\uC7A5\uC18C ID")
12723
+ ).action(async (keyStoreId, _opts, command) => {
12724
+ const opts = command.optsWithGlobals();
12725
+ const parsedId = parseKeyStoreId(keyStoreId);
12726
+ const macAddress = parseMacAddressOption(opts.macAddress);
12727
+ const { client } = await resolveSkmClient({ profile: opts.profile, macAddress });
12728
+ const store = await withSkmSpinner(`SKM \uD0A4 \uC800\uC7A5\uC18C ${parsedId} \uC870\uD68C \uC911...`, () => client.getKeyStore(parsedId));
12729
+ output(opts, {
12730
+ headers: ["field", "value"],
12731
+ rows: KEYSTORE_FIELDS.map((field) => [field, formatCell(store[field])]),
12732
+ raw: store,
12733
+ ids: [String(store.keyStoreId)]
12734
+ });
12735
+ });
12736
+ var authListCommand = withTypeOption(
12737
+ withSkmOptions(
12738
+ new import_commander61.Command("list").description("\uD0A4 \uC800\uC7A5\uC18C \uC778\uC99D \uC815\uBCF4 \uAC12 \uBAA9\uB85D\uC744 \uC870\uD68C\uD55C\uB2E4").argument("<keystore-id>", "\uD0A4 \uC800\uC7A5\uC18C ID")
12739
+ )
12740
+ ).action(async (keyStoreId, _opts, command) => {
12741
+ const opts = command.optsWithGlobals();
12742
+ const parsedId = parseKeyStoreId(keyStoreId);
12743
+ const type = parseAuthTypeOption(opts.type);
12744
+ const macAddress = parseMacAddressOption(opts.macAddress);
12745
+ const { client } = await resolveSkmClient({ profile: opts.profile, macAddress });
12746
+ const values = await withSkmSpinner(`SKM ${type} \uC778\uC99D \uC815\uBCF4 \uBAA9\uB85D \uC870\uD68C \uC911...`, () => client.listAuths(parsedId, type));
12747
+ output(opts, {
12748
+ headers: ["value"],
12749
+ rows: values.map((v) => [formatCell(v)]),
12750
+ raw: values,
12751
+ ids: values.map((v) => formatCell(v))
12752
+ });
12753
+ });
12754
+ var authGetCommand = withTypeOption(
12755
+ withSkmOptions(
12756
+ new import_commander61.Command("get").description("\uD0A4 \uC800\uC7A5\uC18C \uC778\uC99D \uC815\uBCF4 \uC0C1\uC138\uB97C \uC870\uD68C\uD55C\uB2E4 (\uC778\uC99D\uC11C \uBE44\uBC00\uBC88\uD638\uB294 \uAC00\uB9B0\uB2E4)").argument("<keystore-id>", "\uD0A4 \uC800\uC7A5\uC18C ID").argument("<value>", "IPv4\xB7MAC \uAC12 \uB610\uB294 \uC778\uC99D\uC11C \uC774\uB984")
12757
+ )
12758
+ ).action(async (keyStoreId, value, _opts, command) => {
12759
+ const opts = command.optsWithGlobals();
12760
+ const parsedId = parseKeyStoreId(keyStoreId);
12761
+ const parsedValue = parseRequiredArgument(value, "value");
12762
+ const type = parseAuthTypeOption(opts.type);
12763
+ const macAddress = parseMacAddressOption(opts.macAddress);
12764
+ const { client } = await resolveSkmClient({ profile: opts.profile, macAddress });
12765
+ const details = await withSkmSpinner(
12766
+ `SKM ${type} \uC778\uC99D \uC815\uBCF4 \uC870\uD68C \uC911...`,
12767
+ () => client.getAuth(parsedId, type, parsedValue)
12768
+ );
12769
+ const masked = details.map(maskAuthDetail);
12770
+ const keys = masked.map((d) => formatCell(d.value ?? d.name));
12771
+ output(opts, {
12772
+ headers: ["value", "description", "expirationDate", "lastAccessDatetime", "deletionDatetime", "lastChangeDatetime"],
12773
+ rows: masked.map((d, i) => [
12774
+ keys[i] ?? "-",
12775
+ formatCell(d.description),
12776
+ formatCell(d.expirationDate),
12777
+ formatCell(d.lastAccessDatetime),
12778
+ formatCell(d.deletionDatetime),
12779
+ formatCell(d.lastChangeDatetime)
12780
+ ]),
12781
+ raw: masked,
12782
+ ids: keys
12783
+ });
12784
+ });
12785
+ var authCommand = new import_commander61.Command("auth").description("\uD0A4 \uC800\uC7A5\uC18C IPv4\xB7MAC\xB7\uC778\uC99D\uC11C \uC778\uC99D \uC815\uBCF4 \uC870\uD68C\xB7\uAD00\uB9AC").addCommand(authListCommand).addCommand(authGetCommand).addCommand(authAddCommand).addCommand(authDeleteCommand).addCommand(authPurgeCommand);
12786
+ var keystoreCommand = new import_commander61.Command("keystore").description("SKM \uD0A4 \uC800\uC7A5\uC18C\uC640 \uC778\uC99D \uC815\uBCF4 \uC870\uD68C\xB7\uAD00\uB9AC").addCommand(listCommand16).addCommand(getCommand11).addCommand(createCommand11).addCommand(updateCommand4).addCommand(deleteCommand9).addCommand(authCommand);
12787
+
12788
+ // src/commands/skm/key.ts
12789
+ var import_commander63 = require("commander");
12790
+
12791
+ // src/commands/skm/key-manage.ts
12792
+ var import_commander62 = require("commander");
12793
+ var createCommand12 = withSkmOptions(
12794
+ new import_commander62.Command("create").description("\uD0A4 \uC800\uC7A5\uC18C\uC5D0 \uD0A4\uB97C \uB9CC\uB4E0\uB2E4 (\uAE30\uBC00 \uB370\uC774\uD130 \uAC12\uC740 --value, --file, \uD45C\uC900 \uC785\uB825 \uC911 \uD558\uB098)").argument("<keystore-id>", "\uD0A4 \uC800\uC7A5\uC18C ID").requiredOption("--type <type>", "\uD0A4 \uC885\uB958 (secret|symmetric-key|asymmetric-key)").requiredOption("--name <name>", "\uD0A4 \uC774\uB984 (100\uC790 \uC774\uD558)").option("--description <text>", "\uD0A4 \uC124\uBA85").option("--value <value>", "\uAE30\uBC00 \uB370\uC774\uD130 \uAC12 (--type secret \uC804\uC6A9)").option("--file <path>", "\uAE30\uBC00 \uB370\uC774\uD130 \uAC12\uC744 \uC77D\uC744 \uD30C\uC77C (--type secret \uC804\uC6A9)")
12795
+ ).action(async (keyStoreId, _opts, command) => {
12796
+ const opts = command.optsWithGlobals();
12797
+ const parsedId = parseKeyStoreId(keyStoreId);
12798
+ const type = parseKeyTypeOption(opts.type);
12799
+ const name = parseKeyNameOption(opts.name);
12800
+ const description = opts.description?.trim() || void 0;
12801
+ const macAddress = parseMacAddressOption(opts.macAddress);
12802
+ let secretValue;
12803
+ if (type === "SECRET") {
12804
+ const input = readSkmInput(
12805
+ { text: opts.value, file: opts.file },
12806
+ { textFlag: "--value", label: "\uAE30\uBC00 \uB370\uC774\uD130", maxBytes: MAX_JSON_INPUT_BYTES }
12807
+ );
12808
+ secretValue = decodeUtf8Input(input, "\uAE30\uBC00 \uB370\uC774\uD130", "\uBC14\uC774\uB108\uB9AC\uB294 base64\uB85C \uC778\uCF54\uB529\uD574 \uC804\uB2EC\uD558\uC138\uC694.");
12809
+ } else if (opts.value !== void 0 || opts.file !== void 0) {
12810
+ throw new NhnCloudCliError("--value\uC640 --file\uC740 --type secret\uC5D0\uC11C\uB9CC \uC501\uB2C8\uB2E4.", EXIT_PARAM_ERROR);
12811
+ }
12812
+ const { client } = await resolveSkmClient({ profile: opts.profile, macAddress });
12813
+ const created = await withSkmSpinner(`SKM \uD0A4 ${formatCell(name)} \uC0DD\uC131 \uC911...`, async () => {
12814
+ const keyStoreName = await resolveKeyStoreName(client, parsedId);
12815
+ if (secretValue !== void 0) {
12816
+ return client.createSecret(keyStoreName, name, description, secretValue);
12817
+ }
12818
+ if (type === "SYMMETRIC_KEY") {
12819
+ return client.createSymmetricKey(keyStoreName, name, description);
12820
+ }
12821
+ return client.createAsymmetricKey(keyStoreName, name, description);
12822
+ });
12823
+ outputSkmWriteResult(
12824
+ opts,
12825
+ { keyId: created.keyId, keyStatus: created.keyStatus, keyStoreId: parsedId, name },
12826
+ [formatCell(created.keyId)]
12827
+ );
12828
+ });
12829
+ var deleteCommand10 = withSkmOptions(
12830
+ new import_commander62.Command("delete").description("\uD0A4 \uC0AD\uC81C\uB97C \uC608\uC57D\uD55C\uB2E4 (7\uC77C \uB4A4 \uC0AD\uC81C, \uADF8 \uC804\uC5D0\uB294 \uCF58\uC194\uC5D0\uC11C \uCDE8\uC18C \uAC00\uB2A5)").argument("<key-id>", "\uD0A4 ID").option("--yes", "\uC0AD\uC81C \uC608\uC57D \uD655\uC778")
12831
+ ).action(async (keyId, _opts, command) => {
12832
+ const opts = command.optsWithGlobals();
12833
+ requireYes(opts.yes, "\uD0A4 \uC0AD\uC81C \uC608\uC57D");
12834
+ const parsedKeyId = parseRequiredArgument(keyId, "key-id");
12835
+ const macAddress = parseMacAddressOption(opts.macAddress);
12836
+ const { client } = await resolveSkmClient({ profile: opts.profile, macAddress });
12837
+ const deletion = await withSkmSpinner(
12838
+ `SKM \uD0A4 ${formatCell(parsedKeyId)} \uC0AD\uC81C \uC608\uC57D \uC911...`,
12839
+ () => client.scheduleKeyDeletion(parsedKeyId)
12840
+ );
12841
+ outputSkmWriteResult(
12842
+ opts,
12843
+ {
12844
+ operation: "key-delete-scheduled",
12845
+ status: "succeeded",
12846
+ keyId: deletion.keyId,
12847
+ deletionDateTime: deletion.deletionDateTime
12848
+ },
12849
+ [formatCell(deletion.keyId)]
12850
+ );
12851
+ });
12852
+ var purgeCommand = withSkmOptions(
12853
+ new import_commander62.Command("purge").description("\uD0A4\uB97C \uC989\uC2DC \uC0AD\uC81C\uD55C\uB2E4 (\uC0AD\uC81C \uC608\uC57D\uB41C \uD0A4\uB9CC, \uB418\uB3CC\uB9B4 \uC218 \uC5C6\uC74C)").argument("<key-id>", "\uD0A4 ID").option("--yes", "\uC989\uC2DC \uC0AD\uC81C \uD655\uC778")
12854
+ ).action(async (keyId, _opts, command) => {
12855
+ const opts = command.optsWithGlobals();
12856
+ requireYes(opts.yes, "\uD0A4 \uC989\uC2DC \uC0AD\uC81C");
12857
+ const parsedKeyId = parseRequiredArgument(keyId, "key-id");
12858
+ const macAddress = parseMacAddressOption(opts.macAddress);
12859
+ const { client } = await resolveSkmClient({ profile: opts.profile, macAddress });
12860
+ const deletion = await withSkmSpinner(
12861
+ `SKM \uD0A4 ${formatCell(parsedKeyId)} \uC989\uC2DC \uC0AD\uC81C \uC911...`,
12862
+ () => client.deleteKeyNow(parsedKeyId)
12863
+ );
12864
+ outputSkmWriteResult(
12865
+ opts,
12866
+ { operation: "key-purge", status: "succeeded", keyId: deletion.keyId, deletionDateTime: deletion.deletionDateTime },
12867
+ [formatCell(deletion.keyId)]
12868
+ );
12869
+ });
12870
+
12871
+ // src/commands/skm/key.ts
12872
+ var KEY_FIELDS = [
12873
+ "keyId",
12874
+ "name",
12875
+ "description",
12876
+ "keyType",
12877
+ "currentKeyValueVersion",
12878
+ "autoRotationPeriod",
12879
+ "nextAutoRotationDate",
12880
+ "lastAccessDatetime",
12881
+ "deletionDatetime",
12882
+ "creationUser",
12883
+ "creationDatetime",
12884
+ "lastChangeUser",
12885
+ "lastChangeDatetime"
12886
+ ];
12887
+ var listCommand17 = withSkmOptions(
12888
+ new import_commander63.Command("list").description("\uD0A4 \uC800\uC7A5\uC18C\uC758 \uD0A4 \uBAA9\uB85D\uC744 \uC870\uD68C\uD55C\uB2E4 (\uC804\uCCB4 \uD544\uB4DC\uB294 --json)").argument("<keystore-id>", "\uD0A4 \uC800\uC7A5\uC18C ID").option("--type <type>", "\uD0A4 \uC885\uB958 (secret|symmetric-key|asymmetric-key)").option("--name <name>", "\uD0A4 \uC774\uB984 \uD544\uD130 (100\uC790 \uC774\uD558)").option("--status <status>", "\uD0A4 \uC0C1\uD0DC \uD544\uD130 (active|inactive)")
12889
+ ).action(async (keyStoreId, _opts, command) => {
12890
+ const opts = command.optsWithGlobals();
12891
+ const parsedId = parseKeyStoreId(keyStoreId);
12892
+ const type = parseKeyTypeOption(opts.type);
12893
+ const name = parseKeyNameOption(opts.name);
12894
+ const status = parseKeyStatusOption(opts.status);
12895
+ const macAddress = parseMacAddressOption(opts.macAddress);
12896
+ const { client } = await resolveSkmClient({ profile: opts.profile, macAddress });
12897
+ const filter = {
12898
+ ...type !== void 0 && { type },
12899
+ ...name !== void 0 && { name },
12900
+ ...status !== void 0 && { status }
12901
+ };
12902
+ const keys = await withSkmSpinner(
12903
+ `SKM \uD0A4 \uC800\uC7A5\uC18C ${parsedId} \uD0A4 \uBAA9\uB85D \uC870\uD68C \uC911...`,
12904
+ () => client.listKeys(parsedId, filter)
12905
+ );
12906
+ output(opts, {
12907
+ headers: ["keyId", "name", "keyType", "currentKeyValueVersion", "lastAccessDatetime", "deletionDatetime"],
12908
+ rows: keys.map((k) => [
12909
+ formatCell(k.keyId),
12910
+ formatCell(k.name),
12911
+ formatCell(k.keyType),
12912
+ formatCell(k.currentKeyValueVersion),
12913
+ formatCell(k.lastAccessDatetime),
12914
+ formatCell(k.deletionDatetime)
12915
+ ]),
12916
+ raw: keys,
12917
+ ids: keys.map((k) => formatCell(k.keyId))
12918
+ });
12919
+ });
12920
+ var getCommand12 = withSkmOptions(
12921
+ new import_commander63.Command("get").description("\uD0A4 \uD55C \uAC74\uC744 \uC870\uD68C\uD55C\uB2E4").argument("<keystore-id>", "\uD0A4 \uC800\uC7A5\uC18C ID").argument("<key-id>", "\uD0A4 ID")
12922
+ ).action(async (keyStoreId, keyId, _opts, command) => {
12923
+ const opts = command.optsWithGlobals();
12924
+ const parsedId = parseKeyStoreId(keyStoreId);
12925
+ const parsedKeyId = parseRequiredArgument(keyId, "key-id");
12926
+ const macAddress = parseMacAddressOption(opts.macAddress);
12927
+ const { client } = await resolveSkmClient({ profile: opts.profile, macAddress });
12928
+ const key = await withSkmSpinner(
12929
+ `SKM \uD0A4 ${formatCell(parsedKeyId)} \uC870\uD68C \uC911...`,
12930
+ () => client.getKey(parsedId, parsedKeyId)
12931
+ );
12932
+ output(opts, {
12933
+ headers: ["field", "value"],
12934
+ rows: KEY_FIELDS.map((field) => [field, formatCell(key[field])]),
12935
+ raw: key,
12936
+ ids: [formatCell(key.keyId)]
12937
+ });
12938
+ });
12939
+ var keyCommand = new import_commander63.Command("key").description("SKM \uD0A4 \uC870\uD68C\uC640 \uAD00\uB9AC").addCommand(listCommand17).addCommand(getCommand12).addCommand(createCommand12).addCommand(deleteCommand10).addCommand(purgeCommand);
12940
+
12941
+ // src/commands/skm/secret.ts
12942
+ var import_commander64 = require("commander");
12943
+ var getCommand13 = withSkmOptions(
12944
+ new import_commander64.Command("get").description("\uAE30\uBC00 \uB370\uC774\uD130\uB97C \uC870\uD68C\uD55C\uB2E4 (\uC6D0\uBB38\uC744 stdout\uC5D0 \uCD9C\uB825)").argument("<key-id>", "\uD0A4 ID")
12945
+ ).action(async (keyId, _opts, command) => {
12946
+ const opts = command.optsWithGlobals();
12947
+ const parsedKeyId = parseRequiredArgument(keyId, "key-id");
12948
+ const macAddress = parseMacAddressOption(opts.macAddress);
12949
+ const { client } = await resolveSkmClient({ profile: opts.profile, macAddress });
12950
+ const secret = await withSkmSpinner(
12951
+ `SKM \uAE30\uBC00 \uB370\uC774\uD130 ${formatCell(parsedKeyId)} \uC870\uD68C \uC911...`,
12952
+ () => client.getSecret(parsedKeyId)
12953
+ );
12954
+ printSkmValue(opts, secret, { secret });
12955
+ });
12956
+ var updateCommand5 = withSkmOptions(
12957
+ new import_commander64.Command("update").description("\uAE30\uBC00 \uB370\uC774\uD130 \uAC12\uC744 \uBC14\uAFBC\uB2E4 (\uAC12\uC740 --value, --file, \uD45C\uC900 \uC785\uB825 \uC911 \uD558\uB098)").argument("<key-id>", "\uD0A4 ID").option("--value <value>", "\uC0C8 \uAE30\uBC00 \uB370\uC774\uD130 \uAC12").option("--file <path>", "\uC0C8 \uAE30\uBC00 \uB370\uC774\uD130 \uAC12\uC744 \uC77D\uC744 \uD30C\uC77C")
12958
+ ).action(async (keyId, _opts, command) => {
12959
+ const opts = command.optsWithGlobals();
12960
+ const parsedKeyId = parseRequiredArgument(keyId, "key-id");
12961
+ const macAddress = parseMacAddressOption(opts.macAddress);
12962
+ const input = readSkmInput(
12963
+ { text: opts.value, file: opts.file },
12964
+ { textFlag: "--value", label: "\uAE30\uBC00 \uB370\uC774\uD130", maxBytes: MAX_JSON_INPUT_BYTES }
12965
+ );
12966
+ const secretValue = decodeUtf8Input(input, "\uAE30\uBC00 \uB370\uC774\uD130", "\uBC14\uC774\uB108\uB9AC\uB294 base64\uB85C \uC778\uCF54\uB529\uD574 \uC804\uB2EC\uD558\uC138\uC694.");
12967
+ const { client } = await resolveSkmClient({ profile: opts.profile, macAddress });
12968
+ const updated = await withSkmSpinner(
12969
+ `SKM \uAE30\uBC00 \uB370\uC774\uD130 ${formatCell(parsedKeyId)} \uC218\uC815 \uC911...`,
12970
+ () => client.updateSecret(parsedKeyId, secretValue)
12971
+ );
12972
+ const { secretValue: _omitted, ...result } = updated;
12973
+ outputSkmWriteResult(opts, result, [formatCell(updated.keyId)]);
12974
+ });
12975
+ var secretCommand = new import_commander64.Command("secret").description("SKM \uAE30\uBC00 \uB370\uC774\uD130 \uC870\uD68C\uC640 \uC218\uC815").addCommand(getCommand13).addCommand(updateCommand5);
12976
+
12977
+ // src/commands/skm/symmetric-key.ts
12978
+ var import_commander65 = require("commander");
12979
+ var MAX_ENCRYPT_BYTES = 32768;
12980
+ var encryptCommand = withSkmOptions(
12981
+ new import_commander65.Command("encrypt").description("\uB300\uCE6D\uD0A4\uB85C \uB370\uC774\uD130\uB97C \uC554\uD638\uD654\uD55C\uB2E4 (--plaintext / --file / stdin, UTF-8 \uD14D\uC2A4\uD2B8 32KB \uC774\uD558)").argument("<key-id>", "\uD0A4 ID").option("--plaintext <text>", "\uC554\uD638\uD654\uD560 \uB370\uC774\uD130").option("--file <path>", "\uC554\uD638\uD654\uD560 \uB370\uC774\uD130\uB97C \uC77D\uC744 \uD30C\uC77C \uACBD\uB85C")
12982
+ ).action(async (keyId, _opts, command) => {
12983
+ const opts = command.optsWithGlobals();
12984
+ const parsedKeyId = parseRequiredArgument(keyId, "key-id");
12985
+ const macAddress = parseMacAddressOption(opts.macAddress);
12986
+ const label = "\uC554\uD638\uD654\uD560 \uB370\uC774\uD130";
12987
+ const input = readSkmInput(
12988
+ { text: opts.plaintext, file: opts.file },
12989
+ { textFlag: "--plaintext", label, maxBytes: MAX_ENCRYPT_BYTES }
12990
+ );
12991
+ const plaintext = decodeUtf8Input(input, label, "\uBC14\uC774\uB108\uB9AC\uB294 base64\uB85C \uC778\uCF54\uB529\uD574 \uC804\uB2EC\uD558\uC138\uC694.");
12992
+ const { client } = await resolveSkmClient({ profile: opts.profile, macAddress });
12993
+ const body = await withSkmSpinner(
12994
+ `SKM \uB300\uCE6D\uD0A4 ${formatCell(parsedKeyId)}\uB85C \uC554\uD638\uD654 \uC911...`,
12995
+ () => client.encrypt(parsedKeyId, plaintext)
12996
+ );
12997
+ printSkmValue(opts, body.ciphertext, body);
12998
+ });
12999
+ var decryptCommand = withSkmOptions(
13000
+ new import_commander65.Command("decrypt").description("\uB300\uCE6D\uD0A4\uB85C \uC554\uD638\uBB38\uC744 \uBCF5\uD638\uD654\uD55C\uB2E4 (--ciphertext / --file / stdin, \uD3C9\uBB38\uC744 stdout\uC5D0 \uCD9C\uB825)").argument("<key-id>", "\uD0A4 ID").option("--ciphertext <text>", "\uBCF5\uD638\uD654\uD560 \uC554\uD638\uBB38 (base64)").option("--file <path>", "\uBCF5\uD638\uD654\uD560 \uC554\uD638\uBB38\uC744 \uC77D\uC744 \uD30C\uC77C \uACBD\uB85C")
13001
+ ).action(async (keyId, _opts, command) => {
13002
+ const opts = command.optsWithGlobals();
13003
+ const parsedKeyId = parseRequiredArgument(keyId, "key-id");
13004
+ const macAddress = parseMacAddressOption(opts.macAddress);
13005
+ const label = "\uBCF5\uD638\uD654\uD560 \uC554\uD638\uBB38";
13006
+ const input = readSkmInput(
13007
+ { text: opts.ciphertext, file: opts.file },
13008
+ { textFlag: "--ciphertext", label, maxBytes: MAX_JSON_INPUT_BYTES }
13009
+ );
13010
+ const ciphertext = decodeUtf8Input(input, label, "\uC554\uD638\uBB38\uC740 base64 \uBB38\uC790\uC5F4\uC785\uB2C8\uB2E4.").trim();
13011
+ if (!ciphertext) {
13012
+ throw new NhnCloudCliError(`${label}\uC774 \uBE44\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.`, EXIT_PARAM_ERROR);
13013
+ }
13014
+ const { client } = await resolveSkmClient({ profile: opts.profile, macAddress });
13015
+ const body = await withSkmSpinner(
13016
+ `SKM \uB300\uCE6D\uD0A4 ${formatCell(parsedKeyId)}\uB85C \uBCF5\uD638\uD654 \uC911...`,
13017
+ () => client.decrypt(parsedKeyId, ciphertext)
13018
+ );
13019
+ printSkmValue(opts, body.plaintext, body);
13020
+ });
13021
+ var getCommand14 = withSkmOptions(
13022
+ new import_commander65.Command("get").description("\uB300\uCE6D\uD0A4 \uC6D0\uBB38\uC744 \uC870\uD68C\uD55C\uB2E4 (\uC6D0\uBB38\uC744 stdout\uC5D0 \uCD9C\uB825)").argument("<key-id>", "\uD0A4 ID").option("--key-version <n>", "\uC870\uD68C\uD560 \uD0A4 \uBC84\uC804 (0 \uC774\uC0C1, \uC0DD\uB7B5\uD558\uBA74 \uBC84\uC804\uC744 \uC9C0\uC815\uD558\uC9C0 \uC54A\uACE0 \uC694\uCCAD)")
13023
+ ).action(async (keyId, _opts, command) => {
13024
+ const opts = command.optsWithGlobals();
13025
+ const parsedKeyId = parseRequiredArgument(keyId, "key-id");
13026
+ const keyVersion = parseKeyVersionOption(opts.keyVersion);
13027
+ const macAddress = parseMacAddressOption(opts.macAddress);
13028
+ const { client } = await resolveSkmClient({ profile: opts.profile, macAddress });
13029
+ const body = await withSkmSpinner(
13030
+ `SKM \uB300\uCE6D\uD0A4 ${formatCell(parsedKeyId)} \uC870\uD68C \uC911...`,
13031
+ () => client.getSymmetricKey(parsedKeyId, keyVersion)
13032
+ );
13033
+ printSkmValue(opts, body.symmetricKey, body);
13034
+ });
13035
+ var createLocalKeyCommand = withSkmOptions(
13036
+ new import_commander65.Command("create-local-key").description("\uB85C\uCEEC \uC554\uD638\uD654\uC5D0 \uC4F8 \uB370\uC774\uD130 \uD0A4\uB97C \uB9CC\uB4E0\uB2E4 (\uD3C9\uBB38 \uD0A4\uB294 \uC774 \uC751\uB2F5\uC5D0\uC11C\uB9CC \uBC1B\uC744 \uC218 \uC788\uB2E4)").argument("<key-id>", "\uD0A4 ID")
13037
+ ).action(async (keyId, _opts, command) => {
13038
+ const opts = command.optsWithGlobals();
13039
+ const parsedKeyId = parseRequiredArgument(keyId, "key-id");
13040
+ const macAddress = parseMacAddressOption(opts.macAddress);
13041
+ const { client } = await resolveSkmClient({ profile: opts.profile, macAddress });
13042
+ const body = await withSkmSpinner(
13043
+ `SKM \uB300\uCE6D\uD0A4 ${formatCell(parsedKeyId)}\uB85C \uB85C\uCEEC \uD0A4 \uC0DD\uC131 \uC911...`,
13044
+ () => client.createLocalKey(parsedKeyId)
13045
+ );
13046
+ output(opts, {
13047
+ headers: ["field", "value"],
13048
+ rows: [
13049
+ ["localKeyPlaintext", formatCell(body.localKeyPlaintext)],
13050
+ ["localKeyCiphertext", formatCell(body.localKeyCiphertext)],
13051
+ ["keyVersion", formatCell(body.keyVersion)]
13052
+ ],
13053
+ raw: body,
13054
+ ids: [body.localKeyPlaintext, body.localKeyCiphertext]
13055
+ });
13056
+ });
13057
+ var symmetricKeyCommand = new import_commander65.Command("symmetric-key").description("SKM \uB300\uCE6D\uD0A4 \uC554\uBCF5\uD638\uD654\uC640 \uD0A4 \uC870\uD68C").addCommand(encryptCommand).addCommand(decryptCommand).addCommand(getCommand14).addCommand(createLocalKeyCommand);
13058
+
13059
+ // src/commands/skm/asymmetric-key.ts
13060
+ var import_commander66 = require("commander");
13061
+ var MAX_SIGN_BYTES = 245;
13062
+ var MAX_STANDARD_SIGN_BYTES = 65536;
13063
+ function readSignPayload(opts, label) {
13064
+ const input = readSkmInput(
13065
+ { text: opts.plaintext, file: opts.file },
13066
+ { textFlag: "--plaintext", label, maxBytes: opts.standard ? MAX_STANDARD_SIGN_BYTES : MAX_SIGN_BYTES }
13067
+ );
13068
+ if (opts.standard) return input.toString("base64");
13069
+ return decodeUtf8Input(input, label, "\uBC14\uC774\uB108\uB9AC\uB294 --standard\uB85C \uC11C\uBA85\uD558\uC138\uC694.");
13070
+ }
13071
+ function withSignInputOptions(command) {
13072
+ return command.argument("<key-id>", "\uD0A4 ID").option("--plaintext <text>", "\uB370\uC774\uD130 (\uBBF8\uC9C0\uC815 \uC2DC --file \uB610\uB294 stdin)").option("--file <path>", "\uB370\uC774\uD130\uB97C \uC77D\uC744 \uD30C\uC77C \uACBD\uB85C").option("--standard", "\uD45C\uC900 \uC2A4\uD0B4(RSASSA-PSS)\uC73C\uB85C \uCC98\uB9AC\uD55C\uB2E4 (\uBC14\uC774\uB108\uB9AC \uAC00\uB2A5, 64KB \uC774\uD558)");
13073
+ }
13074
+ var signCommand = withSkmOptions(
13075
+ withSignInputOptions(
13076
+ new import_commander66.Command("sign").description("\uBE44\uB300\uCE6D\uD0A4\uB85C \uB370\uC774\uD130\uC5D0 \uC11C\uBA85\uD55C\uB2E4 (\uC77C\uBC18 \uC11C\uBA85\uC740 UTF-8 \uD14D\uC2A4\uD2B8 245\uBC14\uC774\uD2B8 \uC774\uD558)")
13077
+ )
13078
+ ).action(async (keyId, _opts, command) => {
13079
+ const opts = command.optsWithGlobals();
13080
+ const parsedKeyId = parseRequiredArgument(keyId, "key-id");
13081
+ const macAddress = parseMacAddressOption(opts.macAddress);
13082
+ const payload = readSignPayload(opts, "\uC11C\uBA85\uD560 \uB370\uC774\uD130");
13083
+ const { client } = await resolveSkmClient({ profile: opts.profile, macAddress });
13084
+ const body = await withSkmSpinner(
13085
+ `SKM \uBE44\uB300\uCE6D\uD0A4 ${formatCell(parsedKeyId)}\uB85C \uC11C\uBA85 \uC911...`,
13086
+ () => opts.standard ? client.signStandard(parsedKeyId, payload) : client.sign(parsedKeyId, payload)
13087
+ );
13088
+ printSkmValue(opts, body.signature, body);
13089
+ });
13090
+ var verifyCommand = withSkmOptions(
13091
+ withSignInputOptions(
13092
+ new import_commander66.Command("verify").description("\uBE44\uB300\uCE6D\uD0A4\uB85C \uC11C\uBA85\uC744 \uAC80\uC99D\uD55C\uB2E4 (\uAC80\uC99D \uC2E4\uD328\uB294 \uC885\uB8CC \uCF54\uB4DC 1)")
13093
+ ).requiredOption("--signature <sig>", "\uAC80\uC99D\uD560 \uC11C\uBA85\uAC12 (base64)").option("--key-version <n>", "\uC11C\uBA85\uD55C \uD0A4 \uBC84\uC804 (--standard \uAC80\uC99D\uC5D0 \uD544\uC218)")
13094
+ ).action(async (keyId, _opts, command) => {
13095
+ const opts = command.optsWithGlobals();
13096
+ const parsedKeyId = parseRequiredArgument(keyId, "key-id");
13097
+ const signature = opts.signature.trim();
13098
+ if (!signature) {
13099
+ throw new NhnCloudCliError("--signature\uAC00 \uBE44\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.", EXIT_PARAM_ERROR);
13100
+ }
13101
+ const keyVersion = parseKeyVersionOption(opts.keyVersion);
13102
+ if (opts.standard && keyVersion === void 0) {
13103
+ throw new NhnCloudCliError("--standard \uAC80\uC99D\uC5D0\uB294 --key-version\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.", EXIT_PARAM_ERROR);
13104
+ }
13105
+ if (!opts.standard && keyVersion !== void 0) {
13106
+ throw new NhnCloudCliError("--key-version\uC740 --standard\uC640 \uD568\uAED8 \uC9C0\uC815\uD569\uB2C8\uB2E4.", EXIT_PARAM_ERROR);
13107
+ }
13108
+ const macAddress = parseMacAddressOption(opts.macAddress);
13109
+ const payload = readSignPayload(opts, "\uAC80\uC99D\uD560 \uB370\uC774\uD130");
13110
+ const { client } = await resolveSkmClient({ profile: opts.profile, macAddress });
13111
+ const body = await withSkmSpinner(
13112
+ `SKM \uBE44\uB300\uCE6D\uD0A4 ${formatCell(parsedKeyId)}\uB85C \uC11C\uBA85 \uAC80\uC99D \uC911...`,
13113
+ () => keyVersion !== void 0 ? client.verifyStandard(parsedKeyId, payload, signature, keyVersion) : client.verify(parsedKeyId, payload, signature)
13114
+ );
13115
+ if (opts.json) {
13116
+ printJson(body);
13117
+ } else if (!opts.quiet) {
13118
+ const verdict = body.result ? "\uAC80\uC99D \uC131\uACF5" : "\uAC80\uC99D \uC2E4\uD328";
13119
+ process.stdout.write(`${verdict} (keyVersion ${body.keyVersion})
13120
+ `);
13121
+ }
13122
+ if (!body.result) {
13123
+ throw new NhnCloudCliError("\uC11C\uBA85 \uAC80\uC99D\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.", EXIT_API_ERROR);
13124
+ }
13125
+ });
13126
+ function keyMaterialCommand(name, description, fetch, spinnerLabel) {
13127
+ return withSkmOptions(
13128
+ new import_commander66.Command(name).description(description).argument("<key-id>", "\uD0A4 ID").option("--key-version <n>", "\uC870\uD68C\uD560 \uD0A4 \uBC84\uC804 (0 \uC774\uC0C1, \uC0DD\uB7B5\uD558\uBA74 \uBC84\uC804\uC744 \uC9C0\uC815\uD558\uC9C0 \uC54A\uACE0 \uC694\uCCAD)")
13129
+ ).action(async (keyId, _opts, command) => {
13130
+ const opts = command.optsWithGlobals();
13131
+ const parsedKeyId = parseRequiredArgument(keyId, "key-id");
13132
+ const keyVersion = parseKeyVersionOption(opts.keyVersion);
13133
+ const macAddress = parseMacAddressOption(opts.macAddress);
13134
+ const { client } = await resolveSkmClient({ profile: opts.profile, macAddress });
13135
+ const body = await withSkmSpinner(
13136
+ `SKM \uBE44\uB300\uCE6D\uD0A4 ${formatCell(parsedKeyId)} ${spinnerLabel} \uC870\uD68C \uC911...`,
13137
+ () => fetch(client, parsedKeyId, keyVersion)
13138
+ );
13139
+ printSkmValue(opts, body.standardEncodedKey ?? body.encodedKey, body);
13140
+ });
13141
+ }
13142
+ var publicKeyCommand = keyMaterialCommand(
13143
+ "public-key",
13144
+ "\uACF5\uAC1C\uD0A4\uB97C \uC870\uD68C\uD55C\uB2E4 (standardEncodedKey\uB97C, \uC5C6\uC73C\uBA74 encodedKey\uB97C stdout\uC5D0 \uCD9C\uB825)",
13145
+ (client, keyId, keyVersion) => client.getPublicKey(keyId, keyVersion),
13146
+ "\uACF5\uAC1C\uD0A4"
13147
+ );
13148
+ var privateKeyCommand = keyMaterialCommand(
13149
+ "private-key",
13150
+ "\uAC1C\uC778\uD0A4 \uC6D0\uBB38\uC744 \uC870\uD68C\uD55C\uB2E4 (standardEncodedKey\uB97C, \uC5C6\uC73C\uBA74 encodedKey\uB97C stdout\uC5D0 \uCD9C\uB825)",
13151
+ (client, keyId, keyVersion) => client.getPrivateKey(keyId, keyVersion),
13152
+ "\uAC1C\uC778\uD0A4"
13153
+ );
13154
+ var asymmetricKeyCommand = new import_commander66.Command("asymmetric-key").description("SKM \uBE44\uB300\uCE6D\uD0A4 \uC11C\uBA85\xB7\uAC80\uC99D\uACFC \uD0A4 \uC870\uD68C").addCommand(signCommand).addCommand(verifyCommand).addCommand(publicKeyCommand).addCommand(privateKeyCommand);
13155
+
13156
+ // src/commands/loadbalancer/list.ts
13157
+ var import_commander67 = require("commander");
13158
+
13159
+ // src/commands/loadbalancer/helpers.ts
13160
+ var import_node_net3 = require("net");
13161
+
13162
+ // src/services/loadbalancer/client.ts
13163
+ var import_ky17 = __toESM(require("ky"));
13164
+
13165
+ // src/services/loadbalancer/types.ts
13166
+ function isRecord5(value) {
13167
+ return typeof value === "object" && value !== null;
13168
+ }
11216
13169
  function isNonEmptyString(value) {
11217
13170
  return typeof value === "string" && value.length > 0;
11218
13171
  }
@@ -11220,29 +13173,29 @@ function isIpAclAction(value) {
11220
13173
  return value === "ALLOW" || value === "DENY";
11221
13174
  }
11222
13175
  function isLoadBalancerIpAclGroup(value) {
11223
- return isRecord3(value) && isNonEmptyString(value["ipacl_group_id"]);
13176
+ return isRecord5(value) && isNonEmptyString(value["ipacl_group_id"]);
11224
13177
  }
11225
13178
  function isIpAclGroupLoadBalancer(value) {
11226
- return isRecord3(value) && isNonEmptyString(value["loadbalancer_id"]);
13179
+ return isRecord5(value) && isNonEmptyString(value["loadbalancer_id"]);
11227
13180
  }
11228
13181
  function isLoadBalancer(value) {
11229
- if (!isRecord3(value)) return false;
13182
+ if (!isRecord5(value)) return false;
11230
13183
  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);
11231
13184
  }
11232
13185
  function isIpAclGroup(value) {
11233
- if (!isRecord3(value)) return false;
13186
+ if (!isRecord5(value)) return false;
11234
13187
  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);
11235
13188
  }
11236
13189
  function isIpAclTarget(value) {
11237
- if (!isRecord3(value)) return false;
13190
+ if (!isRecord5(value)) return false;
11238
13191
  return isNonEmptyString(value["id"]) && typeof value["cidr_address"] === "string" && typeof value["description"] === "string" && isNonEmptyString(value["ipacl_group_id"]);
11239
13192
  }
11240
13193
  function isIpAclBinding(value) {
11241
- return isRecord3(value) && isNonEmptyString(value["loadbalancer_id"]) && isNonEmptyString(value["ipacl_group_id"]);
13194
+ return isRecord5(value) && isNonEmptyString(value["loadbalancer_id"]) && isNonEmptyString(value["ipacl_group_id"]);
11242
13195
  }
11243
13196
 
11244
13197
  // src/services/loadbalancer/client.ts
11245
- function isRecord4(value) {
13198
+ function isRecord6(value) {
11246
13199
  return typeof value === "object" && value !== null;
11247
13200
  }
11248
13201
  function requireNonEmptyId(id, label) {
@@ -11253,7 +13206,7 @@ function requireNonEmptyId(id, label) {
11253
13206
  return normalized;
11254
13207
  }
11255
13208
  function readArrayField(response, key, itemGuard, missingKeyMessage, invalidItemMessage) {
11256
- if (!isRecord4(response) || !Array.isArray(response[key])) {
13209
+ if (!isRecord6(response) || !Array.isArray(response[key])) {
11257
13210
  throw new NhnCloudCliError(missingKeyMessage, EXIT_API_ERROR);
11258
13211
  }
11259
13212
  const items = response[key];
@@ -11263,7 +13216,7 @@ function readArrayField(response, key, itemGuard, missingKeyMessage, invalidItem
11263
13216
  return items;
11264
13217
  }
11265
13218
  function readObjectField(response, key, itemGuard, missingKeyMessage, invalidItemMessage) {
11266
- if (!isRecord4(response) || !(key in response)) {
13219
+ if (!isRecord6(response) || !(key in response)) {
11267
13220
  throw new NhnCloudCliError(missingKeyMessage, EXIT_API_ERROR);
11268
13221
  }
11269
13222
  const item = response[key];
@@ -11401,10 +13354,10 @@ var LoadBalancerClient = class {
11401
13354
  }
11402
13355
  return raw;
11403
13356
  }
11404
- async getJson(path5, searchParams) {
11405
- const url = `${this.endpoint}${path5}`;
13357
+ async getJson(path4, searchParams) {
13358
+ const url = `${this.endpoint}${path4}`;
11406
13359
  try {
11407
- return await import_ky16.default.get(url, {
13360
+ return await import_ky17.default.get(url, {
11408
13361
  headers: this.authHeaders(),
11409
13362
  searchParams,
11410
13363
  retry: 0,
@@ -11414,10 +13367,10 @@ var LoadBalancerClient = class {
11414
13367
  throw toNhnCloudCliError(error);
11415
13368
  }
11416
13369
  }
11417
- async postJson(path5, json) {
11418
- const url = `${this.endpoint}${path5}`;
13370
+ async postJson(path4, json) {
13371
+ const url = `${this.endpoint}${path4}`;
11419
13372
  try {
11420
- return await import_ky16.default.post(url, {
13373
+ return await import_ky17.default.post(url, {
11421
13374
  headers: this.authHeaders(),
11422
13375
  json,
11423
13376
  retry: 0,
@@ -11427,10 +13380,10 @@ var LoadBalancerClient = class {
11427
13380
  throw toNhnCloudCliError(error);
11428
13381
  }
11429
13382
  }
11430
- async putJson(path5, json) {
11431
- const url = `${this.endpoint}${path5}`;
13383
+ async putJson(path4, json) {
13384
+ const url = `${this.endpoint}${path4}`;
11432
13385
  try {
11433
- return await import_ky16.default.put(url, {
13386
+ return await import_ky17.default.put(url, {
11434
13387
  headers: this.authHeaders(),
11435
13388
  json,
11436
13389
  retry: 0,
@@ -11440,10 +13393,10 @@ var LoadBalancerClient = class {
11440
13393
  throw toNhnCloudCliError(error);
11441
13394
  }
11442
13395
  }
11443
- async deleteNoContent(path5) {
11444
- const url = `${this.endpoint}${path5}`;
13396
+ async deleteNoContent(path4) {
13397
+ const url = `${this.endpoint}${path4}`;
11445
13398
  try {
11446
- await import_ky16.default.delete(url, {
13399
+ await import_ky17.default.delete(url, {
11447
13400
  headers: this.authHeaders(),
11448
13401
  retry: 0,
11449
13402
  timeout: DEFAULT_TIMEOUT_MS
@@ -11481,7 +13434,7 @@ function parseIpOrCidr(value) {
11481
13434
  const normalized = value.trim();
11482
13435
  const parts = normalized.split("/");
11483
13436
  const address = parts[0] ?? "";
11484
- const version = (0, import_node_net2.isIP)(address);
13437
+ const version = (0, import_node_net3.isIP)(address);
11485
13438
  if (version === 0 || parts.length > 2) {
11486
13439
  throw new NhnCloudCliError(
11487
13440
  `--cidr\uC740 IP \uC8FC\uC18C \uB610\uB294 CIDR\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4: ${JSON.stringify(value)}`,
@@ -11534,7 +13487,7 @@ async function resolveLoadBalancerClient(opts) {
11534
13487
  }
11535
13488
 
11536
13489
  // src/commands/loadbalancer/list.ts
11537
- var listCommand16 = new import_commander59.Command("list").description("Load Balancer \uBAA9\uB85D\uC744 \uC870\uD68C\uD55C\uB2E4 (\uC804\uCCB4 \uD544\uB4DC\uB294 --json)").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) => {
13490
+ var listCommand18 = new import_commander67.Command("list").description("Load Balancer \uBAA9\uB85D\uC744 \uC870\uD68C\uD55C\uB2E4 (\uC804\uCCB4 \uD544\uB4DC\uB294 --json)").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) => {
11538
13491
  const opts = cmd.optsWithGlobals();
11539
13492
  const { client } = await resolveLoadBalancerClient(opts);
11540
13493
  startSpinner("Load Balancer \uBAA9\uB85D \uC870\uD68C \uC911...");
@@ -11572,8 +13525,8 @@ var listCommand16 = new import_commander59.Command("list").description("Load Bal
11572
13525
  });
11573
13526
 
11574
13527
  // src/commands/loadbalancer/get.ts
11575
- var import_commander60 = require("commander");
11576
- var getCommand11 = new import_commander60.Command("get").description("Load Balancer\uB97C \uC774\uB984 \uB610\uB294 UUID\uB85C \uC870\uD68C\uD55C\uB2E4").argument("<loadbalancer>", "Load Balancer \uC774\uB984 \uB610\uB294 UUID").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) => {
13528
+ var import_commander68 = require("commander");
13529
+ var getCommand15 = new import_commander68.Command("get").description("Load Balancer\uB97C \uC774\uB984 \uB610\uB294 UUID\uB85C \uC870\uD68C\uD55C\uB2E4").argument("<loadbalancer>", "Load Balancer \uC774\uB984 \uB610\uB294 UUID").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) => {
11577
13530
  const opts = cmd.optsWithGlobals();
11578
13531
  const input = requireResourceInput(loadBalancerInput, "Load Balancer");
11579
13532
  const { client } = await resolveLoadBalancerClient(opts);
@@ -11610,10 +13563,10 @@ var getCommand11 = new import_commander60.Command("get").description("Load Balan
11610
13563
  });
11611
13564
 
11612
13565
  // src/commands/loadbalancer/ipacl.ts
11613
- var import_commander62 = require("commander");
13566
+ var import_commander70 = require("commander");
11614
13567
 
11615
13568
  // src/commands/loadbalancer/target.ts
11616
- var import_commander61 = require("commander");
13569
+ var import_commander69 = require("commander");
11617
13570
 
11618
13571
  // src/commands/loadbalancer/rebind.ts
11619
13572
  function shellQuote(value) {
@@ -11744,7 +13697,7 @@ function targetWriteResult(operation, target, rebind) {
11744
13697
  rebind
11745
13698
  };
11746
13699
  }
11747
- var listCommand17 = new import_commander61.Command("list").description("IP ACL \uB300\uC0C1 \uBAA9\uB85D\uC744 \uC870\uD68C\uD55C\uB2E4").argument("<group>", "IP ACL \uADF8\uB8F9 \uC774\uB984 \uB610\uB294 UUID").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) => {
13700
+ var listCommand19 = new import_commander69.Command("list").description("IP ACL \uB300\uC0C1 \uBAA9\uB85D\uC744 \uC870\uD68C\uD55C\uB2E4").argument("<group>", "IP ACL \uADF8\uB8F9 \uC774\uB984 \uB610\uB294 UUID").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) => {
11748
13701
  const opts = cmd.optsWithGlobals();
11749
13702
  const input = requireResourceInput(groupInput, "IP ACL \uADF8\uB8F9");
11750
13703
  const { client } = await resolveLoadBalancerClient(opts);
@@ -11773,7 +13726,7 @@ var listCommand17 = new import_commander61.Command("list").description("IP ACL \
11773
13726
  ids: targets.map((target) => target.id)
11774
13727
  });
11775
13728
  });
11776
- var addCommand = new import_commander61.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) => {
13729
+ var addCommand = new import_commander69.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) => {
11777
13730
  const opts = cmd.optsWithGlobals();
11778
13731
  requireYes(opts.yes, "IP ACL \uB300\uC0C1 \uCD94\uAC00");
11779
13732
  const parsedGroup = requireResourceInput(groupInput, "IP ACL \uADF8\uB8F9");
@@ -11810,7 +13763,7 @@ var addCommand = new import_commander61.Command("add").description("IP ACL \uB30
11810
13763
  writeTargetWarnings(group, result);
11811
13764
  if (result.status === "partial") process.exitCode = EXIT_API_ERROR;
11812
13765
  });
11813
- var removeCommand = new import_commander61.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) => {
13766
+ var removeCommand = new import_commander69.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) => {
11814
13767
  const opts = cmd.optsWithGlobals();
11815
13768
  requireYes(opts.yes, "IP ACL \uB300\uC0C1 \uC0AD\uC81C");
11816
13769
  const parsedTargetId = requireResourceInput(targetInput, "IP ACL \uB300\uC0C1");
@@ -11841,13 +13794,13 @@ var removeCommand = new import_commander61.Command("remove").description("IP ACL
11841
13794
  writeTargetWarnings(group, result);
11842
13795
  if (result.status === "partial") process.exitCode = EXIT_API_ERROR;
11843
13796
  });
11844
- var targetCommand = new import_commander61.Command("target").description("IP ACL \uB300\uC0C1 \uC870\uD68C\xB7\uAD00\uB9AC");
11845
- targetCommand.addCommand(listCommand17);
13797
+ var targetCommand = new import_commander69.Command("target").description("IP ACL \uB300\uC0C1 \uC870\uD68C\xB7\uAD00\uB9AC");
13798
+ targetCommand.addCommand(listCommand19);
11846
13799
  targetCommand.addCommand(addCommand);
11847
13800
  targetCommand.addCommand(removeCommand);
11848
13801
 
11849
13802
  // src/commands/loadbalancer/ipacl.ts
11850
- var listCommand18 = new import_commander62.Command("list").description("IP ACL \uADF8\uB8F9 \uBAA9\uB85D\uC744 \uC870\uD68C\uD55C\uB2E4 (\uC804\uCCB4 \uD544\uB4DC\uB294 --json)").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) => {
13803
+ var listCommand20 = new import_commander70.Command("list").description("IP ACL \uADF8\uB8F9 \uBAA9\uB85D\uC744 \uC870\uD68C\uD55C\uB2E4 (\uC804\uCCB4 \uD544\uB4DC\uB294 --json)").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) => {
11851
13804
  const opts = cmd.optsWithGlobals();
11852
13805
  const { client } = await resolveLoadBalancerClient(opts);
11853
13806
  startSpinner("IP ACL \uADF8\uB8F9 \uBAA9\uB85D \uC870\uD68C \uC911...");
@@ -11875,7 +13828,7 @@ var listCommand18 = new import_commander62.Command("list").description("IP ACL \
11875
13828
  ids: groups.map((group) => group.id)
11876
13829
  });
11877
13830
  });
11878
- var getCommand12 = new import_commander62.Command("get").description("IP ACL \uADF8\uB8F9\uC744 \uC774\uB984 \uB610\uB294 UUID\uB85C \uC870\uD68C\uD55C\uB2E4").argument("<group>", "IP ACL \uADF8\uB8F9 \uC774\uB984 \uB610\uB294 UUID").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) => {
13831
+ var getCommand16 = new import_commander70.Command("get").description("IP ACL \uADF8\uB8F9\uC744 \uC774\uB984 \uB610\uB294 UUID\uB85C \uC870\uD68C\uD55C\uB2E4").argument("<group>", "IP ACL \uADF8\uB8F9 \uC774\uB984 \uB610\uB294 UUID").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) => {
11879
13832
  const opts = cmd.optsWithGlobals();
11880
13833
  const input = requireResourceInput(groupInput, "IP ACL \uADF8\uB8F9");
11881
13834
  const { client } = await resolveLoadBalancerClient(opts);
@@ -11908,7 +13861,7 @@ var getCommand12 = new import_commander62.Command("get").description("IP ACL \uA
11908
13861
  ids: [group.id]
11909
13862
  });
11910
13863
  });
11911
- var createCommand11 = new import_commander62.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) => {
13864
+ var createCommand13 = new import_commander70.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) => {
11912
13865
  const opts = cmd.optsWithGlobals();
11913
13866
  const parsedName = requireResourceInput(opts.name ?? "", "IP ACL \uADF8\uB8F9");
11914
13867
  const parsedAction = parseIpAclAction(opts.action ?? "");
@@ -11939,7 +13892,7 @@ var createCommand11 = new import_commander62.Command("create").description("IP A
11939
13892
  ids: [group.id]
11940
13893
  });
11941
13894
  });
11942
- var deleteCommand9 = new import_commander62.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) => {
13895
+ var deleteCommand11 = new import_commander70.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) => {
11943
13896
  const opts = cmd.optsWithGlobals();
11944
13897
  const confirmedYes = requireYes(opts.yes, "IP ACL \uADF8\uB8F9 \uC0AD\uC81C");
11945
13898
  const parsedGroup = requireResourceInput(groupInput, "IP ACL \uADF8\uB8F9");
@@ -11971,23 +13924,15 @@ var deleteCommand9 = new import_commander62.Command("delete").description("IP AC
11971
13924
  ids: [groupId]
11972
13925
  });
11973
13926
  });
11974
- var ipaclCommand = new import_commander62.Command("ipacl").description("IP ACL \uADF8\uB8F9\xB7\uB300\uC0C1 \uC870\uD68C");
11975
- ipaclCommand.addCommand(listCommand18);
11976
- ipaclCommand.addCommand(getCommand12);
11977
- ipaclCommand.addCommand(createCommand11);
11978
- ipaclCommand.addCommand(deleteCommand9);
13927
+ var ipaclCommand = new import_commander70.Command("ipacl").description("IP ACL \uADF8\uB8F9\xB7\uB300\uC0C1 \uC870\uD68C");
13928
+ ipaclCommand.addCommand(listCommand20);
13929
+ ipaclCommand.addCommand(getCommand16);
13930
+ ipaclCommand.addCommand(createCommand13);
13931
+ ipaclCommand.addCommand(deleteCommand11);
11979
13932
  ipaclCommand.addCommand(targetCommand);
11980
13933
 
11981
- // src/commands/loadbalancer/help.ts
11982
- function configureLoadBalancerHelp(command) {
11983
- command.configureHelp({ showGlobalOptions: true });
11984
- for (const child of command.commands) {
11985
- configureLoadBalancerHelp(child);
11986
- }
11987
- }
11988
-
11989
13934
  // src/commands/loadbalancer/binding.ts
11990
- var import_commander63 = require("commander");
13935
+ var import_commander71 = require("commander");
11991
13936
  function bindingResult(operation, loadBalancerId, groupIds) {
11992
13937
  return {
11993
13938
  operation,
@@ -12009,7 +13954,7 @@ function outputBindingResult(opts, result) {
12009
13954
  ids: [result.loadbalancer_id]
12010
13955
  });
12011
13956
  }
12012
- var setIpAclCommand2 = new import_commander63.Command("set-ipacl").description("Load Balancer\uC758 IP ACL \uADF8\uB8F9 \uC5F0\uACB0\uC744 \uC804\uCCB4 \uAD50\uCCB4\uD55C\uB2E4").argument("<loadbalancer>", "Load Balancer \uC774\uB984 \uB610\uB294 UUID").requiredOption(
13957
+ var setIpAclCommand2 = new import_commander71.Command("set-ipacl").description("Load Balancer\uC758 IP ACL \uADF8\uB8F9 \uC5F0\uACB0\uC744 \uC804\uCCB4 \uAD50\uCCB4\uD55C\uB2E4").argument("<loadbalancer>", "Load Balancer \uC774\uB984 \uB610\uB294 UUID").requiredOption(
12013
13958
  "--group <group>",
12014
13959
  "\uC801\uC6A9\uD560 IP ACL \uADF8\uB8F9 \uC774\uB984 \uB610\uB294 UUID (\uBC18\uBCF5 \uAC00\uB2A5)",
12015
13960
  collectOption
@@ -12049,7 +13994,7 @@ var setIpAclCommand2 = new import_commander63.Command("set-ipacl").description("
12049
13994
  bindingResult("loadbalancer-set-ipacl", loadBalancerId, groupIds)
12050
13995
  );
12051
13996
  });
12052
- var clearIpAclCommand = new import_commander63.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) => {
13997
+ var clearIpAclCommand = new import_commander71.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) => {
12053
13998
  const opts = cmd.optsWithGlobals();
12054
13999
  const confirmedYes = requireYes(opts.yes, "Load Balancer IP ACL \uC5F0\uACB0 \uD574\uC81C");
12055
14000
  const parsedLoadBalancer = requireResourceInput(loadBalancerInput, "Load Balancer");
@@ -12140,8 +14085,8 @@ Agent workflow:
12140
14085
  2. nhncloud ncs workload list --json
12141
14086
  3. nhncloud ncs workload get <id> --json
12142
14087
  `;
12143
- var program = new import_commander64.Command();
12144
- program.name("nhncloud").description("NHN Cloud CLI \u2014 AI agent & terminal friendly").version("0.18.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);
14088
+ var program = new import_commander72.Command();
14089
+ program.name("nhncloud").description("NHN Cloud CLI \u2014 AI agent & terminal friendly").version(CLI_VERSION).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);
12145
14090
  program.hook("preAction", () => {
12146
14091
  const opts = program.opts();
12147
14092
  const rawTimeout = opts.requestTimeout ?? process.env["NHNCLOUD_REQUEST_TIMEOUT"];
@@ -12162,13 +14107,13 @@ program.hook("preAction", () => {
12162
14107
  }
12163
14108
  });
12164
14109
  program.addCommand(configureCommand);
12165
- var logncrashCommand = new import_commander64.Command("logncrash").description("Log & Crash \uAD00\uB828 \uBA85\uB839").addHelpText("after", logncrashAgentWorkflow);
14110
+ var logncrashCommand = new import_commander72.Command("logncrash").description("Log & Crash \uAD00\uB828 \uBA85\uB839").addHelpText("after", logncrashAgentWorkflow);
12166
14111
  logncrashCommand.addCommand(availableTokenCommand);
12167
14112
  logncrashCommand.addCommand(searchCommand);
12168
14113
  logncrashCommand.addCommand(sendCommand);
12169
14114
  logncrashCommand.addCommand(exportCommand);
12170
14115
  program.addCommand(logncrashCommand);
12171
- var deployCommand2 = new import_commander64.Command("deploy").description("NHN Cloud Deploy \uAD00\uB828 \uBA85\uB839").addHelpText("after", deployAgentWorkflow);
14116
+ var deployCommand2 = new import_commander72.Command("deploy").description("NHN Cloud Deploy \uAD00\uB828 \uBA85\uB839").addHelpText("after", deployAgentWorkflow);
12172
14117
  deployCommand2.hook("preSubcommand", async () => {
12173
14118
  await warnLegacyDeployTargets();
12174
14119
  });
@@ -12182,7 +14127,7 @@ deployCommand2.addCommand(binariesCommand);
12182
14127
  deployCommand2.addCommand(uploadCommand);
12183
14128
  deployCommand2.addCommand(downloadCommand);
12184
14129
  program.addCommand(deployCommand2);
12185
- var instanceCommand = new import_commander64.Command("instance").description("Compute \uC778\uC2A4\uD134\uC2A4 \uAD00\uB828 \uBA85\uB839").addHelpText("after", instanceAgentWorkflow);
14130
+ var instanceCommand = new import_commander72.Command("instance").description("Compute \uC778\uC2A4\uD134\uC2A4 \uAD00\uB828 \uBA85\uB839").addHelpText("after", instanceAgentWorkflow);
12186
14131
  instanceCommand.addCommand(listCommand);
12187
14132
  instanceCommand.addCommand(flavorsCommand);
12188
14133
  instanceCommand.addCommand(availabilityZonesCommand);
@@ -12202,60 +14147,68 @@ instanceCommand.addCommand(volumeCommand);
12202
14147
  instanceCommand.addCommand(volumesCommand);
12203
14148
  instanceCommand.addCommand(securityGroupCommand2);
12204
14149
  program.addCommand(instanceCommand);
12205
- var networkCommand = new import_commander64.Command("network").description("VPC\xB7\uC11C\uBE0C\uB137 \uC870\uD68C\uC640 \uBCF4\uC548\uADF8\uB8F9 \uAD00\uB9AC").addHelpText("after", networkAgentWorkflow);
14150
+ var networkCommand = new import_commander72.Command("network").description("VPC\xB7\uC11C\uBE0C\uB137 \uC870\uD68C\uC640 \uBCF4\uC548\uADF8\uB8F9 \uAD00\uB9AC").addHelpText("after", networkAgentWorkflow);
12206
14151
  networkCommand.addCommand(listCommand3);
12207
14152
  networkCommand.addCommand(subnetCommand);
12208
14153
  networkCommand.addCommand(securityGroupCommand);
12209
14154
  program.addCommand(networkCommand);
12210
- var volumeCommand2 = new import_commander64.Command("volume").description("Block Storage \uBCFC\uB968 \uAD00\uB828 \uBA85\uB839").addHelpText("after", volumeAgentWorkflow);
14155
+ var volumeCommand2 = new import_commander72.Command("volume").description("Block Storage \uBCFC\uB968 \uAD00\uB828 \uBA85\uB839").addHelpText("after", volumeAgentWorkflow);
12211
14156
  volumeCommand2.addCommand(listCommand2);
12212
14157
  volumeCommand2.addCommand(getCommand);
12213
14158
  volumeCommand2.addCommand(createCommand);
12214
14159
  program.addCommand(volumeCommand2);
12215
- var floatingipCommand = new import_commander64.Command("floatingip").description(
14160
+ var floatingipCommand = new import_commander72.Command("floatingip").description(
12216
14161
  "Floating IP(\uC778\uC2A4\uD134\uC2A4 \uACF5\uC778 IP) \uAD00\uB9AC"
12217
14162
  ).addHelpText("after", floatingIpAgentWorkflow);
12218
14163
  floatingipCommand.addCommand(listCommand6);
12219
14164
  floatingipCommand.addCommand(createCommand4);
12220
14165
  floatingipCommand.addCommand(deleteCommand3);
12221
14166
  program.addCommand(floatingipCommand);
12222
- var loadbalancerCommand = new import_commander64.Command("loadbalancer").description("Load Balancer\xB7IP ACL \uC870\uD68C\xB7\uAD00\uB9AC").addHelpText("after", loadbalancerAgentWorkflow);
12223
- loadbalancerCommand.addCommand(listCommand16);
12224
- loadbalancerCommand.addCommand(getCommand11);
14167
+ var loadbalancerCommand = new import_commander72.Command("loadbalancer").description("Load Balancer\xB7IP ACL \uC870\uD68C\xB7\uAD00\uB9AC").addHelpText("after", loadbalancerAgentWorkflow);
14168
+ loadbalancerCommand.addCommand(listCommand18);
14169
+ loadbalancerCommand.addCommand(getCommand15);
12225
14170
  loadbalancerCommand.addCommand(ipaclCommand);
12226
14171
  loadbalancerCommand.addCommand(setIpAclCommand2);
12227
14172
  loadbalancerCommand.addCommand(clearIpAclCommand);
12228
- configureLoadBalancerHelp(loadbalancerCommand);
12229
14173
  program.addCommand(loadbalancerCommand);
12230
- var ncrCommand = new import_commander64.Command("ncr").description("NHN Container Registry \uAD00\uB828 \uBA85\uB839").addHelpText("after", ncrAgentWorkflow);
14174
+ var ncrCommand = new import_commander72.Command("ncr").description("NHN Container Registry \uAD00\uB828 \uBA85\uB839").addHelpText("after", ncrAgentWorkflow);
12231
14175
  ncrCommand.addCommand(listCommand7);
12232
14176
  ncrCommand.addCommand(getCommand5);
12233
14177
  ncrCommand.addCommand(imagesCommand2);
12234
14178
  ncrCommand.addCommand(tagsCommand);
12235
14179
  program.addCommand(ncrCommand);
12236
- var nksCommand = new import_commander64.Command("nks").description("NHN Kubernetes Service \uAD00\uB828 \uBA85\uB839").addHelpText("after", nksAgentWorkflow);
14180
+ var nksCommand = new import_commander72.Command("nks").description("NHN Kubernetes Service \uAD00\uB828 \uBA85\uB839").addHelpText("after", nksAgentWorkflow);
12237
14181
  nksCommand.addCommand(supportsCommand);
12238
14182
  nksCommand.addCommand(clusterCommand);
12239
14183
  nksCommand.addCommand(nodegroupCommand);
12240
14184
  nksCommand.addCommand(addonTypeCommand);
12241
14185
  nksCommand.addCommand(addonCommand);
12242
14186
  program.addCommand(nksCommand);
12243
- var ncsCommand = new import_commander64.Command("ncs").description("NHN Container Service \uAD00\uB828 \uBA85\uB839").addHelpText("after", ncsAgentWorkflow);
14187
+ var ncsCommand = new import_commander72.Command("ncs").description("NHN Container Service \uAD00\uB828 \uBA85\uB839").addHelpText("after", ncsAgentWorkflow);
12244
14188
  ncsCommand.addCommand(templateCommand);
12245
14189
  ncsCommand.addCommand(workloadCommand);
12246
14190
  ncsCommand.addCommand(malwareCommand);
12247
14191
  program.addCommand(ncsCommand);
12248
- var apiGatewayCommand = new import_commander64.Command("apigateway").description("NHN API Gateway \uC870\uD68C \uBA85\uB839");
14192
+ var apiGatewayCommand = new import_commander72.Command("apigateway").description("NHN API Gateway \uC870\uD68C \uBA85\uB839");
12249
14193
  apiGatewayCommand.addCommand(serviceCommand);
12250
14194
  apiGatewayCommand.addCommand(resourceCommand);
12251
14195
  apiGatewayCommand.addCommand(stageCommand);
12252
14196
  program.addCommand(apiGatewayCommand);
14197
+ var skmCommand = new import_commander72.Command("skm").description("NHN Secure Key Manager \uD0A4 \uC800\uC7A5\uC18C\xB7\uD0A4 \uAD00\uB9AC\uC640 \uAE30\uBC00 \uB370\uC774\uD130\xB7\uC554\uBCF5\uD638\uD654\xB7\uC11C\uBA85 \uBA85\uB839");
14198
+ skmCommand.addCommand(confirmCommand);
14199
+ skmCommand.addCommand(keystoreCommand);
14200
+ skmCommand.addCommand(keyCommand);
14201
+ skmCommand.addCommand(secretCommand);
14202
+ skmCommand.addCommand(symmetricKeyCommand);
14203
+ skmCommand.addCommand(asymmetricKeyCommand);
14204
+ program.addCommand(skmCommand);
12253
14205
  program.addCommand(skillsCommand);
12254
14206
  program.addCommand(doctorCommand);
12255
14207
  program.addCommand(createCommandsCommand(program));
12256
14208
  configureCommanderExitCodes(program);
14209
+ configureGlobalOptionsHelp(program);
12257
14210
  program.parseAsync().catch((err) => {
12258
- if (err instanceof import_commander64.CommanderError) {
14211
+ if (err instanceof import_commander72.CommanderError) {
12259
14212
  process.exit(err.exitCode);
12260
14213
  }
12261
14214
  const message = sanitizeMultilineForTerminal(