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.
- package/dist/commands/follow.d.ts +2 -0
- package/dist/commands/follow.js +45 -0
- package/dist/commands/install.js +5 -24
- package/dist/commands/publish.js +5 -6
- package/dist/commands/update.js +5 -6
- package/dist/index.js +2 -0
- package/dist/lib/command-adapter.js +7 -1
- package/dist/lib/contact-format.d.ts +7 -0
- package/dist/lib/contact-format.js +23 -0
- package/dist/types.d.ts +6 -1
- package/package.json +1 -1
|
@@ -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
|
+
}
|
package/dist/commands/install.js
CHANGED
|
@@ -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
|
|
105
|
-
const
|
|
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 (
|
|
114
|
-
|
|
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
|
}
|
package/dist/commands/publish.js
CHANGED
|
@@ -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
|
|
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 (
|
|
447
|
-
|
|
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 &&
|
|
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`);
|
package/dist/commands/update.js
CHANGED
|
@@ -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
|
|
102
|
-
const
|
|
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 (
|
|
111
|
-
|
|
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,
|
|
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;
|