relayax-cli 0.3.65 → 0.3.67
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.
- package/dist/commands/init.d.ts +1 -5
- package/dist/commands/init.js +28 -6
- package/dist/commands/install.js +146 -41
- package/dist/commands/publish.js +103 -74
- package/dist/commands/uninstall.js +20 -4
- package/dist/commands/update.js +67 -31
- package/dist/lib/command-adapter.js +8 -9
- package/dist/lib/guide.d.ts +0 -1
- package/dist/lib/guide.js +27 -77
- package/dist/lib/installer.d.ts +32 -0
- package/dist/lib/installer.js +265 -0
- package/dist/prompts/create.md +108 -0
- package/dist/prompts/explore.md +28 -0
- package/dist/prompts/index.d.ts +2 -7
- package/dist/prompts/index.js +4 -11
- package/dist/types.d.ts +3 -1
- package/package.json +1 -1
package/dist/commands/publish.js
CHANGED
|
@@ -17,7 +17,7 @@ const version_check_js_1 = require("../lib/version-check.js");
|
|
|
17
17
|
const paths_js_1 = require("../lib/paths.js");
|
|
18
18
|
const error_report_js_1 = require("../lib/error-report.js");
|
|
19
19
|
const step_tracker_js_1 = require("../lib/step-tracker.js");
|
|
20
|
-
|
|
20
|
+
// GUIDE_INSTRUCTION removed — share text now uses npx install command directly
|
|
21
21
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
22
22
|
const cliPkg = require('../../package.json');
|
|
23
23
|
const VALID_DIRS = ['skills', 'agents', 'rules', 'commands', 'bin'];
|
|
@@ -49,6 +49,7 @@ function parseRelayYaml(content) {
|
|
|
49
49
|
visibility,
|
|
50
50
|
type,
|
|
51
51
|
source: raw.source ? String(raw.source) : undefined,
|
|
52
|
+
org_slug: raw.org_slug ? String(raw.org_slug) : undefined,
|
|
52
53
|
};
|
|
53
54
|
}
|
|
54
55
|
function detectCommands(agentDir) {
|
|
@@ -289,7 +290,11 @@ function registerPublish(program) {
|
|
|
289
290
|
.description('현재 에이전트 패키지를 Space에 배포합니다 (relay.yaml 필요)')
|
|
290
291
|
.option('--token <token>', '인증 토큰')
|
|
291
292
|
.option('--space <slug>', '배포할 Space 지정')
|
|
293
|
+
.option('--org <slug>', 'Organization slug 지정')
|
|
292
294
|
.option('--version <version>', '배포 버전 지정 (relay.yaml 업데이트)')
|
|
295
|
+
.option('--patch', 'patch 버전 범프')
|
|
296
|
+
.option('--minor', 'minor 버전 범프')
|
|
297
|
+
.option('--major', 'major 버전 범프')
|
|
293
298
|
.option('--project <dir>', '프로젝트 루트 경로 (기본: cwd, 환경변수: RELAY_PROJECT_PATH)')
|
|
294
299
|
.action(async (opts) => {
|
|
295
300
|
const json = program.opts().json ?? false;
|
|
@@ -305,22 +310,6 @@ function registerPublish(program) {
|
|
|
305
310
|
console.error(`\n\x1b[33m⚠ relay v${cliUpdate.latest}이 있습니다\x1b[0m (현재 v${cliUpdate.current})`);
|
|
306
311
|
console.error(' 최신 버전에서는 설치자에게 자동 업데이트 알림이 지원됩니다.');
|
|
307
312
|
console.error(` 업데이트: \x1b[36mnpm update -g relayax-cli\x1b[0m\n`);
|
|
308
|
-
try {
|
|
309
|
-
const { confirm } = await import('@inquirer/prompts');
|
|
310
|
-
const shouldContinue = await confirm({
|
|
311
|
-
message: '현재 버전으로 계속 배포할까요?',
|
|
312
|
-
default: true,
|
|
313
|
-
});
|
|
314
|
-
if (!shouldContinue) {
|
|
315
|
-
(0, error_report_js_1.reportCliError)('publish', 'CANCELLED_CLI_UPDATE', `current:${cliUpdate.current} latest:${cliUpdate.latest}`);
|
|
316
|
-
console.error('\n배포를 취소했습니다. CLI를 업데이트한 후 다시 시도하세요.');
|
|
317
|
-
process.exit(0);
|
|
318
|
-
}
|
|
319
|
-
console.error('');
|
|
320
|
-
}
|
|
321
|
-
catch {
|
|
322
|
-
// non-interactive fallback: continue
|
|
323
|
-
}
|
|
324
313
|
}
|
|
325
314
|
}
|
|
326
315
|
// Check .relay/relay.yaml exists
|
|
@@ -399,13 +388,34 @@ function registerPublish(program) {
|
|
|
399
388
|
}));
|
|
400
389
|
process.exit(1);
|
|
401
390
|
}
|
|
402
|
-
// Version bump: --version flag takes priority
|
|
391
|
+
// Version bump: --version flag takes priority, then --patch/--minor/--major
|
|
392
|
+
const hasBumpFlag = Boolean(opts.patch || opts.minor || opts.major);
|
|
403
393
|
if (opts.version) {
|
|
404
394
|
config.version = opts.version;
|
|
405
395
|
const yamlData = js_yaml_1.default.load(fs_1.default.readFileSync(relayYamlPath, 'utf-8'));
|
|
406
396
|
yamlData.version = opts.version;
|
|
407
397
|
fs_1.default.writeFileSync(relayYamlPath, js_yaml_1.default.dump(yamlData, { lineWidth: 120 }), 'utf-8');
|
|
408
398
|
}
|
|
399
|
+
else if (hasBumpFlag) {
|
|
400
|
+
const [major, minor, patch] = config.version.split('.').map(Number);
|
|
401
|
+
let newVersion;
|
|
402
|
+
if (opts.major) {
|
|
403
|
+
newVersion = `${major + 1}.0.0`;
|
|
404
|
+
}
|
|
405
|
+
else if (opts.minor) {
|
|
406
|
+
newVersion = `${major}.${minor + 1}.0`;
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
newVersion = `${major}.${minor}.${patch + 1}`;
|
|
410
|
+
}
|
|
411
|
+
config.version = newVersion;
|
|
412
|
+
const yamlData = js_yaml_1.default.load(fs_1.default.readFileSync(relayYamlPath, 'utf-8'));
|
|
413
|
+
yamlData.version = newVersion;
|
|
414
|
+
fs_1.default.writeFileSync(relayYamlPath, js_yaml_1.default.dump(yamlData, { lineWidth: 120 }), 'utf-8');
|
|
415
|
+
if (!json) {
|
|
416
|
+
console.error(` → relay.yaml에 version: ${newVersion} 저장됨\n`);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
409
419
|
else if (isTTY) {
|
|
410
420
|
const { select: promptVersion } = await import('@inquirer/prompts');
|
|
411
421
|
const [major, minor, patch] = config.version.split('.').map(Number);
|
|
@@ -462,26 +472,31 @@ function registerPublish(program) {
|
|
|
462
472
|
try {
|
|
463
473
|
const { fetchMyOrgs } = await import('./orgs.js');
|
|
464
474
|
const orgs = await fetchMyOrgs(token);
|
|
465
|
-
//
|
|
466
|
-
|
|
467
|
-
|
|
475
|
+
// Determine explicit org slug: --org > --space (legacy) > relay.yaml org_slug
|
|
476
|
+
const explicitOrgSlug = opts.org ?? opts.space ?? config.org_slug;
|
|
477
|
+
// --org / --space / relay.yaml org_slug: resolve Org by slug
|
|
478
|
+
if (explicitOrgSlug) {
|
|
479
|
+
const matched = orgs.find((o) => o.slug === explicitOrgSlug);
|
|
468
480
|
if (matched) {
|
|
469
481
|
selectedOrgId = matched.id;
|
|
470
482
|
selectedOrgSlug = matched.slug;
|
|
483
|
+
if (!json && (opts.org || config.org_slug)) {
|
|
484
|
+
console.error(`\x1b[2m Organization: ${matched.name} (${matched.slug})\x1b[0m\n`);
|
|
485
|
+
}
|
|
471
486
|
}
|
|
472
487
|
else {
|
|
473
488
|
if (json) {
|
|
474
489
|
console.error(JSON.stringify({
|
|
475
490
|
error: 'INVALID_ORG',
|
|
476
|
-
message: `Organization '${
|
|
491
|
+
message: `Organization '${explicitOrgSlug}'를 찾을 수 없습니다.`,
|
|
477
492
|
fix: `사용 가능한 Org: ${orgs.map((o) => o.slug).join(', ')}`,
|
|
478
493
|
options: orgs.map((o) => ({ value: o.slug, label: `${o.name} (${o.slug})` })),
|
|
479
494
|
}));
|
|
480
495
|
}
|
|
481
496
|
else {
|
|
482
|
-
console.error(`Organization '${
|
|
497
|
+
console.error(`Organization '${explicitOrgSlug}'를 찾을 수 없습니다.`);
|
|
483
498
|
}
|
|
484
|
-
(0, error_report_js_1.reportCliError)('publish', 'INVALID_ORG', `org:${
|
|
499
|
+
(0, error_report_js_1.reportCliError)('publish', 'INVALID_ORG', `org:${explicitOrgSlug}`);
|
|
485
500
|
process.exit(1);
|
|
486
501
|
}
|
|
487
502
|
}
|
|
@@ -537,26 +552,31 @@ function registerPublish(program) {
|
|
|
537
552
|
// Visibility default
|
|
538
553
|
const defaultVisibility = 'public';
|
|
539
554
|
// Visibility validation: must be explicitly set
|
|
555
|
+
// internal은 org가 선택된 경우에만 옵션으로 표시
|
|
556
|
+
const hasOrg = !!selectedOrgId;
|
|
540
557
|
if (!config.visibility) {
|
|
541
558
|
if (isTTY) {
|
|
542
559
|
const { select: promptSelect } = await import('@inquirer/prompts');
|
|
543
560
|
console.error(`\n\x1b[33m⚠ relay.yaml에 visibility가 설정되지 않았습니다.\x1b[0m (기본값: ${defaultVisibility === 'public' ? '공개' : '비공개'})`);
|
|
561
|
+
const visChoices = [
|
|
562
|
+
{
|
|
563
|
+
name: `공개 — 누구나 설치${defaultVisibility === 'public' ? ' ✓ 추천' : ''}`,
|
|
564
|
+
value: 'public',
|
|
565
|
+
},
|
|
566
|
+
{
|
|
567
|
+
name: '링크 공유 — 접근 링크가 있는 사람만 설치',
|
|
568
|
+
value: 'private',
|
|
569
|
+
},
|
|
570
|
+
];
|
|
571
|
+
if (hasOrg) {
|
|
572
|
+
visChoices.push({
|
|
573
|
+
name: '비공개 — Org 멤버만 접근',
|
|
574
|
+
value: 'internal',
|
|
575
|
+
});
|
|
576
|
+
}
|
|
544
577
|
config.visibility = await promptSelect({
|
|
545
578
|
message: '공개 범위를 선택하세요:',
|
|
546
|
-
choices:
|
|
547
|
-
{
|
|
548
|
-
name: `공개 — 누구나 설치${defaultVisibility === 'public' ? ' ✓ 추천' : ''}`,
|
|
549
|
-
value: 'public',
|
|
550
|
-
},
|
|
551
|
-
{
|
|
552
|
-
name: '링크 공유 — 접근 링크가 있는 사람만 설치',
|
|
553
|
-
value: 'private',
|
|
554
|
-
},
|
|
555
|
-
{
|
|
556
|
-
name: `비공개 — Org 멤버만 접근`,
|
|
557
|
-
value: 'internal',
|
|
558
|
-
},
|
|
559
|
-
],
|
|
579
|
+
choices: visChoices,
|
|
560
580
|
default: defaultVisibility,
|
|
561
581
|
});
|
|
562
582
|
// Save back to relay.yaml
|
|
@@ -567,21 +587,25 @@ function registerPublish(program) {
|
|
|
567
587
|
}
|
|
568
588
|
else {
|
|
569
589
|
(0, error_report_js_1.reportCliError)('publish', 'MISSING_VISIBILITY', 'visibility not set in relay.yaml');
|
|
590
|
+
const visOptions = [
|
|
591
|
+
{ value: 'public', label: '공개 — 누구나 설치' },
|
|
592
|
+
{ value: 'private', label: '링크 공유 — 접근 링크가 있는 사람만 설치' },
|
|
593
|
+
];
|
|
594
|
+
if (hasOrg) {
|
|
595
|
+
visOptions.push({ value: 'internal', label: '비공개 — Org 멤버만 접근' });
|
|
596
|
+
}
|
|
570
597
|
console.error(JSON.stringify({
|
|
571
598
|
error: 'MISSING_VISIBILITY',
|
|
572
599
|
message: 'relay.yaml에 visibility를 설정해주세요.',
|
|
573
|
-
options:
|
|
574
|
-
{ value: 'public', label: '공개 — 누구나 설치' },
|
|
575
|
-
{ value: 'private', label: '링크 공유 — 접근 링크가 있는 사람만 설치' },
|
|
576
|
-
{ value: 'internal', label: '비공개 — Org 멤버만 접근' },
|
|
577
|
-
],
|
|
600
|
+
options: visOptions,
|
|
578
601
|
fix: 'relay.yaml의 visibility 필드를 위 옵션 중 하나로 설정하세요.',
|
|
579
602
|
}));
|
|
580
603
|
process.exit(1);
|
|
581
604
|
}
|
|
582
605
|
}
|
|
583
606
|
// Confirm visibility before publish (재배포 시 변경 기회 제공)
|
|
584
|
-
|
|
607
|
+
// Skip when a bump flag is present and visibility is already set in relay.yaml
|
|
608
|
+
if (isTTY && !hasBumpFlag) {
|
|
585
609
|
const { select: promptConfirmVis } = await import('@inquirer/prompts');
|
|
586
610
|
const visLabelMap = {
|
|
587
611
|
public: '공개',
|
|
@@ -589,22 +613,25 @@ function registerPublish(program) {
|
|
|
589
613
|
internal: '비공개',
|
|
590
614
|
};
|
|
591
615
|
const currentVisLabel = visLabelMap[config.visibility ?? 'public'] ?? config.visibility;
|
|
616
|
+
const confirmVisChoices = [
|
|
617
|
+
{
|
|
618
|
+
name: `공개 — 누구나 설치${defaultVisibility === 'public' ? ' ✓ 추천' : ''}`,
|
|
619
|
+
value: 'public',
|
|
620
|
+
},
|
|
621
|
+
{
|
|
622
|
+
name: '링크공유 — 접근 링크가 있는 사람만 설치',
|
|
623
|
+
value: 'private',
|
|
624
|
+
},
|
|
625
|
+
];
|
|
626
|
+
if (hasOrg) {
|
|
627
|
+
confirmVisChoices.push({
|
|
628
|
+
name: '비공개 — Org 멤버만 접근',
|
|
629
|
+
value: 'internal',
|
|
630
|
+
});
|
|
631
|
+
}
|
|
592
632
|
const newVisibility = await promptConfirmVis({
|
|
593
633
|
message: `공개 범위: ${currentVisLabel} — 유지하거나 변경하세요`,
|
|
594
|
-
choices:
|
|
595
|
-
{
|
|
596
|
-
name: `공개 — 누구나 설치${defaultVisibility === 'public' ? ' ✓ 추천' : ''}`,
|
|
597
|
-
value: 'public',
|
|
598
|
-
},
|
|
599
|
-
{
|
|
600
|
-
name: '링크공유 — 접근 링크가 있는 사람만 설치',
|
|
601
|
-
value: 'private',
|
|
602
|
-
},
|
|
603
|
-
{
|
|
604
|
-
name: `비공개 — Org 멤버만 접근`,
|
|
605
|
-
value: 'internal',
|
|
606
|
-
},
|
|
607
|
-
],
|
|
634
|
+
choices: confirmVisChoices,
|
|
608
635
|
default: config.visibility ?? defaultVisibility,
|
|
609
636
|
});
|
|
610
637
|
if (newVisibility !== config.visibility) {
|
|
@@ -701,22 +728,24 @@ function registerPublish(program) {
|
|
|
701
728
|
if (isTTY) {
|
|
702
729
|
const detailSlug = result.slug.startsWith('@') ? result.slug.slice(1) : result.slug;
|
|
703
730
|
const accessCode = result.access_code;
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
console.log(
|
|
717
|
-
console.log(installCmd);
|
|
718
|
-
console.log(
|
|
719
|
-
console.log(
|
|
731
|
+
// Primary: npx 설치 명령어 한 줄
|
|
732
|
+
const visibility = config.visibility ?? 'public';
|
|
733
|
+
let installCmd;
|
|
734
|
+
if (visibility === 'internal' && accessCode) {
|
|
735
|
+
installCmd = `npx relayax-cli install ${result.slug} --join-code ${accessCode}`;
|
|
736
|
+
}
|
|
737
|
+
else if (visibility === 'private' && accessCode) {
|
|
738
|
+
installCmd = `npx relayax-cli install ${result.slug} --code ${accessCode}`;
|
|
739
|
+
}
|
|
740
|
+
else {
|
|
741
|
+
installCmd = `npx relayax-cli install ${result.slug}`;
|
|
742
|
+
}
|
|
743
|
+
console.log(`\n \x1b[90m공유하세요:\x1b[0m`);
|
|
744
|
+
console.log(` ┌${'─'.repeat(installCmd.length + 2)}┐`);
|
|
745
|
+
console.log(` │ ${installCmd} │`);
|
|
746
|
+
console.log(` └${'─'.repeat(installCmd.length + 2)}┘`);
|
|
747
|
+
// Secondary: 에이전트 소개 페이지
|
|
748
|
+
console.log(`\n \x1b[90m에이전트 소개: \x1b[36mhttps://relayax.com/@${detailSlug}\x1b[0m`);
|
|
720
749
|
}
|
|
721
750
|
}
|
|
722
751
|
}
|
|
@@ -67,11 +67,19 @@ function registerUninstall(program) {
|
|
|
67
67
|
if (localEntry) {
|
|
68
68
|
const removed = (0, installer_js_1.uninstallAgent)(localEntry.files);
|
|
69
69
|
totalRemoved += removed.length;
|
|
70
|
-
// Remove deployed
|
|
70
|
+
// Remove deployed symlinks (new)
|
|
71
|
+
if (localEntry.deployed_symlinks && localEntry.deployed_symlinks.length > 0) {
|
|
72
|
+
const symlinkRemoved = (0, installer_js_1.removeSymlinks)(localEntry.deployed_symlinks);
|
|
73
|
+
totalRemoved += symlinkRemoved.length;
|
|
74
|
+
const boundary = inferBoundary(localEntry.deployed_symlinks, (0, paths_js_1.resolveProjectPath)(_opts.project));
|
|
75
|
+
for (const f of symlinkRemoved) {
|
|
76
|
+
(0, installer_js_1.cleanEmptyParents)(f, boundary);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Remove deployed files (legacy)
|
|
71
80
|
if (localEntry.deployed_files && localEntry.deployed_files.length > 0) {
|
|
72
81
|
const deployedRemoved = (0, installer_js_1.uninstallAgent)(localEntry.deployed_files);
|
|
73
82
|
totalRemoved += deployedRemoved.length;
|
|
74
|
-
// Clean empty parent directories
|
|
75
83
|
const boundary = inferBoundary(localEntry.deployed_files, (0, paths_js_1.resolveProjectPath)(_opts.project));
|
|
76
84
|
for (const f of deployedRemoved) {
|
|
77
85
|
(0, installer_js_1.cleanEmptyParents)(f, boundary);
|
|
@@ -87,11 +95,19 @@ function registerUninstall(program) {
|
|
|
87
95
|
const removed = (0, installer_js_1.uninstallAgent)(globalEntry.files);
|
|
88
96
|
totalRemoved += removed.length;
|
|
89
97
|
}
|
|
90
|
-
// Remove
|
|
98
|
+
// Remove deployed symlinks (new)
|
|
99
|
+
if (globalEntry.deployed_symlinks && globalEntry.deployed_symlinks.length > 0) {
|
|
100
|
+
const symlinkRemoved = (0, installer_js_1.removeSymlinks)(globalEntry.deployed_symlinks);
|
|
101
|
+
totalRemoved += symlinkRemoved.length;
|
|
102
|
+
const boundary = inferBoundary(globalEntry.deployed_symlinks, os_1.default.homedir());
|
|
103
|
+
for (const f of symlinkRemoved) {
|
|
104
|
+
(0, installer_js_1.cleanEmptyParents)(f, boundary);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// Remove globally deployed files (legacy)
|
|
91
108
|
if (globalEntry.deployed_files && globalEntry.deployed_files.length > 0) {
|
|
92
109
|
const deployedRemoved = (0, installer_js_1.uninstallAgent)(globalEntry.deployed_files);
|
|
93
110
|
totalRemoved += deployedRemoved.length;
|
|
94
|
-
// Clean empty parent directories
|
|
95
111
|
const boundary = inferBoundary(globalEntry.deployed_files, os_1.default.homedir());
|
|
96
112
|
for (const f of deployedRemoved) {
|
|
97
113
|
(0, installer_js_1.cleanEmptyParents)(f, boundary);
|
package/dist/commands/update.js
CHANGED
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.registerUpdate = registerUpdate;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const os_1 = __importDefault(require("os"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
4
10
|
const api_js_1 = require("../lib/api.js");
|
|
5
11
|
const storage_js_1 = require("../lib/storage.js");
|
|
6
12
|
const installer_js_1 = require("../lib/installer.js");
|
|
7
13
|
const config_js_1 = require("../lib/config.js");
|
|
8
14
|
const slug_js_1 = require("../lib/slug.js");
|
|
9
15
|
const preamble_js_1 = require("../lib/preamble.js");
|
|
16
|
+
const paths_js_1 = require("../lib/paths.js");
|
|
10
17
|
function registerUpdate(program) {
|
|
11
18
|
program
|
|
12
19
|
.command('update <slug>')
|
|
@@ -15,11 +22,12 @@ function registerUpdate(program) {
|
|
|
15
22
|
.option('--code <code>', '초대 코드 (비공개 에이전트 업데이트 시 필요)')
|
|
16
23
|
.action(async (slugInput, opts) => {
|
|
17
24
|
const json = program.opts().json ?? false;
|
|
18
|
-
const installPath = (0, config_js_1.getInstallPath)(opts.path);
|
|
19
25
|
const tempDir = (0, storage_js_1.makeTempDir)();
|
|
26
|
+
const projectPath = (0, paths_js_1.resolveProjectPath)(opts.path);
|
|
20
27
|
try {
|
|
21
|
-
// Resolve scoped slug
|
|
22
|
-
const
|
|
28
|
+
// Resolve scoped slug
|
|
29
|
+
const localInstalled = (0, config_js_1.loadInstalled)();
|
|
30
|
+
const globalInstalled = (0, config_js_1.loadGlobalInstalled)();
|
|
23
31
|
let slug;
|
|
24
32
|
if ((0, slug_js_1.isScopedSlug)(slugInput)) {
|
|
25
33
|
slug = slugInput;
|
|
@@ -28,9 +36,11 @@ function registerUpdate(program) {
|
|
|
28
36
|
const parsed = await (0, slug_js_1.resolveSlug)(slugInput);
|
|
29
37
|
slug = parsed.full;
|
|
30
38
|
}
|
|
31
|
-
//
|
|
32
|
-
const currentEntry =
|
|
39
|
+
// Find current entry (check both registries)
|
|
40
|
+
const currentEntry = localInstalled[slug] ?? globalInstalled[slug];
|
|
33
41
|
const currentVersion = currentEntry?.version ?? null;
|
|
42
|
+
const currentScope = globalInstalled[slug] ? 'global'
|
|
43
|
+
: currentEntry?.deploy_scope ?? 'global';
|
|
34
44
|
// Fetch latest agent metadata
|
|
35
45
|
const agent = await (0, api_js_1.fetchAgentInfo)(slug);
|
|
36
46
|
const latestVersion = agent.version;
|
|
@@ -52,39 +62,62 @@ function registerUpdate(program) {
|
|
|
52
62
|
process.exit(1);
|
|
53
63
|
}
|
|
54
64
|
}
|
|
55
|
-
//
|
|
65
|
+
// Clean up old symlinks (new) and deployed_files (legacy migration)
|
|
66
|
+
if (currentEntry?.deployed_symlinks && currentEntry.deployed_symlinks.length > 0) {
|
|
67
|
+
(0, installer_js_1.removeSymlinks)(currentEntry.deployed_symlinks);
|
|
68
|
+
}
|
|
69
|
+
if (currentEntry?.deployed_files && currentEntry.deployed_files.length > 0) {
|
|
70
|
+
(0, installer_js_1.uninstallAgent)(currentEntry.deployed_files);
|
|
71
|
+
}
|
|
72
|
+
// Determine agent directory
|
|
73
|
+
const parsedSlug = (0, slug_js_1.parseSlug)(slug);
|
|
74
|
+
const owner = parsedSlug?.owner ?? 'unknown';
|
|
75
|
+
const name = parsedSlug?.name ?? slug;
|
|
76
|
+
const agentDir = currentScope === 'global'
|
|
77
|
+
? path_1.default.join(os_1.default.homedir(), '.relay', 'agents', owner, name)
|
|
78
|
+
: path_1.default.join(projectPath, '.relay', 'agents', owner, name);
|
|
79
|
+
// Download & extract
|
|
56
80
|
const tarPath = await (0, storage_js_1.downloadPackage)(agent.package_url, tempDir);
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
(0,
|
|
62
|
-
//
|
|
63
|
-
|
|
64
|
-
//
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
installed[slug] = {
|
|
81
|
+
if (fs_1.default.existsSync(agentDir)) {
|
|
82
|
+
fs_1.default.rmSync(agentDir, { recursive: true, force: true });
|
|
83
|
+
}
|
|
84
|
+
fs_1.default.mkdirSync(agentDir, { recursive: true });
|
|
85
|
+
await (0, storage_js_1.extractPackage)(tarPath, agentDir);
|
|
86
|
+
// Inject preamble
|
|
87
|
+
(0, preamble_js_1.injectPreambleToAgent)(agentDir, slug);
|
|
88
|
+
// Deploy symlinks (always — handles migration from legacy deployed_files)
|
|
89
|
+
const deploy = (0, installer_js_1.deploySymlinks)(agentDir, currentScope, projectPath);
|
|
90
|
+
// Update installed.json
|
|
91
|
+
const installRecord = {
|
|
69
92
|
agent_id: agent.id,
|
|
70
93
|
version: latestVersion,
|
|
71
94
|
installed_at: new Date().toISOString(),
|
|
72
|
-
files,
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
// Clear deployed_files — agent must re-deploy and call deploy-record
|
|
95
|
+
files: [agentDir],
|
|
96
|
+
deploy_scope: currentScope,
|
|
97
|
+
deployed_symlinks: deploy.symlinks,
|
|
76
98
|
};
|
|
77
|
-
(
|
|
78
|
-
|
|
99
|
+
if (currentScope === 'global') {
|
|
100
|
+
globalInstalled[slug] = installRecord;
|
|
101
|
+
(0, config_js_1.saveGlobalInstalled)(globalInstalled);
|
|
102
|
+
// Clean up local entry if migrating
|
|
103
|
+
if (localInstalled[slug]) {
|
|
104
|
+
delete localInstalled[slug];
|
|
105
|
+
(0, config_js_1.saveInstalled)(localInstalled);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
localInstalled[slug] = installRecord;
|
|
110
|
+
(0, config_js_1.saveInstalled)(localInstalled);
|
|
111
|
+
}
|
|
112
|
+
// Report
|
|
79
113
|
await (0, api_js_1.reportInstall)(agent.id, slug, latestVersion);
|
|
80
114
|
const result = {
|
|
81
115
|
status: 'updated',
|
|
82
116
|
slug,
|
|
83
117
|
from_version: currentVersion,
|
|
84
118
|
version: latestVersion,
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
...(hadDeployedFiles ? { needs_redeploy: true, previous_deploy_scope: previousDeployScope } : {}),
|
|
119
|
+
scope: currentScope,
|
|
120
|
+
symlinks: deploy.symlinks.length,
|
|
88
121
|
};
|
|
89
122
|
if (json) {
|
|
90
123
|
console.log(JSON.stringify(result));
|
|
@@ -92,9 +125,9 @@ function registerUpdate(program) {
|
|
|
92
125
|
else {
|
|
93
126
|
const fromLabel = currentVersion ? `v${currentVersion} → ` : '';
|
|
94
127
|
console.log(`\n\x1b[32m✓ ${agent.name} ${fromLabel}v${latestVersion} 업데이트 완료\x1b[0m`);
|
|
95
|
-
console.log(`
|
|
96
|
-
console.log(`
|
|
97
|
-
// Show changelog
|
|
128
|
+
console.log(` 위치: \x1b[36m${agentDir}\x1b[0m`);
|
|
129
|
+
console.log(` symlink: ${deploy.symlinks.length}개`);
|
|
130
|
+
// Show changelog
|
|
98
131
|
try {
|
|
99
132
|
const versions = await (0, api_js_1.fetchAgentVersions)(slug);
|
|
100
133
|
const thisVersion = versions.find((v) => v.version === latestVersion);
|
|
@@ -107,8 +140,11 @@ function registerUpdate(program) {
|
|
|
107
140
|
}
|
|
108
141
|
}
|
|
109
142
|
catch {
|
|
110
|
-
// Non-critical
|
|
143
|
+
// Non-critical
|
|
111
144
|
}
|
|
145
|
+
// Requires check
|
|
146
|
+
const requiresResults = (0, installer_js_1.checkRequires)(agentDir);
|
|
147
|
+
(0, installer_js_1.printRequiresCheck)(requiresResults);
|
|
112
148
|
}
|
|
113
149
|
}
|
|
114
150
|
catch (err) {
|
|
@@ -90,14 +90,13 @@ function getGlobalCommandPathForTool(skillsDir, commandId) {
|
|
|
90
90
|
function formatCommandFile(content) {
|
|
91
91
|
return `---\ndescription: ${content.description}\n---\n\n${content.body}\n`;
|
|
92
92
|
}
|
|
93
|
-
// ───
|
|
94
|
-
// REQUIREMENTS_CHECK, ERROR_HANDLING_GUIDE → import from '../prompts/index.js'
|
|
93
|
+
// ─── 프롬프트는 cli/src/prompts/*.md에서 관리 (SSOT) ───
|
|
95
94
|
// ─── User Commands (글로벌 설치) ───
|
|
96
95
|
exports.USER_COMMANDS = [
|
|
97
96
|
{
|
|
98
|
-
id: 'relay-
|
|
99
|
-
description: 'relay
|
|
100
|
-
body:
|
|
97
|
+
id: 'relay-explore',
|
|
98
|
+
description: 'relay 마켓플레이스를 탐색하고 프로젝트에 맞는 에이전트를 찾습니다',
|
|
99
|
+
body: index_js_1.EXPLORE_PROMPT,
|
|
101
100
|
},
|
|
102
101
|
{
|
|
103
102
|
id: 'relay-status',
|
|
@@ -162,7 +161,7 @@ ${index_js_1.ERROR_HANDLING_GUIDE}
|
|
|
162
161
|
- \`--org <slug>\` 인자가 있으면: \`relay list --org <org-slug> --json\`으로 해당 Organization의 에이전트 목록도 보여줍니다.
|
|
163
162
|
|
|
164
163
|
### 4. 안내
|
|
165
|
-
- 설치된 에이전트가 없으면 \`/relay-
|
|
164
|
+
- 설치된 에이전트가 없으면 \`/relay-explore\`로 에이전트를 탐색해보라고 안내합니다.
|
|
166
165
|
- Org가 있으면 활용법을 안내합니다:
|
|
167
166
|
- Org 에이전트 설치: \`relay install @<org-slug>/<agent>\`
|
|
168
167
|
- Org 관리: www.relayax.com/orgs/<slug>
|
|
@@ -210,9 +209,9 @@ ${index_js_1.ERROR_HANDLING_GUIDE}
|
|
|
210
209
|
→ "✓ @alice/doc-writer 삭제 완료 (12개 파일 제거)"`,
|
|
211
210
|
},
|
|
212
211
|
{
|
|
213
|
-
id: 'relay-
|
|
214
|
-
description: '
|
|
215
|
-
body:
|
|
212
|
+
id: 'relay-create',
|
|
213
|
+
description: '에이전트를 만들거나 업데이트하여 relay에 배포합니다',
|
|
214
|
+
body: index_js_1.CREATE_PROMPT,
|
|
216
215
|
},
|
|
217
216
|
];
|
|
218
217
|
// ─── Builder Commands (로컬 설치) ───
|