relayax-cli 0.1.99 → 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] = {
@@ -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();
@@ -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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "relayax-cli",
3
- "version": "0.1.99",
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": {