relayax-cli 0.3.66 → 0.4.12
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/README.md +156 -24
- package/dist/commands/create.js +13 -1
- package/dist/commands/diff.js +38 -18
- package/dist/commands/export.d.ts +2 -0
- package/dist/commands/export.js +106 -0
- package/dist/commands/init.d.ts +1 -5
- package/dist/commands/init.js +28 -6
- package/dist/commands/install.js +31 -23
- package/dist/commands/publish.js +121 -33
- package/dist/commands/update.js +24 -6
- package/dist/index.js +2 -0
- package/dist/lib/command-adapter.js +1 -1
- package/dist/lib/git-operations.d.ts +38 -0
- package/dist/lib/git-operations.js +168 -0
- package/dist/lib/guide.js +9 -0
- package/dist/lib/manifest-generator.d.ts +20 -0
- package/dist/lib/manifest-generator.js +144 -0
- package/dist/lib/storage.d.ts +5 -0
- package/dist/lib/storage.js +9 -0
- package/dist/mcp/server.js +26 -7
- package/dist/prompts/create.md +60 -14
- package/dist/types.d.ts +3 -0
- package/package.json +1 -1
package/dist/commands/publish.js
CHANGED
|
@@ -17,6 +17,8 @@ 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
|
+
const git_operations_js_1 = require("../lib/git-operations.js");
|
|
21
|
+
const manifest_generator_js_1 = require("../lib/manifest-generator.js");
|
|
20
22
|
// GUIDE_INSTRUCTION removed — share text now uses npx install command directly
|
|
21
23
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
22
24
|
const cliPkg = require('../../package.json');
|
|
@@ -50,6 +52,7 @@ function parseRelayYaml(content) {
|
|
|
50
52
|
type,
|
|
51
53
|
source: raw.source ? String(raw.source) : undefined,
|
|
52
54
|
org_slug: raw.org_slug ? String(raw.org_slug) : undefined,
|
|
55
|
+
platforms: Array.isArray(raw.platforms) ? raw.platforms.map((p) => String(p)) : undefined,
|
|
53
56
|
};
|
|
54
57
|
}
|
|
55
58
|
function detectCommands(agentDir) {
|
|
@@ -552,26 +555,31 @@ function registerPublish(program) {
|
|
|
552
555
|
// Visibility default
|
|
553
556
|
const defaultVisibility = 'public';
|
|
554
557
|
// Visibility validation: must be explicitly set
|
|
558
|
+
// internal은 org가 선택된 경우에만 옵션으로 표시
|
|
559
|
+
const hasOrg = !!selectedOrgId;
|
|
555
560
|
if (!config.visibility) {
|
|
556
561
|
if (isTTY) {
|
|
557
562
|
const { select: promptSelect } = await import('@inquirer/prompts');
|
|
558
563
|
console.error(`\n\x1b[33m⚠ relay.yaml에 visibility가 설정되지 않았습니다.\x1b[0m (기본값: ${defaultVisibility === 'public' ? '공개' : '비공개'})`);
|
|
564
|
+
const visChoices = [
|
|
565
|
+
{
|
|
566
|
+
name: `공개 — 누구나 설치${defaultVisibility === 'public' ? ' ✓ 추천' : ''}`,
|
|
567
|
+
value: 'public',
|
|
568
|
+
},
|
|
569
|
+
{
|
|
570
|
+
name: '링크 공유 — 접근 링크가 있는 사람만 설치',
|
|
571
|
+
value: 'private',
|
|
572
|
+
},
|
|
573
|
+
];
|
|
574
|
+
if (hasOrg) {
|
|
575
|
+
visChoices.push({
|
|
576
|
+
name: '비공개 — Org 멤버만 접근',
|
|
577
|
+
value: 'internal',
|
|
578
|
+
});
|
|
579
|
+
}
|
|
559
580
|
config.visibility = await promptSelect({
|
|
560
581
|
message: '공개 범위를 선택하세요:',
|
|
561
|
-
choices:
|
|
562
|
-
{
|
|
563
|
-
name: `공개 — 누구나 설치${defaultVisibility === 'public' ? ' ✓ 추천' : ''}`,
|
|
564
|
-
value: 'public',
|
|
565
|
-
},
|
|
566
|
-
{
|
|
567
|
-
name: '링크 공유 — 접근 링크가 있는 사람만 설치',
|
|
568
|
-
value: 'private',
|
|
569
|
-
},
|
|
570
|
-
{
|
|
571
|
-
name: `비공개 — Org 멤버만 접근`,
|
|
572
|
-
value: 'internal',
|
|
573
|
-
},
|
|
574
|
-
],
|
|
582
|
+
choices: visChoices,
|
|
575
583
|
default: defaultVisibility,
|
|
576
584
|
});
|
|
577
585
|
// Save back to relay.yaml
|
|
@@ -582,14 +590,17 @@ function registerPublish(program) {
|
|
|
582
590
|
}
|
|
583
591
|
else {
|
|
584
592
|
(0, error_report_js_1.reportCliError)('publish', 'MISSING_VISIBILITY', 'visibility not set in relay.yaml');
|
|
593
|
+
const visOptions = [
|
|
594
|
+
{ value: 'public', label: '공개 — 누구나 설치' },
|
|
595
|
+
{ value: 'private', label: '링크 공유 — 접근 링크가 있는 사람만 설치' },
|
|
596
|
+
];
|
|
597
|
+
if (hasOrg) {
|
|
598
|
+
visOptions.push({ value: 'internal', label: '비공개 — Org 멤버만 접근' });
|
|
599
|
+
}
|
|
585
600
|
console.error(JSON.stringify({
|
|
586
601
|
error: 'MISSING_VISIBILITY',
|
|
587
602
|
message: 'relay.yaml에 visibility를 설정해주세요.',
|
|
588
|
-
options:
|
|
589
|
-
{ value: 'public', label: '공개 — 누구나 설치' },
|
|
590
|
-
{ value: 'private', label: '링크 공유 — 접근 링크가 있는 사람만 설치' },
|
|
591
|
-
{ value: 'internal', label: '비공개 — Org 멤버만 접근' },
|
|
592
|
-
],
|
|
603
|
+
options: visOptions,
|
|
593
604
|
fix: 'relay.yaml의 visibility 필드를 위 옵션 중 하나로 설정하세요.',
|
|
594
605
|
}));
|
|
595
606
|
process.exit(1);
|
|
@@ -605,22 +616,25 @@ function registerPublish(program) {
|
|
|
605
616
|
internal: '비공개',
|
|
606
617
|
};
|
|
607
618
|
const currentVisLabel = visLabelMap[config.visibility ?? 'public'] ?? config.visibility;
|
|
619
|
+
const confirmVisChoices = [
|
|
620
|
+
{
|
|
621
|
+
name: `공개 — 누구나 설치${defaultVisibility === 'public' ? ' ✓ 추천' : ''}`,
|
|
622
|
+
value: 'public',
|
|
623
|
+
},
|
|
624
|
+
{
|
|
625
|
+
name: '링크공유 — 접근 링크가 있는 사람만 설치',
|
|
626
|
+
value: 'private',
|
|
627
|
+
},
|
|
628
|
+
];
|
|
629
|
+
if (hasOrg) {
|
|
630
|
+
confirmVisChoices.push({
|
|
631
|
+
name: '비공개 — Org 멤버만 접근',
|
|
632
|
+
value: 'internal',
|
|
633
|
+
});
|
|
634
|
+
}
|
|
608
635
|
const newVisibility = await promptConfirmVis({
|
|
609
636
|
message: `공개 범위: ${currentVisLabel} — 유지하거나 변경하세요`,
|
|
610
|
-
choices:
|
|
611
|
-
{
|
|
612
|
-
name: `공개 — 누구나 설치${defaultVisibility === 'public' ? ' ✓ 추천' : ''}`,
|
|
613
|
-
value: 'public',
|
|
614
|
-
},
|
|
615
|
-
{
|
|
616
|
-
name: '링크공유 — 접근 링크가 있는 사람만 설치',
|
|
617
|
-
value: 'private',
|
|
618
|
-
},
|
|
619
|
-
{
|
|
620
|
-
name: `비공개 — Org 멤버만 접근`,
|
|
621
|
-
value: 'internal',
|
|
622
|
-
},
|
|
623
|
-
],
|
|
637
|
+
choices: confirmVisChoices,
|
|
624
638
|
default: config.visibility ?? defaultVisibility,
|
|
625
639
|
});
|
|
626
640
|
if (newVisibility !== config.visibility) {
|
|
@@ -685,6 +699,46 @@ function registerPublish(program) {
|
|
|
685
699
|
const entrySlug = config.slug.startsWith('@') ? config.slug.slice(1) : config.slug;
|
|
686
700
|
const entryFileName = entrySlug.replace('/', '-') + '.md';
|
|
687
701
|
fs_1.default.writeFileSync(path_1.default.join(commandsDir, entryFileName), entryContent);
|
|
702
|
+
// Check git is available
|
|
703
|
+
try {
|
|
704
|
+
(0, git_operations_js_1.checkGitInstalled)();
|
|
705
|
+
}
|
|
706
|
+
catch (gitErr) {
|
|
707
|
+
const gitMsg = gitErr instanceof Error ? gitErr.message : String(gitErr);
|
|
708
|
+
(0, error_report_js_1.reportCliError)('publish', 'GIT_NOT_FOUND', gitMsg);
|
|
709
|
+
if (json) {
|
|
710
|
+
console.error(JSON.stringify({ error: 'GIT_NOT_FOUND', message: gitMsg }));
|
|
711
|
+
}
|
|
712
|
+
else {
|
|
713
|
+
console.error(`\x1b[31m${gitMsg}\x1b[0m`);
|
|
714
|
+
}
|
|
715
|
+
process.exit(1);
|
|
716
|
+
}
|
|
717
|
+
// Generate platform manifests (after preamble/command, before git commit)
|
|
718
|
+
const manifestYaml = {
|
|
719
|
+
name: config.name,
|
|
720
|
+
slug: config.slug,
|
|
721
|
+
description: config.description,
|
|
722
|
+
version: config.version,
|
|
723
|
+
source: config.source,
|
|
724
|
+
org_slug: config.org_slug ?? selectedOrgSlug,
|
|
725
|
+
platforms: config.platforms,
|
|
726
|
+
};
|
|
727
|
+
const manifestFiles = (0, manifest_generator_js_1.generateManifests)(manifestYaml, relayDir);
|
|
728
|
+
for (const mf of manifestFiles) {
|
|
729
|
+
const mfPath = path_1.default.join(relayDir, mf.relativePath);
|
|
730
|
+
fs_1.default.mkdirSync(path_1.default.dirname(mfPath), { recursive: true });
|
|
731
|
+
fs_1.default.writeFileSync(mfPath, mf.content);
|
|
732
|
+
}
|
|
733
|
+
const generatedPlatforms = [...new Set(manifestFiles.map((f) => {
|
|
734
|
+
if (f.relativePath.startsWith('.claude-plugin/') || f.relativePath === 'marketplace.json')
|
|
735
|
+
return 'claude-code';
|
|
736
|
+
if (f.relativePath.startsWith('.codex-plugin/'))
|
|
737
|
+
return 'codex';
|
|
738
|
+
if (f.relativePath.startsWith('.agent/'))
|
|
739
|
+
return 'antigravity';
|
|
740
|
+
return 'unknown';
|
|
741
|
+
}))];
|
|
688
742
|
let tarPath = null;
|
|
689
743
|
try {
|
|
690
744
|
tarPath = await createTarball(relayDir);
|
|
@@ -692,6 +746,29 @@ function registerPublish(program) {
|
|
|
692
746
|
console.error(`업로드 중...`);
|
|
693
747
|
}
|
|
694
748
|
const result = await publishToApi(token, tarPath, metadata);
|
|
749
|
+
// Git push: commit and push to git server (non-fatal if git server unavailable)
|
|
750
|
+
try {
|
|
751
|
+
const gitUrl = result.git_url;
|
|
752
|
+
if (gitUrl) {
|
|
753
|
+
if (!json) {
|
|
754
|
+
console.error('git 저장소에 푸시 중...');
|
|
755
|
+
}
|
|
756
|
+
const isFirstPublish = !result.is_update;
|
|
757
|
+
if (isFirstPublish) {
|
|
758
|
+
await (0, git_operations_js_1.gitPublishInit)(relayDir, gitUrl, config.version);
|
|
759
|
+
}
|
|
760
|
+
else {
|
|
761
|
+
await (0, git_operations_js_1.gitPublishUpdate)(relayDir, gitUrl, config.version);
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
}
|
|
765
|
+
catch (gitPushErr) {
|
|
766
|
+
// Git push failure is non-fatal — tar.gz upload already succeeded
|
|
767
|
+
if (!json) {
|
|
768
|
+
const gpMsg = gitPushErr instanceof Error ? gitPushErr.message : String(gitPushErr);
|
|
769
|
+
console.error(`\x1b[33m⚠ git push 실패 (배포는 완료됨): ${gpMsg}\x1b[0m`);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
695
772
|
// Update entry command preamble with scoped slug from server (non-fatal)
|
|
696
773
|
try {
|
|
697
774
|
if (result.slug && result.slug !== config.slug) {
|
|
@@ -713,6 +790,17 @@ function registerPublish(program) {
|
|
|
713
790
|
console.log(`\n\x1b[32m✓ ${config.name} 배포 완료\x1b[0m v${result.version}`);
|
|
714
791
|
console.log(` 슬러그: \x1b[36m${result.slug}\x1b[0m`);
|
|
715
792
|
console.log(` URL: \x1b[36m${result.url}\x1b[0m`);
|
|
793
|
+
// Show generated platform manifests
|
|
794
|
+
if (generatedPlatforms.length > 0) {
|
|
795
|
+
console.log(`\n \x1b[90m플랫폼 매니페스트:\x1b[0m ${generatedPlatforms.join(', ')}`);
|
|
796
|
+
}
|
|
797
|
+
// Show Claude Code plugin install command if claude-code manifest was generated
|
|
798
|
+
if (generatedPlatforms.includes('claude-code')) {
|
|
799
|
+
const pluginSlug = result.slug.startsWith('@') ? result.slug.slice(1) : result.slug;
|
|
800
|
+
const pluginUrl = `${config_js_1.API_URL}/api/registry/@${pluginSlug}/plugin`;
|
|
801
|
+
console.log(`\n \x1b[90mClaude Code 플러그인:\x1b[0m`);
|
|
802
|
+
console.log(` \x1b[36m/plugin marketplace add ${pluginUrl}\x1b[0m`);
|
|
803
|
+
}
|
|
716
804
|
// Show shareable onboarding guide as a plain copyable block
|
|
717
805
|
if (isTTY) {
|
|
718
806
|
const detailSlug = result.slug.startsWith('@') ? result.slug.slice(1) : result.slug;
|
package/dist/commands/update.js
CHANGED
|
@@ -9,6 +9,7 @@ const os_1 = __importDefault(require("os"));
|
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
10
|
const api_js_1 = require("../lib/api.js");
|
|
11
11
|
const storage_js_1 = require("../lib/storage.js");
|
|
12
|
+
const git_operations_js_1 = require("../lib/git-operations.js");
|
|
12
13
|
const installer_js_1 = require("../lib/installer.js");
|
|
13
14
|
const config_js_1 = require("../lib/config.js");
|
|
14
15
|
const slug_js_1 = require("../lib/slug.js");
|
|
@@ -76,13 +77,30 @@ function registerUpdate(program) {
|
|
|
76
77
|
const agentDir = currentScope === 'global'
|
|
77
78
|
? path_1.default.join(os_1.default.homedir(), '.relay', 'agents', owner, name)
|
|
78
79
|
: path_1.default.join(projectPath, '.relay', 'agents', owner, name);
|
|
79
|
-
// Download & extract
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
// Download & extract: prefer git, fallback to tar.gz
|
|
81
|
+
if (agent.git_url && fs_1.default.existsSync(path_1.default.join(agentDir, '.git'))) {
|
|
82
|
+
// Existing git clone — fetch + checkout latest tag
|
|
83
|
+
(0, git_operations_js_1.checkGitInstalled)();
|
|
84
|
+
(0, git_operations_js_1.gitFetch)(agentDir);
|
|
85
|
+
const latestTag = (0, git_operations_js_1.gitLatestTag)(agentDir);
|
|
86
|
+
if (latestTag) {
|
|
87
|
+
(0, git_operations_js_1.gitCheckout)(agentDir, latestTag);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else if (agent.git_url) {
|
|
91
|
+
// No existing clone — fresh git clone
|
|
92
|
+
(0, git_operations_js_1.checkGitInstalled)();
|
|
93
|
+
await (0, storage_js_1.clonePackage)(agent.git_url, agentDir);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
// Legacy tar.gz path
|
|
97
|
+
const tarPath = await (0, storage_js_1.downloadPackage)(agent.package_url, tempDir);
|
|
98
|
+
if (fs_1.default.existsSync(agentDir)) {
|
|
99
|
+
fs_1.default.rmSync(agentDir, { recursive: true, force: true });
|
|
100
|
+
}
|
|
101
|
+
fs_1.default.mkdirSync(agentDir, { recursive: true });
|
|
102
|
+
await (0, storage_js_1.extractPackage)(tarPath, agentDir);
|
|
83
103
|
}
|
|
84
|
-
fs_1.default.mkdirSync(agentDir, { recursive: true });
|
|
85
|
-
await (0, storage_js_1.extractPackage)(tarPath, agentDir);
|
|
86
104
|
// Inject preamble
|
|
87
105
|
(0, preamble_js_1.injectPreambleToAgent)(agentDir, slug);
|
|
88
106
|
// Deploy symlinks (always — handles migration from legacy deployed_files)
|
package/dist/index.js
CHANGED
|
@@ -24,6 +24,7 @@ const access_js_1 = require("./commands/access.js");
|
|
|
24
24
|
const grant_js_1 = require("./commands/grant.js");
|
|
25
25
|
const versions_js_1 = require("./commands/versions.js");
|
|
26
26
|
const diff_js_1 = require("./commands/diff.js");
|
|
27
|
+
const export_js_1 = require("./commands/export.js");
|
|
27
28
|
const feedback_js_1 = require("./commands/feedback.js");
|
|
28
29
|
const server_js_1 = require("./mcp/server.js");
|
|
29
30
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
@@ -56,6 +57,7 @@ program
|
|
|
56
57
|
(0, grant_js_1.registerGrant)(program);
|
|
57
58
|
(0, versions_js_1.registerVersions)(program);
|
|
58
59
|
(0, diff_js_1.registerDiff)(program);
|
|
60
|
+
(0, export_js_1.registerExport)(program);
|
|
59
61
|
(0, feedback_js_1.registerFeedback)(program);
|
|
60
62
|
program
|
|
61
63
|
.command('mcp')
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export declare function checkGitInstalled(): void;
|
|
2
|
+
export declare function gitInit(dir: string): void;
|
|
3
|
+
export declare function gitClone(url: string, destDir: string, opts?: {
|
|
4
|
+
depth?: number;
|
|
5
|
+
}): void;
|
|
6
|
+
export declare function gitAdd(dir: string, files?: string): void;
|
|
7
|
+
export declare function gitCommit(dir: string, message: string): void;
|
|
8
|
+
export declare function gitTag(dir: string, tag: string): void;
|
|
9
|
+
export declare function gitPush(dir: string, remote: string, refspec?: string): void;
|
|
10
|
+
export declare function gitFetch(dir: string): void;
|
|
11
|
+
export declare function gitCheckout(dir: string, ref: string): void;
|
|
12
|
+
export declare function gitDiff(dir: string, from: string, to: string): string;
|
|
13
|
+
export declare function gitLatestTag(dir: string): string | null;
|
|
14
|
+
/**
|
|
15
|
+
* Build an authenticated git URL.
|
|
16
|
+
* For public repos: https://git.relayax.com/@owner/agent.git
|
|
17
|
+
* For gated/private: https://TOKEN:x@git.relayax.com/@owner/agent.git
|
|
18
|
+
*/
|
|
19
|
+
export declare function buildGitUrl(baseUrl: string, auth?: {
|
|
20
|
+
token?: string;
|
|
21
|
+
code?: string;
|
|
22
|
+
}): string;
|
|
23
|
+
/**
|
|
24
|
+
* First-time publish: init → add → commit → tag → push
|
|
25
|
+
*/
|
|
26
|
+
export declare function gitPublishInit(sourceDir: string, remoteUrl: string, version: string): Promise<void>;
|
|
27
|
+
/**
|
|
28
|
+
* Re-publish: clone → replace files → commit → tag → push
|
|
29
|
+
*/
|
|
30
|
+
export declare function gitPublishUpdate(sourceDir: string, remoteUrl: string, version: string): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Install: git clone to destination, optionally checkout a specific version.
|
|
33
|
+
*/
|
|
34
|
+
export declare function gitInstall(gitUrl: string, destDir: string, version?: string): Promise<void>;
|
|
35
|
+
/**
|
|
36
|
+
* Update: fetch latest then checkout the newest tag.
|
|
37
|
+
*/
|
|
38
|
+
export declare function gitUpdate(destDir: string): Promise<string | null>;
|
|
@@ -0,0 +1,168 @@
|
|
|
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.checkGitInstalled = checkGitInstalled;
|
|
7
|
+
exports.gitInit = gitInit;
|
|
8
|
+
exports.gitClone = gitClone;
|
|
9
|
+
exports.gitAdd = gitAdd;
|
|
10
|
+
exports.gitCommit = gitCommit;
|
|
11
|
+
exports.gitTag = gitTag;
|
|
12
|
+
exports.gitPush = gitPush;
|
|
13
|
+
exports.gitFetch = gitFetch;
|
|
14
|
+
exports.gitCheckout = gitCheckout;
|
|
15
|
+
exports.gitDiff = gitDiff;
|
|
16
|
+
exports.gitLatestTag = gitLatestTag;
|
|
17
|
+
exports.buildGitUrl = buildGitUrl;
|
|
18
|
+
exports.gitPublishInit = gitPublishInit;
|
|
19
|
+
exports.gitPublishUpdate = gitPublishUpdate;
|
|
20
|
+
exports.gitInstall = gitInstall;
|
|
21
|
+
exports.gitUpdate = gitUpdate;
|
|
22
|
+
const child_process_1 = require("child_process");
|
|
23
|
+
const fs_1 = __importDefault(require("fs"));
|
|
24
|
+
const path_1 = __importDefault(require("path"));
|
|
25
|
+
const os_1 = __importDefault(require("os"));
|
|
26
|
+
// ─── Git Binary Check ───
|
|
27
|
+
function checkGitInstalled() {
|
|
28
|
+
try {
|
|
29
|
+
(0, child_process_1.execFileSync)('git', ['--version'], { stdio: 'pipe' });
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
throw new Error('git이 설치되어 있지 않습니다.\n' +
|
|
33
|
+
' macOS: xcode-select --install\n' +
|
|
34
|
+
' Ubuntu/Debian: sudo apt install git\n' +
|
|
35
|
+
' Windows: https://git-scm.com/download/win');
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// ─── Core Git Operations ───
|
|
39
|
+
function gitInit(dir) {
|
|
40
|
+
(0, child_process_1.execFileSync)('git', ['init'], { cwd: dir, stdio: 'pipe' });
|
|
41
|
+
}
|
|
42
|
+
function gitClone(url, destDir, opts) {
|
|
43
|
+
const args = ['clone'];
|
|
44
|
+
if (opts?.depth) {
|
|
45
|
+
args.push('--depth', String(opts.depth));
|
|
46
|
+
}
|
|
47
|
+
args.push(url, destDir);
|
|
48
|
+
(0, child_process_1.execFileSync)('git', args, { stdio: 'pipe' });
|
|
49
|
+
}
|
|
50
|
+
function gitAdd(dir, files = '.') {
|
|
51
|
+
(0, child_process_1.execFileSync)('git', ['add', files], { cwd: dir, stdio: 'pipe' });
|
|
52
|
+
}
|
|
53
|
+
function gitCommit(dir, message) {
|
|
54
|
+
// Configure committer for the temp repo
|
|
55
|
+
(0, child_process_1.execFileSync)('git', ['config', 'user.email', 'relay@relayax.com'], { cwd: dir, stdio: 'pipe' });
|
|
56
|
+
(0, child_process_1.execFileSync)('git', ['config', 'user.name', 'Relay CLI'], { cwd: dir, stdio: 'pipe' });
|
|
57
|
+
(0, child_process_1.execFileSync)('git', ['commit', '-m', message], { cwd: dir, stdio: 'pipe' });
|
|
58
|
+
}
|
|
59
|
+
function gitTag(dir, tag) {
|
|
60
|
+
(0, child_process_1.execFileSync)('git', ['tag', tag], { cwd: dir, stdio: 'pipe' });
|
|
61
|
+
}
|
|
62
|
+
function gitPush(dir, remote, refspec) {
|
|
63
|
+
const args = ['push', remote];
|
|
64
|
+
if (refspec)
|
|
65
|
+
args.push(refspec);
|
|
66
|
+
args.push('--tags');
|
|
67
|
+
(0, child_process_1.execFileSync)('git', args, { cwd: dir, stdio: 'pipe' });
|
|
68
|
+
}
|
|
69
|
+
function gitFetch(dir) {
|
|
70
|
+
(0, child_process_1.execFileSync)('git', ['fetch', '--tags'], { cwd: dir, stdio: 'pipe' });
|
|
71
|
+
}
|
|
72
|
+
function gitCheckout(dir, ref) {
|
|
73
|
+
(0, child_process_1.execFileSync)('git', ['checkout', ref], { cwd: dir, stdio: 'pipe' });
|
|
74
|
+
}
|
|
75
|
+
function gitDiff(dir, from, to) {
|
|
76
|
+
return (0, child_process_1.execFileSync)('git', ['diff', `${from}..${to}`], {
|
|
77
|
+
cwd: dir,
|
|
78
|
+
encoding: 'utf-8',
|
|
79
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
function gitLatestTag(dir) {
|
|
83
|
+
try {
|
|
84
|
+
const result = (0, child_process_1.execFileSync)('git', ['describe', '--tags', '--abbrev=0'], {
|
|
85
|
+
cwd: dir,
|
|
86
|
+
encoding: 'utf-8',
|
|
87
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
88
|
+
}).trim();
|
|
89
|
+
return result || null;
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// ─── High-level Operations ───
|
|
96
|
+
/**
|
|
97
|
+
* Build an authenticated git URL.
|
|
98
|
+
* For public repos: https://git.relayax.com/@owner/agent.git
|
|
99
|
+
* For gated/private: https://TOKEN:x@git.relayax.com/@owner/agent.git
|
|
100
|
+
*/
|
|
101
|
+
function buildGitUrl(baseUrl, auth) {
|
|
102
|
+
if (!auth?.token && !auth?.code)
|
|
103
|
+
return baseUrl;
|
|
104
|
+
const url = new URL(baseUrl);
|
|
105
|
+
const credential = auth.token ?? auth.code ?? '';
|
|
106
|
+
url.username = credential;
|
|
107
|
+
url.password = 'x';
|
|
108
|
+
return url.toString();
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* First-time publish: init → add → commit → tag → push
|
|
112
|
+
*/
|
|
113
|
+
async function gitPublishInit(sourceDir, remoteUrl, version) {
|
|
114
|
+
gitInit(sourceDir);
|
|
115
|
+
gitAdd(sourceDir);
|
|
116
|
+
gitCommit(sourceDir, `v${version}`);
|
|
117
|
+
gitTag(sourceDir, `v${version}`);
|
|
118
|
+
gitPush(sourceDir, remoteUrl, 'HEAD:main');
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Re-publish: clone → replace files → commit → tag → push
|
|
122
|
+
*/
|
|
123
|
+
async function gitPublishUpdate(sourceDir, remoteUrl, version) {
|
|
124
|
+
const tempCloneDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'relay-git-'));
|
|
125
|
+
try {
|
|
126
|
+
// Clone existing repo
|
|
127
|
+
gitClone(remoteUrl, tempCloneDir);
|
|
128
|
+
// Copy .git directory to source dir
|
|
129
|
+
const gitDir = path_1.default.join(tempCloneDir, '.git');
|
|
130
|
+
const destGitDir = path_1.default.join(sourceDir, '.git');
|
|
131
|
+
fs_1.default.cpSync(gitDir, destGitDir, { recursive: true });
|
|
132
|
+
// Add all files (including new/changed, removing deleted)
|
|
133
|
+
gitAdd(sourceDir);
|
|
134
|
+
gitCommit(sourceDir, `v${version}`);
|
|
135
|
+
gitTag(sourceDir, `v${version}`);
|
|
136
|
+
gitPush(sourceDir, 'origin');
|
|
137
|
+
}
|
|
138
|
+
finally {
|
|
139
|
+
fs_1.default.rmSync(tempCloneDir, { recursive: true, force: true });
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Install: git clone to destination, optionally checkout a specific version.
|
|
144
|
+
*/
|
|
145
|
+
async function gitInstall(gitUrl, destDir, version) {
|
|
146
|
+
// Remove existing directory if present
|
|
147
|
+
if (fs_1.default.existsSync(destDir)) {
|
|
148
|
+
fs_1.default.rmSync(destDir, { recursive: true, force: true });
|
|
149
|
+
}
|
|
150
|
+
fs_1.default.mkdirSync(path_1.default.dirname(destDir), { recursive: true });
|
|
151
|
+
gitClone(gitUrl, destDir, { depth: 1 });
|
|
152
|
+
if (version) {
|
|
153
|
+
// Fetch all tags then checkout the specific version
|
|
154
|
+
gitFetch(destDir);
|
|
155
|
+
gitCheckout(destDir, `v${version}`);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Update: fetch latest then checkout the newest tag.
|
|
160
|
+
*/
|
|
161
|
+
async function gitUpdate(destDir) {
|
|
162
|
+
gitFetch(destDir);
|
|
163
|
+
const latestTag = gitLatestTag(destDir);
|
|
164
|
+
if (latestTag) {
|
|
165
|
+
gitCheckout(destDir, latestTag);
|
|
166
|
+
}
|
|
167
|
+
return latestTag;
|
|
168
|
+
}
|
package/dist/lib/guide.js
CHANGED
|
@@ -29,16 +29,25 @@ function generateGuide(config, commands, requires) {
|
|
|
29
29
|
const scopedSlug = config.slug.startsWith('@') ? config.slug : `@${config.slug}`;
|
|
30
30
|
const installCmd = `npx relayax-cli install ${scopedSlug}`;
|
|
31
31
|
const requiresSummary = requires ? buildRequiresSummary(requires) : '';
|
|
32
|
+
const slugPart = scopedSlug.startsWith('@') ? scopedSlug.slice(1) : scopedSlug;
|
|
33
|
+
const pluginUrl = `https://www.relayax.com/api/registry/${slugPart}/plugin`;
|
|
32
34
|
return `# ${config.name}
|
|
33
35
|
|
|
34
36
|
> ${config.description}
|
|
35
37
|
|
|
36
38
|
## 설치
|
|
37
39
|
|
|
40
|
+
### CLI
|
|
38
41
|
\`\`\`bash
|
|
39
42
|
${installCmd}
|
|
40
43
|
\`\`\`
|
|
41
44
|
|
|
45
|
+
### Claude Code Plugin
|
|
46
|
+
\`\`\`
|
|
47
|
+
/plugin marketplace add ${pluginUrl}
|
|
48
|
+
/plugin install ${config.slug.split('/').pop() ?? config.slug}
|
|
49
|
+
\`\`\`
|
|
50
|
+
|
|
42
51
|
${commands.length > 0 ? `## 포함된 커맨드
|
|
43
52
|
|
|
44
53
|
${commands.map((cmd) => `- \`/${cmd.name}\`: ${cmd.description}`).join('\n')}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface GeneratedFile {
|
|
2
|
+
relativePath: string;
|
|
3
|
+
content: string;
|
|
4
|
+
}
|
|
5
|
+
export interface ManifestRelayYaml {
|
|
6
|
+
name: string;
|
|
7
|
+
slug: string;
|
|
8
|
+
description: string;
|
|
9
|
+
version: string;
|
|
10
|
+
source?: string;
|
|
11
|
+
org_slug?: string;
|
|
12
|
+
platforms?: string[];
|
|
13
|
+
}
|
|
14
|
+
export declare const SUPPORTED_PLATFORMS: readonly ["claude-code", "codex", "antigravity"];
|
|
15
|
+
export type Platform = typeof SUPPORTED_PLATFORMS[number];
|
|
16
|
+
/**
|
|
17
|
+
* Generate platform-native manifests from relay.yaml metadata.
|
|
18
|
+
* Used by both `relay publish` and `relay export`.
|
|
19
|
+
*/
|
|
20
|
+
export declare function generateManifests(yaml: ManifestRelayYaml, agentDir: string): GeneratedFile[];
|