@bifos/nhncloud-cli 0.19.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
@@ -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,14 +463,97 @@ 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
  }
@@ -422,7 +568,7 @@ function configureGlobalOptionsHelp(root) {
422
568
  }
423
569
 
424
570
  // src/version.ts
425
- var CLI_VERSION = true ? "0.19.0" : "0.0.0-dev";
571
+ var CLI_VERSION = true ? "0.20.0" : "0.0.0-dev";
426
572
 
427
573
  // src/commands/configure.ts
428
574
  var import_commander2 = require("commander");
@@ -2523,14 +2669,14 @@ function compareByCodePoint(left, right) {
2523
2669
  return leftPoints.length - rightPoints.length;
2524
2670
  }
2525
2671
  function ensureRegularFile(filePath) {
2526
- let stat3;
2672
+ let stat4;
2527
2673
  try {
2528
- stat3 = (0, import_node_fs2.lstatSync)(filePath);
2674
+ stat4 = (0, import_node_fs2.lstatSync)(filePath);
2529
2675
  } catch (error) {
2530
2676
  const reason = error instanceof Error ? error.message : String(error);
2531
2677
  throw new NhnCloudCliError(`\uC2A4\uD0AC \uD30C\uC77C\uC744 \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${filePath} (${reason})`, EXIT_PARAM_ERROR);
2532
2678
  }
2533
- if (stat3.isSymbolicLink() || !stat3.isFile()) {
2679
+ if (stat4.isSymbolicLink() || !stat4.isFile()) {
2534
2680
  throw new NhnCloudCliError(`\uC2A4\uD0AC \uCF58\uD150\uCE20\uB294 \uC815\uADDC \uD30C\uC77C\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4: ${filePath}`, EXIT_PARAM_ERROR);
2535
2681
  }
2536
2682
  }
@@ -2547,21 +2693,21 @@ function collectReferenceFiles(referencesRoot, currentDir, files) {
2547
2693
  }
2548
2694
  for (const entry of entries) {
2549
2695
  const entryPath = import_node_path4.default.join(currentDir, entry.name);
2550
- let stat3;
2696
+ let stat4;
2551
2697
  try {
2552
- stat3 = (0, import_node_fs2.lstatSync)(entryPath);
2698
+ stat4 = (0, import_node_fs2.lstatSync)(entryPath);
2553
2699
  } catch (error) {
2554
2700
  const reason = error instanceof Error ? error.message : String(error);
2555
2701
  throw new NhnCloudCliError(`\uC2A4\uD0AC \uCF58\uD150\uCE20\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${entryPath} (${reason})`, EXIT_PARAM_ERROR);
2556
2702
  }
2557
- if (stat3.isSymbolicLink()) {
2703
+ if (stat4.isSymbolicLink()) {
2558
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);
2559
2705
  }
2560
- if (stat3.isDirectory()) {
2706
+ if (stat4.isDirectory()) {
2561
2707
  collectReferenceFiles(referencesRoot, entryPath, files);
2562
2708
  continue;
2563
2709
  }
2564
- if (!stat3.isFile()) {
2710
+ if (!stat4.isFile()) {
2565
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);
2566
2712
  }
2567
2713
  files.push(`references/${import_node_path4.default.relative(referencesRoot, entryPath).split(import_node_path4.default.sep).join("/")}`);
@@ -2609,12 +2755,24 @@ function calculateSkillContentDigest(skillRoot) {
2609
2755
  var PACKAGE_NAME3 = "@bifos/nhncloud-cli";
2610
2756
  var SKILL_NAME2 = "nhncloud-cli";
2611
2757
  var DIGEST_HEX_PATTERN = /^[0-9a-f]{64}$/;
2612
- 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 };
2613
2771
  function sourcePath(context) {
2614
2772
  return import_node_path5.default.join(context.packageRoot, "skills", SKILL_NAME2);
2615
2773
  }
2616
- function destinationPath(context) {
2617
- 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);
2618
2776
  }
2619
2777
  function resolveLinkTarget(linkPath, rawTarget) {
2620
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);
@@ -2725,16 +2883,74 @@ async function inspectRepository(targetPath, expected) {
2725
2883
  }
2726
2884
  return { status: "valid", installedVersion: manifest.packageVersion };
2727
2885
  }
2728
- function statusBase(context) {
2886
+ function statusBase(context, agent) {
2729
2887
  return {
2730
2888
  schemaVersion: 1,
2731
- destination: destinationPath(context),
2889
+ destination: destinationPath(context, agent),
2732
2890
  source: sourcePath(context),
2733
2891
  currentVersion: context.currentVersion
2734
2892
  };
2735
2893
  }
2736
- async function inspectSkill(context) {
2737
- 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);
2738
2954
  const destinationStat = await optionalLstat(base.destination);
2739
2955
  if (!destinationStat) {
2740
2956
  return { ...base, status: "missing", managed: false };
@@ -2767,38 +2983,17 @@ async function inspectSkill(context) {
2767
2983
  };
2768
2984
  }
2769
2985
  if (isManagedRepositoryLocation(context, linkTarget)) {
2770
- if (!repositoryName) {
2771
- return { ...base, status: "corrupt", linkTarget, managed: true };
2772
- }
2773
- const repository = await inspectRepository(linkTarget, repositoryName);
2774
- if (repository.status === "modified" || repository.status === "corrupt") {
2775
- return {
2776
- ...base,
2777
- status: repository.status,
2778
- installedVersion: repository.installedVersion ?? repositoryName.version,
2779
- linkTarget,
2780
- managed: true
2781
- };
2782
- }
2783
- if (repository.status === "missing") {
2784
- return {
2785
- ...base,
2786
- status: "broken",
2787
- installedVersion: repositoryName.version,
2788
- linkTarget,
2789
- managed: true
2790
- };
2791
- }
2792
- const currentDigest = calculateSkillContentDigest(base.source);
2793
- const expectedPath = repositoryPath(context, currentDigest);
2794
- const isCurrent = repositoryName.version === context.currentVersion && repositoryName.digest === currentDigest && import_node_path5.default.resolve(linkTarget) === import_node_path5.default.resolve(expectedPath);
2795
- return {
2796
- ...base,
2797
- status: isCurrent ? "current" : "outdated",
2798
- installedVersion: repository.installedVersion,
2986
+ return managedRepositoryStatus(
2987
+ context,
2988
+ base,
2799
2989
  linkTarget,
2800
- managed: true
2801
- };
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;
2802
2997
  }
2803
2998
  const legacyPackage = targetStat.isDirectory() ? await readLegacyPackageMetadata(linkTarget) : void 0;
2804
2999
  if (legacyPackage) {
@@ -2812,6 +3007,14 @@ async function inspectSkill(context) {
2812
3007
  }
2813
3008
  return { ...base, status: "unmanaged", linkTarget, managed: false };
2814
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
+ }
2815
3018
  function utcBackupSuffix() {
2816
3019
  return `${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}-${(0, import_node_crypto3.randomUUID)()}`;
2817
3020
  }
@@ -2908,35 +3111,123 @@ async function prepareRepository(context, force, operations) {
2908
3111
  await (0, import_promises3.rm)(staging, { recursive: true, force: true });
2909
3112
  }
2910
3113
  }
2911
- async function switchActiveLink(context, repository, previousStatus, force, operations) {
2912
- const destination = destinationPath(context);
2913
- const parent = import_node_path5.default.dirname(destination);
2914
- await (0, import_promises3.mkdir)(parent, { recursive: true });
2915
- const temporaryLink = import_node_path5.default.join(parent, `.${SKILL_NAME2}.link-${(0, import_node_crypto3.randomUUID)()}`);
2916
- const backupPaths = [];
2917
- await (0, import_promises3.symlink)(repository, temporaryLink);
2918
- try {
2919
- if (previousStatus.status === "unmanaged") {
2920
- if (!force) {
2921
- 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}`);
2922
- }
2923
- const backup = await backupPath(destination, operations);
2924
- 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") {
2925
3135
  try {
2926
- await operations.rename(temporaryLink, destination);
3136
+ previousRawTarget = await (0, import_promises3.readlink)(destination);
2927
3137
  } catch (error) {
2928
- return await restoreBackup(backup, destination, operations, error);
3138
+ throw managerError(`\uC2A4\uD0AC \uB9C1\uD06C\uB97C \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${destination}`, error);
2929
3139
  }
2930
- return backupPaths;
2931
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()) {
2932
3159
  try {
2933
- await operations.rename(temporaryLink, destination);
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
+ }
2934
3173
  } catch (error) {
2935
- throw managerError(`\uD65C\uC131 \uC2A4\uD0AC \uB9C1\uD06C\uB97C \uC804\uD658\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${destination}`, error);
3174
+ failed.push(entry.destination);
3175
+ if (entry.backup) {
3176
+ preservedBackups.push(entry.backup);
3177
+ }
3178
+ firstError ??= error;
2936
3179
  }
2937
- return backupPaths;
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;
3190
+ try {
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;
3208
+ } catch (error) {
3209
+ failure = error;
3210
+ }
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
+ );
2938
3229
  } finally {
2939
- await (0, import_promises3.rm)(temporaryLink, { force: true });
3230
+ await Promise.all(temporaryLinks.map((link) => (0, import_promises3.rm)(link, { force: true })));
2940
3231
  }
2941
3232
  }
2942
3233
  function installAction(status) {
@@ -2971,23 +3262,26 @@ async function installSkillInternal(context, options = {}, operations = defaultO
2971
3262
  backupPaths: []
2972
3263
  };
2973
3264
  }
2974
- if ((previousStatus.status === "unmanaged" || previousStatus.status === "modified" || previousStatus.status === "corrupt") && !force) {
2975
- 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
+ }
2976
3274
  }
2977
3275
  const prepared = await prepareRepository(context, force, operations);
2978
- const linkBackups = await switchActiveLink(context, prepared.repository, previousStatus, force, operations);
2979
- const status = await inspectSkill(context);
2980
- if (status.status !== "current") {
2981
- throw managerError(`\uC2A4\uD0AC \uC124\uCE58 \uD6C4 \uC0C1\uD0DC\uAC00 current\uAC00 \uC544\uB2D9\uB2C8\uB2E4: ${status.status}`);
2982
- }
3276
+ const switched = await switchActiveLinks(context, prepared.repository, previousStatus, operations);
2983
3277
  return {
2984
3278
  schemaVersion: 1,
2985
3279
  action: installAction(previousStatus.status),
2986
3280
  changed: true,
2987
3281
  previousStatus,
2988
- status,
3282
+ status: switched.status,
2989
3283
  repositoryPath: prepared.repository,
2990
- backupPaths: [...prepared.backupPaths, ...linkBackups]
3284
+ backupPaths: [...prepared.backupPaths, ...switched.backupPaths]
2991
3285
  };
2992
3286
  }
2993
3287
  async function installSkill(context, options = {}, operations = defaultOperations) {
@@ -3000,73 +3294,185 @@ async function installSkill(context, options = {}, operations = defaultOperation
3000
3294
  throw managerError("\uC2A4\uD0AC\uC744 \uC124\uCE58\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.", error);
3001
3295
  }
3002
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
+ }
3003
3308
  async function restoreUninstallCandidate(candidate, destination, operations, originalError) {
3004
- 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) {
3005
3314
  throw managerError(
3006
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}`,
3007
3316
  originalError
3008
3317
  );
3009
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);
3010
3326
  try {
3011
- await operations.rename(candidate, destination);
3012
- } catch (restoreError) {
3013
- throw managerError(
3014
- `\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)}`,
3015
- restoreError
3016
- );
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 });
3017
3337
  }
3018
- 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);
3019
3338
  }
3020
- async function uninstallSkill(context, operations = defaultOperations) {
3021
- const destination = destinationPath(context);
3022
- const status = await inspectSkill(context);
3023
- if (status.status === "missing") {
3024
- 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
+ );
3025
3379
  }
3026
- if (!status.managed || !status.linkTarget) {
3027
- 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
+ });
3028
3395
  }
3396
+ throw uninstallRollbackError(results, originalError, "\uC62E\uAE34 \uACBD\uB85C");
3397
+ }
3398
+ async function moveUninstallCandidate(target, moved, operations) {
3399
+ const { destination } = target;
3029
3400
  const candidate = import_node_path5.default.join(import_node_path5.default.dirname(destination), `.${SKILL_NAME2}.uninstall-${(0, import_node_crypto3.randomUUID)()}`);
3030
3401
  try {
3031
3402
  await operations.rename(destination, candidate);
3032
3403
  } catch (error) {
3033
3404
  if (isNodeError(error, "ENOENT")) {
3034
- 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;
3035
3410
  }
3036
- 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);
3037
3412
  }
3038
- let candidateTarget;
3413
+ let rawTarget;
3414
+ let verificationError;
3039
3415
  try {
3040
3416
  const candidateStat = await (0, import_promises3.lstat)(candidate);
3041
3417
  if (!candidateStat.isSymbolicLink()) {
3042
- return await restoreUninstallCandidate(
3043
- candidate,
3044
- destination,
3045
- operations,
3046
- 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.")
3047
- );
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
+ }
3048
3424
  }
3049
- candidateTarget = resolveLinkTarget(destination, await (0, import_promises3.readlink)(candidate));
3050
3425
  } catch (error) {
3051
- if (error instanceof NhnCloudCliError) {
3052
- throw error;
3426
+ verificationError = error;
3427
+ }
3428
+ if (verificationError !== void 0 || rawTarget === void 0) {
3429
+ if (moved.length === 0) {
3430
+ return await restoreUninstallCandidate(candidate, destination, operations, verificationError);
3053
3431
  }
3054
- return await restoreUninstallCandidate(candidate, destination, operations, error);
3432
+ return await rollbackMovedCandidates([...moved, { destination, candidate }], operations, verificationError);
3055
3433
  }
3056
- if (candidateTarget !== status.linkTarget) {
3057
- return await restoreUninstallCandidate(
3058
- candidate,
3059
- destination,
3060
- operations,
3061
- new Error("\uAC80\uC0AC \uD6C4 \uD65C\uC131 \uC2A4\uD0AC \uB9C1\uD06C \uB300\uC0C1\uC774 \uBCC0\uACBD\uB418\uC5C8\uC2B5\uB2C8\uB2E4.")
3062
- );
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
+ }
3063
3455
  }
3064
- try {
3065
- await (0, import_promises3.rm)(candidate);
3066
- } catch (error) {
3067
- return await restoreUninstallCandidate(candidate, destination, operations, error);
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
+ }
3068
3465
  }
3069
- return "removed";
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
+ };
3070
3476
  }
3071
3477
 
3072
3478
  // src/formatters/table.ts
@@ -3142,9 +3548,16 @@ function outputSkillStatus(opts, status) {
3142
3548
  rows: [
3143
3549
  ["\uC0C1\uD0DC", status.status],
3144
3550
  ["\uD604\uC7AC \uBC84\uC804", status.currentVersion],
3145
- ["\uC124\uCE58 \uBC84\uC804", terminalText(status.installedVersion)],
3146
- ["\uC124\uCE58 \uACBD\uB85C", terminalText(status.destination)],
3147
- ["\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
+ }),
3148
3561
  ["\uBCF5\uAD6C \uBA85\uB839", skillRecoveryCommand(status.status) ?? "\uC870\uCE58 \uC5C6\uC74C"]
3149
3562
  ],
3150
3563
  raw: status,
@@ -3159,7 +3572,10 @@ function outputSkillInstallResult(opts, result) {
3159
3572
  ["\uBCC0\uACBD \uC5EC\uBD80", result.changed ? "\uBCC0\uACBD\uB428" : "\uBCC0\uACBD \uC5C6\uC74C"],
3160
3573
  ["\uC774\uC804 \uC0C1\uD0DC", result.previousStatus.status],
3161
3574
  ["\uD604\uC7AC \uC0C1\uD0DC", result.status.status],
3162
- ["\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
+ ]),
3163
3579
  ["\uAD00\uB9AC \uC800\uC7A5\uC18C", terminalText(result.repositoryPath)],
3164
3580
  [
3165
3581
  "\uBC31\uC5C5 \uACBD\uB85C",
@@ -3175,7 +3591,14 @@ function outputSkillUninstallResult(opts, result) {
3175
3591
  headers: ["\uD56D\uBAA9", "\uAC12"],
3176
3592
  rows: [
3177
3593
  ["\uC791\uC5C5", result.action === "removed" ? "\uD65C\uC131 \uB9C1\uD06C \uC81C\uAC70 \uC644\uB8CC" : "\uD65C\uC131 \uB9C1\uD06C \uC5C6\uC74C"],
3178
- ["\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
+ }),
3179
3602
  ["\uAD00\uB9AC \uC800\uC7A5\uC18C", "\uBCF4\uC874\uB428"]
3180
3603
  ],
3181
3604
  raw: result,
@@ -3198,7 +3621,7 @@ async function showStatus(cmd, dependencies) {
3198
3621
  }
3199
3622
  function createInstallCommand(name, dependencies) {
3200
3623
  return new import_commander3.Command(name).description(
3201
- 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"
3202
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) => {
3203
3626
  const opts = cmd.optsWithGlobals();
3204
3627
  const context = dependencies.createContext();
@@ -3207,96 +3630,280 @@ function createInstallCommand(name, dependencies) {
3207
3630
  });
3208
3631
  }
3209
3632
  function createSkillsCommand(dependencies = defaultDependencies) {
3210
- 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));
3211
- 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) => {
3212
3635
  const opts = cmd.optsWithGlobals();
3213
3636
  const context = dependencies.createContext();
3214
- const previousStatus = await dependencies.inspect(context);
3215
- const action = await dependencies.uninstall(context);
3637
+ const uninstalled = await dependencies.uninstall(context);
3216
3638
  const result = {
3217
3639
  schemaVersion: 1,
3218
- action,
3219
- changed: action === "removed",
3640
+ action: uninstalled.action,
3641
+ changed: uninstalled.action === "removed",
3220
3642
  status: "missing",
3221
- destination: previousStatus.destination,
3222
- repositoryPreserved: true
3643
+ destination: uninstalled.agents.claude.destination,
3644
+ repositoryPreserved: true,
3645
+ agents: uninstalled.agents
3223
3646
  };
3224
3647
  outputSkillUninstallResult(opts, result);
3225
3648
  });
3226
- 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));
3227
3650
  }
3228
3651
  var skillsCommand = createSkillsCommand();
3229
3652
 
3230
3653
  // src/commands/doctor.ts
3231
3654
  var import_commander4 = require("commander");
3232
3655
  var import_chalk3 = __toESM(require("chalk"));
3233
- var import_promises4 = require("fs/promises");
3234
- var import_node_os4 = require("os");
3235
- var import_node_path6 = __toESM(require("path"));
3236
- var SKILL_NAME4 = "nhncloud-cli";
3237
- var CREDENTIALS_PATH2 = import_node_path6.default.join((0, import_node_os4.homedir)(), ".nhncloud", "credentials.json");
3238
- var CONFIG_PATH2 = import_node_path6.default.join((0, import_node_os4.homedir)(), ".nhncloud", "config.json");
3239
- async function readProfiles() {
3240
- 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) {
3241
3675
  try {
3242
- raw = await (0, import_promises4.readFile)(CREDENTIALS_PATH2, "utf-8");
3243
- } catch {
3244
- 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;
3245
3680
  }
3681
+ }
3682
+ async function inspectAgentSkillForDoctor(dependencies, agent) {
3246
3683
  try {
3247
- const parsed = JSON.parse(raw);
3248
- return { profiles: Object.keys(parsed.profiles ?? {}), parseError: false, missing: false };
3249
- } catch {
3250
- 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 };
3251
3689
  }
3252
3690
  }
3253
- 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) {
3254
3701
  try {
3255
- const raw = await (0, import_promises4.readFile)(CONFIG_PATH2, "utf-8");
3256
- const parsed = JSON.parse(raw);
3257
- return parsed.defaultProfile ?? null;
3258
- } catch {
3259
- 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 };
3260
3706
  }
3261
3707
  }
3262
- 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 () => {
3263
- console.log(import_chalk3.default.bold("\n\u{1F50D} nhncloud-cli \uC9C4\uB2E8\n"));
3264
- const creds = await readProfiles();
3265
- const defaultProfile = await readDefaultProfile();
3266
- console.log(import_chalk3.default.bold("\uC790\uACA9\uC99D\uBA85"));
3267
- if (creds.missing) {
3268
- console.log(` ${import_chalk3.default.red("\u274C \uBBF8\uC124\uC815")} \u2014 ${CREDENTIALS_PATH2} \uC5C6\uC74C. nhncloud configure \uB85C \uC124\uC815\uD558\uC138\uC694.`);
3269
- } else if (creds.parseError) {
3270
- 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.`);
3271
- } else if (creds.profiles.length === 0) {
3272
- console.log(` ${import_chalk3.default.yellow("\u26A0 profile \uC5C6\uC74C")} \u2014 nhncloud configure \uB85C \uC124\uC815\uD558\uC138\uC694.`);
3273
- } else {
3274
- console.log(` ${import_chalk3.default.green("\u2713 \uC124\uC815\uB428")} \u2014 profile: ${creds.profiles.join(", ")}`);
3275
- 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 } };
3276
3714
  }
3277
- console.log(import_chalk3.default.bold("\nClaude Code \uC2A4\uD0AC"));
3278
- const skillStatus = await inspectSkill(createSkillManagerContext());
3279
- if (skillStatus.status === "current") {
3280
- console.log(
3281
- ` ${SKILL_NAME4}: ${import_chalk3.default.green(`\u2713 current (${skillStatus.installedVersion ?? skillStatus.currentVersion})`)}`
3282
- );
3283
- } else if (skillStatus.status === "missing") {
3284
- console.log(
3285
- ` ${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 ?? "")}`
3286
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)}`)}`);
3287
3826
  } else {
3288
- console.log(
3289
- ` ${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.`
3290
3829
  );
3291
3830
  }
3292
- console.log();
3293
- if (!creds.missing && !creds.parseError && creds.profiles.length > 0) {
3294
- 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}`)}`);
3295
3841
  } else {
3296
- 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}`);
3297
3843
  }
3298
- console.log();
3299
- });
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();
3300
3907
 
3301
3908
  // src/commands/logncrash/search.ts
3302
3909
  var import_commander5 = require("commander");
@@ -3545,19 +4152,19 @@ var VALID_LEVELS = ["DEBUG", "INFO", "WARN", "ERROR", "FATAL"];
3545
4152
  function resolveBody(opts) {
3546
4153
  if (opts.body !== void 0) return opts.body;
3547
4154
  if (opts.file !== void 0) {
3548
- let stat3;
4155
+ let stat4;
3549
4156
  try {
3550
- stat3 = (0, import_node_fs3.statSync)(opts.file);
4157
+ stat4 = (0, import_node_fs3.statSync)(opts.file);
3551
4158
  } catch (e) {
3552
4159
  const reason = e.code ?? (e instanceof Error ? e.message : String(e));
3553
4160
  throw new NhnCloudCliError(`\uB85C\uADF8 \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${opts.file} (${reason})`, EXIT_PARAM_ERROR);
3554
4161
  }
3555
- if (!stat3.isFile()) {
4162
+ if (!stat4.isFile()) {
3556
4163
  throw new NhnCloudCliError(`\uB85C\uADF8 \uD30C\uC77C\uC774 \uC77C\uBC18 \uD30C\uC77C\uC774 \uC544\uB2D9\uB2C8\uB2E4: ${opts.file}`, EXIT_PARAM_ERROR);
3557
4164
  }
3558
- if (stat3.size > MAX_LOG_BYTES) {
4165
+ if (stat4.size > MAX_LOG_BYTES) {
3559
4166
  throw new NhnCloudCliError(
3560
- `\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).`,
3561
4168
  EXIT_PARAM_ERROR
3562
4169
  );
3563
4170
  }
@@ -3628,22 +4235,22 @@ credentials.json \uC5D0 "appkey": "<appkey>" \uB97C \uCD94\uAC00\uD558\uC138\uC6
3628
4235
 
3629
4236
  // src/commands/logncrash/export.ts
3630
4237
  var import_commander7 = require("commander");
3631
- var import_promises5 = require("fs/promises");
4238
+ var import_promises4 = require("fs/promises");
3632
4239
  var import_node_fs4 = require("fs");
3633
4240
  var import_node_crypto4 = require("crypto");
3634
4241
  var MAX_TOTAL = 1e5;
3635
- var defaultExportFileOps = { appendFile: import_promises5.appendFile, rename: import_promises5.rename };
3636
- function assertWritable(path5, force) {
4242
+ var defaultExportFileOps = { appendFile: import_promises4.appendFile, rename: import_promises4.rename };
4243
+ function assertWritable(path4, force) {
3637
4244
  if (force) return;
3638
4245
  try {
3639
- (0, import_node_fs4.statSync)(path5);
4246
+ (0, import_node_fs4.statSync)(path4);
3640
4247
  } catch (e) {
3641
4248
  if (e.code === "ENOENT") return;
3642
4249
  const reason = e.code ?? (e instanceof Error ? e.message : String(e));
3643
- 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);
3644
4251
  }
3645
4252
  throw new NhnCloudCliError(
3646
- `--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.`,
3647
4254
  EXIT_PARAM_ERROR
3648
4255
  );
3649
4256
  }
@@ -3754,7 +4361,7 @@ function createExportCommand(finalizeOps) {
3754
4361
  } catch (err) {
3755
4362
  if (failureOrigin !== "search" || !(err instanceof LogncrashServerError)) throw err;
3756
4363
  await endAndClose(stream);
3757
- await (0, import_promises5.truncate)(tmp, checkpoint.bytesWritten);
4364
+ await (0, import_promises4.truncate)(tmp, checkpoint.bytesWritten);
3758
4365
  stream = (0, import_node_fs4.createWriteStream)(tmp, { encoding: "utf-8", flags: "a" });
3759
4366
  bytePosition = checkpoint.bytesWritten;
3760
4367
  count = checkpoint.count;
@@ -3781,14 +4388,14 @@ function createExportCommand(finalizeOps) {
3781
4388
  stream.end();
3782
4389
  });
3783
4390
  if (count === 0) {
3784
- await (0, import_promises5.rm)(tmp, { force: true }).catch(() => {
4391
+ await (0, import_promises4.rm)(tmp, { force: true }).catch(() => {
3785
4392
  });
3786
4393
  throw failure;
3787
4394
  }
3788
4395
  const partial = `${opts.output}.partial`;
3789
4396
  try {
3790
- if (format === "json") await (0, import_promises5.appendFile)(tmp, "]\n");
3791
- 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);
3792
4399
  } catch {
3793
4400
  process.stderr.write(
3794
4401
  `\uACBD\uACE0: \uBD80\uBD84 \uACB0\uACFC\uB97C ${partial} \uC5D0 \uB0A8\uAE30\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uC784\uC2DC \uD30C\uC77C: ${tmp}
@@ -3830,7 +4437,7 @@ function createExportCommand(finalizeOps) {
3830
4437
  throw new NhnCloudCliError(message, EXIT_PARAM_ERROR);
3831
4438
  }
3832
4439
  stopSpinner(true, `${count}\uAC74 \uCD94\uCD9C \uC644\uB8CC`);
3833
- await (0, import_promises5.rm)(`${opts.output}.partial`, { force: true }).catch(() => {
4440
+ await (0, import_promises4.rm)(`${opts.output}.partial`, { force: true }).catch(() => {
3834
4441
  });
3835
4442
  if (count >= MAX_TOTAL && (total > MAX_TOTAL || hasUnqueriedWindows)) {
3836
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`;
@@ -4448,7 +5055,7 @@ var binariesCommand = new import_commander15.Command("binaries").description("\u
4448
5055
  // src/commands/deploy/upload.ts
4449
5056
  var import_commander16 = require("commander");
4450
5057
  var import_node_fs5 = require("fs");
4451
- var import_node_path7 = require("path");
5058
+ var import_node_path6 = require("path");
4452
5059
  var MAX_UPLOAD_BYTES = 512 * 1024 * 1024;
4453
5060
  function parsePositiveInt2(value, flag) {
4454
5061
  if (value === void 0) return void 0;
@@ -4468,9 +5075,9 @@ var uploadCommand = new import_commander16.Command("upload").description("\uB85C
4468
5075
  }
4469
5076
  const artifactId = requireCoordinate(opts.artifactId, "--artifact-id");
4470
5077
  const filePath = opts.file;
4471
- let stat3;
5078
+ let stat4;
4472
5079
  try {
4473
- stat3 = (0, import_node_fs5.statSync)(filePath);
5080
+ stat4 = (0, import_node_fs5.statSync)(filePath);
4474
5081
  } catch (e) {
4475
5082
  const reason = e.code ?? (e instanceof Error ? e.message : String(e));
4476
5083
  throw new NhnCloudCliError(
@@ -4478,17 +5085,17 @@ var uploadCommand = new import_commander16.Command("upload").description("\uB85C
4478
5085
  EXIT_PARAM_ERROR
4479
5086
  );
4480
5087
  }
4481
- if (!stat3.isFile()) {
5088
+ if (!stat4.isFile()) {
4482
5089
  throw new NhnCloudCliError(`--file \uC774 \uC77C\uBC18 \uD30C\uC77C\uC774 \uC544\uB2D9\uB2C8\uB2E4: ${filePath}`, EXIT_PARAM_ERROR);
4483
5090
  }
4484
- if (stat3.size > MAX_UPLOAD_BYTES) {
5091
+ if (stat4.size > MAX_UPLOAD_BYTES) {
4485
5092
  throw new NhnCloudCliError(
4486
- `--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.`,
4487
5094
  EXIT_PARAM_ERROR
4488
5095
  );
4489
5096
  }
4490
5097
  const fileBuffer = (0, import_node_fs5.readFileSync)(filePath);
4491
- const fileName = (0, import_node_path7.basename)(filePath);
5098
+ const fileName = (0, import_node_path6.basename)(filePath);
4492
5099
  const { client, profileName } = await createDeployClient(opts.profile);
4493
5100
  const appKey = await resolveDeployAppKey(profileName);
4494
5101
  startSpinner("\uBC14\uC774\uB108\uB9AC \uC5C5\uB85C\uB4DC \uC911...");
@@ -4534,20 +5141,20 @@ function parsePositiveInt3(value, flag) {
4534
5141
  }
4535
5142
  return Number(value);
4536
5143
  }
4537
- function assertWritable2(path5, force) {
5144
+ function assertWritable2(path4, force) {
4538
5145
  if (force) return;
4539
5146
  try {
4540
- (0, import_node_fs6.statSync)(path5);
5147
+ (0, import_node_fs6.statSync)(path4);
4541
5148
  } catch (e) {
4542
5149
  if (e.code === "ENOENT") return;
4543
5150
  const reason = e.code ?? (e instanceof Error ? e.message : String(e));
4544
5151
  throw new NhnCloudCliError(
4545
- `-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})`,
4546
5153
  EXIT_PARAM_ERROR
4547
5154
  );
4548
5155
  }
4549
5156
  throw new NhnCloudCliError(
4550
- `-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.`,
4551
5158
  EXIT_PARAM_ERROR
4552
5159
  );
4553
5160
  }
@@ -4890,8 +5497,8 @@ var InstanceClient = class {
4890
5497
  return this.serverAction(id, { revertResize: null });
4891
5498
  }
4892
5499
  async listFlavors(params = {}) {
4893
- const path5 = params.detail ? "/flavors/detail" : "/flavors";
4894
- const url = `${this.computeEndpoint}${path5}`;
5500
+ const path4 = params.detail ? "/flavors/detail" : "/flavors";
5501
+ const url = `${this.computeEndpoint}${path4}`;
4895
5502
  const searchParams = {};
4896
5503
  if (params.minDisk !== void 0) searchParams["minDisk"] = params.minDisk;
4897
5504
  if (params.minRam !== void 0) searchParams["minRam"] = params.minRam;
@@ -6571,9 +7178,9 @@ var createCommand5 = new import_commander33.Command("create").description("\uC77
6571
7178
  }
6572
7179
  let userDataBase64;
6573
7180
  if (opts.userData !== void 0) {
6574
- let stat3;
7181
+ let stat4;
6575
7182
  try {
6576
- stat3 = (0, import_node_fs7.statSync)(opts.userData);
7183
+ stat4 = (0, import_node_fs7.statSync)(opts.userData);
6577
7184
  } catch (e) {
6578
7185
  const reason = e.code ?? (e instanceof Error ? e.message : String(e));
6579
7186
  throw new NhnCloudCliError(
@@ -6581,15 +7188,15 @@ var createCommand5 = new import_commander33.Command("create").description("\uC77
6581
7188
  EXIT_PARAM_ERROR
6582
7189
  );
6583
7190
  }
6584
- if (!stat3.isFile()) {
7191
+ if (!stat4.isFile()) {
6585
7192
  throw new NhnCloudCliError(
6586
7193
  `--user-data \uAC00 \uC77C\uBC18 \uD30C\uC77C\uC774 \uC544\uB2D9\uB2C8\uB2E4: ${opts.userData}`,
6587
7194
  EXIT_PARAM_ERROR
6588
7195
  );
6589
7196
  }
6590
- if (stat3.size > 49149) {
7197
+ if (stat4.size > 49149) {
6591
7198
  throw new NhnCloudCliError(
6592
- `--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.`,
6593
7200
  EXIT_PARAM_ERROR
6594
7201
  );
6595
7202
  }
@@ -6874,9 +7481,9 @@ var import_node_fs8 = require("fs");
6874
7481
  var import_node_crypto5 = require("crypto");
6875
7482
  var import_chalk12 = __toESM(require("chalk"));
6876
7483
  function resolvePublicKey(value) {
6877
- let stat3;
7484
+ let stat4;
6878
7485
  try {
6879
- stat3 = (0, import_node_fs8.statSync)(value);
7486
+ stat4 = (0, import_node_fs8.statSync)(value);
6880
7487
  } catch (e) {
6881
7488
  const err = e;
6882
7489
  if (err.code && err.code !== "ENOENT") {
@@ -6887,7 +7494,7 @@ function resolvePublicKey(value) {
6887
7494
  }
6888
7495
  return value;
6889
7496
  }
6890
- if (!stat3.isFile()) {
7497
+ if (!stat4.isFile()) {
6891
7498
  throw new NhnCloudCliError(`--public-key \uAC00 \uC77C\uBC18 \uD30C\uC77C\uC774 \uC544\uB2D9\uB2C8\uB2E4: ${value}`, EXIT_PARAM_ERROR);
6892
7499
  }
6893
7500
  return (0, import_node_fs8.readFileSync)(value, "utf-8").trim();
@@ -7194,12 +7801,12 @@ var HarborClient = class {
7194
7801
  * ky.get() 이 Response 를 반환하므로 .json() 과 .headers.get("link") 를 함께 사용한다
7195
7802
  * (체이닝하면 헤더를 못 본다 — 기존 NCR client 의 .json<T>() 체이닝 패턴과 다름).
7196
7803
  */
7197
- async getAllPages(path5) {
7804
+ async getAllPages(path4) {
7198
7805
  const acc = [];
7199
7806
  let page = 1;
7200
7807
  try {
7201
7808
  for (; ; ) {
7202
- 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}`;
7203
7810
  const res = await import_ky13.default.get(url, {
7204
7811
  headers: this.basicAuthHeaders(),
7205
7812
  retry: 0,
@@ -7439,15 +8046,15 @@ function commandPath(command) {
7439
8046
  return names.join(" ");
7440
8047
  }
7441
8048
  function collectCommand(command) {
7442
- const path5 = commandPath(command);
7443
- if (!path5 || path5 === "help") return null;
8049
+ const path4 = commandPath(command);
8050
+ if (!path4 || path4 === "help") return null;
7444
8051
  return {
7445
- path: path5,
8052
+ path: path4,
7446
8053
  description: command.description() || command.summary() || "",
7447
8054
  arguments: command.registeredArguments.map((arg) => arg.name()),
7448
8055
  options: command.options.filter((option) => !option.hidden).map((option) => option.flags),
7449
8056
  subcommands: command.commands.map((subcommand) => subcommand.name()).filter((name) => name !== "help"),
7450
- ...path5 === "commands" ? { metadata: true } : {}
8057
+ ...path4 === "commands" ? { metadata: true } : {}
7451
8058
  };
7452
8059
  }
7453
8060
  function collectCommandCatalog(program2) {
@@ -7486,7 +8093,7 @@ function createCommandsCommand(program2) {
7486
8093
  var import_commander48 = require("commander");
7487
8094
 
7488
8095
  // src/commands/nks/helpers.ts
7489
- var import_promises6 = require("fs/promises");
8096
+ var import_promises5 = require("fs/promises");
7490
8097
 
7491
8098
  // src/services/nks/client.ts
7492
8099
  var import_ky14 = __toESM(require("ky"));
@@ -7936,15 +8543,15 @@ var NksClient = class {
7936
8543
  throw toNhnCloudCliError(err);
7937
8544
  }
7938
8545
  }
7939
- async getFlatNamedResource(path5, errorMessage) {
7940
- const raw = await this.getJson(path5);
8546
+ async getFlatNamedResource(path4, errorMessage) {
8547
+ const raw = await this.getJson(path4);
7941
8548
  if (!isNksNamedResource(raw)) {
7942
8549
  throw new NhnCloudCliError(errorMessage, EXIT_API_ERROR);
7943
8550
  }
7944
8551
  return raw;
7945
8552
  }
7946
- async getJson(path5, searchParams) {
7947
- const url = `${this.nksEndpoint}${path5}`;
8553
+ async getJson(path4, searchParams) {
8554
+ const url = `${this.nksEndpoint}${path4}`;
7948
8555
  try {
7949
8556
  return await import_ky14.default.get(url, {
7950
8557
  headers: this.authHeaders(),
@@ -7956,15 +8563,15 @@ var NksClient = class {
7956
8563
  throw toNhnCloudCliError(err);
7957
8564
  }
7958
8565
  }
7959
- async requestUuid(method, path5, payload, errorMessage) {
7960
- const raw = await this.requestJson(method, path5, payload);
8566
+ async requestUuid(method, path4, payload, errorMessage) {
8567
+ const raw = await this.requestJson(method, path4, payload);
7961
8568
  if (!isNksUuidResponse(raw)) {
7962
8569
  throw new NhnCloudCliError(errorMessage, EXIT_API_ERROR);
7963
8570
  }
7964
8571
  return raw;
7965
8572
  }
7966
- async requestNoBody(method, path5, payload) {
7967
- const url = `${this.nksEndpoint}${path5}`;
8573
+ async requestNoBody(method, path4, payload) {
8574
+ const url = `${this.nksEndpoint}${path4}`;
7968
8575
  try {
7969
8576
  if (method === "delete") {
7970
8577
  await import_ky14.default.delete(url, { headers: this.authHeaders(), retry: 0, timeout: DEFAULT_TIMEOUT_MS });
@@ -7975,8 +8582,8 @@ var NksClient = class {
7975
8582
  throw toNhnCloudCliError(err);
7976
8583
  }
7977
8584
  }
7978
- async requestJson(method, path5, payload) {
7979
- const url = `${this.nksEndpoint}${path5}`;
8585
+ async requestJson(method, path4, payload) {
8586
+ const url = `${this.nksEndpoint}${path4}`;
7980
8587
  try {
7981
8588
  const options = { headers: this.authHeaders(), json: payload, retry: 0, timeout: DEFAULT_TIMEOUT_MS };
7982
8589
  return method === "post" ? await import_ky14.default.post(url, options).json() : await import_ky14.default.patch(url, options).json();
@@ -8015,13 +8622,13 @@ async function resolveClusterUuid(client, cluster) {
8015
8622
  }
8016
8623
  return uuid;
8017
8624
  }
8018
- async function readJsonFile(path5) {
8625
+ async function readJsonFile(path4) {
8019
8626
  let raw;
8020
8627
  try {
8021
- raw = await (0, import_promises6.readFile)(path5, "utf-8");
8628
+ raw = await (0, import_promises5.readFile)(path4, "utf-8");
8022
8629
  } catch (err) {
8023
8630
  const message = err instanceof Error ? err.message : String(err);
8024
- 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);
8025
8632
  }
8026
8633
  try {
8027
8634
  const parsed = JSON.parse(raw);
@@ -8031,15 +8638,15 @@ async function readJsonFile(path5) {
8031
8638
  return parsed;
8032
8639
  } catch (err) {
8033
8640
  const message = err instanceof Error ? err.message : String(err);
8034
- 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);
8035
8642
  }
8036
8643
  }
8037
- async function readTextFile(path5) {
8644
+ async function readTextFile(path4) {
8038
8645
  try {
8039
- return await (0, import_promises6.readFile)(path5, "utf-8");
8646
+ return await (0, import_promises5.readFile)(path4, "utf-8");
8040
8647
  } catch (err) {
8041
8648
  const message = err instanceof Error ? err.message : String(err);
8042
- 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);
8043
8650
  }
8044
8651
  }
8045
8652
  function parsePositiveInteger(value, optionName) {
@@ -8080,7 +8687,7 @@ var supportsCommand = new import_commander48.Command("supports").description("NK
8080
8687
  });
8081
8688
 
8082
8689
  // src/commands/nks/cluster.ts
8083
- var import_promises7 = require("fs/promises");
8690
+ var import_promises6 = require("fs/promises");
8084
8691
  var import_commander49 = require("commander");
8085
8692
  var import_chalk14 = __toESM(require("chalk"));
8086
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) => {
@@ -8171,18 +8778,18 @@ async function confirmDangerousAction(message, yes) {
8171
8778
  }
8172
8779
  return ok;
8173
8780
  }
8174
- async function writeKubeconfigFile(path5, contents, force) {
8781
+ async function writeKubeconfigFile(path4, contents, force) {
8175
8782
  try {
8176
- await (0, import_promises7.writeFile)(path5, contents, {
8783
+ await (0, import_promises6.writeFile)(path4, contents, {
8177
8784
  encoding: "utf-8",
8178
8785
  mode: 384,
8179
8786
  flag: force ? "w" : "wx"
8180
8787
  });
8181
- await (0, import_promises7.chmod)(path5, 384);
8788
+ await (0, import_promises6.chmod)(path4, 384);
8182
8789
  } catch (err) {
8183
8790
  if (err instanceof Error && "code" in err && err.code === "EEXIST") {
8184
8791
  throw new NhnCloudCliError(
8185
- `\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.`,
8186
8793
  EXIT_PARAM_ERROR
8187
8794
  );
8188
8795
  }
@@ -9001,9 +9608,9 @@ async function resolveNcsClient(opts) {
9001
9608
  return { client: new NcsClient(accessToken, region, appKey), profileName };
9002
9609
  }
9003
9610
  function readJsonPayload(filePath) {
9004
- let stat3;
9611
+ let stat4;
9005
9612
  try {
9006
- stat3 = (0, import_node_fs9.statSync)(filePath);
9613
+ stat4 = (0, import_node_fs9.statSync)(filePath);
9007
9614
  } catch (err) {
9008
9615
  const reason = err.code ?? (err instanceof Error ? err.message : String(err));
9009
9616
  throw new NhnCloudCliError(
@@ -9011,12 +9618,12 @@ function readJsonPayload(filePath) {
9011
9618
  EXIT_PARAM_ERROR
9012
9619
  );
9013
9620
  }
9014
- if (!stat3.isFile()) {
9621
+ if (!stat4.isFile()) {
9015
9622
  throw new NhnCloudCliError(`--file \uC774 \uC77C\uBC18 \uD30C\uC77C\uC774 \uC544\uB2D9\uB2C8\uB2E4: ${filePath}`, EXIT_PARAM_ERROR);
9016
9623
  }
9017
- if (stat3.size > MAX_JSON_INPUT_BYTES) {
9624
+ if (stat4.size > MAX_JSON_INPUT_BYTES) {
9018
9625
  throw new NhnCloudCliError(
9019
- `--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.`,
9020
9627
  EXIT_PARAM_ERROR
9021
9628
  );
9022
9629
  }
@@ -9747,7 +10354,7 @@ var malwareCommand = new import_commander54.Command("malware").description("NCS
9747
10354
  var import_commander55 = require("commander");
9748
10355
 
9749
10356
  // src/commands/apigateway/helpers.ts
9750
- var import_promises8 = require("fs/promises");
10357
+ var import_promises7 = require("fs/promises");
9751
10358
 
9752
10359
  // src/services/apigateway/client.ts
9753
10360
  var import_ky15 = __toESM(require("ky"));
@@ -10358,21 +10965,21 @@ function requireYes2(yes, operation) {
10358
10965
  }
10359
10966
  return true;
10360
10967
  }
10361
- async function readPluginConfigFile(path5) {
10968
+ async function readPluginConfigFile(path4) {
10362
10969
  let fileStat;
10363
10970
  try {
10364
- fileStat = await (0, import_promises8.stat)(path5);
10971
+ fileStat = await (0, import_promises7.stat)(path4);
10365
10972
  } catch (error) {
10366
10973
  const reason = fileErrorReason(error);
10367
10974
  throw new NhnCloudCliError(
10368
- `\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)}).`,
10369
10976
  EXIT_PARAM_ERROR
10370
10977
  );
10371
10978
  }
10372
10979
  if (!fileStat.isFile()) {
10373
10980
  const reason = fileStat.isDirectory() ? "EISDIR" : "EINVAL";
10374
10981
  throw new NhnCloudCliError(
10375
- `\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}).`,
10376
10983
  EXIT_PARAM_ERROR
10377
10984
  );
10378
10985
  }
@@ -10384,11 +10991,11 @@ async function readPluginConfigFile(path5) {
10384
10991
  }
10385
10992
  let raw;
10386
10993
  try {
10387
- raw = await (0, import_promises8.readFile)(path5, "utf-8");
10994
+ raw = await (0, import_promises7.readFile)(path4, "utf-8");
10388
10995
  } catch (error) {
10389
10996
  const reason = fileErrorReason(error);
10390
10997
  throw new NhnCloudCliError(
10391
- `\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)}).`,
10392
10999
  EXIT_PARAM_ERROR
10393
11000
  );
10394
11001
  }
@@ -10399,7 +11006,7 @@ async function readPluginConfigFile(path5) {
10399
11006
  error instanceof Error ? error.message : String(error)
10400
11007
  );
10401
11008
  throw new NhnCloudCliError(
10402
- `\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}).`,
10403
11010
  EXIT_PARAM_ERROR
10404
11011
  );
10405
11012
  }
@@ -10843,7 +11450,7 @@ var setMethodPluginCommand = addApiGatewayOptions(
10843
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);
10844
11451
 
10845
11452
  // src/commands/apigateway/stage.ts
10846
- var import_promises9 = require("fs/promises");
11453
+ var import_promises8 = require("fs/promises");
10847
11454
  var import_commander58 = require("commander");
10848
11455
 
10849
11456
  // src/commands/apigateway/deploy.ts
@@ -11050,9 +11657,9 @@ function fileErrorReason2(error) {
11050
11657
  }
11051
11658
  return error instanceof Error ? error.message : String(error);
11052
11659
  }
11053
- async function writeStageSwaggerFile(path5, swagger, force) {
11660
+ async function writeStageSwaggerFile(path4, swagger, force) {
11054
11661
  try {
11055
- 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", {
11056
11663
  encoding: "utf-8",
11057
11664
  flag: force ? "w" : "wx"
11058
11665
  });
@@ -11060,7 +11667,7 @@ async function writeStageSwaggerFile(path5, swagger, force) {
11060
11667
  const reason = fileErrorReason2(error);
11061
11668
  const guidance = reason === "EEXIST" ? " \uB36E\uC5B4\uC4F0\uB824\uBA74 --force \uB97C \uC9C0\uC815\uD558\uC138\uC694." : "";
11062
11669
  throw new NhnCloudCliError(
11063
- `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}`,
11064
11671
  EXIT_PARAM_ERROR
11065
11672
  );
11066
11673
  }
@@ -11394,10 +12001,10 @@ var SkmClient = class {
11394
12001
  * 요청을 보내고 봉투 형태인지만 확인해 반환한다. 성공 판정은 호출부가 한다.
11395
12002
  * 쓰기 요청도 중복 실행을 막으려고 재시도하지 않는다.
11396
12003
  */
11397
- async send(method, path5, options) {
12004
+ async send(method, path4, options) {
11398
12005
  let response;
11399
12006
  try {
11400
- response = await import_ky16.default[method](`${this.baseUrl}${path5}`, {
12007
+ response = await import_ky16.default[method](`${this.baseUrl}${path4}`, {
11401
12008
  headers: this.headers,
11402
12009
  ...options,
11403
12010
  retry: 0,
@@ -11415,8 +12022,8 @@ var SkmClient = class {
11415
12022
  * 요청을 보내고 봉투를 벗긴 뒤 body 또는 body[field] 를 가드로 검사해 반환한다.
11416
12023
  * field 가 null 이면 body 전체를 검사한다.
11417
12024
  */
11418
- async request(method, path5, field, guard, options = {}) {
11419
- const body = unwrap(await this.send(method, path5, options));
12025
+ async request(method, path4, field, guard, options = {}) {
12026
+ const body = unwrap(await this.send(method, path4, options));
11420
12027
  const value = field === null ? body : isRecord4(body) ? body[field] : void 0;
11421
12028
  if (!guard(value)) {
11422
12029
  throw new NhnCloudCliError(`Secure Key Manager \uC751\uB2F5 \uD615\uC2DD \uC624\uB958: ${field ?? "body"}`, EXIT_API_ERROR);
@@ -11427,8 +12034,8 @@ var SkmClient = class {
11427
12034
  * body 가 null 이거나 없는 응답(키 저장소 수정·삭제)은 검사할 필드가 없어 헤더만 본다 (ADR-040).
11428
12035
  * isSuccessful 이 false 면 unwrapHeader 가 NhnEnvelopeError 를 던진다.
11429
12036
  */
11430
- async requestHeaderOnly(method, path5, options = {}) {
11431
- unwrapHeader(await this.send(method, path5, options));
12037
+ async requestHeaderOnly(method, path4, options = {}) {
12038
+ unwrapHeader(await this.send(method, path4, options));
11432
12039
  }
11433
12040
  keyPath(prefix, keyId, action) {
11434
12041
  return `/${prefix}/${encodeURIComponent(keyId)}/${action}`;
@@ -11476,12 +12083,12 @@ var SkmClient = class {
11476
12083
  return this.request("get", `/keystores/${keyStoreId}/keys/${encodeURIComponent(keyId)}`, null, isSkmKey);
11477
12084
  }
11478
12085
  listAuths(keyStoreId, type) {
11479
- const { path: path5, field } = AUTH_PATHS[type];
11480
- return this.request("get", `/keystores/${keyStoreId}/${path5}`, field, arrayOf(isString));
12086
+ const { path: path4, field } = AUTH_PATHS[type];
12087
+ return this.request("get", `/keystores/${keyStoreId}/${path4}`, field, arrayOf(isString));
11481
12088
  }
11482
12089
  getAuth(keyStoreId, type, value) {
11483
- const { path: path5, field } = AUTH_PATHS[type];
11484
- return this.request("get", `/keystores/${keyStoreId}/${path5}`, field, arrayOf(isSkmAuthDetail), {
12090
+ const { path: path4, field } = AUTH_PATHS[type];
12091
+ return this.request("get", `/keystores/${keyStoreId}/${path4}`, field, arrayOf(isSkmAuthDetail), {
11485
12092
  searchParams: { value }
11486
12093
  });
11487
12094
  }
@@ -11845,17 +12452,17 @@ function assertWithinLimit(size, spec) {
11845
12452
  }
11846
12453
  }
11847
12454
  function readInputFile(file, spec) {
11848
- let stat3;
12455
+ let stat4;
11849
12456
  try {
11850
- stat3 = (0, import_node_fs10.statSync)(file);
12457
+ stat4 = (0, import_node_fs10.statSync)(file);
11851
12458
  } catch (e) {
11852
12459
  const reason = e.code ?? (e instanceof Error ? e.message : String(e));
11853
12460
  throw new NhnCloudCliError(`${spec.label} \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${file} (${reason})`, EXIT_PARAM_ERROR);
11854
12461
  }
11855
- if (!stat3.isFile()) {
12462
+ if (!stat4.isFile()) {
11856
12463
  throw new NhnCloudCliError(`${spec.label} \uD30C\uC77C\uC774 \uC77C\uBC18 \uD30C\uC77C\uC774 \uC544\uB2D9\uB2C8\uB2E4: ${file}`, EXIT_PARAM_ERROR);
11857
12464
  }
11858
- assertWithinLimit(stat3.size, spec);
12465
+ assertWithinLimit(stat4.size, spec);
11859
12466
  return (0, import_node_fs10.readFileSync)(file);
11860
12467
  }
11861
12468
  function readStdin(stdin, spec) {
@@ -12747,8 +13354,8 @@ var LoadBalancerClient = class {
12747
13354
  }
12748
13355
  return raw;
12749
13356
  }
12750
- async getJson(path5, searchParams) {
12751
- const url = `${this.endpoint}${path5}`;
13357
+ async getJson(path4, searchParams) {
13358
+ const url = `${this.endpoint}${path4}`;
12752
13359
  try {
12753
13360
  return await import_ky17.default.get(url, {
12754
13361
  headers: this.authHeaders(),
@@ -12760,8 +13367,8 @@ var LoadBalancerClient = class {
12760
13367
  throw toNhnCloudCliError(error);
12761
13368
  }
12762
13369
  }
12763
- async postJson(path5, json) {
12764
- const url = `${this.endpoint}${path5}`;
13370
+ async postJson(path4, json) {
13371
+ const url = `${this.endpoint}${path4}`;
12765
13372
  try {
12766
13373
  return await import_ky17.default.post(url, {
12767
13374
  headers: this.authHeaders(),
@@ -12773,8 +13380,8 @@ var LoadBalancerClient = class {
12773
13380
  throw toNhnCloudCliError(error);
12774
13381
  }
12775
13382
  }
12776
- async putJson(path5, json) {
12777
- const url = `${this.endpoint}${path5}`;
13383
+ async putJson(path4, json) {
13384
+ const url = `${this.endpoint}${path4}`;
12778
13385
  try {
12779
13386
  return await import_ky17.default.put(url, {
12780
13387
  headers: this.authHeaders(),
@@ -12786,8 +13393,8 @@ var LoadBalancerClient = class {
12786
13393
  throw toNhnCloudCliError(error);
12787
13394
  }
12788
13395
  }
12789
- async deleteNoContent(path5) {
12790
- const url = `${this.endpoint}${path5}`;
13396
+ async deleteNoContent(path4) {
13397
+ const url = `${this.endpoint}${path4}`;
12791
13398
  try {
12792
13399
  await import_ky17.default.delete(url, {
12793
13400
  headers: this.authHeaders(),