relayax-cli 0.1.8 → 0.1.9
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/install.js +24 -6
- package/dist/commands/outdated.d.ts +2 -0
- package/dist/commands/outdated.js +70 -0
- package/dist/commands/publish.d.ts +7 -0
- package/dist/commands/publish.js +118 -6
- package/dist/commands/update.d.ts +2 -0
- package/dist/commands/update.js +93 -0
- package/dist/index.js +4 -0
- package/dist/lib/api.d.ts +7 -1
- package/dist/lib/api.js +13 -2
- package/dist/lib/command-adapter.js +16 -5
- package/dist/types.d.ts +1 -0
- package/package.json +1 -1
package/dist/commands/install.js
CHANGED
|
@@ -10,6 +10,7 @@ function registerInstall(program) {
|
|
|
10
10
|
.command('install <slug>')
|
|
11
11
|
.description('에이전트 팀 설치 (감지된 에이전트 CLI에 설치)')
|
|
12
12
|
.option('--path <install_path>', '설치 경로 지정 (기본: ./.claude)')
|
|
13
|
+
.option('--code <code>', '초대 코드 (invite-only 팀 설치 시 필요)')
|
|
13
14
|
.action(async (slug, opts) => {
|
|
14
15
|
const json = program.opts().json ?? false;
|
|
15
16
|
const installPath = (0, config_js_1.getInstallPath)(opts.path);
|
|
@@ -17,14 +18,31 @@ function registerInstall(program) {
|
|
|
17
18
|
try {
|
|
18
19
|
// 1. Fetch team metadata
|
|
19
20
|
const team = await (0, api_js_1.fetchTeamInfo)(slug);
|
|
20
|
-
// 2.
|
|
21
|
+
// 2. Visibility check
|
|
22
|
+
const visibility = team.visibility ?? 'public';
|
|
23
|
+
if (visibility === 'login-only') {
|
|
24
|
+
const token = (0, config_js_1.loadToken)();
|
|
25
|
+
if (!token) {
|
|
26
|
+
const err = { error: 'LOGIN_REQUIRED', visibility: 'login-only', slug, message: '이 팀은 로그인이 필요합니다.' };
|
|
27
|
+
console.error(JSON.stringify(err));
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
else if (visibility === 'invite-only') {
|
|
32
|
+
if (!opts.code) {
|
|
33
|
+
const err = { error: 'INVITE_REQUIRED', visibility: 'invite-only', slug, message: '초대 코드가 필요합니다.' };
|
|
34
|
+
console.error(JSON.stringify(err));
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// 3. Download package
|
|
21
39
|
const tarPath = await (0, storage_js_1.downloadPackage)(team.package_url, tempDir);
|
|
22
|
-
//
|
|
40
|
+
// 4. Extract
|
|
23
41
|
const extractDir = `${tempDir}/extracted`;
|
|
24
42
|
await (0, storage_js_1.extractPackage)(tarPath, extractDir);
|
|
25
|
-
//
|
|
43
|
+
// 5. Copy files to install_path
|
|
26
44
|
const files = (0, installer_js_1.installTeam)(extractDir, installPath);
|
|
27
|
-
//
|
|
45
|
+
// 6. Record in installed.json
|
|
28
46
|
const installed = (0, config_js_1.loadInstalled)();
|
|
29
47
|
installed[slug] = {
|
|
30
48
|
version: team.version,
|
|
@@ -32,8 +50,8 @@ function registerInstall(program) {
|
|
|
32
50
|
files,
|
|
33
51
|
};
|
|
34
52
|
(0, config_js_1.saveInstalled)(installed);
|
|
35
|
-
//
|
|
36
|
-
await (0, api_js_1.reportInstall)(slug);
|
|
53
|
+
// 7. Report install (non-blocking)
|
|
54
|
+
await (0, api_js_1.reportInstall)(slug, opts.code);
|
|
37
55
|
const result = {
|
|
38
56
|
status: 'ok',
|
|
39
57
|
team: team.name,
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerOutdated = registerOutdated;
|
|
4
|
+
const api_js_1 = require("../lib/api.js");
|
|
5
|
+
const config_js_1 = require("../lib/config.js");
|
|
6
|
+
function registerOutdated(program) {
|
|
7
|
+
program
|
|
8
|
+
.command('outdated')
|
|
9
|
+
.description('설치된 팀의 업데이트 가능 여부를 확인합니다')
|
|
10
|
+
.action(async () => {
|
|
11
|
+
const json = program.opts().json ?? false;
|
|
12
|
+
const installed = (0, config_js_1.loadInstalled)();
|
|
13
|
+
const slugs = Object.keys(installed);
|
|
14
|
+
if (slugs.length === 0) {
|
|
15
|
+
if (json) {
|
|
16
|
+
console.log(JSON.stringify([]));
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
console.log('설치된 팀이 없습니다.');
|
|
20
|
+
}
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
// Fetch latest versions in parallel
|
|
24
|
+
const results = await Promise.all(slugs.map(async (slug) => {
|
|
25
|
+
const current = installed[slug].version;
|
|
26
|
+
try {
|
|
27
|
+
const team = await (0, api_js_1.fetchTeamInfo)(slug);
|
|
28
|
+
const latest = team.version;
|
|
29
|
+
return {
|
|
30
|
+
slug,
|
|
31
|
+
current,
|
|
32
|
+
latest,
|
|
33
|
+
status: current === latest ? 'up-to-date' : 'outdated',
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return { slug, current, latest: '?', status: 'unknown' };
|
|
38
|
+
}
|
|
39
|
+
}));
|
|
40
|
+
if (json) {
|
|
41
|
+
console.log(JSON.stringify(results));
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const allUpToDate = results.every((r) => r.status === 'up-to-date');
|
|
45
|
+
if (allUpToDate) {
|
|
46
|
+
console.log('모든 팀이 최신 버전입니다.');
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
// Determine column widths
|
|
50
|
+
const COL_TEAM = Math.max(4, ...results.map((r) => r.slug.length));
|
|
51
|
+
const COL_CURRENT = Math.max(4, ...results.map((r) => `v${r.current}`.length));
|
|
52
|
+
const COL_LATEST = Math.max(4, ...results.map((r) => `v${r.latest}`.length));
|
|
53
|
+
const pad = (s, len) => s.padEnd(len);
|
|
54
|
+
const header = `${pad('팀', COL_TEAM)} ${pad('현재', COL_CURRENT)} ${pad('최신', COL_LATEST)} 상태`;
|
|
55
|
+
const separator = '-'.repeat(header.length);
|
|
56
|
+
console.log(header);
|
|
57
|
+
console.log(separator);
|
|
58
|
+
for (const entry of results) {
|
|
59
|
+
const statusLabel = entry.status === 'outdated'
|
|
60
|
+
? '\x1b[33m업데이트 가능\x1b[0m'
|
|
61
|
+
: entry.status === 'up-to-date'
|
|
62
|
+
? '\x1b[32m✓ 최신\x1b[0m'
|
|
63
|
+
: '\x1b[31m조회 실패\x1b[0m';
|
|
64
|
+
const slugCol = pad(entry.slug, COL_TEAM);
|
|
65
|
+
const currentCol = pad(`v${entry.current}`, COL_CURRENT);
|
|
66
|
+
const latestCol = pad(`v${entry.latest}`, COL_LATEST);
|
|
67
|
+
console.log(`${slugCol} ${currentCol} ${latestCol} ${statusLabel}`);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
@@ -19,4 +19,11 @@ export interface Requires {
|
|
|
19
19
|
env?: RequiresEnv[];
|
|
20
20
|
teams?: string[];
|
|
21
21
|
}
|
|
22
|
+
export interface ContactInfo {
|
|
23
|
+
email?: string;
|
|
24
|
+
kakao?: string;
|
|
25
|
+
x?: string;
|
|
26
|
+
linkedin?: string;
|
|
27
|
+
website?: string;
|
|
28
|
+
}
|
|
22
29
|
export declare function registerPublish(program: Command): void;
|
package/dist/commands/publish.js
CHANGED
|
@@ -25,15 +25,25 @@ function parseRelayYaml(content) {
|
|
|
25
25
|
})).filter((p) => p.path)
|
|
26
26
|
: [];
|
|
27
27
|
const requires = raw.requires;
|
|
28
|
+
const rawVisibility = String(raw.visibility ?? '');
|
|
29
|
+
const visibility = rawVisibility === 'login-only' ? 'login-only'
|
|
30
|
+
: rawVisibility === 'invite-only' ? 'invite-only'
|
|
31
|
+
: rawVisibility === 'public' ? 'public'
|
|
32
|
+
: undefined;
|
|
28
33
|
return {
|
|
29
34
|
name: String(raw.name ?? ''),
|
|
30
35
|
slug: String(raw.slug ?? ''),
|
|
31
36
|
description: String(raw.description ?? ''),
|
|
32
37
|
version: String(raw.version ?? '1.0.0'),
|
|
38
|
+
changelog: raw.changelog ? String(raw.changelog) : undefined,
|
|
33
39
|
long_description: raw.long_description ? String(raw.long_description) : undefined,
|
|
34
40
|
tags,
|
|
35
41
|
portfolio,
|
|
36
42
|
requires,
|
|
43
|
+
welcome: raw.welcome ? String(raw.welcome) : undefined,
|
|
44
|
+
contact: raw.contact,
|
|
45
|
+
visibility,
|
|
46
|
+
invite_code: raw.invite_code ? String(raw.invite_code) : undefined,
|
|
37
47
|
};
|
|
38
48
|
}
|
|
39
49
|
function detectCommands(teamDir) {
|
|
@@ -173,13 +183,106 @@ function registerPublish(program) {
|
|
|
173
183
|
const json = program.opts().json ?? false;
|
|
174
184
|
const teamDir = process.cwd();
|
|
175
185
|
const relayYamlPath = path_1.default.join(teamDir, 'relay.yaml');
|
|
186
|
+
const isTTY = Boolean(process.stdin.isTTY) && !json;
|
|
176
187
|
// Check relay.yaml exists
|
|
177
188
|
if (!fs_1.default.existsSync(relayYamlPath)) {
|
|
178
|
-
|
|
179
|
-
error
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
189
|
+
if (!isTTY) {
|
|
190
|
+
console.error(JSON.stringify({
|
|
191
|
+
error: 'NOT_INITIALIZED',
|
|
192
|
+
message: 'relay.yaml이 없습니다. 먼저 `relay init`을 실행하세요.',
|
|
193
|
+
}));
|
|
194
|
+
process.exit(1);
|
|
195
|
+
}
|
|
196
|
+
// Interactive onboarding: create relay.yaml
|
|
197
|
+
const { input: promptInput, select: promptSelect, confirm: promptConfirm } = await import('@inquirer/prompts');
|
|
198
|
+
const dirName = path_1.default.basename(teamDir);
|
|
199
|
+
const defaultSlug = dirName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
|
|
200
|
+
console.error('\n\x1b[36m릴레이 팀 패키지를 초기화합니다.\x1b[0m');
|
|
201
|
+
console.error('relay.yaml을 생성하기 위해 몇 가지 정보를 입력해주세요.\n');
|
|
202
|
+
const name = await promptInput({
|
|
203
|
+
message: '팀 이름:',
|
|
204
|
+
default: dirName,
|
|
205
|
+
});
|
|
206
|
+
const slug = await promptInput({
|
|
207
|
+
message: '슬러그 (URL에 사용되는 고유 식별자):',
|
|
208
|
+
default: defaultSlug,
|
|
209
|
+
});
|
|
210
|
+
const description = await promptInput({
|
|
211
|
+
message: '팀 설명 (필수):',
|
|
212
|
+
validate: (v) => v.trim().length > 0 ? true : '설명을 입력해주세요.',
|
|
213
|
+
});
|
|
214
|
+
const tagsRaw = await promptInput({
|
|
215
|
+
message: '태그 (쉼표로 구분, 선택):',
|
|
216
|
+
default: '',
|
|
217
|
+
});
|
|
218
|
+
const visibility = await promptSelect({
|
|
219
|
+
message: '공개 범위:',
|
|
220
|
+
choices: [
|
|
221
|
+
{ name: '전체 공개', value: 'public' },
|
|
222
|
+
{ name: '로그인 사용자만', value: 'login-only' },
|
|
223
|
+
{ name: '초대 코드 필요', value: 'invite-only' },
|
|
224
|
+
],
|
|
225
|
+
});
|
|
226
|
+
let invite_code;
|
|
227
|
+
if (visibility === 'invite-only') {
|
|
228
|
+
invite_code = await promptInput({
|
|
229
|
+
message: '초대 코드:',
|
|
230
|
+
validate: (v) => v.trim().length > 0 ? true : '초대 코드를 입력해주세요.',
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
// Welcome message section
|
|
234
|
+
console.error('\n\x1b[33m┌─────────────────────────────────────────────────────────────┐\x1b[0m');
|
|
235
|
+
console.error('\x1b[33m│ welcome 메시지란? │\x1b[0m');
|
|
236
|
+
console.error('\x1b[33m│ 설치할 때마다 이 메시지와 연락처가 설치자에게 전달됩니다. │\x1b[0m');
|
|
237
|
+
console.error('\x1b[33m│ (설치 = 명함 전달) │\x1b[0m');
|
|
238
|
+
console.error('\x1b[33m└─────────────────────────────────────────────────────────────┘\x1b[0m\n');
|
|
239
|
+
const wantsWelcome = await promptConfirm({
|
|
240
|
+
message: 'welcome 메시지를 작성하시겠습니까?',
|
|
241
|
+
default: false,
|
|
242
|
+
});
|
|
243
|
+
let welcome;
|
|
244
|
+
let contactEmail;
|
|
245
|
+
let contactKakao;
|
|
246
|
+
if (wantsWelcome) {
|
|
247
|
+
welcome = await promptInput({
|
|
248
|
+
message: 'welcome 메시지:',
|
|
249
|
+
validate: (v) => v.trim().length > 0 ? true : '메시지를 입력해주세요.',
|
|
250
|
+
});
|
|
251
|
+
contactEmail = await promptInput({
|
|
252
|
+
message: '이메일 (선택):',
|
|
253
|
+
default: '',
|
|
254
|
+
});
|
|
255
|
+
contactKakao = await promptInput({
|
|
256
|
+
message: '카카오 오픈채팅 링크 (선택):',
|
|
257
|
+
default: '',
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
const tags = tagsRaw
|
|
261
|
+
.split(',')
|
|
262
|
+
.map((t) => t.trim())
|
|
263
|
+
.filter(Boolean);
|
|
264
|
+
const yamlData = {
|
|
265
|
+
name,
|
|
266
|
+
slug,
|
|
267
|
+
description,
|
|
268
|
+
version: '1.0.0',
|
|
269
|
+
tags,
|
|
270
|
+
visibility,
|
|
271
|
+
};
|
|
272
|
+
if (invite_code)
|
|
273
|
+
yamlData.invite_code = invite_code;
|
|
274
|
+
if (welcome)
|
|
275
|
+
yamlData.welcome = welcome;
|
|
276
|
+
if (contactEmail || contactKakao) {
|
|
277
|
+
const contact = {};
|
|
278
|
+
if (contactEmail)
|
|
279
|
+
contact.email = contactEmail;
|
|
280
|
+
if (contactKakao)
|
|
281
|
+
contact.kakao = contactKakao;
|
|
282
|
+
yamlData.contact = contact;
|
|
283
|
+
}
|
|
284
|
+
fs_1.default.writeFileSync(relayYamlPath, js_yaml_1.default.dump(yamlData, { lineWidth: 120 }), 'utf-8');
|
|
285
|
+
console.error(`\n\x1b[32m✓ relay.yaml이 생성되었습니다.\x1b[0m\n`);
|
|
183
286
|
}
|
|
184
287
|
// Parse relay.yaml
|
|
185
288
|
const yamlContent = fs_1.default.readFileSync(relayYamlPath, 'utf-8');
|
|
@@ -191,6 +294,10 @@ function registerPublish(program) {
|
|
|
191
294
|
}));
|
|
192
295
|
process.exit(1);
|
|
193
296
|
}
|
|
297
|
+
// Welcome hint when welcome field is missing and TTY is available
|
|
298
|
+
if (isTTY && !config.welcome) {
|
|
299
|
+
console.error('💡 welcome 메시지를 추가하면 설치할 때마다 명함이 전달됩니다. relay.yaml에 welcome 필드를 추가해보세요.');
|
|
300
|
+
}
|
|
194
301
|
// Validate structure
|
|
195
302
|
const hasDirs = VALID_DIRS.some((d) => {
|
|
196
303
|
const dirPath = path_1.default.join(teamDir, d);
|
|
@@ -205,7 +312,7 @@ function registerPublish(program) {
|
|
|
205
312
|
}));
|
|
206
313
|
process.exit(1);
|
|
207
314
|
}
|
|
208
|
-
// Get token
|
|
315
|
+
// Get token (checked before tarball creation)
|
|
209
316
|
const token = opts.token ?? process.env.RELAY_TOKEN ?? (0, config_js_1.loadToken)();
|
|
210
317
|
if (!token) {
|
|
211
318
|
console.error(JSON.stringify({
|
|
@@ -232,7 +339,12 @@ function registerPublish(program) {
|
|
|
232
339
|
commands: detectedCommands,
|
|
233
340
|
components,
|
|
234
341
|
version: config.version,
|
|
342
|
+
changelog: config.changelog,
|
|
235
343
|
requires: config.requires,
|
|
344
|
+
welcome: config.welcome,
|
|
345
|
+
contact: config.contact,
|
|
346
|
+
visibility: config.visibility,
|
|
347
|
+
invite_code: config.invite_code,
|
|
236
348
|
};
|
|
237
349
|
if (!json) {
|
|
238
350
|
console.error(`패키지 생성 중... (${config.name} v${config.version})`);
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerUpdate = registerUpdate;
|
|
4
|
+
const api_js_1 = require("../lib/api.js");
|
|
5
|
+
const storage_js_1 = require("../lib/storage.js");
|
|
6
|
+
const installer_js_1 = require("../lib/installer.js");
|
|
7
|
+
const config_js_1 = require("../lib/config.js");
|
|
8
|
+
function registerUpdate(program) {
|
|
9
|
+
program
|
|
10
|
+
.command('update <slug>')
|
|
11
|
+
.description('설치된 에이전트 팀을 최신 버전으로 업데이트합니다')
|
|
12
|
+
.option('--path <install_path>', '설치 경로 지정 (기본: ./.claude)')
|
|
13
|
+
.option('--code <code>', '초대 코드 (invite-only 팀 업데이트 시 필요)')
|
|
14
|
+
.action(async (slug, opts) => {
|
|
15
|
+
const json = program.opts().json ?? false;
|
|
16
|
+
const installPath = (0, config_js_1.getInstallPath)(opts.path);
|
|
17
|
+
const tempDir = (0, storage_js_1.makeTempDir)();
|
|
18
|
+
try {
|
|
19
|
+
// Check installed.json for current version
|
|
20
|
+
const installed = (0, config_js_1.loadInstalled)();
|
|
21
|
+
const currentEntry = installed[slug];
|
|
22
|
+
const currentVersion = currentEntry?.version ?? null;
|
|
23
|
+
// Fetch latest team metadata
|
|
24
|
+
const team = await (0, api_js_1.fetchTeamInfo)(slug);
|
|
25
|
+
const latestVersion = team.version;
|
|
26
|
+
if (currentVersion && currentVersion === latestVersion) {
|
|
27
|
+
if (json) {
|
|
28
|
+
console.log(JSON.stringify({ status: 'up-to-date', slug, version: latestVersion }));
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
console.log(`이미 최신 버전입니다 (${slug} v${latestVersion})`);
|
|
32
|
+
}
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
// Visibility check
|
|
36
|
+
const visibility = team.visibility ?? 'public';
|
|
37
|
+
if (visibility === 'login-only') {
|
|
38
|
+
const token = (0, config_js_1.loadToken)();
|
|
39
|
+
if (!token) {
|
|
40
|
+
console.error('이 팀은 로그인이 필요합니다. `relay login`을 먼저 실행하세요.');
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
else if (visibility === 'invite-only') {
|
|
45
|
+
if (!opts.code) {
|
|
46
|
+
console.error('초대 코드가 필요합니다. `relay update ' + slug + ' --code <code>`로 업데이트하세요.');
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// Download package
|
|
51
|
+
const tarPath = await (0, storage_js_1.downloadPackage)(team.package_url, tempDir);
|
|
52
|
+
// Extract
|
|
53
|
+
const extractDir = `${tempDir}/extracted`;
|
|
54
|
+
await (0, storage_js_1.extractPackage)(tarPath, extractDir);
|
|
55
|
+
// Copy files to install_path
|
|
56
|
+
const files = (0, installer_js_1.installTeam)(extractDir, installPath);
|
|
57
|
+
// Update installed.json with new version
|
|
58
|
+
installed[slug] = {
|
|
59
|
+
version: latestVersion,
|
|
60
|
+
installed_at: new Date().toISOString(),
|
|
61
|
+
files,
|
|
62
|
+
};
|
|
63
|
+
(0, config_js_1.saveInstalled)(installed);
|
|
64
|
+
// Report install (non-blocking)
|
|
65
|
+
await (0, api_js_1.reportInstall)(slug, opts.code);
|
|
66
|
+
const result = {
|
|
67
|
+
status: 'updated',
|
|
68
|
+
slug,
|
|
69
|
+
from_version: currentVersion,
|
|
70
|
+
version: latestVersion,
|
|
71
|
+
files_installed: files.length,
|
|
72
|
+
install_path: installPath,
|
|
73
|
+
};
|
|
74
|
+
if (json) {
|
|
75
|
+
console.log(JSON.stringify(result));
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
const fromLabel = currentVersion ? `v${currentVersion} → ` : '';
|
|
79
|
+
console.log(`\n\x1b[32m✓ ${team.name} ${fromLabel}v${latestVersion} 업데이트 완료\x1b[0m`);
|
|
80
|
+
console.log(` 설치 위치: \x1b[36m${installPath}\x1b[0m`);
|
|
81
|
+
console.log(` 파일 수: ${files.length}개`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
86
|
+
console.error(JSON.stringify({ error: 'UPDATE_FAILED', message }));
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
finally {
|
|
90
|
+
(0, storage_js_1.removeTempDir)(tempDir);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -9,6 +9,8 @@ const list_js_1 = require("./commands/list.js");
|
|
|
9
9
|
const uninstall_js_1 = require("./commands/uninstall.js");
|
|
10
10
|
const publish_js_1 = require("./commands/publish.js");
|
|
11
11
|
const login_js_1 = require("./commands/login.js");
|
|
12
|
+
const update_js_1 = require("./commands/update.js");
|
|
13
|
+
const outdated_js_1 = require("./commands/outdated.js");
|
|
12
14
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
13
15
|
const pkg = require('../package.json');
|
|
14
16
|
const program = new commander_1.Command();
|
|
@@ -24,4 +26,6 @@ program
|
|
|
24
26
|
(0, uninstall_js_1.registerUninstall)(program);
|
|
25
27
|
(0, publish_js_1.registerPublish)(program);
|
|
26
28
|
(0, login_js_1.registerLogin)(program);
|
|
29
|
+
(0, update_js_1.registerUpdate)(program);
|
|
30
|
+
(0, outdated_js_1.registerOutdated)(program);
|
|
27
31
|
program.parse();
|
package/dist/lib/api.d.ts
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import type { TeamRegistryInfo, SearchResult } from '../types.js';
|
|
2
2
|
export declare function fetchTeamInfo(slug: string): Promise<TeamRegistryInfo>;
|
|
3
3
|
export declare function searchTeams(query: string, tag?: string): Promise<SearchResult[]>;
|
|
4
|
-
export
|
|
4
|
+
export interface TeamVersionInfo {
|
|
5
|
+
version: string;
|
|
6
|
+
changelog: string | null;
|
|
7
|
+
created_at: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function fetchTeamVersions(slug: string): Promise<TeamVersionInfo[]>;
|
|
10
|
+
export declare function reportInstall(slug: string, code?: string): Promise<void>;
|
package/dist/lib/api.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.fetchTeamInfo = fetchTeamInfo;
|
|
4
4
|
exports.searchTeams = searchTeams;
|
|
5
|
+
exports.fetchTeamVersions = fetchTeamVersions;
|
|
5
6
|
exports.reportInstall = reportInstall;
|
|
6
7
|
const config_js_1 = require("./config.js");
|
|
7
8
|
async function fetchTeamInfo(slug) {
|
|
@@ -26,8 +27,18 @@ async function searchTeams(query, tag) {
|
|
|
26
27
|
const data = (await res.json());
|
|
27
28
|
return data.results;
|
|
28
29
|
}
|
|
29
|
-
async function
|
|
30
|
-
const url = `${config_js_1.API_URL}/api/registry/${slug}/
|
|
30
|
+
async function fetchTeamVersions(slug) {
|
|
31
|
+
const url = `${config_js_1.API_URL}/api/registry/${slug}/versions`;
|
|
32
|
+
const res = await fetch(url);
|
|
33
|
+
if (!res.ok) {
|
|
34
|
+
const body = await res.text();
|
|
35
|
+
throw new Error(`버전 목록 조회 실패 (${res.status}): ${body}`);
|
|
36
|
+
}
|
|
37
|
+
return res.json();
|
|
38
|
+
}
|
|
39
|
+
async function reportInstall(slug, code) {
|
|
40
|
+
const base = `${config_js_1.API_URL}/api/registry/${slug}/install`;
|
|
41
|
+
const url = code ? `${base}?code=${encodeURIComponent(code)}` : base;
|
|
31
42
|
await fetch(url, { method: 'POST' }).catch(() => {
|
|
32
43
|
// non-critical: ignore errors
|
|
33
44
|
});
|
|
@@ -69,8 +69,16 @@ exports.RELAY_COMMANDS = [
|
|
|
69
69
|
- **mcp**: MCP 서버 설정 안내
|
|
70
70
|
- **teams**: \`relay install <team>\`으로 재귀 설치
|
|
71
71
|
|
|
72
|
-
### 3.
|
|
73
|
-
-
|
|
72
|
+
### 3. 제작자 소개 (welcome 메시지)
|
|
73
|
+
- API 응답의 \`welcome\` 필드가 있으면 제작자 메시지를 보여줍니다.
|
|
74
|
+
- \`contact\` 필드가 있으면 연락처도 함께 표시합니다.
|
|
75
|
+
|
|
76
|
+
### 4. 팔로우 제안
|
|
77
|
+
- "이 팀의 제작자 @username을 팔로우할까요?"라고 제안합니다.
|
|
78
|
+
- 인증되어 있으면 POST /api/follows로 팔로우합니다.
|
|
79
|
+
- 미인증이면 \`relay login\` 후 팔로우할 수 있다고 안내합니다.
|
|
80
|
+
|
|
81
|
+
### 5. 완료 안내
|
|
74
82
|
- 사용 가능한 커맨드 안내
|
|
75
83
|
- "바로 사용해볼까요?" 제안
|
|
76
84
|
|
|
@@ -79,9 +87,12 @@ exports.RELAY_COMMANDS = [
|
|
|
79
87
|
사용자: /relay-install contents-team
|
|
80
88
|
→ relay install contents-team 실행
|
|
81
89
|
→ requires 확인: ✓ playwright, ✓ sharp 설치됨
|
|
82
|
-
→
|
|
83
|
-
→
|
|
84
|
-
→
|
|
90
|
+
→ ✓ 설치 완료!
|
|
91
|
+
→ ┌─ @haemin ──────────────────────────────┐
|
|
92
|
+
→ │ contents-team을 설치해주셔서 감사합니다! │
|
|
93
|
+
→ │ 💬 카카오톡 📧 이메일 │
|
|
94
|
+
→ └─────────────────────────────────────────┘
|
|
95
|
+
→ "@haemin을 팔로우할까요?" → Yes → ✓ 팔로우 완료
|
|
85
96
|
→ "바로 /cardnews를 사용해볼까요?"`,
|
|
86
97
|
},
|
|
87
98
|
{
|
package/dist/types.d.ts
CHANGED