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
@@ -23,8 +23,8 @@ function parseRelayYaml(content) {
23
23
  : [];
24
24
  const requires = raw.requires;
25
25
  const rawVisibility = String(raw.visibility ?? '');
26
- const visibility = rawVisibility === 'private' ? 'private'
27
- : rawVisibility === 'gated' ? 'gated'
26
+ const visibility = rawVisibility === 'internal' ? 'internal'
27
+ : rawVisibility === 'private' ? 'private'
28
28
  : rawVisibility === 'public' ? 'public'
29
29
  : undefined;
30
30
  const rawType = String(raw.type ?? '');
@@ -46,8 +46,8 @@ function parseRelayYaml(content) {
46
46
  source: raw.source ? String(raw.source) : undefined,
47
47
  };
48
48
  }
49
- function detectCommands(teamDir) {
50
- const cmdDir = path_1.default.join(teamDir, 'commands');
49
+ function detectCommands(agentDir) {
50
+ const cmdDir = path_1.default.join(agentDir, 'commands');
51
51
  if (!fs_1.default.existsSync(cmdDir))
52
52
  return [];
53
53
  const entries = [];
@@ -78,8 +78,8 @@ function detectCommands(teamDir) {
78
78
  }
79
79
  return entries;
80
80
  }
81
- function detectSkills(teamDir) {
82
- const skillsDir = path_1.default.join(teamDir, 'skills');
81
+ function detectSkills(agentDir) {
82
+ const skillsDir = path_1.default.join(agentDir, 'skills');
83
83
  if (!fs_1.default.existsSync(skillsDir))
84
84
  return [];
85
85
  const entries = [];
@@ -118,8 +118,8 @@ function detectSkills(teamDir) {
118
118
  return entries;
119
119
  }
120
120
  const MCP_KEYWORDS = ['mcp', 'supabase', 'github', 'slack', 'notion', 'linear', 'jira', 'figma', 'stripe', 'openai', 'anthropic', 'postgres', 'mysql', 'redis', 'mongodb', 'firebase', 'aws', 'gcp', 'azure', 'vercel', 'netlify', 'docker', 'kubernetes'];
121
- function detectAgentDetails(teamDir, requires) {
122
- const agentsDir = path_1.default.join(teamDir, 'agents');
121
+ function detectAgentDetails(agentDir, requires) {
122
+ const agentsDir = path_1.default.join(agentDir, 'agents');
123
123
  if (!fs_1.default.existsSync(agentsDir))
124
124
  return [];
125
125
  const mcpNames = new Set((requires?.mcp ?? []).map((m) => m.name.toLowerCase()));
@@ -168,8 +168,8 @@ function detectAgentDetails(teamDir, requires) {
168
168
  return entries;
169
169
  }
170
170
  /**
171
- * 진입점 커맨드(commands/{author}-{name}.md)를 생성한다.
172
- * root SKILL.md를 대체하여 팀의 얼굴 역할을 한다.
171
+ * 에이전트 진입점 커맨드(commands/{author}-{name}.md)를 생성한다.
172
+ * root SKILL.md를 대체하여 에이전트의 얼굴 역할을 한다.
173
173
  */
174
174
  function generateEntryCommand(config, commands, skills, scopedSlug) {
175
175
  const lines = [];
@@ -181,7 +181,7 @@ function generateEntryCommand(config, commands, skills, scopedSlug) {
181
181
  // Preamble
182
182
  lines.push((0, preamble_js_1.generatePreamble)(scopedSlug));
183
183
  lines.push('');
184
- // Team header
184
+ // Agent header
185
185
  lines.push(`## ${config.name}`);
186
186
  lines.push('');
187
187
  lines.push(`v${config.version} — ${scopedSlug}`);
@@ -210,14 +210,14 @@ function generateEntryCommand(config, commands, skills, scopedSlug) {
210
210
  lines.push('');
211
211
  return lines.join('\n');
212
212
  }
213
- function countDir(teamDir, dirName) {
214
- const dirPath = path_1.default.join(teamDir, dirName);
213
+ function countDir(agentDir, dirName) {
214
+ const dirPath = path_1.default.join(agentDir, dirName);
215
215
  if (!fs_1.default.existsSync(dirPath))
216
216
  return 0;
217
217
  return fs_1.default.readdirSync(dirPath).filter((f) => !f.startsWith('.')).length;
218
218
  }
219
- function listDir(teamDir, dirName) {
220
- const dirPath = path_1.default.join(teamDir, dirName);
219
+ function listDir(agentDir, dirName) {
220
+ const dirPath = path_1.default.join(agentDir, dirName);
221
221
  if (!fs_1.default.existsSync(dirPath))
222
222
  return [];
223
223
  return fs_1.default.readdirSync(dirPath).filter((f) => !f.startsWith('.'));
@@ -227,10 +227,10 @@ function listDir(teamDir, dirName) {
227
227
  * 1. relay.yaml에 있으면 사용
228
228
  * 2. README.md가 있으면 fallback
229
229
  */
230
- function resolveLongDescription(teamDir, yamlValue) {
230
+ function resolveLongDescription(agentDir, yamlValue) {
231
231
  if (yamlValue)
232
232
  return yamlValue;
233
- const readmePath = path_1.default.join(teamDir, 'README.md');
233
+ const readmePath = path_1.default.join(agentDir, 'README.md');
234
234
  if (fs_1.default.existsSync(readmePath)) {
235
235
  try {
236
236
  return fs_1.default.readFileSync(readmePath, 'utf-8').trim() || undefined;
@@ -241,21 +241,21 @@ function resolveLongDescription(teamDir, yamlValue) {
241
241
  }
242
242
  return undefined;
243
243
  }
244
- async function createTarball(teamDir) {
244
+ async function createTarball(agentDir) {
245
245
  const tmpFile = path_1.default.join(os_1.default.tmpdir(), `relay-publish-${Date.now()}.tar.gz`);
246
- const dirsToInclude = VALID_DIRS.filter((d) => fs_1.default.existsSync(path_1.default.join(teamDir, d)));
246
+ const dirsToInclude = VALID_DIRS.filter((d) => fs_1.default.existsSync(path_1.default.join(agentDir, d)));
247
247
  // Include root-level files if they exist
248
248
  const entries = [...dirsToInclude];
249
249
  const rootFiles = ['relay.yaml', 'SKILL.md', 'guide.md'];
250
250
  for (const file of rootFiles) {
251
- if (fs_1.default.existsSync(path_1.default.join(teamDir, file))) {
251
+ if (fs_1.default.existsSync(path_1.default.join(agentDir, file))) {
252
252
  entries.push(file);
253
253
  }
254
254
  }
255
255
  await (0, tar_1.create)({
256
256
  gzip: true,
257
257
  file: tmpFile,
258
- cwd: teamDir,
258
+ cwd: agentDir,
259
259
  }, entries);
260
260
  return tmpFile;
261
261
  }
@@ -281,14 +281,14 @@ async function publishToApi(token, tarPath, metadata) {
281
281
  function registerPublish(program) {
282
282
  program
283
283
  .command('publish')
284
- .description('현재 패키지를 Space에 배포합니다 (relay.yaml 필요)')
284
+ .description('현재 에이전트 패키지를 Space에 배포합니다 (relay.yaml 필요)')
285
285
  .option('--token <token>', '인증 토큰')
286
286
  .option('--space <slug>', '배포할 Space 지정')
287
287
  .option('--version <version>', '배포 버전 지정 (relay.yaml 업데이트)')
288
288
  .action(async (opts) => {
289
289
  const json = program.opts().json ?? false;
290
- const teamDir = process.cwd();
291
- const relayDir = path_1.default.join(teamDir, '.relay');
290
+ const agentDir = process.cwd();
291
+ const relayDir = path_1.default.join(agentDir, '.relay');
292
292
  const relayYamlPath = path_1.default.join(relayDir, 'relay.yaml');
293
293
  const isTTY = Boolean(process.stdin.isTTY) && !json;
294
294
  // CLI update check before publish
@@ -327,12 +327,12 @@ function registerPublish(program) {
327
327
  }
328
328
  // Interactive onboarding: create relay.yaml
329
329
  const { input: promptInput, select: promptSelect } = await import('@inquirer/prompts');
330
- const dirName = path_1.default.basename(teamDir);
330
+ const dirName = path_1.default.basename(agentDir);
331
331
  const defaultSlug = dirName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
332
- console.error('\n\x1b[36m릴레이 패키지를 초기화합니다.\x1b[0m');
332
+ console.error('\n\x1b[36m릴레이 에이전트 패키지를 초기화합니다.\x1b[0m');
333
333
  console.error('.relay/relay.yaml을 생성하기 위해 몇 가지 정보를 입력해주세요.\n');
334
334
  const name = await promptInput({
335
- message: ' 이름:',
335
+ message: '에이전트 이름:',
336
336
  default: dirName,
337
337
  });
338
338
  const slug = await promptInput({
@@ -340,7 +340,7 @@ function registerPublish(program) {
340
340
  default: defaultSlug,
341
341
  });
342
342
  const description = await promptInput({
343
- message: ' 설명 (필수):',
343
+ message: '에이전트 설명 (필수):',
344
344
  validate: (v) => v.trim().length > 0 ? true : '설명을 입력해주세요.',
345
345
  });
346
346
  const tagsRaw = await promptInput({
@@ -351,16 +351,16 @@ function registerPublish(program) {
351
351
  message: '공개 범위:',
352
352
  choices: [
353
353
  { name: '공개 — 누구나 설치', value: 'public' },
354
- { name: '링크 공유 — 접근 링크가 있는 사람만 설치', value: 'gated' },
355
- { name: '비공개 — Space 멤버만', value: 'private' },
354
+ { name: '링크 공유 — 접근 링크가 있는 사람만 설치', value: 'private' },
355
+ { name: '비공개 — Org 멤버만', value: 'internal' },
356
356
  ],
357
357
  });
358
358
  console.error('\n\x1b[2m💡 프로필에 연락처를 설정하면 설치 시 명함이 전달됩니다: www.relayax.com/dashboard/profile\x1b[0m');
359
- if (visibility === 'gated') {
360
- console.error('\x1b[2m💡 링크 공유 팀은 웹 대시보드에서 접근 링크와 구매 안내를 설정하세요: www.relayax.com/dashboard\x1b[0m');
359
+ if (visibility === 'private') {
360
+ console.error('\x1b[2m💡 링크 공유 에이전트는 웹 대시보드에서 접근 링크와 구매 안내를 설정하세요: www.relayax.com/dashboard\x1b[0m');
361
361
  }
362
- else if (visibility === 'private') {
363
- console.error('\x1b[2m💡 비공개 팀은 Space를 통해 멤버를 관리하세요: www.relayax.com/dashboard/teams\x1b[0m');
362
+ else if (visibility === 'internal') {
363
+ console.error('\x1b[2m💡 비공개 에이전트는 Org를 통해 멤버를 관리하세요: www.relayax.com/dashboard/agents\x1b[0m');
364
364
  }
365
365
  console.error('');
366
366
  const tags = tagsRaw
@@ -445,90 +445,84 @@ function registerPublish(program) {
445
445
  }));
446
446
  process.exit(1);
447
447
  }
448
- // Fetch user's Spaces and select publish target
449
- let selectedSpaceId;
450
- let selectedSpaceSlug;
451
- let selectedJoinPolicy;
448
+ // Fetch user's Orgs and select publish target
449
+ let selectedOrgId;
450
+ let selectedOrgSlug;
452
451
  try {
453
- const { fetchMySpaces } = await import('./spaces.js');
454
- const spaces = await fetchMySpaces(token);
455
- // --space flag: resolve Space by slug
452
+ const { fetchMyOrgs } = await import('./orgs.js');
453
+ const orgs = await fetchMyOrgs(token);
454
+ // --space flag (legacy alias for --org): resolve Org by slug
456
455
  if (opts.space) {
457
- const matched = spaces.find((s) => s.slug === opts.space);
456
+ const matched = orgs.find((o) => o.slug === opts.space);
458
457
  if (matched) {
459
- selectedSpaceId = matched.id;
460
- selectedSpaceSlug = matched.slug;
461
- selectedJoinPolicy = matched.join_policy;
458
+ selectedOrgId = matched.id;
459
+ selectedOrgSlug = matched.slug;
462
460
  }
463
461
  else {
464
462
  if (json) {
465
463
  console.error(JSON.stringify({
466
- error: 'INVALID_SPACE',
467
- message: `Space '${opts.space}'를 찾을 수 없습니다.`,
468
- fix: `사용 가능한 Space: ${spaces.map((s) => s.slug).join(', ')}`,
469
- options: spaces.map((s) => ({ value: s.slug, label: `${s.name} (${s.slug})` })),
464
+ error: 'INVALID_ORG',
465
+ message: `Organization '${opts.space}'를 찾을 수 없습니다.`,
466
+ fix: `사용 가능한 Org: ${orgs.map((o) => o.slug).join(', ')}`,
467
+ options: orgs.map((o) => ({ value: o.slug, label: `${o.name} (${o.slug})` })),
470
468
  }));
471
469
  }
472
470
  else {
473
- console.error(`Space '${opts.space}'를 찾을 수 없습니다.`);
471
+ console.error(`Organization '${opts.space}'를 찾을 수 없습니다.`);
474
472
  }
475
473
  process.exit(1);
476
474
  }
477
475
  }
478
476
  else if (isTTY) {
479
- if (spaces.length === 0) {
480
- // No spaces at all — publish without space_id
481
- console.error('\x1b[33m⚠ 소속 Space가 없습니다. 개인 계정으로 배포합니다.\x1b[0m\n');
477
+ if (orgs.length === 0) {
478
+ // No orgs — publish without org_id
479
+ console.error('\x1b[33m⚠ 소속 Organization이 없습니다. 개인 계정으로 배포합니다.\x1b[0m\n');
482
480
  }
483
- else if (spaces.length === 1) {
484
- // Only one Space — auto-select regardless of type
485
- selectedSpaceId = spaces[0].id;
486
- selectedSpaceSlug = spaces[0].slug;
487
- selectedJoinPolicy = spaces[0].join_policy;
488
- console.error(`\x1b[2m Space: ${spaces[0].name} (${spaces[0].slug})\x1b[0m\n`);
481
+ else if (orgs.length === 1) {
482
+ // Only one Org — auto-select
483
+ selectedOrgId = orgs[0].id;
484
+ selectedOrgSlug = orgs[0].slug;
485
+ console.error(`\x1b[2m Organization: ${orgs[0].name} (${orgs[0].slug})\x1b[0m\n`);
489
486
  }
490
487
  else {
491
- // Multiple spaces — prompt user
492
- const { select: selectSpace } = await import('@inquirer/prompts');
493
- const spaceChoices = spaces.map((s) => ({
494
- name: `${s.name} (${s.slug})`,
495
- value: s.id,
496
- slug: s.slug,
497
- join_policy: s.join_policy,
488
+ // Multiple orgs — prompt user
489
+ const { select: selectOrg } = await import('@inquirer/prompts');
490
+ const orgChoices = orgs.map((o) => ({
491
+ name: `${o.name} (${o.slug})`,
492
+ value: o.id,
493
+ slug: o.slug,
498
494
  }));
499
- const chosenId = await selectSpace({
500
- message: '어떤 Space에 배포할까요?',
501
- choices: spaceChoices.map((c) => ({ name: c.name, value: c.value })),
495
+ const chosenId = await selectOrg({
496
+ message: '어떤 Organization에 배포할까요?',
497
+ choices: orgChoices.map((c) => ({ name: c.name, value: c.value })),
502
498
  });
503
- const chosen = spaceChoices.find((c) => c.value === chosenId);
504
- selectedSpaceId = chosenId;
505
- selectedSpaceSlug = chosen?.slug;
506
- selectedJoinPolicy = chosen?.join_policy;
499
+ const chosen = orgChoices.find((c) => c.value === chosenId);
500
+ selectedOrgId = chosenId;
501
+ selectedOrgSlug = chosen?.slug;
507
502
  const chosenLabel = chosen?.name ?? chosenId;
508
- console.error(` → Space: ${chosenLabel}\n`);
503
+ console.error(` → Organization: ${chosenLabel}\n`);
509
504
  }
510
505
  }
511
- else if (spaces.length > 1 && json) {
512
- // --json 모드 + 여러 Space: 에이전트가 선택할 수 있도록 에러 반환
506
+ else if (orgs.length > 1 && json) {
507
+ // --json 모드 + 여러 Org: 에이전트가 선택할 수 있도록 에러 반환
513
508
  console.error(JSON.stringify({
514
- error: 'MISSING_SPACE',
515
- message: '배포할 Space를 선택하세요.',
516
- fix: `relay publish --space <slug> --json`,
517
- options: spaces.map((s) => ({ value: s.slug, label: `${s.name} (${s.slug})` })),
509
+ error: 'MISSING_ORG',
510
+ message: '배포할 Organization을 선택하세요.',
511
+ fix: `relay publish --org <slug> --json`,
512
+ options: orgs.map((o) => ({ value: o.slug, label: `${o.name} (${o.slug})` })),
518
513
  }));
519
514
  process.exit(1);
520
515
  }
521
- else if (spaces.length > 0) {
522
- selectedSpaceId = spaces[0].id;
523
- selectedSpaceSlug = spaces[0].slug;
524
- selectedJoinPolicy = spaces[0].join_policy;
516
+ else if (orgs.length > 0) {
517
+ selectedOrgId = orgs[0].id;
518
+ selectedOrgSlug = orgs[0].slug;
525
519
  }
526
520
  }
527
521
  catch {
528
- // Space 조회 실패 시 무시하고 계속 진행
522
+ // Org 조회 실패 시 무시하고 계속 진행
529
523
  }
530
- // Visibility default based on join_policy: approval → private, otherwise → public
531
- const defaultVisibility = selectedJoinPolicy === 'approval' ? 'private' : 'public';
524
+ // Visibility default
525
+ const defaultVisibility = 'public';
532
526
  // Visibility validation: must be explicitly set
533
527
  if (!config.visibility) {
534
528
  if (isTTY) {
@@ -543,11 +537,11 @@ function registerPublish(program) {
543
537
  },
544
538
  {
545
539
  name: '링크 공유 — 접근 링크가 있는 사람만 설치',
546
- value: 'gated',
540
+ value: 'private',
547
541
  },
548
542
  {
549
- name: `비공개 — Space 멤버만 접근${defaultVisibility === 'private' ? ' ✓ 추천' : ''}`,
550
- value: 'private',
543
+ name: `비공개 — Org 멤버만 접근`,
544
+ value: 'internal',
551
545
  },
552
546
  ],
553
547
  default: defaultVisibility,
@@ -564,8 +558,8 @@ function registerPublish(program) {
564
558
  message: 'relay.yaml에 visibility를 설정해주세요.',
565
559
  options: [
566
560
  { value: 'public', label: '공개 — 누구나 설치' },
567
- { value: 'gated', label: '링크 공유 — 접근 링크가 있는 사람만 설치' },
568
- { value: 'private', label: '비공개 — Space 멤버만 접근' },
561
+ { value: 'private', label: '링크 공유 — 접근 링크가 있는 사람만 설치' },
562
+ { value: 'internal', label: '비공개 — Org 멤버만 접근' },
569
563
  ],
570
564
  fix: 'relay.yaml의 visibility 필드를 위 옵션 중 하나로 설정하세요.',
571
565
  }));
@@ -577,8 +571,8 @@ function registerPublish(program) {
577
571
  const { select: promptConfirmVis } = await import('@inquirer/prompts');
578
572
  const visLabelMap = {
579
573
  public: '공개',
580
- gated: '링크공유',
581
- private: '비공개',
574
+ private: '링크공유',
575
+ internal: '비공개',
582
576
  };
583
577
  const currentVisLabel = visLabelMap[config.visibility ?? 'public'] ?? config.visibility;
584
578
  const newVisibility = await promptConfirmVis({
@@ -590,11 +584,11 @@ function registerPublish(program) {
590
584
  },
591
585
  {
592
586
  name: '링크공유 — 접근 링크가 있는 사람만 설치',
593
- value: 'gated',
587
+ value: 'private',
594
588
  },
595
589
  {
596
- name: `비공개 — Space 멤버만 접근${defaultVisibility === 'private' ? ' ✓ 추천' : ''}`,
597
- value: 'private',
590
+ name: `비공개 — Org 멤버만 접근`,
591
+ value: 'internal',
598
592
  },
599
593
  ],
600
594
  default: config.visibility ?? defaultVisibility,
@@ -638,8 +632,8 @@ function registerPublish(program) {
638
632
  type: config.type ?? 'hybrid',
639
633
  agent_details: detectedAgents,
640
634
  skill_details: detectedSkills,
641
- ...(selectedSpaceId ? { space_id: selectedSpaceId } : {}),
642
- ...(selectedSpaceSlug ? { space_slug: selectedSpaceSlug } : {}),
635
+ ...(selectedOrgId ? { org_id: selectedOrgId } : {}),
636
+ ...(selectedOrgSlug ? { org_slug: selectedOrgSlug } : {}),
643
637
  };
644
638
  if (!json) {
645
639
  console.error(`패키지 생성 중... (${config.name} v${config.version})`);
@@ -647,7 +641,7 @@ function registerPublish(program) {
647
641
  // GUIDE.html deprecation warning
648
642
  if (fs_1.default.existsSync(path_1.default.join(relayDir, 'GUIDE.html'))) {
649
643
  console.error('\x1b[33m⚠ GUIDE.html은 더 이상 지원되지 않습니다. 상세페이지가 가이드 역할을 합니다.\x1b[0m');
650
- console.error(' long_description을 활용하거나 relayax.com에서 정보를 편집하세요.\n');
644
+ console.error(' long_description을 활용하거나 relayax.com에서 에이전트 정보를 편집하세요.\n');
651
645
  }
652
646
  // Generate guide.md (consumer install guide)
653
647
  const { generateGuide } = await import('../lib/guide.js');
@@ -717,7 +711,10 @@ function registerPublish(program) {
717
711
  // Show shareable onboarding guide as a plain copyable block
718
712
  if (isTTY) {
719
713
  const detailSlug = result.slug.startsWith('@') ? result.slug.slice(1) : result.slug;
720
- const guideUrl = `https://relayax.com/api/registry/${detailSlug}/guide.md`;
714
+ const accessCode = result.access_code;
715
+ const guideUrl = accessCode
716
+ ? `https://relayax.com/api/registry/${detailSlug}/guide.md?code=${accessCode}`
717
+ : `https://relayax.com/api/registry/${detailSlug}/guide.md`;
721
718
  console.log(`\n \x1b[90m주변인에게 공유하세요:\x1b[0m\n`);
722
719
  console.log('```');
723
720
  console.log('아래 가이드를 읽고 그대로 따라해줘:');
@@ -725,9 +722,14 @@ function registerPublish(program) {
725
722
  console.log('```');
726
723
  console.log(`\n \x1b[90mCLI 설치된 사용자용 (짧은 버전):\x1b[0m`);
727
724
  console.log(` /relay:relay-install ${result.slug}`);
728
- if (config.visibility !== 'private') {
725
+ if (config.visibility === 'private') {
726
+ console.log(`\n \x1b[90mprivate 에이전트:\x1b[0m`);
727
+ console.log(` 접근 링크를 생성한 뒤 guide.md?code={agent_code}로 공유하세요.`);
728
+ console.log(` 접근 링크 관리: \x1b[36mrelayax.com/dashboard/agent-access/${config.slug}\x1b[0m`);
729
+ }
730
+ else if (config.visibility !== 'internal') {
729
731
  console.log(`\n \x1b[90m유료 판매하려면:\x1b[0m`);
730
- console.log(` 1. 가시성을 "링크 공유"로 변경: \x1b[36mrelayax.com/dashboard\x1b[0m`);
732
+ console.log(` 1. 가시성을 "private"로 변경: \x1b[36mrelayax.com/dashboard\x1b[0m`);
731
733
  console.log(` 2. API 키 발급: \x1b[36mrelayax.com/dashboard/keys\x1b[0m`);
732
734
  console.log(` 3. 웹훅 연동 가이드: \x1b[36mrelayax.com/docs/webhook-guide.md\x1b[0m`);
733
735
  }
@@ -26,13 +26,13 @@ function formatTable(results) {
26
26
  function registerSearch(program) {
27
27
  program
28
28
  .command('search <keyword>')
29
- .description('Space에서 에이전트 검색 (공개 + 내 Space )')
29
+ .description('Space에서 에이전트 검색 (공개 에이전트 + 내 Space 에이전트)')
30
30
  .option('--tag <tag>', '태그로 필터링')
31
31
  .option('--space <space>', '특정 Space 내에서 검색')
32
32
  .action(async (keyword, opts) => {
33
33
  const json = program.opts().json ?? false;
34
34
  try {
35
- const results = await (0, api_js_1.searchTeams)(keyword, opts.tag, opts.space);
35
+ const results = await (0, api_js_1.searchAgents)(keyword, opts.tag);
36
36
  if (json) {
37
37
  console.log(JSON.stringify({ results }));
38
38
  }
@@ -47,27 +47,27 @@ function registerStatus(program) {
47
47
  const localDir = path_1.default.join(projectPath, primaryAgent.skillsDir, 'commands', 'relay');
48
48
  hasLocal = command_adapter_js_1.BUILDER_COMMANDS.some((cmd) => fs_1.default.existsSync(path_1.default.join(localDir, `${cmd.id}.md`)));
49
49
  }
50
- // 3. 정보
50
+ // 3. 에이전트 프로젝트 정보
51
51
  const relayYamlPath = path_1.default.join(projectPath, '.relay', 'relay.yaml');
52
- let team = null;
52
+ let project = null;
53
53
  if (fs_1.default.existsSync(relayYamlPath)) {
54
54
  try {
55
55
  const yaml = await import('js-yaml');
56
56
  const content = fs_1.default.readFileSync(relayYamlPath, 'utf-8');
57
57
  const raw = yaml.load(content);
58
- team = {
59
- is_team: true,
58
+ project = {
59
+ is_agent: true,
60
60
  name: String(raw.name ?? ''),
61
61
  slug: String(raw.slug ?? ''),
62
62
  version: String(raw.version ?? ''),
63
63
  };
64
64
  }
65
65
  catch {
66
- team = { is_team: true };
66
+ project = { is_agent: true };
67
67
  }
68
68
  }
69
69
  else {
70
- team = { is_team: false };
70
+ project = { is_agent: false };
71
71
  }
72
72
  // 4. 출력
73
73
  if (json) {
@@ -78,7 +78,7 @@ function registerStatus(program) {
78
78
  global_commands: hasGlobal,
79
79
  local_commands: hasLocal,
80
80
  },
81
- team,
81
+ project,
82
82
  };
83
83
  console.log(JSON.stringify(result));
84
84
  }
@@ -103,12 +103,12 @@ function registerStatus(program) {
103
103
  else {
104
104
  console.log(` \x1b[31m✗\x1b[0m 에이전트: 감지 안 됨`);
105
105
  }
106
- //
107
- if (team?.is_team && team.name) {
108
- console.log(` \x1b[32m✓\x1b[0m 현재 팀: \x1b[36m${team.name}\x1b[0m v${team.version}`);
106
+ // 에이전트 프로젝트
107
+ if (project?.is_agent && project.name) {
108
+ console.log(` \x1b[32m✓\x1b[0m 현재 에이전트: \x1b[36m${project.name}\x1b[0m v${project.version}`);
109
109
  }
110
110
  else {
111
- console.log(` \x1b[2m—\x1b[0m 현재 프로젝트: 아님`);
111
+ console.log(` \x1b[2m—\x1b[0m 현재 프로젝트: 에이전트 아님`);
112
112
  }
113
113
  console.log('');
114
114
  }
@@ -12,12 +12,12 @@ const slug_js_1 = require("../lib/slug.js");
12
12
  function registerUninstall(program) {
13
13
  program
14
14
  .command('uninstall <slug>')
15
- .description('에이전트 제거')
15
+ .description('에이전트 제거')
16
16
  .action((slugInput) => {
17
17
  const json = program.opts().json ?? false;
18
18
  const localInstalled = (0, config_js_1.loadInstalled)();
19
19
  const globalInstalled = (0, config_js_1.loadGlobalInstalled)();
20
- // Resolve slug — support short names like "cardnews-team"
20
+ // Resolve slug — support short names like "cardnews-agent"
21
21
  let slug;
22
22
  if ((0, slug_js_1.isScopedSlug)(slugInput)) {
23
23
  slug = slugInput;
@@ -45,11 +45,11 @@ function registerUninstall(program) {
45
45
  let totalRemoved = 0;
46
46
  // Remove from local registry
47
47
  if (localEntry) {
48
- const removed = (0, installer_js_1.uninstallTeam)(localEntry.files);
48
+ const removed = (0, installer_js_1.uninstallAgent)(localEntry.files);
49
49
  totalRemoved += removed.length;
50
50
  // Remove deployed files
51
51
  if (localEntry.deployed_files && localEntry.deployed_files.length > 0) {
52
- const deployedRemoved = (0, installer_js_1.uninstallTeam)(localEntry.deployed_files);
52
+ const deployedRemoved = (0, installer_js_1.uninstallAgent)(localEntry.deployed_files);
53
53
  totalRemoved += deployedRemoved.length;
54
54
  // Clean empty parent directories
55
55
  const boundary = path_1.default.join(process.cwd(), '.claude');
@@ -64,12 +64,12 @@ function registerUninstall(program) {
64
64
  if (globalEntry) {
65
65
  // Only remove files if not already handled by local entry
66
66
  if (!localEntry) {
67
- const removed = (0, installer_js_1.uninstallTeam)(globalEntry.files);
67
+ const removed = (0, installer_js_1.uninstallAgent)(globalEntry.files);
68
68
  totalRemoved += removed.length;
69
69
  }
70
70
  // Remove globally deployed files
71
71
  if (globalEntry.deployed_files && globalEntry.deployed_files.length > 0) {
72
- const deployedRemoved = (0, installer_js_1.uninstallTeam)(globalEntry.deployed_files);
72
+ const deployedRemoved = (0, installer_js_1.uninstallAgent)(globalEntry.deployed_files);
73
73
  totalRemoved += deployedRemoved.length;
74
74
  // Clean empty parent directories
75
75
  const boundary = path_1.default.join(os_1.default.homedir(), '.claude');
@@ -82,7 +82,7 @@ function registerUninstall(program) {
82
82
  }
83
83
  const result = {
84
84
  status: 'ok',
85
- team: slug,
85
+ agent: slug,
86
86
  files_removed: totalRemoved,
87
87
  };
88
88
  if (json) {