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.
Files changed (139) hide show
  1. package/README.md +174 -0
  2. package/dist/commands/access.d.ts +2 -0
  3. package/dist/commands/access.js +90 -0
  4. package/dist/commands/adopt.d.ts +2 -0
  5. package/dist/commands/adopt.js +177 -0
  6. package/dist/commands/changelog.d.ts +2 -0
  7. package/dist/commands/changelog.js +67 -0
  8. package/dist/commands/check-update.d.ts +2 -0
  9. package/dist/commands/check-update.js +76 -0
  10. package/dist/commands/config.d.ts +2 -0
  11. package/dist/commands/config.js +84 -0
  12. package/dist/commands/create.d.ts +2 -0
  13. package/dist/commands/create.js +227 -0
  14. package/dist/commands/deploy-record.d.ts +2 -0
  15. package/dist/commands/deploy-record.js +93 -0
  16. package/dist/commands/deploy.d.ts +2 -0
  17. package/dist/commands/deploy.js +284 -0
  18. package/dist/commands/diff.d.ts +2 -0
  19. package/dist/commands/diff.js +92 -0
  20. package/dist/commands/feedback.d.ts +2 -0
  21. package/dist/commands/feedback.js +71 -0
  22. package/dist/commands/grant.d.ts +33 -0
  23. package/dist/commands/grant.js +190 -0
  24. package/dist/commands/hub.d.ts +2 -0
  25. package/dist/commands/hub.js +171 -0
  26. package/dist/commands/init.d.ts +13 -0
  27. package/dist/commands/init.js +172 -0
  28. package/dist/commands/install.d.ts +2 -0
  29. package/dist/commands/install.js +626 -0
  30. package/dist/commands/join.d.ts +6 -0
  31. package/dist/commands/join.js +90 -0
  32. package/dist/commands/link.d.ts +2 -0
  33. package/dist/commands/link.js +112 -0
  34. package/dist/commands/list.d.ts +2 -0
  35. package/dist/commands/list.js +144 -0
  36. package/dist/commands/login.d.ts +7 -0
  37. package/dist/commands/login.js +235 -0
  38. package/dist/commands/orgs.d.ts +10 -0
  39. package/dist/commands/orgs.js +128 -0
  40. package/dist/commands/outdated.d.ts +2 -0
  41. package/dist/commands/outdated.js +70 -0
  42. package/dist/commands/package.d.ts +57 -0
  43. package/dist/commands/package.js +569 -0
  44. package/dist/commands/ping.d.ts +2 -0
  45. package/dist/commands/ping.js +40 -0
  46. package/dist/commands/publish.d.ts +98 -0
  47. package/dist/commands/publish.js +899 -0
  48. package/dist/commands/run.d.ts +2 -0
  49. package/dist/commands/run.js +249 -0
  50. package/dist/commands/search.d.ts +2 -0
  51. package/dist/commands/search.js +57 -0
  52. package/dist/commands/status.d.ts +2 -0
  53. package/dist/commands/status.js +159 -0
  54. package/dist/commands/uninstall.d.ts +2 -0
  55. package/dist/commands/uninstall.js +132 -0
  56. package/dist/commands/update.d.ts +2 -0
  57. package/dist/commands/update.js +171 -0
  58. package/dist/commands/versions.d.ts +2 -0
  59. package/dist/commands/versions.js +44 -0
  60. package/dist/index.d.ts +2 -0
  61. package/dist/index.js +91 -0
  62. package/dist/lib/agent-status.d.ts +23 -0
  63. package/dist/lib/agent-status.js +127 -0
  64. package/dist/lib/ai-tools.d.ts +34 -0
  65. package/dist/lib/ai-tools.js +104 -0
  66. package/dist/lib/anpm-config.d.ts +39 -0
  67. package/dist/lib/anpm-config.js +112 -0
  68. package/dist/lib/api.d.ts +24 -0
  69. package/dist/lib/api.js +151 -0
  70. package/dist/lib/auto-detect.d.ts +30 -0
  71. package/dist/lib/auto-detect.js +112 -0
  72. package/dist/lib/cloud-providers/anthropic.d.ts +19 -0
  73. package/dist/lib/cloud-providers/anthropic.js +200 -0
  74. package/dist/lib/cloud-providers/package-mapper.d.ts +9 -0
  75. package/dist/lib/cloud-providers/package-mapper.js +34 -0
  76. package/dist/lib/cloud-providers/provider.d.ts +60 -0
  77. package/dist/lib/cloud-providers/provider.js +7 -0
  78. package/dist/lib/command-adapter.d.ts +41 -0
  79. package/dist/lib/command-adapter.js +188 -0
  80. package/dist/lib/config.d.ts +50 -0
  81. package/dist/lib/config.js +274 -0
  82. package/dist/lib/contact-format.d.ts +7 -0
  83. package/dist/lib/contact-format.js +23 -0
  84. package/dist/lib/device-hash.d.ts +1 -0
  85. package/dist/lib/device-hash.js +16 -0
  86. package/dist/lib/error-report.d.ts +5 -0
  87. package/dist/lib/error-report.js +28 -0
  88. package/dist/lib/git-installer.d.ts +16 -0
  89. package/dist/lib/git-installer.js +97 -0
  90. package/dist/lib/git-operations.d.ts +38 -0
  91. package/dist/lib/git-operations.js +183 -0
  92. package/dist/lib/hub-notify.d.ts +9 -0
  93. package/dist/lib/hub-notify.js +66 -0
  94. package/dist/lib/install-source.d.ts +33 -0
  95. package/dist/lib/install-source.js +98 -0
  96. package/dist/lib/installer.d.ts +40 -0
  97. package/dist/lib/installer.js +358 -0
  98. package/dist/lib/local-installer.d.ts +15 -0
  99. package/dist/lib/local-installer.js +73 -0
  100. package/dist/lib/lockfile.d.ts +13 -0
  101. package/dist/lib/lockfile.js +42 -0
  102. package/dist/lib/manifest.d.ts +65 -0
  103. package/dist/lib/manifest.js +113 -0
  104. package/dist/lib/migration.d.ts +10 -0
  105. package/dist/lib/migration.js +91 -0
  106. package/dist/lib/paths.d.ts +10 -0
  107. package/dist/lib/paths.js +22 -0
  108. package/dist/lib/preamble.d.ts +22 -0
  109. package/dist/lib/preamble.js +133 -0
  110. package/dist/lib/relay-config.d.ts +13 -0
  111. package/dist/lib/relay-config.js +46 -0
  112. package/dist/lib/requires-suggest.d.ts +23 -0
  113. package/dist/lib/requires-suggest.js +295 -0
  114. package/dist/lib/setup-command.d.ts +6 -0
  115. package/dist/lib/setup-command.js +72 -0
  116. package/dist/lib/slug.d.ts +24 -0
  117. package/dist/lib/slug.js +100 -0
  118. package/dist/lib/step-tracker.d.ts +8 -0
  119. package/dist/lib/step-tracker.js +28 -0
  120. package/dist/lib/storage.d.ts +6 -0
  121. package/dist/lib/storage.js +23 -0
  122. package/dist/lib/update-cache.d.ts +2 -0
  123. package/dist/lib/update-cache.js +51 -0
  124. package/dist/lib/version-check.d.ts +10 -0
  125. package/dist/lib/version-check.js +75 -0
  126. package/dist/mcp/server.d.ts +3 -0
  127. package/dist/mcp/server.js +112 -0
  128. package/dist/postinstall.d.ts +8 -0
  129. package/dist/postinstall.js +41 -0
  130. package/dist/prompts/_error-handling.md +38 -0
  131. package/dist/prompts/create.md +170 -0
  132. package/dist/prompts/explore.md +30 -0
  133. package/dist/prompts/index.d.ts +3 -0
  134. package/dist/prompts/index.js +22 -0
  135. package/dist/relay-compat.d.ts +2 -0
  136. package/dist/relay-compat.js +7 -0
  137. package/dist/types.d.ts +118 -0
  138. package/dist/types.js +2 -0
  139. package/package.json +51 -0
@@ -0,0 +1,626 @@
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.registerInstall = registerInstall;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const os_1 = __importDefault(require("os"));
9
+ const path_1 = __importDefault(require("path"));
10
+ const api_js_1 = require("../lib/api.js");
11
+ const storage_js_1 = require("../lib/storage.js");
12
+ const git_operations_js_1 = require("../lib/git-operations.js");
13
+ const config_js_1 = require("../lib/config.js");
14
+ const slug_js_1 = require("../lib/slug.js");
15
+ const preamble_js_1 = require("../lib/preamble.js");
16
+ const init_js_1 = require("./init.js");
17
+ const paths_js_1 = require("../lib/paths.js");
18
+ const error_report_js_1 = require("../lib/error-report.js");
19
+ const step_tracker_js_1 = require("../lib/step-tracker.js");
20
+ const installer_js_1 = require("../lib/installer.js");
21
+ const ai_tools_js_1 = require("../lib/ai-tools.js");
22
+ const install_source_js_1 = require("../lib/install-source.js");
23
+ const local_installer_js_1 = require("../lib/local-installer.js");
24
+ const git_installer_js_1 = require("../lib/git-installer.js");
25
+ const auto_detect_js_1 = require("../lib/auto-detect.js");
26
+ const manifest_js_1 = require("../lib/manifest.js");
27
+ const lockfile_js_1 = require("../lib/lockfile.js");
28
+ function registerInstall(program) {
29
+ program
30
+ .command('install [slug]')
31
+ .description('Install an agent package to .relay/agents/')
32
+ .option('--code <code>', 'Access code (for private/internal agents)')
33
+ .option('--global', 'Global install (home directory)')
34
+ .option('--local', 'Local install (project directory)')
35
+ .option('--project <dir>', 'Project root path (default: cwd, env: RELAY_PROJECT_PATH)')
36
+ .option('--path <subpath>', 'Subpath within a monorepo (for git installs)')
37
+ .option('--yes', 'Skip confirmation prompts')
38
+ .option('--save', 'Add agent to relay.yaml agents')
39
+ .option('--prune', 'Remove agents not in relay.yaml')
40
+ .action(async (slugInput, _opts) => {
41
+ const json = program.opts().json ?? false;
42
+ const projectPath = (0, paths_js_1.resolveProjectPath)(_opts.project);
43
+ // ── Manifest mode (no args) ──
44
+ if (!slugInput) {
45
+ const { manifest, filePath: manifestPath } = (0, manifest_js_1.loadManifest)(projectPath);
46
+ if (!manifest?.agents || Object.keys(manifest.agents).length === 0) {
47
+ if (json) {
48
+ console.log(JSON.stringify({ error: 'NO_MANIFEST', message: 'No anpm.yaml with agents found.' }));
49
+ }
50
+ else {
51
+ console.error('No anpm.yaml with agents found. Run `anpm install <slug>` to install an agent.');
52
+ }
53
+ process.exit(1);
54
+ }
55
+ const agents = manifest.agents;
56
+ const _lock = (0, lockfile_js_1.loadLockfile)(projectPath); // used in future for pinned versions
57
+ const localInstalled = (0, config_js_1.loadInstalled)();
58
+ let installed = 0;
59
+ let skipped = 0;
60
+ if (!json)
61
+ console.error(`\x1b[2mInstalling from ${manifestPath}...\x1b[0m`);
62
+ for (const [slug, range] of Object.entries(agents)) {
63
+ // Check if already installed with compatible version
64
+ const existing = localInstalled[slug];
65
+ if (existing && (0, manifest_js_1.satisfiesRange)(existing.version, range)) {
66
+ skipped++;
67
+ continue;
68
+ }
69
+ try {
70
+ const source = (0, install_source_js_1.parseInstallSource)(slug);
71
+ if (source.type === 'local') {
72
+ (0, local_installer_js_1.installFromLocal)(source.absolutePath, { scope: 'local', projectPath });
73
+ }
74
+ else if (source.type === 'git') {
75
+ (0, git_installer_js_1.installFromGit)(source, { scope: 'local', projectPath });
76
+ }
77
+ else {
78
+ // Registry install — reuse the existing flow by recursing
79
+ // For now, use a simplified approach: call the single-agent install logic
80
+ if (!json)
81
+ console.error(` Installing ${slug}...`);
82
+ // We'll let the user run `relay install <slug>` for registry agents
83
+ // Full manifest registry install requires the same token/fetch flow
84
+ if (!json)
85
+ console.error(` \x1b[33m⚠ Registry agent ${slug} — run: anpm install ${slug} --save\x1b[0m`);
86
+ continue;
87
+ }
88
+ // Update lock
89
+ (0, lockfile_js_1.updateLockEntry)(projectPath, slug, {
90
+ version: existing?.version ?? '0.0.0',
91
+ resolved: source.type === 'local' ? `local:${source.absolutePath}` : `git:${slug}`,
92
+ });
93
+ installed++;
94
+ if (!json)
95
+ console.error(` \x1b[32m✓\x1b[0m ${slug}`);
96
+ }
97
+ catch (err) {
98
+ const msg = err instanceof Error ? err.message : String(err);
99
+ if (!json)
100
+ console.error(` \x1b[31m✖\x1b[0m ${slug}: ${msg}`);
101
+ }
102
+ }
103
+ if (json) {
104
+ console.log(JSON.stringify({ status: 'ok', installed, skipped, total: Object.keys(agents).length }));
105
+ }
106
+ else {
107
+ console.log(`\n\x1b[32m✓\x1b[0m Manifest install complete: ${installed} installed, ${skipped} skipped`);
108
+ }
109
+ return;
110
+ }
111
+ (0, step_tracker_js_1.trackCommand)('install', { slug: slugInput });
112
+ // ── Source type branching ──
113
+ const source = (0, install_source_js_1.parseInstallSource)(slugInput);
114
+ if (source.type === 'local' || source.type === 'git') {
115
+ try {
116
+ const interactive = Boolean(process.stdin.isTTY) && !json;
117
+ const scope = _opts.global ? 'global' : 'local';
118
+ let agentDir;
119
+ let installSlug;
120
+ let sourceTag;
121
+ if (source.type === 'local') {
122
+ if (!json)
123
+ console.error(`\x1b[2mInstalling from local path: ${source.absolutePath}\x1b[0m`);
124
+ const result = (0, local_installer_js_1.installFromLocal)(source.absolutePath, { scope, projectPath });
125
+ // Confirm auto-detected structure if no relay.yaml
126
+ if (result.detected.method !== 'relay-yaml' && interactive && !_opts.yes) {
127
+ console.error(`\n Detected structure (${result.detected.method}):`);
128
+ console.error((0, auto_detect_js_1.formatDetectedStructure)(result.detected));
129
+ const { confirm } = await import('@inquirer/prompts');
130
+ const ok = await confirm({ message: 'Install these?', default: true });
131
+ if (!ok) {
132
+ process.exit(0);
133
+ }
134
+ }
135
+ agentDir = result.agentDir;
136
+ installSlug = `local/${result.name}`;
137
+ sourceTag = `local:${source.absolutePath}`;
138
+ }
139
+ else {
140
+ if (!json)
141
+ console.error(`\x1b[2mInstalling from git: ${source.url}${source.ref ? `#${source.ref}` : ''}\x1b[0m`);
142
+ const result = (0, git_installer_js_1.installFromGit)(source, { scope, projectPath, subpath: _opts.path });
143
+ if (result.detected.method !== 'relay-yaml' && interactive && !_opts.yes) {
144
+ console.error(`\n Detected structure (${result.detected.method}):`);
145
+ console.error((0, auto_detect_js_1.formatDetectedStructure)(result.detected));
146
+ const { confirm } = await import('@inquirer/prompts');
147
+ const ok = await confirm({ message: 'Install these?', default: true });
148
+ if (!ok) {
149
+ process.exit(0);
150
+ }
151
+ }
152
+ agentDir = result.agentDir;
153
+ installSlug = result.slug;
154
+ sourceTag = `git:${source.host}:${source.user}/${source.repo}${source.ref ? `#${source.ref}` : ''}`;
155
+ }
156
+ // Deploy symlinks to detected harnesses
157
+ const deploy = await (0, installer_js_1.deploySymlinks)(agentDir, scope, projectPath);
158
+ for (const w of deploy.warnings) {
159
+ if (!json)
160
+ console.error(`\x1b[33m${w}\x1b[0m`);
161
+ }
162
+ // Record in installed.json
163
+ const installRecord = {
164
+ version: '0.0.0',
165
+ installed_at: new Date().toISOString(),
166
+ files: [agentDir],
167
+ deploy_scope: scope,
168
+ deployed_symlinks: deploy.symlinks,
169
+ source: sourceTag,
170
+ };
171
+ if (scope === 'global') {
172
+ const globalInstalled = (0, config_js_1.loadGlobalInstalled)();
173
+ globalInstalled[installSlug] = installRecord;
174
+ (0, config_js_1.saveGlobalInstalled)(globalInstalled);
175
+ }
176
+ else {
177
+ const installed = (0, config_js_1.loadInstalled)();
178
+ installed[installSlug] = installRecord;
179
+ (0, config_js_1.saveInstalled)(installed);
180
+ }
181
+ // Count files
182
+ function countFilesInDir(dir) {
183
+ let count = 0;
184
+ if (!fs_1.default.existsSync(dir))
185
+ return 0;
186
+ for (const entry of fs_1.default.readdirSync(dir, { withFileTypes: true })) {
187
+ if (entry.isDirectory())
188
+ count += countFilesInDir(path_1.default.join(dir, entry.name));
189
+ else
190
+ count++;
191
+ }
192
+ return count;
193
+ }
194
+ const fileCount = countFilesInDir(agentDir);
195
+ const scopeLabel = scope === 'global' ? 'global' : 'local';
196
+ if (json) {
197
+ console.log(JSON.stringify({
198
+ status: 'ok',
199
+ slug: installSlug,
200
+ source: sourceTag,
201
+ files: fileCount,
202
+ install_path: agentDir,
203
+ scope,
204
+ symlinks: deploy.symlinks,
205
+ }));
206
+ }
207
+ else {
208
+ console.log(`\n\x1b[32m✓ Installed ${installSlug}\x1b[0m`);
209
+ console.log(` path: \x1b[36m${agentDir}\x1b[0m`);
210
+ console.log(` scope: ${scopeLabel}`);
211
+ console.log(` files: ${fileCount}, symlinks: ${deploy.symlinks.length}`);
212
+ }
213
+ // --save: add to relay.yaml
214
+ if (_opts.save) {
215
+ (0, manifest_js_1.addAgentToManifest)(projectPath, installSlug, '*');
216
+ if (!json)
217
+ console.log(` \x1b[32m✓\x1b[0m Added to anpm.yaml`);
218
+ }
219
+ return;
220
+ }
221
+ catch (err) {
222
+ const message = err instanceof Error ? err.message : String(err);
223
+ (0, error_report_js_1.reportCliError)('install', 'INSTALL_FAILED', message);
224
+ if (json) {
225
+ console.error(JSON.stringify({ error: 'INSTALL_FAILED', message }));
226
+ }
227
+ else {
228
+ console.error(`\x1b[31m✖ ${message}\x1b[0m`);
229
+ }
230
+ process.exit(1);
231
+ }
232
+ }
233
+ // ── Registry install flow (existing) ──
234
+ const tempDir = (0, storage_js_1.makeTempDir)();
235
+ try {
236
+ // Resolve scoped slug and fetch agent metadata
237
+ let agent;
238
+ let slug;
239
+ let parsed;
240
+ // Extract version from @owner/agent@version syntax (e.g. acme/writer@1.2.0)
241
+ // Version-specific install is not yet supported by the registry API;
242
+ // the match is kept for future use when per-version package URLs are available.
243
+ const versionMatch = slugInput.match(/^(.+)@(\d+\.\d+\.\d+.*)$/);
244
+ const actualSlugInput = versionMatch ? versionMatch[1] : slugInput;
245
+ parsed = await (0, slug_js_1.resolveSlug)(actualSlugInput);
246
+ slug = parsed.full;
247
+ // Helper: ensure a valid token exists, triggering auto-login in TTY if needed.
248
+ // Returns the token string or null if login failed / not available.
249
+ async function ensureToken() {
250
+ let token = await (0, config_js_1.getValidToken)();
251
+ if (!token) {
252
+ const isTTY = Boolean(process.stdin.isTTY);
253
+ if (isTTY && !json) {
254
+ console.error('\x1b[33m⚙ 이 에이전트는 로그인이 필요합니다. 로그인을 시작합니다...\x1b[0m');
255
+ const { runLogin } = await import('./login.js');
256
+ await runLogin();
257
+ token = await (0, config_js_1.getValidToken)();
258
+ }
259
+ }
260
+ return token ?? null;
261
+ }
262
+ // Pre-fetch auto-login: --code always requires auth.
263
+ if (_opts.code) {
264
+ const token = await ensureToken();
265
+ if (!token) {
266
+ if (json) {
267
+ console.error(JSON.stringify({
268
+ error: 'LOGIN_REQUIRED',
269
+ slug,
270
+ message: '이 에이전트는 로그인이 필요합니다. anpm login을 먼저 실행하세요.',
271
+ fix: 'anpm login 실행 후 재시도하세요.',
272
+ }));
273
+ }
274
+ else {
275
+ console.error('\x1b[31m이 에이전트는 로그인이 필요합니다. anpm login 을 먼저 실행하세요.\x1b[0m');
276
+ }
277
+ process.exit(1);
278
+ }
279
+ }
280
+ try {
281
+ agent = await (0, api_js_1.fetchAgentInfo)(slug);
282
+ }
283
+ catch (fetchErr) {
284
+ const fetchMsg = fetchErr instanceof Error ? fetchErr.message : String(fetchErr);
285
+ if (fetchMsg.includes('403')) {
286
+ // Parse error body for membership_status, visibility, purchase_info
287
+ let membershipStatus;
288
+ let errorVisibility;
289
+ let purchaseInfo;
290
+ try {
291
+ const errBody = JSON.parse(fetchMsg.replace(/^.*?(\{)/, '{'));
292
+ membershipStatus = typeof errBody.membership_status === 'string' ? errBody.membership_status : undefined;
293
+ errorVisibility = typeof errBody.visibility === 'string' ? errBody.visibility : undefined;
294
+ if (errBody.purchase_info && typeof errBody.purchase_info === 'object') {
295
+ purchaseInfo = errBody.purchase_info;
296
+ }
297
+ }
298
+ catch { /* ignore parse errors */ }
299
+ // --code provided → use unified access-codes API (handles both org join and agent grant)
300
+ if (_opts.code) {
301
+ if (!json) {
302
+ console.error('\x1b[33m⚙ 접근 코드로 권한을 요청합니다...\x1b[0m');
303
+ }
304
+ const token = await (0, config_js_1.getValidToken)();
305
+ if (!token) {
306
+ if (json) {
307
+ console.error(JSON.stringify({
308
+ error: 'LOGIN_REQUIRED',
309
+ slug,
310
+ message: '이 에이전트는 로그인이 필요합니다. anpm login을 먼저 실행하세요.',
311
+ fix: 'anpm login 실행 후 재시도하세요.',
312
+ }));
313
+ }
314
+ else {
315
+ console.error('\x1b[31m이 에이전트는 로그인이 필요합니다. anpm login 을 먼저 실행하세요.\x1b[0m');
316
+ }
317
+ process.exit(1);
318
+ }
319
+ const codeRes = await fetch(`${config_js_1.API_URL}/api/access-codes/${_opts.code}/use`, {
320
+ method: 'POST',
321
+ headers: {
322
+ 'Content-Type': 'application/json',
323
+ Authorization: `Bearer ${token}`,
324
+ },
325
+ signal: AbortSignal.timeout(10000),
326
+ });
327
+ if (!codeRes.ok) {
328
+ const codeBody = (await codeRes.json().catch(() => ({})));
329
+ const codeErrCode = codeBody.error ?? String(codeRes.status);
330
+ if (codeErrCode === 'INVALID_LINK')
331
+ throw new Error('접근 코드가 유효하지 않거나 만료되었습니다.');
332
+ throw new Error(codeBody.message ?? `접근 권한 요청 실패 (${codeRes.status})`);
333
+ }
334
+ agent = await (0, api_js_1.fetchAgentInfo)(slug);
335
+ }
336
+ // No code provided: show appropriate error messages
337
+ else if (errorVisibility === 'private' || purchaseInfo) {
338
+ // Private agent: show purchase info + relay access hint
339
+ if (json) {
340
+ console.error(JSON.stringify({
341
+ error: 'ACCESS_REQUIRED',
342
+ message: '이 에이전트는 접근 권한이 필요합니다.',
343
+ slug,
344
+ purchase_info: purchaseInfo ?? null,
345
+ fix: '접근 링크 코드가 있으면: anpm install ' + slugInput + ' --code <코드>',
346
+ }));
347
+ }
348
+ else {
349
+ console.error('\x1b[31m이 에이전트는 접근 권한이 필요합니다.\x1b[0m');
350
+ if (purchaseInfo?.message) {
351
+ console.error(`\n \x1b[36m${purchaseInfo.message}\x1b[0m`);
352
+ }
353
+ if (purchaseInfo?.url) {
354
+ console.error(` \x1b[36m${purchaseInfo.url}\x1b[0m`);
355
+ }
356
+ console.error(`\n\x1b[33m접근 링크 코드가 있으면: anpm install ${slugInput} --code <코드>\x1b[0m`);
357
+ }
358
+ process.exit(1);
359
+ }
360
+ else if (membershipStatus === 'member') {
361
+ // Member but no access to this specific agent
362
+ if (json) {
363
+ console.error(JSON.stringify({
364
+ error: 'NO_ACCESS',
365
+ message: '이 에이전트에 대한 접근 권한이 없습니다.',
366
+ slug,
367
+ fix: '이 에이전트의 접근 링크 코드가 있으면 `anpm install ' + slugInput + ' --code <코드>`로 접근 권한을 얻으세요. 없으면 에이전트 제작자에게 문의하세요.',
368
+ }));
369
+ }
370
+ else {
371
+ console.error('\x1b[31m이 에이전트에 대한 접근 권한이 없습니다.\x1b[0m');
372
+ }
373
+ process.exit(1);
374
+ }
375
+ else {
376
+ if (json) {
377
+ console.error(JSON.stringify({
378
+ error: 'ACCESS_REQUIRED',
379
+ message: '이 에이전트는 접근 권한이 필요합니다.',
380
+ slug,
381
+ fix: '접근 코드가 있으면 `anpm install ' + slugInput + ' --code <코드>`로 설치하세요.',
382
+ }));
383
+ }
384
+ else {
385
+ console.error('\x1b[31m이 에이전트는 접근 권한이 필요합니다.\x1b[0m');
386
+ console.error('\x1b[33m접근 코드가 있으면 `anpm install ' + slugInput + ' --code <코드>`로 설치하세요.\x1b[0m');
387
+ }
388
+ process.exit(1);
389
+ }
390
+ }
391
+ else {
392
+ throw fetchErr;
393
+ }
394
+ }
395
+ if (!agent)
396
+ throw new Error('에이전트 정보를 가져오지 못했습니다.');
397
+ // Re-bind as non-optional so TypeScript tracks the narrowing through nested scopes
398
+ let resolvedAgent = agent;
399
+ const isTTY = Boolean(process.stdin.isTTY);
400
+ const interactive = isTTY && !json;
401
+ const defaultScope = resolvedAgent.recommended_scope ?? (resolvedAgent.type === 'passive' ? 'local' : 'global');
402
+ // ── 1. AI tools 선택 (scope 무관, 항상 홈 디렉토리에서 감지) ──
403
+ let selectedTools;
404
+ if (interactive) {
405
+ const detected = (0, ai_tools_js_1.detectGlobalCLIs)();
406
+ if (!detected.some((t) => t.value === 'claude')) {
407
+ detected.push({ name: 'Claude Code', value: 'claude', skillsDir: '.claude' });
408
+ }
409
+ const detectedValues = new Set(detected.map((t) => t.value));
410
+ const { checkbox } = await import('@inquirer/prompts');
411
+ selectedTools = await checkbox({
412
+ message: '설치할 AI 도구를 선택하세요 (감지된 도구는 자동 선택됨)',
413
+ choices: ai_tools_js_1.AI_TOOLS.map((t) => ({
414
+ name: t.name,
415
+ value: t,
416
+ checked: detectedValues.has(t.value),
417
+ })),
418
+ });
419
+ }
420
+ // ── 2. 글로벌 slash commands 설치/업데이트 ──
421
+ if (selectedTools) {
422
+ (0, init_js_1.installGlobalUserCommands)(selectedTools);
423
+ }
424
+ else if (!(0, init_js_1.hasGlobalUserCommands)()) {
425
+ if (!json) {
426
+ console.error('\x1b[33m⚙ 글로벌 커맨드를 자동 설치합니다...\x1b[0m');
427
+ }
428
+ (0, init_js_1.installGlobalUserCommands)();
429
+ }
430
+ // ── 3. Scope 결정: 플래그 > TTY prompt > 자동결정 ──
431
+ let scope;
432
+ if (_opts.global) {
433
+ scope = 'global';
434
+ }
435
+ else if (_opts.local) {
436
+ scope = 'local';
437
+ }
438
+ else if (interactive) {
439
+ const { select } = await import('@inquirer/prompts');
440
+ const recommendLabel = defaultScope === 'global' ? '글로벌' : '로컬';
441
+ scope = await select({
442
+ message: `설치 범위를 선택하세요 (제작자 권장: ${recommendLabel})`,
443
+ choices: [
444
+ { name: '글로벌 (~/.relay/agents/) — 모든 프로젝트에서 사용', value: 'global' },
445
+ { name: '로컬 (./.relay/agents/) — 이 프로젝트에서만 사용', value: 'local' },
446
+ ],
447
+ default: defaultScope,
448
+ });
449
+ }
450
+ else {
451
+ scope = defaultScope;
452
+ }
453
+ const agentDir = scope === 'global'
454
+ ? path_1.default.join(os_1.default.homedir(), '.relay', 'agents', parsed.owner, parsed.name)
455
+ : path_1.default.join(projectPath, '.relay', 'agents', parsed.owner, parsed.name);
456
+ // 2. 로그인 필수 (git clone에 relay token 필요)
457
+ const token = await ensureToken();
458
+ if (!token) {
459
+ if (json) {
460
+ console.error(JSON.stringify({
461
+ error: 'LOGIN_REQUIRED',
462
+ slug,
463
+ message: '로그인이 필요합니다. anpm login을 먼저 실행하세요.',
464
+ fix: 'anpm login 실행 후 재시도하세요.',
465
+ }));
466
+ }
467
+ else {
468
+ console.error('\x1b[31m로그인이 필요합니다. anpm login 을 먼저 실행하세요.\x1b[0m');
469
+ }
470
+ process.exit(1);
471
+ }
472
+ // 3. Download package via git clone
473
+ const requestedVersion = versionMatch ? versionMatch[2] : undefined;
474
+ if (!resolvedAgent.git_url) {
475
+ const errMsg = '이 에이전트는 재publish가 필요합니다. 빌더에게 문의하세요.';
476
+ if (json) {
477
+ console.log(JSON.stringify({ error: 'NO_GIT_URL', message: errMsg }));
478
+ }
479
+ else {
480
+ console.error(`\x1b[31m✖ ${errMsg}\x1b[0m`);
481
+ }
482
+ process.exit(1);
483
+ }
484
+ (0, git_operations_js_1.checkGitInstalled)();
485
+ const gitUrl = (0, git_operations_js_1.buildGitUrl)(resolvedAgent.git_url, { token });
486
+ await (0, storage_js_1.clonePackage)(gitUrl, agentDir, requestedVersion);
487
+ // Verify clone has actual files (not just .git)
488
+ const clonedEntries = fs_1.default.readdirSync(agentDir).filter((f) => f !== '.git');
489
+ if (clonedEntries.length === 0) {
490
+ fs_1.default.rmSync(agentDir, { recursive: true, force: true });
491
+ const errMsg = '에이전트 패키지가 비어있습니다. 빌더에게 재publish를 요청하세요.';
492
+ if (json) {
493
+ console.log(JSON.stringify({ error: 'EMPTY_PACKAGE', message: errMsg }));
494
+ }
495
+ else {
496
+ console.error(`\x1b[31m✖ ${errMsg}\x1b[0m`);
497
+ }
498
+ process.exit(1);
499
+ }
500
+ // 4.5. Inject preamble (update check) into SKILL.md and commands
501
+ (0, preamble_js_1.injectPreambleToAgent)(agentDir, slug);
502
+ // 5. Deploy symlinks to detected AI tool directories
503
+ const deploy = await (0, installer_js_1.deploySymlinks)(agentDir, scope, projectPath, selectedTools);
504
+ for (const w of deploy.warnings) {
505
+ if (!json)
506
+ console.error(`\x1b[33m${w}\x1b[0m`);
507
+ }
508
+ // 6. Count extracted files
509
+ function countFiles(dir) {
510
+ let count = 0;
511
+ if (!fs_1.default.existsSync(dir))
512
+ return 0;
513
+ for (const entry of fs_1.default.readdirSync(dir, { withFileTypes: true })) {
514
+ if (entry.isDirectory()) {
515
+ count += countFiles(path_1.default.join(dir, entry.name));
516
+ }
517
+ else {
518
+ count++;
519
+ }
520
+ }
521
+ return count;
522
+ }
523
+ const fileCount = countFiles(agentDir);
524
+ // 7. Record in installed.json (scope-aware)
525
+ const installRecord = {
526
+ agent_id: resolvedAgent.id,
527
+ version: resolvedAgent.version,
528
+ installed_at: new Date().toISOString(),
529
+ files: [agentDir],
530
+ deploy_scope: scope,
531
+ deployed_symlinks: deploy.symlinks,
532
+ };
533
+ if (scope === 'global') {
534
+ const globalInstalled = (0, config_js_1.loadGlobalInstalled)();
535
+ globalInstalled[slug] = installRecord;
536
+ (0, config_js_1.saveGlobalInstalled)(globalInstalled);
537
+ }
538
+ else {
539
+ const installed = (0, config_js_1.loadInstalled)();
540
+ installed[slug] = installRecord;
541
+ (0, config_js_1.saveInstalled)(installed);
542
+ }
543
+ // 8. Report install + usage ping (non-blocking, agent_id 기반)
544
+ await (0, api_js_1.reportInstall)(resolvedAgent.id, slug, resolvedAgent.version);
545
+ (0, api_js_1.sendUsagePing)(resolvedAgent.id, slug, resolvedAgent.version);
546
+ const result = {
547
+ status: 'ok',
548
+ agent: resolvedAgent.name,
549
+ slug,
550
+ version: resolvedAgent.version,
551
+ commands: resolvedAgent.commands,
552
+ files: fileCount,
553
+ install_path: agentDir,
554
+ scope,
555
+ symlinks: deploy.symlinks,
556
+ author: resolvedAgent.author ? {
557
+ username: resolvedAgent.author.username,
558
+ display_name: resolvedAgent.author.display_name ?? null,
559
+ contact_links: resolvedAgent.author.contact_links ?? [],
560
+ } : null,
561
+ welcome: resolvedAgent.welcome ?? null,
562
+ };
563
+ if (json) {
564
+ console.log(JSON.stringify(result));
565
+ }
566
+ else {
567
+ const authorUsername = resolvedAgent.author?.username;
568
+ const authorSuffix = authorUsername ? ` \x1b[90mby @${authorUsername}\x1b[0m` : '';
569
+ const scopeLabel = scope === 'global' ? '글로벌' : '로컬';
570
+ console.log(`\n\x1b[32m✓ ${resolvedAgent.name} 설치 완료\x1b[0m v${resolvedAgent.version}${authorSuffix}`);
571
+ console.log(` 위치: \x1b[36m${agentDir}\x1b[0m`);
572
+ console.log(` 범위: ${scopeLabel}`);
573
+ console.log(` 파일: ${fileCount}개, symlink: ${deploy.symlinks.length}개`);
574
+ const userCommands = resolvedAgent.commands.filter((c) => !c.name.startsWith('setup-'));
575
+ if (userCommands.length > 0) {
576
+ console.log('\n 포함된 커맨드:');
577
+ for (const cmd of userCommands) {
578
+ console.log(` \x1b[33m/${cmd.name}\x1b[0m - ${cmd.description}`);
579
+ }
580
+ }
581
+ // Usage hint (type-aware, setup command 제외)
582
+ const agentType = resolvedAgent.type;
583
+ const mainCommand = userCommands[0];
584
+ if (agentType === 'passive') {
585
+ console.log(`\n\x1b[33m💡 자동 적용됩니다. 별도 실행 없이 동작합니다.\x1b[0m`);
586
+ }
587
+ else if (agentType === 'hybrid' && mainCommand) {
588
+ console.log(`\n\x1b[33m💡 자동 적용 + \x1b[1m/${mainCommand.name}\x1b[0m\x1b[33m 으로 추가 기능을 사용할 수 있습니다.\x1b[0m`);
589
+ }
590
+ else if (mainCommand) {
591
+ console.log(`\n\x1b[33m💡 사용법: \x1b[1m/${mainCommand.name}\x1b[0m`);
592
+ }
593
+ else {
594
+ console.log(`\n\x1b[33m💡 설치 완료! AI 에이전트에서 사용할 수 있습니다.\x1b[0m`);
595
+ }
596
+ // Requires check + setup CTA
597
+ const requiresResults = (0, installer_js_1.checkRequires)(agentDir);
598
+ (0, installer_js_1.printRequiresCheck)(requiresResults);
599
+ const setupCmd = resolvedAgent.commands.find((c) => c.name.startsWith('setup-'));
600
+ if (setupCmd && requiresResults.some((r) => r.status === 'missing' || r.status === 'warn')) {
601
+ const toolNames = selectedTools
602
+ ? selectedTools.map((t) => t.name).slice(0, 2).join(' 또는 ')
603
+ : 'Claude Code';
604
+ console.log(`\n \x1b[36m👉 설정이 필요합니다.\x1b[0m`);
605
+ console.log(` \x1b[36m ${toolNames}를 열고 \x1b[1m/${setupCmd.name}\x1b[0m\x1b[36m 을 입력하세요\x1b[0m`);
606
+ }
607
+ // Cloud deploy hint
608
+ const cloudConfig = resolvedAgent.cloud_config;
609
+ if (cloudConfig) {
610
+ const providers = cloudConfig.supported_providers;
611
+ const providerStr = providers?.join(', ') ?? 'anthropic';
612
+ console.log(`\n ☁️ Cloud deploy available. Run: \x1b[36manpm deploy ${slugInput} --to ${providerStr}\x1b[0m`);
613
+ }
614
+ }
615
+ }
616
+ catch (err) {
617
+ const message = err instanceof Error ? err.message : String(err);
618
+ (0, error_report_js_1.reportCliError)('install', 'INSTALL_FAILED', message);
619
+ console.error(JSON.stringify({ error: 'INSTALL_FAILED', message, fix: message }));
620
+ process.exit(1);
621
+ }
622
+ finally {
623
+ (0, storage_js_1.removeTempDir)(tempDir);
624
+ }
625
+ });
626
+ }
@@ -0,0 +1,6 @@
1
+ import { Command } from 'commander';
2
+ export declare function joinOrg(orgSlug: string, code: string): Promise<{
3
+ type: string;
4
+ role?: string;
5
+ }>;
6
+ export declare function registerJoin(program: Command): void;