relayax-cli 0.2.41 → 0.3.42
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/access.js +12 -12
- package/dist/commands/changelog.js +2 -2
- package/dist/commands/check-update.js +12 -12
- package/dist/commands/create.js +46 -19
- package/dist/commands/deploy-record.js +2 -2
- package/dist/commands/diff.d.ts +2 -0
- package/dist/commands/diff.js +72 -0
- package/dist/commands/grant.d.ts +33 -0
- package/dist/commands/grant.js +190 -0
- package/dist/commands/init.js +10 -10
- package/dist/commands/install.js +125 -256
- package/dist/commands/join.d.ts +3 -2
- package/dist/commands/join.js +18 -69
- package/dist/commands/list.js +23 -26
- package/dist/commands/login.js +10 -3
- package/dist/commands/orgs.d.ts +10 -0
- package/dist/commands/orgs.js +128 -0
- package/dist/commands/outdated.js +7 -7
- package/dist/commands/package.d.ts +18 -0
- package/dist/commands/package.js +355 -146
- package/dist/commands/ping.js +5 -5
- package/dist/commands/publish.d.ts +1 -1
- package/dist/commands/publish.js +105 -103
- package/dist/commands/search.js +2 -2
- package/dist/commands/status.js +11 -11
- package/dist/commands/uninstall.js +7 -7
- package/dist/commands/update.js +22 -22
- package/dist/commands/versions.d.ts +2 -0
- package/dist/commands/versions.js +44 -0
- package/dist/index.js +8 -2
- package/dist/lib/ai-tools.d.ts +15 -0
- package/dist/lib/ai-tools.js +48 -1
- package/dist/lib/api.d.ts +13 -12
- package/dist/lib/api.js +24 -39
- package/dist/lib/command-adapter.js +41 -693
- package/dist/lib/config.d.ts +10 -5
- package/dist/lib/config.js +106 -24
- package/dist/lib/guide.js +34 -79
- package/dist/lib/installer.d.ts +2 -2
- package/dist/lib/installer.js +4 -4
- package/dist/lib/preamble.d.ts +4 -4
- package/dist/lib/preamble.js +14 -15
- package/dist/lib/slug.d.ts +5 -1
- package/dist/lib/slug.js +52 -9
- package/dist/lib/update-cache.js +4 -4
- package/dist/lib/version-check.d.ts +3 -3
- package/dist/lib/version-check.js +13 -13
- package/dist/prompts/_business-card.md +41 -0
- package/dist/prompts/_error-handling.md +38 -0
- package/dist/prompts/_requirements-check.md +59 -0
- package/dist/prompts/_setup-cli.md +19 -0
- package/dist/prompts/_setup-login.md +7 -0
- package/dist/prompts/_setup-org.md +27 -0
- package/dist/prompts/business-card.md +41 -0
- package/dist/prompts/error-handling.md +38 -0
- package/dist/prompts/index.d.ts +7 -0
- package/dist/prompts/index.js +28 -0
- package/dist/prompts/install.md +187 -0
- package/dist/prompts/publish.md +444 -0
- package/dist/prompts/requirements-check.md +59 -0
- package/dist/types.d.ts +10 -10
- package/package.json +3 -3
package/dist/commands/join.js
CHANGED
|
@@ -1,54 +1,38 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.joinOrg = joinOrg;
|
|
4
4
|
exports.registerJoin = registerJoin;
|
|
5
5
|
const config_js_1 = require("../lib/config.js");
|
|
6
6
|
const init_js_1 = require("./init.js");
|
|
7
|
-
async function
|
|
8
|
-
const res = await fetch(`${config_js_1.API_URL}/api/spaces/${spaceSlug}/teams`, {
|
|
9
|
-
headers: { Authorization: `Bearer ${token}` },
|
|
10
|
-
signal: AbortSignal.timeout(5000),
|
|
11
|
-
});
|
|
12
|
-
if (!res.ok)
|
|
13
|
-
throw new Error(`${res.status}`);
|
|
14
|
-
const data = (await res.json());
|
|
15
|
-
if (Array.isArray(data))
|
|
16
|
-
return data;
|
|
17
|
-
return data.teams ?? [];
|
|
18
|
-
}
|
|
19
|
-
async function joinSpace(spaceSlug, code) {
|
|
7
|
+
async function joinOrg(orgSlug, code) {
|
|
20
8
|
const token = await (0, config_js_1.getValidToken)();
|
|
21
9
|
if (!token) {
|
|
22
10
|
throw new Error('LOGIN_REQUIRED');
|
|
23
11
|
}
|
|
24
|
-
|
|
12
|
+
// Use the access code via API
|
|
13
|
+
const res = await fetch(`${config_js_1.API_URL}/api/access-codes/${code}/use`, {
|
|
25
14
|
method: 'POST',
|
|
26
15
|
headers: {
|
|
27
16
|
'Content-Type': 'application/json',
|
|
28
17
|
Authorization: `Bearer ${token}`,
|
|
29
18
|
},
|
|
30
|
-
body: JSON.stringify({ code }),
|
|
31
19
|
});
|
|
32
|
-
const body = (await res.json().catch(() => ({})));
|
|
33
20
|
if (!res.ok) {
|
|
21
|
+
const body = await res.json().catch(() => ({}));
|
|
34
22
|
const errCode = body.error ?? String(res.status);
|
|
35
23
|
switch (errCode) {
|
|
36
|
-
case '
|
|
37
|
-
throw new Error('초대 코드가
|
|
38
|
-
case 'EXPIRED_CODE':
|
|
39
|
-
throw new Error('초대 코드가 만료되었습니다.');
|
|
40
|
-
case 'ALREADY_MEMBER':
|
|
41
|
-
throw new Error('ALREADY_MEMBER');
|
|
24
|
+
case 'INVALID_LINK':
|
|
25
|
+
throw new Error('초대 코드가 유효하지 않거나 만료되었습니다.');
|
|
42
26
|
default:
|
|
43
|
-
throw new Error(body.message ??
|
|
27
|
+
throw new Error(body.message ?? `가입 실패 (${res.status})`);
|
|
44
28
|
}
|
|
45
29
|
}
|
|
46
|
-
return
|
|
30
|
+
return res.json();
|
|
47
31
|
}
|
|
48
32
|
function registerJoin(program) {
|
|
49
33
|
program
|
|
50
34
|
.command('join <slug>')
|
|
51
|
-
.description('
|
|
35
|
+
.description('Organization에 초대 코드로 가입합니다')
|
|
52
36
|
.requiredOption('--code <code>', '초대 코드 (UUID)')
|
|
53
37
|
.action(async (slug, opts) => {
|
|
54
38
|
const json = program.opts().json ?? false;
|
|
@@ -62,57 +46,22 @@ function registerJoin(program) {
|
|
|
62
46
|
process.exit(1);
|
|
63
47
|
}
|
|
64
48
|
try {
|
|
65
|
-
const
|
|
49
|
+
const result = await joinOrg(slug, opts.code);
|
|
66
50
|
if (json) {
|
|
67
|
-
|
|
68
|
-
let teams = [];
|
|
69
|
-
try {
|
|
70
|
-
const token = await (0, config_js_1.getValidToken)();
|
|
71
|
-
if (token)
|
|
72
|
-
teams = await fetchSpaceTeams(slug, token);
|
|
73
|
-
}
|
|
74
|
-
catch {
|
|
75
|
-
// ignore
|
|
76
|
-
}
|
|
77
|
-
console.log(JSON.stringify({ status: 'ok', space: slug, space_name: spaceName, teams: teams.map((t) => ({ slug: t.slug, name: t.name })) }));
|
|
51
|
+
console.log(JSON.stringify({ status: 'ok', ...result }));
|
|
78
52
|
}
|
|
79
53
|
else {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const token = await (0, config_js_1.getValidToken)();
|
|
84
|
-
if (token) {
|
|
85
|
-
const teams = await fetchSpaceTeams(slug, token);
|
|
86
|
-
if (teams.length === 0) {
|
|
87
|
-
console.log('\n아직 추가된 팀이 없습니다.');
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
90
|
-
console.log('\n\x1b[1m📦 사용 가능한 팀:\x1b[0m');
|
|
91
|
-
for (const t of teams) {
|
|
92
|
-
const desc = t.description ? ` \x1b[90m— ${t.description}\x1b[0m` : '';
|
|
93
|
-
console.log(` \x1b[36m•\x1b[0m \x1b[1m${t.slug}\x1b[0m${desc}`);
|
|
94
|
-
}
|
|
95
|
-
console.log(`\n\x1b[33m💡 전체 설치: relay install @spaces/${slug}/<팀슬러그>\x1b[0m`);
|
|
96
|
-
console.log(`\x1b[33m💡 가이드 URL 공유: https://relayax.com/api/spaces/${slug}/guide.md\x1b[0m`);
|
|
97
|
-
}
|
|
98
|
-
}
|
|
54
|
+
if (result.type === 'org') {
|
|
55
|
+
console.log(`\x1b[32m✅ @${slug} Organization에 가입했습니다 (역할: ${result.role ?? 'member'})\x1b[0m`);
|
|
56
|
+
console.log(`\n\x1b[33m 대시보드: www.relayax.com/orgs/${slug}\x1b[0m`);
|
|
99
57
|
}
|
|
100
|
-
|
|
101
|
-
|
|
58
|
+
else {
|
|
59
|
+
console.log(`\x1b[32m✅ 에이전트 접근 권한이 부여되었습니다\x1b[0m`);
|
|
102
60
|
}
|
|
103
61
|
}
|
|
104
62
|
}
|
|
105
63
|
catch (err) {
|
|
106
64
|
const message = err instanceof Error ? err.message : String(err);
|
|
107
|
-
if (message === 'ALREADY_MEMBER') {
|
|
108
|
-
if (json) {
|
|
109
|
-
console.log(JSON.stringify({ status: 'already_member', space: slug }));
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
console.log(`\x1b[33m이미 ${slug} Space의 멤버입니다.\x1b[0m`);
|
|
113
|
-
}
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
65
|
if (message === 'LOGIN_REQUIRED') {
|
|
117
66
|
if (json) {
|
|
118
67
|
console.error(JSON.stringify({
|
|
@@ -128,7 +77,7 @@ function registerJoin(program) {
|
|
|
128
77
|
process.exit(1);
|
|
129
78
|
}
|
|
130
79
|
if (json) {
|
|
131
|
-
console.error(JSON.stringify({ error: 'JOIN_FAILED', message, fix: '
|
|
80
|
+
console.error(JSON.stringify({ error: 'JOIN_FAILED', message, fix: 'slug와 초대 코드를 확인 후 재시도하세요.' }));
|
|
132
81
|
}
|
|
133
82
|
else {
|
|
134
83
|
console.error(`\x1b[31m오류: ${message}\x1b[0m`);
|
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 fetchOrgAgentList(orgSlug, token) {
|
|
6
|
+
const res = await fetch(`${config_js_1.API_URL}/api/orgs/${orgSlug}/agents`, {
|
|
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
|
-
.description('설치된 에이전트
|
|
23
|
-
.option('--
|
|
19
|
+
.description('설치된 에이전트 목록')
|
|
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
|
|
38
|
+
const agents = await fetchOrgAgentList(orgSlug, token);
|
|
42
39
|
if (json) {
|
|
43
|
-
console.log(JSON.stringify({
|
|
40
|
+
console.log(JSON.stringify({ org: orgSlug, agents }));
|
|
44
41
|
return;
|
|
45
42
|
}
|
|
46
|
-
if (
|
|
47
|
-
console.log(`\n
|
|
43
|
+
if (agents.length === 0) {
|
|
44
|
+
console.log(`\n@${orgSlug} Organization에 에이전트가 없습니다.`);
|
|
48
45
|
return;
|
|
49
46
|
}
|
|
50
|
-
console.log(`\n\x1b[1m
|
|
51
|
-
for (const t of
|
|
47
|
+
console.log(`\n\x1b[1m@${orgSlug} 에이전트 목록\x1b[0m (${agents.length}개):\n`);
|
|
48
|
+
for (const t of agents) {
|
|
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) {
|
|
@@ -102,10 +99,10 @@ function registerList(program) {
|
|
|
102
99
|
}
|
|
103
100
|
else {
|
|
104
101
|
if (allEntries.length === 0) {
|
|
105
|
-
console.log('\n설치된
|
|
102
|
+
console.log('\n설치된 에이전트가 없습니다. `relay install <slug>`로 설치하세요.');
|
|
106
103
|
return;
|
|
107
104
|
}
|
|
108
|
-
console.log(`\n설치된
|
|
105
|
+
console.log(`\n설치된 에이전트 (${allEntries.length}개):\n`);
|
|
109
106
|
for (const item of allEntries) {
|
|
110
107
|
const date = new Date(item.installed_at).toLocaleDateString('ko-KR');
|
|
111
108
|
const scopeLabel = item.deploy_scope === 'global'
|
|
@@ -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
|
});
|
package/dist/commands/login.js
CHANGED
|
@@ -20,9 +20,10 @@ function openBrowser(url) {
|
|
|
20
20
|
else {
|
|
21
21
|
(0, child_process_1.execSync)(`xdg-open "${url}"`, { stdio: 'ignore' });
|
|
22
22
|
}
|
|
23
|
+
return true;
|
|
23
24
|
}
|
|
24
25
|
catch {
|
|
25
|
-
|
|
26
|
+
return false;
|
|
26
27
|
}
|
|
27
28
|
}
|
|
28
29
|
async function verifyToken(token) {
|
|
@@ -106,10 +107,16 @@ function findAvailablePort() {
|
|
|
106
107
|
async function loginWithBrowser(json) {
|
|
107
108
|
const port = await findAvailablePort();
|
|
108
109
|
const loginUrl = `${config_js_1.API_URL}/auth/cli-login?port=${port}`;
|
|
110
|
+
const opened = openBrowser(loginUrl);
|
|
109
111
|
if (!json) {
|
|
110
|
-
|
|
112
|
+
if (opened) {
|
|
113
|
+
console.error(`브라우저에서 로그인 페이지를 엽니다...`);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
console.error(`브라우저를 자동으로 열 수 없습니다. 아래 URL을 브라우저에서 직접 열어주세요:\n`);
|
|
117
|
+
console.error(` ${loginUrl}\n`);
|
|
118
|
+
}
|
|
111
119
|
}
|
|
112
|
-
openBrowser(loginUrl);
|
|
113
120
|
return waitForToken(port);
|
|
114
121
|
}
|
|
115
122
|
/**
|
|
@@ -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
|
+
}
|
|
@@ -6,7 +6,7 @@ const config_js_1 = require("../lib/config.js");
|
|
|
6
6
|
function registerOutdated(program) {
|
|
7
7
|
program
|
|
8
8
|
.command('outdated')
|
|
9
|
-
.description('설치된
|
|
9
|
+
.description('설치된 에이전트의 업데이트 가능 여부를 확인합니다')
|
|
10
10
|
.action(async () => {
|
|
11
11
|
const json = program.opts().json ?? false;
|
|
12
12
|
const installed = (0, config_js_1.loadInstalled)();
|
|
@@ -16,7 +16,7 @@ function registerOutdated(program) {
|
|
|
16
16
|
console.log(JSON.stringify([]));
|
|
17
17
|
}
|
|
18
18
|
else {
|
|
19
|
-
console.log('설치된
|
|
19
|
+
console.log('설치된 에이전트가 없습니다.');
|
|
20
20
|
}
|
|
21
21
|
return;
|
|
22
22
|
}
|
|
@@ -24,8 +24,8 @@ function registerOutdated(program) {
|
|
|
24
24
|
const results = await Promise.all(slugs.map(async (slug) => {
|
|
25
25
|
const current = installed[slug].version;
|
|
26
26
|
try {
|
|
27
|
-
const
|
|
28
|
-
const latest =
|
|
27
|
+
const agent = await (0, api_js_1.fetchAgentInfo)(slug);
|
|
28
|
+
const latest = agent.version;
|
|
29
29
|
return {
|
|
30
30
|
slug,
|
|
31
31
|
current,
|
|
@@ -43,15 +43,15 @@ function registerOutdated(program) {
|
|
|
43
43
|
}
|
|
44
44
|
const allUpToDate = results.every((r) => r.status === 'up-to-date');
|
|
45
45
|
if (allUpToDate) {
|
|
46
|
-
console.log('모든
|
|
46
|
+
console.log('모든 에이전트가 최신 버전입니다.');
|
|
47
47
|
return;
|
|
48
48
|
}
|
|
49
49
|
// Determine column widths
|
|
50
|
-
const COL_TEAM = Math.max(
|
|
50
|
+
const COL_TEAM = Math.max(9, ...results.map((r) => r.slug.length));
|
|
51
51
|
const COL_CURRENT = Math.max(4, ...results.map((r) => `v${r.current}`.length));
|
|
52
52
|
const COL_LATEST = Math.max(4, ...results.map((r) => `v${r.latest}`.length));
|
|
53
53
|
const pad = (s, len) => s.padEnd(len);
|
|
54
|
-
const header = `${pad('
|
|
54
|
+
const header = `${pad('에이전트', COL_TEAM)} ${pad('현재', COL_CURRENT)} ${pad('최신', COL_LATEST)} 상태`;
|
|
55
55
|
const separator = '-'.repeat(header.length);
|
|
56
56
|
console.log(header);
|
|
57
57
|
console.log(separator);
|
|
@@ -1,2 +1,20 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
+
import type { ContentType } from '../lib/ai-tools.js';
|
|
3
|
+
export interface ContentEntry {
|
|
4
|
+
name: string;
|
|
5
|
+
type: ContentType;
|
|
6
|
+
from: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* 패키지 홈 디렉토리를 결정한다.
|
|
10
|
+
* 1. 프로젝트에 .relay/가 있으면 → projectPath/.relay/
|
|
11
|
+
* 2. 없으면 → ~/.relay/agents/<slug>/ (slug 필요)
|
|
12
|
+
*
|
|
13
|
+
* slug가 없고 프로젝트에도 .relay/가 없으면 null 반환.
|
|
14
|
+
*/
|
|
15
|
+
export declare function resolveRelayDir(projectPath: string, slug?: string): string | null;
|
|
16
|
+
/**
|
|
17
|
+
* 글로벌 에이전트 홈에 패키지 구조를 초기화한다.
|
|
18
|
+
*/
|
|
19
|
+
export declare function initGlobalAgentHome(slug: string, yamlData: Record<string, unknown>): string;
|
|
2
20
|
export declare function registerPackage(program: Command): void;
|