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.
Files changed (59) hide show
  1. package/dist/commands/access.js +12 -12
  2. package/dist/commands/changelog.js +2 -2
  3. package/dist/commands/check-update.js +12 -12
  4. package/dist/commands/create.js +46 -19
  5. package/dist/commands/deploy-record.js +2 -2
  6. package/dist/commands/diff.js +2 -2
  7. package/dist/commands/grant.d.ts +33 -0
  8. package/dist/commands/grant.js +190 -0
  9. package/dist/commands/init.js +13 -15
  10. package/dist/commands/install.js +69 -68
  11. package/dist/commands/join.js +3 -3
  12. package/dist/commands/list.js +15 -15
  13. package/dist/commands/login.js +10 -3
  14. package/dist/commands/orgs.js +1 -1
  15. package/dist/commands/outdated.js +7 -7
  16. package/dist/commands/package.d.ts +18 -0
  17. package/dist/commands/package.js +355 -146
  18. package/dist/commands/ping.js +5 -5
  19. package/dist/commands/publish.d.ts +1 -1
  20. package/dist/commands/publish.js +56 -48
  21. package/dist/commands/search.js +2 -2
  22. package/dist/commands/status.js +11 -11
  23. package/dist/commands/uninstall.js +27 -9
  24. package/dist/commands/update.js +22 -22
  25. package/dist/commands/versions.js +2 -2
  26. package/dist/index.js +2 -0
  27. package/dist/lib/ai-tools.d.ts +15 -0
  28. package/dist/lib/ai-tools.js +48 -1
  29. package/dist/lib/api.d.ts +7 -7
  30. package/dist/lib/api.js +11 -11
  31. package/dist/lib/command-adapter.d.ts +4 -3
  32. package/dist/lib/command-adapter.js +37 -688
  33. package/dist/lib/config.d.ts +1 -1
  34. package/dist/lib/config.js +2 -2
  35. package/dist/lib/guide.js +34 -79
  36. package/dist/lib/installer.d.ts +2 -2
  37. package/dist/lib/installer.js +4 -4
  38. package/dist/lib/preamble.d.ts +4 -4
  39. package/dist/lib/preamble.js +14 -14
  40. package/dist/lib/slug.d.ts +5 -0
  41. package/dist/lib/slug.js +49 -2
  42. package/dist/lib/update-cache.js +4 -4
  43. package/dist/lib/version-check.d.ts +3 -3
  44. package/dist/lib/version-check.js +13 -13
  45. package/dist/prompts/_business-card.md +41 -0
  46. package/dist/prompts/_error-handling.md +38 -0
  47. package/dist/prompts/_requirements-check.md +59 -0
  48. package/dist/prompts/_setup-cli.md +19 -0
  49. package/dist/prompts/_setup-login.md +7 -0
  50. package/dist/prompts/_setup-org.md +27 -0
  51. package/dist/prompts/business-card.md +41 -0
  52. package/dist/prompts/error-handling.md +38 -0
  53. package/dist/prompts/index.d.ts +7 -0
  54. package/dist/prompts/index.js +28 -0
  55. package/dist/prompts/install.md +191 -0
  56. package/dist/prompts/publish.md +448 -0
  57. package/dist/prompts/requirements-check.md +59 -0
  58. package/dist/types.d.ts +9 -9
  59. package/package.json +3 -3
@@ -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 VALID_TEAM_DIRS = ['skills', 'agents', 'rules', 'commands'];
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 Team Marketplace',
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가 없으면 Claude Code에만 설치 (기본)
73
- const targetDirs = globalCLIs.length > 0
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
- * 프로젝트인지 감지한다 (.relay/ 디렉토리 내 relay.yaml 또는 디렉토리 구조).
102
+ * 에이전트 프로젝트인지 감지한다 (.relay/ 디렉토리 내 relay.yaml 또는 에이전트 디렉토리 구조).
105
103
  */
106
- function isTeamProject(projectPath) {
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 VALID_TEAM_DIRS.some((d) => {
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 = isTeamProject(projectPath);
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(', ') : 'Claude Code';
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(' 팀을 만들려면 \x1b[33mrelay create <name>\x1b[0m을 사용하세요.');
268
+ console.log(' 에이전트를 만들려면 \x1b[33mrelay create <name>\x1b[0m을 사용하세요.');
271
269
  console.log();
272
270
  }
273
271
  console.log(' IDE를 재시작하면 슬래시 커맨드가 활성화됩니다.');
@@ -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('에이전트 패키지를 .relay/teams/에 다운로드합니다')
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 team metadata
34
- let team;
33
+ // Resolve scoped slug and fetch agent metadata
34
+ let agent;
35
35
  let slug;
36
36
  let parsed;
37
- // Extract version from @owner/team@version syntax
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
- team = await (0, api_js_1.fetchTeamInfo)(slug);
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
- // Gated team: show purchase info + relay access hint
65
- if (errorVisibility === 'gated' || purchaseInfo) {
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: 'GATED_ACCESS_REQUIRED',
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이 팀은 접근 권한이 필요합니다.\x1b[0m');
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 team
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: '이 팀의 접근 링크 코드가 있으면 `relay access ' + slugInput + ' --code <코드>`로 접근 권한을 얻으세요. 없으면 제작자에게 문의하세요.',
93
+ fix: '이 에이전트의 접근 링크 코드가 있으면 `relay access ' + slugInput + ' --code <코드>`로 접근 권한을 얻으세요. 없으면 에이전트 제작자에게 문의하세요.',
95
94
  }));
96
95
  }
97
96
  else {
98
- console.error('\x1b[31m이 팀에 대한 접근 권한이 없습니다.\x1b[0m');
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이 팀은 접근 권한이 필요합니다.\x1b[0m');
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 (!team)
123
- throw new Error(' 정보를 가져오지 못했습니다.');
124
- const teamDir = path_1.default.join(projectPath, '.relay', 'teams', parsed.owner, parsed.name);
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 = team.visibility ?? 'public';
127
- if (visibility === 'private') {
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⚙ 이 팀은 로그인이 필요합니다. 로그인을 시작합니다...\x1b[0m');
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: '이 팀은 로그인이 필요합니다. relay login을 먼저 실행하세요.',
145
+ message: '이 에이전트는 로그인이 필요합니다. relay login을 먼저 실행하세요.',
145
146
  fix: 'relay login 실행 후 재시도하세요.',
146
147
  }));
147
148
  }
148
149
  else {
149
- console.error('\x1b[31m이 팀은 로그인이 필요합니다. relay login 을 먼저 실행하세요.\x1b[0m');
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)(team.package_url, tempDir);
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 team info for new URL and retry
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
- team = await (0, api_js_1.fetchTeamInfo)(slug);
168
- tarPath = await (0, storage_js_1.downloadPackage)(team.package_url, tempDir);
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/teams/<slug>/
175
- if (fs_1.default.existsSync(teamDir)) {
176
- fs_1.default.rmSync(teamDir, { recursive: true, force: true });
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(teamDir, { recursive: true });
179
- await (0, storage_js_1.extractPackage)(tarPath, teamDir);
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.injectPreambleToTeam)(teamDir, slug);
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(teamDir);
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
- team_id: team.id,
202
- version: team.version,
202
+ agent_id: resolvedAgent.id,
203
+ version: resolvedAgent.version,
203
204
  installed_at: new Date().toISOString(),
204
- files: [teamDir],
205
+ files: [agentDir],
205
206
  };
206
207
  (0, config_js_1.saveInstalled)(installed);
207
- // 7. Report install + usage ping (non-blocking, team_id 기반)
208
- await (0, api_js_1.reportInstall)(team.id, slug, team.version);
209
- (0, api_js_1.sendUsagePing)(team.id, slug, team.version);
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
- team: team.name,
213
+ agent: resolvedAgent.name,
213
214
  slug,
214
- version: team.version,
215
- commands: team.commands,
215
+ version: resolvedAgent.version,
216
+ commands: resolvedAgent.commands,
216
217
  files: fileCount,
217
- install_path: teamDir,
218
- author: team.author ? {
219
- username: team.author.username,
220
- display_name: team.author.display_name ?? null,
221
- contact_links: team.author.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: team.welcome ?? null,
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 = team.author?.username;
230
- const authorDisplayName = team.author?.display_name ?? authorUsername ?? '';
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✓ ${team.name} 다운로드 완료\x1b[0m v${team.version}${authorSuffix}`);
233
- console.log(` 위치: \x1b[36m${teamDir}\x1b[0m`);
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 (team.commands.length > 0) {
236
+ if (resolvedAgent.commands.length > 0) {
236
237
  console.log('\n 포함된 커맨드:');
237
- for (const cmd of team.commands) {
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)(team.author?.contact_links);
243
- const hasCard = team.welcome || contactParts.length > 0 || authorUsername;
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 (team.welcome) {
247
- const truncated = team.welcome.length > 45 ? team.welcome.slice(0, 45) + '...' : team.welcome;
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 teamType = team.type;
260
- if (teamType === 'passive') {
260
+ const agentType = resolvedAgent.type;
261
+ if (agentType === 'passive') {
261
262
  console.log(`\n\x1b[33m💡 자동 적용됩니다. 별도 실행 없이 동작합니다.\x1b[0m`);
262
263
  }
263
- else if (teamType === 'hybrid' && team.commands && team.commands.length > 0) {
264
- console.log(`\n\x1b[33m💡 자동 적용 + \x1b[1m/${team.commands[0].name}\x1b[0m\x1b[33m 으로 추가 기능을 사용할 수 있습니다.\x1b[0m`);
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 (team.commands && team.commands.length > 0) {
267
- console.log(`\n\x1b[33m💡 사용법: \x1b[1m/${team.commands[0].name}\x1b[0m`);
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`);
@@ -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 invite link via API
13
- const res = await fetch(`${config_js_1.API_URL}/api/invite-links/${code}/use`, {
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✅ 접근 권한이 부여되었습니다\x1b[0m`);
59
+ console.log(`\x1b[32m✅ 에이전트 접근 권한이 부여되었습니다\x1b[0m`);
60
60
  }
61
61
  }
62
62
  }
@@ -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 fetchOrgTeamList(orgSlug, token) {
6
- const res = await fetch(`${config_js_1.API_URL}/api/orgs/${orgSlug}/teams`, {
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 목록 조회 실패 (${res.status}): ${body}`);
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 teams = await fetchOrgTeamList(orgSlug, token);
38
+ const agents = await fetchOrgAgentList(orgSlug, token);
39
39
  if (json) {
40
- console.log(JSON.stringify({ org: orgSlug, teams }));
40
+ console.log(JSON.stringify({ org: orgSlug, agents }));
41
41
  return;
42
42
  }
43
- if (teams.length === 0) {
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} 목록\x1b[0m (${teams.length}개):\n`);
48
- for (const t of teams) {
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}/<팀슬러그>\x1b[0m`);
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설치된 팀이 없습니다. `relay install <slug>`로 설치하세요.');
102
+ console.log('\n설치된 에이전트가 없습니다. `relay install <slug>`로 설치하세요.');
103
103
  return;
104
104
  }
105
- console.log(`\n설치된 (${allEntries.length}개):\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'
@@ -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
- // ignore browser open errors
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
- console.error(`브라우저에서 로그인 페이지를 엽니다...`);
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
  /**
@@ -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 배포: relay publish --org ${org.slug}\x1b[0m`);
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 team = await (0, api_js_1.fetchTeamInfo)(slug);
28
- const latest = team.version;
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(4, ...results.map((r) => r.slug.length));
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('', COL_TEAM)} ${pad('현재', COL_CURRENT)} ${pad('최신', COL_LATEST)} 상태`;
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;