relayax-cli 0.2.41 → 0.3.41
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/diff.d.ts +2 -0
- package/dist/commands/diff.js +72 -0
- package/dist/commands/install.js +73 -205
- package/dist/commands/join.d.ts +3 -2
- package/dist/commands/join.js +18 -69
- package/dist/commands/list.js +18 -21
- package/dist/commands/orgs.d.ts +10 -0
- package/dist/commands/orgs.js +128 -0
- package/dist/commands/publish.js +51 -57
- package/dist/commands/search.js +1 -1
- package/dist/commands/versions.d.ts +2 -0
- package/dist/commands/versions.js +44 -0
- package/dist/index.js +6 -2
- package/dist/lib/api.d.ts +7 -6
- package/dist/lib/api.js +14 -29
- package/dist/lib/command-adapter.js +15 -15
- package/dist/lib/config.d.ts +9 -4
- package/dist/lib/config.js +105 -23
- package/dist/lib/preamble.js +3 -4
- package/dist/lib/slug.d.ts +0 -1
- package/dist/lib/slug.js +3 -7
- package/dist/types.d.ts +2 -2
- package/package.json +1 -1
package/dist/commands/list.js
CHANGED
|
@@ -2,30 +2,27 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.registerList = registerList;
|
|
4
4
|
const config_js_1 = require("../lib/config.js");
|
|
5
|
-
async function
|
|
6
|
-
const res = await fetch(`${config_js_1.API_URL}/api/
|
|
5
|
+
async function fetchOrgTeamList(orgSlug, token) {
|
|
6
|
+
const res = await fetch(`${config_js_1.API_URL}/api/orgs/${orgSlug}/teams`, {
|
|
7
7
|
headers: { Authorization: `Bearer ${token}` },
|
|
8
8
|
signal: AbortSignal.timeout(8000),
|
|
9
9
|
});
|
|
10
10
|
if (!res.ok) {
|
|
11
11
|
const body = await res.text();
|
|
12
|
-
throw new Error(`
|
|
12
|
+
throw new Error(`Org 팀 목록 조회 실패 (${res.status}): ${body}`);
|
|
13
13
|
}
|
|
14
|
-
|
|
15
|
-
if (Array.isArray(data))
|
|
16
|
-
return data;
|
|
17
|
-
return data.teams ?? [];
|
|
14
|
+
return (await res.json());
|
|
18
15
|
}
|
|
19
16
|
function registerList(program) {
|
|
20
17
|
program
|
|
21
18
|
.command('list')
|
|
22
19
|
.description('설치된 에이전트 팀 목록')
|
|
23
|
-
.option('--
|
|
20
|
+
.option('--org <slug>', 'Organization 팀 목록 조회')
|
|
24
21
|
.action(async (opts) => {
|
|
25
22
|
const json = program.opts().json ?? false;
|
|
26
|
-
// --
|
|
27
|
-
if (opts.
|
|
28
|
-
const
|
|
23
|
+
// --org 옵션: Org 팀 목록
|
|
24
|
+
if (opts.org) {
|
|
25
|
+
const orgSlug = opts.org;
|
|
29
26
|
const token = await (0, config_js_1.getValidToken)();
|
|
30
27
|
if (!token) {
|
|
31
28
|
if (json) {
|
|
@@ -38,23 +35,23 @@ function registerList(program) {
|
|
|
38
35
|
process.exit(1);
|
|
39
36
|
}
|
|
40
37
|
try {
|
|
41
|
-
const teams = await
|
|
38
|
+
const teams = await fetchOrgTeamList(orgSlug, token);
|
|
42
39
|
if (json) {
|
|
43
|
-
console.log(JSON.stringify({
|
|
40
|
+
console.log(JSON.stringify({ org: orgSlug, teams }));
|
|
44
41
|
return;
|
|
45
42
|
}
|
|
46
43
|
if (teams.length === 0) {
|
|
47
|
-
console.log(`\n
|
|
44
|
+
console.log(`\n@${orgSlug} Organization에 팀이 없습니다.`);
|
|
48
45
|
return;
|
|
49
46
|
}
|
|
50
|
-
console.log(`\n\x1b[1m
|
|
47
|
+
console.log(`\n\x1b[1m@${orgSlug} 팀 목록\x1b[0m (${teams.length}개):\n`);
|
|
51
48
|
for (const t of teams) {
|
|
52
49
|
const desc = t.description
|
|
53
50
|
? ` \x1b[90m${t.description.length > 50 ? t.description.slice(0, 50) + '...' : t.description}\x1b[0m`
|
|
54
51
|
: '';
|
|
55
|
-
console.log(` \x1b[36m
|
|
52
|
+
console.log(` \x1b[36m@${t.owner}/${t.slug}\x1b[0m \x1b[1m${t.name}\x1b[0m${desc}`);
|
|
56
53
|
}
|
|
57
|
-
console.log(`\n\x1b[33m
|
|
54
|
+
console.log(`\n\x1b[33m 설치: relay install @${orgSlug}/<팀슬러그>\x1b[0m`);
|
|
58
55
|
}
|
|
59
56
|
catch (err) {
|
|
60
57
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -80,7 +77,7 @@ function registerList(program) {
|
|
|
80
77
|
installed_at: info.installed_at,
|
|
81
78
|
scope: 'global',
|
|
82
79
|
deploy_scope: info.deploy_scope,
|
|
83
|
-
|
|
80
|
+
org_slug: info.org_slug,
|
|
84
81
|
});
|
|
85
82
|
seen.add(slug);
|
|
86
83
|
}
|
|
@@ -94,7 +91,7 @@ function registerList(program) {
|
|
|
94
91
|
installed_at: info.installed_at,
|
|
95
92
|
scope: 'local',
|
|
96
93
|
deploy_scope: info.deploy_scope,
|
|
97
|
-
|
|
94
|
+
org_slug: info.org_slug,
|
|
98
95
|
});
|
|
99
96
|
}
|
|
100
97
|
if (json) {
|
|
@@ -113,8 +110,8 @@ function registerList(program) {
|
|
|
113
110
|
: item.deploy_scope === 'local'
|
|
114
111
|
? '\x1b[33m로컬\x1b[0m'
|
|
115
112
|
: '\x1b[90m미배치\x1b[0m';
|
|
116
|
-
const
|
|
117
|
-
console.log(` \x1b[36m${item.slug}\x1b[0m v${item.version} ${scopeLabel} (${date})${
|
|
113
|
+
const orgLabel = item.org_slug ? ` \x1b[90m[Org: ${item.org_slug}]\x1b[0m` : '';
|
|
114
|
+
console.log(` \x1b[36m${item.slug}\x1b[0m v${item.version} ${scopeLabel} (${date})${orgLabel}`);
|
|
118
115
|
}
|
|
119
116
|
}
|
|
120
117
|
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
export interface OrgInfo {
|
|
3
|
+
id: string;
|
|
4
|
+
slug: string;
|
|
5
|
+
name: string;
|
|
6
|
+
description: string | null;
|
|
7
|
+
role: string;
|
|
8
|
+
}
|
|
9
|
+
export declare function fetchMyOrgs(token: string): Promise<OrgInfo[]>;
|
|
10
|
+
export declare function registerOrgs(program: Command): void;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fetchMyOrgs = fetchMyOrgs;
|
|
4
|
+
exports.registerOrgs = registerOrgs;
|
|
5
|
+
const config_js_1 = require("../lib/config.js");
|
|
6
|
+
async function fetchMyOrgs(token) {
|
|
7
|
+
const res = await fetch(`${config_js_1.API_URL}/api/orgs`, {
|
|
8
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
9
|
+
signal: AbortSignal.timeout(8000),
|
|
10
|
+
});
|
|
11
|
+
if (!res.ok) {
|
|
12
|
+
throw new Error(`Organization 목록 조회 실패 (${res.status})`);
|
|
13
|
+
}
|
|
14
|
+
return (await res.json());
|
|
15
|
+
}
|
|
16
|
+
function registerOrgs(program) {
|
|
17
|
+
const orgsCmd = program
|
|
18
|
+
.command('orgs')
|
|
19
|
+
.description('Organization 관련 명령어');
|
|
20
|
+
orgsCmd
|
|
21
|
+
.command('list')
|
|
22
|
+
.description('내 Organization 목록을 확인합니다')
|
|
23
|
+
.action(async () => {
|
|
24
|
+
const json = program.opts().json ?? false;
|
|
25
|
+
const token = await (0, config_js_1.getValidToken)();
|
|
26
|
+
if (!token) {
|
|
27
|
+
if (json) {
|
|
28
|
+
console.error(JSON.stringify({ error: 'LOGIN_REQUIRED', message: '로그인이 필요합니다.', fix: 'relay login 실행 후 재시도하세요.' }));
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
console.error('\x1b[31m오류: 로그인이 필요합니다.\x1b[0m');
|
|
32
|
+
console.error(' relay login을 먼저 실행하세요.');
|
|
33
|
+
}
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
const orgs = await fetchMyOrgs(token);
|
|
38
|
+
if (json) {
|
|
39
|
+
console.log(JSON.stringify({ orgs }));
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (orgs.length === 0) {
|
|
43
|
+
console.log('\nOrganization이 없습니다.');
|
|
44
|
+
console.log('\x1b[33m Organization을 만들려면: relay orgs create "이름"\x1b[0m');
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
console.log(`\n\x1b[1m내 Organization\x1b[0m (${orgs.length}개):\n`);
|
|
48
|
+
for (const o of orgs) {
|
|
49
|
+
const role = o.role === 'owner' ? '\x1b[33m오너\x1b[0m'
|
|
50
|
+
: o.role === 'admin' ? '\x1b[36m관리자\x1b[0m'
|
|
51
|
+
: o.role === 'builder' ? '\x1b[36m빌더\x1b[0m'
|
|
52
|
+
: '\x1b[90m멤버\x1b[0m';
|
|
53
|
+
const desc = o.description
|
|
54
|
+
? ` \x1b[90m${o.description.length > 40 ? o.description.slice(0, 40) + '...' : o.description}\x1b[0m`
|
|
55
|
+
: '';
|
|
56
|
+
console.log(` \x1b[36m@${o.slug}\x1b[0m \x1b[1m${o.name}\x1b[0m ${role}${desc}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
62
|
+
if (json) {
|
|
63
|
+
console.error(JSON.stringify({ error: 'FETCH_FAILED', message, fix: '네트워크 연결을 확인하거나 잠시 후 재시도하세요.' }));
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
console.error(`\x1b[31m오류: ${message}\x1b[0m`);
|
|
67
|
+
}
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
orgsCmd
|
|
72
|
+
.command('create <name>')
|
|
73
|
+
.description('새 Organization을 생성합니다')
|
|
74
|
+
.option('--slug <slug>', 'URL slug (미지정 시 이름에서 자동 생성)')
|
|
75
|
+
.action(async (name, opts) => {
|
|
76
|
+
const json = program.opts().json ?? false;
|
|
77
|
+
const token = await (0, config_js_1.getValidToken)();
|
|
78
|
+
if (!token) {
|
|
79
|
+
if (json) {
|
|
80
|
+
console.error(JSON.stringify({ error: 'LOGIN_REQUIRED', message: '로그인이 필요합니다.', fix: 'relay login 실행 후 재시도하세요.' }));
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
console.error('\x1b[31m오류: 로그인이 필요합니다.\x1b[0m');
|
|
84
|
+
}
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
const slug = opts.slug ?? name
|
|
88
|
+
.toLowerCase()
|
|
89
|
+
.replace(/[^a-z0-9\s-]/g, '')
|
|
90
|
+
.replace(/[\s]+/g, '-')
|
|
91
|
+
.replace(/-+/g, '-')
|
|
92
|
+
.replace(/^-|-$/g, '')
|
|
93
|
+
.slice(0, 50);
|
|
94
|
+
try {
|
|
95
|
+
const res = await fetch(`${config_js_1.API_URL}/api/orgs`, {
|
|
96
|
+
method: 'POST',
|
|
97
|
+
headers: {
|
|
98
|
+
'Content-Type': 'application/json',
|
|
99
|
+
Authorization: `Bearer ${token}`,
|
|
100
|
+
},
|
|
101
|
+
body: JSON.stringify({ name, slug }),
|
|
102
|
+
});
|
|
103
|
+
if (!res.ok) {
|
|
104
|
+
const body = await res.json().catch(() => ({ message: `${res.status}` }));
|
|
105
|
+
throw new Error(body.message ?? `Organization 생성 실패 (${res.status})`);
|
|
106
|
+
}
|
|
107
|
+
const org = await res.json();
|
|
108
|
+
if (json) {
|
|
109
|
+
console.log(JSON.stringify({ status: 'created', org }));
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
console.log(`\x1b[32m✅ Organization "${org.name}" (@${org.slug}) 생성 완료\x1b[0m`);
|
|
113
|
+
console.log(`\n\x1b[33m 팀 배포: relay publish --org ${org.slug}\x1b[0m`);
|
|
114
|
+
console.log(`\x1b[33m 멤버 초대: www.relayax.com/orgs/${org.slug}/members\x1b[0m`);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
119
|
+
if (json) {
|
|
120
|
+
console.error(JSON.stringify({ error: 'CREATE_FAILED', message }));
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
console.error(`\x1b[31m오류: ${message}\x1b[0m`);
|
|
124
|
+
}
|
|
125
|
+
process.exit(1);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
package/dist/commands/publish.js
CHANGED
|
@@ -445,90 +445,84 @@ function registerPublish(program) {
|
|
|
445
445
|
}));
|
|
446
446
|
process.exit(1);
|
|
447
447
|
}
|
|
448
|
-
// Fetch user's
|
|
449
|
-
let
|
|
450
|
-
let
|
|
451
|
-
let selectedJoinPolicy;
|
|
448
|
+
// Fetch user's Orgs and select publish target
|
|
449
|
+
let selectedOrgId;
|
|
450
|
+
let selectedOrgSlug;
|
|
452
451
|
try {
|
|
453
|
-
const {
|
|
454
|
-
const
|
|
455
|
-
// --space flag: resolve
|
|
452
|
+
const { fetchMyOrgs } = await import('./orgs.js');
|
|
453
|
+
const orgs = await fetchMyOrgs(token);
|
|
454
|
+
// --space flag (legacy alias for --org): resolve Org by slug
|
|
456
455
|
if (opts.space) {
|
|
457
|
-
const matched =
|
|
456
|
+
const matched = orgs.find((o) => o.slug === opts.space);
|
|
458
457
|
if (matched) {
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
selectedJoinPolicy = matched.join_policy;
|
|
458
|
+
selectedOrgId = matched.id;
|
|
459
|
+
selectedOrgSlug = matched.slug;
|
|
462
460
|
}
|
|
463
461
|
else {
|
|
464
462
|
if (json) {
|
|
465
463
|
console.error(JSON.stringify({
|
|
466
|
-
error: '
|
|
467
|
-
message: `
|
|
468
|
-
fix: `사용 가능한
|
|
469
|
-
options:
|
|
464
|
+
error: 'INVALID_ORG',
|
|
465
|
+
message: `Organization '${opts.space}'를 찾을 수 없습니다.`,
|
|
466
|
+
fix: `사용 가능한 Org: ${orgs.map((o) => o.slug).join(', ')}`,
|
|
467
|
+
options: orgs.map((o) => ({ value: o.slug, label: `${o.name} (${o.slug})` })),
|
|
470
468
|
}));
|
|
471
469
|
}
|
|
472
470
|
else {
|
|
473
|
-
console.error(`
|
|
471
|
+
console.error(`Organization '${opts.space}'를 찾을 수 없습니다.`);
|
|
474
472
|
}
|
|
475
473
|
process.exit(1);
|
|
476
474
|
}
|
|
477
475
|
}
|
|
478
476
|
else if (isTTY) {
|
|
479
|
-
if (
|
|
480
|
-
// No
|
|
481
|
-
console.error('\x1b[33m⚠ 소속
|
|
477
|
+
if (orgs.length === 0) {
|
|
478
|
+
// No orgs — publish without org_id
|
|
479
|
+
console.error('\x1b[33m⚠ 소속 Organization이 없습니다. 개인 계정으로 배포합니다.\x1b[0m\n');
|
|
482
480
|
}
|
|
483
|
-
else if (
|
|
484
|
-
// Only one
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
console.error(`\x1b[2m Space: ${spaces[0].name} (${spaces[0].slug})\x1b[0m\n`);
|
|
481
|
+
else if (orgs.length === 1) {
|
|
482
|
+
// Only one Org — auto-select
|
|
483
|
+
selectedOrgId = orgs[0].id;
|
|
484
|
+
selectedOrgSlug = orgs[0].slug;
|
|
485
|
+
console.error(`\x1b[2m Organization: ${orgs[0].name} (${orgs[0].slug})\x1b[0m\n`);
|
|
489
486
|
}
|
|
490
487
|
else {
|
|
491
|
-
// Multiple
|
|
492
|
-
const { select:
|
|
493
|
-
const
|
|
494
|
-
name: `${
|
|
495
|
-
value:
|
|
496
|
-
slug:
|
|
497
|
-
join_policy: s.join_policy,
|
|
488
|
+
// Multiple orgs — prompt user
|
|
489
|
+
const { select: selectOrg } = await import('@inquirer/prompts');
|
|
490
|
+
const orgChoices = orgs.map((o) => ({
|
|
491
|
+
name: `${o.name} (${o.slug})`,
|
|
492
|
+
value: o.id,
|
|
493
|
+
slug: o.slug,
|
|
498
494
|
}));
|
|
499
|
-
const chosenId = await
|
|
500
|
-
message: '어떤
|
|
501
|
-
choices:
|
|
495
|
+
const chosenId = await selectOrg({
|
|
496
|
+
message: '어떤 Organization에 배포할까요?',
|
|
497
|
+
choices: orgChoices.map((c) => ({ name: c.name, value: c.value })),
|
|
502
498
|
});
|
|
503
|
-
const chosen =
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
selectedJoinPolicy = chosen?.join_policy;
|
|
499
|
+
const chosen = orgChoices.find((c) => c.value === chosenId);
|
|
500
|
+
selectedOrgId = chosenId;
|
|
501
|
+
selectedOrgSlug = chosen?.slug;
|
|
507
502
|
const chosenLabel = chosen?.name ?? chosenId;
|
|
508
|
-
console.error(` →
|
|
503
|
+
console.error(` → Organization: ${chosenLabel}\n`);
|
|
509
504
|
}
|
|
510
505
|
}
|
|
511
|
-
else if (
|
|
512
|
-
// --json 모드 + 여러
|
|
506
|
+
else if (orgs.length > 1 && json) {
|
|
507
|
+
// --json 모드 + 여러 Org: 에이전트가 선택할 수 있도록 에러 반환
|
|
513
508
|
console.error(JSON.stringify({
|
|
514
|
-
error: '
|
|
515
|
-
message: '배포할
|
|
516
|
-
fix: `relay publish --
|
|
517
|
-
options:
|
|
509
|
+
error: 'MISSING_ORG',
|
|
510
|
+
message: '배포할 Organization을 선택하세요.',
|
|
511
|
+
fix: `relay publish --org <slug> --json`,
|
|
512
|
+
options: orgs.map((o) => ({ value: o.slug, label: `${o.name} (${o.slug})` })),
|
|
518
513
|
}));
|
|
519
514
|
process.exit(1);
|
|
520
515
|
}
|
|
521
|
-
else if (
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
selectedJoinPolicy = spaces[0].join_policy;
|
|
516
|
+
else if (orgs.length > 0) {
|
|
517
|
+
selectedOrgId = orgs[0].id;
|
|
518
|
+
selectedOrgSlug = orgs[0].slug;
|
|
525
519
|
}
|
|
526
520
|
}
|
|
527
521
|
catch {
|
|
528
|
-
//
|
|
522
|
+
// Org 조회 실패 시 무시하고 계속 진행
|
|
529
523
|
}
|
|
530
|
-
// Visibility default
|
|
531
|
-
const defaultVisibility =
|
|
524
|
+
// Visibility default
|
|
525
|
+
const defaultVisibility = 'public';
|
|
532
526
|
// Visibility validation: must be explicitly set
|
|
533
527
|
if (!config.visibility) {
|
|
534
528
|
if (isTTY) {
|
|
@@ -546,7 +540,7 @@ function registerPublish(program) {
|
|
|
546
540
|
value: 'gated',
|
|
547
541
|
},
|
|
548
542
|
{
|
|
549
|
-
name: `비공개 — Space 멤버만
|
|
543
|
+
name: `비공개 — Space 멤버만 접근`,
|
|
550
544
|
value: 'private',
|
|
551
545
|
},
|
|
552
546
|
],
|
|
@@ -593,7 +587,7 @@ function registerPublish(program) {
|
|
|
593
587
|
value: 'gated',
|
|
594
588
|
},
|
|
595
589
|
{
|
|
596
|
-
name: `비공개 — Space 멤버만
|
|
590
|
+
name: `비공개 — Space 멤버만 접근`,
|
|
597
591
|
value: 'private',
|
|
598
592
|
},
|
|
599
593
|
],
|
|
@@ -638,8 +632,8 @@ function registerPublish(program) {
|
|
|
638
632
|
type: config.type ?? 'hybrid',
|
|
639
633
|
agent_details: detectedAgents,
|
|
640
634
|
skill_details: detectedSkills,
|
|
641
|
-
...(
|
|
642
|
-
...(
|
|
635
|
+
...(selectedOrgId ? { org_id: selectedOrgId } : {}),
|
|
636
|
+
...(selectedOrgSlug ? { org_slug: selectedOrgSlug } : {}),
|
|
643
637
|
};
|
|
644
638
|
if (!json) {
|
|
645
639
|
console.error(`패키지 생성 중... (${config.name} v${config.version})`);
|
package/dist/commands/search.js
CHANGED
|
@@ -32,7 +32,7 @@ function registerSearch(program) {
|
|
|
32
32
|
.action(async (keyword, opts) => {
|
|
33
33
|
const json = program.opts().json ?? false;
|
|
34
34
|
try {
|
|
35
|
-
const results = await (0, api_js_1.searchTeams)(keyword, opts.tag
|
|
35
|
+
const results = await (0, api_js_1.searchTeams)(keyword, opts.tag);
|
|
36
36
|
if (json) {
|
|
37
37
|
console.log(JSON.stringify({ results }));
|
|
38
38
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerVersions = registerVersions;
|
|
4
|
+
const api_js_1 = require("../lib/api.js");
|
|
5
|
+
const slug_js_1 = require("../lib/slug.js");
|
|
6
|
+
function registerVersions(program) {
|
|
7
|
+
program
|
|
8
|
+
.command('versions <slug>')
|
|
9
|
+
.description('팀 버전 목록과 릴리즈 노트를 확인합니다')
|
|
10
|
+
.action(async (slugInput) => {
|
|
11
|
+
const json = program.opts().json ?? false;
|
|
12
|
+
try {
|
|
13
|
+
const resolved = await (0, slug_js_1.resolveSlug)(slugInput);
|
|
14
|
+
const versions = await (0, api_js_1.fetchTeamVersions)(resolved.full);
|
|
15
|
+
if (json) {
|
|
16
|
+
console.log(JSON.stringify({ slug: resolved.full, versions }));
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
if (versions.length === 0) {
|
|
20
|
+
console.log(`\n${resolved.full} 버전 이력이 없습니다.`);
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
console.log(`\n\x1b[1m${resolved.full} 버전 이력\x1b[0m (${versions.length}개):\n`);
|
|
24
|
+
for (const v of versions) {
|
|
25
|
+
const date = new Date(v.created_at).toLocaleDateString('ko-KR');
|
|
26
|
+
console.log(` \x1b[36mv${v.version}\x1b[0m (${date})`);
|
|
27
|
+
if (v.changelog) {
|
|
28
|
+
console.log(` \x1b[90m${v.changelog}\x1b[0m`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
console.log(`\n\x1b[33m 특정 버전 설치: relay install ${resolved.full}@<version>\x1b[0m`);
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
35
|
+
if (json) {
|
|
36
|
+
console.error(JSON.stringify({ error: 'VERSIONS_FAILED', message }));
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
console.error(`\x1b[31m오류: ${message}\x1b[0m`);
|
|
40
|
+
}
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -18,10 +18,12 @@ const check_update_js_1 = require("./commands/check-update.js");
|
|
|
18
18
|
const follow_js_1 = require("./commands/follow.js");
|
|
19
19
|
const changelog_js_1 = require("./commands/changelog.js");
|
|
20
20
|
const join_js_1 = require("./commands/join.js");
|
|
21
|
-
const
|
|
21
|
+
const orgs_js_1 = require("./commands/orgs.js");
|
|
22
22
|
const deploy_record_js_1 = require("./commands/deploy-record.js");
|
|
23
23
|
const ping_js_1 = require("./commands/ping.js");
|
|
24
24
|
const access_js_1 = require("./commands/access.js");
|
|
25
|
+
const versions_js_1 = require("./commands/versions.js");
|
|
26
|
+
const diff_js_1 = require("./commands/diff.js");
|
|
25
27
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
26
28
|
const pkg = require('../package.json');
|
|
27
29
|
const program = new commander_1.Command();
|
|
@@ -46,8 +48,10 @@ program
|
|
|
46
48
|
(0, follow_js_1.registerFollow)(program);
|
|
47
49
|
(0, changelog_js_1.registerChangelog)(program);
|
|
48
50
|
(0, join_js_1.registerJoin)(program);
|
|
49
|
-
(0,
|
|
51
|
+
(0, orgs_js_1.registerOrgs)(program);
|
|
50
52
|
(0, deploy_record_js_1.registerDeployRecord)(program);
|
|
51
53
|
(0, ping_js_1.registerPing)(program);
|
|
52
54
|
(0, access_js_1.registerAccess)(program);
|
|
55
|
+
(0, versions_js_1.registerVersions)(program);
|
|
56
|
+
(0, diff_js_1.registerDiff)(program);
|
|
53
57
|
program.parse();
|
package/dist/lib/api.d.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import type { TeamRegistryInfo, SearchResult } from '../types.js';
|
|
2
|
+
export declare function fetchMyOrgs(): Promise<{
|
|
3
|
+
id: string;
|
|
4
|
+
slug: string;
|
|
5
|
+
name: string;
|
|
6
|
+
role: string;
|
|
7
|
+
}[]>;
|
|
2
8
|
export declare function fetchTeamInfo(slug: string): Promise<TeamRegistryInfo>;
|
|
3
|
-
export declare function searchTeams(query: string, tag?: string
|
|
9
|
+
export declare function searchTeams(query: string, tag?: string): Promise<SearchResult[]>;
|
|
4
10
|
export interface TeamVersionInfo {
|
|
5
11
|
version: string;
|
|
6
12
|
changelog: string | null;
|
|
@@ -8,11 +14,6 @@ export interface TeamVersionInfo {
|
|
|
8
14
|
}
|
|
9
15
|
export declare function fetchTeamVersions(slug: string): Promise<TeamVersionInfo[]>;
|
|
10
16
|
export declare function reportInstall(teamId: string, slug: string, version?: string): Promise<void>;
|
|
11
|
-
/**
|
|
12
|
-
* Space 팀 설치: 멤버십 검증 + install count 증가 + 팀 메타데이터 반환을 한 번에 처리.
|
|
13
|
-
* POST /api/spaces/{spaceSlug}/teams/{teamSlug}/install
|
|
14
|
-
*/
|
|
15
|
-
export declare function installSpaceTeam(spaceSlug: string, teamSlug: string, version?: string): Promise<TeamRegistryInfo>;
|
|
16
17
|
export interface ResolvedSlug {
|
|
17
18
|
owner: string;
|
|
18
19
|
name: string;
|
package/dist/lib/api.js
CHANGED
|
@@ -1,14 +1,26 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fetchMyOrgs = fetchMyOrgs;
|
|
3
4
|
exports.fetchTeamInfo = fetchTeamInfo;
|
|
4
5
|
exports.searchTeams = searchTeams;
|
|
5
6
|
exports.fetchTeamVersions = fetchTeamVersions;
|
|
6
7
|
exports.reportInstall = reportInstall;
|
|
7
|
-
exports.installSpaceTeam = installSpaceTeam;
|
|
8
8
|
exports.resolveSlugFromServer = resolveSlugFromServer;
|
|
9
9
|
exports.sendUsagePing = sendUsagePing;
|
|
10
10
|
exports.followBuilder = followBuilder;
|
|
11
11
|
const config_js_1 = require("./config.js");
|
|
12
|
+
async function fetchMyOrgs() {
|
|
13
|
+
const token = await (0, config_js_1.getValidToken)();
|
|
14
|
+
if (!token)
|
|
15
|
+
return [];
|
|
16
|
+
const res = await fetch(`${config_js_1.API_URL}/api/orgs`, {
|
|
17
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
18
|
+
signal: AbortSignal.timeout(8000),
|
|
19
|
+
});
|
|
20
|
+
if (!res.ok)
|
|
21
|
+
return [];
|
|
22
|
+
return res.json();
|
|
23
|
+
}
|
|
12
24
|
async function fetchTeamInfo(slug) {
|
|
13
25
|
const registrySlug = slug.startsWith('@') ? slug.slice(1) : slug;
|
|
14
26
|
const url = `${config_js_1.API_URL}/api/registry/${registrySlug}`;
|
|
@@ -19,12 +31,10 @@ async function fetchTeamInfo(slug) {
|
|
|
19
31
|
}
|
|
20
32
|
return res.json();
|
|
21
33
|
}
|
|
22
|
-
async function searchTeams(query, tag
|
|
34
|
+
async function searchTeams(query, tag) {
|
|
23
35
|
const params = new URLSearchParams({ q: query });
|
|
24
36
|
if (tag)
|
|
25
37
|
params.set('tag', tag);
|
|
26
|
-
if (space)
|
|
27
|
-
params.set('space', space);
|
|
28
38
|
const url = `${config_js_1.API_URL}/api/registry/search?${params.toString()}`;
|
|
29
39
|
const res = await fetch(url);
|
|
30
40
|
if (!res.ok) {
|
|
@@ -69,31 +79,6 @@ async function reportInstall(teamId, slug, version) {
|
|
|
69
79
|
// network error: ignore silently
|
|
70
80
|
}
|
|
71
81
|
}
|
|
72
|
-
/**
|
|
73
|
-
* Space 팀 설치: 멤버십 검증 + install count 증가 + 팀 메타데이터 반환을 한 번에 처리.
|
|
74
|
-
* POST /api/spaces/{spaceSlug}/teams/{teamSlug}/install
|
|
75
|
-
*/
|
|
76
|
-
async function installSpaceTeam(spaceSlug, teamSlug, version) {
|
|
77
|
-
const url = `${config_js_1.API_URL}/api/spaces/${spaceSlug}/teams/${teamSlug}/install`;
|
|
78
|
-
const headers = { 'Content-Type': 'application/json' };
|
|
79
|
-
const token = await (0, config_js_1.getValidToken)();
|
|
80
|
-
if (token) {
|
|
81
|
-
headers['Authorization'] = `Bearer ${token}`;
|
|
82
|
-
}
|
|
83
|
-
const body = {};
|
|
84
|
-
if (version)
|
|
85
|
-
body.version = version;
|
|
86
|
-
const res = await fetch(url, {
|
|
87
|
-
method: 'POST',
|
|
88
|
-
headers,
|
|
89
|
-
body: JSON.stringify(body),
|
|
90
|
-
});
|
|
91
|
-
if (!res.ok) {
|
|
92
|
-
const text = await res.text();
|
|
93
|
-
throw new Error(`Space 팀 설치 실패 (${res.status}): ${text}`);
|
|
94
|
-
}
|
|
95
|
-
return res.json();
|
|
96
|
-
}
|
|
97
82
|
async function resolveSlugFromServer(name) {
|
|
98
83
|
const url = `${config_js_1.API_URL}/api/registry/resolve?name=${encodeURIComponent(name)}`;
|
|
99
84
|
const res = await fetch(url, { signal: AbortSignal.timeout(5000) });
|
|
@@ -412,7 +412,7 @@ https://relayax.com/api/registry/{owner}/{slug}/guide.md
|
|
|
412
412
|
"installed_at": "2026-03-20T12:00:00.000Z",
|
|
413
413
|
"scope": "global",
|
|
414
414
|
"deploy_scope": "global",
|
|
415
|
-
"
|
|
415
|
+
"org_slug": null
|
|
416
416
|
}
|
|
417
417
|
]
|
|
418
418
|
}
|
|
@@ -425,19 +425,19 @@ https://relayax.com/api/registry/{owner}/{slug}/guide.md
|
|
|
425
425
|
| @author/team-name | v1.2.0 | 글로벌 | 3/20 |
|
|
426
426
|
|
|
427
427
|
- \`deploy_scope\`가 \`"global"\` → 글로벌, \`"local"\` → 로컬, 없으면 → 미배치
|
|
428
|
-
- \`
|
|
428
|
+
- \`org_slug\`가 있으면 \`[Org: slug]\` 표시
|
|
429
429
|
|
|
430
|
-
### 2.
|
|
430
|
+
### 2. Organization 목록
|
|
431
431
|
|
|
432
|
-
\`relay
|
|
432
|
+
\`relay orgs list --json\` 명령어를 실행합니다.
|
|
433
433
|
|
|
434
434
|
**JSON 응답 구조:**
|
|
435
435
|
\`\`\`json
|
|
436
436
|
{
|
|
437
|
-
"
|
|
437
|
+
"orgs": [
|
|
438
438
|
{
|
|
439
|
-
"slug": "my-
|
|
440
|
-
"name": "내
|
|
439
|
+
"slug": "my-org",
|
|
440
|
+
"name": "내 조직",
|
|
441
441
|
"description": "설명",
|
|
442
442
|
"role": "owner"
|
|
443
443
|
}
|
|
@@ -446,24 +446,24 @@ https://relayax.com/api/registry/{owner}/{slug}/guide.md
|
|
|
446
446
|
\`\`\`
|
|
447
447
|
|
|
448
448
|
**표시:**
|
|
449
|
-
- \`role\`: owner →
|
|
449
|
+
- \`role\`: owner → 오너, admin → 관리자, builder → 빌더, member → 멤버
|
|
450
450
|
${ERROR_HANDLING_GUIDE}
|
|
451
|
-
-
|
|
451
|
+
- Org 조회 실패해도 설치된 팀 목록은 정상 표시합니다 (로컬 데이터).
|
|
452
452
|
|
|
453
|
-
### 3.
|
|
454
|
-
- \`--
|
|
453
|
+
### 3. Org 팀 목록 (옵션)
|
|
454
|
+
- \`--org <slug>\` 인자가 있으면: \`relay list --org <org-slug> --json\`으로 해당 Organization의 팀 목록도 보여줍니다.
|
|
455
455
|
|
|
456
456
|
### 4. 안내
|
|
457
457
|
- 설치된 팀이 없으면 \`/relay-install\`로 팀을 탐색·설치해보라고 안내합니다.
|
|
458
|
-
-
|
|
459
|
-
-
|
|
460
|
-
-
|
|
458
|
+
- Org가 있으면 활용법을 안내합니다:
|
|
459
|
+
- Org 팀 설치: \`relay install @<org-slug>/<team>\`
|
|
460
|
+
- Org 관리: www.relayax.com/orgs/<slug>
|
|
461
461
|
|
|
462
462
|
## 예시
|
|
463
463
|
|
|
464
464
|
사용자: /relay-status
|
|
465
465
|
→ relay list --json 실행
|
|
466
|
-
→ relay
|
|
466
|
+
→ relay orgs list --json 실행 (병렬 가능)
|
|
467
467
|
|
|
468
468
|
**설치된 팀 (2개)**
|
|
469
469
|
|