relayax-cli 0.3.41 → 0.3.43
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.js +2 -2
- package/dist/commands/grant.d.ts +33 -0
- package/dist/commands/grant.js +190 -0
- package/dist/commands/init.js +13 -15
- package/dist/commands/install.js +69 -68
- package/dist/commands/join.js +3 -3
- package/dist/commands/list.js +15 -15
- package/dist/commands/login.js +10 -3
- package/dist/commands/orgs.js +1 -1
- 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 +56 -48
- package/dist/commands/search.js +2 -2
- package/dist/commands/status.js +11 -11
- package/dist/commands/uninstall.js +27 -9
- package/dist/commands/update.js +22 -22
- package/dist/commands/versions.js +2 -2
- package/dist/index.js +2 -0
- package/dist/lib/ai-tools.d.ts +15 -0
- package/dist/lib/ai-tools.js +48 -1
- package/dist/lib/api.d.ts +7 -7
- package/dist/lib/api.js +11 -11
- package/dist/lib/command-adapter.d.ts +4 -3
- package/dist/lib/command-adapter.js +37 -688
- package/dist/lib/config.d.ts +1 -1
- package/dist/lib/config.js +2 -2
- 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 -14
- package/dist/lib/slug.d.ts +5 -0
- package/dist/lib/slug.js +49 -2
- 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 +191 -0
- package/dist/prompts/publish.md +448 -0
- package/dist/prompts/requirements-check.md +59 -0
- package/dist/types.d.ts +9 -9
- package/package.json +3 -3
package/dist/commands/access.js
CHANGED
|
@@ -8,7 +8,7 @@ async function claimAccess(slug, code) {
|
|
|
8
8
|
if (!token) {
|
|
9
9
|
throw new Error('LOGIN_REQUIRED');
|
|
10
10
|
}
|
|
11
|
-
const res = await fetch(`${config_js_1.API_URL}/api/
|
|
11
|
+
const res = await fetch(`${config_js_1.API_URL}/api/agents/${slug}/claim-access`, {
|
|
12
12
|
method: 'POST',
|
|
13
13
|
headers: {
|
|
14
14
|
'Content-Type': 'application/json',
|
|
@@ -24,7 +24,7 @@ async function claimAccess(slug, code) {
|
|
|
24
24
|
case 'INVALID_LINK':
|
|
25
25
|
throw new Error('초대 링크가 유효하지 않거나 만료되었습니다.');
|
|
26
26
|
case 'NOT_FOUND':
|
|
27
|
-
throw new Error('
|
|
27
|
+
throw new Error('에이전트를 찾을 수 없습니다.');
|
|
28
28
|
case 'UNAUTHORIZED':
|
|
29
29
|
throw new Error('LOGIN_REQUIRED');
|
|
30
30
|
default:
|
|
@@ -36,31 +36,31 @@ async function claimAccess(slug, code) {
|
|
|
36
36
|
function registerAccess(program) {
|
|
37
37
|
program
|
|
38
38
|
.command('access <slug>')
|
|
39
|
-
.description('초대 코드로
|
|
40
|
-
.requiredOption('--code <code>', '
|
|
39
|
+
.description('초대 코드로 에이전트에 접근 권한을 얻고 바로 설치합니다')
|
|
40
|
+
.requiredOption('--code <code>', '에이전트 초대 코드')
|
|
41
41
|
.action(async (slug, opts) => {
|
|
42
42
|
const json = program.opts().json ?? false;
|
|
43
43
|
try {
|
|
44
44
|
const result = await claimAccess(slug, opts.code);
|
|
45
|
-
if (!result.success || !result.
|
|
45
|
+
if (!result.success || !result.agent) {
|
|
46
46
|
throw new Error('서버 응답이 올바르지 않습니다.');
|
|
47
47
|
}
|
|
48
|
-
const
|
|
48
|
+
const agentSlug = result.agent.slug;
|
|
49
49
|
if (json) {
|
|
50
|
-
console.log(JSON.stringify({ status: 'ok',
|
|
50
|
+
console.log(JSON.stringify({ status: 'ok', agent: result.agent }));
|
|
51
51
|
}
|
|
52
52
|
else {
|
|
53
|
-
console.log(`\x1b[32m접근 권한이 부여되었습니다: ${result.
|
|
54
|
-
console.log(`\x1b[33m
|
|
53
|
+
console.log(`\x1b[32m접근 권한이 부여되었습니다: ${result.agent.name}\x1b[0m`);
|
|
54
|
+
console.log(`\x1b[33m에이전트를 설치합니다: relay install ${agentSlug}\x1b[0m\n`);
|
|
55
55
|
}
|
|
56
|
-
// Automatically install the
|
|
56
|
+
// Automatically install the agent
|
|
57
57
|
const { registerInstall } = await import('./install.js');
|
|
58
58
|
const subProgram = new commander_1.Command();
|
|
59
59
|
subProgram.option('--json', '구조화된 JSON 출력');
|
|
60
60
|
if (json)
|
|
61
61
|
subProgram.setOptionValue('json', true);
|
|
62
62
|
registerInstall(subProgram);
|
|
63
|
-
await subProgram.parseAsync(['node', 'relay', 'install',
|
|
63
|
+
await subProgram.parseAsync(['node', 'relay', 'install', agentSlug]);
|
|
64
64
|
}
|
|
65
65
|
catch (err) {
|
|
66
66
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -79,7 +79,7 @@ function registerAccess(program) {
|
|
|
79
79
|
process.exit(1);
|
|
80
80
|
}
|
|
81
81
|
if (json) {
|
|
82
|
-
console.error(JSON.stringify({ error: 'ACCESS_FAILED', message, fix: '접근 링크 코드를 확인하거나
|
|
82
|
+
console.error(JSON.stringify({ error: 'ACCESS_FAILED', message, fix: '접근 링크 코드를 확인하거나 에이전트 제작자에게 문의하세요.' }));
|
|
83
83
|
}
|
|
84
84
|
else {
|
|
85
85
|
console.error(`\x1b[31m오류: ${message}\x1b[0m`);
|
|
@@ -10,7 +10,7 @@ const js_yaml_1 = __importDefault(require("js-yaml"));
|
|
|
10
10
|
function registerChangelog(program) {
|
|
11
11
|
const changelog = program
|
|
12
12
|
.command('changelog')
|
|
13
|
-
.description('
|
|
13
|
+
.description('에이전트 패키지의 changelog를 관리합니다');
|
|
14
14
|
changelog
|
|
15
15
|
.command('add')
|
|
16
16
|
.description('relay.yaml에 changelog 엔트리를 추가합니다')
|
|
@@ -18,7 +18,7 @@ function registerChangelog(program) {
|
|
|
18
18
|
.action(async (message) => {
|
|
19
19
|
const yamlPath = path_1.default.resolve('relay.yaml');
|
|
20
20
|
if (!fs_1.default.existsSync(yamlPath)) {
|
|
21
|
-
console.error('relay.yaml을 찾을 수 없습니다.
|
|
21
|
+
console.error('relay.yaml을 찾을 수 없습니다. 에이전트 패키지 디렉토리에서 실행하세요.');
|
|
22
22
|
process.exit(1);
|
|
23
23
|
}
|
|
24
24
|
const content = fs_1.default.readFileSync(yamlPath, 'utf-8');
|
|
@@ -6,7 +6,7 @@ const slug_js_1 = require("../lib/slug.js");
|
|
|
6
6
|
function registerCheckUpdate(program) {
|
|
7
7
|
program
|
|
8
8
|
.command('check-update [slug]')
|
|
9
|
-
.description('CLI 및 설치된
|
|
9
|
+
.description('CLI 및 설치된 에이전트의 업데이트를 확인합니다')
|
|
10
10
|
.option('--quiet', '업데이트가 있을 때만 머신 리더블 출력')
|
|
11
11
|
.option('--force', '캐시를 무시하고 강제 체크')
|
|
12
12
|
.action(async (slug, opts) => {
|
|
@@ -23,7 +23,7 @@ function registerCheckUpdate(program) {
|
|
|
23
23
|
console.log(` 실행: npm update -g relayax-cli\n`);
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
|
-
//
|
|
26
|
+
// Agent version check
|
|
27
27
|
if (slug) {
|
|
28
28
|
// Resolve to scoped slug
|
|
29
29
|
let scopedSlug;
|
|
@@ -39,15 +39,15 @@ function registerCheckUpdate(program) {
|
|
|
39
39
|
scopedSlug = slug;
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
|
-
const
|
|
43
|
-
if (
|
|
42
|
+
const agentResult = await (0, version_check_js_1.checkAgentVersion)(scopedSlug, force);
|
|
43
|
+
if (agentResult) {
|
|
44
44
|
if (quiet) {
|
|
45
|
-
const byAuthor =
|
|
46
|
-
console.log(`
|
|
45
|
+
const byAuthor = agentResult.author ? ` ${agentResult.author}` : '';
|
|
46
|
+
console.log(`AGENT_UPGRADE_AVAILABLE ${slug} ${agentResult.current} ${agentResult.latest}${byAuthor}`);
|
|
47
47
|
}
|
|
48
48
|
else {
|
|
49
|
-
const byAuthor =
|
|
50
|
-
console.log(`\x1b[33m⚠ ${slug} v${
|
|
49
|
+
const byAuthor = agentResult.author ? ` \x1b[90m(by @${agentResult.author})\x1b[0m` : '';
|
|
50
|
+
console.log(`\x1b[33m⚠ ${slug} v${agentResult.latest} available\x1b[0m${byAuthor} (현재 v${agentResult.current})`);
|
|
51
51
|
console.log(` 실행: relay update ${slug}`);
|
|
52
52
|
}
|
|
53
53
|
}
|
|
@@ -56,11 +56,11 @@ function registerCheckUpdate(program) {
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
else {
|
|
59
|
-
const
|
|
60
|
-
for (const result of
|
|
59
|
+
const agentResults = await (0, version_check_js_1.checkAllAgents)(force);
|
|
60
|
+
for (const result of agentResults) {
|
|
61
61
|
if (quiet) {
|
|
62
62
|
const byAuthor = result.author ? ` ${result.author}` : '';
|
|
63
|
-
console.log(`
|
|
63
|
+
console.log(`AGENT_UPGRADE_AVAILABLE ${result.slug} ${result.current} ${result.latest}${byAuthor}`);
|
|
64
64
|
}
|
|
65
65
|
else {
|
|
66
66
|
const byAuthor = result.author ? ` \x1b[90m(by @${result.author})\x1b[0m` : '';
|
|
@@ -68,7 +68,7 @@ function registerCheckUpdate(program) {
|
|
|
68
68
|
console.log(` 실행: relay update ${result.slug}`);
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
|
-
if (!quiet && !cliResult &&
|
|
71
|
+
if (!quiet && !cliResult && agentResults.length === 0) {
|
|
72
72
|
console.log('모든 것이 최신 상태입니다.');
|
|
73
73
|
}
|
|
74
74
|
}
|
package/dist/commands/create.js
CHANGED
|
@@ -10,6 +10,7 @@ const js_yaml_1 = __importDefault(require("js-yaml"));
|
|
|
10
10
|
const ai_tools_js_1 = require("../lib/ai-tools.js");
|
|
11
11
|
const command_adapter_js_1 = require("../lib/command-adapter.js");
|
|
12
12
|
const init_js_1 = require("./init.js");
|
|
13
|
+
const slug_js_1 = require("../lib/slug.js");
|
|
13
14
|
const DEFAULT_DIRS = ['.relay/skills', '.relay/commands'];
|
|
14
15
|
/**
|
|
15
16
|
* 글로벌 User 커맨드가 없으면 설치한다.
|
|
@@ -23,10 +24,11 @@ function ensureGlobalUserCommands() {
|
|
|
23
24
|
function registerCreate(program) {
|
|
24
25
|
program
|
|
25
26
|
.command('create <name>')
|
|
26
|
-
.description('새 에이전트
|
|
27
|
-
.option('--description <desc>', '
|
|
27
|
+
.description('새 에이전트 프로젝트를 생성합니다')
|
|
28
|
+
.option('--description <desc>', '에이전트 설명')
|
|
29
|
+
.option('--slug <slug>', 'URL용 식별자 (영문 소문자, 숫자, 하이픈)')
|
|
28
30
|
.option('--tags <tags>', '태그 (쉼표 구분)')
|
|
29
|
-
.option('--visibility <visibility>', '공개 범위 (public,
|
|
31
|
+
.option('--visibility <visibility>', '공개 범위 (public, private, internal)')
|
|
30
32
|
.action(async (name, opts) => {
|
|
31
33
|
const json = program.opts().json ?? false;
|
|
32
34
|
const projectPath = process.cwd();
|
|
@@ -39,21 +41,30 @@ function registerCreate(program) {
|
|
|
39
41
|
console.error(JSON.stringify({ error: 'ALREADY_EXISTS', message: '.relay/relay.yaml이 이미 존재합니다.', fix: '기존 .relay/relay.yaml을 확인하세요. 새로 시작하려면 삭제 후 재시도.' }));
|
|
40
42
|
}
|
|
41
43
|
else {
|
|
42
|
-
console.error('.relay/relay.yaml이 이미 존재합니다. 기존
|
|
44
|
+
console.error('.relay/relay.yaml이 이미 존재합니다. 기존 에이전트 프로젝트에서는 `relay init`을 사용하세요.');
|
|
43
45
|
}
|
|
44
46
|
process.exit(1);
|
|
45
47
|
}
|
|
46
48
|
// 2. 메타데이터 수집
|
|
47
|
-
|
|
49
|
+
let slug = opts.slug ?? (0, slug_js_1.slugify)(name);
|
|
48
50
|
let description = opts.description ?? '';
|
|
49
51
|
let tags = opts.tags ? opts.tags.split(',').map((t) => t.trim()).filter(Boolean) : [];
|
|
50
52
|
let visibility = opts.visibility ?? 'public';
|
|
51
53
|
if (json) {
|
|
54
|
+
// --json 모드: slug가 비어있으면 에러
|
|
55
|
+
if (!slug) {
|
|
56
|
+
console.error(JSON.stringify({
|
|
57
|
+
error: 'INVALID_SLUG',
|
|
58
|
+
message: '이름에서 유효한 slug를 생성할 수 없습니다. 영문 이름을 사용하거나 --slug 옵션을 지정하세요.',
|
|
59
|
+
fix: `relay create "${name}" --slug <영문-slug> --description <설명> --json`,
|
|
60
|
+
}));
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
52
63
|
// --json 모드: 필수 값 부족 시 에러 반환 (프롬프트 없음)
|
|
53
64
|
if (!opts.description) {
|
|
54
65
|
console.error(JSON.stringify({
|
|
55
66
|
error: 'MISSING_FIELD',
|
|
56
|
-
message: '
|
|
67
|
+
message: '에이전트 설명이 필요합니다.',
|
|
57
68
|
fix: `relay create ${name} --description <설명> --json`,
|
|
58
69
|
field: 'description',
|
|
59
70
|
}));
|
|
@@ -66,21 +77,21 @@ function registerCreate(program) {
|
|
|
66
77
|
fix: `relay create ${name} --description "${description}" --visibility <visibility> --json`,
|
|
67
78
|
options: [
|
|
68
79
|
{ value: 'public', label: '공개 — 누구나 설치' },
|
|
69
|
-
{ value: '
|
|
70
|
-
{ value: '
|
|
80
|
+
{ value: 'private', label: '링크 공유 — 접근 링크가 있는 사람만' },
|
|
81
|
+
{ value: 'internal', label: '비공개 — Org 멤버만' },
|
|
71
82
|
],
|
|
72
83
|
}));
|
|
73
84
|
process.exit(1);
|
|
74
85
|
}
|
|
75
|
-
if (!['public', '
|
|
86
|
+
if (!['public', 'private', 'internal'].includes(opts.visibility)) {
|
|
76
87
|
console.error(JSON.stringify({
|
|
77
88
|
error: 'INVALID_FIELD',
|
|
78
89
|
message: `유효하지 않은 visibility 값: ${opts.visibility}`,
|
|
79
|
-
fix: `visibility는 public,
|
|
90
|
+
fix: `visibility는 public, private, internal 중 하나여야 합니다.`,
|
|
80
91
|
options: [
|
|
81
92
|
{ value: 'public', label: '공개' },
|
|
82
|
-
{ value: '
|
|
83
|
-
{ value: '
|
|
93
|
+
{ value: 'private', label: '링크 공유' },
|
|
94
|
+
{ value: 'internal', label: '비공개' },
|
|
84
95
|
],
|
|
85
96
|
}));
|
|
86
97
|
process.exit(1);
|
|
@@ -88,10 +99,25 @@ function registerCreate(program) {
|
|
|
88
99
|
}
|
|
89
100
|
else if (isTTY) {
|
|
90
101
|
const { input: promptInput, select: promptSelect } = await import('@inquirer/prompts');
|
|
91
|
-
console.log(`\n \x1b[33m⚡\x1b[0m \x1b[1mrelay create\x1b[0m — 새
|
|
102
|
+
console.log(`\n \x1b[33m⚡\x1b[0m \x1b[1mrelay create\x1b[0m — 새 에이전트 프로젝트\n`);
|
|
103
|
+
// slug가 비어있으면 (한국어 등 비ASCII 이름) slug를 직접 입력받음
|
|
104
|
+
if (!slug) {
|
|
105
|
+
slug = await promptInput({
|
|
106
|
+
message: 'Slug (URL/설치에 사용되는 영문 식별자):',
|
|
107
|
+
validate: (v) => {
|
|
108
|
+
const trimmed = v.trim();
|
|
109
|
+
if (!trimmed)
|
|
110
|
+
return 'slug를 입력해주세요.';
|
|
111
|
+
if (!/^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(trimmed))
|
|
112
|
+
return '소문자, 숫자, 하이픈만 사용 가능합니다.';
|
|
113
|
+
return true;
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
slug = slug.trim();
|
|
117
|
+
}
|
|
92
118
|
if (!description) {
|
|
93
119
|
description = await promptInput({
|
|
94
|
-
message: '
|
|
120
|
+
message: '에이전트 설명:',
|
|
95
121
|
validate: (v) => v.trim().length > 0 ? true : '설명을 입력해주세요.',
|
|
96
122
|
});
|
|
97
123
|
}
|
|
@@ -107,8 +133,8 @@ function registerCreate(program) {
|
|
|
107
133
|
message: '공개 범위:',
|
|
108
134
|
choices: [
|
|
109
135
|
{ name: '공개', value: 'public' },
|
|
110
|
-
{ name: '링크 공유 (접근 링크 필요)', value: '
|
|
111
|
-
{ name: '비공개 (
|
|
136
|
+
{ name: '링크 공유 (접근 링크 필요)', value: 'private' },
|
|
137
|
+
{ name: '비공개 (Org 멤버만)', value: 'internal' },
|
|
112
138
|
],
|
|
113
139
|
});
|
|
114
140
|
}
|
|
@@ -117,12 +143,13 @@ function registerCreate(program) {
|
|
|
117
143
|
fs_1.default.mkdirSync(relayDir, { recursive: true });
|
|
118
144
|
const yamlData = {
|
|
119
145
|
name,
|
|
120
|
-
slug:
|
|
146
|
+
slug: slug,
|
|
121
147
|
description,
|
|
122
148
|
version: '1.0.0',
|
|
123
149
|
type: 'hybrid',
|
|
124
150
|
tags,
|
|
125
151
|
visibility,
|
|
152
|
+
contents: [],
|
|
126
153
|
};
|
|
127
154
|
fs_1.default.writeFileSync(relayYamlPath, js_yaml_1.default.dump(yamlData, { lineWidth: 120 }), 'utf-8');
|
|
128
155
|
// 4. 디렉토리 구조 생성
|
|
@@ -155,7 +182,7 @@ function registerCreate(program) {
|
|
|
155
182
|
console.log(JSON.stringify({
|
|
156
183
|
status: 'ok',
|
|
157
184
|
name,
|
|
158
|
-
slug:
|
|
185
|
+
slug: slug,
|
|
159
186
|
relay_yaml: 'created',
|
|
160
187
|
directories: createdDirs,
|
|
161
188
|
local_commands: localResults,
|
|
@@ -163,7 +190,7 @@ function registerCreate(program) {
|
|
|
163
190
|
}));
|
|
164
191
|
}
|
|
165
192
|
else {
|
|
166
|
-
console.log(`\n\x1b[32m✓ ${name}
|
|
193
|
+
console.log(`\n\x1b[32m✓ ${name} 에이전트 프로젝트 생성 완료\x1b[0m\n`);
|
|
167
194
|
console.log(` .relay/relay.yaml 생성됨`);
|
|
168
195
|
if (createdDirs.length > 0) {
|
|
169
196
|
console.log(` 디렉토리 생성: ${createdDirs.join(', ')}`);
|
|
@@ -31,7 +31,7 @@ function registerDeployRecord(program) {
|
|
|
31
31
|
const resolvedFiles = files.map((f) => f.startsWith('/') || f.startsWith('~')
|
|
32
32
|
? f
|
|
33
33
|
: path_1.default.resolve(f));
|
|
34
|
-
// Find the
|
|
34
|
+
// Find the agent in the appropriate registry
|
|
35
35
|
const localRegistry = (0, config_js_1.loadInstalled)();
|
|
36
36
|
const globalRegistry = (0, config_js_1.loadGlobalInstalled)();
|
|
37
37
|
// Resolve slug — check both registries for short name match
|
|
@@ -47,7 +47,7 @@ function registerDeployRecord(program) {
|
|
|
47
47
|
});
|
|
48
48
|
slug = match ?? slugInput;
|
|
49
49
|
}
|
|
50
|
-
// Check if
|
|
50
|
+
// Check if agent exists in either registry
|
|
51
51
|
const entry = localRegistry[slug] ?? globalRegistry[slug];
|
|
52
52
|
if (!entry) {
|
|
53
53
|
const msg = { error: 'NOT_INSTALLED', message: `'${slugInput}'는 설치되어 있지 않습니다.` };
|
package/dist/commands/diff.js
CHANGED
|
@@ -12,7 +12,7 @@ function registerDiff(program) {
|
|
|
12
12
|
const json = program.opts().json ?? false;
|
|
13
13
|
try {
|
|
14
14
|
const resolved = await (0, slug_js_1.resolveSlug)(slugInput);
|
|
15
|
-
const versions = await (0, api_js_1.
|
|
15
|
+
const versions = await (0, api_js_1.fetchAgentVersions)(resolved.full);
|
|
16
16
|
const ver1 = versions.find((v) => v.version === v1);
|
|
17
17
|
const ver2 = versions.find((v) => v.version === v2);
|
|
18
18
|
if (!ver1 || !ver2) {
|
|
@@ -29,7 +29,7 @@ function registerDiff(program) {
|
|
|
29
29
|
// For now, we use the current version's package_url as fallback
|
|
30
30
|
// The registry API returns the latest version; for specific versions,
|
|
31
31
|
// we'd need a version-specific endpoint
|
|
32
|
-
const info = await (0, api_js_1.
|
|
32
|
+
const info = await (0, api_js_1.fetchAgentInfo)(resolved.full);
|
|
33
33
|
if (!info.package_url) {
|
|
34
34
|
throw new Error('패키지 URL을 가져올 수 없습니다');
|
|
35
35
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
interface AccessCodeResult {
|
|
3
|
+
status: string;
|
|
4
|
+
type: 'org' | 'agent';
|
|
5
|
+
org_id?: string;
|
|
6
|
+
agent_id?: string;
|
|
7
|
+
role?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Use an access code — the code type (org/agent) is resolved server-side.
|
|
11
|
+
* For org codes: joins the org as member.
|
|
12
|
+
* For agent codes: grants agent access (+ auto org join for org private agents).
|
|
13
|
+
*/
|
|
14
|
+
export declare function useAccessCode(code: string): Promise<AccessCodeResult>;
|
|
15
|
+
interface CreateAccessCodeResult {
|
|
16
|
+
id: string;
|
|
17
|
+
code: string;
|
|
18
|
+
type: string;
|
|
19
|
+
max_uses: number | null;
|
|
20
|
+
expires_at: string | null;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Create a new access code for an agent or org.
|
|
24
|
+
*/
|
|
25
|
+
export declare function createAccessCode(opts: {
|
|
26
|
+
type: 'org' | 'agent';
|
|
27
|
+
org_id?: string;
|
|
28
|
+
agent_id?: string;
|
|
29
|
+
max_uses?: number;
|
|
30
|
+
expires_at?: string;
|
|
31
|
+
}): Promise<CreateAccessCodeResult>;
|
|
32
|
+
export declare function registerGrant(program: Command): void;
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useAccessCode = useAccessCode;
|
|
4
|
+
exports.createAccessCode = createAccessCode;
|
|
5
|
+
exports.registerGrant = registerGrant;
|
|
6
|
+
const config_js_1 = require("../lib/config.js");
|
|
7
|
+
const init_js_1 = require("./init.js");
|
|
8
|
+
/**
|
|
9
|
+
* Use an access code — the code type (org/agent) is resolved server-side.
|
|
10
|
+
* For org codes: joins the org as member.
|
|
11
|
+
* For agent codes: grants agent access (+ auto org join for org private agents).
|
|
12
|
+
*/
|
|
13
|
+
async function useAccessCode(code) {
|
|
14
|
+
const token = await (0, config_js_1.getValidToken)();
|
|
15
|
+
if (!token) {
|
|
16
|
+
throw new Error('LOGIN_REQUIRED');
|
|
17
|
+
}
|
|
18
|
+
const res = await fetch(`${config_js_1.API_URL}/api/access-codes/${code}/use`, {
|
|
19
|
+
method: 'POST',
|
|
20
|
+
headers: {
|
|
21
|
+
'Content-Type': 'application/json',
|
|
22
|
+
Authorization: `Bearer ${token}`,
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
if (!res.ok) {
|
|
26
|
+
const body = await res.json().catch(() => ({}));
|
|
27
|
+
const errCode = body.error ?? String(res.status);
|
|
28
|
+
switch (errCode) {
|
|
29
|
+
case 'INVALID_LINK':
|
|
30
|
+
throw new Error('접근 코드가 유효하지 않거나 만료되었습니다.');
|
|
31
|
+
default:
|
|
32
|
+
throw new Error(body.message ?? `접근 코드 사용 실패 (${res.status})`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return res.json();
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Create a new access code for an agent or org.
|
|
39
|
+
*/
|
|
40
|
+
async function createAccessCode(opts) {
|
|
41
|
+
const token = await (0, config_js_1.getValidToken)();
|
|
42
|
+
if (!token) {
|
|
43
|
+
throw new Error('LOGIN_REQUIRED');
|
|
44
|
+
}
|
|
45
|
+
const res = await fetch(`${config_js_1.API_URL}/api/access-codes`, {
|
|
46
|
+
method: 'POST',
|
|
47
|
+
headers: {
|
|
48
|
+
'Content-Type': 'application/json',
|
|
49
|
+
Authorization: `Bearer ${token}`,
|
|
50
|
+
},
|
|
51
|
+
body: JSON.stringify(opts),
|
|
52
|
+
});
|
|
53
|
+
if (!res.ok) {
|
|
54
|
+
const body = await res.json().catch(() => ({}));
|
|
55
|
+
throw new Error(body.message ?? `접근 코드 생성 실패 (${res.status})`);
|
|
56
|
+
}
|
|
57
|
+
return res.json();
|
|
58
|
+
}
|
|
59
|
+
function ensureInit(json) {
|
|
60
|
+
if (!(0, init_js_1.hasGlobalUserCommands)()) {
|
|
61
|
+
if (json) {
|
|
62
|
+
console.error(JSON.stringify({ error: 'NOT_INITIALIZED', message: 'relay init을 먼저 실행하세요.', fix: 'relay init 실행하세요.' }));
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
console.error('\x1b[33m⚠ relay init이 실행되지 않았습니다. 먼저 relay init을 실행하세요.\x1b[0m');
|
|
66
|
+
}
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function handleError(err, json) {
|
|
71
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
72
|
+
if (message === 'LOGIN_REQUIRED') {
|
|
73
|
+
if (json) {
|
|
74
|
+
console.error(JSON.stringify({ error: 'LOGIN_REQUIRED', message: '로그인이 필요합니다.', fix: 'relay login 실행 후 재시도하세요.' }));
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
console.error('\x1b[31m오류: 로그인이 필요합니다.\x1b[0m');
|
|
78
|
+
console.error(' relay login 을 먼저 실행하세요.');
|
|
79
|
+
}
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
if (json) {
|
|
83
|
+
console.error(JSON.stringify({ error: 'GRANT_FAILED', message, fix: '접근 코드를 확인 후 재시도하세요.' }));
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
console.error(`\x1b[31m오류: ${message}\x1b[0m`);
|
|
87
|
+
}
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
function registerGrant(program) {
|
|
91
|
+
const grant = program
|
|
92
|
+
.command('grant')
|
|
93
|
+
.description('접근 코드를 사용하거나 생성합니다');
|
|
94
|
+
// relay grant --code <code> (use an access code)
|
|
95
|
+
grant
|
|
96
|
+
.command('use')
|
|
97
|
+
.description('접근 코드를 사용하여 org 가입 또는 에이전트 접근 권한을 획득합니다')
|
|
98
|
+
.requiredOption('--code <code>', '접근 코드')
|
|
99
|
+
.action(async (opts) => {
|
|
100
|
+
const json = program.opts().json ?? false;
|
|
101
|
+
ensureInit(json);
|
|
102
|
+
try {
|
|
103
|
+
const result = await useAccessCode(opts.code);
|
|
104
|
+
if (json) {
|
|
105
|
+
console.log(JSON.stringify({ ...result, status: 'ok' }));
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
if (result.type === 'org') {
|
|
109
|
+
console.log(`\x1b[32m✅ Organization에 가입했습니다 (역할: ${result.role ?? 'member'})\x1b[0m`);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
console.log(`\x1b[32m✅ 에이전트 접근 권한이 부여되었습니다\x1b[0m`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
handleError(err, json);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
// relay grant create --agent <slug> [--max-uses N] [--expires-at DATE]
|
|
121
|
+
grant
|
|
122
|
+
.command('create')
|
|
123
|
+
.description('에이전트 또는 org의 접근 코드를 생성합니다')
|
|
124
|
+
.option('--agent <slug>', '에이전트 slug')
|
|
125
|
+
.option('--org <slug>', 'Organization slug')
|
|
126
|
+
.option('--max-uses <n>', '최대 사용 횟수', parseInt)
|
|
127
|
+
.option('--expires-at <date>', '만료일 (ISO 8601)')
|
|
128
|
+
.action(async (opts) => {
|
|
129
|
+
const json = program.opts().json ?? false;
|
|
130
|
+
ensureInit(json);
|
|
131
|
+
if (!opts.agent && !opts.org) {
|
|
132
|
+
const msg = '--agent 또는 --org 옵션이 필요합니다.';
|
|
133
|
+
if (json) {
|
|
134
|
+
console.error(JSON.stringify({ error: 'MISSING_OPTION', message: msg }));
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
console.error(`\x1b[31m오류: ${msg}\x1b[0m`);
|
|
138
|
+
}
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
const token = await (0, config_js_1.getValidToken)();
|
|
143
|
+
if (!token)
|
|
144
|
+
throw new Error('LOGIN_REQUIRED');
|
|
145
|
+
// Resolve agent/org ID from slug
|
|
146
|
+
let agentId;
|
|
147
|
+
let orgId;
|
|
148
|
+
if (opts.agent) {
|
|
149
|
+
const res = await fetch(`${config_js_1.API_URL}/api/agents/${opts.agent}`, {
|
|
150
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
151
|
+
});
|
|
152
|
+
if (!res.ok)
|
|
153
|
+
throw new Error('에이전트를 찾을 수 없습니다.');
|
|
154
|
+
const agent = await res.json();
|
|
155
|
+
agentId = agent.id;
|
|
156
|
+
}
|
|
157
|
+
if (opts.org) {
|
|
158
|
+
const res = await fetch(`${config_js_1.API_URL}/api/orgs/${opts.org}`, {
|
|
159
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
160
|
+
});
|
|
161
|
+
if (!res.ok)
|
|
162
|
+
throw new Error('Organization을 찾을 수 없습니다.');
|
|
163
|
+
const org = await res.json();
|
|
164
|
+
orgId = org.id;
|
|
165
|
+
}
|
|
166
|
+
const result = await createAccessCode({
|
|
167
|
+
type: agentId ? 'agent' : 'org',
|
|
168
|
+
agent_id: agentId,
|
|
169
|
+
org_id: orgId,
|
|
170
|
+
max_uses: opts.maxUses,
|
|
171
|
+
expires_at: opts.expiresAt,
|
|
172
|
+
});
|
|
173
|
+
if (json) {
|
|
174
|
+
console.log(JSON.stringify({ status: 'created', ...result }));
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
console.log(`\x1b[32m✅ 접근 코드가 생성되었습니다\x1b[0m`);
|
|
178
|
+
console.log(`\n 코드: \x1b[36m${result.code}\x1b[0m`);
|
|
179
|
+
if (result.max_uses)
|
|
180
|
+
console.log(` 최대 사용: ${result.max_uses}회`);
|
|
181
|
+
if (result.expires_at)
|
|
182
|
+
console.log(` 만료: ${new Date(result.expires_at).toLocaleDateString('ko-KR')}`);
|
|
183
|
+
console.log(`\n \x1b[90m사용 방법: relay grant use --code ${result.code}\x1b[0m`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
catch (err) {
|
|
187
|
+
handleError(err, json);
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
}
|