relayax-cli 0.1.998 → 0.2.12

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.
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerFollow(program: Command): void;
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerFollow = registerFollow;
4
+ const api_js_1 = require("../lib/api.js");
5
+ const config_js_1 = require("../lib/config.js");
6
+ function registerFollow(program) {
7
+ program
8
+ .command('follow <username>')
9
+ .description('빌더를 팔로우합니다 (새 버전 알림)')
10
+ .action(async (username) => {
11
+ const json = program.opts().json ?? false;
12
+ // Strip @ prefix if present
13
+ const cleanUsername = username.startsWith('@') ? username.slice(1) : username;
14
+ const token = await (0, config_js_1.getValidToken)();
15
+ if (!token) {
16
+ const msg = '로그인이 필요합니다. `relay login`을 먼저 실행하세요.';
17
+ if (json) {
18
+ console.log(JSON.stringify({ error: 'NO_TOKEN', message: msg }));
19
+ }
20
+ else {
21
+ console.error(msg);
22
+ }
23
+ process.exit(1);
24
+ }
25
+ try {
26
+ await (0, api_js_1.followBuilder)(cleanUsername);
27
+ if (json) {
28
+ console.log(JSON.stringify({ ok: true, following: cleanUsername }));
29
+ }
30
+ else {
31
+ console.log(`\x1b[32m✓ @${cleanUsername} 팔로우 완료\x1b[0m — 새 버전 알림을 받습니다.`);
32
+ }
33
+ }
34
+ catch (err) {
35
+ const message = err instanceof Error ? err.message : String(err);
36
+ if (json) {
37
+ console.log(JSON.stringify({ error: 'FOLLOW_FAILED', message }));
38
+ }
39
+ else {
40
+ console.error(`팔로우 실패: ${message}`);
41
+ }
42
+ process.exit(1);
43
+ }
44
+ });
45
+ }
@@ -10,6 +10,7 @@ const api_js_1 = require("../lib/api.js");
10
10
  const storage_js_1 = require("../lib/storage.js");
11
11
  const config_js_1 = require("../lib/config.js");
12
12
  const slug_js_1 = require("../lib/slug.js");
13
+ const contact_format_js_1 = require("../lib/contact-format.js");
13
14
  function registerInstall(program) {
14
15
  program
15
16
  .command('install <slug>')
@@ -101,18 +102,16 @@ function registerInstall(program) {
101
102
  }
102
103
  }
103
104
  // Builder business card
104
- const contactLinks = team.author?.contact_links ?? {};
105
- const contactEntries = Object.entries(contactLinks);
106
- const hasCard = team.welcome || contactEntries.length > 0 || authorUsername;
105
+ const contactParts = (0, contact_format_js_1.formatContactParts)(team.author?.contact_links);
106
+ const hasCard = team.welcome || contactParts.length > 0 || authorUsername;
107
107
  if (hasCard) {
108
108
  console.log(`\n \x1b[90m┌─ ${authorDisplayName || authorUsername || '빌더'}의 명함 ${'─'.repeat(Math.max(0, 34 - (authorDisplayName || authorUsername || '빌더').length))}┐\x1b[0m`);
109
109
  if (team.welcome) {
110
110
  const truncated = team.welcome.length > 45 ? team.welcome.slice(0, 45) + '...' : team.welcome;
111
111
  console.log(` \x1b[90m│\x1b[0m 💬 "${truncated}"`);
112
112
  }
113
- if (contactEntries.length > 0) {
114
- const parts = contactEntries.map(([k, v]) => `${k}: ${v}`).join(' ');
115
- console.log(` \x1b[90m│\x1b[0m 📇 ${parts}`);
113
+ if (contactParts.length > 0) {
114
+ console.log(` \x1b[90m│\x1b[0m 📇 ${contactParts.join(' ')}`);
116
115
  }
117
116
  if (authorUsername) {
118
117
  console.log(` \x1b[90m│\x1b[0m 👤 relayax.com/@${authorUsername}`);
@@ -122,24 +121,6 @@ function registerInstall(program) {
122
121
  }
123
122
  console.log(` \x1b[90m└${'─'.repeat(44)}┘\x1b[0m`);
124
123
  }
125
- // Follow prompt (only when logged in)
126
- const token = await (0, config_js_1.getValidToken)();
127
- if (authorUsername && token) {
128
- try {
129
- const { confirm } = await import('@inquirer/prompts');
130
- const shouldFollow = await confirm({
131
- message: `@${authorUsername}을 팔로우하시겠습니까? (이메일로 새 소식을 받습니다)`,
132
- default: true,
133
- });
134
- if (shouldFollow) {
135
- await (0, api_js_1.followBuilder)(authorUsername);
136
- console.log(`\x1b[32m ✓ @${authorUsername} 팔로우 완료\x1b[0m`);
137
- }
138
- }
139
- catch {
140
- // non-critical: skip on error or non-interactive terminal
141
- }
142
- }
143
124
  console.log('\n 에이전트가 /relay-install로 환경을 구성합니다.');
144
125
  }
145
126
  }
@@ -10,6 +10,7 @@ const os_1 = __importDefault(require("os"));
10
10
  const js_yaml_1 = __importDefault(require("js-yaml"));
11
11
  const tar_1 = require("tar");
12
12
  const config_js_1 = require("../lib/config.js");
13
+ const contact_format_js_1 = require("../lib/contact-format.js");
13
14
  const version_check_js_1 = require("../lib/version-check.js");
14
15
  // eslint-disable-next-line @typescript-eslint/no-var-requires
15
16
  const cliPkg = require('../../package.json');
@@ -436,21 +437,19 @@ function registerPublish(program) {
436
437
  // Show business card preview
437
438
  const profile = result.profile;
438
439
  if (profile) {
439
- const contacts = profile.contact_links ?? {};
440
- const contactEntries = Object.entries(contacts);
440
+ const contactParts = (0, contact_format_js_1.formatContactParts)(profile.contact_links);
441
441
  const welcome = profile.default_welcome ?? '';
442
442
  console.log(`\n \x1b[90m┌─ 설치자에게 보이는 명함 ${'─'.repeat(24)}┐\x1b[0m`);
443
443
  if (welcome) {
444
444
  console.log(` \x1b[90m│\x1b[0m 💬 "${welcome.length > 45 ? welcome.slice(0, 45) + '...' : welcome}"`);
445
445
  }
446
- if (contactEntries.length > 0) {
447
- const parts = contactEntries.map(([k, v]) => `${k}: ${v}`).join(' ');
448
- console.log(` \x1b[90m│\x1b[0m 📇 ${parts}`);
446
+ if (contactParts.length > 0) {
447
+ console.log(` \x1b[90m│\x1b[0m 📇 ${contactParts.join(' ')}`);
449
448
  }
450
449
  if (profile.username) {
451
450
  console.log(` \x1b[90m│\x1b[0m 👤 relayax.com/@${profile.username}`);
452
451
  }
453
- if (!welcome && contactEntries.length === 0) {
452
+ if (!welcome && contactParts.length === 0) {
454
453
  console.log(` \x1b[90m│\x1b[0m \x1b[2m명함이 비어있습니다\x1b[0m`);
455
454
  }
456
455
  console.log(` \x1b[90m└${'─'.repeat(44)}┘\x1b[0m`);
@@ -6,6 +6,7 @@ const storage_js_1 = require("../lib/storage.js");
6
6
  const installer_js_1 = require("../lib/installer.js");
7
7
  const config_js_1 = require("../lib/config.js");
8
8
  const slug_js_1 = require("../lib/slug.js");
9
+ const contact_format_js_1 = require("../lib/contact-format.js");
9
10
  function registerUpdate(program) {
10
11
  program
11
12
  .command('update <slug>')
@@ -98,18 +99,16 @@ function registerUpdate(program) {
98
99
  // Builder business card
99
100
  const authorUsername = team.author?.username;
100
101
  const authorDisplayName = team.author?.display_name ?? authorUsername ?? '';
101
- const contactLinks = team.author?.contact_links ?? {};
102
- const contactEntries = Object.entries(contactLinks);
103
- const hasCard = team.welcome || contactEntries.length > 0 || authorUsername;
102
+ const contactParts = (0, contact_format_js_1.formatContactParts)(team.author?.contact_links);
103
+ const hasCard = team.welcome || contactParts.length > 0 || authorUsername;
104
104
  if (hasCard) {
105
105
  console.log(`\n \x1b[90m┌─ ${authorDisplayName || '빌더'}의 명함 ${'─'.repeat(Math.max(0, 34 - (authorDisplayName || '빌더').length))}┐\x1b[0m`);
106
106
  if (team.welcome) {
107
107
  const truncated = team.welcome.length > 45 ? team.welcome.slice(0, 45) + '...' : team.welcome;
108
108
  console.log(` \x1b[90m│\x1b[0m 💬 "${truncated}"`);
109
109
  }
110
- if (contactEntries.length > 0) {
111
- const parts = contactEntries.map(([k, v]) => `${k}: ${v}`).join(' ');
112
- console.log(` \x1b[90m│\x1b[0m 📇 ${parts}`);
110
+ if (contactParts.length > 0) {
111
+ console.log(` \x1b[90m│\x1b[0m 📇 ${contactParts.join(' ')}`);
113
112
  }
114
113
  if (authorUsername) {
115
114
  console.log(` \x1b[90m│\x1b[0m 👤 relayax.com/@${authorUsername}`);
package/dist/index.js CHANGED
@@ -14,6 +14,7 @@ const login_js_1 = require("./commands/login.js");
14
14
  const update_js_1 = require("./commands/update.js");
15
15
  const outdated_js_1 = require("./commands/outdated.js");
16
16
  const check_update_js_1 = require("./commands/check-update.js");
17
+ const follow_js_1 = require("./commands/follow.js");
17
18
  // eslint-disable-next-line @typescript-eslint/no-var-requires
18
19
  const pkg = require('../package.json');
19
20
  const program = new commander_1.Command();
@@ -34,4 +35,5 @@ program
34
35
  (0, update_js_1.registerUpdate)(program);
35
36
  (0, outdated_js_1.registerOutdated)(program);
36
37
  (0, check_update_js_1.registerCheckUpdate)(program);
38
+ (0, follow_js_1.registerFollow)(program);
37
39
  program.parse();
@@ -120,6 +120,10 @@ ${LOGIN_JIT_GUIDE}
120
120
 
121
121
  ### 5. 완료 안내
122
122
  - 배치된 파일과 활성화된 커맨드 목록을 보여줍니다.
123
+ - CLI 출력에 포함된 **빌더 명함**(💬 환영 메시지, 📇 연락처, 👤 프로필 URL)을 사용자에게 그대로 보여줍니다.
124
+ - **팔로우 제안**: 사용자에게 "@{username}을 팔로우할까요? 새 버전 알림을 받을 수 있습니다. (Y/n)"라고 반드시 물어봅니다.
125
+ - 수락하면: \`relay follow @{username}\` 실행. 로그인이 안 되어 있으면 먼저 \`relay login\` 실행 후 재시도.
126
+ - 거절하면: 건너뜁니다
123
127
  - "바로 사용해볼까요?" 제안
124
128
 
125
129
  ## 예시
@@ -169,6 +173,7 @@ ${LOGIN_JIT_GUIDE}
169
173
  ### 특정 팀 업데이트
170
174
  - 사용자가 팀 이름을 지정한 경우: \`relay update <@author/slug> --json\` 실행
171
175
  - 업데이트 결과를 보여줍니다 (이전 버전 → 새 버전)
176
+ - CLI 출력에 포함된 **빌더 명함**(💬 환영 메시지, 📇 연락처, 👤 프로필 URL)을 사용자에게 그대로 보여줍니다.
172
177
 
173
178
  ### 전체 업데이트 확인
174
179
  - 팀 이름을 지정하지 않은 경우:
@@ -282,7 +287,7 @@ requires:
282
287
  config:
283
288
  command: "npx"
284
289
  args: ["-y", "@supabase/mcp-server"]
285
- env: [SUPABASE_URL, SUPABASE_SERVICE_ROLE_KEY]
290
+ env: [SUPABASE_URL, SUPABASE_SECRET_KEY]
286
291
  - name: notion
287
292
  package: "@notionhq/mcp-server"
288
293
  required: false
@@ -368,6 +373,7 @@ portfolio:
368
373
  ### 7. 배포
369
374
  - \`relay publish\` 명령어를 실행합니다.
370
375
  - 배포 결과와 마켓플레이스 URL을 보여줍니다.
376
+ - CLI 출력에 포함된 **빌더 명함 미리보기**(💬 환영 메시지, 📇 연락처, 👤 프로필 URL)를 사용자에게 그대로 보여줍니다.
371
377
 
372
378
  ## 예시
373
379
 
@@ -0,0 +1,7 @@
1
+ import type { ContactItem } from '../types.js';
2
+ /**
3
+ * contact_links를 "key: value" 쌍의 배열로 정규화한다.
4
+ * - 배열 형식 (신규): [{type, label, value}, ...] → ["label: value", ...]
5
+ * - 객체 형식 (레거시): {email: "a@b"} → ["email: a@b", ...]
6
+ */
7
+ export declare function formatContactParts(contactLinks: ContactItem[] | Record<string, string> | unknown): string[];
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatContactParts = formatContactParts;
4
+ /**
5
+ * contact_links를 "key: value" 쌍의 배열로 정규화한다.
6
+ * - 배열 형식 (신규): [{type, label, value}, ...] → ["label: value", ...]
7
+ * - 객체 형식 (레거시): {email: "a@b"} → ["email: a@b", ...]
8
+ */
9
+ function formatContactParts(contactLinks) {
10
+ if (!contactLinks)
11
+ return [];
12
+ if (Array.isArray(contactLinks)) {
13
+ return contactLinks
14
+ .filter((c) => c.value?.trim())
15
+ .map((c) => `${c.label || c.type}: ${c.value}`);
16
+ }
17
+ if (typeof contactLinks === 'object') {
18
+ return Object.entries(contactLinks)
19
+ .filter(([, v]) => typeof v === 'string' && v.trim())
20
+ .map(([k, v]) => `${k}: ${v}`);
21
+ }
22
+ return [];
23
+ }
package/dist/types.d.ts CHANGED
@@ -1,3 +1,8 @@
1
+ export interface ContactItem {
2
+ type: string;
3
+ label: string;
4
+ value: string;
5
+ }
1
6
  export interface InstalledTeam {
2
7
  version: string;
3
8
  installed_at: string;
@@ -31,7 +36,7 @@ export interface TeamRegistryInfo {
31
36
  author?: {
32
37
  username: string;
33
38
  display_name: string | null;
34
- contact_links: Record<string, string>;
39
+ contact_links: ContactItem[] | Record<string, string>;
35
40
  } | null;
36
41
  latest_post?: {
37
42
  title: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "relayax-cli",
3
- "version": "0.1.998",
3
+ "version": "0.2.12",
4
4
  "description": "RelayAX Agent Team Marketplace CLI - Install and manage agent teams",
5
5
  "main": "dist/index.js",
6
6
  "bin": {