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/init.js
CHANGED
|
@@ -13,7 +13,7 @@ const command_adapter_js_1 = require("../lib/command-adapter.js");
|
|
|
13
13
|
const config_js_1 = require("../lib/config.js");
|
|
14
14
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
15
15
|
const pkg = require('../../package.json');
|
|
16
|
-
const
|
|
16
|
+
const VALID_AGENT_DIRS = ['skills', 'agents', 'rules', 'commands'];
|
|
17
17
|
function resolveTools(toolsArg) {
|
|
18
18
|
const raw = toolsArg.trim().toLowerCase();
|
|
19
19
|
if (raw === 'all') {
|
|
@@ -30,14 +30,14 @@ function resolveTools(toolsArg) {
|
|
|
30
30
|
function showWelcome() {
|
|
31
31
|
const lines = [
|
|
32
32
|
'',
|
|
33
|
-
' \x1b[33m⚡\x1b[0m \x1b[1mrelay\x1b[0m — Agent
|
|
33
|
+
' \x1b[33m⚡\x1b[0m \x1b[1mrelay\x1b[0m — Agent Marketplace',
|
|
34
34
|
'',
|
|
35
35
|
' 에이전트 CLI에 relay 커맨드를 연결합니다.',
|
|
36
36
|
'',
|
|
37
37
|
' \x1b[2mUser 커맨드 (글로벌)\x1b[0m',
|
|
38
|
-
' /relay-install
|
|
38
|
+
' /relay-install 에이전트 탐색 & 설치',
|
|
39
39
|
' /relay-status 설치 현황 & Space',
|
|
40
|
-
' /relay-uninstall
|
|
40
|
+
' /relay-uninstall 에이전트 삭제',
|
|
41
41
|
'',
|
|
42
42
|
];
|
|
43
43
|
console.log(lines.join('\n'));
|
|
@@ -69,10 +69,8 @@ function installGlobalUserCommands() {
|
|
|
69
69
|
const currentIds = new Set(command_adapter_js_1.USER_COMMANDS.map((c) => c.id));
|
|
70
70
|
const commands = [];
|
|
71
71
|
const tools = [];
|
|
72
|
-
// 감지된 CLI가 없으면
|
|
73
|
-
const targetDirs = globalCLIs.
|
|
74
|
-
? globalCLIs.map((t) => ({ name: t.name, dir: (0, command_adapter_js_1.getGlobalCommandDirForTool)(t.skillsDir), getPath: (id) => (0, command_adapter_js_1.getGlobalCommandPathForTool)(t.skillsDir, id) }))
|
|
75
|
-
: [{ name: 'Claude Code', dir: (0, command_adapter_js_1.getGlobalCommandDir)(), getPath: (id) => (0, command_adapter_js_1.getGlobalCommandPath)(id) }];
|
|
72
|
+
// 감지된 CLI가 없으면 설치하지 않음 (사용자가 --tools로 지정하거나 CLI를 먼저 설치해야 함)
|
|
73
|
+
const targetDirs = globalCLIs.map((t) => ({ name: t.name, dir: (0, command_adapter_js_1.getGlobalCommandDirForTool)(t.skillsDir), getPath: (id) => (0, command_adapter_js_1.getGlobalCommandPathForTool)(t.skillsDir, id) }));
|
|
76
74
|
for (const target of targetDirs) {
|
|
77
75
|
fs_1.default.mkdirSync(target.dir, { recursive: true });
|
|
78
76
|
// 기존 파일 중 현재 목록에 없는 것 제거
|
|
@@ -101,16 +99,16 @@ function hasGlobalUserCommands() {
|
|
|
101
99
|
return command_adapter_js_1.USER_COMMANDS.every((cmd) => fs_1.default.existsSync((0, command_adapter_js_1.getGlobalCommandPath)(cmd.id)));
|
|
102
100
|
}
|
|
103
101
|
/**
|
|
104
|
-
*
|
|
102
|
+
* 에이전트 프로젝트인지 감지한다 (.relay/ 디렉토리 내 relay.yaml 또는 에이전트 디렉토리 구조).
|
|
105
103
|
*/
|
|
106
|
-
function
|
|
104
|
+
function isAgentProject(projectPath) {
|
|
107
105
|
const relayDir = path_1.default.join(projectPath, '.relay');
|
|
108
106
|
if (!fs_1.default.existsSync(relayDir))
|
|
109
107
|
return false;
|
|
110
108
|
if (fs_1.default.existsSync(path_1.default.join(relayDir, 'relay.yaml'))) {
|
|
111
109
|
return true;
|
|
112
110
|
}
|
|
113
|
-
return
|
|
111
|
+
return VALID_AGENT_DIRS.some((d) => {
|
|
114
112
|
const dirPath = path_1.default.join(relayDir, d);
|
|
115
113
|
if (!fs_1.default.existsSync(dirPath))
|
|
116
114
|
return false;
|
|
@@ -131,7 +129,7 @@ function registerInit(program) {
|
|
|
131
129
|
const projectPath = process.cwd();
|
|
132
130
|
const detected = (0, ai_tools_js_1.detectAgentCLIs)(projectPath);
|
|
133
131
|
const detectedIds = new Set(detected.map((t) => t.value));
|
|
134
|
-
const isBuilder =
|
|
132
|
+
const isBuilder = isAgentProject(projectPath);
|
|
135
133
|
// ── 0. --json 모드에서 --tools/--all 없으면 MISSING_TOOLS 에러 ──
|
|
136
134
|
if (json && !opts.tools && !opts.all && !opts.auto) {
|
|
137
135
|
const detectedOptions = detected.map((t) => ({ value: t.value, label: t.name }));
|
|
@@ -163,7 +161,7 @@ function registerInit(program) {
|
|
|
163
161
|
};
|
|
164
162
|
(0, config_js_1.saveInstalled)(installed);
|
|
165
163
|
}
|
|
166
|
-
// ── 2. 로컬 Builder 커맨드 (
|
|
164
|
+
// ── 2. 로컬 Builder 커맨드 (에이전트 프로젝트인 경우) ──
|
|
167
165
|
// relay-publish가 글로벌로 승격되어 BUILDER_COMMANDS가 비어있으면 스킵
|
|
168
166
|
const localResults = [];
|
|
169
167
|
if (isBuilder && command_adapter_js_1.BUILDER_COMMANDS.length > 0) {
|
|
@@ -247,7 +245,7 @@ function registerInit(program) {
|
|
|
247
245
|
console.log(`\n\x1b[32m✓ relay 초기화 완료\x1b[0m\n`);
|
|
248
246
|
// 글로벌
|
|
249
247
|
{
|
|
250
|
-
const toolNames = globalTools.length > 0 ? globalTools.join(', ') : '
|
|
248
|
+
const toolNames = globalTools.length > 0 ? globalTools.join(', ') : '(감지된 CLI 없음)';
|
|
251
249
|
console.log(` \x1b[36mUser 커맨드 (글로벌)\x1b[0m — ${globalStatus === 'updated' ? '업데이트됨' : '설치됨'}`);
|
|
252
250
|
console.log(` 감지된 CLI: \x1b[36m${toolNames}\x1b[0m`);
|
|
253
251
|
for (const cmd of command_adapter_js_1.USER_COMMANDS) {
|
|
@@ -267,7 +265,7 @@ function registerInit(program) {
|
|
|
267
265
|
console.log();
|
|
268
266
|
}
|
|
269
267
|
if (!isBuilder) {
|
|
270
|
-
console.log('
|
|
268
|
+
console.log(' 에이전트를 만들려면 \x1b[33mrelay create <name>\x1b[0m을 사용하세요.');
|
|
271
269
|
console.log();
|
|
272
270
|
}
|
|
273
271
|
console.log(' IDE를 재시작하면 슬래시 커맨드가 활성화됩니다.');
|
package/dist/commands/install.js
CHANGED
|
@@ -16,8 +16,8 @@ const init_js_1 = require("./init.js");
|
|
|
16
16
|
function registerInstall(program) {
|
|
17
17
|
program
|
|
18
18
|
.command('install <slug>')
|
|
19
|
-
.description('에이전트
|
|
20
|
-
.option('--join-code <code>', '초대 코드 (Organization
|
|
19
|
+
.description('에이전트 패키지를 .relay/agents/에 다운로드합니다')
|
|
20
|
+
.option('--join-code <code>', '초대 코드 (Organization 에이전트 설치 시 자동 가입)')
|
|
21
21
|
.action(async (slugInput, _opts) => {
|
|
22
22
|
const json = program.opts().json ?? false;
|
|
23
23
|
const projectPath = process.cwd();
|
|
@@ -30,12 +30,11 @@ function registerInstall(program) {
|
|
|
30
30
|
(0, init_js_1.installGlobalUserCommands)();
|
|
31
31
|
}
|
|
32
32
|
try {
|
|
33
|
-
// Resolve scoped slug and fetch
|
|
34
|
-
let
|
|
33
|
+
// Resolve scoped slug and fetch agent metadata
|
|
34
|
+
let agent;
|
|
35
35
|
let slug;
|
|
36
36
|
let parsed;
|
|
37
|
-
// Extract version from @owner/
|
|
38
|
-
// Extract version from @owner/team@version syntax (e.g. acme/writer@1.2.0)
|
|
37
|
+
// Extract version from @owner/agent@version syntax (e.g. acme/writer@1.2.0)
|
|
39
38
|
// Version-specific install is not yet supported by the registry API;
|
|
40
39
|
// the match is kept for future use when per-version package URLs are available.
|
|
41
40
|
const versionMatch = slugInput.match(/^(.+)@(\d+\.\d+\.\d+.*)$/);
|
|
@@ -43,7 +42,7 @@ function registerInstall(program) {
|
|
|
43
42
|
parsed = await (0, slug_js_1.resolveSlug)(actualSlugInput);
|
|
44
43
|
slug = parsed.full;
|
|
45
44
|
try {
|
|
46
|
-
|
|
45
|
+
agent = await (0, api_js_1.fetchAgentInfo)(slug);
|
|
47
46
|
}
|
|
48
47
|
catch (fetchErr) {
|
|
49
48
|
const fetchMsg = fetchErr instanceof Error ? fetchErr.message : String(fetchErr);
|
|
@@ -61,19 +60,19 @@ function registerInstall(program) {
|
|
|
61
60
|
}
|
|
62
61
|
}
|
|
63
62
|
catch { /* ignore parse errors */ }
|
|
64
|
-
//
|
|
65
|
-
if (errorVisibility === '
|
|
63
|
+
// Private agent: show purchase info + relay access hint
|
|
64
|
+
if (errorVisibility === 'private' || purchaseInfo) {
|
|
66
65
|
if (json) {
|
|
67
66
|
console.error(JSON.stringify({
|
|
68
|
-
error: '
|
|
69
|
-
message: '이
|
|
67
|
+
error: 'ACCESS_REQUIRED',
|
|
68
|
+
message: '이 에이전트는 접근 권한이 필요합니다.',
|
|
70
69
|
slug,
|
|
71
70
|
purchase_info: purchaseInfo ?? null,
|
|
72
71
|
fix: '접근 링크 코드가 있으면: relay access <slug> --code <코드>',
|
|
73
72
|
}));
|
|
74
73
|
}
|
|
75
74
|
else {
|
|
76
|
-
console.error('\x1b[31m이
|
|
75
|
+
console.error('\x1b[31m이 에이전트는 접근 권한이 필요합니다.\x1b[0m');
|
|
77
76
|
if (purchaseInfo?.message) {
|
|
78
77
|
console.error(`\n \x1b[36m${purchaseInfo.message}\x1b[0m`);
|
|
79
78
|
}
|
|
@@ -85,17 +84,17 @@ function registerInstall(program) {
|
|
|
85
84
|
process.exit(1);
|
|
86
85
|
}
|
|
87
86
|
if (membershipStatus === 'member') {
|
|
88
|
-
// Member but no access to this specific
|
|
87
|
+
// Member but no access to this specific agent
|
|
89
88
|
if (json) {
|
|
90
89
|
console.error(JSON.stringify({
|
|
91
90
|
error: 'NO_ACCESS',
|
|
92
|
-
message: '이
|
|
91
|
+
message: '이 에이전트에 대한 접근 권한이 없습니다.',
|
|
93
92
|
slug,
|
|
94
|
-
fix: '이
|
|
93
|
+
fix: '이 에이전트의 접근 링크 코드가 있으면 `relay access ' + slugInput + ' --code <코드>`로 접근 권한을 얻으세요. 없으면 에이전트 제작자에게 문의하세요.',
|
|
95
94
|
}));
|
|
96
95
|
}
|
|
97
96
|
else {
|
|
98
|
-
console.error('\x1b[31m이
|
|
97
|
+
console.error('\x1b[31m이 에이전트에 대한 접근 권한이 없습니다.\x1b[0m');
|
|
99
98
|
}
|
|
100
99
|
process.exit(1);
|
|
101
100
|
}
|
|
@@ -103,13 +102,13 @@ function registerInstall(program) {
|
|
|
103
102
|
if (json) {
|
|
104
103
|
console.error(JSON.stringify({
|
|
105
104
|
error: 'ACCESS_REQUIRED',
|
|
106
|
-
message: '이
|
|
105
|
+
message: '이 에이전트는 접근 권한이 필요합니다.',
|
|
107
106
|
slug,
|
|
108
107
|
fix: '초대 코드가 있으면 `relay join <org-slug> --code <코드>`로 가입하세요.',
|
|
109
108
|
}));
|
|
110
109
|
}
|
|
111
110
|
else {
|
|
112
|
-
console.error('\x1b[31m이
|
|
111
|
+
console.error('\x1b[31m이 에이전트는 접근 권한이 필요합니다.\x1b[0m');
|
|
113
112
|
console.error('\x1b[33m초대 코드가 있으면 `relay join <org-slug> --code <코드>`로 가입하세요.\x1b[0m');
|
|
114
113
|
}
|
|
115
114
|
process.exit(1);
|
|
@@ -119,18 +118,20 @@ function registerInstall(program) {
|
|
|
119
118
|
throw fetchErr;
|
|
120
119
|
}
|
|
121
120
|
}
|
|
122
|
-
if (!
|
|
123
|
-
throw new Error('
|
|
124
|
-
|
|
121
|
+
if (!agent)
|
|
122
|
+
throw new Error('에이전트 정보를 가져오지 못했습니다.');
|
|
123
|
+
// Re-bind as non-optional so TypeScript tracks the narrowing through nested scopes
|
|
124
|
+
let resolvedAgent = agent;
|
|
125
|
+
const agentDir = path_1.default.join(projectPath, '.relay', 'agents', parsed.owner, parsed.name);
|
|
125
126
|
// 2. Visibility check + auto-login
|
|
126
|
-
const visibility =
|
|
127
|
-
if (visibility === '
|
|
127
|
+
const visibility = resolvedAgent.visibility ?? 'public';
|
|
128
|
+
if (visibility === 'internal') {
|
|
128
129
|
let token = await (0, config_js_1.getValidToken)();
|
|
129
130
|
if (!token) {
|
|
130
131
|
const isTTY = Boolean(process.stdin.isTTY);
|
|
131
132
|
if (isTTY && !json) {
|
|
132
133
|
// Auto-login: TTY 환경에서 자동으로 login 플로우 트리거
|
|
133
|
-
console.error('\x1b[33m⚙ 이
|
|
134
|
+
console.error('\x1b[33m⚙ 이 에이전트는 로그인이 필요합니다. 로그인을 시작합니다...\x1b[0m');
|
|
134
135
|
const { runLogin } = await import('./login.js');
|
|
135
136
|
await runLogin();
|
|
136
137
|
token = await (0, config_js_1.getValidToken)();
|
|
@@ -141,12 +142,12 @@ function registerInstall(program) {
|
|
|
141
142
|
error: 'LOGIN_REQUIRED',
|
|
142
143
|
visibility,
|
|
143
144
|
slug,
|
|
144
|
-
message: '이
|
|
145
|
+
message: '이 에이전트는 로그인이 필요합니다. relay login을 먼저 실행하세요.',
|
|
145
146
|
fix: 'relay login 실행 후 재시도하세요.',
|
|
146
147
|
}));
|
|
147
148
|
}
|
|
148
149
|
else {
|
|
149
|
-
console.error('\x1b[31m이
|
|
150
|
+
console.error('\x1b[31m이 에이전트는 로그인이 필요합니다. relay login 을 먼저 실행하세요.\x1b[0m');
|
|
150
151
|
}
|
|
151
152
|
process.exit(1);
|
|
152
153
|
}
|
|
@@ -155,30 +156,30 @@ function registerInstall(program) {
|
|
|
155
156
|
// 3. Download package (retry once if signed URL expired)
|
|
156
157
|
let tarPath;
|
|
157
158
|
try {
|
|
158
|
-
tarPath = await (0, storage_js_1.downloadPackage)(
|
|
159
|
+
tarPath = await (0, storage_js_1.downloadPackage)(resolvedAgent.package_url, tempDir);
|
|
159
160
|
}
|
|
160
161
|
catch (dlErr) {
|
|
161
162
|
const dlMsg = dlErr instanceof Error ? dlErr.message : String(dlErr);
|
|
162
163
|
if (dlMsg.includes('403') || dlMsg.includes('expired')) {
|
|
163
|
-
// Signed URL expired — re-fetch
|
|
164
|
+
// Signed URL expired — re-fetch agent info for new URL and retry
|
|
164
165
|
if (!json) {
|
|
165
166
|
console.error('\x1b[33m⚙ 다운로드 URL 만료, 재시도 중...\x1b[0m');
|
|
166
167
|
}
|
|
167
|
-
|
|
168
|
-
tarPath = await (0, storage_js_1.downloadPackage)(
|
|
168
|
+
resolvedAgent = await (0, api_js_1.fetchAgentInfo)(slug);
|
|
169
|
+
tarPath = await (0, storage_js_1.downloadPackage)(resolvedAgent.package_url, tempDir);
|
|
169
170
|
}
|
|
170
171
|
else {
|
|
171
172
|
throw dlErr;
|
|
172
173
|
}
|
|
173
174
|
}
|
|
174
|
-
// 4. Extract to .relay/
|
|
175
|
-
if (fs_1.default.existsSync(
|
|
176
|
-
fs_1.default.rmSync(
|
|
175
|
+
// 4. Extract to .relay/agents/<slug>/
|
|
176
|
+
if (fs_1.default.existsSync(agentDir)) {
|
|
177
|
+
fs_1.default.rmSync(agentDir, { recursive: true, force: true });
|
|
177
178
|
}
|
|
178
|
-
fs_1.default.mkdirSync(
|
|
179
|
-
await (0, storage_js_1.extractPackage)(tarPath,
|
|
179
|
+
fs_1.default.mkdirSync(agentDir, { recursive: true });
|
|
180
|
+
await (0, storage_js_1.extractPackage)(tarPath, agentDir);
|
|
180
181
|
// 4.5. Inject preamble (update check) into SKILL.md and commands
|
|
181
|
-
(0, preamble_js_1.
|
|
182
|
+
(0, preamble_js_1.injectPreambleToAgent)(agentDir, slug);
|
|
182
183
|
// 5. Count extracted files
|
|
183
184
|
function countFiles(dir) {
|
|
184
185
|
let count = 0;
|
|
@@ -194,57 +195,57 @@ function registerInstall(program) {
|
|
|
194
195
|
}
|
|
195
196
|
return count;
|
|
196
197
|
}
|
|
197
|
-
const fileCount = countFiles(
|
|
198
|
+
const fileCount = countFiles(agentDir);
|
|
198
199
|
// 6. Record in installed.json
|
|
199
200
|
const installed = (0, config_js_1.loadInstalled)();
|
|
200
201
|
installed[slug] = {
|
|
201
|
-
|
|
202
|
-
version:
|
|
202
|
+
agent_id: resolvedAgent.id,
|
|
203
|
+
version: resolvedAgent.version,
|
|
203
204
|
installed_at: new Date().toISOString(),
|
|
204
|
-
files: [
|
|
205
|
+
files: [agentDir],
|
|
205
206
|
};
|
|
206
207
|
(0, config_js_1.saveInstalled)(installed);
|
|
207
|
-
// 7. Report install + usage ping (non-blocking,
|
|
208
|
-
await (0, api_js_1.reportInstall)(
|
|
209
|
-
(0, api_js_1.sendUsagePing)(
|
|
208
|
+
// 7. Report install + usage ping (non-blocking, agent_id 기반)
|
|
209
|
+
await (0, api_js_1.reportInstall)(resolvedAgent.id, slug, resolvedAgent.version);
|
|
210
|
+
(0, api_js_1.sendUsagePing)(resolvedAgent.id, slug, resolvedAgent.version);
|
|
210
211
|
const result = {
|
|
211
212
|
status: 'ok',
|
|
212
|
-
|
|
213
|
+
agent: resolvedAgent.name,
|
|
213
214
|
slug,
|
|
214
|
-
version:
|
|
215
|
-
commands:
|
|
215
|
+
version: resolvedAgent.version,
|
|
216
|
+
commands: resolvedAgent.commands,
|
|
216
217
|
files: fileCount,
|
|
217
|
-
install_path:
|
|
218
|
-
author:
|
|
219
|
-
username:
|
|
220
|
-
display_name:
|
|
221
|
-
contact_links:
|
|
218
|
+
install_path: agentDir,
|
|
219
|
+
author: resolvedAgent.author ? {
|
|
220
|
+
username: resolvedAgent.author.username,
|
|
221
|
+
display_name: resolvedAgent.author.display_name ?? null,
|
|
222
|
+
contact_links: resolvedAgent.author.contact_links ?? [],
|
|
222
223
|
} : null,
|
|
223
|
-
welcome:
|
|
224
|
+
welcome: resolvedAgent.welcome ?? null,
|
|
224
225
|
};
|
|
225
226
|
if (json) {
|
|
226
227
|
console.log(JSON.stringify(result));
|
|
227
228
|
}
|
|
228
229
|
else {
|
|
229
|
-
const authorUsername =
|
|
230
|
-
const authorDisplayName =
|
|
230
|
+
const authorUsername = resolvedAgent.author?.username;
|
|
231
|
+
const authorDisplayName = resolvedAgent.author?.display_name ?? authorUsername ?? '';
|
|
231
232
|
const authorSuffix = authorUsername ? ` \x1b[90mby @${authorUsername}\x1b[0m` : '';
|
|
232
|
-
console.log(`\n\x1b[32m✓ ${
|
|
233
|
-
console.log(` 위치: \x1b[36m${
|
|
233
|
+
console.log(`\n\x1b[32m✓ ${resolvedAgent.name} 다운로드 완료\x1b[0m v${resolvedAgent.version}${authorSuffix}`);
|
|
234
|
+
console.log(` 위치: \x1b[36m${agentDir}\x1b[0m`);
|
|
234
235
|
console.log(` 파일: ${fileCount}개`);
|
|
235
|
-
if (
|
|
236
|
+
if (resolvedAgent.commands.length > 0) {
|
|
236
237
|
console.log('\n 포함된 커맨드:');
|
|
237
|
-
for (const cmd of
|
|
238
|
+
for (const cmd of resolvedAgent.commands) {
|
|
238
239
|
console.log(` \x1b[33m/${cmd.name}\x1b[0m - ${cmd.description}`);
|
|
239
240
|
}
|
|
240
241
|
}
|
|
241
242
|
// Builder business card
|
|
242
|
-
const contactParts = (0, contact_format_js_1.formatContactParts)(
|
|
243
|
-
const hasCard =
|
|
243
|
+
const contactParts = (0, contact_format_js_1.formatContactParts)(resolvedAgent.author?.contact_links);
|
|
244
|
+
const hasCard = resolvedAgent.welcome || contactParts.length > 0 || authorUsername;
|
|
244
245
|
if (hasCard) {
|
|
245
246
|
console.log(`\n \x1b[90m┌─ ${authorDisplayName || authorUsername || '빌더'}의 명함 ${'─'.repeat(Math.max(0, 34 - (authorDisplayName || authorUsername || '빌더').length))}┐\x1b[0m`);
|
|
246
|
-
if (
|
|
247
|
-
const truncated =
|
|
247
|
+
if (resolvedAgent.welcome) {
|
|
248
|
+
const truncated = resolvedAgent.welcome.length > 45 ? resolvedAgent.welcome.slice(0, 45) + '...' : resolvedAgent.welcome;
|
|
248
249
|
console.log(` \x1b[90m│\x1b[0m 💬 "${truncated}"`);
|
|
249
250
|
}
|
|
250
251
|
if (contactParts.length > 0) {
|
|
@@ -256,15 +257,15 @@ function registerInstall(program) {
|
|
|
256
257
|
console.log(` \x1b[90m└${'─'.repeat(44)}┘\x1b[0m`);
|
|
257
258
|
}
|
|
258
259
|
// Usage hint (type-aware)
|
|
259
|
-
const
|
|
260
|
-
if (
|
|
260
|
+
const agentType = resolvedAgent.type;
|
|
261
|
+
if (agentType === 'passive') {
|
|
261
262
|
console.log(`\n\x1b[33m💡 자동 적용됩니다. 별도 실행 없이 동작합니다.\x1b[0m`);
|
|
262
263
|
}
|
|
263
|
-
else if (
|
|
264
|
-
console.log(`\n\x1b[33m💡 자동 적용 + \x1b[1m/${
|
|
264
|
+
else if (agentType === 'hybrid' && resolvedAgent.commands && resolvedAgent.commands.length > 0) {
|
|
265
|
+
console.log(`\n\x1b[33m💡 자동 적용 + \x1b[1m/${resolvedAgent.commands[0].name}\x1b[0m\x1b[33m 으로 추가 기능을 사용할 수 있습니다.\x1b[0m`);
|
|
265
266
|
}
|
|
266
|
-
else if (
|
|
267
|
-
console.log(`\n\x1b[33m💡 사용법: \x1b[1m/${
|
|
267
|
+
else if (resolvedAgent.commands && resolvedAgent.commands.length > 0) {
|
|
268
|
+
console.log(`\n\x1b[33m💡 사용법: \x1b[1m/${resolvedAgent.commands[0].name}\x1b[0m`);
|
|
268
269
|
}
|
|
269
270
|
else {
|
|
270
271
|
console.log(`\n\x1b[33m💡 설치 완료! AI 에이전트에서 사용할 수 있습니다.\x1b[0m`);
|
package/dist/commands/join.js
CHANGED
|
@@ -9,8 +9,8 @@ async function joinOrg(orgSlug, code) {
|
|
|
9
9
|
if (!token) {
|
|
10
10
|
throw new Error('LOGIN_REQUIRED');
|
|
11
11
|
}
|
|
12
|
-
// Use the
|
|
13
|
-
const res = await fetch(`${config_js_1.API_URL}/api/
|
|
12
|
+
// Use the access code via API
|
|
13
|
+
const res = await fetch(`${config_js_1.API_URL}/api/access-codes/${code}/use`, {
|
|
14
14
|
method: 'POST',
|
|
15
15
|
headers: {
|
|
16
16
|
'Content-Type': 'application/json',
|
|
@@ -56,7 +56,7 @@ function registerJoin(program) {
|
|
|
56
56
|
console.log(`\n\x1b[33m 대시보드: www.relayax.com/orgs/${slug}\x1b[0m`);
|
|
57
57
|
}
|
|
58
58
|
else {
|
|
59
|
-
console.log(`\x1b[32m✅
|
|
59
|
+
console.log(`\x1b[32m✅ 에이전트 접근 권한이 부여되었습니다\x1b[0m`);
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
}
|
package/dist/commands/list.js
CHANGED
|
@@ -2,25 +2,25 @@
|
|
|
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/orgs/${orgSlug}/
|
|
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(`Org
|
|
12
|
+
throw new Error(`Org 에이전트 목록 조회 실패 (${res.status}): ${body}`);
|
|
13
13
|
}
|
|
14
14
|
return (await res.json());
|
|
15
15
|
}
|
|
16
16
|
function registerList(program) {
|
|
17
17
|
program
|
|
18
18
|
.command('list')
|
|
19
|
-
.description('설치된 에이전트
|
|
20
|
-
.option('--org <slug>', 'Organization
|
|
19
|
+
.description('설치된 에이전트 목록')
|
|
20
|
+
.option('--org <slug>', 'Organization 에이전트 목록 조회')
|
|
21
21
|
.action(async (opts) => {
|
|
22
22
|
const json = program.opts().json ?? false;
|
|
23
|
-
// --org 옵션: Org
|
|
23
|
+
// --org 옵션: Org 에이전트 목록
|
|
24
24
|
if (opts.org) {
|
|
25
25
|
const orgSlug = opts.org;
|
|
26
26
|
const token = await (0, config_js_1.getValidToken)();
|
|
@@ -35,23 +35,23 @@ function registerList(program) {
|
|
|
35
35
|
process.exit(1);
|
|
36
36
|
}
|
|
37
37
|
try {
|
|
38
|
-
const
|
|
38
|
+
const agents = await fetchOrgAgentList(orgSlug, token);
|
|
39
39
|
if (json) {
|
|
40
|
-
console.log(JSON.stringify({ org: orgSlug,
|
|
40
|
+
console.log(JSON.stringify({ org: orgSlug, agents }));
|
|
41
41
|
return;
|
|
42
42
|
}
|
|
43
|
-
if (
|
|
44
|
-
console.log(`\n@${orgSlug} Organization에
|
|
43
|
+
if (agents.length === 0) {
|
|
44
|
+
console.log(`\n@${orgSlug} Organization에 에이전트가 없습니다.`);
|
|
45
45
|
return;
|
|
46
46
|
}
|
|
47
|
-
console.log(`\n\x1b[1m@${orgSlug}
|
|
48
|
-
for (const t of
|
|
47
|
+
console.log(`\n\x1b[1m@${orgSlug} 에이전트 목록\x1b[0m (${agents.length}개):\n`);
|
|
48
|
+
for (const t of agents) {
|
|
49
49
|
const desc = t.description
|
|
50
50
|
? ` \x1b[90m${t.description.length > 50 ? t.description.slice(0, 50) + '...' : t.description}\x1b[0m`
|
|
51
51
|
: '';
|
|
52
52
|
console.log(` \x1b[36m@${t.owner}/${t.slug}\x1b[0m \x1b[1m${t.name}\x1b[0m${desc}`);
|
|
53
53
|
}
|
|
54
|
-
console.log(`\n\x1b[33m 설치: relay install @${orgSlug}
|
|
54
|
+
console.log(`\n\x1b[33m 설치: relay install @${orgSlug}/<에이전트슬러그>\x1b[0m`);
|
|
55
55
|
}
|
|
56
56
|
catch (err) {
|
|
57
57
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -99,10 +99,10 @@ function registerList(program) {
|
|
|
99
99
|
}
|
|
100
100
|
else {
|
|
101
101
|
if (allEntries.length === 0) {
|
|
102
|
-
console.log('\n설치된
|
|
102
|
+
console.log('\n설치된 에이전트가 없습니다. `relay install <slug>`로 설치하세요.');
|
|
103
103
|
return;
|
|
104
104
|
}
|
|
105
|
-
console.log(`\n설치된
|
|
105
|
+
console.log(`\n설치된 에이전트 (${allEntries.length}개):\n`);
|
|
106
106
|
for (const item of allEntries) {
|
|
107
107
|
const date = new Date(item.installed_at).toLocaleDateString('ko-KR');
|
|
108
108
|
const scopeLabel = item.deploy_scope === 'global'
|
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
|
/**
|
package/dist/commands/orgs.js
CHANGED
|
@@ -110,7 +110,7 @@ function registerOrgs(program) {
|
|
|
110
110
|
}
|
|
111
111
|
else {
|
|
112
112
|
console.log(`\x1b[32m✅ Organization "${org.name}" (@${org.slug}) 생성 완료\x1b[0m`);
|
|
113
|
-
console.log(`\n\x1b[33m
|
|
113
|
+
console.log(`\n\x1b[33m 에이전트 배포: relay publish --org ${org.slug}\x1b[0m`);
|
|
114
114
|
console.log(`\x1b[33m 멤버 초대: www.relayax.com/orgs/${org.slug}/members\x1b[0m`);
|
|
115
115
|
}
|
|
116
116
|
}
|
|
@@ -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;
|