relayax-cli 0.3.64 → 0.3.66
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/install.js +146 -41
- package/dist/commands/publish.js +59 -41
- 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/mcp/server.js +23 -0
- package/dist/prompts/create.md +62 -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/install.js
CHANGED
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.registerInstall = registerInstall;
|
|
7
7
|
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const os_1 = __importDefault(require("os"));
|
|
8
9
|
const path_1 = __importDefault(require("path"));
|
|
9
10
|
const api_js_1 = require("../lib/api.js");
|
|
10
11
|
const storage_js_1 = require("../lib/storage.js");
|
|
@@ -15,11 +16,15 @@ const init_js_1 = require("./init.js");
|
|
|
15
16
|
const paths_js_1 = require("../lib/paths.js");
|
|
16
17
|
const error_report_js_1 = require("../lib/error-report.js");
|
|
17
18
|
const step_tracker_js_1 = require("../lib/step-tracker.js");
|
|
19
|
+
const installer_js_1 = require("../lib/installer.js");
|
|
18
20
|
function registerInstall(program) {
|
|
19
21
|
program
|
|
20
22
|
.command('install <slug>')
|
|
21
23
|
.description('에이전트 패키지를 .relay/agents/에 다운로드합니다')
|
|
22
24
|
.option('--join-code <code>', '초대 코드 (Organization 에이전트 설치 시 자동 가입)')
|
|
25
|
+
.option('--code <code>', '접근 코드 (private 에이전트 설치 시)')
|
|
26
|
+
.option('--global', '글로벌 설치 (홈 디렉토리)')
|
|
27
|
+
.option('--local', '로컬 설치 (프로젝트 디렉토리)')
|
|
23
28
|
.option('--project <dir>', '프로젝트 루트 경로 (기본: cwd, 환경변수: RELAY_PROJECT_PATH)')
|
|
24
29
|
.action(async (slugInput, _opts) => {
|
|
25
30
|
const json = program.opts().json ?? false;
|
|
@@ -45,6 +50,39 @@ function registerInstall(program) {
|
|
|
45
50
|
const actualSlugInput = versionMatch ? versionMatch[1] : slugInput;
|
|
46
51
|
parsed = await (0, slug_js_1.resolveSlug)(actualSlugInput);
|
|
47
52
|
slug = parsed.full;
|
|
53
|
+
// Helper: ensure a valid token exists, triggering auto-login in TTY if needed.
|
|
54
|
+
// Returns the token string or null if login failed / not available.
|
|
55
|
+
async function ensureToken() {
|
|
56
|
+
let token = await (0, config_js_1.getValidToken)();
|
|
57
|
+
if (!token) {
|
|
58
|
+
const isTTY = Boolean(process.stdin.isTTY);
|
|
59
|
+
if (isTTY && !json) {
|
|
60
|
+
console.error('\x1b[33m⚙ 이 에이전트는 로그인이 필요합니다. 로그인을 시작합니다...\x1b[0m');
|
|
61
|
+
const { runLogin } = await import('./login.js');
|
|
62
|
+
await runLogin();
|
|
63
|
+
token = await (0, config_js_1.getValidToken)();
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return token ?? null;
|
|
67
|
+
}
|
|
68
|
+
// Pre-fetch auto-login: --join-code and --code always require auth.
|
|
69
|
+
if (_opts.joinCode || _opts.code) {
|
|
70
|
+
const token = await ensureToken();
|
|
71
|
+
if (!token) {
|
|
72
|
+
if (json) {
|
|
73
|
+
console.error(JSON.stringify({
|
|
74
|
+
error: 'LOGIN_REQUIRED',
|
|
75
|
+
slug,
|
|
76
|
+
message: '이 에이전트는 로그인이 필요합니다. relay login을 먼저 실행하세요.',
|
|
77
|
+
fix: 'relay login 실행 후 재시도하세요.',
|
|
78
|
+
}));
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
console.error('\x1b[31m이 에이전트는 로그인이 필요합니다. relay login 을 먼저 실행하세요.\x1b[0m');
|
|
82
|
+
}
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
48
86
|
try {
|
|
49
87
|
agent = await (0, api_js_1.fetchAgentInfo)(slug);
|
|
50
88
|
}
|
|
@@ -64,15 +102,63 @@ function registerInstall(program) {
|
|
|
64
102
|
}
|
|
65
103
|
}
|
|
66
104
|
catch { /* ignore parse errors */ }
|
|
67
|
-
//
|
|
68
|
-
if (
|
|
105
|
+
// Task 2.1: --join-code provided and not yet a member → join org then retry
|
|
106
|
+
if (_opts.joinCode && membershipStatus !== 'member') {
|
|
107
|
+
if (!json) {
|
|
108
|
+
console.error('\x1b[33m⚙ 초대 코드로 Organization에 가입합니다...\x1b[0m');
|
|
109
|
+
}
|
|
110
|
+
const { joinOrg } = await import('./join.js');
|
|
111
|
+
await joinOrg(parsed.owner, _opts.joinCode);
|
|
112
|
+
agent = await (0, api_js_1.fetchAgentInfo)(slug);
|
|
113
|
+
}
|
|
114
|
+
// Task 2.2: --code provided and agent is private → claim access then retry
|
|
115
|
+
else if (_opts.code && (errorVisibility === 'private' || purchaseInfo)) {
|
|
116
|
+
if (!json) {
|
|
117
|
+
console.error('\x1b[33m⚙ 접근 코드로 에이전트 접근 권한을 요청합니다...\x1b[0m');
|
|
118
|
+
}
|
|
119
|
+
const token = await (0, config_js_1.getValidToken)();
|
|
120
|
+
if (!token) {
|
|
121
|
+
if (json) {
|
|
122
|
+
console.error(JSON.stringify({
|
|
123
|
+
error: 'LOGIN_REQUIRED',
|
|
124
|
+
slug,
|
|
125
|
+
message: '이 에이전트는 로그인이 필요합니다. relay login을 먼저 실행하세요.',
|
|
126
|
+
fix: 'relay login 실행 후 재시도하세요.',
|
|
127
|
+
}));
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
console.error('\x1b[31m이 에이전트는 로그인이 필요합니다. relay login 을 먼저 실행하세요.\x1b[0m');
|
|
131
|
+
}
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
const claimRes = await fetch(`${config_js_1.API_URL}/api/agents/${slug}/claim-access`, {
|
|
135
|
+
method: 'POST',
|
|
136
|
+
headers: {
|
|
137
|
+
'Content-Type': 'application/json',
|
|
138
|
+
Authorization: `Bearer ${token}`,
|
|
139
|
+
},
|
|
140
|
+
body: JSON.stringify({ code: _opts.code }),
|
|
141
|
+
signal: AbortSignal.timeout(10000),
|
|
142
|
+
});
|
|
143
|
+
if (!claimRes.ok) {
|
|
144
|
+
const claimBody = (await claimRes.json().catch(() => ({})));
|
|
145
|
+
const claimErrCode = claimBody.error ?? String(claimRes.status);
|
|
146
|
+
if (claimErrCode === 'INVALID_LINK')
|
|
147
|
+
throw new Error('초대 링크가 유효하지 않거나 만료되었습니다.');
|
|
148
|
+
throw new Error(claimBody.message ?? `접근 권한 요청 실패 (${claimRes.status})`);
|
|
149
|
+
}
|
|
150
|
+
agent = await (0, api_js_1.fetchAgentInfo)(slug);
|
|
151
|
+
}
|
|
152
|
+
// No code provided: show appropriate error messages
|
|
153
|
+
else if (errorVisibility === 'private' || purchaseInfo) {
|
|
154
|
+
// Private agent: show purchase info + relay access hint
|
|
69
155
|
if (json) {
|
|
70
156
|
console.error(JSON.stringify({
|
|
71
157
|
error: 'ACCESS_REQUIRED',
|
|
72
158
|
message: '이 에이전트는 접근 권한이 필요합니다.',
|
|
73
159
|
slug,
|
|
74
160
|
purchase_info: purchaseInfo ?? null,
|
|
75
|
-
fix: '접근 링크 코드가 있으면: relay
|
|
161
|
+
fix: '접근 링크 코드가 있으면: relay install ' + slugInput + ' --code <코드>',
|
|
76
162
|
}));
|
|
77
163
|
}
|
|
78
164
|
else {
|
|
@@ -83,18 +169,18 @@ function registerInstall(program) {
|
|
|
83
169
|
if (purchaseInfo?.url) {
|
|
84
170
|
console.error(` \x1b[36m${purchaseInfo.url}\x1b[0m`);
|
|
85
171
|
}
|
|
86
|
-
console.error(`\n\x1b[33m접근 링크 코드가 있으면: relay
|
|
172
|
+
console.error(`\n\x1b[33m접근 링크 코드가 있으면: relay install ${slugInput} --code <코드>\x1b[0m`);
|
|
87
173
|
}
|
|
88
174
|
process.exit(1);
|
|
89
175
|
}
|
|
90
|
-
if (membershipStatus === 'member') {
|
|
176
|
+
else if (membershipStatus === 'member') {
|
|
91
177
|
// Member but no access to this specific agent
|
|
92
178
|
if (json) {
|
|
93
179
|
console.error(JSON.stringify({
|
|
94
180
|
error: 'NO_ACCESS',
|
|
95
181
|
message: '이 에이전트에 대한 접근 권한이 없습니다.',
|
|
96
182
|
slug,
|
|
97
|
-
fix: '이 에이전트의 접근 링크 코드가 있으면 `relay
|
|
183
|
+
fix: '이 에이전트의 접근 링크 코드가 있으면 `relay install ' + slugInput + ' --code <코드>`로 접근 권한을 얻으세요. 없으면 에이전트 제작자에게 문의하세요.',
|
|
98
184
|
}));
|
|
99
185
|
}
|
|
100
186
|
else {
|
|
@@ -108,12 +194,12 @@ function registerInstall(program) {
|
|
|
108
194
|
error: 'ACCESS_REQUIRED',
|
|
109
195
|
message: '이 에이전트는 접근 권한이 필요합니다.',
|
|
110
196
|
slug,
|
|
111
|
-
fix: '초대 코드가 있으면 `relay
|
|
197
|
+
fix: '초대 코드가 있으면 `relay install ' + slugInput + ' --join-code <코드>`로 가입하세요.',
|
|
112
198
|
}));
|
|
113
199
|
}
|
|
114
200
|
else {
|
|
115
201
|
console.error('\x1b[31m이 에이전트는 접근 권한이 필요합니다.\x1b[0m');
|
|
116
|
-
console.error('\x1b[33m초대 코드가 있으면 `relay
|
|
202
|
+
console.error('\x1b[33m초대 코드가 있으면 `relay install ' + slugInput + ' --join-code <코드>`로 가입하세요.\x1b[0m');
|
|
117
203
|
}
|
|
118
204
|
process.exit(1);
|
|
119
205
|
}
|
|
@@ -126,35 +212,32 @@ function registerInstall(program) {
|
|
|
126
212
|
throw new Error('에이전트 정보를 가져오지 못했습니다.');
|
|
127
213
|
// Re-bind as non-optional so TypeScript tracks the narrowing through nested scopes
|
|
128
214
|
let resolvedAgent = agent;
|
|
129
|
-
|
|
130
|
-
|
|
215
|
+
// Scope 자동결정: --global/--local 플래그 > agent_type 기반
|
|
216
|
+
const scope = _opts.global ? 'global'
|
|
217
|
+
: _opts.local ? 'local'
|
|
218
|
+
: resolvedAgent.type === 'passive' ? 'local'
|
|
219
|
+
: 'global';
|
|
220
|
+
const agentDir = scope === 'global'
|
|
221
|
+
? path_1.default.join(os_1.default.homedir(), '.relay', 'agents', parsed.owner, parsed.name)
|
|
222
|
+
: path_1.default.join(projectPath, '.relay', 'agents', parsed.owner, parsed.name);
|
|
223
|
+
// 2. Visibility check + auto-login (internal agents always require a token)
|
|
131
224
|
const visibility = resolvedAgent.visibility ?? 'public';
|
|
132
225
|
if (visibility === 'internal') {
|
|
133
|
-
|
|
226
|
+
const token = await ensureToken();
|
|
134
227
|
if (!token) {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
228
|
+
if (json) {
|
|
229
|
+
console.error(JSON.stringify({
|
|
230
|
+
error: 'LOGIN_REQUIRED',
|
|
231
|
+
visibility,
|
|
232
|
+
slug,
|
|
233
|
+
message: '이 에이전트는 로그인이 필요합니다. relay login을 먼저 실행하세요.',
|
|
234
|
+
fix: 'relay login 실행 후 재시도하세요.',
|
|
235
|
+
}));
|
|
142
236
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
console.error(JSON.stringify({
|
|
146
|
-
error: 'LOGIN_REQUIRED',
|
|
147
|
-
visibility,
|
|
148
|
-
slug,
|
|
149
|
-
message: '이 에이전트는 로그인이 필요합니다. relay login을 먼저 실행하세요.',
|
|
150
|
-
fix: 'relay login 실행 후 재시도하세요.',
|
|
151
|
-
}));
|
|
152
|
-
}
|
|
153
|
-
else {
|
|
154
|
-
console.error('\x1b[31m이 에이전트는 로그인이 필요합니다. relay login 을 먼저 실행하세요.\x1b[0m');
|
|
155
|
-
}
|
|
156
|
-
process.exit(1);
|
|
237
|
+
else {
|
|
238
|
+
console.error('\x1b[31m이 에이전트는 로그인이 필요합니다. relay login 을 먼저 실행하세요.\x1b[0m');
|
|
157
239
|
}
|
|
240
|
+
process.exit(1);
|
|
158
241
|
}
|
|
159
242
|
}
|
|
160
243
|
// 3. Download package (retry once if signed URL expired)
|
|
@@ -184,7 +267,13 @@ function registerInstall(program) {
|
|
|
184
267
|
await (0, storage_js_1.extractPackage)(tarPath, agentDir);
|
|
185
268
|
// 4.5. Inject preamble (update check) into SKILL.md and commands
|
|
186
269
|
(0, preamble_js_1.injectPreambleToAgent)(agentDir, slug);
|
|
187
|
-
// 5.
|
|
270
|
+
// 5. Deploy symlinks to detected AI tool directories
|
|
271
|
+
const deploy = (0, installer_js_1.deploySymlinks)(agentDir, scope, projectPath);
|
|
272
|
+
for (const w of deploy.warnings) {
|
|
273
|
+
if (!json)
|
|
274
|
+
console.error(`\x1b[33m${w}\x1b[0m`);
|
|
275
|
+
}
|
|
276
|
+
// 6. Count extracted files
|
|
188
277
|
function countFiles(dir) {
|
|
189
278
|
let count = 0;
|
|
190
279
|
if (!fs_1.default.existsSync(dir))
|
|
@@ -200,16 +289,26 @@ function registerInstall(program) {
|
|
|
200
289
|
return count;
|
|
201
290
|
}
|
|
202
291
|
const fileCount = countFiles(agentDir);
|
|
203
|
-
//
|
|
204
|
-
const
|
|
205
|
-
installed[slug] = {
|
|
292
|
+
// 7. Record in installed.json (scope-aware)
|
|
293
|
+
const installRecord = {
|
|
206
294
|
agent_id: resolvedAgent.id,
|
|
207
295
|
version: resolvedAgent.version,
|
|
208
296
|
installed_at: new Date().toISOString(),
|
|
209
297
|
files: [agentDir],
|
|
298
|
+
deploy_scope: scope,
|
|
299
|
+
deployed_symlinks: deploy.symlinks,
|
|
210
300
|
};
|
|
211
|
-
(
|
|
212
|
-
|
|
301
|
+
if (scope === 'global') {
|
|
302
|
+
const globalInstalled = (0, config_js_1.loadGlobalInstalled)();
|
|
303
|
+
globalInstalled[slug] = installRecord;
|
|
304
|
+
(0, config_js_1.saveGlobalInstalled)(globalInstalled);
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
const installed = (0, config_js_1.loadInstalled)();
|
|
308
|
+
installed[slug] = installRecord;
|
|
309
|
+
(0, config_js_1.saveInstalled)(installed);
|
|
310
|
+
}
|
|
311
|
+
// 8. Report install + usage ping (non-blocking, agent_id 기반)
|
|
213
312
|
await (0, api_js_1.reportInstall)(resolvedAgent.id, slug, resolvedAgent.version);
|
|
214
313
|
(0, api_js_1.sendUsagePing)(resolvedAgent.id, slug, resolvedAgent.version);
|
|
215
314
|
const result = {
|
|
@@ -220,6 +319,8 @@ function registerInstall(program) {
|
|
|
220
319
|
commands: resolvedAgent.commands,
|
|
221
320
|
files: fileCount,
|
|
222
321
|
install_path: agentDir,
|
|
322
|
+
scope,
|
|
323
|
+
symlinks: deploy.symlinks,
|
|
223
324
|
author: resolvedAgent.author ? {
|
|
224
325
|
username: resolvedAgent.author.username,
|
|
225
326
|
display_name: resolvedAgent.author.display_name ?? null,
|
|
@@ -233,9 +334,11 @@ function registerInstall(program) {
|
|
|
233
334
|
else {
|
|
234
335
|
const authorUsername = resolvedAgent.author?.username;
|
|
235
336
|
const authorSuffix = authorUsername ? ` \x1b[90mby @${authorUsername}\x1b[0m` : '';
|
|
236
|
-
|
|
337
|
+
const scopeLabel = scope === 'global' ? '글로벌' : '로컬';
|
|
338
|
+
console.log(`\n\x1b[32m✓ ${resolvedAgent.name} 설치 완료\x1b[0m v${resolvedAgent.version}${authorSuffix}`);
|
|
237
339
|
console.log(` 위치: \x1b[36m${agentDir}\x1b[0m`);
|
|
238
|
-
console.log(`
|
|
340
|
+
console.log(` 범위: ${scopeLabel}`);
|
|
341
|
+
console.log(` 파일: ${fileCount}개, symlink: ${deploy.symlinks.length}개`);
|
|
239
342
|
if (resolvedAgent.commands.length > 0) {
|
|
240
343
|
console.log('\n 포함된 커맨드:');
|
|
241
344
|
for (const cmd of resolvedAgent.commands) {
|
|
@@ -256,7 +359,9 @@ function registerInstall(program) {
|
|
|
256
359
|
else {
|
|
257
360
|
console.log(`\n\x1b[33m💡 설치 완료! AI 에이전트에서 사용할 수 있습니다.\x1b[0m`);
|
|
258
361
|
}
|
|
259
|
-
|
|
362
|
+
// Requires check
|
|
363
|
+
const requiresResults = (0, installer_js_1.checkRequires)(agentDir);
|
|
364
|
+
(0, installer_js_1.printRequiresCheck)(requiresResults);
|
|
260
365
|
}
|
|
261
366
|
}
|
|
262
367
|
catch (err) {
|
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
|
}
|
|
@@ -581,7 +596,8 @@ function registerPublish(program) {
|
|
|
581
596
|
}
|
|
582
597
|
}
|
|
583
598
|
// Confirm visibility before publish (재배포 시 변경 기회 제공)
|
|
584
|
-
|
|
599
|
+
// Skip when a bump flag is present and visibility is already set in relay.yaml
|
|
600
|
+
if (isTTY && !hasBumpFlag) {
|
|
585
601
|
const { select: promptConfirmVis } = await import('@inquirer/prompts');
|
|
586
602
|
const visLabelMap = {
|
|
587
603
|
public: '공개',
|
|
@@ -701,22 +717,24 @@ function registerPublish(program) {
|
|
|
701
717
|
if (isTTY) {
|
|
702
718
|
const detailSlug = result.slug.startsWith('@') ? result.slug.slice(1) : result.slug;
|
|
703
719
|
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(
|
|
720
|
+
// Primary: npx 설치 명령어 한 줄
|
|
721
|
+
const visibility = config.visibility ?? 'public';
|
|
722
|
+
let installCmd;
|
|
723
|
+
if (visibility === 'internal' && accessCode) {
|
|
724
|
+
installCmd = `npx relayax-cli install ${result.slug} --join-code ${accessCode}`;
|
|
725
|
+
}
|
|
726
|
+
else if (visibility === 'private' && accessCode) {
|
|
727
|
+
installCmd = `npx relayax-cli install ${result.slug} --code ${accessCode}`;
|
|
728
|
+
}
|
|
729
|
+
else {
|
|
730
|
+
installCmd = `npx relayax-cli install ${result.slug}`;
|
|
731
|
+
}
|
|
732
|
+
console.log(`\n \x1b[90m공유하세요:\x1b[0m`);
|
|
733
|
+
console.log(` ┌${'─'.repeat(installCmd.length + 2)}┐`);
|
|
734
|
+
console.log(` │ ${installCmd} │`);
|
|
735
|
+
console.log(` └${'─'.repeat(installCmd.length + 2)}┘`);
|
|
736
|
+
// Secondary: 에이전트 소개 페이지
|
|
737
|
+
console.log(`\n \x1b[90m에이전트 소개: \x1b[36mhttps://relayax.com/@${detailSlug}\x1b[0m`);
|
|
720
738
|
}
|
|
721
739
|
}
|
|
722
740
|
}
|
|
@@ -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);
|