agent-resource-management 1.3.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 (30) hide show
  1. package/.claude/settings.local.json +8 -0
  2. package/README.md +196 -0
  3. package/bun.lock +32 -0
  4. package/data/skills/bad-name-skill/SKILL.md +8 -0
  5. package/data/skills/github-tool/SKILL.md +11 -0
  6. package/data/skills/invalid-skill/SKILL.md +3 -0
  7. package/data/skills/pdf-tool/SKILL.md +11 -0
  8. package/data/skills/pdf-tool.zip +0 -0
  9. package/dist/main.js +1330 -0
  10. package/dist//344/273/277/347/234/237/350/260/203/350/257/225/345/212/251/346/211/213/AGENT.md +18 -0
  11. package/dist//344/273/277/347/234/237/350/260/203/350/257/225/345/212/251/346/211/213/knowledges//344/273/277/347/234/237/345/271/263/345/217/260Loki/346/227/245/345/277/227/346/216/222/346/237/245/346/226/271/346/263/225.md +27 -0
  12. package/dist//344/273/277/347/234/237/350/260/203/350/257/225/345/212/251/346/211/213/knowledges//344/273/277/347/234/237/345/271/263/345/217/260/344/273/273/345/212/241/350/247/246/345/217/221/345/220/270/346/224/266/345/231/250/344/270/216/345/215/240/344/275/215/350/247/246/345/217/221/345/220/270/346/224/266/345/231/250/344/275/277/347/224/250/345/234/272/346/231/257.md +18 -0
  13. package/dist//344/273/277/347/234/237/350/260/203/350/257/225/345/212/251/346/211/213/knowledges//344/273/277/347/234/237/345/271/263/345/217/260/345/220/216/347/253/257Swagger/346/216/245/345/217/243/346/226/207/346/241/243.md +22 -0
  14. package/dist//344/273/277/347/234/237/350/260/203/350/257/225/345/212/251/346/211/213/knowledges//344/273/277/347/234/237/345/271/263/345/217/260/345/275/223/345/211/215/350/277/220/350/241/214/345/256/236/344/276/213/345/217/212/345/256/236/344/276/213/350/277/220/350/241/214/346/227/245/345/277/227/346/216/222/346/237/245.md +39 -0
  15. package/dist//344/273/277/347/234/237/350/260/203/350/257/225/345/212/251/346/211/213/knowledges//344/273/277/347/234/237/345/271/263/345/217/260/346/216/245/345/217/243/346/216/222/346/237/245/350/203/214/346/231/257/347/237/245/350/257/206.md +49 -0
  16. package/dist//344/273/277/347/234/237/350/260/203/350/257/225/345/212/251/346/211/213/knowledges//344/273/277/347/234/237/350/264/247/350/275/275/347/224/237/345/221/275/345/221/250/346/234/237/346/237/245/350/257/242/346/265/201/347/250/213.md +165 -0
  17. package/dist//344/273/277/347/234/237/350/260/203/350/257/225/345/212/251/346/211/213/knowledges//345/244/251/350/275/246/350/260/203/345/272/246/347/263/273/347/273/237/351/207/215/347/273/204/346/216/245/345/217/243/350/260/203/347/224/250/344/270/216/346/227/245/345/277/227/345/210/206/346/236/220/346/226/271/346/263/225.md +137 -0
  18. package/package.json +20 -0
  19. package/src/cmd/agent.ts +137 -0
  20. package/src/cmd/auth.ts +50 -0
  21. package/src/cmd/knowledge.ts +171 -0
  22. package/src/cmd/server.ts +11 -0
  23. package/src/cmd/skill.ts +220 -0
  24. package/src/lib/client.ts +274 -0
  25. package/src/lib/formatter.ts +196 -0
  26. package/src/lib/storage.ts +63 -0
  27. package/src/lib/validate.ts +210 -0
  28. package/src/main.ts +235 -0
  29. package/test-regression.sh +273 -0
  30. package/tsconfig.json +17 -0
@@ -0,0 +1,274 @@
1
+ import type { User, Skill, SkillListResponse, ApiResponse, LoginResponse, Agent, AgentListResponse } from '@pkg/types/skill';
2
+
3
+ export class ApiClient {
4
+ private token: string | null = null;
5
+ private serverUrl: string;
6
+
7
+ constructor(serverUrl: string, token?: string) {
8
+ this.serverUrl = serverUrl.replace(/\/$/, '');
9
+ this.token = token || null;
10
+ }
11
+
12
+ setToken(token: string) {
13
+ this.token = token;
14
+ }
15
+
16
+ clearToken() {
17
+ this.token = null;
18
+ }
19
+
20
+ private async request<T>(
21
+ path: string,
22
+ options: RequestInit = {}
23
+ ): Promise<ApiResponse<T>> {
24
+ const headers: Record<string, string> = {
25
+ 'Content-Type': 'application/json',
26
+ ...(options.headers as Record<string, string>),
27
+ };
28
+
29
+ if (this.token) {
30
+ headers['Authorization'] = `Bearer ${this.token}`;
31
+ }
32
+
33
+ const res = await fetch(`${this.serverUrl}/api/v1${path}`, {
34
+ ...options,
35
+ headers,
36
+ });
37
+
38
+ return res.json();
39
+ }
40
+
41
+ async login(apiKey: string): Promise<LoginResponse> {
42
+ const res = await this.request<LoginResponse>('/auth/login', {
43
+ method: 'POST',
44
+ body: JSON.stringify({ apiKey }),
45
+ });
46
+ if (!res.ok) {
47
+ throw new Error(res.msg);
48
+ }
49
+ return res.data;
50
+ }
51
+
52
+ async me(): Promise<User> {
53
+ const res = await this.request<User>('/auth/me');
54
+ if (!res.ok) {
55
+ throw new Error(res.msg);
56
+ }
57
+ return res.data;
58
+ }
59
+
60
+ async listSkills(keyword?: string, page = 1, pageSize = 20): Promise<SkillListResponse> {
61
+ let path = `/skills?page=${page}&pageSize=${pageSize}`;
62
+ if (keyword) {
63
+ path += `&keyword=${encodeURIComponent(keyword)}`;
64
+ }
65
+ const res = await this.request<SkillListResponse>(path);
66
+ if (!res.ok) {
67
+ throw new Error(res.msg);
68
+ }
69
+ return res.data;
70
+ }
71
+
72
+ async getSkill(name: string): Promise<Skill> {
73
+ const res = await this.request<Skill>(`/skills/${name}`);
74
+ if (!res.ok) {
75
+ throw new Error(res.msg);
76
+ }
77
+ return res.data;
78
+ }
79
+
80
+ async getMySkills(): Promise<Skill[]> {
81
+ const res = await this.request<Skill[]>('/users/me/skills');
82
+ if (!res.ok) {
83
+ throw new Error(res.msg);
84
+ }
85
+ return res.data;
86
+ }
87
+
88
+ async uploadSkill(filePath: string): Promise<Skill> {
89
+ const { readFileSync } = await import('fs');
90
+ const { basename } = await import('path');
91
+ const fileName = basename(filePath);
92
+ const fileBuffer = readFileSync(filePath);
93
+
94
+ const formData = new FormData();
95
+ const blob = new Blob([fileBuffer]);
96
+ formData.append('file', blob, fileName);
97
+
98
+ const headers: Record<string, string> = {};
99
+ if (this.token) {
100
+ headers['Authorization'] = `Bearer ${this.token}`;
101
+ }
102
+
103
+ const res = await fetch(`${this.serverUrl}/api/v1/skills`, {
104
+ method: 'POST',
105
+ headers,
106
+ body: formData,
107
+ });
108
+
109
+ const data = await res.json();
110
+ console.error('DEBUG server response:', JSON.stringify(data));
111
+ if (!data.ok) {
112
+ throw new Error(data.msg);
113
+ }
114
+ return data.data;
115
+ }
116
+
117
+ async deleteSkill(name: string): Promise<void> {
118
+ const res = await this.request<null>(`/skills/${name}`, {
119
+ method: 'DELETE',
120
+ });
121
+ if (!res.ok) {
122
+ throw new Error(res.msg);
123
+ }
124
+ }
125
+
126
+ async downloadSkill(name: string): Promise<ArrayBuffer> {
127
+ const headers: Record<string, string> = {};
128
+ if (this.token) {
129
+ headers['Authorization'] = `Bearer ${this.token}`;
130
+ }
131
+
132
+ const res = await fetch(`${this.serverUrl}/api/v1/skills/${name}/download`, {
133
+ headers,
134
+ });
135
+
136
+ if (!res.ok) {
137
+ if (res.status === 404) {
138
+ throw new Error('Skill 不存在');
139
+ }
140
+ throw new Error('下载失败');
141
+ }
142
+
143
+ return res.arrayBuffer();
144
+ }
145
+
146
+ async listAgents(keyword?: string, page = 1, pageSize = 20): Promise<AgentListResponse> {
147
+ let path = `/agents?page=${page}&pageSize=${pageSize}`;
148
+ if (keyword) {
149
+ path += `&keyword=${encodeURIComponent(keyword)}`;
150
+ }
151
+ const res = await this.request<AgentListResponse>(path);
152
+ if (!res.ok) {
153
+ throw new Error(res.msg);
154
+ }
155
+ return res.data;
156
+ }
157
+
158
+ async getAgent(id: string): Promise<Agent> {
159
+ const res = await this.request<Agent>(`/agents/${id}`);
160
+ if (!res.ok) {
161
+ throw new Error(res.msg);
162
+ }
163
+ return res.data;
164
+ }
165
+
166
+ async downloadAgent(id: string): Promise<{ buffer: ArrayBuffer; version: string }> {
167
+ const headers: Record<string, string> = {};
168
+ if (this.token) {
169
+ headers['Authorization'] = `Bearer ${this.token}`;
170
+ }
171
+
172
+ const res = await fetch(`${this.serverUrl}/api/v1/agents/${id}/download`, {
173
+ headers,
174
+ });
175
+
176
+ if (!res.ok) {
177
+ if (res.status === 404) {
178
+ throw new Error('Agent 不存在');
179
+ }
180
+ throw new Error('下载失败');
181
+ }
182
+
183
+ const version = res.headers.get('X-Version') || 'unknown';
184
+ return {
185
+ buffer: await res.arrayBuffer(),
186
+ version,
187
+ };
188
+ }
189
+
190
+ async listKnowledge(keyword?: string, page = 1, pageSize = 20): Promise<{ knowledges: any[]; total: number }> {
191
+ let path = `/knowledges?page=${page}&pageSize=${pageSize}`;
192
+ if (keyword) {
193
+ path += `&keyword=${encodeURIComponent(keyword)}`;
194
+ }
195
+ const res = await this.request<{ knowledges: any[]; total: number }>(path);
196
+ if (!res.ok) {
197
+ throw new Error(res.msg);
198
+ }
199
+ return res.data;
200
+ }
201
+
202
+ async getKnowledge(name: string): Promise<any> {
203
+ const res = await this.request<any>(`/knowledges/${name}`);
204
+ if (!res.ok) {
205
+ throw new Error(res.msg);
206
+ }
207
+ return res.data;
208
+ }
209
+
210
+ async getMyKnowledge(): Promise<any[]> {
211
+ const res = await this.request<any[]>('/users/me/knowledges');
212
+ if (!res.ok) {
213
+ throw new Error(res.msg);
214
+ }
215
+ return res.data;
216
+ }
217
+
218
+ async uploadKnowledge(filePath: string): Promise<any> {
219
+ const { readFileSync } = await import('fs');
220
+ const { basename } = await import('path');
221
+ const fileName = basename(filePath);
222
+ const fileBuffer = readFileSync(filePath);
223
+
224
+ const formData = new FormData();
225
+ const blob = new Blob([fileBuffer]);
226
+ formData.append('file', blob, fileName);
227
+
228
+ const headers: Record<string, string> = {};
229
+ if (this.token) {
230
+ headers['Authorization'] = `Bearer ${this.token}`;
231
+ }
232
+
233
+ const res = await fetch(`${this.serverUrl}/api/v1/knowledges`, {
234
+ method: 'POST',
235
+ headers,
236
+ body: formData,
237
+ });
238
+
239
+ const data = await res.json();
240
+ if (!data.ok) {
241
+ throw new Error(data.msg);
242
+ }
243
+ return data.data;
244
+ }
245
+
246
+ async deleteKnowledge(name: string): Promise<void> {
247
+ const res = await this.request<null>(`/knowledges/${name}`, {
248
+ method: 'DELETE',
249
+ });
250
+ if (!res.ok) {
251
+ throw new Error(res.msg);
252
+ }
253
+ }
254
+
255
+ async downloadKnowledge(name: string): Promise<ArrayBuffer> {
256
+ const headers: Record<string, string> = {};
257
+ if (this.token) {
258
+ headers['Authorization'] = `Bearer ${this.token}`;
259
+ }
260
+
261
+ const res = await fetch(`${this.serverUrl}/api/v1/knowledges/${name}/download`, {
262
+ headers,
263
+ });
264
+
265
+ if (!res.ok) {
266
+ if (res.status === 404) {
267
+ throw new Error('Knowledge 不存在');
268
+ }
269
+ throw new Error('下载失败');
270
+ }
271
+
272
+ return res.arrayBuffer();
273
+ }
274
+ }
@@ -0,0 +1,196 @@
1
+ export function formatSkill(skill: { name: string; description: string; downloadCount: number; license?: string }): string {
2
+ const lines = [
3
+ `\x1b[1m${skill.name}\x1b[0m`,
4
+ ` ${skill.description}`,
5
+ ` 下载: ${skill.downloadCount}${skill.license ? ` | 许可: ${skill.license}` : ''}`,
6
+ ];
7
+ return lines.join('\n');
8
+ }
9
+
10
+ export function formatSkillDetail(skill: {
11
+ name: string;
12
+ description: string;
13
+ license?: string;
14
+ compatibility?: string;
15
+ allowedTools?: string[];
16
+ fileSize: number;
17
+ fileHash: string;
18
+ downloadCount: number;
19
+ publishedAt: string;
20
+ metadata?: Record<string, string>;
21
+ }): string {
22
+ const lines = [
23
+ `\x1b[1m${skill.name}\x1b[0m`,
24
+ '',
25
+ `描述: ${skill.description}`,
26
+ `文件大小: ${skill.fileSize} bytes`,
27
+ `下载次数: ${skill.downloadCount}`,
28
+ `发布时间: ${skill.publishedAt}`,
29
+ ];
30
+
31
+ if (skill.license) {
32
+ lines.push(`许可: ${skill.license}`);
33
+ }
34
+ if (skill.compatibility) {
35
+ lines.push(`兼容性: ${skill.compatibility}`);
36
+ }
37
+ if (skill.allowedTools && skill.allowedTools.length > 0) {
38
+ lines.push(`允许工具: ${skill.allowedTools.join(', ')}`);
39
+ }
40
+ if (skill.metadata) {
41
+ lines.push(`元数据: ${JSON.stringify(skill.metadata)}`);
42
+ }
43
+
44
+ return lines.join('\n');
45
+ }
46
+
47
+ export function success(msg: string): void {
48
+ console.log(`\x1b[32m✓ ${msg}\x1b[0m`);
49
+ }
50
+
51
+ export function error(msg: string): void {
52
+ console.error(`\x1b[31m✗ ${msg}\x1b[0m`);
53
+ }
54
+
55
+ export function info(msg: string): void {
56
+ console.log(`\x1b[34mℹ ${msg}\x1b[0m`);
57
+ }
58
+
59
+ export function formatAgent(agent: {
60
+ name: string;
61
+ version: string;
62
+ description: string;
63
+ status: string;
64
+ skillsCount?: number;
65
+ knowledgesCount?: number;
66
+ }): string {
67
+ const lines = [
68
+ `\x1b[1m${agent.name}\x1b[0m`,
69
+ ` ${agent.description}`,
70
+ ` 版本: ${agent.version} | 状态: ${agent.status}${agent.skillsCount !== undefined ? ` | Skills: ${agent.skillsCount}` : ''}${agent.knowledgesCount !== undefined ? ` | Knowledges: ${agent.knowledgesCount}` : ''}`,
71
+ ];
72
+ return lines.join('\n');
73
+ }
74
+
75
+ export function formatAgentDetail(agent: {
76
+ name: string;
77
+ version: string;
78
+ description: string;
79
+ status: string;
80
+ prompt: string;
81
+ createdAt: string;
82
+ updatedAt: string;
83
+ skills?: Array<{
84
+ skill: { name: string };
85
+ config?: Record<string, unknown>;
86
+ }>;
87
+ knowledges?: Array<{
88
+ knowledgeId: string;
89
+ knowledgeName?: string;
90
+ retrievalConfig?: { topK?: number; similarityThreshold?: number };
91
+ }>;
92
+ }): string {
93
+ const lines = [
94
+ `\x1b[1m${agent.name}\x1b[0m`,
95
+ '',
96
+ `版本: ${agent.version}`,
97
+ `描述: ${agent.description || '暂无描述'}`,
98
+ `状态: ${agent.status}`,
99
+ `创建时间: ${formatTime(agent.createdAt)}`,
100
+ `更新时间: ${formatTime(agent.updatedAt)}`,
101
+ ];
102
+
103
+ if (agent.skills && agent.skills.length > 0) {
104
+ lines.push('');
105
+ lines.push('Skills:');
106
+ for (const s of agent.skills) {
107
+ const hasConfig = s.config && Object.keys(s.config).length > 0;
108
+ lines.push(` - ${s.skill.name}${hasConfig ? ` (config: ${JSON.stringify(s.config)})` : ''}`);
109
+ }
110
+ }
111
+
112
+ if (agent.knowledges && agent.knowledges.length > 0) {
113
+ lines.push('');
114
+ lines.push('Knowledges:');
115
+ for (const k of agent.knowledges) {
116
+ const name = k.knowledgeName || k.knowledgeId;
117
+ const topK = k.retrievalConfig?.topK;
118
+ lines.push(` - ${name}${topK ? ` (topK: ${topK})` : ''}`);
119
+ }
120
+ }
121
+
122
+ if (agent.prompt) {
123
+ lines.push('');
124
+ lines.push('System Prompt:');
125
+ lines.push('---');
126
+ lines.push(agent.prompt);
127
+ lines.push('---');
128
+ }
129
+
130
+ return lines.join('\n');
131
+ }
132
+
133
+ function formatTime(isoString: string): string {
134
+ const date = new Date(isoString);
135
+ const year = date.getFullYear();
136
+ const month = String(date.getMonth() + 1).padStart(2, '0');
137
+ const day = String(date.getDate()).padStart(2, '0');
138
+ const hours = String(date.getHours()).padStart(2, '0');
139
+ const minutes = String(date.getMinutes()).padStart(2, '0');
140
+ const seconds = String(date.getSeconds()).padStart(2, '0');
141
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
142
+ }
143
+
144
+ export function formatKnowledge(knowledge: {
145
+ name: string;
146
+ description: string;
147
+ type: string;
148
+ size: number;
149
+ downloadCount: number;
150
+ }): string {
151
+ const sizeStr = knowledge.size > 1024 * 1024
152
+ ? `${(knowledge.size / (1024 * 1024)).toFixed(2)} MB`
153
+ : knowledge.size > 1024
154
+ ? `${(knowledge.size / 1024).toFixed(2)} KB`
155
+ : `${knowledge.size} B`;
156
+
157
+ const lines = [
158
+ `\x1b[1m${knowledge.name}\x1b[0m`,
159
+ ` ${knowledge.description}`,
160
+ ` 类型: ${knowledge.type} | 大小: ${sizeStr} | 下载: ${knowledge.downloadCount}`,
161
+ ];
162
+ return lines.join('\n');
163
+ }
164
+
165
+ export function formatKnowledgeDetail(knowledge: {
166
+ name: string;
167
+ description: string;
168
+ type: string;
169
+ fileSize: number;
170
+ fileHash: string;
171
+ downloadCount: number;
172
+ publishedAt: string;
173
+ tags?: string[];
174
+ }): string {
175
+ const sizeStr = knowledge.fileSize > 1024 * 1024
176
+ ? `${(knowledge.fileSize / (1024 * 1024)).toFixed(2)} MB`
177
+ : knowledge.fileSize > 1024
178
+ ? `${(knowledge.fileSize / 1024).toFixed(2)} KB`
179
+ : `${knowledge.fileSize} B`;
180
+
181
+ const lines = [
182
+ `\x1b[1m${knowledge.name}\x1b[0m`,
183
+ '',
184
+ `描述: ${knowledge.description}`,
185
+ `类型: ${knowledge.type}`,
186
+ `文件大小: ${sizeStr}`,
187
+ `下载次数: ${knowledge.downloadCount}`,
188
+ `发布时间: ${knowledge.publishedAt}`,
189
+ ];
190
+
191
+ if (knowledge.tags && knowledge.tags.length > 0) {
192
+ lines.push(`标签: ${knowledge.tags.join(', ')}`);
193
+ }
194
+
195
+ return lines.join('\n');
196
+ }
@@ -0,0 +1,63 @@
1
+ import { writeFileSync, readFileSync, existsSync, mkdirSync } from 'fs';
2
+ import { join } from 'path';
3
+
4
+ const CONFIG_DIR = join(process.env.HOME || '/root', '.adk');
5
+ const CONFIG_FILE = join(CONFIG_DIR, 'config.json');
6
+
7
+ interface Config {
8
+ serverUrl: string;
9
+ token?: string;
10
+ user?: {
11
+ id: string;
12
+ name: string;
13
+ email: string;
14
+ };
15
+ }
16
+
17
+ function ensureConfigDir() {
18
+ if (!existsSync(CONFIG_DIR)) {
19
+ mkdirSync(CONFIG_DIR, { recursive: true });
20
+ }
21
+ }
22
+
23
+ export function saveConfig(config: Config): void {
24
+ ensureConfigDir();
25
+ writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
26
+ }
27
+
28
+ export function loadConfig(): Config | null {
29
+ try {
30
+ ensureConfigDir();
31
+ if (!existsSync(CONFIG_FILE)) {
32
+ return null;
33
+ }
34
+ const content = readFileSync(CONFIG_FILE, 'utf-8');
35
+ return JSON.parse(content);
36
+ } catch {
37
+ return null;
38
+ }
39
+ }
40
+
41
+ export function clearConfig(): void {
42
+ try {
43
+ if (existsSync(CONFIG_FILE)) {
44
+ const content = readFileSync(CONFIG_FILE, 'utf-8');
45
+ const config: Config = JSON.parse(content);
46
+ config.token = undefined;
47
+ config.user = undefined;
48
+ saveConfig(config);
49
+ }
50
+ } catch {
51
+ }
52
+ }
53
+
54
+ export function getServerUrl(): string {
55
+ const config = loadConfig();
56
+ return config?.serverUrl || 'http://localhost:3000';
57
+ }
58
+
59
+ export function setServerUrl(url: string): void {
60
+ const config = loadConfig() || {};
61
+ config.serverUrl = url;
62
+ saveConfig(config);
63
+ }