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.
Files changed (62) 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.d.ts +2 -0
  7. package/dist/commands/diff.js +72 -0
  8. package/dist/commands/grant.d.ts +33 -0
  9. package/dist/commands/grant.js +190 -0
  10. package/dist/commands/init.js +10 -10
  11. package/dist/commands/install.js +125 -256
  12. package/dist/commands/join.d.ts +3 -2
  13. package/dist/commands/join.js +18 -69
  14. package/dist/commands/list.js +23 -26
  15. package/dist/commands/login.js +10 -3
  16. package/dist/commands/orgs.d.ts +10 -0
  17. package/dist/commands/orgs.js +128 -0
  18. package/dist/commands/outdated.js +7 -7
  19. package/dist/commands/package.d.ts +18 -0
  20. package/dist/commands/package.js +355 -146
  21. package/dist/commands/ping.js +5 -5
  22. package/dist/commands/publish.d.ts +1 -1
  23. package/dist/commands/publish.js +105 -103
  24. package/dist/commands/search.js +2 -2
  25. package/dist/commands/status.js +11 -11
  26. package/dist/commands/uninstall.js +7 -7
  27. package/dist/commands/update.js +22 -22
  28. package/dist/commands/versions.d.ts +2 -0
  29. package/dist/commands/versions.js +44 -0
  30. package/dist/index.js +8 -2
  31. package/dist/lib/ai-tools.d.ts +15 -0
  32. package/dist/lib/ai-tools.js +48 -1
  33. package/dist/lib/api.d.ts +13 -12
  34. package/dist/lib/api.js +24 -39
  35. package/dist/lib/command-adapter.js +41 -693
  36. package/dist/lib/config.d.ts +10 -5
  37. package/dist/lib/config.js +106 -24
  38. package/dist/lib/guide.js +34 -79
  39. package/dist/lib/installer.d.ts +2 -2
  40. package/dist/lib/installer.js +4 -4
  41. package/dist/lib/preamble.d.ts +4 -4
  42. package/dist/lib/preamble.js +14 -15
  43. package/dist/lib/slug.d.ts +5 -1
  44. package/dist/lib/slug.js +52 -9
  45. package/dist/lib/update-cache.js +4 -4
  46. package/dist/lib/version-check.d.ts +3 -3
  47. package/dist/lib/version-check.js +13 -13
  48. package/dist/prompts/_business-card.md +41 -0
  49. package/dist/prompts/_error-handling.md +38 -0
  50. package/dist/prompts/_requirements-check.md +59 -0
  51. package/dist/prompts/_setup-cli.md +19 -0
  52. package/dist/prompts/_setup-login.md +7 -0
  53. package/dist/prompts/_setup-org.md +27 -0
  54. package/dist/prompts/business-card.md +41 -0
  55. package/dist/prompts/error-handling.md +38 -0
  56. package/dist/prompts/index.d.ts +7 -0
  57. package/dist/prompts/index.js +28 -0
  58. package/dist/prompts/install.md +187 -0
  59. package/dist/prompts/publish.md +444 -0
  60. package/dist/prompts/requirements-check.md +59 -0
  61. package/dist/types.d.ts +10 -10
  62. 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'));
@@ -101,16 +101,16 @@ function hasGlobalUserCommands() {
101
101
  return command_adapter_js_1.USER_COMMANDS.every((cmd) => fs_1.default.existsSync((0, command_adapter_js_1.getGlobalCommandPath)(cmd.id)));
102
102
  }
103
103
  /**
104
- * 프로젝트인지 감지한다 (.relay/ 디렉토리 내 relay.yaml 또는 디렉토리 구조).
104
+ * 에이전트 프로젝트인지 감지한다 (.relay/ 디렉토리 내 relay.yaml 또는 에이전트 디렉토리 구조).
105
105
  */
106
- function isTeamProject(projectPath) {
106
+ function isAgentProject(projectPath) {
107
107
  const relayDir = path_1.default.join(projectPath, '.relay');
108
108
  if (!fs_1.default.existsSync(relayDir))
109
109
  return false;
110
110
  if (fs_1.default.existsSync(path_1.default.join(relayDir, 'relay.yaml'))) {
111
111
  return true;
112
112
  }
113
- return VALID_TEAM_DIRS.some((d) => {
113
+ return VALID_AGENT_DIRS.some((d) => {
114
114
  const dirPath = path_1.default.join(relayDir, d);
115
115
  if (!fs_1.default.existsSync(dirPath))
116
116
  return false;
@@ -131,7 +131,7 @@ function registerInit(program) {
131
131
  const projectPath = process.cwd();
132
132
  const detected = (0, ai_tools_js_1.detectAgentCLIs)(projectPath);
133
133
  const detectedIds = new Set(detected.map((t) => t.value));
134
- const isBuilder = isTeamProject(projectPath);
134
+ const isBuilder = isAgentProject(projectPath);
135
135
  // ── 0. --json 모드에서 --tools/--all 없으면 MISSING_TOOLS 에러 ──
136
136
  if (json && !opts.tools && !opts.all && !opts.auto) {
137
137
  const detectedOptions = detected.map((t) => ({ value: t.value, label: t.name }));
@@ -163,7 +163,7 @@ function registerInit(program) {
163
163
  };
164
164
  (0, config_js_1.saveInstalled)(installed);
165
165
  }
166
- // ── 2. 로컬 Builder 커맨드 ( 프로젝트인 경우) ──
166
+ // ── 2. 로컬 Builder 커맨드 (에이전트 프로젝트인 경우) ──
167
167
  // relay-publish가 글로벌로 승격되어 BUILDER_COMMANDS가 비어있으면 스킵
168
168
  const localResults = [];
169
169
  if (isBuilder && command_adapter_js_1.BUILDER_COMMANDS.length > 0) {
@@ -267,7 +267,7 @@ function registerInit(program) {
267
267
  console.log();
268
268
  }
269
269
  if (!isBuilder) {
270
- console.log(' 팀을 만들려면 \x1b[33mrelay create <name>\x1b[0m을 사용하세요.');
270
+ console.log(' 에이전트를 만들려면 \x1b[33mrelay create <name>\x1b[0m을 사용하세요.');
271
271
  console.log();
272
272
  }
273
273
  console.log(' IDE를 재시작하면 슬래시 커맨드가 활성화됩니다.');
@@ -12,31 +12,13 @@ const config_js_1 = require("../lib/config.js");
12
12
  const slug_js_1 = require("../lib/slug.js");
13
13
  const contact_format_js_1 = require("../lib/contact-format.js");
14
14
  const preamble_js_1 = require("../lib/preamble.js");
15
- const join_js_1 = require("./join.js");
16
15
  const init_js_1 = require("./init.js");
17
- /**
18
- * slugInput이 "@spaces/{spaceSlug}/{teamSlug}" 또는 "@{spaceSlug}/{teamSlug}" 형식이면 파싱해 반환.
19
- * 아니면 null.
20
- */
21
- function parseSpaceTarget(slugInput) {
22
- // @spaces/{spaceSlug}/{teamSlug} 형식 (기존)
23
- const m1 = slugInput.match(/^@spaces\/([a-z0-9][a-z0-9-]*)\/([a-z0-9][a-z0-9-]*)$/);
24
- if (m1) {
25
- return { spaceSlug: m1[1], rawTeamSlug: m1[2], teamSlug: `@${m1[1]}/${m1[2]}` };
26
- }
27
- // @{spaceSlug}/{teamSlug} 형식 (신규)
28
- const m2 = slugInput.match(/^@([a-z0-9][a-z0-9-]*)\/([a-z0-9][a-z0-9-]*)$/);
29
- if (m2) {
30
- return { spaceSlug: m2[1], rawTeamSlug: m2[2], teamSlug: `@${m2[1]}/${m2[2]}` };
31
- }
32
- return null;
33
- }
34
16
  function registerInstall(program) {
35
17
  program
36
18
  .command('install <slug>')
37
- .description('에이전트 패키지를 .relay/teams/에 다운로드합니다')
38
- .option('--join-code <code>', 'Space 초대 코드 (Space 설치 시 자동 가입)')
39
- .action(async (slugInput, opts) => {
19
+ .description('에이전트 패키지를 .relay/agents/에 다운로드합니다')
20
+ .option('--join-code <code>', '초대 코드 (Organization 에이전트 설치 시 자동 가입)')
21
+ .action(async (slugInput, _opts) => {
40
22
  const json = program.opts().json ?? false;
41
23
  const projectPath = process.cwd();
42
24
  const tempDir = (0, storage_js_1.makeTempDir)();
@@ -48,214 +30,108 @@ function registerInstall(program) {
48
30
  (0, init_js_1.installGlobalUserCommands)();
49
31
  }
50
32
  try {
51
- // 0. @spaces/{spaceSlug}/{teamSlug} 형식 감지 파싱
52
- const spaceTarget = parseSpaceTarget(slugInput);
53
- // 0a. --join-code가 있으면 먼저 Space 가입 시도
54
- if (opts.joinCode && spaceTarget) {
55
- try {
56
- const { spaceName } = await (0, join_js_1.joinSpace)(spaceTarget.spaceSlug, opts.joinCode);
57
- if (!json) {
58
- console.log(`\x1b[32m✅ ${spaceName} Space에 가입했습니다\x1b[0m`);
59
- }
60
- }
61
- catch (joinErr) {
62
- const joinMsg = joinErr instanceof Error ? joinErr.message : String(joinErr);
63
- // 이미 멤버인 경우 설치 계속 진행
64
- if (joinMsg !== 'ALREADY_MEMBER') {
65
- if (!json) {
66
- console.error(`\x1b[33m경고: Space 가입 실패 — ${joinMsg}\x1b[0m`);
67
- }
68
- }
69
- }
70
- }
71
- // 0b. Resolve scoped slug and fetch team metadata
72
- let team;
33
+ // Resolve scoped slug and fetch agent metadata
34
+ let agent;
73
35
  let slug;
74
36
  let parsed;
75
- // Whether the spaceTarget was matched via the ambiguous @{slug}/{team} pattern
76
- // (i.e. NOT the explicit @spaces/... prefix). Used for 404 fallback below.
77
- const isAmbiguousSpaceTarget = spaceTarget !== null && !slugInput.startsWith('@spaces/');
78
- if (spaceTarget) {
79
- // Space 팀: POST /api/spaces/{spaceSlug}/teams/{teamSlug}/install
80
- // This verifies membership, increments install count, and returns metadata.
81
- let usedSpacePath = true;
82
- try {
83
- team = await (0, api_js_1.installSpaceTeam)(spaceTarget.spaceSlug, spaceTarget.rawTeamSlug);
84
- }
85
- catch (fetchErr) {
86
- const fetchMsg = fetchErr instanceof Error ? fetchErr.message : String(fetchErr);
87
- if (fetchMsg.includes('404') && isAmbiguousSpaceTarget) {
88
- // Space not found @{owner}/{team} is actually a normal registry slug, fall back
89
- usedSpacePath = false;
37
+ // Extract version from @owner/agent@version syntax (e.g. acme/writer@1.2.0)
38
+ // Version-specific install is not yet supported by the registry API;
39
+ // the match is kept for future use when per-version package URLs are available.
40
+ const versionMatch = slugInput.match(/^(.+)@(\d+\.\d+\.\d+.*)$/);
41
+ const actualSlugInput = versionMatch ? versionMatch[1] : slugInput;
42
+ parsed = await (0, slug_js_1.resolveSlug)(actualSlugInput);
43
+ slug = parsed.full;
44
+ try {
45
+ agent = await (0, api_js_1.fetchAgentInfo)(slug);
46
+ }
47
+ catch (fetchErr) {
48
+ const fetchMsg = fetchErr instanceof Error ? fetchErr.message : String(fetchErr);
49
+ if (fetchMsg.includes('403')) {
50
+ // Parse error body for membership_status, visibility, purchase_info
51
+ let membershipStatus;
52
+ let errorVisibility;
53
+ let purchaseInfo;
54
+ try {
55
+ const errBody = JSON.parse(fetchMsg.replace(/^.*?(\{)/, '{'));
56
+ membershipStatus = typeof errBody.membership_status === 'string' ? errBody.membership_status : undefined;
57
+ errorVisibility = typeof errBody.visibility === 'string' ? errBody.visibility : undefined;
58
+ if (errBody.purchase_info && typeof errBody.purchase_info === 'object') {
59
+ purchaseInfo = errBody.purchase_info;
60
+ }
90
61
  }
91
- else if (fetchMsg.includes('403')) {
62
+ catch { /* ignore parse errors */ }
63
+ // Private agent: show purchase info + relay access hint
64
+ if (errorVisibility === 'private' || purchaseInfo) {
92
65
  if (json) {
93
66
  console.error(JSON.stringify({
94
- error: 'SPACE_ONLY',
95
- message: '이 팀은 Space 멤버만 설치 가능합니다.',
96
- spaceSlug: spaceTarget.spaceSlug,
97
- fix: `relay join ${spaceTarget.spaceSlug} --code <초대코드>로 Space에 가입한 후 재시도하세요.`,
67
+ error: 'ACCESS_REQUIRED',
68
+ message: '이 에이전트는 접근 권한이 필요합니다.',
69
+ slug,
70
+ purchase_info: purchaseInfo ?? null,
71
+ fix: '접근 링크 코드가 있으면: relay access <slug> --code <코드>',
98
72
  }));
99
73
  }
100
74
  else {
101
- console.error('\x1b[31m이 팀은 Space 멤버만 설치 가능합니다.\x1b[0m');
102
- console.error(`\x1b[33mSpace 관리자에게 초대 코드를 요청하세요: relay join ${spaceTarget.spaceSlug} --code <코드>\x1b[0m`);
103
- }
104
- process.exit(1);
105
- }
106
- else {
107
- throw fetchErr;
108
- }
109
- }
110
- if (!usedSpacePath) {
111
- // Fallback: treat as normal registry install
112
- parsed = await (0, slug_js_1.resolveSlug)(slugInput);
113
- slug = parsed.full;
114
- team = await (0, api_js_1.fetchTeamInfo)(slug);
115
- }
116
- else {
117
- // slug from server is "@spaces/{spaceSlug}/{teamSlug}" — derive local path parts
118
- // team is guaranteed assigned by installSpaceTeam above (usedSpacePath === true)
119
- parsed = { owner: spaceTarget.spaceSlug, name: spaceTarget.rawTeamSlug, full: team.slug };
120
- slug = team.slug;
121
- }
122
- }
123
- else {
124
- // Normal registry install
125
- parsed = await (0, slug_js_1.resolveSlug)(slugInput);
126
- slug = parsed.full;
127
- try {
128
- team = await (0, api_js_1.fetchTeamInfo)(slug);
129
- }
130
- catch (fetchErr) {
131
- const fetchMsg = fetchErr instanceof Error ? fetchErr.message : String(fetchErr);
132
- if (fetchMsg.includes('403')) {
133
- // Parse error body for join_policy, membership_status, visibility, purchase_info
134
- let joinPolicy;
135
- let membershipStatus;
136
- let errorVisibility;
137
- let purchaseInfo;
138
- try {
139
- const errBody = JSON.parse(fetchMsg.replace(/^.*?(\{)/, '{'));
140
- joinPolicy = typeof errBody.join_policy === 'string' ? errBody.join_policy : undefined;
141
- membershipStatus = typeof errBody.membership_status === 'string' ? errBody.membership_status : undefined;
142
- errorVisibility = typeof errBody.visibility === 'string' ? errBody.visibility : undefined;
143
- if (errBody.purchase_info && typeof errBody.purchase_info === 'object') {
144
- purchaseInfo = errBody.purchase_info;
145
- }
146
- }
147
- catch { /* ignore parse errors */ }
148
- // Gated team: show purchase info + relay access hint
149
- if (errorVisibility === 'gated' || purchaseInfo) {
150
- if (json) {
151
- console.error(JSON.stringify({
152
- error: 'GATED_ACCESS_REQUIRED',
153
- message: '이 팀은 접근 권한이 필요합니다.',
154
- slug,
155
- purchase_info: purchaseInfo ?? null,
156
- fix: '접근 링크 코드가 있으면: relay access <slug> --code <코드>',
157
- }));
158
- }
159
- else {
160
- console.error('\x1b[31m🔒 이 팀은 접근 권한이 필요합니다.\x1b[0m');
161
- if (purchaseInfo?.message) {
162
- console.error(`\n \x1b[36m${purchaseInfo.message}\x1b[0m`);
163
- }
164
- if (purchaseInfo?.url) {
165
- console.error(` \x1b[36m${purchaseInfo.url}\x1b[0m`);
166
- }
167
- console.error(`\n\x1b[33m접근 링크 코드가 있으면: relay access ${slugInput} --code <코드>\x1b[0m`);
168
- }
169
- process.exit(1);
170
- }
171
- if (joinPolicy === 'auto') {
172
- // Auto-join the Space then retry install
173
- if (!json) {
174
- console.error(`\x1b[33m⚙ Space에 자동 가입합니다...\x1b[0m`);
175
- }
176
- try {
177
- const spaceSlug = parsed.owner;
178
- await (0, join_js_1.joinSpace)(spaceSlug, '');
179
- if (!json) {
180
- console.error(`\x1b[32m✓ Space에 가입했습니다\x1b[0m`);
181
- }
182
- team = await (0, api_js_1.fetchTeamInfo)(slug);
183
- }
184
- catch (joinErr) {
185
- const joinMsg = joinErr instanceof Error ? joinErr.message : String(joinErr);
186
- if (json) {
187
- console.error(JSON.stringify({ error: 'JOIN_FAILED', message: joinMsg, slug, fix: 'Space slug와 초대 코드를 확인 후 재시도하세요.' }));
188
- }
189
- else {
190
- console.error(`\x1b[31mSpace 가입 실패: ${joinMsg}\x1b[0m`);
191
- }
192
- process.exit(1);
193
- }
194
- }
195
- else if (joinPolicy === 'approval') {
196
- const spaceSlug = parsed.owner;
197
- if (json) {
198
- console.error(JSON.stringify({
199
- error: 'APPROVAL_REQUIRED',
200
- message: `가입 신청이 필요합니다.`,
201
- slug,
202
- spaceSlug,
203
- fix: `relay join @${spaceSlug} --code <초대코드>로 Space에 가입한 후 재시도하세요. 초대 코드는 Space 관리자에게 요청하세요.`,
204
- }));
75
+ console.error('\x1b[31m이 에이전트는 접근 권한이 필요합니다.\x1b[0m');
76
+ if (purchaseInfo?.message) {
77
+ console.error(`\n \x1b[36m${purchaseInfo.message}\x1b[0m`);
205
78
  }
206
- else {
207
- console.error(`\x1b[33m가입 신청이 필요합니다. \`relay join @${spaceSlug}\`로 가입하세요.\x1b[0m`);
79
+ if (purchaseInfo?.url) {
80
+ console.error(` \x1b[36m${purchaseInfo.url}\x1b[0m`);
208
81
  }
209
- process.exit(1);
82
+ console.error(`\n\x1b[33m접근 링크 코드가 있으면: relay access ${slugInput} --code <코드>\x1b[0m`);
210
83
  }
211
- else if (membershipStatus === 'member') {
212
- // Member but no access to this specific team
213
- if (json) {
214
- console.error(JSON.stringify({
215
- error: 'NO_ACCESS',
216
- message: '이 팀에 대한 접근 권한이 없습니다.',
217
- slug,
218
- fix: '이 팀의 접근 링크 코드가 있으면 `relay access ' + slugInput + ' --code <코드>`로 접근 권한을 얻으세요. 없으면 팀 제작자에게 문의하세요.',
219
- }));
220
- }
221
- else {
222
- console.error('\x1b[31m이 팀에 대한 접근 권한이 없습니다.\x1b[0m');
223
- }
224
- process.exit(1);
84
+ process.exit(1);
85
+ }
86
+ if (membershipStatus === 'member') {
87
+ // Member but no access to this specific agent
88
+ if (json) {
89
+ console.error(JSON.stringify({
90
+ error: 'NO_ACCESS',
91
+ message: '이 에이전트에 대한 접근 권한이 없습니다.',
92
+ slug,
93
+ fix: '이 에이전트의 접근 링크 코드가 있으면 `relay access ' + slugInput + ' --code <코드>`로 접근 권한을 얻으세요. 없으면 에이전트 제작자에게 문의하세요.',
94
+ }));
225
95
  }
226
96
  else {
227
- if (json) {
228
- console.error(JSON.stringify({
229
- error: 'SPACE_ONLY',
230
- message: '이 팀은 Space 멤버만 설치 가능합니다.',
231
- slug,
232
- fix: 'Space 관리자에게 초대 코드를 요청한 후 `relay join <space-slug> --code <코드>`로 가입하세요.',
233
- }));
234
- }
235
- else {
236
- console.error('\x1b[31m이 팀은 Space 멤버만 설치 가능합니다.\x1b[0m');
237
- console.error('\x1b[33mSpace 관리자에게 초대 코드를 요청하세요.\x1b[0m');
238
- }
239
- process.exit(1);
97
+ console.error('\x1b[31m이 에이전트에 대한 접근 권한이 없습니다.\x1b[0m');
240
98
  }
99
+ process.exit(1);
241
100
  }
242
101
  else {
243
- throw fetchErr;
102
+ if (json) {
103
+ console.error(JSON.stringify({
104
+ error: 'ACCESS_REQUIRED',
105
+ message: '이 에이전트는 접근 권한이 필요합니다.',
106
+ slug,
107
+ fix: '초대 코드가 있으면 `relay join <org-slug> --code <코드>`로 가입하세요.',
108
+ }));
109
+ }
110
+ else {
111
+ console.error('\x1b[31m이 에이전트는 접근 권한이 필요합니다.\x1b[0m');
112
+ console.error('\x1b[33m초대 코드가 있으면 `relay join <org-slug> --code <코드>`로 가입하세요.\x1b[0m');
113
+ }
114
+ process.exit(1);
244
115
  }
245
116
  }
117
+ else {
118
+ throw fetchErr;
119
+ }
246
120
  }
247
- if (!team)
248
- throw new Error(' 정보를 가져오지 못했습니다.');
249
- 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);
250
126
  // 2. Visibility check + auto-login
251
- const visibility = team.visibility ?? 'public';
252
- if (visibility === 'private') {
127
+ const visibility = resolvedAgent.visibility ?? 'public';
128
+ if (visibility === 'internal') {
253
129
  let token = await (0, config_js_1.getValidToken)();
254
130
  if (!token) {
255
131
  const isTTY = Boolean(process.stdin.isTTY);
256
132
  if (isTTY && !json) {
257
133
  // Auto-login: TTY 환경에서 자동으로 login 플로우 트리거
258
- console.error('\x1b[33m⚙ 이 팀은 로그인이 필요합니다. 로그인을 시작합니다...\x1b[0m');
134
+ console.error('\x1b[33m⚙ 이 에이전트는 로그인이 필요합니다. 로그인을 시작합니다...\x1b[0m');
259
135
  const { runLogin } = await import('./login.js');
260
136
  await runLogin();
261
137
  token = await (0, config_js_1.getValidToken)();
@@ -266,12 +142,12 @@ function registerInstall(program) {
266
142
  error: 'LOGIN_REQUIRED',
267
143
  visibility,
268
144
  slug,
269
- message: '이 팀은 로그인이 필요합니다. relay login을 먼저 실행하세요.',
145
+ message: '이 에이전트는 로그인이 필요합니다. relay login을 먼저 실행하세요.',
270
146
  fix: 'relay login 실행 후 재시도하세요.',
271
147
  }));
272
148
  }
273
149
  else {
274
- console.error('\x1b[31m이 팀은 로그인이 필요합니다. relay login 을 먼저 실행하세요.\x1b[0m');
150
+ console.error('\x1b[31m이 에이전트는 로그인이 필요합니다. relay login 을 먼저 실행하세요.\x1b[0m');
275
151
  }
276
152
  process.exit(1);
277
153
  }
@@ -280,35 +156,30 @@ function registerInstall(program) {
280
156
  // 3. Download package (retry once if signed URL expired)
281
157
  let tarPath;
282
158
  try {
283
- tarPath = await (0, storage_js_1.downloadPackage)(team.package_url, tempDir);
159
+ tarPath = await (0, storage_js_1.downloadPackage)(resolvedAgent.package_url, tempDir);
284
160
  }
285
161
  catch (dlErr) {
286
162
  const dlMsg = dlErr instanceof Error ? dlErr.message : String(dlErr);
287
163
  if (dlMsg.includes('403') || dlMsg.includes('expired')) {
288
- // 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
289
165
  if (!json) {
290
166
  console.error('\x1b[33m⚙ 다운로드 URL 만료, 재시도 중...\x1b[0m');
291
167
  }
292
- if (spaceTarget) {
293
- team = await (0, api_js_1.installSpaceTeam)(spaceTarget.spaceSlug, spaceTarget.rawTeamSlug);
294
- }
295
- else {
296
- team = await (0, api_js_1.fetchTeamInfo)(slug);
297
- }
298
- 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);
299
170
  }
300
171
  else {
301
172
  throw dlErr;
302
173
  }
303
174
  }
304
- // 4. Extract to .relay/teams/<slug>/
305
- if (fs_1.default.existsSync(teamDir)) {
306
- 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 });
307
178
  }
308
- fs_1.default.mkdirSync(teamDir, { recursive: true });
309
- 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);
310
181
  // 4.5. Inject preamble (update check) into SKILL.md and commands
311
- (0, preamble_js_1.injectPreambleToTeam)(teamDir, slug);
182
+ (0, preamble_js_1.injectPreambleToAgent)(agentDir, slug);
312
183
  // 5. Count extracted files
313
184
  function countFiles(dir) {
314
185
  let count = 0;
@@ -324,59 +195,57 @@ function registerInstall(program) {
324
195
  }
325
196
  return count;
326
197
  }
327
- const fileCount = countFiles(teamDir);
328
- // 6. Record in installed.json (team_id, space_slug 포함)
198
+ const fileCount = countFiles(agentDir);
199
+ // 6. Record in installed.json
329
200
  const installed = (0, config_js_1.loadInstalled)();
330
201
  installed[slug] = {
331
- team_id: team.id,
332
- version: team.version,
202
+ agent_id: resolvedAgent.id,
203
+ version: resolvedAgent.version,
333
204
  installed_at: new Date().toISOString(),
334
- files: [teamDir],
335
- ...(spaceTarget ? { space_slug: spaceTarget.spaceSlug } : {}),
205
+ files: [agentDir],
336
206
  };
337
207
  (0, config_js_1.saveInstalled)(installed);
338
- // 7. Report install + usage ping (non-blocking, team_id 기반)
339
- await (0, api_js_1.reportInstall)(team.id, slug, team.version);
340
- (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);
341
211
  const result = {
342
212
  status: 'ok',
343
- team: team.name,
213
+ agent: resolvedAgent.name,
344
214
  slug,
345
- version: team.version,
346
- commands: team.commands,
215
+ version: resolvedAgent.version,
216
+ commands: resolvedAgent.commands,
347
217
  files: fileCount,
348
- install_path: teamDir,
349
- ...(spaceTarget ? { space_slug: spaceTarget.spaceSlug } : {}),
350
- author: team.author ? {
351
- username: team.author.username,
352
- display_name: team.author.display_name ?? null,
353
- 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 ?? [],
354
223
  } : null,
355
- welcome: team.welcome ?? null,
224
+ welcome: resolvedAgent.welcome ?? null,
356
225
  };
357
226
  if (json) {
358
227
  console.log(JSON.stringify(result));
359
228
  }
360
229
  else {
361
- const authorUsername = team.author?.username;
362
- const authorDisplayName = team.author?.display_name ?? authorUsername ?? '';
230
+ const authorUsername = resolvedAgent.author?.username;
231
+ const authorDisplayName = resolvedAgent.author?.display_name ?? authorUsername ?? '';
363
232
  const authorSuffix = authorUsername ? ` \x1b[90mby @${authorUsername}\x1b[0m` : '';
364
- console.log(`\n\x1b[32m✓ ${team.name} 다운로드 완료\x1b[0m v${team.version}${authorSuffix}`);
365
- 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`);
366
235
  console.log(` 파일: ${fileCount}개`);
367
- if (team.commands.length > 0) {
236
+ if (resolvedAgent.commands.length > 0) {
368
237
  console.log('\n 포함된 커맨드:');
369
- for (const cmd of team.commands) {
238
+ for (const cmd of resolvedAgent.commands) {
370
239
  console.log(` \x1b[33m/${cmd.name}\x1b[0m - ${cmd.description}`);
371
240
  }
372
241
  }
373
242
  // Builder business card
374
- const contactParts = (0, contact_format_js_1.formatContactParts)(team.author?.contact_links);
375
- 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;
376
245
  if (hasCard) {
377
246
  console.log(`\n \x1b[90m┌─ ${authorDisplayName || authorUsername || '빌더'}의 명함 ${'─'.repeat(Math.max(0, 34 - (authorDisplayName || authorUsername || '빌더').length))}┐\x1b[0m`);
378
- if (team.welcome) {
379
- 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;
380
249
  console.log(` \x1b[90m│\x1b[0m 💬 "${truncated}"`);
381
250
  }
382
251
  if (contactParts.length > 0) {
@@ -388,15 +257,15 @@ function registerInstall(program) {
388
257
  console.log(` \x1b[90m└${'─'.repeat(44)}┘\x1b[0m`);
389
258
  }
390
259
  // Usage hint (type-aware)
391
- const teamType = team.type;
392
- if (teamType === 'passive') {
260
+ const agentType = resolvedAgent.type;
261
+ if (agentType === 'passive') {
393
262
  console.log(`\n\x1b[33m💡 자동 적용됩니다. 별도 실행 없이 동작합니다.\x1b[0m`);
394
263
  }
395
- else if (teamType === 'hybrid' && team.commands && team.commands.length > 0) {
396
- 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`);
397
266
  }
398
- else if (team.commands && team.commands.length > 0) {
399
- 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`);
400
269
  }
401
270
  else {
402
271
  console.log(`\n\x1b[33m💡 설치 완료! AI 에이전트에서 사용할 수 있습니다.\x1b[0m`);
@@ -1,5 +1,6 @@
1
1
  import { Command } from 'commander';
2
- export declare function joinSpace(spaceSlug: string, code: string): Promise<{
3
- spaceName: string;
2
+ export declare function joinOrg(orgSlug: string, code: string): Promise<{
3
+ type: string;
4
+ role?: string;
4
5
  }>;
5
6
  export declare function registerJoin(program: Command): void;