mcp-aws-manager 0.3.7 → 0.3.8

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/README.md CHANGED
@@ -91,7 +91,7 @@ If AWS auth is not available, use manual fallback:
91
91
  mcp-aws-manager discover --manual-server-list ./servers.csv --pem-paths C:\keys\prod.pem --no-progress
92
92
  ```
93
93
 
94
- GUI report is generated by default (`./aws-inventory.html`):
94
+ GUI report is generated by default (auto path: workspace/home `aws-inventory.html`):
95
95
 
96
96
  ```bash
97
97
  mcp-aws-manager discover --profiles default --no-progress
@@ -234,7 +234,7 @@ aws sts get-caller-identity --profile default
234
234
  - `--ssh-user <name>`
235
235
  - `--ssh-port <port>`
236
236
  - `--ssh-connect-timeout <seconds>`
237
- - `--html-out <path>` (default: `./aws-inventory.html`)
237
+ - `--html-out <path>` (default: auto path, workspace/home `aws-inventory.html`)
238
238
  - `--open-html` (force open)
239
239
  - `--no-open-html` (disable auto-open)
240
240
  - `--auto-sso-login` / `--no-auto-sso-login`
package/README_KO.md CHANGED
@@ -19,7 +19,7 @@ mcp-aws-manager discover --profiles default --no-progress
19
19
  - SSM 상태 확인: managed/online
20
20
  - 런타임 스냅샷(선택), SSM 완화(선택)
21
21
  - AWS 인증이 안 될 때 수동 모드: JSON/CSV 서버 목록 + PEM SSH
22
- - GUI 리포트 기본 생성: `./aws-inventory.html` (검색/필터/CSV 다운로드 버튼 포함)
22
+ - GUI 리포트 기본 생성: 워크스페이스/홈 자동 경로 `aws-inventory.html` (검색/필터/CSV 다운로드 버튼 포함)
23
23
  - 사람이 개입해야 할 상황을 `ACTION_REQUIRED`로 표준화
24
24
 
25
25
  ## 바이너리
@@ -73,7 +73,7 @@ mcp-aws-manager discover --manual-server-list ./servers.csv --pem-paths C:\keys\
73
73
  mcp-aws-manager discover --profiles default --no-progress
74
74
  ```
75
75
 
76
- - 기본 경로: `./aws-inventory.html`
76
+ - 기본 경로: 워크스페이스/홈 자동 선택(`aws-inventory.html`)
77
77
  - `--html-out <path>`: 리포트 경로 변경
78
78
  - `--open-html`: 생성 후 브라우저 오픈 강제
79
79
  - `--no-open-html`: 자동 오픈 비활성화
@@ -207,6 +207,64 @@ function expandHome(input) {
207
207
  return value;
208
208
  }
209
209
 
210
+ function isWritableDirectory(dirPath) {
211
+ try {
212
+ const resolved = path.resolve(String(dirPath || ""));
213
+ fs.mkdirSync(resolved, { recursive: true });
214
+ fs.accessSync(resolved, fs.constants.W_OK);
215
+ return true;
216
+ } catch (_error) {
217
+ return false;
218
+ }
219
+ }
220
+
221
+ function isLikelyProtectedInstallDir(dirPath) {
222
+ const resolved = path.resolve(String(dirPath || ""));
223
+ if (!resolved) return false;
224
+ const normalized = resolved.replace(/\//g, "\\").toLowerCase();
225
+ if (process.platform === "win32") {
226
+ if (normalized.includes("\\program files\\")) return true;
227
+ if (/^[a-z]:\\windows(\\|$)/.test(normalized)) return true;
228
+ } else {
229
+ if (normalized === "\\usr" || normalized.startsWith("\\usr\\")) return true;
230
+ if (normalized === "\\opt" || normalized.startsWith("\\opt\\")) return true;
231
+ if (normalized === "\\system" || normalized.startsWith("\\system\\")) return true;
232
+ }
233
+ return false;
234
+ }
235
+
236
+ function resolveDefaultHtmlOutPath() {
237
+ const explicit = envText("MCP_AWS_WORKSPACE_DIR");
238
+ if (explicit) {
239
+ const dir = path.resolve(expandHome(explicit));
240
+ if (isWritableDirectory(dir)) {
241
+ return path.join(dir, DEFAULT_HTML_REPORT_NAME);
242
+ }
243
+ }
244
+
245
+ const candidates = [
246
+ envText("PWD"),
247
+ envText("INIT_CWD"),
248
+ envText("WORKSPACE_DIR"),
249
+ envText("PROJECT_ROOT")
250
+ ];
251
+ for (const raw of candidates) {
252
+ if (!raw) continue;
253
+ const dir = path.resolve(expandHome(raw));
254
+ if (isLikelyProtectedInstallDir(dir)) continue;
255
+ if (isWritableDirectory(dir)) {
256
+ return path.join(dir, DEFAULT_HTML_REPORT_NAME);
257
+ }
258
+ }
259
+
260
+ const home = os.homedir();
261
+ if (isWritableDirectory(home)) {
262
+ return path.join(home, DEFAULT_HTML_REPORT_NAME);
263
+ }
264
+
265
+ return path.resolve(process.cwd(), DEFAULT_HTML_REPORT_NAME);
266
+ }
267
+
210
268
  function usageText() {
211
269
  const clientList = Array.from(SUPPORTED_CLIENTS).join(",");
212
270
  return [
@@ -267,7 +325,7 @@ function usageText() {
267
325
  " --ssh-user <name> (default: ec2-user)",
268
326
  " --ssh-port <port> (default: 22)",
269
327
  " --ssh-connect-timeout <seconds> (default: 8)",
270
- " --html-out <path> (default: ./aws-inventory.html)",
328
+ " --html-out <path> (default: auto -> workspace/home aws-inventory.html)",
271
329
  " --open-html (force opening HTML report after write)",
272
330
  " --no-open-html (disable auto-open of HTML report)",
273
331
  " --auto-sso-login / --no-auto-sso-login",
@@ -287,7 +345,7 @@ function usageText() {
287
345
  " MCP_AWS_SNAPSHOT_MAX_KB, MCP_AWS_AUTO_SSO_LOGIN",
288
346
  " MCP_AWS_MANUAL_SERVER_LIST, MCP_AWS_PEM_PATHS, MCP_AWS_SSH_USER, MCP_AWS_SSH_PORT",
289
347
  " MCP_AWS_SSH_CONNECT_TIMEOUT",
290
- " MCP_AWS_HTML_OUT, MCP_AWS_OPEN_HTML, MCP_AWS_HEADLESS",
348
+ " MCP_AWS_HTML_OUT, MCP_AWS_OPEN_HTML, MCP_AWS_HEADLESS, MCP_AWS_WORKSPACE_DIR",
291
349
  " MCP_AWS_FORMAT, MCP_AWS_OUT, MCP_AWS_NO_PROGRESS",
292
350
  ""
293
351
  ].join("\n");
@@ -566,7 +624,7 @@ function parseDiscoverArgs(argv) {
566
624
  sshConnectTimeoutSec: Math.round(toPosNum(options.sshConnectTimeoutSec || envText("MCP_AWS_SSH_CONNECT_TIMEOUT") || "8", "--ssh-connect-timeout", 8)),
567
625
  htmlOutPath: options.htmlOutPath
568
626
  ? path.resolve(expandHome(options.htmlOutPath))
569
- : (envText("MCP_AWS_HTML_OUT") ? path.resolve(expandHome(envText("MCP_AWS_HTML_OUT"))) : path.resolve(process.cwd(), DEFAULT_HTML_REPORT_NAME)),
627
+ : (envText("MCP_AWS_HTML_OUT") ? path.resolve(expandHome(envText("MCP_AWS_HTML_OUT"))) : resolveDefaultHtmlOutPath()),
570
628
  openHtml: options.openHtml != null ? options.openHtml : (envOpenHtml != null ? envBool("MCP_AWS_OPEN_HTML") : null),
571
629
  autoSsoLogin: options.autoSsoLogin != null ? options.autoSsoLogin : (envText("MCP_AWS_AUTO_SSO_LOGIN") != null ? envBool("MCP_AWS_AUTO_SSO_LOGIN") : true),
572
630
  format,
@@ -2939,9 +2997,25 @@ function renderHtmlReport(records) {
2939
2997
  function writeHtmlReport(config, records) {
2940
2998
  if (!config.htmlOutPath) return null;
2941
2999
  const html = renderHtmlReport(records);
2942
- fs.writeFileSync(config.htmlOutPath, html, "utf8");
2943
- eprint(`Wrote HTML report to ${config.htmlOutPath}`);
2944
- return config.htmlOutPath;
3000
+ try {
3001
+ fs.writeFileSync(config.htmlOutPath, html, "utf8");
3002
+ eprint(`Wrote HTML report to ${config.htmlOutPath}`);
3003
+ return config.htmlOutPath;
3004
+ } catch (error) {
3005
+ const code = String(error && error.code ? error.code : "");
3006
+ if (!["EACCES", "EPERM", "EROFS"].includes(code)) {
3007
+ throw error;
3008
+ }
3009
+
3010
+ const fallbackPath = path.join(os.homedir(), path.basename(config.htmlOutPath) || DEFAULT_HTML_REPORT_NAME);
3011
+ if (path.resolve(fallbackPath) === path.resolve(config.htmlOutPath)) {
3012
+ throw error;
3013
+ }
3014
+ fs.mkdirSync(path.dirname(fallbackPath), { recursive: true });
3015
+ fs.writeFileSync(fallbackPath, html, "utf8");
3016
+ eprint(`WARNING: failed to write HTML report to ${config.htmlOutPath} (${code}); wrote fallback to ${fallbackPath}`);
3017
+ return fallbackPath;
3018
+ }
2945
3019
  }
2946
3020
 
2947
3021
  function tryOpenFile(targetPath) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-aws-manager",
3
- "version": "0.3.7",
3
+ "version": "0.3.8",
4
4
  "description": "AWS operations CLI and MCP server (SSM-only) for EC2/Lambda inventory, remediation, and runtime snapshots",
5
5
  "license": "MIT",
6
6
  "publishConfig": {