relayax-cli 0.2.41 → 0.3.42
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/access.js +12 -12
- package/dist/commands/changelog.js +2 -2
- package/dist/commands/check-update.js +12 -12
- package/dist/commands/create.js +46 -19
- package/dist/commands/deploy-record.js +2 -2
- package/dist/commands/diff.d.ts +2 -0
- package/dist/commands/diff.js +72 -0
- package/dist/commands/grant.d.ts +33 -0
- package/dist/commands/grant.js +190 -0
- package/dist/commands/init.js +10 -10
- package/dist/commands/install.js +125 -256
- package/dist/commands/join.d.ts +3 -2
- package/dist/commands/join.js +18 -69
- package/dist/commands/list.js +23 -26
- package/dist/commands/login.js +10 -3
- package/dist/commands/orgs.d.ts +10 -0
- package/dist/commands/orgs.js +128 -0
- package/dist/commands/outdated.js +7 -7
- package/dist/commands/package.d.ts +18 -0
- package/dist/commands/package.js +355 -146
- package/dist/commands/ping.js +5 -5
- package/dist/commands/publish.d.ts +1 -1
- package/dist/commands/publish.js +105 -103
- package/dist/commands/search.js +2 -2
- package/dist/commands/status.js +11 -11
- package/dist/commands/uninstall.js +7 -7
- package/dist/commands/update.js +22 -22
- package/dist/commands/versions.d.ts +2 -0
- package/dist/commands/versions.js +44 -0
- package/dist/index.js +8 -2
- package/dist/lib/ai-tools.d.ts +15 -0
- package/dist/lib/ai-tools.js +48 -1
- package/dist/lib/api.d.ts +13 -12
- package/dist/lib/api.js +24 -39
- package/dist/lib/command-adapter.js +41 -693
- package/dist/lib/config.d.ts +10 -5
- package/dist/lib/config.js +106 -24
- package/dist/lib/guide.js +34 -79
- package/dist/lib/installer.d.ts +2 -2
- package/dist/lib/installer.js +4 -4
- package/dist/lib/preamble.d.ts +4 -4
- package/dist/lib/preamble.js +14 -15
- package/dist/lib/slug.d.ts +5 -1
- package/dist/lib/slug.js +52 -9
- package/dist/lib/update-cache.js +4 -4
- package/dist/lib/version-check.d.ts +3 -3
- package/dist/lib/version-check.js +13 -13
- package/dist/prompts/_business-card.md +41 -0
- package/dist/prompts/_error-handling.md +38 -0
- package/dist/prompts/_requirements-check.md +59 -0
- package/dist/prompts/_setup-cli.md +19 -0
- package/dist/prompts/_setup-login.md +7 -0
- package/dist/prompts/_setup-org.md +27 -0
- package/dist/prompts/business-card.md +41 -0
- package/dist/prompts/error-handling.md +38 -0
- package/dist/prompts/index.d.ts +7 -0
- package/dist/prompts/index.js +28 -0
- package/dist/prompts/install.md +187 -0
- package/dist/prompts/publish.md +444 -0
- package/dist/prompts/requirements-check.md +59 -0
- package/dist/types.d.ts +10 -10
- package/package.json +3 -3
package/dist/commands/publish.js
CHANGED
|
@@ -23,8 +23,8 @@ function parseRelayYaml(content) {
|
|
|
23
23
|
: [];
|
|
24
24
|
const requires = raw.requires;
|
|
25
25
|
const rawVisibility = String(raw.visibility ?? '');
|
|
26
|
-
const visibility = rawVisibility === '
|
|
27
|
-
: rawVisibility === '
|
|
26
|
+
const visibility = rawVisibility === 'internal' ? 'internal'
|
|
27
|
+
: rawVisibility === 'private' ? 'private'
|
|
28
28
|
: rawVisibility === 'public' ? 'public'
|
|
29
29
|
: undefined;
|
|
30
30
|
const rawType = String(raw.type ?? '');
|
|
@@ -46,8 +46,8 @@ function parseRelayYaml(content) {
|
|
|
46
46
|
source: raw.source ? String(raw.source) : undefined,
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
|
-
function detectCommands(
|
|
50
|
-
const cmdDir = path_1.default.join(
|
|
49
|
+
function detectCommands(agentDir) {
|
|
50
|
+
const cmdDir = path_1.default.join(agentDir, 'commands');
|
|
51
51
|
if (!fs_1.default.existsSync(cmdDir))
|
|
52
52
|
return [];
|
|
53
53
|
const entries = [];
|
|
@@ -78,8 +78,8 @@ function detectCommands(teamDir) {
|
|
|
78
78
|
}
|
|
79
79
|
return entries;
|
|
80
80
|
}
|
|
81
|
-
function detectSkills(
|
|
82
|
-
const skillsDir = path_1.default.join(
|
|
81
|
+
function detectSkills(agentDir) {
|
|
82
|
+
const skillsDir = path_1.default.join(agentDir, 'skills');
|
|
83
83
|
if (!fs_1.default.existsSync(skillsDir))
|
|
84
84
|
return [];
|
|
85
85
|
const entries = [];
|
|
@@ -118,8 +118,8 @@ function detectSkills(teamDir) {
|
|
|
118
118
|
return entries;
|
|
119
119
|
}
|
|
120
120
|
const MCP_KEYWORDS = ['mcp', 'supabase', 'github', 'slack', 'notion', 'linear', 'jira', 'figma', 'stripe', 'openai', 'anthropic', 'postgres', 'mysql', 'redis', 'mongodb', 'firebase', 'aws', 'gcp', 'azure', 'vercel', 'netlify', 'docker', 'kubernetes'];
|
|
121
|
-
function detectAgentDetails(
|
|
122
|
-
const agentsDir = path_1.default.join(
|
|
121
|
+
function detectAgentDetails(agentDir, requires) {
|
|
122
|
+
const agentsDir = path_1.default.join(agentDir, 'agents');
|
|
123
123
|
if (!fs_1.default.existsSync(agentsDir))
|
|
124
124
|
return [];
|
|
125
125
|
const mcpNames = new Set((requires?.mcp ?? []).map((m) => m.name.toLowerCase()));
|
|
@@ -168,8 +168,8 @@ function detectAgentDetails(teamDir, requires) {
|
|
|
168
168
|
return entries;
|
|
169
169
|
}
|
|
170
170
|
/**
|
|
171
|
-
*
|
|
172
|
-
* root SKILL.md를 대체하여
|
|
171
|
+
* 에이전트 진입점 커맨드(commands/{author}-{name}.md)를 생성한다.
|
|
172
|
+
* root SKILL.md를 대체하여 에이전트의 얼굴 역할을 한다.
|
|
173
173
|
*/
|
|
174
174
|
function generateEntryCommand(config, commands, skills, scopedSlug) {
|
|
175
175
|
const lines = [];
|
|
@@ -181,7 +181,7 @@ function generateEntryCommand(config, commands, skills, scopedSlug) {
|
|
|
181
181
|
// Preamble
|
|
182
182
|
lines.push((0, preamble_js_1.generatePreamble)(scopedSlug));
|
|
183
183
|
lines.push('');
|
|
184
|
-
//
|
|
184
|
+
// Agent header
|
|
185
185
|
lines.push(`## ${config.name}`);
|
|
186
186
|
lines.push('');
|
|
187
187
|
lines.push(`v${config.version} — ${scopedSlug}`);
|
|
@@ -210,14 +210,14 @@ function generateEntryCommand(config, commands, skills, scopedSlug) {
|
|
|
210
210
|
lines.push('');
|
|
211
211
|
return lines.join('\n');
|
|
212
212
|
}
|
|
213
|
-
function countDir(
|
|
214
|
-
const dirPath = path_1.default.join(
|
|
213
|
+
function countDir(agentDir, dirName) {
|
|
214
|
+
const dirPath = path_1.default.join(agentDir, dirName);
|
|
215
215
|
if (!fs_1.default.existsSync(dirPath))
|
|
216
216
|
return 0;
|
|
217
217
|
return fs_1.default.readdirSync(dirPath).filter((f) => !f.startsWith('.')).length;
|
|
218
218
|
}
|
|
219
|
-
function listDir(
|
|
220
|
-
const dirPath = path_1.default.join(
|
|
219
|
+
function listDir(agentDir, dirName) {
|
|
220
|
+
const dirPath = path_1.default.join(agentDir, dirName);
|
|
221
221
|
if (!fs_1.default.existsSync(dirPath))
|
|
222
222
|
return [];
|
|
223
223
|
return fs_1.default.readdirSync(dirPath).filter((f) => !f.startsWith('.'));
|
|
@@ -227,10 +227,10 @@ function listDir(teamDir, dirName) {
|
|
|
227
227
|
* 1. relay.yaml에 있으면 사용
|
|
228
228
|
* 2. README.md가 있으면 fallback
|
|
229
229
|
*/
|
|
230
|
-
function resolveLongDescription(
|
|
230
|
+
function resolveLongDescription(agentDir, yamlValue) {
|
|
231
231
|
if (yamlValue)
|
|
232
232
|
return yamlValue;
|
|
233
|
-
const readmePath = path_1.default.join(
|
|
233
|
+
const readmePath = path_1.default.join(agentDir, 'README.md');
|
|
234
234
|
if (fs_1.default.existsSync(readmePath)) {
|
|
235
235
|
try {
|
|
236
236
|
return fs_1.default.readFileSync(readmePath, 'utf-8').trim() || undefined;
|
|
@@ -241,21 +241,21 @@ function resolveLongDescription(teamDir, yamlValue) {
|
|
|
241
241
|
}
|
|
242
242
|
return undefined;
|
|
243
243
|
}
|
|
244
|
-
async function createTarball(
|
|
244
|
+
async function createTarball(agentDir) {
|
|
245
245
|
const tmpFile = path_1.default.join(os_1.default.tmpdir(), `relay-publish-${Date.now()}.tar.gz`);
|
|
246
|
-
const dirsToInclude = VALID_DIRS.filter((d) => fs_1.default.existsSync(path_1.default.join(
|
|
246
|
+
const dirsToInclude = VALID_DIRS.filter((d) => fs_1.default.existsSync(path_1.default.join(agentDir, d)));
|
|
247
247
|
// Include root-level files if they exist
|
|
248
248
|
const entries = [...dirsToInclude];
|
|
249
249
|
const rootFiles = ['relay.yaml', 'SKILL.md', 'guide.md'];
|
|
250
250
|
for (const file of rootFiles) {
|
|
251
|
-
if (fs_1.default.existsSync(path_1.default.join(
|
|
251
|
+
if (fs_1.default.existsSync(path_1.default.join(agentDir, file))) {
|
|
252
252
|
entries.push(file);
|
|
253
253
|
}
|
|
254
254
|
}
|
|
255
255
|
await (0, tar_1.create)({
|
|
256
256
|
gzip: true,
|
|
257
257
|
file: tmpFile,
|
|
258
|
-
cwd:
|
|
258
|
+
cwd: agentDir,
|
|
259
259
|
}, entries);
|
|
260
260
|
return tmpFile;
|
|
261
261
|
}
|
|
@@ -281,14 +281,14 @@ async function publishToApi(token, tarPath, metadata) {
|
|
|
281
281
|
function registerPublish(program) {
|
|
282
282
|
program
|
|
283
283
|
.command('publish')
|
|
284
|
-
.description('현재
|
|
284
|
+
.description('현재 에이전트 패키지를 Space에 배포합니다 (relay.yaml 필요)')
|
|
285
285
|
.option('--token <token>', '인증 토큰')
|
|
286
286
|
.option('--space <slug>', '배포할 Space 지정')
|
|
287
287
|
.option('--version <version>', '배포 버전 지정 (relay.yaml 업데이트)')
|
|
288
288
|
.action(async (opts) => {
|
|
289
289
|
const json = program.opts().json ?? false;
|
|
290
|
-
const
|
|
291
|
-
const relayDir = path_1.default.join(
|
|
290
|
+
const agentDir = process.cwd();
|
|
291
|
+
const relayDir = path_1.default.join(agentDir, '.relay');
|
|
292
292
|
const relayYamlPath = path_1.default.join(relayDir, 'relay.yaml');
|
|
293
293
|
const isTTY = Boolean(process.stdin.isTTY) && !json;
|
|
294
294
|
// CLI update check before publish
|
|
@@ -327,12 +327,12 @@ function registerPublish(program) {
|
|
|
327
327
|
}
|
|
328
328
|
// Interactive onboarding: create relay.yaml
|
|
329
329
|
const { input: promptInput, select: promptSelect } = await import('@inquirer/prompts');
|
|
330
|
-
const dirName = path_1.default.basename(
|
|
330
|
+
const dirName = path_1.default.basename(agentDir);
|
|
331
331
|
const defaultSlug = dirName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '');
|
|
332
|
-
console.error('\n\x1b[36m릴레이
|
|
332
|
+
console.error('\n\x1b[36m릴레이 에이전트 패키지를 초기화합니다.\x1b[0m');
|
|
333
333
|
console.error('.relay/relay.yaml을 생성하기 위해 몇 가지 정보를 입력해주세요.\n');
|
|
334
334
|
const name = await promptInput({
|
|
335
|
-
message: '
|
|
335
|
+
message: '에이전트 이름:',
|
|
336
336
|
default: dirName,
|
|
337
337
|
});
|
|
338
338
|
const slug = await promptInput({
|
|
@@ -340,7 +340,7 @@ function registerPublish(program) {
|
|
|
340
340
|
default: defaultSlug,
|
|
341
341
|
});
|
|
342
342
|
const description = await promptInput({
|
|
343
|
-
message: '
|
|
343
|
+
message: '에이전트 설명 (필수):',
|
|
344
344
|
validate: (v) => v.trim().length > 0 ? true : '설명을 입력해주세요.',
|
|
345
345
|
});
|
|
346
346
|
const tagsRaw = await promptInput({
|
|
@@ -351,16 +351,16 @@ function registerPublish(program) {
|
|
|
351
351
|
message: '공개 범위:',
|
|
352
352
|
choices: [
|
|
353
353
|
{ name: '공개 — 누구나 설치', value: 'public' },
|
|
354
|
-
{ name: '링크 공유 — 접근 링크가 있는 사람만 설치', value: '
|
|
355
|
-
{ name: '비공개 —
|
|
354
|
+
{ name: '링크 공유 — 접근 링크가 있는 사람만 설치', value: 'private' },
|
|
355
|
+
{ name: '비공개 — Org 멤버만', value: 'internal' },
|
|
356
356
|
],
|
|
357
357
|
});
|
|
358
358
|
console.error('\n\x1b[2m💡 프로필에 연락처를 설정하면 설치 시 명함이 전달됩니다: www.relayax.com/dashboard/profile\x1b[0m');
|
|
359
|
-
if (visibility === '
|
|
360
|
-
console.error('\x1b[2m💡 링크 공유
|
|
359
|
+
if (visibility === 'private') {
|
|
360
|
+
console.error('\x1b[2m💡 링크 공유 에이전트는 웹 대시보드에서 접근 링크와 구매 안내를 설정하세요: www.relayax.com/dashboard\x1b[0m');
|
|
361
361
|
}
|
|
362
|
-
else if (visibility === '
|
|
363
|
-
console.error('\x1b[2m💡 비공개
|
|
362
|
+
else if (visibility === 'internal') {
|
|
363
|
+
console.error('\x1b[2m💡 비공개 에이전트는 Org를 통해 멤버를 관리하세요: www.relayax.com/dashboard/agents\x1b[0m');
|
|
364
364
|
}
|
|
365
365
|
console.error('');
|
|
366
366
|
const tags = tagsRaw
|
|
@@ -445,90 +445,84 @@ function registerPublish(program) {
|
|
|
445
445
|
}));
|
|
446
446
|
process.exit(1);
|
|
447
447
|
}
|
|
448
|
-
// Fetch user's
|
|
449
|
-
let
|
|
450
|
-
let
|
|
451
|
-
let selectedJoinPolicy;
|
|
448
|
+
// Fetch user's Orgs and select publish target
|
|
449
|
+
let selectedOrgId;
|
|
450
|
+
let selectedOrgSlug;
|
|
452
451
|
try {
|
|
453
|
-
const {
|
|
454
|
-
const
|
|
455
|
-
// --space flag: resolve
|
|
452
|
+
const { fetchMyOrgs } = await import('./orgs.js');
|
|
453
|
+
const orgs = await fetchMyOrgs(token);
|
|
454
|
+
// --space flag (legacy alias for --org): resolve Org by slug
|
|
456
455
|
if (opts.space) {
|
|
457
|
-
const matched =
|
|
456
|
+
const matched = orgs.find((o) => o.slug === opts.space);
|
|
458
457
|
if (matched) {
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
selectedJoinPolicy = matched.join_policy;
|
|
458
|
+
selectedOrgId = matched.id;
|
|
459
|
+
selectedOrgSlug = matched.slug;
|
|
462
460
|
}
|
|
463
461
|
else {
|
|
464
462
|
if (json) {
|
|
465
463
|
console.error(JSON.stringify({
|
|
466
|
-
error: '
|
|
467
|
-
message: `
|
|
468
|
-
fix: `사용 가능한
|
|
469
|
-
options:
|
|
464
|
+
error: 'INVALID_ORG',
|
|
465
|
+
message: `Organization '${opts.space}'를 찾을 수 없습니다.`,
|
|
466
|
+
fix: `사용 가능한 Org: ${orgs.map((o) => o.slug).join(', ')}`,
|
|
467
|
+
options: orgs.map((o) => ({ value: o.slug, label: `${o.name} (${o.slug})` })),
|
|
470
468
|
}));
|
|
471
469
|
}
|
|
472
470
|
else {
|
|
473
|
-
console.error(`
|
|
471
|
+
console.error(`Organization '${opts.space}'를 찾을 수 없습니다.`);
|
|
474
472
|
}
|
|
475
473
|
process.exit(1);
|
|
476
474
|
}
|
|
477
475
|
}
|
|
478
476
|
else if (isTTY) {
|
|
479
|
-
if (
|
|
480
|
-
// No
|
|
481
|
-
console.error('\x1b[33m⚠ 소속
|
|
477
|
+
if (orgs.length === 0) {
|
|
478
|
+
// No orgs — publish without org_id
|
|
479
|
+
console.error('\x1b[33m⚠ 소속 Organization이 없습니다. 개인 계정으로 배포합니다.\x1b[0m\n');
|
|
482
480
|
}
|
|
483
|
-
else if (
|
|
484
|
-
// Only one
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
console.error(`\x1b[2m Space: ${spaces[0].name} (${spaces[0].slug})\x1b[0m\n`);
|
|
481
|
+
else if (orgs.length === 1) {
|
|
482
|
+
// Only one Org — auto-select
|
|
483
|
+
selectedOrgId = orgs[0].id;
|
|
484
|
+
selectedOrgSlug = orgs[0].slug;
|
|
485
|
+
console.error(`\x1b[2m Organization: ${orgs[0].name} (${orgs[0].slug})\x1b[0m\n`);
|
|
489
486
|
}
|
|
490
487
|
else {
|
|
491
|
-
// Multiple
|
|
492
|
-
const { select:
|
|
493
|
-
const
|
|
494
|
-
name: `${
|
|
495
|
-
value:
|
|
496
|
-
slug:
|
|
497
|
-
join_policy: s.join_policy,
|
|
488
|
+
// Multiple orgs — prompt user
|
|
489
|
+
const { select: selectOrg } = await import('@inquirer/prompts');
|
|
490
|
+
const orgChoices = orgs.map((o) => ({
|
|
491
|
+
name: `${o.name} (${o.slug})`,
|
|
492
|
+
value: o.id,
|
|
493
|
+
slug: o.slug,
|
|
498
494
|
}));
|
|
499
|
-
const chosenId = await
|
|
500
|
-
message: '어떤
|
|
501
|
-
choices:
|
|
495
|
+
const chosenId = await selectOrg({
|
|
496
|
+
message: '어떤 Organization에 배포할까요?',
|
|
497
|
+
choices: orgChoices.map((c) => ({ name: c.name, value: c.value })),
|
|
502
498
|
});
|
|
503
|
-
const chosen =
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
selectedJoinPolicy = chosen?.join_policy;
|
|
499
|
+
const chosen = orgChoices.find((c) => c.value === chosenId);
|
|
500
|
+
selectedOrgId = chosenId;
|
|
501
|
+
selectedOrgSlug = chosen?.slug;
|
|
507
502
|
const chosenLabel = chosen?.name ?? chosenId;
|
|
508
|
-
console.error(` →
|
|
503
|
+
console.error(` → Organization: ${chosenLabel}\n`);
|
|
509
504
|
}
|
|
510
505
|
}
|
|
511
|
-
else if (
|
|
512
|
-
// --json 모드 + 여러
|
|
506
|
+
else if (orgs.length > 1 && json) {
|
|
507
|
+
// --json 모드 + 여러 Org: 에이전트가 선택할 수 있도록 에러 반환
|
|
513
508
|
console.error(JSON.stringify({
|
|
514
|
-
error: '
|
|
515
|
-
message: '배포할
|
|
516
|
-
fix: `relay publish --
|
|
517
|
-
options:
|
|
509
|
+
error: 'MISSING_ORG',
|
|
510
|
+
message: '배포할 Organization을 선택하세요.',
|
|
511
|
+
fix: `relay publish --org <slug> --json`,
|
|
512
|
+
options: orgs.map((o) => ({ value: o.slug, label: `${o.name} (${o.slug})` })),
|
|
518
513
|
}));
|
|
519
514
|
process.exit(1);
|
|
520
515
|
}
|
|
521
|
-
else if (
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
selectedJoinPolicy = spaces[0].join_policy;
|
|
516
|
+
else if (orgs.length > 0) {
|
|
517
|
+
selectedOrgId = orgs[0].id;
|
|
518
|
+
selectedOrgSlug = orgs[0].slug;
|
|
525
519
|
}
|
|
526
520
|
}
|
|
527
521
|
catch {
|
|
528
|
-
//
|
|
522
|
+
// Org 조회 실패 시 무시하고 계속 진행
|
|
529
523
|
}
|
|
530
|
-
// Visibility default
|
|
531
|
-
const defaultVisibility =
|
|
524
|
+
// Visibility default
|
|
525
|
+
const defaultVisibility = 'public';
|
|
532
526
|
// Visibility validation: must be explicitly set
|
|
533
527
|
if (!config.visibility) {
|
|
534
528
|
if (isTTY) {
|
|
@@ -543,11 +537,11 @@ function registerPublish(program) {
|
|
|
543
537
|
},
|
|
544
538
|
{
|
|
545
539
|
name: '링크 공유 — 접근 링크가 있는 사람만 설치',
|
|
546
|
-
value: '
|
|
540
|
+
value: 'private',
|
|
547
541
|
},
|
|
548
542
|
{
|
|
549
|
-
name: `비공개 —
|
|
550
|
-
value: '
|
|
543
|
+
name: `비공개 — Org 멤버만 접근`,
|
|
544
|
+
value: 'internal',
|
|
551
545
|
},
|
|
552
546
|
],
|
|
553
547
|
default: defaultVisibility,
|
|
@@ -564,8 +558,8 @@ function registerPublish(program) {
|
|
|
564
558
|
message: 'relay.yaml에 visibility를 설정해주세요.',
|
|
565
559
|
options: [
|
|
566
560
|
{ value: 'public', label: '공개 — 누구나 설치' },
|
|
567
|
-
{ value: '
|
|
568
|
-
{ value: '
|
|
561
|
+
{ value: 'private', label: '링크 공유 — 접근 링크가 있는 사람만 설치' },
|
|
562
|
+
{ value: 'internal', label: '비공개 — Org 멤버만 접근' },
|
|
569
563
|
],
|
|
570
564
|
fix: 'relay.yaml의 visibility 필드를 위 옵션 중 하나로 설정하세요.',
|
|
571
565
|
}));
|
|
@@ -577,8 +571,8 @@ function registerPublish(program) {
|
|
|
577
571
|
const { select: promptConfirmVis } = await import('@inquirer/prompts');
|
|
578
572
|
const visLabelMap = {
|
|
579
573
|
public: '공개',
|
|
580
|
-
|
|
581
|
-
|
|
574
|
+
private: '링크공유',
|
|
575
|
+
internal: '비공개',
|
|
582
576
|
};
|
|
583
577
|
const currentVisLabel = visLabelMap[config.visibility ?? 'public'] ?? config.visibility;
|
|
584
578
|
const newVisibility = await promptConfirmVis({
|
|
@@ -590,11 +584,11 @@ function registerPublish(program) {
|
|
|
590
584
|
},
|
|
591
585
|
{
|
|
592
586
|
name: '링크공유 — 접근 링크가 있는 사람만 설치',
|
|
593
|
-
value: '
|
|
587
|
+
value: 'private',
|
|
594
588
|
},
|
|
595
589
|
{
|
|
596
|
-
name: `비공개 —
|
|
597
|
-
value: '
|
|
590
|
+
name: `비공개 — Org 멤버만 접근`,
|
|
591
|
+
value: 'internal',
|
|
598
592
|
},
|
|
599
593
|
],
|
|
600
594
|
default: config.visibility ?? defaultVisibility,
|
|
@@ -638,8 +632,8 @@ function registerPublish(program) {
|
|
|
638
632
|
type: config.type ?? 'hybrid',
|
|
639
633
|
agent_details: detectedAgents,
|
|
640
634
|
skill_details: detectedSkills,
|
|
641
|
-
...(
|
|
642
|
-
...(
|
|
635
|
+
...(selectedOrgId ? { org_id: selectedOrgId } : {}),
|
|
636
|
+
...(selectedOrgSlug ? { org_slug: selectedOrgSlug } : {}),
|
|
643
637
|
};
|
|
644
638
|
if (!json) {
|
|
645
639
|
console.error(`패키지 생성 중... (${config.name} v${config.version})`);
|
|
@@ -647,7 +641,7 @@ function registerPublish(program) {
|
|
|
647
641
|
// GUIDE.html deprecation warning
|
|
648
642
|
if (fs_1.default.existsSync(path_1.default.join(relayDir, 'GUIDE.html'))) {
|
|
649
643
|
console.error('\x1b[33m⚠ GUIDE.html은 더 이상 지원되지 않습니다. 상세페이지가 가이드 역할을 합니다.\x1b[0m');
|
|
650
|
-
console.error(' long_description을 활용하거나 relayax.com에서
|
|
644
|
+
console.error(' long_description을 활용하거나 relayax.com에서 에이전트 정보를 편집하세요.\n');
|
|
651
645
|
}
|
|
652
646
|
// Generate guide.md (consumer install guide)
|
|
653
647
|
const { generateGuide } = await import('../lib/guide.js');
|
|
@@ -717,7 +711,10 @@ function registerPublish(program) {
|
|
|
717
711
|
// Show shareable onboarding guide as a plain copyable block
|
|
718
712
|
if (isTTY) {
|
|
719
713
|
const detailSlug = result.slug.startsWith('@') ? result.slug.slice(1) : result.slug;
|
|
720
|
-
const
|
|
714
|
+
const accessCode = result.access_code;
|
|
715
|
+
const guideUrl = accessCode
|
|
716
|
+
? `https://relayax.com/api/registry/${detailSlug}/guide.md?code=${accessCode}`
|
|
717
|
+
: `https://relayax.com/api/registry/${detailSlug}/guide.md`;
|
|
721
718
|
console.log(`\n \x1b[90m주변인에게 공유하세요:\x1b[0m\n`);
|
|
722
719
|
console.log('```');
|
|
723
720
|
console.log('아래 가이드를 읽고 그대로 따라해줘:');
|
|
@@ -725,9 +722,14 @@ function registerPublish(program) {
|
|
|
725
722
|
console.log('```');
|
|
726
723
|
console.log(`\n \x1b[90mCLI 설치된 사용자용 (짧은 버전):\x1b[0m`);
|
|
727
724
|
console.log(` /relay:relay-install ${result.slug}`);
|
|
728
|
-
if (config.visibility
|
|
725
|
+
if (config.visibility === 'private') {
|
|
726
|
+
console.log(`\n \x1b[90mprivate 에이전트:\x1b[0m`);
|
|
727
|
+
console.log(` 접근 링크를 생성한 뒤 guide.md?code={agent_code}로 공유하세요.`);
|
|
728
|
+
console.log(` 접근 링크 관리: \x1b[36mrelayax.com/dashboard/agent-access/${config.slug}\x1b[0m`);
|
|
729
|
+
}
|
|
730
|
+
else if (config.visibility !== 'internal') {
|
|
729
731
|
console.log(`\n \x1b[90m유료 판매하려면:\x1b[0m`);
|
|
730
|
-
console.log(` 1. 가시성을 "
|
|
732
|
+
console.log(` 1. 가시성을 "private"로 변경: \x1b[36mrelayax.com/dashboard\x1b[0m`);
|
|
731
733
|
console.log(` 2. API 키 발급: \x1b[36mrelayax.com/dashboard/keys\x1b[0m`);
|
|
732
734
|
console.log(` 3. 웹훅 연동 가이드: \x1b[36mrelayax.com/docs/webhook-guide.md\x1b[0m`);
|
|
733
735
|
}
|
package/dist/commands/search.js
CHANGED
|
@@ -26,13 +26,13 @@ function formatTable(results) {
|
|
|
26
26
|
function registerSearch(program) {
|
|
27
27
|
program
|
|
28
28
|
.command('search <keyword>')
|
|
29
|
-
.description('Space에서 에이전트
|
|
29
|
+
.description('Space에서 에이전트 검색 (공개 에이전트 + 내 Space 에이전트)')
|
|
30
30
|
.option('--tag <tag>', '태그로 필터링')
|
|
31
31
|
.option('--space <space>', '특정 Space 내에서 검색')
|
|
32
32
|
.action(async (keyword, opts) => {
|
|
33
33
|
const json = program.opts().json ?? false;
|
|
34
34
|
try {
|
|
35
|
-
const results = await (0, api_js_1.
|
|
35
|
+
const results = await (0, api_js_1.searchAgents)(keyword, opts.tag);
|
|
36
36
|
if (json) {
|
|
37
37
|
console.log(JSON.stringify({ results }));
|
|
38
38
|
}
|
package/dist/commands/status.js
CHANGED
|
@@ -47,27 +47,27 @@ function registerStatus(program) {
|
|
|
47
47
|
const localDir = path_1.default.join(projectPath, primaryAgent.skillsDir, 'commands', 'relay');
|
|
48
48
|
hasLocal = command_adapter_js_1.BUILDER_COMMANDS.some((cmd) => fs_1.default.existsSync(path_1.default.join(localDir, `${cmd.id}.md`)));
|
|
49
49
|
}
|
|
50
|
-
// 3.
|
|
50
|
+
// 3. 에이전트 프로젝트 정보
|
|
51
51
|
const relayYamlPath = path_1.default.join(projectPath, '.relay', 'relay.yaml');
|
|
52
|
-
let
|
|
52
|
+
let project = null;
|
|
53
53
|
if (fs_1.default.existsSync(relayYamlPath)) {
|
|
54
54
|
try {
|
|
55
55
|
const yaml = await import('js-yaml');
|
|
56
56
|
const content = fs_1.default.readFileSync(relayYamlPath, 'utf-8');
|
|
57
57
|
const raw = yaml.load(content);
|
|
58
|
-
|
|
59
|
-
|
|
58
|
+
project = {
|
|
59
|
+
is_agent: true,
|
|
60
60
|
name: String(raw.name ?? ''),
|
|
61
61
|
slug: String(raw.slug ?? ''),
|
|
62
62
|
version: String(raw.version ?? ''),
|
|
63
63
|
};
|
|
64
64
|
}
|
|
65
65
|
catch {
|
|
66
|
-
|
|
66
|
+
project = { is_agent: true };
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
else {
|
|
70
|
-
|
|
70
|
+
project = { is_agent: false };
|
|
71
71
|
}
|
|
72
72
|
// 4. 출력
|
|
73
73
|
if (json) {
|
|
@@ -78,7 +78,7 @@ function registerStatus(program) {
|
|
|
78
78
|
global_commands: hasGlobal,
|
|
79
79
|
local_commands: hasLocal,
|
|
80
80
|
},
|
|
81
|
-
|
|
81
|
+
project,
|
|
82
82
|
};
|
|
83
83
|
console.log(JSON.stringify(result));
|
|
84
84
|
}
|
|
@@ -103,12 +103,12 @@ function registerStatus(program) {
|
|
|
103
103
|
else {
|
|
104
104
|
console.log(` \x1b[31m✗\x1b[0m 에이전트: 감지 안 됨`);
|
|
105
105
|
}
|
|
106
|
-
//
|
|
107
|
-
if (
|
|
108
|
-
console.log(` \x1b[32m✓\x1b[0m 현재
|
|
106
|
+
// 에이전트 프로젝트
|
|
107
|
+
if (project?.is_agent && project.name) {
|
|
108
|
+
console.log(` \x1b[32m✓\x1b[0m 현재 에이전트: \x1b[36m${project.name}\x1b[0m v${project.version}`);
|
|
109
109
|
}
|
|
110
110
|
else {
|
|
111
|
-
console.log(` \x1b[2m—\x1b[0m 현재 프로젝트:
|
|
111
|
+
console.log(` \x1b[2m—\x1b[0m 현재 프로젝트: 에이전트 아님`);
|
|
112
112
|
}
|
|
113
113
|
console.log('');
|
|
114
114
|
}
|
|
@@ -12,12 +12,12 @@ const slug_js_1 = require("../lib/slug.js");
|
|
|
12
12
|
function registerUninstall(program) {
|
|
13
13
|
program
|
|
14
14
|
.command('uninstall <slug>')
|
|
15
|
-
.description('에이전트
|
|
15
|
+
.description('에이전트 제거')
|
|
16
16
|
.action((slugInput) => {
|
|
17
17
|
const json = program.opts().json ?? false;
|
|
18
18
|
const localInstalled = (0, config_js_1.loadInstalled)();
|
|
19
19
|
const globalInstalled = (0, config_js_1.loadGlobalInstalled)();
|
|
20
|
-
// Resolve slug — support short names like "cardnews-
|
|
20
|
+
// Resolve slug — support short names like "cardnews-agent"
|
|
21
21
|
let slug;
|
|
22
22
|
if ((0, slug_js_1.isScopedSlug)(slugInput)) {
|
|
23
23
|
slug = slugInput;
|
|
@@ -45,11 +45,11 @@ function registerUninstall(program) {
|
|
|
45
45
|
let totalRemoved = 0;
|
|
46
46
|
// Remove from local registry
|
|
47
47
|
if (localEntry) {
|
|
48
|
-
const removed = (0, installer_js_1.
|
|
48
|
+
const removed = (0, installer_js_1.uninstallAgent)(localEntry.files);
|
|
49
49
|
totalRemoved += removed.length;
|
|
50
50
|
// Remove deployed files
|
|
51
51
|
if (localEntry.deployed_files && localEntry.deployed_files.length > 0) {
|
|
52
|
-
const deployedRemoved = (0, installer_js_1.
|
|
52
|
+
const deployedRemoved = (0, installer_js_1.uninstallAgent)(localEntry.deployed_files);
|
|
53
53
|
totalRemoved += deployedRemoved.length;
|
|
54
54
|
// Clean empty parent directories
|
|
55
55
|
const boundary = path_1.default.join(process.cwd(), '.claude');
|
|
@@ -64,12 +64,12 @@ function registerUninstall(program) {
|
|
|
64
64
|
if (globalEntry) {
|
|
65
65
|
// Only remove files if not already handled by local entry
|
|
66
66
|
if (!localEntry) {
|
|
67
|
-
const removed = (0, installer_js_1.
|
|
67
|
+
const removed = (0, installer_js_1.uninstallAgent)(globalEntry.files);
|
|
68
68
|
totalRemoved += removed.length;
|
|
69
69
|
}
|
|
70
70
|
// Remove globally deployed files
|
|
71
71
|
if (globalEntry.deployed_files && globalEntry.deployed_files.length > 0) {
|
|
72
|
-
const deployedRemoved = (0, installer_js_1.
|
|
72
|
+
const deployedRemoved = (0, installer_js_1.uninstallAgent)(globalEntry.deployed_files);
|
|
73
73
|
totalRemoved += deployedRemoved.length;
|
|
74
74
|
// Clean empty parent directories
|
|
75
75
|
const boundary = path_1.default.join(os_1.default.homedir(), '.claude');
|
|
@@ -82,7 +82,7 @@ function registerUninstall(program) {
|
|
|
82
82
|
}
|
|
83
83
|
const result = {
|
|
84
84
|
status: 'ok',
|
|
85
|
-
|
|
85
|
+
agent: slug,
|
|
86
86
|
files_removed: totalRemoved,
|
|
87
87
|
};
|
|
88
88
|
if (json) {
|