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
package/README.md
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# relay
|
|
2
|
+
|
|
3
|
+
**The package manager for AI agents.**
|
|
4
|
+
|
|
5
|
+
Write once, run on any harness. One command install. Built-in usage analytics.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx relayax-cli install @gstack/code-review
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
╭──────────────────────────────────────────────╮
|
|
13
|
+
│ │
|
|
14
|
+
│ relay — AI agent distribution for humans │
|
|
15
|
+
│ and machines. │
|
|
16
|
+
│ │
|
|
17
|
+
│ ✓ installed @gstack/code-review (v2.1.0) │
|
|
18
|
+
│ 3 skills, 1 agent ready │
|
|
19
|
+
│ │
|
|
20
|
+
╰──────────────────────────────────────────────╯
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Why
|
|
26
|
+
|
|
27
|
+
AI agents are stuck in silos. You build an agent for Claude Code — it doesn't work on OpenClaw. You share it on GitHub — no one knows it exists. You have no idea if anyone actually uses it.
|
|
28
|
+
|
|
29
|
+
**Relay fixes this.**
|
|
30
|
+
|
|
31
|
+
| Problem | Relay |
|
|
32
|
+
|---|---|
|
|
33
|
+
| Agents locked to one harness | Cross-harness compatibility (Claude, OpenClaw, nanoclaw) |
|
|
34
|
+
| No distribution channel | `relay install` — one command, done |
|
|
35
|
+
| Zero feedback from users | Built-in analytics — see which skills get used |
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Quick Start
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# Install an agent — no setup required
|
|
43
|
+
npx relayax-cli install @author/agent-name
|
|
44
|
+
|
|
45
|
+
# Or install globally
|
|
46
|
+
npm i -g relayax-cli
|
|
47
|
+
relay install @author/agent-name
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
That's it. The agent is ready in your `.relay/agents/` directory, compatible with your harness.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## For Agent Builders
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# Publish your agent to the registry
|
|
58
|
+
relay publish
|
|
59
|
+
|
|
60
|
+
# See who's using it
|
|
61
|
+
relay status --analytics
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Relay tracks skill-level usage out of the box. No extra setup. You'll know exactly which skills land and which don't — so you can ship better agents, faster.
|
|
65
|
+
|
|
66
|
+
### Package Format
|
|
67
|
+
|
|
68
|
+
```yaml
|
|
69
|
+
# team.yaml
|
|
70
|
+
name: code-review
|
|
71
|
+
version: 2.1.0
|
|
72
|
+
harness:
|
|
73
|
+
- claude
|
|
74
|
+
- openclaw
|
|
75
|
+
- nanoclaw
|
|
76
|
+
agents:
|
|
77
|
+
- name: reviewer
|
|
78
|
+
type: passive
|
|
79
|
+
skills:
|
|
80
|
+
- name: review-pr
|
|
81
|
+
- name: security-check
|
|
82
|
+
- name: style-lint
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
One spec. Every harness.
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## Commands
|
|
90
|
+
|
|
91
|
+
| Command | What it does |
|
|
92
|
+
|---|---|
|
|
93
|
+
| `relay install <name>` | Install an agent |
|
|
94
|
+
| `relay search <keyword>` | Find agents in the registry |
|
|
95
|
+
| `relay publish` | Publish your agent |
|
|
96
|
+
| `relay list` | List installed agents |
|
|
97
|
+
| `relay status` | Check environment + analytics |
|
|
98
|
+
| `relay update` | Update agents to latest |
|
|
99
|
+
| `relay uninstall <name>` | Remove an agent |
|
|
100
|
+
| `relay diff <name>` | See what changed between versions |
|
|
101
|
+
|
|
102
|
+
All output is JSON by default (for AI agents). Add `--pretty` for human-readable format.
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## How It Works
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
relay install @team/agent
|
|
110
|
+
│
|
|
111
|
+
╭──────────┴──────────╮
|
|
112
|
+
│ Relay Registry │
|
|
113
|
+
│ (relay.ax cloud) │
|
|
114
|
+
╰──────────┬──────────╯
|
|
115
|
+
│
|
|
116
|
+
╭──────────┴──────────╮
|
|
117
|
+
│ relay agent spec │
|
|
118
|
+
│ (universal format) │
|
|
119
|
+
╰──┬───────┬───────┬──╯
|
|
120
|
+
│ │ │
|
|
121
|
+
┌─────┴─┐ ┌──┴───┐ ┌─┴──────┐
|
|
122
|
+
│Claude │ │Open │ │nano │
|
|
123
|
+
│ Code │ │Claw │ │claw │
|
|
124
|
+
└───────┘ └──────┘ └────────┘
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Relay resolves the right format for your harness automatically. Builders write one spec, users install with one command.
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## AI-Native
|
|
132
|
+
|
|
133
|
+
Relay is built for AI agents as first-class users. The CLI outputs structured JSON so agents can search, install, and manage other agents autonomously.
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
# An agent searching for tools
|
|
137
|
+
relay search "database migration" | jq '.results[].slug'
|
|
138
|
+
|
|
139
|
+
# An agent installing what it needs
|
|
140
|
+
relay install @tools/db-migrate
|
|
141
|
+
# → {"status":"ok","agent":"db-migrate","skills":["migrate","rollback","seed"]}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Relay also ships as an **MCP server**, so any MCP-compatible agent can use it directly:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
relay mcp
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Open Core
|
|
153
|
+
|
|
154
|
+
The CLI and agent spec are open source (MIT). Build agents, publish them, self-host your own registry — no vendor lock-in.
|
|
155
|
+
|
|
156
|
+
[relay.ax](https://relayax.com) provides the hosted registry with:
|
|
157
|
+
- Private agent hosting
|
|
158
|
+
- Organization management & access control
|
|
159
|
+
- Usage analytics dashboard
|
|
160
|
+
- Enterprise SSO & audit logs
|
|
161
|
+
|
|
162
|
+
---
|
|
163
|
+
|
|
164
|
+
## Community
|
|
165
|
+
|
|
166
|
+
- [25+ production agents](https://relayax.com) ready to install
|
|
167
|
+
- [Builder docs](https://relayax.com/docs) for creating your own
|
|
168
|
+
- [Discord](#) for help and discussion
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
<p align="center">
|
|
173
|
+
<sub>Built by <a href="https://relayax.com">RelayAX</a></sub>
|
|
174
|
+
</p>
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerAccess = registerAccess;
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const config_js_1 = require("../lib/config.js");
|
|
6
|
+
async function claimAccess(slug, code) {
|
|
7
|
+
const token = await (0, config_js_1.getValidToken)();
|
|
8
|
+
if (!token) {
|
|
9
|
+
throw new Error('LOGIN_REQUIRED');
|
|
10
|
+
}
|
|
11
|
+
const res = await fetch(`${config_js_1.API_URL}/api/agents/${slug}/claim-access`, {
|
|
12
|
+
method: 'POST',
|
|
13
|
+
headers: {
|
|
14
|
+
'Content-Type': 'application/json',
|
|
15
|
+
Authorization: `Bearer ${token}`,
|
|
16
|
+
},
|
|
17
|
+
body: JSON.stringify({ code }),
|
|
18
|
+
signal: AbortSignal.timeout(10000),
|
|
19
|
+
});
|
|
20
|
+
const body = (await res.json().catch(() => ({})));
|
|
21
|
+
if (!res.ok) {
|
|
22
|
+
const errCode = body.error ?? String(res.status);
|
|
23
|
+
switch (errCode) {
|
|
24
|
+
case 'INVALID_LINK':
|
|
25
|
+
throw new Error('초대 링크가 유효하지 않거나 만료되었습니다.');
|
|
26
|
+
case 'NOT_FOUND':
|
|
27
|
+
throw new Error('에이전트를 찾을 수 없습니다.');
|
|
28
|
+
case 'UNAUTHORIZED':
|
|
29
|
+
throw new Error('LOGIN_REQUIRED');
|
|
30
|
+
default:
|
|
31
|
+
throw new Error(body.message ?? `접근 권한 요청 실패 (${res.status})`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return body;
|
|
35
|
+
}
|
|
36
|
+
function registerAccess(program) {
|
|
37
|
+
program
|
|
38
|
+
.command('access <slug>')
|
|
39
|
+
.description('초대 코드로 에이전트에 접근 권한을 얻고 바로 설치합니다')
|
|
40
|
+
.requiredOption('--code <code>', '에이전트 초대 코드')
|
|
41
|
+
.action(async (slug, opts) => {
|
|
42
|
+
const json = program.opts().json ?? false;
|
|
43
|
+
try {
|
|
44
|
+
const result = await claimAccess(slug, opts.code);
|
|
45
|
+
if (!result.success || !result.agent) {
|
|
46
|
+
throw new Error('서버 응답이 올바르지 않습니다.');
|
|
47
|
+
}
|
|
48
|
+
const agentSlug = result.agent.slug;
|
|
49
|
+
if (json) {
|
|
50
|
+
console.log(JSON.stringify({ status: 'ok', agent: result.agent }));
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
console.log(`\x1b[32m접근 권한이 부여되었습니다: ${result.agent.name}\x1b[0m`);
|
|
54
|
+
console.log(`\x1b[33m에이전트를 설치합니다: anpm install ${agentSlug}\x1b[0m\n`);
|
|
55
|
+
}
|
|
56
|
+
// Automatically install the agent
|
|
57
|
+
const { registerInstall } = await import('./install.js');
|
|
58
|
+
const subProgram = new commander_1.Command();
|
|
59
|
+
subProgram.option('--json', '구조화된 JSON 출력');
|
|
60
|
+
if (json)
|
|
61
|
+
subProgram.setOptionValue('json', true);
|
|
62
|
+
registerInstall(subProgram);
|
|
63
|
+
await subProgram.parseAsync(['node', 'relay', 'install', agentSlug]);
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
67
|
+
if (message === 'LOGIN_REQUIRED') {
|
|
68
|
+
if (json) {
|
|
69
|
+
console.error(JSON.stringify({
|
|
70
|
+
error: 'LOGIN_REQUIRED',
|
|
71
|
+
message: '로그인이 필요합니다. anpm login을 먼저 실행하세요.',
|
|
72
|
+
fix: 'anpm login 실행 후 재시도하세요.',
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
console.error('\x1b[31m오류: 로그인이 필요합니다.\x1b[0m');
|
|
77
|
+
console.error(' anpm login을 먼저 실행하세요.');
|
|
78
|
+
}
|
|
79
|
+
process.exit(1);
|
|
80
|
+
}
|
|
81
|
+
if (json) {
|
|
82
|
+
console.error(JSON.stringify({ error: 'ACCESS_FAILED', message, fix: '접근 링크 코드를 확인하거나 에이전트 제작자에게 문의하세요.' }));
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
console.error(`\x1b[31m오류: ${message}\x1b[0m`);
|
|
86
|
+
}
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
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.registerAdopt = registerAdopt;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const os_1 = __importDefault(require("os"));
|
|
10
|
+
const config_js_1 = require("../lib/config.js");
|
|
11
|
+
const paths_js_1 = require("../lib/paths.js");
|
|
12
|
+
const installer_js_1 = require("../lib/installer.js");
|
|
13
|
+
const agent_status_js_1 = require("../lib/agent-status.js");
|
|
14
|
+
function registerAdopt(program) {
|
|
15
|
+
program
|
|
16
|
+
.command('adopt [path]')
|
|
17
|
+
.description('Adopt unmanaged skills into anpm management')
|
|
18
|
+
.option('--all', 'Adopt all unmanaged skills')
|
|
19
|
+
.option('--global', 'Adopt to global scope')
|
|
20
|
+
.option('--project <dir>', 'Project root path')
|
|
21
|
+
.option('--yes', 'Skip confirmation prompts')
|
|
22
|
+
.action(async (inputPath, opts) => {
|
|
23
|
+
const json = program.opts().json ?? false;
|
|
24
|
+
const projectPath = (0, paths_js_1.resolveProjectPath)(opts.project);
|
|
25
|
+
const scope = opts.global ? 'global' : 'local';
|
|
26
|
+
try {
|
|
27
|
+
if (opts.all) {
|
|
28
|
+
// Adopt all unmanaged content
|
|
29
|
+
const unmanaged = (0, agent_status_js_1.findUnmanagedContent)(projectPath);
|
|
30
|
+
if (unmanaged.length === 0) {
|
|
31
|
+
if (json)
|
|
32
|
+
console.log(JSON.stringify({ status: 'ok', message: 'All content is already managed by anpm' }));
|
|
33
|
+
else
|
|
34
|
+
console.log('\x1b[32m✓\x1b[0m All content is already managed by anpm.');
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (!json) {
|
|
38
|
+
console.log(`\nFound ${unmanaged.length} unmanaged item(s):\n`);
|
|
39
|
+
for (const item of unmanaged) {
|
|
40
|
+
console.log(` ${item.type}/${item.name} \x1b[90m(${item.harness})\x1b[0m`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
if (!opts.yes && !json) {
|
|
44
|
+
const { confirm } = await import('@inquirer/prompts');
|
|
45
|
+
const ok = await confirm({ message: `Adopt all ${unmanaged.length} items?`, default: true });
|
|
46
|
+
if (!ok) {
|
|
47
|
+
process.exit(0);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
let adopted = 0;
|
|
51
|
+
for (const item of unmanaged) {
|
|
52
|
+
try {
|
|
53
|
+
await adoptSingle(item.path, item.name, scope, projectPath, json);
|
|
54
|
+
adopted++;
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
58
|
+
if (!json)
|
|
59
|
+
console.error(` \x1b[33m⚠️ Skipping ${item.name}: ${msg}\x1b[0m`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (json)
|
|
63
|
+
console.log(JSON.stringify({ status: 'ok', adopted }));
|
|
64
|
+
else
|
|
65
|
+
console.log(`\n\x1b[32m✓\x1b[0m Adopted ${adopted} item(s).`);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
// Single path adopt
|
|
69
|
+
if (!inputPath) {
|
|
70
|
+
console.error('Usage: anpm adopt <path> or anpm adopt --all');
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
const absPath = path_1.default.resolve(inputPath);
|
|
74
|
+
if (!fs_1.default.existsSync(absPath)) {
|
|
75
|
+
throw new Error(`Path not found: ${absPath}`);
|
|
76
|
+
}
|
|
77
|
+
// Check if already a relay symlink
|
|
78
|
+
try {
|
|
79
|
+
if (fs_1.default.lstatSync(absPath).isSymbolicLink()) {
|
|
80
|
+
const target = fs_1.default.readlinkSync(absPath);
|
|
81
|
+
if (target.includes('.relay/agents/')) {
|
|
82
|
+
if (json)
|
|
83
|
+
console.log(JSON.stringify({ status: 'ok', message: 'Already managed by anpm' }));
|
|
84
|
+
else
|
|
85
|
+
console.log(`\x1b[32m✓\x1b[0m Already managed by anpm.`);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
catch { /* not a symlink */ }
|
|
91
|
+
const name = path_1.default.basename(absPath);
|
|
92
|
+
await adoptSingle(absPath, name, scope, projectPath, json);
|
|
93
|
+
if (json) {
|
|
94
|
+
console.log(JSON.stringify({ status: 'ok', slug: `adopted/${name}` }));
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
console.log(`\n\x1b[32m✓ Adopted ${name}\x1b[0m`);
|
|
98
|
+
console.log(` Now managed by anpm and deployed to all detected harnesses.`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
103
|
+
if (json)
|
|
104
|
+
console.error(JSON.stringify({ error: 'ADOPT_FAILED', message }));
|
|
105
|
+
else
|
|
106
|
+
console.error(`\x1b[31m✖ ${message}\x1b[0m`);
|
|
107
|
+
process.exit(1);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
async function adoptSingle(sourcePath, name, scope, projectPath, json) {
|
|
112
|
+
const baseDir = scope === 'global'
|
|
113
|
+
? path_1.default.join(os_1.default.homedir(), '.relay', 'agents', 'local', name)
|
|
114
|
+
: path_1.default.join(projectPath, '.relay', 'agents', 'local', name);
|
|
115
|
+
// Detect what type of content this is (skill, command, rule)
|
|
116
|
+
// by looking at the parent directory name
|
|
117
|
+
const parentDir = path_1.default.basename(path_1.default.dirname(sourcePath));
|
|
118
|
+
const contentType = ['skills', 'commands', 'rules', 'agents'].includes(parentDir) ? parentDir : 'skills';
|
|
119
|
+
const destDir = path_1.default.join(baseDir, contentType, name);
|
|
120
|
+
// Safety: copy first, then symlink, then delete original
|
|
121
|
+
// 1. Copy to .relay/agents/
|
|
122
|
+
fs_1.default.mkdirSync(destDir, { recursive: true });
|
|
123
|
+
copyDirRecursive(sourcePath, destDir);
|
|
124
|
+
// 2. Verify copy exists
|
|
125
|
+
if (!fs_1.default.existsSync(destDir)) {
|
|
126
|
+
throw new Error(`Copy failed: ${destDir}`);
|
|
127
|
+
}
|
|
128
|
+
// 3. Remove original and replace with symlink
|
|
129
|
+
if (fs_1.default.lstatSync(sourcePath).isDirectory()) {
|
|
130
|
+
fs_1.default.rmSync(sourcePath, { recursive: true, force: true });
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
fs_1.default.unlinkSync(sourcePath);
|
|
134
|
+
}
|
|
135
|
+
const relativeSrc = path_1.default.relative(path_1.default.dirname(sourcePath), destDir);
|
|
136
|
+
fs_1.default.symlinkSync(relativeSrc, sourcePath);
|
|
137
|
+
// 4. Deploy to other harnesses
|
|
138
|
+
const deploy = await (0, installer_js_1.deploySymlinks)(baseDir, scope, projectPath);
|
|
139
|
+
for (const w of deploy.warnings) {
|
|
140
|
+
if (!json)
|
|
141
|
+
console.error(`\x1b[33m${w}\x1b[0m`);
|
|
142
|
+
}
|
|
143
|
+
// 5. Record in installed.json
|
|
144
|
+
const slug = `adopted/${name}`;
|
|
145
|
+
const record = {
|
|
146
|
+
version: '0.0.0',
|
|
147
|
+
installed_at: new Date().toISOString(),
|
|
148
|
+
files: [baseDir],
|
|
149
|
+
deploy_scope: scope,
|
|
150
|
+
deployed_symlinks: [sourcePath, ...deploy.symlinks],
|
|
151
|
+
source: `adopted:${sourcePath}`,
|
|
152
|
+
};
|
|
153
|
+
if (scope === 'global') {
|
|
154
|
+
const installed = (0, config_js_1.loadGlobalInstalled)();
|
|
155
|
+
installed[slug] = record;
|
|
156
|
+
(0, config_js_1.saveGlobalInstalled)(installed);
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
const installed = (0, config_js_1.loadInstalled)();
|
|
160
|
+
installed[slug] = record;
|
|
161
|
+
(0, config_js_1.saveInstalled)(installed);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
function copyDirRecursive(src, dest) {
|
|
165
|
+
const stat = fs_1.default.statSync(src);
|
|
166
|
+
if (stat.isDirectory()) {
|
|
167
|
+
fs_1.default.mkdirSync(dest, { recursive: true });
|
|
168
|
+
for (const entry of fs_1.default.readdirSync(src, { withFileTypes: true })) {
|
|
169
|
+
if (entry.name.startsWith('.'))
|
|
170
|
+
continue;
|
|
171
|
+
copyDirRecursive(path_1.default.join(src, entry.name), path_1.default.join(dest, entry.name));
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
fs_1.default.copyFileSync(src, dest);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
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.registerChangelog = registerChangelog;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const js_yaml_1 = __importDefault(require("js-yaml"));
|
|
10
|
+
function registerChangelog(program) {
|
|
11
|
+
const changelog = program
|
|
12
|
+
.command('changelog')
|
|
13
|
+
.description('에이전트 패키지의 changelog를 관리합니다');
|
|
14
|
+
changelog
|
|
15
|
+
.command('add')
|
|
16
|
+
.description('relay.yaml에 changelog 엔트리를 추가합니다')
|
|
17
|
+
.argument('[message]', 'changelog 메시지 (없으면 에디터에서 입력)')
|
|
18
|
+
.action(async (message) => {
|
|
19
|
+
const yamlPath = path_1.default.resolve('relay.yaml');
|
|
20
|
+
if (!fs_1.default.existsSync(yamlPath)) {
|
|
21
|
+
console.error('relay.yaml을 찾을 수 없습니다. 에이전트 패키지 디렉토리에서 실행하세요.');
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
const content = fs_1.default.readFileSync(yamlPath, 'utf-8');
|
|
25
|
+
const doc = js_yaml_1.default.load(content) ?? {};
|
|
26
|
+
if (!message) {
|
|
27
|
+
// Read from stdin if piped, otherwise prompt
|
|
28
|
+
const readline = await import('readline');
|
|
29
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
30
|
+
message = await new Promise((resolve) => {
|
|
31
|
+
rl.question('Changelog 메시지: ', (answer) => {
|
|
32
|
+
rl.close();
|
|
33
|
+
resolve(answer);
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
if (!message || message.trim() === '') {
|
|
38
|
+
console.error('changelog 메시지가 비어있습니다.');
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
const version = String(doc.version ?? '1.0.0');
|
|
42
|
+
const date = new Date().toISOString().split('T')[0];
|
|
43
|
+
const entry = `## v${version} (${date})\n\n- ${message.trim()}`;
|
|
44
|
+
const existing = doc.changelog ? String(doc.changelog) : '';
|
|
45
|
+
doc.changelog = existing ? `${entry}\n\n${existing}` : entry;
|
|
46
|
+
fs_1.default.writeFileSync(yamlPath, js_yaml_1.default.dump(doc, { lineWidth: -1, noRefs: true }), 'utf-8');
|
|
47
|
+
console.log(`\x1b[32m✓\x1b[0m changelog 추가됨 (v${version})`);
|
|
48
|
+
console.log(` ${message.trim()}`);
|
|
49
|
+
});
|
|
50
|
+
changelog
|
|
51
|
+
.command('show')
|
|
52
|
+
.description('현재 relay.yaml의 changelog를 표시합니다')
|
|
53
|
+
.action(() => {
|
|
54
|
+
const yamlPath = path_1.default.resolve('relay.yaml');
|
|
55
|
+
if (!fs_1.default.existsSync(yamlPath)) {
|
|
56
|
+
console.error('relay.yaml을 찾을 수 없습니다.');
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
const content = fs_1.default.readFileSync(yamlPath, 'utf-8');
|
|
60
|
+
const doc = js_yaml_1.default.load(content) ?? {};
|
|
61
|
+
if (!doc.changelog) {
|
|
62
|
+
console.log('changelog가 없습니다. `anpm changelog add "메시지"`로 추가하세요.');
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
console.log(String(doc.changelog));
|
|
66
|
+
});
|
|
67
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerCheckUpdate = registerCheckUpdate;
|
|
4
|
+
const version_check_js_1 = require("../lib/version-check.js");
|
|
5
|
+
const slug_js_1 = require("../lib/slug.js");
|
|
6
|
+
function registerCheckUpdate(program) {
|
|
7
|
+
program
|
|
8
|
+
.command('check-update [slug]')
|
|
9
|
+
.description('CLI 및 설치된 에이전트의 업데이트를 확인합니다')
|
|
10
|
+
.option('--quiet', '업데이트가 있을 때만 머신 리더블 출력')
|
|
11
|
+
.option('--force', '캐시를 무시하고 강제 체크')
|
|
12
|
+
.action(async (slug, opts) => {
|
|
13
|
+
const quiet = opts.quiet ?? false;
|
|
14
|
+
const force = opts.force ?? false;
|
|
15
|
+
// CLI version check
|
|
16
|
+
const cliResult = await (0, version_check_js_1.checkCliVersion)(force);
|
|
17
|
+
if (cliResult) {
|
|
18
|
+
if (quiet) {
|
|
19
|
+
console.log(`CLI_UPGRADE_AVAILABLE ${cliResult.current} ${cliResult.latest}`);
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
console.log(`\n\x1b[33m⚠ anpm v${cliResult.latest} available\x1b[0m (현재 v${cliResult.current})`);
|
|
23
|
+
console.log(` 실행: npm update -g relayax-cli\n`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// Agent version check
|
|
27
|
+
if (slug) {
|
|
28
|
+
// Resolve to scoped slug
|
|
29
|
+
let scopedSlug;
|
|
30
|
+
if ((0, slug_js_1.isScopedSlug)(slug)) {
|
|
31
|
+
scopedSlug = slug;
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
try {
|
|
35
|
+
const parsed = await (0, slug_js_1.resolveSlug)(slug);
|
|
36
|
+
scopedSlug = parsed.full;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
scopedSlug = slug;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const agentResult = await (0, version_check_js_1.checkAgentVersion)(scopedSlug, force);
|
|
43
|
+
if (agentResult) {
|
|
44
|
+
if (quiet) {
|
|
45
|
+
const byAuthor = agentResult.author ? ` ${agentResult.author}` : '';
|
|
46
|
+
console.log(`AGENT_UPGRADE_AVAILABLE ${slug} ${agentResult.current} ${agentResult.latest}${byAuthor}`);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
const byAuthor = agentResult.author ? ` \x1b[90m(by @${agentResult.author})\x1b[0m` : '';
|
|
50
|
+
console.log(`\x1b[33m⚠ ${slug} v${agentResult.latest} available\x1b[0m${byAuthor} (현재 v${agentResult.current})`);
|
|
51
|
+
console.log(` 실행: anpm update ${slug}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
else if (!quiet && !cliResult) {
|
|
55
|
+
console.log('모든 것이 최신 상태입니다.');
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
const agentResults = await (0, version_check_js_1.checkAllAgents)(force);
|
|
60
|
+
for (const result of agentResults) {
|
|
61
|
+
if (quiet) {
|
|
62
|
+
const byAuthor = result.author ? ` ${result.author}` : '';
|
|
63
|
+
console.log(`AGENT_UPGRADE_AVAILABLE ${result.slug} ${result.current} ${result.latest}${byAuthor}`);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
const byAuthor = result.author ? ` \x1b[90m(by @${result.author})\x1b[0m` : '';
|
|
67
|
+
console.log(`\x1b[33m⚠ ${result.slug} v${result.latest} available\x1b[0m${byAuthor} (현재 v${result.current})`);
|
|
68
|
+
console.log(` 실행: anpm update ${result.slug}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (!quiet && !cliResult && agentResults.length === 0) {
|
|
72
|
+
console.log('모든 것이 최신 상태입니다.');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|