anpm-io 1.0.0
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 +174 -0
- package/dist/commands/access.d.ts +2 -0
- package/dist/commands/access.js +90 -0
- package/dist/commands/adopt.d.ts +2 -0
- package/dist/commands/adopt.js +177 -0
- package/dist/commands/changelog.d.ts +2 -0
- package/dist/commands/changelog.js +67 -0
- package/dist/commands/check-update.d.ts +2 -0
- package/dist/commands/check-update.js +76 -0
- package/dist/commands/config.d.ts +2 -0
- package/dist/commands/config.js +84 -0
- package/dist/commands/create.d.ts +2 -0
- package/dist/commands/create.js +227 -0
- package/dist/commands/deploy-record.d.ts +2 -0
- package/dist/commands/deploy-record.js +93 -0
- package/dist/commands/deploy.d.ts +2 -0
- package/dist/commands/deploy.js +284 -0
- package/dist/commands/diff.d.ts +2 -0
- package/dist/commands/diff.js +92 -0
- package/dist/commands/feedback.d.ts +2 -0
- package/dist/commands/feedback.js +71 -0
- package/dist/commands/grant.d.ts +33 -0
- package/dist/commands/grant.js +190 -0
- package/dist/commands/hub.d.ts +2 -0
- package/dist/commands/hub.js +171 -0
- package/dist/commands/init.d.ts +13 -0
- package/dist/commands/init.js +172 -0
- package/dist/commands/install.d.ts +2 -0
- package/dist/commands/install.js +626 -0
- package/dist/commands/join.d.ts +6 -0
- package/dist/commands/join.js +90 -0
- package/dist/commands/link.d.ts +2 -0
- package/dist/commands/link.js +112 -0
- package/dist/commands/list.d.ts +2 -0
- package/dist/commands/list.js +144 -0
- package/dist/commands/login.d.ts +7 -0
- package/dist/commands/login.js +235 -0
- package/dist/commands/orgs.d.ts +10 -0
- package/dist/commands/orgs.js +128 -0
- package/dist/commands/outdated.d.ts +2 -0
- package/dist/commands/outdated.js +70 -0
- package/dist/commands/package.d.ts +57 -0
- package/dist/commands/package.js +569 -0
- package/dist/commands/ping.d.ts +2 -0
- package/dist/commands/ping.js +40 -0
- package/dist/commands/publish.d.ts +98 -0
- package/dist/commands/publish.js +899 -0
- package/dist/commands/run.d.ts +2 -0
- package/dist/commands/run.js +249 -0
- package/dist/commands/search.d.ts +2 -0
- package/dist/commands/search.js +57 -0
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.js +159 -0
- package/dist/commands/uninstall.d.ts +2 -0
- package/dist/commands/uninstall.js +132 -0
- package/dist/commands/update.d.ts +2 -0
- package/dist/commands/update.js +171 -0
- package/dist/commands/versions.d.ts +2 -0
- package/dist/commands/versions.js +44 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +91 -0
- package/dist/lib/agent-status.d.ts +23 -0
- package/dist/lib/agent-status.js +127 -0
- package/dist/lib/ai-tools.d.ts +34 -0
- package/dist/lib/ai-tools.js +104 -0
- package/dist/lib/anpm-config.d.ts +39 -0
- package/dist/lib/anpm-config.js +112 -0
- package/dist/lib/api.d.ts +24 -0
- package/dist/lib/api.js +151 -0
- package/dist/lib/auto-detect.d.ts +30 -0
- package/dist/lib/auto-detect.js +112 -0
- package/dist/lib/cloud-providers/anthropic.d.ts +19 -0
- package/dist/lib/cloud-providers/anthropic.js +200 -0
- package/dist/lib/cloud-providers/package-mapper.d.ts +9 -0
- package/dist/lib/cloud-providers/package-mapper.js +34 -0
- package/dist/lib/cloud-providers/provider.d.ts +60 -0
- package/dist/lib/cloud-providers/provider.js +7 -0
- package/dist/lib/command-adapter.d.ts +41 -0
- package/dist/lib/command-adapter.js +188 -0
- package/dist/lib/config.d.ts +50 -0
- package/dist/lib/config.js +274 -0
- package/dist/lib/contact-format.d.ts +7 -0
- package/dist/lib/contact-format.js +23 -0
- package/dist/lib/device-hash.d.ts +1 -0
- package/dist/lib/device-hash.js +16 -0
- package/dist/lib/error-report.d.ts +5 -0
- package/dist/lib/error-report.js +28 -0
- package/dist/lib/git-installer.d.ts +16 -0
- package/dist/lib/git-installer.js +97 -0
- package/dist/lib/git-operations.d.ts +38 -0
- package/dist/lib/git-operations.js +183 -0
- package/dist/lib/hub-notify.d.ts +9 -0
- package/dist/lib/hub-notify.js +66 -0
- package/dist/lib/install-source.d.ts +33 -0
- package/dist/lib/install-source.js +98 -0
- package/dist/lib/installer.d.ts +40 -0
- package/dist/lib/installer.js +358 -0
- package/dist/lib/local-installer.d.ts +15 -0
- package/dist/lib/local-installer.js +73 -0
- package/dist/lib/lockfile.d.ts +13 -0
- package/dist/lib/lockfile.js +42 -0
- package/dist/lib/manifest.d.ts +65 -0
- package/dist/lib/manifest.js +113 -0
- package/dist/lib/migration.d.ts +10 -0
- package/dist/lib/migration.js +91 -0
- package/dist/lib/paths.d.ts +10 -0
- package/dist/lib/paths.js +22 -0
- package/dist/lib/preamble.d.ts +22 -0
- package/dist/lib/preamble.js +133 -0
- package/dist/lib/relay-config.d.ts +13 -0
- package/dist/lib/relay-config.js +46 -0
- package/dist/lib/requires-suggest.d.ts +23 -0
- package/dist/lib/requires-suggest.js +295 -0
- package/dist/lib/setup-command.d.ts +6 -0
- package/dist/lib/setup-command.js +72 -0
- package/dist/lib/slug.d.ts +24 -0
- package/dist/lib/slug.js +100 -0
- package/dist/lib/step-tracker.d.ts +8 -0
- package/dist/lib/step-tracker.js +28 -0
- package/dist/lib/storage.d.ts +6 -0
- package/dist/lib/storage.js +23 -0
- package/dist/lib/update-cache.d.ts +2 -0
- package/dist/lib/update-cache.js +51 -0
- package/dist/lib/version-check.d.ts +10 -0
- package/dist/lib/version-check.js +75 -0
- package/dist/mcp/server.d.ts +3 -0
- package/dist/mcp/server.js +112 -0
- package/dist/postinstall.d.ts +8 -0
- package/dist/postinstall.js +41 -0
- package/dist/prompts/_error-handling.md +38 -0
- package/dist/prompts/create.md +170 -0
- package/dist/prompts/explore.md +30 -0
- package/dist/prompts/index.d.ts +3 -0
- package/dist/prompts/index.js +22 -0
- package/dist/relay-compat.d.ts +2 -0
- package/dist/relay-compat.js +7 -0
- package/dist/types.d.ts +118 -0
- package/dist/types.js +2 -0
- package/package.json +51 -0
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateSetupCommand = generateSetupCommand;
|
|
4
|
+
/**
|
|
5
|
+
* relay.yaml의 requires를 기반으로 setup slash command .md 내용을 생성한다.
|
|
6
|
+
* requires가 없거나 빈 객체이면 null을 반환한다.
|
|
7
|
+
*/
|
|
8
|
+
function generateSetupCommand(agentName, requires, mainCommandName) {
|
|
9
|
+
if (!requires)
|
|
10
|
+
return null;
|
|
11
|
+
const sections = [];
|
|
12
|
+
// runtime
|
|
13
|
+
if (requires.runtime) {
|
|
14
|
+
const items = [];
|
|
15
|
+
if (requires.runtime.node)
|
|
16
|
+
items.push(`- Node.js \`>=${requires.runtime.node}\``);
|
|
17
|
+
if (requires.runtime.python)
|
|
18
|
+
items.push(`- Python \`>=${requires.runtime.python}\``);
|
|
19
|
+
if (items.length > 0) {
|
|
20
|
+
sections.push(`### 런타임\n${items.join('\n')}`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
// cli
|
|
24
|
+
if (requires.cli && requires.cli.length > 0) {
|
|
25
|
+
const items = requires.cli.map((c) => {
|
|
26
|
+
const req = c.required !== false ? '필수' : '선택';
|
|
27
|
+
const install = c.install ? ` — 설치: \`${c.install}\`` : '';
|
|
28
|
+
return `- \`${c.name}\` (${req})${install}`;
|
|
29
|
+
});
|
|
30
|
+
sections.push(`### CLI 도구\n${items.join('\n')}`);
|
|
31
|
+
}
|
|
32
|
+
// env
|
|
33
|
+
if (requires.env && requires.env.length > 0) {
|
|
34
|
+
const items = requires.env.map((e) => {
|
|
35
|
+
const req = e.required !== false ? '필수' : '선택';
|
|
36
|
+
const desc = e.description ? ` — ${e.description}` : '';
|
|
37
|
+
const hint = e.setup_hint ? `\n 설정 방법:\n${e.setup_hint.split('\n').map((l) => ` ${l}`).join('\n')}` : '';
|
|
38
|
+
return `- \`${e.name}\` (${req})${desc}${hint}`;
|
|
39
|
+
});
|
|
40
|
+
sections.push(`### 환경변수\n${items.join('\n')}`);
|
|
41
|
+
}
|
|
42
|
+
// npm
|
|
43
|
+
if (requires.npm && requires.npm.length > 0) {
|
|
44
|
+
const items = requires.npm.map((n) => {
|
|
45
|
+
const name = typeof n === 'string' ? n : n.name;
|
|
46
|
+
const req = typeof n === 'string' ? '필수' : (n.required !== false ? '필수' : '선택');
|
|
47
|
+
return `- \`${name}\` (${req})`;
|
|
48
|
+
});
|
|
49
|
+
sections.push(`### npm 패키지\n${items.join('\n')}`);
|
|
50
|
+
}
|
|
51
|
+
// mcp
|
|
52
|
+
if (requires.mcp && requires.mcp.length > 0) {
|
|
53
|
+
const items = requires.mcp.map((m) => {
|
|
54
|
+
const req = m.required !== false ? '필수' : '선택';
|
|
55
|
+
const pkg = m.package ? ` — 패키지: \`${m.package}\`` : '';
|
|
56
|
+
const envList = m.env && m.env.length > 0 ? `\n 필요한 환경변수: ${m.env.map((e) => `\`${e}\``).join(', ')}` : '';
|
|
57
|
+
const config = m.config ? `\n 설정: \`${JSON.stringify(m.config)}\`` : '';
|
|
58
|
+
return `- \`${m.name}\` MCP 서버 (${req})${pkg}${envList}${config}`;
|
|
59
|
+
});
|
|
60
|
+
sections.push(`### MCP 서버\n${items.join('\n')}`);
|
|
61
|
+
}
|
|
62
|
+
if (sections.length === 0)
|
|
63
|
+
return null;
|
|
64
|
+
const body = `# ${agentName} 설정 가이드
|
|
65
|
+
|
|
66
|
+
아래 요구사항을 각각 체크하고, 미충족 항목이 있으면 사용자가 설정할 수 있도록 안내하세요.
|
|
67
|
+
모든 항목이 충족될 때까지 멈추지 말고 끝까지 진행하세요.
|
|
68
|
+
${mainCommandName ? `\n모든 설정이 완료되면 \`/${mainCommandName}\`으로 에이전트를 사용할 수 있다고 안내하세요.` : ''}
|
|
69
|
+
|
|
70
|
+
${sections.join('\n\n')}`;
|
|
71
|
+
return `---\ndescription: ${agentName} 설정 가이드 — 필수 요구사항을 확인하고 설정합니다\n---\n\n${body}\n`;
|
|
72
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface ParsedSlug {
|
|
2
|
+
owner: string;
|
|
3
|
+
name: string;
|
|
4
|
+
full: string;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* 임의의 문자열을 slug로 변환한다.
|
|
8
|
+
* 한글은 로마자로 변환된다 (예: "콘텐츠 에이전트" → "kontencheu-eijenteu").
|
|
9
|
+
*/
|
|
10
|
+
export declare function slugify(input: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Scoped slug(`@owner/name`)를 동기적으로 파싱한다.
|
|
13
|
+
* 단순 slug는 파싱할 수 없으므로 null을 반환한다.
|
|
14
|
+
*/
|
|
15
|
+
export declare function parseSlug(input: string): ParsedSlug | null;
|
|
16
|
+
/** input이 scoped slug인지 확인 */
|
|
17
|
+
export declare function isScopedSlug(input: string): boolean;
|
|
18
|
+
/** input이 단순 slug인지 확인 */
|
|
19
|
+
export declare function isSimpleSlug(input: string): boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Scoped 또는 단순 slug를 받아 ParsedSlug를 반환한다.
|
|
22
|
+
* 단순 slug는 서버에 resolve를 요청한다.
|
|
23
|
+
*/
|
|
24
|
+
export declare function resolveSlug(input: string): Promise<ParsedSlug>;
|
package/dist/lib/slug.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.slugify = slugify;
|
|
4
|
+
exports.parseSlug = parseSlug;
|
|
5
|
+
exports.isScopedSlug = isScopedSlug;
|
|
6
|
+
exports.isSimpleSlug = isSimpleSlug;
|
|
7
|
+
exports.resolveSlug = resolveSlug;
|
|
8
|
+
const api_js_1 = require("./api.js");
|
|
9
|
+
const SCOPED_SLUG_RE = /^@([a-z0-9](?:[a-z0-9-]*[a-z0-9])?)\/([a-z0-9](?:[a-z0-9-]*[a-z0-9])?)$/;
|
|
10
|
+
const SIMPLE_SLUG_RE = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
11
|
+
// ── 한글 로마자 변환 (Revised Romanization) ──
|
|
12
|
+
const INITIALS = [
|
|
13
|
+
'g', 'kk', 'n', 'd', 'tt', 'r', 'm', 'b', 'pp',
|
|
14
|
+
's', 'ss', '', 'j', 'jj', 'ch', 'k', 't', 'p', 'h',
|
|
15
|
+
];
|
|
16
|
+
const MEDIALS = [
|
|
17
|
+
'a', 'ae', 'ya', 'yae', 'eo', 'e', 'yeo', 'ye', 'o',
|
|
18
|
+
'wa', 'wae', 'oe', 'yo', 'u', 'wo', 'we', 'wi', 'yu',
|
|
19
|
+
'eu', 'ui', 'i',
|
|
20
|
+
];
|
|
21
|
+
const FINALS = [
|
|
22
|
+
'', 'k', 'k', 'k', 'n', 'n', 'n', 't', 'l',
|
|
23
|
+
'l', 'l', 'l', 'l', 'l', 'l', 'l', 'm', 'p',
|
|
24
|
+
'p', 't', 't', 'ng', 't', 't', 'k', 't', 'p', 't',
|
|
25
|
+
];
|
|
26
|
+
const HANGUL_BASE = 0xAC00;
|
|
27
|
+
function romanize(input) {
|
|
28
|
+
let result = '';
|
|
29
|
+
for (const ch of input) {
|
|
30
|
+
const code = ch.codePointAt(0);
|
|
31
|
+
if (code >= HANGUL_BASE && code < HANGUL_BASE + 11172) {
|
|
32
|
+
const offset = code - HANGUL_BASE;
|
|
33
|
+
const initial = Math.floor(offset / (21 * 28));
|
|
34
|
+
const medial = Math.floor((offset % (21 * 28)) / 28);
|
|
35
|
+
const final = offset % 28;
|
|
36
|
+
result += INITIALS[initial] + MEDIALS[medial] + FINALS[final];
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
result += ch;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 임의의 문자열을 slug로 변환한다.
|
|
46
|
+
* 한글은 로마자로 변환된다 (예: "콘텐츠 에이전트" → "kontencheu-eijenteu").
|
|
47
|
+
*/
|
|
48
|
+
function slugify(input) {
|
|
49
|
+
return romanize(input)
|
|
50
|
+
.toLowerCase()
|
|
51
|
+
.replace(/[^a-z0-9\s-]/g, '')
|
|
52
|
+
.trim()
|
|
53
|
+
.replace(/\s+/g, '-')
|
|
54
|
+
.replace(/-+/g, '-')
|
|
55
|
+
.slice(0, 50);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Scoped slug(`@owner/name`)를 동기적으로 파싱한다.
|
|
59
|
+
* 단순 slug는 파싱할 수 없으므로 null을 반환한다.
|
|
60
|
+
*/
|
|
61
|
+
function parseSlug(input) {
|
|
62
|
+
const m = input.match(SCOPED_SLUG_RE);
|
|
63
|
+
if (!m)
|
|
64
|
+
return null;
|
|
65
|
+
return { owner: m[1], name: m[2], full: input };
|
|
66
|
+
}
|
|
67
|
+
/** input이 scoped slug인지 확인 */
|
|
68
|
+
function isScopedSlug(input) {
|
|
69
|
+
return SCOPED_SLUG_RE.test(input);
|
|
70
|
+
}
|
|
71
|
+
/** input이 단순 slug인지 확인 */
|
|
72
|
+
function isSimpleSlug(input) {
|
|
73
|
+
return SIMPLE_SLUG_RE.test(input);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Scoped 또는 단순 slug를 받아 ParsedSlug를 반환한다.
|
|
77
|
+
* 단순 slug는 서버에 resolve를 요청한다.
|
|
78
|
+
*/
|
|
79
|
+
async function resolveSlug(input) {
|
|
80
|
+
// scoped slug면 바로 파싱
|
|
81
|
+
const parsed = parseSlug(input);
|
|
82
|
+
if (parsed)
|
|
83
|
+
return parsed;
|
|
84
|
+
// 단순 slug인지 검증
|
|
85
|
+
if (!isSimpleSlug(input)) {
|
|
86
|
+
throw new Error(`잘못된 slug 형식입니다: '${input}'. @owner/name 또는 name 형태로 입력하세요.`);
|
|
87
|
+
}
|
|
88
|
+
// 서버에 resolve 요청
|
|
89
|
+
const results = await (0, api_js_1.resolveSlugFromServer)(input);
|
|
90
|
+
if (results.length === 0) {
|
|
91
|
+
throw new Error(`'${input}' 에이전트를 찾을 수 없습니다.`);
|
|
92
|
+
}
|
|
93
|
+
if (results.length === 1) {
|
|
94
|
+
const r = results[0];
|
|
95
|
+
return { owner: r.owner, name: r.name, full: r.full };
|
|
96
|
+
}
|
|
97
|
+
// 여러 개 매칭
|
|
98
|
+
const list = results.map((r) => ` ${r.full}`).join('\n');
|
|
99
|
+
throw new Error(`'${input}'에 해당하는 에이전트가 여러 개입니다. 전체 slug를 지정해주세요:\n${list}`);
|
|
100
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.trackCommand = trackCommand;
|
|
4
|
+
const config_js_1 = require("./config.js");
|
|
5
|
+
const device_hash_js_1 = require("./device-hash.js");
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
7
|
+
const pkg = require('../../package.json');
|
|
8
|
+
/**
|
|
9
|
+
* CLI 명령 실행을 서버에 기록한다 (fire-and-forget).
|
|
10
|
+
* device_hash 기준으로 사용자 여정(login → create → publish)을 추적.
|
|
11
|
+
*/
|
|
12
|
+
function trackCommand(command, opts) {
|
|
13
|
+
const deviceHash = (0, device_hash_js_1.getDeviceHash)();
|
|
14
|
+
fetch(`${config_js_1.API_URL}/api/analytics/cli-commands`, {
|
|
15
|
+
method: 'POST',
|
|
16
|
+
headers: { 'Content-Type': 'application/json' },
|
|
17
|
+
body: JSON.stringify({
|
|
18
|
+
device_hash: deviceHash,
|
|
19
|
+
command,
|
|
20
|
+
slug: opts?.slug ?? null,
|
|
21
|
+
success: opts?.success ?? true,
|
|
22
|
+
cli_version: pkg.version,
|
|
23
|
+
}),
|
|
24
|
+
signal: AbortSignal.timeout(5000),
|
|
25
|
+
}).catch(() => {
|
|
26
|
+
// fire-and-forget
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function makeTempDir(): string;
|
|
2
|
+
export declare function removeTempDir(dir: string): void;
|
|
3
|
+
/**
|
|
4
|
+
* Clone an agent from git URL to destination directory.
|
|
5
|
+
*/
|
|
6
|
+
export declare function clonePackage(gitUrl: string, destDir: string, version?: string): Promise<void>;
|
|
@@ -0,0 +1,23 @@
|
|
|
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.makeTempDir = makeTempDir;
|
|
7
|
+
exports.removeTempDir = removeTempDir;
|
|
8
|
+
exports.clonePackage = clonePackage;
|
|
9
|
+
const fs_1 = __importDefault(require("fs"));
|
|
10
|
+
const os_1 = __importDefault(require("os"));
|
|
11
|
+
const git_operations_js_1 = require("./git-operations.js");
|
|
12
|
+
function makeTempDir() {
|
|
13
|
+
return fs_1.default.mkdtempSync(os_1.default.tmpdir() + '/relay-');
|
|
14
|
+
}
|
|
15
|
+
function removeTempDir(dir) {
|
|
16
|
+
fs_1.default.rmSync(dir, { recursive: true, force: true });
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Clone an agent from git URL to destination directory.
|
|
20
|
+
*/
|
|
21
|
+
async function clonePackage(gitUrl, destDir, version) {
|
|
22
|
+
await (0, git_operations_js_1.gitInstall)(gitUrl, destDir, version);
|
|
23
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.isCacheValid = isCacheValid;
|
|
7
|
+
exports.updateCacheTimestamp = updateCacheTimestamp;
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const os_1 = __importDefault(require("os"));
|
|
11
|
+
const RELAY_DIR = path_1.default.join(os_1.default.homedir(), '.relay');
|
|
12
|
+
const CACHE_FILE = path_1.default.join(RELAY_DIR, 'last-update-check');
|
|
13
|
+
const CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
|
|
14
|
+
function loadCache() {
|
|
15
|
+
try {
|
|
16
|
+
if (!fs_1.default.existsSync(CACHE_FILE))
|
|
17
|
+
return {};
|
|
18
|
+
return JSON.parse(fs_1.default.readFileSync(CACHE_FILE, 'utf-8'));
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return {};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function saveCache(data) {
|
|
25
|
+
if (!fs_1.default.existsSync(RELAY_DIR)) {
|
|
26
|
+
fs_1.default.mkdirSync(RELAY_DIR, { recursive: true });
|
|
27
|
+
}
|
|
28
|
+
fs_1.default.writeFileSync(CACHE_FILE, JSON.stringify(data, null, 2));
|
|
29
|
+
}
|
|
30
|
+
function isCacheValid(key, force) {
|
|
31
|
+
if (force)
|
|
32
|
+
return false;
|
|
33
|
+
const cache = loadCache();
|
|
34
|
+
const timestamp = key === 'cli' ? cache.cli : cache.agents?.[key];
|
|
35
|
+
if (!timestamp)
|
|
36
|
+
return false;
|
|
37
|
+
return Date.now() - new Date(timestamp).getTime() < CACHE_TTL_MS;
|
|
38
|
+
}
|
|
39
|
+
function updateCacheTimestamp(key) {
|
|
40
|
+
const cache = loadCache();
|
|
41
|
+
const now = new Date().toISOString();
|
|
42
|
+
if (key === 'cli') {
|
|
43
|
+
cache.cli = now;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
if (!cache.agents)
|
|
47
|
+
cache.agents = {};
|
|
48
|
+
cache.agents[key] = now;
|
|
49
|
+
}
|
|
50
|
+
saveCache(cache);
|
|
51
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface UpdateResult {
|
|
2
|
+
type: 'cli' | 'agent';
|
|
3
|
+
slug?: string;
|
|
4
|
+
current: string;
|
|
5
|
+
latest: string;
|
|
6
|
+
author?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function checkCliVersion(force?: boolean): Promise<UpdateResult | null>;
|
|
9
|
+
export declare function checkAgentVersion(slug: string, force?: boolean): Promise<UpdateResult | null>;
|
|
10
|
+
export declare function checkAllAgents(force?: boolean): Promise<UpdateResult[]>;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkCliVersion = checkCliVersion;
|
|
4
|
+
exports.checkAgentVersion = checkAgentVersion;
|
|
5
|
+
exports.checkAllAgents = checkAllAgents;
|
|
6
|
+
const config_js_1 = require("./config.js");
|
|
7
|
+
const api_js_1 = require("./api.js");
|
|
8
|
+
const update_cache_js_1 = require("./update-cache.js");
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
10
|
+
const pkg = require('../../package.json');
|
|
11
|
+
async function checkCliVersion(force) {
|
|
12
|
+
if ((0, update_cache_js_1.isCacheValid)('cli', force))
|
|
13
|
+
return null;
|
|
14
|
+
try {
|
|
15
|
+
const res = await fetch('https://registry.npmjs.org/relayax-cli/latest', {
|
|
16
|
+
signal: AbortSignal.timeout(3000),
|
|
17
|
+
});
|
|
18
|
+
if (!res.ok)
|
|
19
|
+
return null;
|
|
20
|
+
const data = (await res.json());
|
|
21
|
+
(0, update_cache_js_1.updateCacheTimestamp)('cli');
|
|
22
|
+
if (data.version !== pkg.version) {
|
|
23
|
+
return { type: 'cli', current: pkg.version, latest: data.version };
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// network error — silently skip
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
async function checkAgentVersion(slug, force) {
|
|
32
|
+
if ((0, update_cache_js_1.isCacheValid)(slug, force))
|
|
33
|
+
return null;
|
|
34
|
+
try {
|
|
35
|
+
const installed = (0, config_js_1.loadInstalled)();
|
|
36
|
+
const entry = installed[slug];
|
|
37
|
+
if (!entry?.version)
|
|
38
|
+
return null;
|
|
39
|
+
// system 타입(relay-core)은 CLI 버전 체크로 대체
|
|
40
|
+
if (entry.type === 'system') {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
const agent = await (0, api_js_1.fetchAgentInfo)(slug);
|
|
44
|
+
(0, update_cache_js_1.updateCacheTimestamp)(slug);
|
|
45
|
+
// Fire-and-forget usage ping (only when cache expired = actual API call happened)
|
|
46
|
+
const agentId = entry.agent_id ?? agent.id;
|
|
47
|
+
if (agentId) {
|
|
48
|
+
(0, api_js_1.sendUsagePing)(agentId, slug, entry.version);
|
|
49
|
+
}
|
|
50
|
+
if (agent.version !== entry.version) {
|
|
51
|
+
return {
|
|
52
|
+
type: 'agent',
|
|
53
|
+
slug,
|
|
54
|
+
current: entry.version,
|
|
55
|
+
latest: agent.version,
|
|
56
|
+
author: agent.author?.username,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// network error — silently skip
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
async function checkAllAgents(force) {
|
|
66
|
+
const installed = (0, config_js_1.loadInstalled)();
|
|
67
|
+
const slugs = Object.keys(installed);
|
|
68
|
+
const results = [];
|
|
69
|
+
for (const slug of slugs) {
|
|
70
|
+
const result = await checkAgentVersion(slug, force);
|
|
71
|
+
if (result)
|
|
72
|
+
results.push(result);
|
|
73
|
+
}
|
|
74
|
+
return results;
|
|
75
|
+
}
|
|
@@ -0,0 +1,112 @@
|
|
|
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.createMcpServer = createMcpServer;
|
|
7
|
+
exports.startMcpServer = startMcpServer;
|
|
8
|
+
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
9
|
+
const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
|
|
10
|
+
const zod_1 = require("zod");
|
|
11
|
+
const config_js_1 = require("../lib/config.js");
|
|
12
|
+
const fs_1 = __importDefault(require("fs"));
|
|
13
|
+
const path_1 = __importDefault(require("path"));
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
15
|
+
const pkg = require('../../package.json');
|
|
16
|
+
// ─── Helpers ───
|
|
17
|
+
function jsonText(obj) {
|
|
18
|
+
return { type: 'text', text: JSON.stringify(obj) };
|
|
19
|
+
}
|
|
20
|
+
// ─── Server ───
|
|
21
|
+
function createMcpServer() {
|
|
22
|
+
const server = new mcp_js_1.McpServer({ name: 'relay', version: pkg.version }, { capabilities: { tools: {} } });
|
|
23
|
+
// ═══ Detail Images — 에이전트 상세페이지 이미지 관리 ═══
|
|
24
|
+
server.tool('relay_detail_upload', '에이전트 상세페이지 이미지를 업로드합니다. 폴더 내 이미지를 파일명 순으로 정렬하여 업로드합니다 (기존 이미지 전체 교체).', {
|
|
25
|
+
slug: zod_1.z.string().describe('에이전트 slug'),
|
|
26
|
+
path: zod_1.z.string().describe('이미지가 있는 폴더 경로 (PNG/GIF/JPEG/WebP)'),
|
|
27
|
+
}, async ({ slug, path: dirPath }) => {
|
|
28
|
+
try {
|
|
29
|
+
const token = await (0, config_js_1.getValidToken)();
|
|
30
|
+
if (!token)
|
|
31
|
+
return { content: [jsonText({ error: 'LOGIN_REQUIRED', message: '로그인이 필요합니다.' })], isError: true };
|
|
32
|
+
const absPath = path_1.default.resolve(dirPath);
|
|
33
|
+
if (!fs_1.default.existsSync(absPath) || !fs_1.default.statSync(absPath).isDirectory()) {
|
|
34
|
+
return { content: [jsonText({ error: `폴더를 찾을 수 없습니다: ${absPath}` })], isError: true };
|
|
35
|
+
}
|
|
36
|
+
const imageExts = ['.png', '.jpg', '.jpeg', '.gif', '.webp'];
|
|
37
|
+
const files = fs_1.default.readdirSync(absPath)
|
|
38
|
+
.filter((f) => imageExts.includes(path_1.default.extname(f).toLowerCase()))
|
|
39
|
+
.sort();
|
|
40
|
+
if (files.length === 0) {
|
|
41
|
+
return { content: [jsonText({ error: '폴더에 이미지 파일이 없습니다 (PNG/GIF/JPEG/WebP)' })], isError: true };
|
|
42
|
+
}
|
|
43
|
+
const formData = new FormData();
|
|
44
|
+
for (const file of files) {
|
|
45
|
+
const filePath = path_1.default.join(absPath, file);
|
|
46
|
+
const buffer = fs_1.default.readFileSync(filePath);
|
|
47
|
+
const ext = path_1.default.extname(file).toLowerCase();
|
|
48
|
+
const mimeMap = { '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.gif': 'image/gif', '.webp': 'image/webp' };
|
|
49
|
+
const blob = new Blob([buffer], { type: mimeMap[ext] || 'image/png' });
|
|
50
|
+
formData.append('files', blob, file);
|
|
51
|
+
}
|
|
52
|
+
const res = await fetch(`${config_js_1.API_URL}/api/agents/${slug}/detail-images`, {
|
|
53
|
+
method: 'POST',
|
|
54
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
55
|
+
body: formData,
|
|
56
|
+
});
|
|
57
|
+
if (!res.ok) {
|
|
58
|
+
const body = await res.json().catch(() => ({}));
|
|
59
|
+
return { content: [jsonText({ error: body.message || `업로드 실패 (${res.status})` })], isError: true };
|
|
60
|
+
}
|
|
61
|
+
const result = await res.json();
|
|
62
|
+
return { content: [jsonText({ status: 'uploaded', count: result.count, images: result.detail_images })] };
|
|
63
|
+
}
|
|
64
|
+
catch (err) {
|
|
65
|
+
return { content: [jsonText({ error: String(err) })], isError: true };
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
server.tool('relay_detail_list', '에이전트 상세페이지 이미지 목록을 조회합니다', {
|
|
69
|
+
slug: zod_1.z.string().describe('에이전트 slug'),
|
|
70
|
+
}, async ({ slug }) => {
|
|
71
|
+
try {
|
|
72
|
+
const res = await fetch(`${config_js_1.API_URL}/api/agents/${slug}/detail-images`);
|
|
73
|
+
if (!res.ok) {
|
|
74
|
+
return { content: [jsonText({ error: `조회 실패 (${res.status})` })], isError: true };
|
|
75
|
+
}
|
|
76
|
+
const data = await res.json();
|
|
77
|
+
return { content: [jsonText({ detail_images: data.detail_images, count: data.detail_images.length })] };
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
return { content: [jsonText({ error: String(err) })], isError: true };
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
server.tool('relay_detail_clear', '에이전트 상세페이지 이미지를 모두 삭제합니다', {
|
|
84
|
+
slug: zod_1.z.string().describe('에이전트 slug'),
|
|
85
|
+
}, async ({ slug }) => {
|
|
86
|
+
try {
|
|
87
|
+
const token = await (0, config_js_1.getValidToken)();
|
|
88
|
+
if (!token)
|
|
89
|
+
return { content: [jsonText({ error: 'LOGIN_REQUIRED', message: '로그인이 필요합니다.' })], isError: true };
|
|
90
|
+
const res = await fetch(`${config_js_1.API_URL}/api/agents/${slug}/detail-images`, {
|
|
91
|
+
method: 'DELETE',
|
|
92
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
93
|
+
});
|
|
94
|
+
if (!res.ok) {
|
|
95
|
+
const body = await res.json().catch(() => ({}));
|
|
96
|
+
return { content: [jsonText({ error: body.message || `삭제 실패 (${res.status})` })], isError: true };
|
|
97
|
+
}
|
|
98
|
+
const result = await res.json();
|
|
99
|
+
return { content: [jsonText({ status: 'cleared', deleted: result.deleted })] };
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
return { content: [jsonText({ error: String(err) })], isError: true };
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
return server;
|
|
106
|
+
}
|
|
107
|
+
// ─── Start ───
|
|
108
|
+
async function startMcpServer() {
|
|
109
|
+
const server = createMcpServer();
|
|
110
|
+
const transport = new stdio_js_1.StdioServerTransport();
|
|
111
|
+
await server.connect(transport);
|
|
112
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* postinstall — npm install -g relayax-cli 후 자동 실행
|
|
5
|
+
*
|
|
6
|
+
* 1. 감지된 에이전트 CLI에 글로벌 슬래시 커맨드 설치
|
|
7
|
+
* 2. 설치 결과 안내 메시지 출력
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
const init_js_1 = require("./commands/init.js");
|
|
11
|
+
const ai_tools_js_1 = require("./lib/ai-tools.js");
|
|
12
|
+
try {
|
|
13
|
+
const detectedCLIs = (0, ai_tools_js_1.detectGlobalCLIs)();
|
|
14
|
+
const result = (0, init_js_1.installGlobalUserCommands)();
|
|
15
|
+
console.log('');
|
|
16
|
+
console.log(' \x1b[32m✓ anpm 설치 완료!\x1b[0m');
|
|
17
|
+
console.log('');
|
|
18
|
+
if (result.tools.length > 0) {
|
|
19
|
+
console.log(` \x1b[36m슬래시 커맨드 설치됨:\x1b[0m ${result.tools.join(', ')}`);
|
|
20
|
+
console.log('');
|
|
21
|
+
console.log(' 사용 가능한 커맨드:');
|
|
22
|
+
console.log(' /relay-explore 에이전트 탐색 & 추천');
|
|
23
|
+
console.log(' /relay-create 에이전트 생성 & 배포');
|
|
24
|
+
console.log(' /relay-status 설치 현황 & Organization');
|
|
25
|
+
console.log(' /relay-uninstall 에이전트 삭제');
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
console.log(' \x1b[33m에이전트 CLI가 감지되지 않았습니다.\x1b[0m');
|
|
29
|
+
console.log(' Claude Code, Cursor 등 설치 후 \x1b[36manpm init\x1b[0m을 실행하세요.');
|
|
30
|
+
}
|
|
31
|
+
console.log('');
|
|
32
|
+
console.log(' \x1b[2m시작하기: anpm login\x1b[0m');
|
|
33
|
+
console.log('');
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
// postinstall 실패해도 CLI 설치는 성공해야 함
|
|
37
|
+
console.log('');
|
|
38
|
+
console.log(' \x1b[32m✓ anpm 설치 완료!\x1b[0m');
|
|
39
|
+
console.log(' \x1b[2m시작하기: anpm login\x1b[0m');
|
|
40
|
+
console.log('');
|
|
41
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
### 에러 처리 가이드
|
|
2
|
+
|
|
3
|
+
CLI 명령 실행 후 JSON 에러가 반환되면 아래 기준에 따라 처리합니다.
|
|
4
|
+
**원칙: "되돌릴 수 없는 영향이 있는가?"로 판단합니다.**
|
|
5
|
+
|
|
6
|
+
#### 1. 자동 해결 (사용자에게 물어보지 않음)
|
|
7
|
+
되돌릴 수 있고, 부작용 없는 에러:
|
|
8
|
+
|
|
9
|
+
| 에러 코드 | 행동 |
|
|
10
|
+
|-----------|------|
|
|
11
|
+
| `LOGIN_REQUIRED` / `NO_TOKEN` | `anpm login` 실행 (timeout 300초, 브라우저 자동 열림) → 성공 후 원래 명령 재시도 |
|
|
12
|
+
| `NOT_INITIALIZED` | `anpm init --all --json` 실행 → 원래 명령 재시도 |
|
|
13
|
+
| `FETCH_FAILED` | 3초 대기 후 원래 명령 재시도 (최대 2회). 2회 실패 시 사용자에게 안내 |
|
|
14
|
+
|
|
15
|
+
#### 2. 사용자에게 선택지 제시 (사용자 질문 도구)
|
|
16
|
+
`options` 필드가 있는 에러:
|
|
17
|
+
|
|
18
|
+
| 에러 코드 | 행동 |
|
|
19
|
+
|-----------|------|
|
|
20
|
+
| `MISSING_VISIBILITY` | options의 label을 선택지로 사용자 질문 도구 호출 |
|
|
21
|
+
| `MISSING_FIELD` | fix 안내 + 사용자에게 값 입력 요청 |
|
|
22
|
+
| `MISSING_TOOLS` | options의 감지된 도구 목록을 선택지로 사용자 질문 도구 호출 |
|
|
23
|
+
| `MISSING_SPACE` | options의 Space 목록을 선택지로 사용자 질문 도구 호출 |
|
|
24
|
+
|
|
25
|
+
사용자가 선택하면, 선택된 값을 CLI 플래그에 반영하여 명령을 재호출합니다.
|
|
26
|
+
|
|
27
|
+
#### 3. 사용자에게 안내 (되돌릴 수 없는 에러)
|
|
28
|
+
구매, 접근 권한, 보안 관련:
|
|
29
|
+
|
|
30
|
+
| 에러 코드 | 행동 |
|
|
31
|
+
|-----------|------|
|
|
32
|
+
| `GATED_ACCESS_REQUIRED` | purchase_info의 message/url 표시 → "접근 코드가 있으신가요?" 사용자 질문 도구 호출 |
|
|
33
|
+
| `SPACE_ONLY` | Space 가입 필요 안내 → "초대 코드가 있으신가요?" 사용자 질문 도구 호출 |
|
|
34
|
+
| `APPROVAL_REQUIRED` | 승인 대기 안내 |
|
|
35
|
+
| `NO_ACCESS` | 접근 방법 안내 |
|
|
36
|
+
|
|
37
|
+
#### 4. 그 외 에러
|
|
38
|
+
`fix` 필드의 메시지를 사용자에게 전달하고, 필요하면 다음 행동을 제안합니다.
|