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,284 @@
|
|
|
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.registerDeploy = registerDeploy;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
10
|
+
const manifest_js_1 = require("../lib/manifest.js");
|
|
11
|
+
const paths_js_1 = require("../lib/paths.js");
|
|
12
|
+
const anpm_config_js_1 = require("../lib/anpm-config.js");
|
|
13
|
+
const config_js_1 = require("../lib/config.js");
|
|
14
|
+
const anthropic_js_1 = require("../lib/cloud-providers/anthropic.js");
|
|
15
|
+
const package_mapper_js_1 = require("../lib/cloud-providers/package-mapper.js");
|
|
16
|
+
const step_tracker_js_1 = require("../lib/step-tracker.js");
|
|
17
|
+
const error_report_js_1 = require("../lib/error-report.js");
|
|
18
|
+
const SUPPORTED_PROVIDERS = ['anthropic'];
|
|
19
|
+
/** Scan skills directories and return SkillDir entries */
|
|
20
|
+
function scanSkillDirs(agentPath) {
|
|
21
|
+
const results = [];
|
|
22
|
+
const skillsPaths = [
|
|
23
|
+
path_1.default.join(agentPath, 'skills'),
|
|
24
|
+
path_1.default.join(agentPath, '.claude', 'skills'),
|
|
25
|
+
path_1.default.join(agentPath, '.anpm', 'skills'),
|
|
26
|
+
];
|
|
27
|
+
for (const skillsRoot of skillsPaths) {
|
|
28
|
+
if (!fs_1.default.existsSync(skillsRoot))
|
|
29
|
+
continue;
|
|
30
|
+
for (const entry of fs_1.default.readdirSync(skillsRoot, { withFileTypes: true })) {
|
|
31
|
+
if (!entry.isDirectory())
|
|
32
|
+
continue;
|
|
33
|
+
const skillPath = path_1.default.join(skillsRoot, entry.name);
|
|
34
|
+
const skillMd = path_1.default.join(skillPath, 'SKILL.md');
|
|
35
|
+
if (!fs_1.default.existsSync(skillMd))
|
|
36
|
+
continue;
|
|
37
|
+
const files = [];
|
|
38
|
+
const walk = (dir) => {
|
|
39
|
+
for (const f of fs_1.default.readdirSync(dir, { withFileTypes: true })) {
|
|
40
|
+
const fp = path_1.default.join(dir, f.name);
|
|
41
|
+
if (f.isDirectory())
|
|
42
|
+
walk(fp);
|
|
43
|
+
else
|
|
44
|
+
files.push(fp);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
walk(skillPath);
|
|
48
|
+
results.push({ name: entry.name, path: skillPath, files });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return results;
|
|
52
|
+
}
|
|
53
|
+
/** Load deploy registry from .anpm/deploy.json */
|
|
54
|
+
function loadDeployRegistry(projectPath) {
|
|
55
|
+
const dir = (0, config_js_1.getProjectAnpmDir)();
|
|
56
|
+
const file = path_1.default.join(dir, 'deploy.json');
|
|
57
|
+
if (!fs_1.default.existsSync(file))
|
|
58
|
+
return {};
|
|
59
|
+
try {
|
|
60
|
+
return JSON.parse(fs_1.default.readFileSync(file, 'utf-8'));
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return {};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/** Save deploy registry */
|
|
67
|
+
function saveDeployRegistry(projectPath, registry) {
|
|
68
|
+
const dir = (0, config_js_1.getProjectAnpmDir)();
|
|
69
|
+
if (!fs_1.default.existsSync(dir))
|
|
70
|
+
fs_1.default.mkdirSync(dir, { recursive: true });
|
|
71
|
+
fs_1.default.writeFileSync(path_1.default.join(dir, 'deploy.json'), JSON.stringify(registry, null, 2));
|
|
72
|
+
}
|
|
73
|
+
/** Hash manifest for change detection */
|
|
74
|
+
function hashManifest(manifest) {
|
|
75
|
+
const content = JSON.stringify({ agent: manifest.agent, cloud: manifest.cloud, requires: manifest.requires });
|
|
76
|
+
return crypto_1.default.createHash('sha256').update(content).digest('hex').slice(0, 16);
|
|
77
|
+
}
|
|
78
|
+
function registerDeploy(program) {
|
|
79
|
+
program
|
|
80
|
+
.command('deploy [slug]')
|
|
81
|
+
.description('Deploy an agent to a cloud provider')
|
|
82
|
+
.option('--to <provider>', 'Target cloud provider (e.g., anthropic)')
|
|
83
|
+
.option('--api-key <key>', 'Provider API key')
|
|
84
|
+
.option('--project <dir>', 'Project root path')
|
|
85
|
+
.action(async (slugInput, opts) => {
|
|
86
|
+
const json = program.opts().json ?? false;
|
|
87
|
+
(0, step_tracker_js_1.trackCommand)('deploy');
|
|
88
|
+
try {
|
|
89
|
+
const projectPath = (0, paths_js_1.resolveProjectPath)(opts.project);
|
|
90
|
+
// ── 1. Resolve agent source ──
|
|
91
|
+
let agentPath;
|
|
92
|
+
let manifest;
|
|
93
|
+
let agentSlug;
|
|
94
|
+
if (slugInput) {
|
|
95
|
+
// Installed agent
|
|
96
|
+
const local = (0, config_js_1.loadInstalled)();
|
|
97
|
+
const global = (0, config_js_1.loadGlobalInstalled)();
|
|
98
|
+
const entry = local[slugInput] ?? global[slugInput];
|
|
99
|
+
if (!entry) {
|
|
100
|
+
throw new Error(`Agent "${slugInput}" not found. Install it first: anpm install ${slugInput}`);
|
|
101
|
+
}
|
|
102
|
+
const anpmDir = (0, config_js_1.getProjectAnpmDir)();
|
|
103
|
+
agentPath = path_1.default.join(anpmDir, 'agents', slugInput);
|
|
104
|
+
if (!fs_1.default.existsSync(agentPath)) {
|
|
105
|
+
// Try legacy .relay path
|
|
106
|
+
const relayPath = path_1.default.join(projectPath, '.relay', 'agents', slugInput);
|
|
107
|
+
if (fs_1.default.existsSync(relayPath)) {
|
|
108
|
+
agentPath = relayPath;
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
throw new Error(`Agent directory not found: ${agentPath}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const result = (0, manifest_js_1.loadManifest)(agentPath);
|
|
115
|
+
manifest = result.manifest;
|
|
116
|
+
agentSlug = slugInput;
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
// Current directory (builder mode)
|
|
120
|
+
agentPath = projectPath;
|
|
121
|
+
const result = (0, manifest_js_1.loadManifest)(projectPath);
|
|
122
|
+
manifest = result.manifest;
|
|
123
|
+
agentSlug = manifest?.slug ?? manifest?.name ?? path_1.default.basename(projectPath);
|
|
124
|
+
}
|
|
125
|
+
if (!manifest) {
|
|
126
|
+
throw new Error('No anpm.yaml found. Specify an agent slug or run from a project with anpm.yaml.');
|
|
127
|
+
}
|
|
128
|
+
// ── 2. Determine provider ──
|
|
129
|
+
const cloudConfig = manifest.cloud;
|
|
130
|
+
if (!cloudConfig) {
|
|
131
|
+
throw new Error('No cloud: section in anpm.yaml. Add cloud provider config to deploy.');
|
|
132
|
+
}
|
|
133
|
+
let providerName = opts.to;
|
|
134
|
+
if (!providerName) {
|
|
135
|
+
const providers = Object.keys(cloudConfig).filter(k => typeof cloudConfig[k] === 'object');
|
|
136
|
+
if (providers.length === 1) {
|
|
137
|
+
providerName = providers[0];
|
|
138
|
+
}
|
|
139
|
+
else if (providers.length === 0) {
|
|
140
|
+
throw new Error('No providers configured in cloud: section.');
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
throw new Error(`Multiple providers found (${providers.join(', ')}). Specify one with --to.`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (!SUPPORTED_PROVIDERS.includes(providerName)) {
|
|
147
|
+
throw new Error(`Provider '${providerName}' is not yet supported. Available: ${SUPPORTED_PROVIDERS.join(', ')}`);
|
|
148
|
+
}
|
|
149
|
+
// ── 3. Resolve API key ──
|
|
150
|
+
const apiKey = (0, anpm_config_js_1.resolveProviderApiKey)(providerName, opts.apiKey);
|
|
151
|
+
if (!apiKey) {
|
|
152
|
+
throw new Error(`${providerName.charAt(0).toUpperCase() + providerName.slice(1)} API key not found.\n` +
|
|
153
|
+
`Set ${providerName === 'anthropic' ? 'ANTHROPIC_API_KEY' : providerName.toUpperCase() + '_API_KEY'} or run:\n` +
|
|
154
|
+
` anpm config set provider.${providerName}.api-key <key>`);
|
|
155
|
+
}
|
|
156
|
+
// ── 4. Check for changes ──
|
|
157
|
+
const deployReg = loadDeployRegistry(projectPath);
|
|
158
|
+
const manifestHash = hashManifest(manifest);
|
|
159
|
+
const existing = deployReg[agentSlug]?.[providerName];
|
|
160
|
+
if (existing && existing.manifest_hash === manifestHash) {
|
|
161
|
+
if (json) {
|
|
162
|
+
console.log(JSON.stringify({ status: 'no_changes', agent_id: existing.agent_id, environment_id: existing.environment_id }));
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
console.error('No changes detected. Skipping deploy.');
|
|
166
|
+
console.error(` Agent ID: ${existing.agent_id}`);
|
|
167
|
+
}
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
// ── 5. Create provider ──
|
|
171
|
+
let provider;
|
|
172
|
+
if (providerName === 'anthropic') {
|
|
173
|
+
provider = new anthropic_js_1.AnthropicProvider(apiKey);
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
throw new Error(`Provider '${providerName}' not implemented`);
|
|
177
|
+
}
|
|
178
|
+
if (!json)
|
|
179
|
+
console.error(`Validating ${providerName} credentials...`);
|
|
180
|
+
const valid = await provider.validateCredentials();
|
|
181
|
+
if (!valid) {
|
|
182
|
+
throw new Error(`Invalid ${providerName} API key. Check your key and try again.`);
|
|
183
|
+
}
|
|
184
|
+
// ── 6. Upload skills ──
|
|
185
|
+
const skillDirs = scanSkillDirs(agentPath);
|
|
186
|
+
let uploadedSkills = [];
|
|
187
|
+
if (skillDirs.length > 0) {
|
|
188
|
+
if (!json)
|
|
189
|
+
console.error(`Uploading ${skillDirs.length} skill(s)...`);
|
|
190
|
+
uploadedSkills = await provider.uploadSkills(skillDirs);
|
|
191
|
+
if (!json) {
|
|
192
|
+
for (const s of uploadedSkills) {
|
|
193
|
+
console.error(` ✓ ${s.name} → ${s.skill_id}`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// ── 7. Create environment ──
|
|
198
|
+
if (!json)
|
|
199
|
+
console.error('Creating environment...');
|
|
200
|
+
const anthConfig = cloudConfig.anthropic;
|
|
201
|
+
const packages = manifest.requires ? (0, package_mapper_js_1.mapRequiresToPackages)(manifest.requires) : {};
|
|
202
|
+
const envName = `anpm-${agentSlug.replace(/[@/]/g, '-').replace(/^-/, '')}`;
|
|
203
|
+
const envId = await provider.createEnvironment({
|
|
204
|
+
name: envName,
|
|
205
|
+
packages,
|
|
206
|
+
networking: anthConfig?.networking ?? 'unrestricted',
|
|
207
|
+
allowed_hosts: anthConfig?.allowed_hosts,
|
|
208
|
+
});
|
|
209
|
+
if (!json)
|
|
210
|
+
console.error(` ✓ Environment: ${envId}`);
|
|
211
|
+
// ── 8. Create or update agent ──
|
|
212
|
+
const agentConfig = {
|
|
213
|
+
name: manifest.name ?? agentSlug,
|
|
214
|
+
model: anthConfig?.model ?? 'claude-sonnet-4-6',
|
|
215
|
+
system: manifest.agent?.system,
|
|
216
|
+
tools: manifest.agent?.tools,
|
|
217
|
+
skill_ids: uploadedSkills.map(s => s.skill_id),
|
|
218
|
+
mcp_servers: manifest.agent?.mcp_servers,
|
|
219
|
+
};
|
|
220
|
+
let agentId;
|
|
221
|
+
let agentVersion;
|
|
222
|
+
if (existing) {
|
|
223
|
+
if (!json)
|
|
224
|
+
console.error('Updating agent...');
|
|
225
|
+
const result = await provider.updateAgent(existing.agent_id, existing.agent_version, agentConfig);
|
|
226
|
+
agentId = result.agent_id;
|
|
227
|
+
agentVersion = result.version;
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
if (!json)
|
|
231
|
+
console.error('Creating agent...');
|
|
232
|
+
const result = await provider.createAgent(agentConfig);
|
|
233
|
+
agentId = result.agent_id;
|
|
234
|
+
agentVersion = result.version;
|
|
235
|
+
}
|
|
236
|
+
if (!json)
|
|
237
|
+
console.error(` ✓ Agent: ${agentId} (v${agentVersion})`);
|
|
238
|
+
// ── 9. Save deploy record ──
|
|
239
|
+
if (!deployReg[agentSlug])
|
|
240
|
+
deployReg[agentSlug] = {};
|
|
241
|
+
deployReg[agentSlug][providerName] = {
|
|
242
|
+
agent_id: agentId,
|
|
243
|
+
agent_version: agentVersion,
|
|
244
|
+
environment_id: envId,
|
|
245
|
+
skill_ids: uploadedSkills.map(s => s.skill_id),
|
|
246
|
+
deployed_at: new Date().toISOString(),
|
|
247
|
+
manifest_hash: manifestHash,
|
|
248
|
+
};
|
|
249
|
+
saveDeployRegistry(projectPath, deployReg);
|
|
250
|
+
// ── 10. Output ──
|
|
251
|
+
if (json) {
|
|
252
|
+
console.log(JSON.stringify({
|
|
253
|
+
status: 'deployed',
|
|
254
|
+
agent_id: agentId,
|
|
255
|
+
agent_version: agentVersion,
|
|
256
|
+
environment_id: envId,
|
|
257
|
+
skill_ids: uploadedSkills.map(s => s.skill_id),
|
|
258
|
+
deployed_at: deployReg[agentSlug][providerName].deployed_at,
|
|
259
|
+
}));
|
|
260
|
+
}
|
|
261
|
+
else {
|
|
262
|
+
console.error('');
|
|
263
|
+
console.error(`\x1b[32m✓ Deployed to ${providerName}\x1b[0m`);
|
|
264
|
+
console.error(` Agent: ${agentId} (v${agentVersion})`);
|
|
265
|
+
console.error(` Environment: ${envId}`);
|
|
266
|
+
console.error(` Skills: ${uploadedSkills.length}`);
|
|
267
|
+
console.error('');
|
|
268
|
+
console.error(` Start a session:`);
|
|
269
|
+
console.error(` anpm session start ${agentSlug} --provider ${providerName}`);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
catch (err) {
|
|
273
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
274
|
+
(0, error_report_js_1.reportCliError)('deploy', 'DEPLOY_FAILED', msg);
|
|
275
|
+
if (json) {
|
|
276
|
+
console.error(JSON.stringify({ error: 'DEPLOY_FAILED', message: msg }));
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
console.error(`\x1b[31m오류: ${msg}\x1b[0m`);
|
|
280
|
+
}
|
|
281
|
+
process.exit(1);
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
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.registerDiff = registerDiff;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const os_1 = __importDefault(require("os"));
|
|
10
|
+
const api_js_1 = require("../lib/api.js");
|
|
11
|
+
const slug_js_1 = require("../lib/slug.js");
|
|
12
|
+
const git_operations_js_1 = require("../lib/git-operations.js");
|
|
13
|
+
function registerDiff(program) {
|
|
14
|
+
program
|
|
15
|
+
.command('diff <slug> <v1> <v2>')
|
|
16
|
+
.description('두 버전의 패키지를 비교합니다')
|
|
17
|
+
.action(async (slugInput, v1, v2) => {
|
|
18
|
+
const json = program.opts().json ?? false;
|
|
19
|
+
try {
|
|
20
|
+
const resolved = await (0, slug_js_1.resolveSlug)(slugInput);
|
|
21
|
+
const versions = await (0, api_js_1.fetchAgentVersions)(resolved.full);
|
|
22
|
+
const info = await (0, api_js_1.fetchAgentInfo)(resolved.full);
|
|
23
|
+
const ver1 = versions.find((v) => v.version === v1);
|
|
24
|
+
const ver2 = versions.find((v) => v.version === v2);
|
|
25
|
+
if (!ver1 || !ver2) {
|
|
26
|
+
throw new Error(`버전을 찾을 수 없습니다. 사용 가능: ${versions.map((v) => v.version).join(', ')}`);
|
|
27
|
+
}
|
|
28
|
+
if (!json) {
|
|
29
|
+
console.log(`\n\x1b[1m${resolved.full}\x1b[0m v${v1} ↔ v${v2} 비교 중...\n`);
|
|
30
|
+
}
|
|
31
|
+
// Use git diff if git_url is available
|
|
32
|
+
if (info.git_url) {
|
|
33
|
+
(0, git_operations_js_1.checkGitInstalled)();
|
|
34
|
+
const tempDir = fs_1.default.mkdtempSync(path_1.default.join(os_1.default.tmpdir(), 'relay-diff-'));
|
|
35
|
+
try {
|
|
36
|
+
(0, git_operations_js_1.gitClone)(info.git_url, tempDir);
|
|
37
|
+
const diffOutput = (0, git_operations_js_1.gitDiff)(tempDir, `v${v1}`, `v${v2}`);
|
|
38
|
+
if (json) {
|
|
39
|
+
console.log(JSON.stringify({
|
|
40
|
+
slug: resolved.full,
|
|
41
|
+
v1: { version: v1, created_at: ver1.created_at, changelog: ver1.changelog },
|
|
42
|
+
v2: { version: v2, created_at: ver2.created_at, changelog: ver2.changelog },
|
|
43
|
+
diff: diffOutput,
|
|
44
|
+
}));
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
if (diffOutput.trim()) {
|
|
48
|
+
console.log(diffOutput);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
console.log(' 변경 사항이 없습니다.');
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
finally {
|
|
56
|
+
fs_1.default.rmSync(tempDir, { recursive: true, force: true });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
// Fallback: show version info only (no git URL available)
|
|
61
|
+
if (json) {
|
|
62
|
+
console.log(JSON.stringify({
|
|
63
|
+
slug: resolved.full,
|
|
64
|
+
v1: { version: v1, created_at: ver1.created_at, changelog: ver1.changelog },
|
|
65
|
+
v2: { version: v2, created_at: ver2.created_at, changelog: ver2.changelog },
|
|
66
|
+
}));
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
console.log(` v${v1} (${new Date(ver1.created_at).toLocaleDateString('ko-KR')})`);
|
|
70
|
+
if (ver1.changelog)
|
|
71
|
+
console.log(` ${ver1.changelog}`);
|
|
72
|
+
console.log();
|
|
73
|
+
console.log(` v${v2} (${new Date(ver2.created_at).toLocaleDateString('ko-KR')})`);
|
|
74
|
+
if (ver2.changelog)
|
|
75
|
+
console.log(` ${ver2.changelog}`);
|
|
76
|
+
console.log();
|
|
77
|
+
console.log(`\x1b[33m git 기반 diff는 새로 배포된 에이전트에서만 지원됩니다.\x1b[0m`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
83
|
+
if (json) {
|
|
84
|
+
console.error(JSON.stringify({ error: 'DIFF_FAILED', message }));
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
console.error(`\x1b[31m오류: ${message}\x1b[0m`);
|
|
88
|
+
}
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerFeedback = registerFeedback;
|
|
4
|
+
const config_js_1 = require("../lib/config.js");
|
|
5
|
+
const device_hash_js_1 = require("../lib/device-hash.js");
|
|
6
|
+
function registerFeedback(program) {
|
|
7
|
+
program
|
|
8
|
+
.command('feedback <message>')
|
|
9
|
+
.description('피드백을 전송합니다')
|
|
10
|
+
.action(async (message) => {
|
|
11
|
+
const json = program.opts().json ?? false;
|
|
12
|
+
const deviceHash = (0, device_hash_js_1.getDeviceHash)();
|
|
13
|
+
// 설치된 에이전트 slug 목록
|
|
14
|
+
const installed = (0, config_js_1.loadInstalled)();
|
|
15
|
+
const installedAgents = Object.keys(installed);
|
|
16
|
+
// 로그인 사용자 정보 (optional)
|
|
17
|
+
let userId;
|
|
18
|
+
let username;
|
|
19
|
+
const token = await (0, config_js_1.getValidToken)();
|
|
20
|
+
if (token) {
|
|
21
|
+
try {
|
|
22
|
+
const res = await fetch(`${config_js_1.API_URL}/api/auth/me`, {
|
|
23
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
24
|
+
signal: AbortSignal.timeout(5000),
|
|
25
|
+
});
|
|
26
|
+
if (res.ok) {
|
|
27
|
+
const me = (await res.json());
|
|
28
|
+
userId = me.id;
|
|
29
|
+
username = me.username;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
// ignore
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
try {
|
|
37
|
+
const res = await fetch(`${config_js_1.API_URL}/api/feedback`, {
|
|
38
|
+
method: 'POST',
|
|
39
|
+
headers: { 'Content-Type': 'application/json' },
|
|
40
|
+
body: JSON.stringify({
|
|
41
|
+
message,
|
|
42
|
+
user_id: userId ?? null,
|
|
43
|
+
username: username ?? null,
|
|
44
|
+
device_hash: deviceHash,
|
|
45
|
+
installed_agents: installedAgents.length > 0 ? installedAgents : null,
|
|
46
|
+
}),
|
|
47
|
+
signal: AbortSignal.timeout(10000),
|
|
48
|
+
});
|
|
49
|
+
if (!res.ok) {
|
|
50
|
+
const body = await res.text().catch(() => '');
|
|
51
|
+
throw new Error(`서버 응답 오류 (${res.status}): ${body}`);
|
|
52
|
+
}
|
|
53
|
+
if (json) {
|
|
54
|
+
console.log(JSON.stringify({ status: 'ok', message: '피드백이 전송되었습니다' }));
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
console.log('\x1b[32m✓ 피드백이 전송되었습니다. 감사합니다!\x1b[0m');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
62
|
+
if (json) {
|
|
63
|
+
console.error(JSON.stringify({ error: 'FEEDBACK_FAILED', message: errMsg }));
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
console.error(`\x1b[31m피드백 전송 실패: ${errMsg}\x1b[0m`);
|
|
67
|
+
}
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
interface AccessCodeResult {
|
|
3
|
+
status: string;
|
|
4
|
+
type: 'org' | 'agent';
|
|
5
|
+
org_id?: string;
|
|
6
|
+
agent_id?: string;
|
|
7
|
+
role?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Use an access code — the code type (org/agent) is resolved server-side.
|
|
11
|
+
* For org codes: joins the org as member.
|
|
12
|
+
* For agent codes: grants agent access (+ auto org join for org private agents).
|
|
13
|
+
*/
|
|
14
|
+
export declare function useAccessCode(code: string): Promise<AccessCodeResult>;
|
|
15
|
+
interface CreateAccessCodeResult {
|
|
16
|
+
id: string;
|
|
17
|
+
code: string;
|
|
18
|
+
type: string;
|
|
19
|
+
max_uses: number | null;
|
|
20
|
+
expires_at: string | null;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Create a new access code for an agent or org.
|
|
24
|
+
*/
|
|
25
|
+
export declare function createAccessCode(opts: {
|
|
26
|
+
type: 'org' | 'agent';
|
|
27
|
+
org_id?: string;
|
|
28
|
+
agent_id?: string;
|
|
29
|
+
max_uses?: number;
|
|
30
|
+
expires_at?: string;
|
|
31
|
+
}): Promise<CreateAccessCodeResult>;
|
|
32
|
+
export declare function registerGrant(program: Command): void;
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useAccessCode = useAccessCode;
|
|
4
|
+
exports.createAccessCode = createAccessCode;
|
|
5
|
+
exports.registerGrant = registerGrant;
|
|
6
|
+
const config_js_1 = require("../lib/config.js");
|
|
7
|
+
const init_js_1 = require("./init.js");
|
|
8
|
+
/**
|
|
9
|
+
* Use an access code — the code type (org/agent) is resolved server-side.
|
|
10
|
+
* For org codes: joins the org as member.
|
|
11
|
+
* For agent codes: grants agent access (+ auto org join for org private agents).
|
|
12
|
+
*/
|
|
13
|
+
async function useAccessCode(code) {
|
|
14
|
+
const token = await (0, config_js_1.getValidToken)();
|
|
15
|
+
if (!token) {
|
|
16
|
+
throw new Error('LOGIN_REQUIRED');
|
|
17
|
+
}
|
|
18
|
+
const res = await fetch(`${config_js_1.API_URL}/api/access-codes/${code}/use`, {
|
|
19
|
+
method: 'POST',
|
|
20
|
+
headers: {
|
|
21
|
+
'Content-Type': 'application/json',
|
|
22
|
+
Authorization: `Bearer ${token}`,
|
|
23
|
+
},
|
|
24
|
+
});
|
|
25
|
+
if (!res.ok) {
|
|
26
|
+
const body = await res.json().catch(() => ({}));
|
|
27
|
+
const errCode = body.error ?? String(res.status);
|
|
28
|
+
switch (errCode) {
|
|
29
|
+
case 'INVALID_LINK':
|
|
30
|
+
throw new Error('접근 코드가 유효하지 않거나 만료되었습니다.');
|
|
31
|
+
default:
|
|
32
|
+
throw new Error(body.message ?? `접근 코드 사용 실패 (${res.status})`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return res.json();
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Create a new access code for an agent or org.
|
|
39
|
+
*/
|
|
40
|
+
async function createAccessCode(opts) {
|
|
41
|
+
const token = await (0, config_js_1.getValidToken)();
|
|
42
|
+
if (!token) {
|
|
43
|
+
throw new Error('LOGIN_REQUIRED');
|
|
44
|
+
}
|
|
45
|
+
const res = await fetch(`${config_js_1.API_URL}/api/access-codes`, {
|
|
46
|
+
method: 'POST',
|
|
47
|
+
headers: {
|
|
48
|
+
'Content-Type': 'application/json',
|
|
49
|
+
Authorization: `Bearer ${token}`,
|
|
50
|
+
},
|
|
51
|
+
body: JSON.stringify(opts),
|
|
52
|
+
});
|
|
53
|
+
if (!res.ok) {
|
|
54
|
+
const body = await res.json().catch(() => ({}));
|
|
55
|
+
throw new Error(body.message ?? `접근 코드 생성 실패 (${res.status})`);
|
|
56
|
+
}
|
|
57
|
+
return res.json();
|
|
58
|
+
}
|
|
59
|
+
function ensureInit(json) {
|
|
60
|
+
if (!(0, init_js_1.hasGlobalUserCommands)()) {
|
|
61
|
+
if (json) {
|
|
62
|
+
console.error(JSON.stringify({ error: 'NOT_INITIALIZED', message: 'anpm init을 먼저 실행하세요.', fix: 'anpm init 실행하세요.' }));
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
console.error('\x1b[33m⚠ anpm init이 실행되지 않았습니다. 먼저 anpm init을 실행하세요.\x1b[0m');
|
|
66
|
+
}
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
function handleError(err, json) {
|
|
71
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
72
|
+
if (message === 'LOGIN_REQUIRED') {
|
|
73
|
+
if (json) {
|
|
74
|
+
console.error(JSON.stringify({ error: 'LOGIN_REQUIRED', message: '로그인이 필요합니다.', fix: 'anpm login 실행 후 재시도하세요.' }));
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
console.error('\x1b[31m오류: 로그인이 필요합니다.\x1b[0m');
|
|
78
|
+
console.error(' anpm login 을 먼저 실행하세요.');
|
|
79
|
+
}
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
if (json) {
|
|
83
|
+
console.error(JSON.stringify({ error: 'GRANT_FAILED', message, fix: '접근 코드를 확인 후 재시도하세요.' }));
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
console.error(`\x1b[31m오류: ${message}\x1b[0m`);
|
|
87
|
+
}
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
function registerGrant(program) {
|
|
91
|
+
const grant = program
|
|
92
|
+
.command('grant')
|
|
93
|
+
.description('접근 코드를 사용하거나 생성합니다');
|
|
94
|
+
// relay grant --code <code> (use an access code)
|
|
95
|
+
grant
|
|
96
|
+
.command('use')
|
|
97
|
+
.description('접근 코드를 사용하여 org 가입 또는 에이전트 접근 권한을 획득합니다')
|
|
98
|
+
.requiredOption('--code <code>', '접근 코드')
|
|
99
|
+
.action(async (opts) => {
|
|
100
|
+
const json = program.opts().json ?? false;
|
|
101
|
+
ensureInit(json);
|
|
102
|
+
try {
|
|
103
|
+
const result = await useAccessCode(opts.code);
|
|
104
|
+
if (json) {
|
|
105
|
+
console.log(JSON.stringify({ ...result, status: 'ok' }));
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
if (result.type === 'org') {
|
|
109
|
+
console.log(`\x1b[32m✅ Organization에 가입했습니다 (역할: ${result.role ?? 'member'})\x1b[0m`);
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
console.log(`\x1b[32m✅ 에이전트 접근 권한이 부여되었습니다\x1b[0m`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
handleError(err, json);
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
// relay grant create --agent <slug> [--max-uses N] [--expires-at DATE]
|
|
121
|
+
grant
|
|
122
|
+
.command('create')
|
|
123
|
+
.description('에이전트 또는 org의 접근 코드를 생성합니다')
|
|
124
|
+
.option('--agent <slug>', '에이전트 slug')
|
|
125
|
+
.option('--org <slug>', 'Organization slug')
|
|
126
|
+
.option('--max-uses <n>', '최대 사용 횟수', parseInt)
|
|
127
|
+
.option('--expires-at <date>', '만료일 (ISO 8601)')
|
|
128
|
+
.action(async (opts) => {
|
|
129
|
+
const json = program.opts().json ?? false;
|
|
130
|
+
ensureInit(json);
|
|
131
|
+
if (!opts.agent && !opts.org) {
|
|
132
|
+
const msg = '--agent 또는 --org 옵션이 필요합니다.';
|
|
133
|
+
if (json) {
|
|
134
|
+
console.error(JSON.stringify({ error: 'MISSING_OPTION', message: msg }));
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
console.error(`\x1b[31m오류: ${msg}\x1b[0m`);
|
|
138
|
+
}
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
const token = await (0, config_js_1.getValidToken)();
|
|
143
|
+
if (!token)
|
|
144
|
+
throw new Error('LOGIN_REQUIRED');
|
|
145
|
+
// Resolve agent/org ID from slug
|
|
146
|
+
let agentId;
|
|
147
|
+
let orgId;
|
|
148
|
+
if (opts.agent) {
|
|
149
|
+
const res = await fetch(`${config_js_1.API_URL}/api/agents/${opts.agent}`, {
|
|
150
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
151
|
+
});
|
|
152
|
+
if (!res.ok)
|
|
153
|
+
throw new Error('에이전트를 찾을 수 없습니다.');
|
|
154
|
+
const agent = await res.json();
|
|
155
|
+
agentId = agent.id;
|
|
156
|
+
}
|
|
157
|
+
if (opts.org) {
|
|
158
|
+
const res = await fetch(`${config_js_1.API_URL}/api/orgs/${opts.org}`, {
|
|
159
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
160
|
+
});
|
|
161
|
+
if (!res.ok)
|
|
162
|
+
throw new Error('Organization을 찾을 수 없습니다.');
|
|
163
|
+
const org = await res.json();
|
|
164
|
+
orgId = org.id;
|
|
165
|
+
}
|
|
166
|
+
const result = await createAccessCode({
|
|
167
|
+
type: agentId ? 'agent' : 'org',
|
|
168
|
+
agent_id: agentId,
|
|
169
|
+
org_id: orgId,
|
|
170
|
+
max_uses: opts.maxUses,
|
|
171
|
+
expires_at: opts.expiresAt,
|
|
172
|
+
});
|
|
173
|
+
if (json) {
|
|
174
|
+
console.log(JSON.stringify({ status: 'created', ...result }));
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
console.log(`\x1b[32m✅ 접근 코드가 생성되었습니다\x1b[0m`);
|
|
178
|
+
console.log(`\n 코드: \x1b[36m${result.code}\x1b[0m`);
|
|
179
|
+
if (result.max_uses)
|
|
180
|
+
console.log(` 최대 사용: ${result.max_uses}회`);
|
|
181
|
+
if (result.expires_at)
|
|
182
|
+
console.log(` 만료: ${new Date(result.expires_at).toLocaleDateString('ko-KR')}`);
|
|
183
|
+
console.log(`\n \x1b[90m사용 방법: anpm grant use --code ${result.code}\x1b[0m`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
catch (err) {
|
|
187
|
+
handleError(err, json);
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
}
|