relayax-cli 0.3.59 → 0.3.62
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/commands/package.js +10 -4
- package/dist/mcp/server.js +15 -11
- package/dist/prompts/publish.md +15 -2
- package/package.json +1 -1
package/dist/commands/package.js
CHANGED
|
@@ -213,12 +213,18 @@ function syncContentsToRelay(contents, contentsDiff, relayDir, projectPath) {
|
|
|
213
213
|
continue;
|
|
214
214
|
const absFrom = resolveFromPath(content.from, projectPath);
|
|
215
215
|
const relaySubPath = deriveRelaySubPath(content);
|
|
216
|
-
const
|
|
217
|
-
//
|
|
216
|
+
const relayTarget = path_1.default.join(relayDir, relaySubPath);
|
|
217
|
+
// 단일 파일인 경우 직접 복사 (디렉토리 기반 diff/sync 불필요)
|
|
218
|
+
if (fs_1.default.existsSync(absFrom) && fs_1.default.statSync(absFrom).isFile()) {
|
|
219
|
+
fs_1.default.mkdirSync(path_1.default.dirname(relayTarget), { recursive: true });
|
|
220
|
+
fs_1.default.copyFileSync(absFrom, relayTarget);
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
// 디렉토리인 경우 diff 기반 동기화
|
|
218
224
|
const sourceFiles = scanPath(absFrom);
|
|
219
|
-
const relayFiles = scanPath(
|
|
225
|
+
const relayFiles = scanPath(relayTarget);
|
|
220
226
|
const fileDiff = computeDiff(sourceFiles, relayFiles);
|
|
221
|
-
syncToRelay(absFrom,
|
|
227
|
+
syncToRelay(absFrom, relayTarget, fileDiff);
|
|
222
228
|
}
|
|
223
229
|
}
|
|
224
230
|
// ─── Global Agent Home ───
|
package/dist/mcp/server.js
CHANGED
|
@@ -23,18 +23,18 @@ const js_yaml_1 = __importDefault(require("js-yaml"));
|
|
|
23
23
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
24
24
|
const pkg = require('../../package.json');
|
|
25
25
|
// ─── Helpers ───
|
|
26
|
-
async function
|
|
26
|
+
async function resolveUserInfo(token) {
|
|
27
27
|
try {
|
|
28
28
|
const res = await fetch(`${config_js_1.API_URL}/api/auth/me`, {
|
|
29
29
|
headers: { Authorization: `Bearer ${token}` },
|
|
30
30
|
});
|
|
31
31
|
if (!res.ok)
|
|
32
|
-
return
|
|
32
|
+
return {};
|
|
33
33
|
const body = await res.json();
|
|
34
|
-
return body.username;
|
|
34
|
+
return { username: body.username, email: body.email };
|
|
35
35
|
}
|
|
36
36
|
catch {
|
|
37
|
-
return
|
|
37
|
+
return {};
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
function countFiles(dir) {
|
|
@@ -208,8 +208,12 @@ function createMcpServer() {
|
|
|
208
208
|
const projectPath = resolveMcpProjectPath(project_path);
|
|
209
209
|
const token = await (0, config_js_1.getValidToken)();
|
|
210
210
|
let username;
|
|
211
|
-
|
|
212
|
-
|
|
211
|
+
let email;
|
|
212
|
+
if (token) {
|
|
213
|
+
const info = await resolveUserInfo(token);
|
|
214
|
+
username = info.username;
|
|
215
|
+
email = info.email;
|
|
216
|
+
}
|
|
213
217
|
const detected = (0, ai_tools_js_1.detectAgentCLIs)(projectPath);
|
|
214
218
|
const mounted = (0, ai_tools_js_1.detectMountedCLIs)();
|
|
215
219
|
const relayYaml = path_1.default.join(projectPath, '.relay', 'relay.yaml');
|
|
@@ -226,7 +230,7 @@ function createMcpServer() {
|
|
|
226
230
|
const cliUpdate = await checkCliVersion(true);
|
|
227
231
|
return { content: [jsonText({
|
|
228
232
|
cli: { version: pkg.version, update_available: cliUpdate ? cliUpdate.latest : null },
|
|
229
|
-
login: { authenticated: !!token, username },
|
|
233
|
+
login: { authenticated: !!token, username, email },
|
|
230
234
|
agent_clis: detected.map((t) => t.name),
|
|
231
235
|
mounted_paths: mounted.map((m) => m.basePath),
|
|
232
236
|
project,
|
|
@@ -624,8 +628,8 @@ function createMcpServer() {
|
|
|
624
628
|
// 이미 로그인되어 있는지 확인
|
|
625
629
|
const existingToken = await (0, config_js_1.getValidToken)();
|
|
626
630
|
if (existingToken) {
|
|
627
|
-
const username = await
|
|
628
|
-
return { content: [jsonText({ status: 'already_authenticated', username })] };
|
|
631
|
+
const { username, email } = await resolveUserInfo(existingToken);
|
|
632
|
+
return { content: [jsonText({ status: 'already_authenticated', username, email })] };
|
|
629
633
|
}
|
|
630
634
|
// Device code 발급
|
|
631
635
|
const res = await fetch(`${config_js_1.API_URL}/api/auth/device/request`, { method: 'POST' });
|
|
@@ -663,8 +667,8 @@ function createMcpServer() {
|
|
|
663
667
|
refresh_token: data.refresh_token,
|
|
664
668
|
expires_at: data.expires_at ? Number(data.expires_at) : undefined,
|
|
665
669
|
});
|
|
666
|
-
const username = await
|
|
667
|
-
return { content: [jsonText({ status: 'ok', message: '로그인 완료', username })] };
|
|
670
|
+
const { username, email } = await resolveUserInfo(data.token);
|
|
671
|
+
return { content: [jsonText({ status: 'ok', message: '로그인 완료', username, email })] };
|
|
668
672
|
}
|
|
669
673
|
}
|
|
670
674
|
return { content: [jsonText({ status: 'timeout', verification_url, user_code, message: `브라우저에서 ${verification_url} 을 열고 코드 ${user_code} 를 입력해주세요.` })], isError: true };
|
package/dist/prompts/publish.md
CHANGED
|
@@ -291,7 +291,11 @@ Organization 목록을 조회합니다:
|
|
|
291
291
|
- question: "Organization이 없습니다. 비공개 배포를 하려면 Organization이 필요합니다. Organization을 만들까요?"
|
|
292
292
|
- options: `["Organization 생성", "Organization 없이 계속 (공개/링크공유만 가능)"]`
|
|
293
293
|
- "Organization 생성" 선택 시:
|
|
294
|
-
-
|
|
294
|
+
- 로그인 정보(username, email)를 기반으로 Organization 이름을 추천합니다:
|
|
295
|
+
- 업무용 이메일(커스텀 도메인)이면 → 도메인에서 회사명 추출하여 추천. 예: `haemin@relayax.com` → "relayax"
|
|
296
|
+
- 비업무용 이메일(gmail.com, naver.com, kakao.com, daum.net, hotmail.com, outlook.com, yahoo.com, icloud.com 등 무료 메일)이면 → username을 추천. 예: `haemin` → "haemin"
|
|
297
|
+
- email이 없으면 → username을 추천
|
|
298
|
+
- **사용자 질문 도구 호출:** question: "Organization 이름을 입력하세요. (추천: {추천이름})"
|
|
295
299
|
- 환경 A: `relay orgs create "이름" --json` 실행
|
|
296
300
|
- 환경 B: `relay_org_create` MCP tool 호출
|
|
297
301
|
- 생성 후 org 목록을 갱신합니다.
|
|
@@ -341,12 +345,21 @@ Org가 없는 경우 (개인 배포):
|
|
|
341
345
|
|
|
342
346
|
.relay/ 내 모든 파일을 자동 분석합니다.
|
|
343
347
|
|
|
344
|
-
#### 2-1. 시크릿 스캔 (자동)
|
|
348
|
+
#### 2-1. 시크릿 & 개인정보 스캔 (자동)
|
|
349
|
+
|
|
350
|
+
**시크릿 스캔:**
|
|
345
351
|
- 하드코딩된 API 키, 토큰, 비밀번호, Private Key 등을 탐색합니다.
|
|
346
352
|
- 예: sk-..., ghp_..., AKIA..., Bearer 토큰, JWT, -----BEGIN PRIVATE KEY----- 등
|
|
347
353
|
- 발견 시 **즉시 사용자에게 경고**하고, 환경변수로 대체하도록 안내합니다.
|
|
348
354
|
- 시크릿이 제거되지 않으면 배포를 진행하지 않습니다.
|
|
349
355
|
|
|
356
|
+
**개인정보 스캔:**
|
|
357
|
+
- 이메일 주소, 전화번호, 실명, 주소 등 개인정보가 포함된 파일을 탐색합니다.
|
|
358
|
+
- **중요: 패키지에 포함된 모든 파일은 설치한 사람이 볼 수 있습니다.** 상세페이지에 노출되지 않더라도 패키지 자체에 포함됩니다.
|
|
359
|
+
- 발견 시 사용자에게 경고하고 제거/수정 여부를 확인받습니다:
|
|
360
|
+
- "⚠ {파일명}에 개인정보({종류})가 포함되어 있습니다. 이 파일은 패키지에 포함되어 설치한 사람이 볼 수 있습니다."
|
|
361
|
+
- **사용자 질문 도구 호출:** question: "개인정보가 포함된 파일을 어떻게 처리할까요?", options: `["제거 후 배포", "그대로 배포", "취소"]`
|
|
362
|
+
|
|
350
363
|
#### 2-2. 환경변수 & 의존성 분석 (자동)
|
|
351
364
|
분석 대상:
|
|
352
365
|
- **env**: 환경변수 참조 (process.env.*, ${VAR}, os.environ 등)
|