relayax-cli 0.2.30 → 0.2.35

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.
@@ -181,18 +181,19 @@ function registerInstall(program) {
181
181
  return count;
182
182
  }
183
183
  const fileCount = countFiles(teamDir);
184
- // 6. Record in installed.json (space_slug 포함)
184
+ // 6. Record in installed.json (team_id, space_slug 포함)
185
185
  const installed = (0, config_js_1.loadInstalled)();
186
186
  installed[slug] = {
187
+ team_id: team.id,
187
188
  version: team.version,
188
189
  installed_at: new Date().toISOString(),
189
190
  files: [teamDir],
190
191
  ...(spaceTarget ? { space_slug: spaceTarget.spaceSlug } : {}),
191
192
  };
192
193
  (0, config_js_1.saveInstalled)(installed);
193
- // 7. Report install + usage ping (non-blocking)
194
- await (0, api_js_1.reportInstall)(slug, team.version);
195
- (0, api_js_1.sendUsagePing)(slug, team.version);
194
+ // 7. Report install + usage ping (non-blocking, team_id 기반)
195
+ await (0, api_js_1.reportInstall)(team.id, slug, team.version);
196
+ (0, api_js_1.sendUsagePing)(team.id, slug, team.version);
196
197
  const result = {
197
198
  status: 'ok',
198
199
  team: team.name,
@@ -25,13 +25,16 @@ function registerPing(program) {
25
25
  });
26
26
  slug = match ?? slugInput;
27
27
  }
28
- // Resolve version from installed registry
28
+ // Resolve version and team_id from installed registry
29
29
  const local = (0, config_js_1.loadInstalled)();
30
30
  const global = (0, config_js_1.loadGlobalInstalled)();
31
31
  const entry = local[slug] ?? global[slug];
32
32
  const version = entry?.version;
33
- // Fire-and-forget ping
34
- await (0, api_js_1.sendUsagePing)(slug, version);
33
+ const teamId = entry?.team_id;
34
+ // Fire-and-forget ping (team_id 기반, 없으면 skip)
35
+ if (teamId) {
36
+ await (0, api_js_1.sendUsagePing)(teamId, slug, version);
37
+ }
35
38
  if (!opts.quiet) {
36
39
  console.log(`RELAY_READY: ${slug}`);
37
40
  }
@@ -249,10 +249,13 @@ function resolveLongDescription(teamDir, yamlValue) {
249
249
  async function createTarball(teamDir) {
250
250
  const tmpFile = path_1.default.join(os_1.default.tmpdir(), `relay-publish-${Date.now()}.tar.gz`);
251
251
  const dirsToInclude = VALID_DIRS.filter((d) => fs_1.default.existsSync(path_1.default.join(teamDir, d)));
252
- // Include GUIDE.html if it exists (root SKILL.md is replaced by entry command)
252
+ // Include root-level files if they exist
253
253
  const entries = [...dirsToInclude];
254
- if (fs_1.default.existsSync(path_1.default.join(teamDir, 'GUIDE.html'))) {
255
- entries.push('GUIDE.html');
254
+ const rootFiles = ['relay.yaml', 'GUIDE.html', 'SKILL.md'];
255
+ for (const file of rootFiles) {
256
+ if (fs_1.default.existsSync(path_1.default.join(teamDir, file))) {
257
+ entries.push(file);
258
+ }
256
259
  }
257
260
  await (0, tar_1.create)({
258
261
  gzip: true,
@@ -67,6 +67,7 @@ function registerUpdate(program) {
67
67
  const hadDeployedFiles = (currentEntry?.deployed_files?.length ?? 0) > 0;
68
68
  // Update installed.json with new version
69
69
  installed[slug] = {
70
+ team_id: team.id,
70
71
  version: latestVersion,
71
72
  installed_at: new Date().toISOString(),
72
73
  files,
@@ -75,8 +76,8 @@ function registerUpdate(program) {
75
76
  // Clear deployed_files — agent must re-deploy and call deploy-record
76
77
  };
77
78
  (0, config_js_1.saveInstalled)(installed);
78
- // Report install (non-blocking)
79
- await (0, api_js_1.reportInstall)(slug);
79
+ // Report install (non-blocking, team_id 기반)
80
+ await (0, api_js_1.reportInstall)(team.id, slug, latestVersion);
80
81
  const result = {
81
82
  status: 'updated',
82
83
  slug,
package/dist/lib/api.d.ts CHANGED
@@ -7,7 +7,7 @@ export interface TeamVersionInfo {
7
7
  created_at: string;
8
8
  }
9
9
  export declare function fetchTeamVersions(slug: string): Promise<TeamVersionInfo[]>;
10
- export declare function reportInstall(slug: string, version?: string): Promise<void>;
10
+ export declare function reportInstall(teamId: string, slug: string, version?: string): Promise<void>;
11
11
  /**
12
12
  * Space 팀 설치: 멤버십 검증 + install count 증가 + 팀 메타데이터 반환을 한 번에 처리.
13
13
  * POST /api/spaces/{spaceSlug}/teams/{teamSlug}/install
@@ -19,5 +19,5 @@ export interface ResolvedSlug {
19
19
  full: string;
20
20
  }
21
21
  export declare function resolveSlugFromServer(name: string): Promise<ResolvedSlug[]>;
22
- export declare function sendUsagePing(slug: string, version?: string): Promise<void>;
22
+ export declare function sendUsagePing(teamId: string, slug: string, version?: string): Promise<void>;
23
23
  export declare function followBuilder(username: string): Promise<void>;
package/dist/lib/api.js CHANGED
@@ -42,26 +42,30 @@ async function fetchTeamVersions(slug) {
42
42
  }
43
43
  return res.json();
44
44
  }
45
- async function reportInstall(slug, version) {
46
- const registrySlug = slug.startsWith('@') ? slug.slice(1) : slug;
47
- const url = `${config_js_1.API_URL}/api/registry/${registrySlug}/install`;
48
- const headers = {};
49
- const body = {};
50
- if (version) {
51
- headers['Content-Type'] = 'application/json';
45
+ async function reportInstall(teamId, slug, version) {
46
+ const url = `${config_js_1.API_URL}/api/teams/${teamId}/install`;
47
+ const headers = { 'Content-Type': 'application/json' };
48
+ const body = { slug };
49
+ if (version)
52
50
  body.version = version;
53
- }
54
51
  const token = await (0, config_js_1.getValidToken)();
55
52
  if (token) {
56
53
  headers['Authorization'] = `Bearer ${token}`;
57
54
  }
58
- await fetch(url, {
59
- method: 'POST',
60
- headers: Object.keys(headers).length > 0 ? headers : undefined,
61
- body: Object.keys(body).length > 0 ? JSON.stringify(body) : undefined,
62
- }).catch(() => {
63
- // non-critical: ignore errors
64
- });
55
+ try {
56
+ const res = await fetch(url, {
57
+ method: 'POST',
58
+ headers,
59
+ body: JSON.stringify(body),
60
+ });
61
+ if (!res.ok) {
62
+ const text = await res.text().catch(() => '');
63
+ console.error(`\x1b[33m⚠ 설치 카운트 업데이트 실패 (${res.status}): ${text}\x1b[0m`);
64
+ }
65
+ }
66
+ catch {
67
+ // network error: ignore silently
68
+ }
65
69
  }
66
70
  /**
67
71
  * Space 팀 설치: 멤버십 검증 + install count 증가 + 팀 메타데이터 반환을 한 번에 처리.
@@ -97,15 +101,14 @@ async function resolveSlugFromServer(name) {
97
101
  const data = (await res.json());
98
102
  return data.results;
99
103
  }
100
- async function sendUsagePing(slug, version) {
101
- const registrySlug = slug.startsWith('@') ? slug.slice(1) : slug;
104
+ async function sendUsagePing(teamId, slug, version) {
102
105
  const { createHash } = await import('crypto');
103
106
  const { hostname, userInfo } = await import('os');
104
107
  const deviceHash = createHash('sha256')
105
108
  .update(`${hostname()}:${userInfo().username}`)
106
109
  .digest('hex');
107
- const url = `${config_js_1.API_URL}/api/registry/${registrySlug}/ping`;
108
- const payload = { device_hash: deviceHash };
110
+ const url = `${config_js_1.API_URL}/api/teams/${teamId}/ping`;
111
+ const payload = { device_hash: deviceHash, slug };
109
112
  if (version)
110
113
  payload.installed_version = version;
111
114
  const headers = { 'Content-Type': 'application/json' };
@@ -223,14 +223,65 @@ slug가 직접 주어지면 (\`/relay-install @alice/doc-writer\`) 이 단계를
223
223
  relay deploy-record <slug> --scope <global|local> --files <배치된_파일1> <배치된_파일2> ...
224
224
  \`\`\`
225
225
 
226
- #### 2-5. Requirements 확인 설치
227
- \`<install_path>/relay.yaml\`의 \`requires\` 섹션을 읽고 처리합니다:
228
- - **cli**: \`which <name>\`으로 확인 없으면 install 명령 실행 또는 안내
229
- - **npm**: \`npm list <package>\`로 확인 → 없으면 \`npm install\`
230
- - **env**: 환경변수 확인 → required이면 설정 안내, optional이면 알림
231
- - **mcp**: MCP 서버 설정 에이전트의 MCP 설정에 추가 안내
232
- - **runtime**: Node.js/Python 버전 확인
233
- - **teams**: 의존하는 다른 팀 → \`relay install <@author/team>\`으로 재귀 설치
226
+ #### 2-5. Requirements 체크리스트 (필수 — 항목이 있으면 반드시 수행)
227
+
228
+ \`<install_path>/relay.yaml\`의 \`requires\` 섹션을 읽고, **각 항목을 하나씩 확인하여 체크리스트로 표시**합니다.
229
+ requires 섹션이 없거나 비어있으면 단계를 건너뜁니다.
230
+
231
+ **출력 형식** (반드시 형식으로 사용자에게 보여줍니다):
232
+ \`\`\`
233
+ 📋 Requirements 확인
234
+
235
+ [runtime]
236
+ ✅ Node.js >=18 — v20.11.0 확인됨
237
+ ❌ Python >=3.10 — v3.8.5 (업그레이드 필요)
238
+
239
+ [cli]
240
+ ✅ playwright — 설치됨
241
+ ❌ ffmpeg — 미설치 → 설치 명령: brew install ffmpeg
242
+
243
+ [npm]
244
+ ✅ sharp — 설치됨
245
+ ❌ puppeteer — 미설치 → 설치 중...
246
+
247
+ [env]
248
+ ✅ OPENAI_API_KEY — 설정됨
249
+ ❌ SLACK_WEBHOOK_URL (선택) — 미설정. 알림 전송에 필요
250
+
251
+ [mcp]
252
+ ⚙️ supabase — MCP 서버 설정 필요 (아래 안내 참고)
253
+
254
+ [teams]
255
+ ✅ @alice/doc-writer — 이미 설치됨
256
+ 📦 @bob/utils — 미설치 → 설치 중...
257
+ \`\`\`
258
+
259
+ **처리 규칙 (각 카테고리별):**
260
+
261
+ 1. **runtime**: \`node --version\`, \`python3 --version\`으로 확인. 버전 미달이면 ❌ 표시 후 업그레이드 안내.
262
+ 2. **cli**: \`which <name>\`으로 확인.
263
+ - 설치됨 → ✅
264
+ - 미설치 + \`install\` 필드 있음 → 사용자에게 설치할지 물어본 후 실행
265
+ - 미설치 + \`install\` 필드 없음 → ❌ 표시 후 수동 설치 안내
266
+ 3. **npm**: \`npm list <package> 2>/dev/null\`으로 확인.
267
+ - 설치됨 → ✅
268
+ - 미설치 + required → \`npm install <package>\` 실행
269
+ - 미설치 + optional → ❌ 표시 후 안내만
270
+ 4. **env**: \`echo $<NAME>\`으로 확인.
271
+ - 설정됨 → ✅
272
+ - 미설정 + required → ❌ 표시 후 \`description\`과 함께 설정 방법 안내
273
+ - 미설정 + optional → ⚠️ 표시 후 용도 안내
274
+ 5. **mcp**: MCP 서버 설정이 필요한 경우 ⚙️ 표시 후 설정 방법을 상세히 안내.
275
+ - \`config\` 필드가 있으면 settings.json에 추가할 JSON 블록을 보여줍니다.
276
+ - \`env\` 필드가 있으면 필요한 환경변수도 함께 안내합니다.
277
+ 6. **teams**: \`relay list --json\`으로 설치 여부 확인.
278
+ - 설치됨 → ✅
279
+ - 미설치 → \`relay install <@author/team> --json\` 실행하여 재귀 설치
280
+
281
+ **중요**: 모든 required 항목이 ❌인 경우, 체크리스트 끝에 경고를 표시합니다:
282
+ \`\`\`
283
+ ⚠️ 필수 요구사항이 충족되지 않았습니다. 팀 기능이 제한될 수 있습니다.
284
+ \`\`\`
234
285
  ${LOGIN_JIT_GUIDE}
235
286
 
236
287
  ### Step 3. 완료 & 팔로우 제안
@@ -43,7 +43,10 @@ async function checkTeamVersion(slug, force) {
43
43
  const team = await (0, api_js_1.fetchTeamInfo)(slug);
44
44
  (0, update_cache_js_1.updateCacheTimestamp)(slug);
45
45
  // Fire-and-forget usage ping (only when cache expired = actual API call happened)
46
- (0, api_js_1.sendUsagePing)(slug, entry.version);
46
+ const teamId = entry.team_id ?? team.id;
47
+ if (teamId) {
48
+ (0, api_js_1.sendUsagePing)(teamId, slug, entry.version);
49
+ }
47
50
  if (team.version !== entry.version) {
48
51
  return {
49
52
  type: 'team',
package/dist/types.d.ts CHANGED
@@ -4,6 +4,8 @@ export interface ContactItem {
4
4
  value: string;
5
5
  }
6
6
  export interface InstalledTeam {
7
+ /** team UUID — 설치 카운트/핑 등 서버 통신에 사용 */
8
+ team_id?: string;
7
9
  version: string;
8
10
  installed_at: string;
9
11
  files: string[];
@@ -20,6 +22,8 @@ export interface InstalledRegistry {
20
22
  [scopedSlug: string]: InstalledTeam;
21
23
  }
22
24
  export interface TeamRegistryInfo {
25
+ /** team UUID */
26
+ id: string;
23
27
  /** scoped slug 포맷: "@owner/name" */
24
28
  slug: string;
25
29
  name: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "relayax-cli",
3
- "version": "0.2.30",
3
+ "version": "0.2.35",
4
4
  "description": "RelayAX Agent Team Marketplace CLI - Install and manage agent teams",
5
5
  "main": "dist/index.js",
6
6
  "bin": {