relayax-cli 0.2.40 → 0.3.41

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.
@@ -43,6 +43,7 @@ function parseRelayYaml(content) {
43
43
  requires,
44
44
  visibility,
45
45
  type,
46
+ source: raw.source ? String(raw.source) : undefined,
46
47
  };
47
48
  }
48
49
  function detectCommands(teamDir) {
@@ -245,7 +246,7 @@ async function createTarball(teamDir) {
245
246
  const dirsToInclude = VALID_DIRS.filter((d) => fs_1.default.existsSync(path_1.default.join(teamDir, d)));
246
247
  // Include root-level files if they exist
247
248
  const entries = [...dirsToInclude];
248
- const rootFiles = ['relay.yaml', 'SKILL.md'];
249
+ const rootFiles = ['relay.yaml', 'SKILL.md', 'guide.md'];
249
250
  for (const file of rootFiles) {
250
251
  if (fs_1.default.existsSync(path_1.default.join(teamDir, file))) {
251
252
  entries.push(file);
@@ -444,90 +445,84 @@ function registerPublish(program) {
444
445
  }));
445
446
  process.exit(1);
446
447
  }
447
- // Fetch user's Spaces and select publish target
448
- let selectedSpaceId;
449
- let selectedSpaceSlug;
450
- let selectedJoinPolicy;
448
+ // Fetch user's Orgs and select publish target
449
+ let selectedOrgId;
450
+ let selectedOrgSlug;
451
451
  try {
452
- const { fetchMySpaces } = await import('./spaces.js');
453
- const spaces = await fetchMySpaces(token);
454
- // --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
455
455
  if (opts.space) {
456
- const matched = spaces.find((s) => s.slug === opts.space);
456
+ const matched = orgs.find((o) => o.slug === opts.space);
457
457
  if (matched) {
458
- selectedSpaceId = matched.id;
459
- selectedSpaceSlug = matched.slug;
460
- selectedJoinPolicy = matched.join_policy;
458
+ selectedOrgId = matched.id;
459
+ selectedOrgSlug = matched.slug;
461
460
  }
462
461
  else {
463
462
  if (json) {
464
463
  console.error(JSON.stringify({
465
- error: 'INVALID_SPACE',
466
- message: `Space '${opts.space}'를 찾을 수 없습니다.`,
467
- fix: `사용 가능한 Space: ${spaces.map((s) => s.slug).join(', ')}`,
468
- 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})` })),
469
468
  }));
470
469
  }
471
470
  else {
472
- console.error(`Space '${opts.space}'를 찾을 수 없습니다.`);
471
+ console.error(`Organization '${opts.space}'를 찾을 수 없습니다.`);
473
472
  }
474
473
  process.exit(1);
475
474
  }
476
475
  }
477
476
  else if (isTTY) {
478
- if (spaces.length === 0) {
479
- // No spaces at all — publish without space_id
480
- 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');
481
480
  }
482
- else if (spaces.length === 1) {
483
- // Only one Space — auto-select regardless of type
484
- selectedSpaceId = spaces[0].id;
485
- selectedSpaceSlug = spaces[0].slug;
486
- selectedJoinPolicy = spaces[0].join_policy;
487
- 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`);
488
486
  }
489
487
  else {
490
- // Multiple spaces — prompt user
491
- const { select: selectSpace } = await import('@inquirer/prompts');
492
- const spaceChoices = spaces.map((s) => ({
493
- name: `${s.name} (${s.slug})`,
494
- value: s.id,
495
- slug: s.slug,
496
- 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,
497
494
  }));
498
- const chosenId = await selectSpace({
499
- message: '어떤 Space에 배포할까요?',
500
- 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 })),
501
498
  });
502
- const chosen = spaceChoices.find((c) => c.value === chosenId);
503
- selectedSpaceId = chosenId;
504
- selectedSpaceSlug = chosen?.slug;
505
- selectedJoinPolicy = chosen?.join_policy;
499
+ const chosen = orgChoices.find((c) => c.value === chosenId);
500
+ selectedOrgId = chosenId;
501
+ selectedOrgSlug = chosen?.slug;
506
502
  const chosenLabel = chosen?.name ?? chosenId;
507
- console.error(` → Space: ${chosenLabel}\n`);
503
+ console.error(` → Organization: ${chosenLabel}\n`);
508
504
  }
509
505
  }
510
- else if (spaces.length > 1 && json) {
511
- // --json 모드 + 여러 Space: 에이전트가 선택할 수 있도록 에러 반환
506
+ else if (orgs.length > 1 && json) {
507
+ // --json 모드 + 여러 Org: 에이전트가 선택할 수 있도록 에러 반환
512
508
  console.error(JSON.stringify({
513
- error: 'MISSING_SPACE',
514
- message: '배포할 Space를 선택하세요.',
515
- fix: `relay publish --space <slug> --json`,
516
- 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})` })),
517
513
  }));
518
514
  process.exit(1);
519
515
  }
520
- else if (spaces.length > 0) {
521
- selectedSpaceId = spaces[0].id;
522
- selectedSpaceSlug = spaces[0].slug;
523
- selectedJoinPolicy = spaces[0].join_policy;
516
+ else if (orgs.length > 0) {
517
+ selectedOrgId = orgs[0].id;
518
+ selectedOrgSlug = orgs[0].slug;
524
519
  }
525
520
  }
526
521
  catch {
527
- // Space 조회 실패 시 무시하고 계속 진행
522
+ // Org 조회 실패 시 무시하고 계속 진행
528
523
  }
529
- // Visibility default based on join_policy: approval → private, otherwise → public
530
- const defaultVisibility = selectedJoinPolicy === 'approval' ? 'private' : 'public';
524
+ // Visibility default
525
+ const defaultVisibility = 'public';
531
526
  // Visibility validation: must be explicitly set
532
527
  if (!config.visibility) {
533
528
  if (isTTY) {
@@ -545,7 +540,7 @@ function registerPublish(program) {
545
540
  value: 'gated',
546
541
  },
547
542
  {
548
- name: `비공개 — Space 멤버만 접근${defaultVisibility === 'private' ? ' ✓ 추천' : ''}`,
543
+ name: `비공개 — Space 멤버만 접근`,
549
544
  value: 'private',
550
545
  },
551
546
  ],
@@ -592,7 +587,7 @@ function registerPublish(program) {
592
587
  value: 'gated',
593
588
  },
594
589
  {
595
- name: `비공개 — Space 멤버만 접근${defaultVisibility === 'private' ? ' ✓ 추천' : ''}`,
590
+ name: `비공개 — Space 멤버만 접근`,
596
591
  value: 'private',
597
592
  },
598
593
  ],
@@ -637,8 +632,8 @@ function registerPublish(program) {
637
632
  type: config.type ?? 'hybrid',
638
633
  agent_details: detectedAgents,
639
634
  skill_details: detectedSkills,
640
- ...(selectedSpaceId ? { space_id: selectedSpaceId } : {}),
641
- ...(selectedSpaceSlug ? { space_slug: selectedSpaceSlug } : {}),
635
+ ...(selectedOrgId ? { org_id: selectedOrgId } : {}),
636
+ ...(selectedOrgSlug ? { org_slug: selectedOrgSlug } : {}),
642
637
  };
643
638
  if (!json) {
644
639
  console.error(`패키지 생성 중... (${config.name} v${config.version})`);
@@ -648,6 +643,10 @@ function registerPublish(program) {
648
643
  console.error('\x1b[33m⚠ GUIDE.html은 더 이상 지원되지 않습니다. 상세페이지가 가이드 역할을 합니다.\x1b[0m');
649
644
  console.error(' long_description을 활용하거나 relayax.com에서 팀 정보를 편집하세요.\n');
650
645
  }
646
+ // Generate guide.md (consumer install guide)
647
+ const { generateGuide } = await import('../lib/guide.js');
648
+ const guideContent = generateGuide(config, detectedCommands, config.requires);
649
+ fs_1.default.writeFileSync(path_1.default.join(relayDir, 'guide.md'), guideContent);
651
650
  // Generate bin/relay-preamble.sh (self-contained tracking + update check)
652
651
  (0, preamble_js_1.generatePreambleBin)(relayDir, config.slug, config_js_1.API_URL);
653
652
  // Generate entry command (commands/{author}-{name}.md)
@@ -720,6 +719,12 @@ function registerPublish(program) {
720
719
  console.log('```');
721
720
  console.log(`\n \x1b[90mCLI 설치된 사용자용 (짧은 버전):\x1b[0m`);
722
721
  console.log(` /relay:relay-install ${result.slug}`);
722
+ if (config.visibility !== 'private') {
723
+ console.log(`\n \x1b[90m유료 판매하려면:\x1b[0m`);
724
+ console.log(` 1. 가시성을 "링크 공유"로 변경: \x1b[36mrelayax.com/dashboard\x1b[0m`);
725
+ console.log(` 2. API 키 발급: \x1b[36mrelayax.com/dashboard/keys\x1b[0m`);
726
+ console.log(` 3. 웹훅 연동 가이드: \x1b[36mrelayax.com/docs/webhook-guide.md\x1b[0m`);
727
+ }
723
728
  console.log(`\n \x1b[90m상세페이지: \x1b[36mrelayax.com/@${detailSlug}\x1b[0m`);
724
729
  }
725
730
  }
@@ -32,7 +32,7 @@ function registerSearch(program) {
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.searchTeams)(keyword, opts.tag);
36
36
  if (json) {
37
37
  console.log(JSON.stringify({ results }));
38
38
  }
@@ -4,7 +4,6 @@ export interface SpaceInfo {
4
4
  slug: string;
5
5
  name: string;
6
6
  description: string | null;
7
- space_type?: string;
8
7
  join_policy?: string;
9
8
  role: string;
10
9
  }
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerVersions(program: Command): void;
@@ -0,0 +1,44 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerVersions = registerVersions;
4
+ const api_js_1 = require("../lib/api.js");
5
+ const slug_js_1 = require("../lib/slug.js");
6
+ function registerVersions(program) {
7
+ program
8
+ .command('versions <slug>')
9
+ .description('팀 버전 목록과 릴리즈 노트를 확인합니다')
10
+ .action(async (slugInput) => {
11
+ const json = program.opts().json ?? false;
12
+ try {
13
+ const resolved = await (0, slug_js_1.resolveSlug)(slugInput);
14
+ const versions = await (0, api_js_1.fetchTeamVersions)(resolved.full);
15
+ if (json) {
16
+ console.log(JSON.stringify({ slug: resolved.full, versions }));
17
+ return;
18
+ }
19
+ if (versions.length === 0) {
20
+ console.log(`\n${resolved.full} 버전 이력이 없습니다.`);
21
+ return;
22
+ }
23
+ console.log(`\n\x1b[1m${resolved.full} 버전 이력\x1b[0m (${versions.length}개):\n`);
24
+ for (const v of versions) {
25
+ const date = new Date(v.created_at).toLocaleDateString('ko-KR');
26
+ console.log(` \x1b[36mv${v.version}\x1b[0m (${date})`);
27
+ if (v.changelog) {
28
+ console.log(` \x1b[90m${v.changelog}\x1b[0m`);
29
+ }
30
+ }
31
+ console.log(`\n\x1b[33m 특정 버전 설치: relay install ${resolved.full}@<version>\x1b[0m`);
32
+ }
33
+ catch (err) {
34
+ const message = err instanceof Error ? err.message : String(err);
35
+ if (json) {
36
+ console.error(JSON.stringify({ error: 'VERSIONS_FAILED', message }));
37
+ }
38
+ else {
39
+ console.error(`\x1b[31m오류: ${message}\x1b[0m`);
40
+ }
41
+ process.exit(1);
42
+ }
43
+ });
44
+ }
package/dist/index.js CHANGED
@@ -9,6 +9,7 @@ const search_js_1 = require("./commands/search.js");
9
9
  const install_js_1 = require("./commands/install.js");
10
10
  const list_js_1 = require("./commands/list.js");
11
11
  const uninstall_js_1 = require("./commands/uninstall.js");
12
+ const package_js_1 = require("./commands/package.js");
12
13
  const publish_js_1 = require("./commands/publish.js");
13
14
  const login_js_1 = require("./commands/login.js");
14
15
  const update_js_1 = require("./commands/update.js");
@@ -17,10 +18,12 @@ const check_update_js_1 = require("./commands/check-update.js");
17
18
  const follow_js_1 = require("./commands/follow.js");
18
19
  const changelog_js_1 = require("./commands/changelog.js");
19
20
  const join_js_1 = require("./commands/join.js");
20
- const spaces_js_1 = require("./commands/spaces.js");
21
+ const orgs_js_1 = require("./commands/orgs.js");
21
22
  const deploy_record_js_1 = require("./commands/deploy-record.js");
22
23
  const ping_js_1 = require("./commands/ping.js");
23
24
  const access_js_1 = require("./commands/access.js");
25
+ const versions_js_1 = require("./commands/versions.js");
26
+ const diff_js_1 = require("./commands/diff.js");
24
27
  // eslint-disable-next-line @typescript-eslint/no-var-requires
25
28
  const pkg = require('../package.json');
26
29
  const program = new commander_1.Command();
@@ -36,6 +39,7 @@ program
36
39
  (0, install_js_1.registerInstall)(program);
37
40
  (0, list_js_1.registerList)(program);
38
41
  (0, uninstall_js_1.registerUninstall)(program);
42
+ (0, package_js_1.registerPackage)(program);
39
43
  (0, publish_js_1.registerPublish)(program);
40
44
  (0, login_js_1.registerLogin)(program);
41
45
  (0, update_js_1.registerUpdate)(program);
@@ -44,8 +48,10 @@ program
44
48
  (0, follow_js_1.registerFollow)(program);
45
49
  (0, changelog_js_1.registerChangelog)(program);
46
50
  (0, join_js_1.registerJoin)(program);
47
- (0, spaces_js_1.registerSpaces)(program);
51
+ (0, orgs_js_1.registerOrgs)(program);
48
52
  (0, deploy_record_js_1.registerDeployRecord)(program);
49
53
  (0, ping_js_1.registerPing)(program);
50
54
  (0, access_js_1.registerAccess)(program);
55
+ (0, versions_js_1.registerVersions)(program);
56
+ (0, diff_js_1.registerDiff)(program);
51
57
  program.parse();
package/dist/lib/api.d.ts CHANGED
@@ -1,6 +1,12 @@
1
1
  import type { TeamRegistryInfo, SearchResult } from '../types.js';
2
+ export declare function fetchMyOrgs(): Promise<{
3
+ id: string;
4
+ slug: string;
5
+ name: string;
6
+ role: string;
7
+ }[]>;
2
8
  export declare function fetchTeamInfo(slug: string): Promise<TeamRegistryInfo>;
3
- export declare function searchTeams(query: string, tag?: string, space?: string): Promise<SearchResult[]>;
9
+ export declare function searchTeams(query: string, tag?: string): Promise<SearchResult[]>;
4
10
  export interface TeamVersionInfo {
5
11
  version: string;
6
12
  changelog: string | null;
@@ -8,11 +14,6 @@ export interface TeamVersionInfo {
8
14
  }
9
15
  export declare function fetchTeamVersions(slug: string): Promise<TeamVersionInfo[]>;
10
16
  export declare function reportInstall(teamId: string, slug: string, version?: string): Promise<void>;
11
- /**
12
- * Space 팀 설치: 멤버십 검증 + install count 증가 + 팀 메타데이터 반환을 한 번에 처리.
13
- * POST /api/spaces/{spaceSlug}/teams/{teamSlug}/install
14
- */
15
- export declare function installSpaceTeam(spaceSlug: string, teamSlug: string, version?: string): Promise<TeamRegistryInfo>;
16
17
  export interface ResolvedSlug {
17
18
  owner: string;
18
19
  name: string;
package/dist/lib/api.js CHANGED
@@ -1,14 +1,26 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fetchMyOrgs = fetchMyOrgs;
3
4
  exports.fetchTeamInfo = fetchTeamInfo;
4
5
  exports.searchTeams = searchTeams;
5
6
  exports.fetchTeamVersions = fetchTeamVersions;
6
7
  exports.reportInstall = reportInstall;
7
- exports.installSpaceTeam = installSpaceTeam;
8
8
  exports.resolveSlugFromServer = resolveSlugFromServer;
9
9
  exports.sendUsagePing = sendUsagePing;
10
10
  exports.followBuilder = followBuilder;
11
11
  const config_js_1 = require("./config.js");
12
+ async function fetchMyOrgs() {
13
+ const token = await (0, config_js_1.getValidToken)();
14
+ if (!token)
15
+ return [];
16
+ const res = await fetch(`${config_js_1.API_URL}/api/orgs`, {
17
+ headers: { Authorization: `Bearer ${token}` },
18
+ signal: AbortSignal.timeout(8000),
19
+ });
20
+ if (!res.ok)
21
+ return [];
22
+ return res.json();
23
+ }
12
24
  async function fetchTeamInfo(slug) {
13
25
  const registrySlug = slug.startsWith('@') ? slug.slice(1) : slug;
14
26
  const url = `${config_js_1.API_URL}/api/registry/${registrySlug}`;
@@ -19,12 +31,10 @@ async function fetchTeamInfo(slug) {
19
31
  }
20
32
  return res.json();
21
33
  }
22
- async function searchTeams(query, tag, space) {
34
+ async function searchTeams(query, tag) {
23
35
  const params = new URLSearchParams({ q: query });
24
36
  if (tag)
25
37
  params.set('tag', tag);
26
- if (space)
27
- params.set('space', space);
28
38
  const url = `${config_js_1.API_URL}/api/registry/search?${params.toString()}`;
29
39
  const res = await fetch(url);
30
40
  if (!res.ok) {
@@ -69,31 +79,6 @@ async function reportInstall(teamId, slug, version) {
69
79
  // network error: ignore silently
70
80
  }
71
81
  }
72
- /**
73
- * Space 팀 설치: 멤버십 검증 + install count 증가 + 팀 메타데이터 반환을 한 번에 처리.
74
- * POST /api/spaces/{spaceSlug}/teams/{teamSlug}/install
75
- */
76
- async function installSpaceTeam(spaceSlug, teamSlug, version) {
77
- const url = `${config_js_1.API_URL}/api/spaces/${spaceSlug}/teams/${teamSlug}/install`;
78
- const headers = { 'Content-Type': 'application/json' };
79
- const token = await (0, config_js_1.getValidToken)();
80
- if (token) {
81
- headers['Authorization'] = `Bearer ${token}`;
82
- }
83
- const body = {};
84
- if (version)
85
- body.version = version;
86
- const res = await fetch(url, {
87
- method: 'POST',
88
- headers,
89
- body: JSON.stringify(body),
90
- });
91
- if (!res.ok) {
92
- const text = await res.text();
93
- throw new Error(`Space 팀 설치 실패 (${res.status}): ${text}`);
94
- }
95
- return res.json();
96
- }
97
82
  async function resolveSlugFromServer(name) {
98
83
  const url = `${config_js_1.API_URL}/api/registry/resolve?name=${encodeURIComponent(name)}`;
99
84
  const res = await fetch(url, { signal: AbortSignal.timeout(5000) });