relayax-cli 0.1.98 → 0.1.991

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.
@@ -0,0 +1,2 @@
1
+ import { Command } from 'commander';
2
+ export declare function registerCheckUpdate(program: Command): void;
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerCheckUpdate = registerCheckUpdate;
4
+ const version_check_js_1 = require("../lib/version-check.js");
5
+ function registerCheckUpdate(program) {
6
+ program
7
+ .command('check-update [slug]')
8
+ .description('CLI 및 설치된 팀의 업데이트를 확인합니다')
9
+ .option('--quiet', '업데이트가 있을 때만 머신 리더블 출력')
10
+ .option('--force', '캐시를 무시하고 강제 체크')
11
+ .action(async (slug, opts) => {
12
+ const quiet = opts.quiet ?? false;
13
+ const force = opts.force ?? false;
14
+ // CLI version check
15
+ const cliResult = await (0, version_check_js_1.checkCliVersion)(force);
16
+ if (cliResult) {
17
+ if (quiet) {
18
+ console.log(`CLI_UPGRADE_AVAILABLE ${cliResult.current} ${cliResult.latest}`);
19
+ }
20
+ else {
21
+ console.log(`\n\x1b[33m⚠ relay v${cliResult.latest} available\x1b[0m (현재 v${cliResult.current})`);
22
+ console.log(` 실행: npm update -g relayax-cli\n`);
23
+ }
24
+ }
25
+ // Team version check
26
+ if (slug) {
27
+ const teamResult = await (0, version_check_js_1.checkTeamVersion)(slug, force);
28
+ if (teamResult) {
29
+ if (quiet) {
30
+ const byAuthor = teamResult.author ? ` ${teamResult.author}` : '';
31
+ console.log(`TEAM_UPGRADE_AVAILABLE ${slug} ${teamResult.current} ${teamResult.latest}${byAuthor}`);
32
+ }
33
+ else {
34
+ const byAuthor = teamResult.author ? ` \x1b[90m(by @${teamResult.author})\x1b[0m` : '';
35
+ console.log(`\x1b[33m⚠ ${slug} v${teamResult.latest} available\x1b[0m${byAuthor} (현재 v${teamResult.current})`);
36
+ console.log(` 실행: relay update ${slug}`);
37
+ }
38
+ }
39
+ else if (!quiet && !cliResult) {
40
+ console.log('모든 것이 최신 상태입니다.');
41
+ }
42
+ }
43
+ else {
44
+ const teamResults = await (0, version_check_js_1.checkAllTeams)(force);
45
+ for (const result of teamResults) {
46
+ if (quiet) {
47
+ const byAuthor = result.author ? ` ${result.author}` : '';
48
+ console.log(`TEAM_UPGRADE_AVAILABLE ${result.slug} ${result.current} ${result.latest}${byAuthor}`);
49
+ }
50
+ else {
51
+ const byAuthor = result.author ? ` \x1b[90m(by @${result.author})\x1b[0m` : '';
52
+ console.log(`\x1b[33m⚠ ${result.slug} v${result.latest} available\x1b[0m${byAuthor} (현재 v${result.current})`);
53
+ console.log(` 실행: relay update ${result.slug}`);
54
+ }
55
+ }
56
+ if (!quiet && !cliResult && teamResults.length === 0) {
57
+ console.log('모든 것이 최신 상태입니다.');
58
+ }
59
+ }
60
+ });
61
+ }
@@ -9,6 +9,7 @@ const path_1 = __importDefault(require("path"));
9
9
  const api_js_1 = require("../lib/api.js");
10
10
  const storage_js_1 = require("../lib/storage.js");
11
11
  const config_js_1 = require("../lib/config.js");
12
+ const preamble_js_1 = require("../lib/preamble.js");
12
13
  function registerInstall(program) {
13
14
  program
14
15
  .command('install <slug>')
@@ -61,6 +62,8 @@ function registerInstall(program) {
61
62
  return count;
62
63
  }
63
64
  const fileCount = countFiles(teamDir);
65
+ // 5.5. Inject update-check preamble into SKILL.md files
66
+ (0, preamble_js_1.injectPreambleToTeam)(teamDir, slug);
64
67
  // 6. Record in installed.json
65
68
  const installed = (0, config_js_1.loadInstalled)();
66
69
  installed[slug] = {
@@ -84,7 +87,10 @@ function registerInstall(program) {
84
87
  console.log(JSON.stringify(result));
85
88
  }
86
89
  else {
87
- console.log(`\n\x1b[32m✓ ${team.name} 다운로드 완료\x1b[0m v${team.version}`);
90
+ const authorUsername = team.author?.username;
91
+ const authorDisplayName = team.author?.display_name ?? authorUsername ?? '';
92
+ const authorSuffix = authorUsername ? ` \x1b[90mby @${authorUsername}\x1b[0m` : '';
93
+ console.log(`\n\x1b[32m✓ ${team.name} 다운로드 완료\x1b[0m v${team.version}${authorSuffix}`);
88
94
  console.log(` 위치: \x1b[36m${teamDir}\x1b[0m`);
89
95
  console.log(` 파일: ${fileCount}개`);
90
96
  if (team.commands.length > 0) {
@@ -93,6 +99,45 @@ function registerInstall(program) {
93
99
  console.log(` \x1b[33m/${cmd.name}\x1b[0m - ${cmd.description}`);
94
100
  }
95
101
  }
102
+ // Builder info block
103
+ if (team.welcome) {
104
+ console.log(`\n ┌─ 💬 ${authorDisplayName}님의 메시지 ${'─'.repeat(Math.max(0, 38 - authorDisplayName.length))}┐`);
105
+ const lines = team.welcome.match(/.{1,50}/g) ?? [team.welcome];
106
+ for (const line of lines) {
107
+ console.log(` │ "${line}"`);
108
+ }
109
+ console.log(` └${'─'.repeat(44)}┘`);
110
+ }
111
+ const contactLinks = team.author?.contact_links ?? {};
112
+ const contactEntries = Object.entries(contactLinks);
113
+ if (contactEntries.length > 0) {
114
+ const parts = contactEntries.map(([key, val]) => `${key}: ${val}`);
115
+ console.log(`\n 연락처: ${parts.join(' | ')}`);
116
+ }
117
+ if (authorUsername) {
118
+ console.log(`\n \x1b[90m👤 프로필: relayax.com/@${authorUsername}\x1b[0m`);
119
+ }
120
+ if (team.latest_post && authorUsername) {
121
+ console.log(` \x1b[90m📝 활용 팁: relayax.com/@${authorUsername}/posts/${team.latest_post.slug}\x1b[0m`);
122
+ }
123
+ // Follow prompt (only when logged in)
124
+ const token = (0, config_js_1.loadToken)();
125
+ if (authorUsername && token) {
126
+ try {
127
+ const { confirm } = await import('@inquirer/prompts');
128
+ const shouldFollow = await confirm({
129
+ message: `@${authorUsername}을 팔로우하시겠습니까? (이메일로 새 소식을 받습니다)`,
130
+ default: true,
131
+ });
132
+ if (shouldFollow) {
133
+ await (0, api_js_1.followBuilder)(authorUsername);
134
+ console.log(`\x1b[32m ✓ @${authorUsername} 팔로우 완료\x1b[0m`);
135
+ }
136
+ }
137
+ catch {
138
+ // non-critical: skip on error or non-interactive terminal
139
+ }
140
+ }
96
141
  console.log('\n 에이전트가 /relay-install로 환경을 구성합니다.');
97
142
  }
98
143
  }
@@ -10,6 +10,9 @@ const os_1 = __importDefault(require("os"));
10
10
  const js_yaml_1 = __importDefault(require("js-yaml"));
11
11
  const tar_1 = require("tar");
12
12
  const config_js_1 = require("../lib/config.js");
13
+ const version_check_js_1 = require("../lib/version-check.js");
14
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
15
+ const cliPkg = require('../../package.json');
13
16
  const VALID_DIRS = ['skills', 'agents', 'rules', 'commands'];
14
17
  const IMAGE_EXTS = ['.png', '.jpg', '.jpeg', '.webp'];
15
18
  /** 개별 포트폴리오 이미지 최대 크기 (2 MB) */
@@ -263,6 +266,30 @@ function registerPublish(program) {
263
266
  const relayDir = path_1.default.join(teamDir, '.relay');
264
267
  const relayYamlPath = path_1.default.join(relayDir, 'relay.yaml');
265
268
  const isTTY = Boolean(process.stdin.isTTY) && !json;
269
+ // CLI update check before publish
270
+ if (isTTY) {
271
+ const cliUpdate = await (0, version_check_js_1.checkCliVersion)(true);
272
+ if (cliUpdate) {
273
+ console.error(`\n\x1b[33m⚠ relay v${cliUpdate.latest}이 있습니다\x1b[0m (현재 v${cliUpdate.current})`);
274
+ console.error(' 최신 버전에서는 설치자에게 자동 업데이트 알림이 지원됩니다.');
275
+ console.error(` 업데이트: \x1b[36mnpm update -g relayax-cli\x1b[0m\n`);
276
+ try {
277
+ const { confirm } = await import('@inquirer/prompts');
278
+ const shouldContinue = await confirm({
279
+ message: '현재 버전으로 계속 배포할까요?',
280
+ default: true,
281
+ });
282
+ if (!shouldContinue) {
283
+ console.error('\n배포를 취소했습니다. CLI를 업데이트한 후 다시 시도하세요.');
284
+ process.exit(0);
285
+ }
286
+ console.error('');
287
+ }
288
+ catch {
289
+ // non-interactive fallback: continue
290
+ }
291
+ }
292
+ }
266
293
  // Check .relay/relay.yaml exists
267
294
  if (!fs_1.default.existsSync(relayYamlPath)) {
268
295
  if (!isTTY) {
@@ -302,9 +329,9 @@ function registerPublish(program) {
302
329
  { name: '초대 코드 필요', value: 'invite-only' },
303
330
  ],
304
331
  });
305
- console.error('\n\x1b[2m💡 프로필에 연락처를 설정하면 설치 시 명함이 전달됩니다: www.relayax.com/dashboard/edit\x1b[0m');
332
+ console.error('\n\x1b[2m💡 프로필에 연락처를 설정하면 설치 시 명함이 전달됩니다: www.relayax.com/dashboard/profile\x1b[0m');
306
333
  if (visibility === 'invite-only') {
307
- console.error('\x1b[2m💡 invite-only 팀은 웹 대시보드에서 사용자를 초대하세요: www.relayax.com/dashboard\x1b[0m');
334
+ console.error('\x1b[2m💡 invite-only 팀은 웹 대시보드에서 사용자를 초대하세요: www.relayax.com/dashboard/teams\x1b[0m');
308
335
  }
309
336
  console.error('');
310
337
  const tags = tagsRaw
@@ -335,7 +362,7 @@ function registerPublish(program) {
335
362
  }
336
363
  // Profile hint
337
364
  if (isTTY) {
338
- console.error('💡 프로필에 연락처를 설정하면 설치 시 명함이 전달됩니다: www.relayax.com/dashboard/edit');
365
+ console.error('💡 프로필에 연락처를 설정하면 설치 시 명함이 전달됩니다: www.relayax.com/dashboard/profile');
339
366
  }
340
367
  // Validate structure (콘텐츠는 .relay/ 안에 있음)
341
368
  const hasDirs = VALID_DIRS.some((d) => {
@@ -381,6 +408,7 @@ function registerPublish(program) {
381
408
  changelog: config.changelog,
382
409
  requires: config.requires,
383
410
  visibility: config.visibility,
411
+ cli_version: cliPkg.version,
384
412
  };
385
413
  if (!json) {
386
414
  console.error(`패키지 생성 중... (${config.name} v${config.version})`);
@@ -5,6 +5,7 @@ const api_js_1 = require("../lib/api.js");
5
5
  const storage_js_1 = require("../lib/storage.js");
6
6
  const installer_js_1 = require("../lib/installer.js");
7
7
  const config_js_1 = require("../lib/config.js");
8
+ const preamble_js_1 = require("../lib/preamble.js");
8
9
  function registerUpdate(program) {
9
10
  program
10
11
  .command('update <slug>')
@@ -54,6 +55,8 @@ function registerUpdate(program) {
54
55
  await (0, storage_js_1.extractPackage)(tarPath, extractDir);
55
56
  // Copy files to install_path
56
57
  const files = (0, installer_js_1.installTeam)(extractDir, installPath);
58
+ // Inject update-check preamble into SKILL.md files
59
+ (0, preamble_js_1.injectPreambleToTeam)(installPath, slug);
57
60
  // Update installed.json with new version
58
61
  installed[slug] = {
59
62
  version: latestVersion,
package/dist/index.js CHANGED
@@ -13,6 +13,7 @@ const publish_js_1 = require("./commands/publish.js");
13
13
  const login_js_1 = require("./commands/login.js");
14
14
  const update_js_1 = require("./commands/update.js");
15
15
  const outdated_js_1 = require("./commands/outdated.js");
16
+ const check_update_js_1 = require("./commands/check-update.js");
16
17
  // eslint-disable-next-line @typescript-eslint/no-var-requires
17
18
  const pkg = require('../package.json');
18
19
  const program = new commander_1.Command();
@@ -32,4 +33,5 @@ program
32
33
  (0, login_js_1.registerLogin)(program);
33
34
  (0, update_js_1.registerUpdate)(program);
34
35
  (0, outdated_js_1.registerOutdated)(program);
36
+ (0, check_update_js_1.registerCheckUpdate)(program);
35
37
  program.parse();
package/dist/lib/api.d.ts CHANGED
@@ -8,3 +8,4 @@ export interface TeamVersionInfo {
8
8
  }
9
9
  export declare function fetchTeamVersions(slug: string): Promise<TeamVersionInfo[]>;
10
10
  export declare function reportInstall(slug: string): Promise<void>;
11
+ export declare function followBuilder(username: string): Promise<void>;
package/dist/lib/api.js CHANGED
@@ -4,6 +4,7 @@ exports.fetchTeamInfo = fetchTeamInfo;
4
4
  exports.searchTeams = searchTeams;
5
5
  exports.fetchTeamVersions = fetchTeamVersions;
6
6
  exports.reportInstall = reportInstall;
7
+ exports.followBuilder = followBuilder;
7
8
  const config_js_1 = require("./config.js");
8
9
  async function fetchTeamInfo(slug) {
9
10
  const url = `${config_js_1.API_URL}/api/registry/${slug}`;
@@ -42,3 +43,21 @@ async function reportInstall(slug) {
42
43
  // non-critical: ignore errors
43
44
  });
44
45
  }
46
+ async function followBuilder(username) {
47
+ const token = (0, config_js_1.loadToken)();
48
+ const headers = {
49
+ 'Content-Type': 'application/json',
50
+ };
51
+ if (token) {
52
+ headers['Authorization'] = `Bearer ${token}`;
53
+ }
54
+ const res = await fetch(`https://www.relayax.com/api/follows`, {
55
+ method: 'POST',
56
+ headers,
57
+ body: JSON.stringify({ following_username: username, email_opt_in: true }),
58
+ });
59
+ if (!res.ok) {
60
+ const body = await res.text();
61
+ throw new Error(`팔로우 실패 (${res.status}): ${body}`);
62
+ }
63
+ }
@@ -300,13 +300,30 @@ requires:
300
300
  - .relay/portfolio/cover.png에 저장합니다.
301
301
  - 사용자에게 "이 cover를 사용할까요?" 확인. 직접 제공도 가능.
302
302
 
303
- #### 슬롯 2: demo (선택 — 동작 시연)
304
- - 유형에 따라 제안 여부를 판단합니다:
305
- - 브라우저 자동화 키워드 감지 (playwright, puppeteer, crawl, scrape, browser) → GIF 데모 제안
306
- - 그 외 → 건너뜀 (사용자가 원하면 수동 추가)
307
- - GIF: 최대 5MB, .relay/portfolio/demo.gif에 저장
308
- - 또는 외부 영상 URL (YouTube, Loom 등)을 relay.yaml에 기록
309
- - 사용자에게 "데모를 녹화할까요?" / "영상 URL이 있나요?" 확인
303
+ #### 슬롯 2: demo (자동 생성 — 동작 시연)
304
+ 에이전트가 **직접 팀의 커맨드를 실행하여** demo를 자동 생성합니다. 사용자에게 묻기 전에 먼저 만듭니다.
305
+
306
+ **생성 절차:**
307
+ 1. .relay/commands/ 의 커맨드 목록을 읽고, 가장 대표적인 커맨드를 선택합니다.
308
+ 2. 해당 커맨드를 예시 주제/데이터로 실행합니다.
309
+ - 콘텐츠 팀: "라즈베리파이5 신제품 소개" 같은 예시 주제로 커맨드 실행
310
+ - 크롤링 팀: 예시 URL로 실행하며 브라우저 동작을 녹화
311
+ - 분석 팀: 샘플 데이터로 실행하여 결과 캡처
312
+ 3. 실행 결과물로 demo를 생성합니다:
313
+ - 여러 장 이미지 (카드뉴스 등): 슬라이드쇼 GIF (각 장 2초 간격)
314
+ - 브라우저 동작: 동작 녹화 GIF
315
+ - 단일 결과물: 결과 이미지를 demo로 사용
316
+ 4. 생성된 demo를 사용자에게 보여줍니다: "이 demo를 사용할까요?"
317
+ - 수정 요청 가능: "다른 주제로 다시 만들어줘", "좀 더 짧게"
318
+ - 확정 시 .relay/portfolio/demo.gif에 저장
319
+
320
+ **예시 주제 선택 기준:**
321
+ - relay.yaml의 description, tags에서 도메인 힌트 추출
322
+ - 기존 output/에 결과물이 있으면 같은 주제 활용
323
+ - 없으면 팀 설명에서 가장 그럴듯한 예시 주제를 자체 생성
324
+
325
+ **GIF 규격:** 최대 5MB, .relay/portfolio/demo.gif
326
+ **대안:** 외부 영상 URL (YouTube, Loom)도 가능 — relay.yaml에 기록
310
327
 
311
328
  #### 슬롯 3: gallery (선택 — 결과물 쇼케이스, 최대 5장)
312
329
  - 규격: 800x600px 이하, WebP, 각 500KB 이하
@@ -0,0 +1 @@
1
+ export declare function getDeviceHash(): string;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getDeviceHash = getDeviceHash;
7
+ const crypto_1 = require("crypto");
8
+ const os_1 = __importDefault(require("os"));
9
+ let cachedHash = null;
10
+ function getDeviceHash() {
11
+ if (cachedHash)
12
+ return cachedHash;
13
+ const raw = `${os_1.default.hostname()}:${os_1.default.userInfo().username}`;
14
+ cachedHash = (0, crypto_1.createHash)('sha256').update(raw).digest('hex');
15
+ return cachedHash;
16
+ }
@@ -0,0 +1,3 @@
1
+ export declare function generatePreamble(slug: string): string;
2
+ export declare function injectPreamble(filePath: string, slug: string): void;
3
+ export declare function injectPreambleToTeam(teamDir: string, slug: string): number;
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.generatePreamble = generatePreamble;
7
+ exports.injectPreamble = injectPreamble;
8
+ exports.injectPreambleToTeam = injectPreambleToTeam;
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const path_1 = __importDefault(require("path"));
11
+ const PREAMBLE_START = '<!-- RELAY_PREAMBLE_START - DO NOT EDIT -->';
12
+ const PREAMBLE_END = '<!-- RELAY_PREAMBLE_END -->';
13
+ function generatePreamble(slug) {
14
+ return `${PREAMBLE_START}
15
+ ## Preamble (auto-generated by relay)
16
+
17
+ \`\`\`bash
18
+ relay check-update ${slug} --quiet 2>/dev/null
19
+ \`\`\`
20
+
21
+ If output shows \`CLI_UPGRADE_AVAILABLE <old> <new>\`: tell user "relay v{new} available. Run: npm update -g relayax-cli"
22
+ If output shows \`TEAM_UPGRADE_AVAILABLE\`: tell user "${slug} v{new} available (by @{author}). Run: relay update ${slug}"
23
+ ${PREAMBLE_END}`;
24
+ }
25
+ function injectPreamble(filePath, slug) {
26
+ const content = fs_1.default.readFileSync(filePath, 'utf-8');
27
+ const preamble = generatePreamble(slug);
28
+ // Replace existing preamble or prepend
29
+ const startIdx = content.indexOf(PREAMBLE_START);
30
+ const endIdx = content.indexOf(PREAMBLE_END);
31
+ let newContent;
32
+ if (startIdx !== -1 && endIdx !== -1) {
33
+ // Replace existing
34
+ newContent =
35
+ content.slice(0, startIdx) +
36
+ preamble +
37
+ content.slice(endIdx + PREAMBLE_END.length);
38
+ }
39
+ else {
40
+ // Prepend
41
+ newContent = preamble + '\n\n' + content;
42
+ }
43
+ fs_1.default.writeFileSync(filePath, newContent);
44
+ }
45
+ function injectPreambleToTeam(teamDir, slug) {
46
+ let count = 0;
47
+ function walk(dir) {
48
+ if (!fs_1.default.existsSync(dir))
49
+ return;
50
+ for (const entry of fs_1.default.readdirSync(dir, { withFileTypes: true })) {
51
+ const fullPath = path_1.default.join(dir, entry.name);
52
+ if (entry.isDirectory()) {
53
+ walk(fullPath);
54
+ }
55
+ else if (entry.name === 'SKILL.md') {
56
+ injectPreamble(fullPath, slug);
57
+ count++;
58
+ }
59
+ }
60
+ }
61
+ walk(teamDir);
62
+ return count;
63
+ }
@@ -0,0 +1,2 @@
1
+ export declare function isCacheValid(key: 'cli' | string, force?: boolean): boolean;
2
+ export declare function updateCacheTimestamp(key: 'cli' | string): void;
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.isCacheValid = isCacheValid;
7
+ exports.updateCacheTimestamp = updateCacheTimestamp;
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const os_1 = __importDefault(require("os"));
11
+ const RELAY_DIR = path_1.default.join(os_1.default.homedir(), '.relay');
12
+ const CACHE_FILE = path_1.default.join(RELAY_DIR, 'last-update-check');
13
+ const CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
14
+ function loadCache() {
15
+ try {
16
+ if (!fs_1.default.existsSync(CACHE_FILE))
17
+ return {};
18
+ return JSON.parse(fs_1.default.readFileSync(CACHE_FILE, 'utf-8'));
19
+ }
20
+ catch {
21
+ return {};
22
+ }
23
+ }
24
+ function saveCache(data) {
25
+ if (!fs_1.default.existsSync(RELAY_DIR)) {
26
+ fs_1.default.mkdirSync(RELAY_DIR, { recursive: true });
27
+ }
28
+ fs_1.default.writeFileSync(CACHE_FILE, JSON.stringify(data, null, 2));
29
+ }
30
+ function isCacheValid(key, force) {
31
+ if (force)
32
+ return false;
33
+ const cache = loadCache();
34
+ const timestamp = key === 'cli' ? cache.cli : cache.teams?.[key];
35
+ if (!timestamp)
36
+ return false;
37
+ return Date.now() - new Date(timestamp).getTime() < CACHE_TTL_MS;
38
+ }
39
+ function updateCacheTimestamp(key) {
40
+ const cache = loadCache();
41
+ const now = new Date().toISOString();
42
+ if (key === 'cli') {
43
+ cache.cli = now;
44
+ }
45
+ else {
46
+ if (!cache.teams)
47
+ cache.teams = {};
48
+ cache.teams[key] = now;
49
+ }
50
+ saveCache(cache);
51
+ }
@@ -0,0 +1,10 @@
1
+ export interface UpdateResult {
2
+ type: 'cli' | 'team';
3
+ slug?: string;
4
+ current: string;
5
+ latest: string;
6
+ author?: string;
7
+ }
8
+ export declare function checkCliVersion(force?: boolean): Promise<UpdateResult | null>;
9
+ export declare function checkTeamVersion(slug: string, force?: boolean): Promise<UpdateResult | null>;
10
+ export declare function checkAllTeams(force?: boolean): Promise<UpdateResult[]>;
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.checkCliVersion = checkCliVersion;
4
+ exports.checkTeamVersion = checkTeamVersion;
5
+ exports.checkAllTeams = checkAllTeams;
6
+ const config_js_1 = require("./config.js");
7
+ const api_js_1 = require("./api.js");
8
+ const update_cache_js_1 = require("./update-cache.js");
9
+ const device_hash_js_1 = require("./device-hash.js");
10
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
11
+ const pkg = require('../../package.json');
12
+ async function checkCliVersion(force) {
13
+ if ((0, update_cache_js_1.isCacheValid)('cli', force))
14
+ return null;
15
+ try {
16
+ const res = await fetch('https://registry.npmjs.org/relayax-cli/latest', {
17
+ signal: AbortSignal.timeout(3000),
18
+ });
19
+ if (!res.ok)
20
+ return null;
21
+ const data = (await res.json());
22
+ (0, update_cache_js_1.updateCacheTimestamp)('cli');
23
+ if (data.version !== pkg.version) {
24
+ return { type: 'cli', current: pkg.version, latest: data.version };
25
+ }
26
+ }
27
+ catch {
28
+ // network error — silently skip
29
+ }
30
+ return null;
31
+ }
32
+ async function checkTeamVersion(slug, force) {
33
+ if ((0, update_cache_js_1.isCacheValid)(slug, force))
34
+ return null;
35
+ try {
36
+ const installed = (0, config_js_1.loadInstalled)();
37
+ const entry = installed[slug];
38
+ if (!entry?.version)
39
+ return null;
40
+ const team = await (0, api_js_1.fetchTeamInfo)(slug);
41
+ (0, update_cache_js_1.updateCacheTimestamp)(slug);
42
+ // Send usage ping (fire-and-forget)
43
+ fetch(`${config_js_1.API_URL}/api/registry/${slug}/ping`, {
44
+ method: 'POST',
45
+ headers: { 'Content-Type': 'application/json' },
46
+ body: JSON.stringify({ device_hash: (0, device_hash_js_1.getDeviceHash)() }),
47
+ signal: AbortSignal.timeout(3000),
48
+ }).catch(() => { });
49
+ if (team.version !== entry.version) {
50
+ return {
51
+ type: 'team',
52
+ slug,
53
+ current: entry.version,
54
+ latest: team.version,
55
+ author: team.author?.username,
56
+ };
57
+ }
58
+ }
59
+ catch {
60
+ // network error — silently skip
61
+ }
62
+ return null;
63
+ }
64
+ async function checkAllTeams(force) {
65
+ const installed = (0, config_js_1.loadInstalled)();
66
+ const slugs = Object.keys(installed);
67
+ const results = [];
68
+ for (const slug of slugs) {
69
+ const result = await checkTeamVersion(slug, force);
70
+ if (result)
71
+ results.push(result);
72
+ }
73
+ return results;
74
+ }
package/dist/types.d.ts CHANGED
@@ -9,6 +9,7 @@ export interface InstalledRegistry {
9
9
  export interface TeamRegistryInfo {
10
10
  slug: string;
11
11
  name: string;
12
+ description?: string;
12
13
  version: string;
13
14
  package_url: string;
14
15
  commands: {
@@ -20,7 +21,21 @@ export interface TeamRegistryInfo {
20
21
  rules: number;
21
22
  skills: number;
22
23
  };
24
+ tags?: string[];
25
+ install_count?: number;
26
+ requires?: Record<string, unknown>;
23
27
  visibility?: "public" | "login-only" | "invite-only";
28
+ welcome?: string | null;
29
+ contact?: Record<string, string> | null;
30
+ author?: {
31
+ username: string;
32
+ display_name: string | null;
33
+ contact_links: Record<string, string>;
34
+ } | null;
35
+ latest_post?: {
36
+ title: string;
37
+ slug: string;
38
+ } | null;
24
39
  }
25
40
  export interface SearchResult {
26
41
  slug: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "relayax-cli",
3
- "version": "0.1.98",
3
+ "version": "0.1.991",
4
4
  "description": "RelayAX Agent Team Marketplace CLI - Install and manage agent teams",
5
5
  "main": "dist/index.js",
6
6
  "bin": {