@airmcp-dev/cli 0.1.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.
Files changed (122) hide show
  1. package/LICENSE +17 -0
  2. package/dist/commands/add.d.ts +3 -0
  3. package/dist/commands/add.d.ts.map +1 -0
  4. package/dist/commands/add.js +192 -0
  5. package/dist/commands/add.js.map +1 -0
  6. package/dist/commands/check.d.ts +3 -0
  7. package/dist/commands/check.d.ts.map +1 -0
  8. package/dist/commands/check.js +218 -0
  9. package/dist/commands/check.js.map +1 -0
  10. package/dist/commands/connect.d.ts +3 -0
  11. package/dist/commands/connect.d.ts.map +1 -0
  12. package/dist/commands/connect.js +135 -0
  13. package/dist/commands/connect.js.map +1 -0
  14. package/dist/commands/create.d.ts +3 -0
  15. package/dist/commands/create.d.ts.map +1 -0
  16. package/dist/commands/create.js +150 -0
  17. package/dist/commands/create.js.map +1 -0
  18. package/dist/commands/dev.d.ts +3 -0
  19. package/dist/commands/dev.d.ts.map +1 -0
  20. package/dist/commands/dev.js +184 -0
  21. package/dist/commands/dev.js.map +1 -0
  22. package/dist/commands/disconnect.d.ts +3 -0
  23. package/dist/commands/disconnect.d.ts.map +1 -0
  24. package/dist/commands/disconnect.js +69 -0
  25. package/dist/commands/disconnect.js.map +1 -0
  26. package/dist/commands/index.d.ts +13 -0
  27. package/dist/commands/index.d.ts.map +1 -0
  28. package/dist/commands/index.js +16 -0
  29. package/dist/commands/index.js.map +1 -0
  30. package/dist/commands/inspect.d.ts +3 -0
  31. package/dist/commands/inspect.d.ts.map +1 -0
  32. package/dist/commands/inspect.js +159 -0
  33. package/dist/commands/inspect.js.map +1 -0
  34. package/dist/commands/license.d.ts +3 -0
  35. package/dist/commands/license.d.ts.map +1 -0
  36. package/dist/commands/license.js +113 -0
  37. package/dist/commands/license.js.map +1 -0
  38. package/dist/commands/list.d.ts +3 -0
  39. package/dist/commands/list.d.ts.map +1 -0
  40. package/dist/commands/list.js +101 -0
  41. package/dist/commands/list.js.map +1 -0
  42. package/dist/commands/start.d.ts +3 -0
  43. package/dist/commands/start.d.ts.map +1 -0
  44. package/dist/commands/start.js +130 -0
  45. package/dist/commands/start.js.map +1 -0
  46. package/dist/commands/status.d.ts +3 -0
  47. package/dist/commands/status.d.ts.map +1 -0
  48. package/dist/commands/status.js +83 -0
  49. package/dist/commands/status.js.map +1 -0
  50. package/dist/commands/stop.d.ts +3 -0
  51. package/dist/commands/stop.d.ts.map +1 -0
  52. package/dist/commands/stop.js +56 -0
  53. package/dist/commands/stop.js.map +1 -0
  54. package/dist/index.d.ts +3 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +46 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/templates/agent/.ai/context.md +36 -0
  59. package/dist/templates/agent/.vscode/air.code-snippets +178 -0
  60. package/dist/templates/agent/package.json +17 -0
  61. package/dist/templates/agent/src/index.ts +98 -0
  62. package/dist/templates/agent/tsconfig.json +14 -0
  63. package/dist/templates/agent-ko/.ai/context.md +36 -0
  64. package/dist/templates/agent-ko/.vscode/air.code-snippets +178 -0
  65. package/dist/templates/agent-ko/package.json +17 -0
  66. package/dist/templates/agent-ko/src/index.ts +92 -0
  67. package/dist/templates/agent-ko/tsconfig.json +14 -0
  68. package/dist/templates/api/.ai/context.md +36 -0
  69. package/dist/templates/api/.vscode/air.code-snippets +178 -0
  70. package/dist/templates/api/package.json +17 -0
  71. package/dist/templates/api/src/index.ts +76 -0
  72. package/dist/templates/api/tsconfig.json +14 -0
  73. package/dist/templates/api-ko/.ai/context.md +36 -0
  74. package/dist/templates/api-ko/.vscode/air.code-snippets +178 -0
  75. package/dist/templates/api-ko/package.json +17 -0
  76. package/dist/templates/api-ko/src/index.ts +70 -0
  77. package/dist/templates/api-ko/tsconfig.json +14 -0
  78. package/dist/templates/basic/.ai/context.md +36 -0
  79. package/dist/templates/basic/.vscode/air.code-snippets +178 -0
  80. package/dist/templates/basic/package.json +17 -0
  81. package/dist/templates/basic/src/index.ts +39 -0
  82. package/dist/templates/basic/tsconfig.json +15 -0
  83. package/dist/templates/basic-ko/.ai/context.md +36 -0
  84. package/dist/templates/basic-ko/.vscode/air.code-snippets +178 -0
  85. package/dist/templates/basic-ko/package.json +17 -0
  86. package/dist/templates/basic-ko/src/index.ts +39 -0
  87. package/dist/templates/basic-ko/tsconfig.json +14 -0
  88. package/dist/templates/crud/.ai/context.md +36 -0
  89. package/dist/templates/crud/.vscode/air.code-snippets +178 -0
  90. package/dist/templates/crud/package.json +17 -0
  91. package/dist/templates/crud/src/index.ts +82 -0
  92. package/dist/templates/crud/tsconfig.json +14 -0
  93. package/dist/templates/crud-ko/.ai/context.md +36 -0
  94. package/dist/templates/crud-ko/.vscode/air.code-snippets +178 -0
  95. package/dist/templates/crud-ko/package.json +17 -0
  96. package/dist/templates/crud-ko/src/index.ts +81 -0
  97. package/dist/templates/crud-ko/tsconfig.json +14 -0
  98. package/dist/utils/index.d.ts +7 -0
  99. package/dist/utils/index.d.ts.map +1 -0
  100. package/dist/utils/index.js +8 -0
  101. package/dist/utils/index.js.map +1 -0
  102. package/dist/utils/json-editor.d.ts +45 -0
  103. package/dist/utils/json-editor.d.ts.map +1 -0
  104. package/dist/utils/json-editor.js +122 -0
  105. package/dist/utils/json-editor.js.map +1 -0
  106. package/dist/utils/path-resolver.d.ts +30 -0
  107. package/dist/utils/path-resolver.d.ts.map +1 -0
  108. package/dist/utils/path-resolver.js +121 -0
  109. package/dist/utils/path-resolver.js.map +1 -0
  110. package/dist/utils/printer.d.ts +26 -0
  111. package/dist/utils/printer.d.ts.map +1 -0
  112. package/dist/utils/printer.js +65 -0
  113. package/dist/utils/printer.js.map +1 -0
  114. package/dist/utils/process-manager.d.ts +36 -0
  115. package/dist/utils/process-manager.d.ts.map +1 -0
  116. package/dist/utils/process-manager.js +143 -0
  117. package/dist/utils/process-manager.js.map +1 -0
  118. package/dist/utils/test-console.d.ts +8 -0
  119. package/dist/utils/test-console.d.ts.map +1 -0
  120. package/dist/utils/test-console.js +198 -0
  121. package/dist/utils/test-console.js.map +1 -0
  122. package/package.json +52 -0
@@ -0,0 +1,45 @@
1
+ export declare class JsonEditor {
2
+ private readonly filePath;
3
+ private data;
4
+ private indent;
5
+ private trailingNewline;
6
+ private constructor();
7
+ /**
8
+ * JSON 파일을 로드한다.
9
+ * 파일이 없으면 빈 객체로 시작.
10
+ */
11
+ static load(filePath: string): Promise<JsonEditor>;
12
+ /**
13
+ * dot-path로 값을 가져온다.
14
+ *
15
+ * @example editor.get('mcpServers.my-tool')
16
+ */
17
+ get<T = any>(path: string): T | undefined;
18
+ /**
19
+ * dot-path로 값을 설정한다.
20
+ * 중간 경로가 없으면 자동 생성.
21
+ *
22
+ * @example editor.set('mcpServers.my-tool', { command: 'air', args: ['start'] })
23
+ */
24
+ set(path: string, value: any): void;
25
+ /**
26
+ * dot-path의 값을 삭제한다.
27
+ *
28
+ * @returns 삭제 성공 여부
29
+ */
30
+ delete(path: string): boolean;
31
+ /**
32
+ * dot-path에 값이 존재하는지 확인.
33
+ */
34
+ has(path: string): boolean;
35
+ /**
36
+ * 전체 데이터를 반환한다 (읽기 전용 용도).
37
+ */
38
+ toJSON(): Record<string, any>;
39
+ /**
40
+ * 파일에 저장한다.
41
+ * 디렉토리가 없으면 자동 생성.
42
+ */
43
+ save(): Promise<void>;
44
+ }
45
+ //# sourceMappingURL=json-editor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-editor.d.ts","sourceRoot":"","sources":["../../src/utils/json-editor.ts"],"names":[],"mappings":"AAgBA,qBAAa,UAAU;IAMnB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAL3B,OAAO,CAAC,IAAI,CAAsB;IAClC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,eAAe,CAAU;IAEjC,OAAO;IAcP;;;OAGG;WACU,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;IAUxD;;;;OAIG;IACH,GAAG,CAAC,CAAC,GAAG,GAAG,EAAE,IAAI,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS;IAUzC;;;;;OAKG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,IAAI;IAcnC;;;;OAIG;IACH,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAiB7B;;OAEG;IACH,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI1B;;OAEG;IACH,MAAM,IAAI,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAI7B;;;OAGG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAS5B"}
@@ -0,0 +1,122 @@
1
+ // Copyright 2026 CodePedia Labs. Licensed under Apache-2.0.
2
+ // air CLI — utils/json-editor.ts
3
+ //
4
+ // JSON 설정 파일을 안전하게 읽기/수정/쓰기.
5
+ // 기존 포매팅(들여쓰기, 후행 줄바꿈)을 최대한 보존한다.
6
+ // connect/disconnect에서 클라이언트 config.json을 조작할 때 사용.
7
+ //
8
+ // @example
9
+ // const editor = await JsonEditor.load('~/.config/Claude/claude_desktop_config.json');
10
+ // editor.set('mcpServers.my-tool', { command: 'air', args: ['start'] });
11
+ // editor.delete('mcpServers.old-tool');
12
+ // await editor.save();
13
+ import { readFile, writeFile, mkdir } from 'node:fs/promises';
14
+ import { dirname } from 'node:path';
15
+ export class JsonEditor {
16
+ filePath;
17
+ data;
18
+ indent;
19
+ trailingNewline;
20
+ constructor(filePath, raw) {
21
+ this.filePath = filePath;
22
+ // 들여쓰기 감지 (2 or 4 스페이스, 기본 2)
23
+ const indentMatch = raw.match(/^(\s+)"/m);
24
+ this.indent = indentMatch ? indentMatch[1].length : 2;
25
+ // 후행 줄바꿈 감지
26
+ this.trailingNewline = raw.endsWith('\n');
27
+ this.data = JSON.parse(raw);
28
+ }
29
+ /**
30
+ * JSON 파일을 로드한다.
31
+ * 파일이 없으면 빈 객체로 시작.
32
+ */
33
+ static async load(filePath) {
34
+ let raw;
35
+ try {
36
+ raw = await readFile(filePath, 'utf-8');
37
+ }
38
+ catch {
39
+ raw = '{}';
40
+ }
41
+ return new JsonEditor(filePath, raw);
42
+ }
43
+ /**
44
+ * dot-path로 값을 가져온다.
45
+ *
46
+ * @example editor.get('mcpServers.my-tool')
47
+ */
48
+ get(path) {
49
+ const keys = path.split('.');
50
+ let current = this.data;
51
+ for (const key of keys) {
52
+ if (current == null || typeof current !== 'object')
53
+ return undefined;
54
+ current = current[key];
55
+ }
56
+ return current;
57
+ }
58
+ /**
59
+ * dot-path로 값을 설정한다.
60
+ * 중간 경로가 없으면 자동 생성.
61
+ *
62
+ * @example editor.set('mcpServers.my-tool', { command: 'air', args: ['start'] })
63
+ */
64
+ set(path, value) {
65
+ const keys = path.split('.');
66
+ const lastKey = keys.pop();
67
+ let current = this.data;
68
+ for (const key of keys) {
69
+ if (current[key] == null || typeof current[key] !== 'object') {
70
+ current[key] = {};
71
+ }
72
+ current = current[key];
73
+ }
74
+ current[lastKey] = value;
75
+ }
76
+ /**
77
+ * dot-path의 값을 삭제한다.
78
+ *
79
+ * @returns 삭제 성공 여부
80
+ */
81
+ delete(path) {
82
+ const keys = path.split('.');
83
+ const lastKey = keys.pop();
84
+ let current = this.data;
85
+ for (const key of keys) {
86
+ if (current == null || typeof current !== 'object')
87
+ return false;
88
+ current = current[key];
89
+ }
90
+ if (current == null || typeof current !== 'object')
91
+ return false;
92
+ if (!(lastKey in current))
93
+ return false;
94
+ delete current[lastKey];
95
+ return true;
96
+ }
97
+ /**
98
+ * dot-path에 값이 존재하는지 확인.
99
+ */
100
+ has(path) {
101
+ return this.get(path) !== undefined;
102
+ }
103
+ /**
104
+ * 전체 데이터를 반환한다 (읽기 전용 용도).
105
+ */
106
+ toJSON() {
107
+ return structuredClone(this.data);
108
+ }
109
+ /**
110
+ * 파일에 저장한다.
111
+ * 디렉토리가 없으면 자동 생성.
112
+ */
113
+ async save() {
114
+ const dir = dirname(this.filePath);
115
+ await mkdir(dir, { recursive: true });
116
+ let content = JSON.stringify(this.data, null, this.indent);
117
+ if (this.trailingNewline)
118
+ content += '\n';
119
+ await writeFile(this.filePath, content, 'utf-8');
120
+ }
121
+ }
122
+ //# sourceMappingURL=json-editor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json-editor.js","sourceRoot":"","sources":["../../src/utils/json-editor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ export type SupportedClient = 'claude-desktop' | 'claude-code' | 'cursor' | 'vscode' | 'chatgpt' | 'ollama' | 'vllm' | 'lm-studio' | 'custom';
2
+ export interface ClientPathInfo {
3
+ /** 설정 파일 절대 경로 */
4
+ configPath: string;
5
+ /** MCP 서버 등록 키 (JSON 내부 위치) */
6
+ mcpKey: string;
7
+ /** 클라이언트 표시 이름 */
8
+ displayName: string;
9
+ }
10
+ /**
11
+ * 클라이언트의 설정 파일 경로를 반환한다.
12
+ *
13
+ * @param client 클라이언트 식별자
14
+ * @returns 경로 정보 (configPath, mcpKey, displayName)
15
+ * @throws 지원하지 않는 클라이언트
16
+ */
17
+ export declare function resolveClientConfig(client: string): ClientPathInfo;
18
+ /**
19
+ * 지원하는 클라이언트 목록 반환.
20
+ */
21
+ export declare function listClients(): Array<{
22
+ id: string;
23
+ name: string;
24
+ }>;
25
+ /**
26
+ * air 프로젝트의 설정 파일(air.config.ts / air.config.json) 경로를 탐색한다.
27
+ * cwd 기준으로 찾고 없으면 null.
28
+ */
29
+ export declare function resolveAirConfig(cwd?: string): string | null;
30
+ //# sourceMappingURL=path-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-resolver.d.ts","sourceRoot":"","sources":["../../src/utils/path-resolver.ts"],"names":[],"mappings":"AAmBA,MAAM,MAAM,eAAe,GACvB,gBAAgB,GAChB,aAAa,GACb,QAAQ,GACR,QAAQ,GACR,SAAS,GACT,QAAQ,GACR,MAAM,GACN,WAAW,GACX,QAAQ,CAAC;AAEb,MAAM,WAAW,cAAc;IAC7B,kBAAkB;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,+BAA+B;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,kBAAkB;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAsED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,cAAc,CAOlE;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAKjE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAsB,GAAG,MAAM,GAAG,IAAI,CAc3E"}
@@ -0,0 +1,121 @@
1
+ // Copyright 2026 CodePedia Labs. Licensed under Apache-2.0.
2
+ // air CLI — utils/path-resolver.ts
3
+ //
4
+ // OS별 MCP 클라이언트 설정 파일 경로를 해석한다.
5
+ // connect/disconnect 명령에서 사용.
6
+ //
7
+ // 지원 클라이언트:
8
+ // claude-desktop, claude-code, cursor, vscode,
9
+ // chatgpt, ollama, vllm, lm-studio, custom
10
+ //
11
+ // @example
12
+ // const configPath = resolveClientConfig('claude-desktop');
13
+ // // macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
14
+ // // Windows: %APPDATA%/Claude/claude_desktop_config.json
15
+ // // Linux: ~/.config/Claude/claude_desktop_config.json
16
+ import { homedir, platform } from 'node:os';
17
+ import { join } from 'node:path';
18
+ /** 홈 디렉토리 기준 OS별 config 루트 */
19
+ function configRoot() {
20
+ const os = platform();
21
+ const home = homedir();
22
+ if (os === 'darwin')
23
+ return join(home, 'Library', 'Application Support');
24
+ if (os === 'win32')
25
+ return process.env.APPDATA || join(home, 'AppData', 'Roaming');
26
+ // linux, freebsd 등
27
+ return process.env.XDG_CONFIG_HOME || join(home, '.config');
28
+ }
29
+ /** 클라이언트별 설정 경로 매핑 */
30
+ const CLIENT_PATHS = {
31
+ 'claude-desktop': (root) => ({
32
+ configPath: join(root, 'Claude', 'claude_desktop_config.json'),
33
+ mcpKey: 'mcpServers',
34
+ displayName: 'Claude Desktop',
35
+ }),
36
+ 'claude-code': (root) => ({
37
+ configPath: join(root, 'claude-code', 'config.json'),
38
+ mcpKey: 'mcpServers',
39
+ displayName: 'Claude Code',
40
+ }),
41
+ cursor: (root) => ({
42
+ configPath: join(root, 'Cursor', 'User', 'globalStorage', 'cursor.mcp', 'config.json'),
43
+ mcpKey: 'mcpServers',
44
+ displayName: 'Cursor',
45
+ }),
46
+ vscode: (root) => ({
47
+ configPath: join(root, 'Code', 'User', 'settings.json'),
48
+ mcpKey: 'mcp.servers',
49
+ displayName: 'VS Code',
50
+ }),
51
+ chatgpt: (root) => ({
52
+ configPath: join(root, 'ChatGPT', 'mcp_config.json'),
53
+ mcpKey: 'mcpServers',
54
+ displayName: 'ChatGPT Desktop',
55
+ }),
56
+ ollama: (_root) => ({
57
+ configPath: join(homedir(), '.ollama', 'mcp.json'),
58
+ mcpKey: 'servers',
59
+ displayName: 'Ollama',
60
+ }),
61
+ vllm: (_root) => ({
62
+ configPath: join(homedir(), '.vllm', 'mcp.json'),
63
+ mcpKey: 'servers',
64
+ displayName: 'vLLM',
65
+ }),
66
+ 'lm-studio': (root) => ({
67
+ configPath: join(root, 'LM Studio', 'mcp.json'),
68
+ mcpKey: 'servers',
69
+ displayName: 'LM Studio',
70
+ }),
71
+ custom: (_root) => ({
72
+ configPath: '',
73
+ mcpKey: 'mcpServers',
74
+ displayName: 'Custom',
75
+ }),
76
+ };
77
+ /**
78
+ * 클라이언트의 설정 파일 경로를 반환한다.
79
+ *
80
+ * @param client 클라이언트 식별자
81
+ * @returns 경로 정보 (configPath, mcpKey, displayName)
82
+ * @throws 지원하지 않는 클라이언트
83
+ */
84
+ export function resolveClientConfig(client) {
85
+ const resolver = CLIENT_PATHS[client];
86
+ if (!resolver) {
87
+ const supported = Object.keys(CLIENT_PATHS).join(', ');
88
+ throw new Error(`Unknown client "${client}". Supported: ${supported}`);
89
+ }
90
+ return resolver(configRoot());
91
+ }
92
+ /**
93
+ * 지원하는 클라이언트 목록 반환.
94
+ */
95
+ export function listClients() {
96
+ return Object.entries(CLIENT_PATHS).map(([id, resolver]) => ({
97
+ id,
98
+ name: resolver(configRoot()).displayName,
99
+ }));
100
+ }
101
+ /**
102
+ * air 프로젝트의 설정 파일(air.config.ts / air.config.json) 경로를 탐색한다.
103
+ * cwd 기준으로 찾고 없으면 null.
104
+ */
105
+ export function resolveAirConfig(cwd = process.cwd()) {
106
+ const candidates = ['air.config.ts', 'air.config.json', 'air.config.js'];
107
+ for (const name of candidates) {
108
+ const full = join(cwd, name);
109
+ try {
110
+ // 존재 여부만 확인 (import 없이)
111
+ const { accessSync } = require('node:fs');
112
+ accessSync(full);
113
+ return full;
114
+ }
115
+ catch {
116
+ continue;
117
+ }
118
+ }
119
+ return null;
120
+ }
121
+ //# sourceMappingURL=path-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"path-resolver.js","sourceRoot":"","sources":["../../src/utils/path-resolver.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ export declare const printer: {
2
+ /** 성공 메시지 (✔ 초록) */
3
+ success(msg: string): void;
4
+ /** 에러 메시지 (✖ 빨강) */
5
+ error(msg: string): void;
6
+ /** 경고 메시지 (⚠ 노랑) */
7
+ warn(msg: string): void;
8
+ /** 정보 메시지 (ℹ 파랑) */
9
+ info(msg: string): void;
10
+ /** 단계 표시 (1/4 등) */
11
+ step(current: number, total: number, msg: string): void;
12
+ /** 빈 줄 */
13
+ blank(): void;
14
+ /** 제목 (굵은 글씨) */
15
+ heading(msg: string): void;
16
+ /** 키-값 출력 (상태 표시 등) */
17
+ kv(key: string, value: string): void;
18
+ /** 테이블 형태 목록 (도구 목록 등) */
19
+ list(items: Array<{
20
+ name: string;
21
+ description?: string;
22
+ }>): void;
23
+ /** air 배너 (초기 출력용) */
24
+ banner(version: string): void;
25
+ };
26
+ //# sourceMappingURL=printer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"printer.d.ts","sourceRoot":"","sources":["../../src/utils/printer.ts"],"names":[],"mappings":"AAeA,eAAO,MAAM,OAAO;IAClB,oBAAoB;iBACP,MAAM,GAAG,IAAI;IAI1B,oBAAoB;eACT,MAAM,GAAG,IAAI;IAIxB,oBAAoB;cACV,MAAM,GAAG,IAAI;IAIvB,oBAAoB;cACV,MAAM,GAAG,IAAI;IAIvB,oBAAoB;kBACN,MAAM,SAAS,MAAM,OAAO,MAAM,GAAG,IAAI;IAKvD,UAAU;aACD,IAAI;IAIb,iBAAiB;iBACJ,MAAM,GAAG,IAAI;IAI1B,uBAAuB;YACf,MAAM,SAAS,MAAM,GAAG,IAAI;IAIpC,0BAA0B;gBACd,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,IAAI;IAShE,sBAAsB;oBACN,MAAM,GAAG,IAAI;CAM9B,CAAC"}
@@ -0,0 +1,65 @@
1
+ // Copyright 2026 CodePedia Labs. Licensed under Apache-2.0.
2
+ // air CLI — utils/printer.ts
3
+ //
4
+ // 터미널 출력 헬퍼.
5
+ // chalk 기반 색상 + 아이콘으로 일관된 CLI 경험 제공.
6
+ //
7
+ // @example
8
+ // printer.success('Server started on port 3000');
9
+ // printer.error('Failed to load config');
10
+ // printer.info('Watching for changes...');
11
+ // printer.step(1, 4, 'Installing dependencies');
12
+ // printer.banner();
13
+ import chalk from 'chalk';
14
+ export const printer = {
15
+ /** 성공 메시지 (✔ 초록) */
16
+ success(msg) {
17
+ console.log(chalk.green(' ✔'), msg);
18
+ },
19
+ /** 에러 메시지 (✖ 빨강) */
20
+ error(msg) {
21
+ console.error(chalk.red(' ✖'), msg);
22
+ },
23
+ /** 경고 메시지 (⚠ 노랑) */
24
+ warn(msg) {
25
+ console.log(chalk.yellow(' ⚠'), msg);
26
+ },
27
+ /** 정보 메시지 (ℹ 파랑) */
28
+ info(msg) {
29
+ console.log(chalk.blue(' ℹ'), msg);
30
+ },
31
+ /** 단계 표시 (1/4 등) */
32
+ step(current, total, msg) {
33
+ const label = chalk.dim(`[${current}/${total}]`);
34
+ console.log(` ${label} ${msg}`);
35
+ },
36
+ /** 빈 줄 */
37
+ blank() {
38
+ console.log();
39
+ },
40
+ /** 제목 (굵은 글씨) */
41
+ heading(msg) {
42
+ console.log(chalk.bold(`\n ${msg}\n`));
43
+ },
44
+ /** 키-값 출력 (상태 표시 등) */
45
+ kv(key, value) {
46
+ console.log(` ${chalk.dim(key + ':')} ${value}`);
47
+ },
48
+ /** 테이블 형태 목록 (도구 목록 등) */
49
+ list(items) {
50
+ const maxLen = Math.max(...items.map((i) => i.name.length));
51
+ for (const item of items) {
52
+ const name = chalk.cyan(item.name.padEnd(maxLen + 2));
53
+ const desc = item.description ? chalk.dim(item.description) : '';
54
+ console.log(` ${name}${desc}`);
55
+ }
56
+ },
57
+ /** air 배너 (초기 출력용) */
58
+ banner(version) {
59
+ console.log();
60
+ console.log(chalk.bold(' air'), chalk.dim(`v${version}`));
61
+ console.log(chalk.dim(' Build, run, and manage MCP servers'));
62
+ console.log();
63
+ },
64
+ };
65
+ //# sourceMappingURL=printer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"printer.js","sourceRoot":"","sources":["../../src/utils/printer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,36 @@
1
+ export interface ServerProcess {
2
+ name: string;
3
+ pid: number;
4
+ alive: boolean;
5
+ }
6
+ export declare const ProcessManager: {
7
+ /**
8
+ * 서버 PID를 기록한다.
9
+ */
10
+ save(name: string, pid: number, cwd?: string): Promise<void>;
11
+ /**
12
+ * 서버 PID를 읽는다.
13
+ * 파일이 없거나 프로세스가 죽었으면 null.
14
+ */
15
+ read(name: string, cwd?: string): Promise<number | null>;
16
+ /**
17
+ * 서버가 실행 중인지 확인한다.
18
+ */
19
+ isRunning(name: string, cwd?: string): Promise<boolean>;
20
+ /**
21
+ * 서버 프로세스를 종료한다.
22
+ * SIGTERM → 2초 대기 → 여전히 살아있으면 SIGKILL.
23
+ *
24
+ * @returns 종료 성공 여부
25
+ */
26
+ kill(name: string, cwd?: string): Promise<boolean>;
27
+ /**
28
+ * PID 파일을 삭제한다.
29
+ */
30
+ remove(name: string, cwd?: string): Promise<void>;
31
+ /**
32
+ * 현재 프로젝트에 등록된 모든 서버 프로세스 목록.
33
+ */
34
+ listAll(cwd?: string): Promise<ServerProcess[]>;
35
+ };
36
+ //# sourceMappingURL=process-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-manager.d.ts","sourceRoot":"","sources":["../../src/utils/process-manager.ts"],"names":[],"mappings":"AAqCA,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,eAAO,MAAM,cAAc;IACzB;;OAEG;eACc,MAAM,OAAO,MAAM,QAAO,MAAM,GAAmB,OAAO,CAAC,IAAI,CAAC;IAMjF;;;OAGG;eACc,MAAM,QAAO,MAAM,GAAmB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAW7E;;OAEG;oBACmB,MAAM,QAAO,MAAM,GAAmB,OAAO,CAAC,OAAO,CAAC;IAM5E;;;;;OAKG;eACc,MAAM,QAAO,MAAM,GAAmB,OAAO,CAAC,OAAO,CAAC;IA+BvE;;OAEG;iBACgB,MAAM,QAAO,MAAM,GAAmB,OAAO,CAAC,IAAI,CAAC;IAQtE;;OAEG;kBACgB,MAAM,GAAmB,OAAO,CAAC,aAAa,EAAE,CAAC;CAoBrE,CAAC"}
@@ -0,0 +1,143 @@
1
+ // Copyright 2026 CodePedia Labs. Licensed under Apache-2.0.
2
+ // air CLI — utils/process-manager.ts
3
+ //
4
+ // MCP 서버 프로세스의 PID를 추적한다.
5
+ // air start → PID 기록, air stop → PID로 종료, air status → 살아있는지 확인.
6
+ //
7
+ // PID 파일 위치: <project>/.air/pids/<server-name>.pid
8
+ // .air/ 디렉토리는 .gitignore 대상.
9
+ //
10
+ // @example
11
+ // await ProcessManager.save('my-tool', 12345);
12
+ // await ProcessManager.isRunning('my-tool'); // true
13
+ // await ProcessManager.kill('my-tool'); // SIGTERM → PID 파일 삭제
14
+ import { readFile, writeFile, unlink, mkdir, readdir } from 'node:fs/promises';
15
+ import { join } from 'node:path';
16
+ /** PID 디렉토리 경로 (.air/pids/) */
17
+ function pidDir(cwd) {
18
+ return join(cwd, '.air', 'pids');
19
+ }
20
+ /** PID 파일 경로 */
21
+ function pidFile(cwd, name) {
22
+ return join(pidDir(cwd), `${name}.pid`);
23
+ }
24
+ /** 프로세스가 살아있는지 확인 (signal 0) */
25
+ function processAlive(pid) {
26
+ try {
27
+ process.kill(pid, 0);
28
+ return true;
29
+ }
30
+ catch {
31
+ return false;
32
+ }
33
+ }
34
+ export const ProcessManager = {
35
+ /**
36
+ * 서버 PID를 기록한다.
37
+ */
38
+ async save(name, pid, cwd = process.cwd()) {
39
+ const dir = pidDir(cwd);
40
+ await mkdir(dir, { recursive: true });
41
+ await writeFile(pidFile(cwd, name), String(pid), 'utf-8');
42
+ },
43
+ /**
44
+ * 서버 PID를 읽는다.
45
+ * 파일이 없거나 프로세스가 죽었으면 null.
46
+ */
47
+ async read(name, cwd = process.cwd()) {
48
+ try {
49
+ const raw = await readFile(pidFile(cwd, name), 'utf-8');
50
+ const pid = parseInt(raw.trim(), 10);
51
+ if (isNaN(pid))
52
+ return null;
53
+ return pid;
54
+ }
55
+ catch {
56
+ return null;
57
+ }
58
+ },
59
+ /**
60
+ * 서버가 실행 중인지 확인한다.
61
+ */
62
+ async isRunning(name, cwd = process.cwd()) {
63
+ const pid = await this.read(name, cwd);
64
+ if (pid === null)
65
+ return false;
66
+ return processAlive(pid);
67
+ },
68
+ /**
69
+ * 서버 프로세스를 종료한다.
70
+ * SIGTERM → 2초 대기 → 여전히 살아있으면 SIGKILL.
71
+ *
72
+ * @returns 종료 성공 여부
73
+ */
74
+ async kill(name, cwd = process.cwd()) {
75
+ const pid = await this.read(name, cwd);
76
+ if (pid === null)
77
+ return false;
78
+ if (!processAlive(pid)) {
79
+ await this.remove(name, cwd);
80
+ return false;
81
+ }
82
+ // graceful 종료 시도
83
+ try {
84
+ process.kill(pid, 'SIGTERM');
85
+ }
86
+ catch {
87
+ await this.remove(name, cwd);
88
+ return false;
89
+ }
90
+ // 2초 대기 후 강제 종료
91
+ await sleep(2000);
92
+ if (processAlive(pid)) {
93
+ try {
94
+ process.kill(pid, 'SIGKILL');
95
+ }
96
+ catch {
97
+ // 이미 죽었을 수 있음
98
+ }
99
+ }
100
+ await this.remove(name, cwd);
101
+ return true;
102
+ },
103
+ /**
104
+ * PID 파일을 삭제한다.
105
+ */
106
+ async remove(name, cwd = process.cwd()) {
107
+ try {
108
+ await unlink(pidFile(cwd, name));
109
+ }
110
+ catch {
111
+ // 이미 없으면 무시
112
+ }
113
+ },
114
+ /**
115
+ * 현재 프로젝트에 등록된 모든 서버 프로세스 목록.
116
+ */
117
+ async listAll(cwd = process.cwd()) {
118
+ const dir = pidDir(cwd);
119
+ let files;
120
+ try {
121
+ files = await readdir(dir);
122
+ }
123
+ catch {
124
+ return [];
125
+ }
126
+ const results = [];
127
+ for (const file of files) {
128
+ if (!file.endsWith('.pid'))
129
+ continue;
130
+ const name = file.replace(/\.pid$/, '');
131
+ const pid = await this.read(name, cwd);
132
+ if (pid !== null) {
133
+ results.push({ name, pid, alive: processAlive(pid) });
134
+ }
135
+ }
136
+ return results;
137
+ },
138
+ };
139
+ /** 간단한 sleep 헬퍼 */
140
+ function sleep(ms) {
141
+ return new Promise((resolve) => setTimeout(resolve, ms));
142
+ }
143
+ //# sourceMappingURL=process-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"process-manager.js","sourceRoot":"","sources":["../../src/utils/process-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ interface TestConsoleOptions {
2
+ serverUrl: string;
3
+ onQuit: () => void;
4
+ }
5
+ /** SSE 클라이언트로 서버에 연결하여 테스트 콘솔 제공 */
6
+ export declare function startTestConsole(options: TestConsoleOptions): Promise<void>;
7
+ export {};
8
+ //# sourceMappingURL=test-console.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test-console.d.ts","sourceRoot":"","sources":["../../src/utils/test-console.ts"],"names":[],"mappings":"AAkBA,UAAU,kBAAkB;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB;AA0BD,oCAAoC;AACpC,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CA2KjF"}