@nodeskai/genehub 2026.3.2-9 → 2026.3.3-2
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/LICENSE +21 -0
- package/README.md +96 -0
- package/dist/index.js +821 -85
- package/dist/index.js.map +1 -1
- package/package.json +16 -16
package/dist/index.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command12 } from "commander";
|
|
5
5
|
|
|
6
|
-
// src/commands/
|
|
6
|
+
// src/commands/auth.ts
|
|
7
|
+
import { createServer } from "http";
|
|
7
8
|
import { Command } from "commander";
|
|
8
9
|
|
|
9
10
|
// src/config.ts
|
|
@@ -16,12 +17,31 @@ var DEFAULT_CONFIG = {
|
|
|
16
17
|
registryUrl: "https://genehub.nodeskai.com"
|
|
17
18
|
};
|
|
18
19
|
async function loadConfig() {
|
|
20
|
+
let fileConfig = {};
|
|
19
21
|
try {
|
|
20
22
|
const raw = await readFile(CONFIG_PATH, "utf-8");
|
|
21
|
-
|
|
23
|
+
fileConfig = JSON.parse(raw);
|
|
22
24
|
} catch {
|
|
23
|
-
return DEFAULT_CONFIG;
|
|
24
25
|
}
|
|
26
|
+
const merged = { ...DEFAULT_CONFIG, ...fileConfig };
|
|
27
|
+
const envUrl = process.env.GENEHUB_REGISTRY_URL ?? process.env.GENEHUB_REGISTRY;
|
|
28
|
+
if (envUrl) {
|
|
29
|
+
merged.registryUrl = envUrl;
|
|
30
|
+
}
|
|
31
|
+
const envToken = process.env.GENEHUB_TOKEN;
|
|
32
|
+
if (envToken) {
|
|
33
|
+
merged.token = envToken;
|
|
34
|
+
}
|
|
35
|
+
return merged;
|
|
36
|
+
}
|
|
37
|
+
function getConfigSource(key) {
|
|
38
|
+
if (key === "registry") {
|
|
39
|
+
if (process.env.GENEHUB_REGISTRY_URL || process.env.GENEHUB_REGISTRY) return "env";
|
|
40
|
+
}
|
|
41
|
+
if (key === "token") {
|
|
42
|
+
if (process.env.GENEHUB_TOKEN) return "env";
|
|
43
|
+
}
|
|
44
|
+
return "file";
|
|
25
45
|
}
|
|
26
46
|
async function saveConfig(config) {
|
|
27
47
|
const current = await loadConfig();
|
|
@@ -56,8 +76,102 @@ function table(headers, rows) {
|
|
|
56
76
|
console.log(t.toString());
|
|
57
77
|
}
|
|
58
78
|
|
|
79
|
+
// src/commands/auth.ts
|
|
80
|
+
var authCommand = new Command("auth").description("\u8BA4\u8BC1\u7BA1\u7406");
|
|
81
|
+
authCommand.command("login").description("\u901A\u8FC7 GitHub OAuth \u767B\u5F55\u5E76\u81EA\u52A8\u521B\u5EFA API Key").action(async () => {
|
|
82
|
+
const config = await loadConfig();
|
|
83
|
+
const registryUrl = config.registryUrl.replace(/\/$/, "");
|
|
84
|
+
const port = await findAvailablePort(9876);
|
|
85
|
+
const callbackUrl = `http://localhost:${port}/callback`;
|
|
86
|
+
const tokenPromise = waitForCallback(port);
|
|
87
|
+
const authUrl = `${registryUrl}/auth/github?cli_callback=${encodeURIComponent(callbackUrl)}`;
|
|
88
|
+
info(`Opening browser for GitHub login...`);
|
|
89
|
+
info(` ${authUrl}`);
|
|
90
|
+
const open = await import("open").catch(() => null);
|
|
91
|
+
if (open) {
|
|
92
|
+
await open.default(authUrl);
|
|
93
|
+
} else {
|
|
94
|
+
warn("Cannot open browser automatically. Please open the URL above manually.");
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
const { token, login } = await tokenPromise;
|
|
98
|
+
await saveConfig({ token });
|
|
99
|
+
ok(`Logged in as @${login}`);
|
|
100
|
+
ok(`Token saved to config (${token.slice(0, 12)}****)`);
|
|
101
|
+
} catch (err) {
|
|
102
|
+
fail(err instanceof Error ? err.message : "Login failed");
|
|
103
|
+
process.exit(1);
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
authCommand.command("status").description("\u67E5\u770B\u5F53\u524D\u767B\u5F55\u72B6\u6001").action(async () => {
|
|
107
|
+
const config = await loadConfig();
|
|
108
|
+
if (!config.token) {
|
|
109
|
+
info("Not logged in");
|
|
110
|
+
info(" Run: genehub auth login");
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
ok(`Token: ${config.token.slice(0, 12)}****`);
|
|
114
|
+
info(`Registry: ${config.registryUrl}`);
|
|
115
|
+
});
|
|
116
|
+
authCommand.command("logout").description("\u9000\u51FA\u767B\u5F55\uFF08\u6E05\u9664\u672C\u5730 token\uFF09").action(async () => {
|
|
117
|
+
await saveConfig({ token: void 0 });
|
|
118
|
+
ok("Logged out. Token removed.");
|
|
119
|
+
});
|
|
120
|
+
function findAvailablePort(preferred) {
|
|
121
|
+
return new Promise((resolve5, reject) => {
|
|
122
|
+
const server = createServer();
|
|
123
|
+
server.listen(preferred, () => {
|
|
124
|
+
server.close(() => resolve5(preferred));
|
|
125
|
+
});
|
|
126
|
+
server.on("error", () => {
|
|
127
|
+
const fallback = createServer();
|
|
128
|
+
fallback.listen(0, () => {
|
|
129
|
+
const addr = fallback.address();
|
|
130
|
+
const port = typeof addr === "object" && addr ? addr.port : 0;
|
|
131
|
+
fallback.close(() => resolve5(port));
|
|
132
|
+
});
|
|
133
|
+
fallback.on("error", reject);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
function waitForCallback(port) {
|
|
138
|
+
return new Promise((resolve5, reject) => {
|
|
139
|
+
const timeout = setTimeout(() => {
|
|
140
|
+
server.close();
|
|
141
|
+
reject(new Error("Login timeout (60s)"));
|
|
142
|
+
}, 6e4);
|
|
143
|
+
const server = createServer((req, res) => {
|
|
144
|
+
if (!req.url?.startsWith("/callback")) {
|
|
145
|
+
res.writeHead(404);
|
|
146
|
+
res.end();
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const url = new URL(req.url, `http://localhost:${port}`);
|
|
150
|
+
const token = url.searchParams.get("token");
|
|
151
|
+
const login = url.searchParams.get("login");
|
|
152
|
+
if (!token) {
|
|
153
|
+
res.writeHead(400, { "Content-Type": "text/html; charset=utf-8" });
|
|
154
|
+
res.end("<h2>Login failed</h2><p>No token received.</p>");
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
|
|
158
|
+
res.end(`<h2>Login successful</h2><p>@${login ?? "unknown"} - you can close this tab.</p>`);
|
|
159
|
+
clearTimeout(timeout);
|
|
160
|
+
server.close();
|
|
161
|
+
resolve5({ token, login: login ?? "" });
|
|
162
|
+
});
|
|
163
|
+
server.listen(port);
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
59
167
|
// src/commands/config.ts
|
|
60
|
-
|
|
168
|
+
import { Command as Command2 } from "commander";
|
|
169
|
+
var configCommand = new Command2("config").description("\u7BA1\u7406 GeneHub CLI \u914D\u7F6E");
|
|
170
|
+
function sourceLabel(source) {
|
|
171
|
+
if (source === "env") return " [env]";
|
|
172
|
+
if (source === "default") return " [default]";
|
|
173
|
+
return "";
|
|
174
|
+
}
|
|
61
175
|
configCommand.command("set <key> <value>").description("\u8BBE\u7F6E\u914D\u7F6E\u9879\uFF08registry / token\uFF09").action(async (key, value) => {
|
|
62
176
|
const validKeys = ["registry", "token"];
|
|
63
177
|
if (!validKeys.includes(key)) {
|
|
@@ -71,24 +185,257 @@ configCommand.command("set <key> <value>").description("\u8BBE\u7F6E\u914D\u7F6E
|
|
|
71
185
|
}
|
|
72
186
|
ok(`${key} = ${key === "token" ? `${value.slice(0, 8)}***` : value}`);
|
|
73
187
|
});
|
|
74
|
-
configCommand.command("get [key]").description("\u67E5\u770B\u914D\u7F6E").action(async (key) => {
|
|
188
|
+
configCommand.command("get [key]").description("\u67E5\u770B\u914D\u7F6E\uFF08\u652F\u6301 GENEHUB_REGISTRY_URL / GENEHUB_TOKEN \u73AF\u5883\u53D8\u91CF\u8986\u76D6\uFF09").action(async (key) => {
|
|
75
189
|
const config = await loadConfig();
|
|
76
190
|
if (key) {
|
|
77
191
|
const map = {
|
|
78
192
|
registry: config.registryUrl,
|
|
79
193
|
token: config.token ? `${config.token.slice(0, 8)}***` : void 0
|
|
80
194
|
};
|
|
81
|
-
|
|
195
|
+
const src = sourceLabel(getConfigSource(key));
|
|
196
|
+
info(`${key} = ${map[key] ?? "(\u672A\u8BBE\u7F6E)"}${src}`);
|
|
82
197
|
} else {
|
|
83
|
-
|
|
84
|
-
|
|
198
|
+
const regSrc = sourceLabel(getConfigSource("registry"));
|
|
199
|
+
const tokSrc = sourceLabel(getConfigSource("token"));
|
|
200
|
+
info(`registry = ${config.registryUrl}${regSrc}`);
|
|
201
|
+
info(
|
|
202
|
+
`token = ${config.token ? `${config.token.slice(0, 8)}***` : "(\u672A\u8BBE\u7F6E)"}${tokSrc}`
|
|
203
|
+
);
|
|
85
204
|
}
|
|
86
205
|
});
|
|
87
206
|
|
|
207
|
+
// src/commands/genome.ts
|
|
208
|
+
import { readdir, readFile as readFile2, rm, writeFile as writeFile2 } from "fs/promises";
|
|
209
|
+
import { tmpdir } from "os";
|
|
210
|
+
import { join as join2, relative, resolve } from "path";
|
|
211
|
+
import { detectAdapter, GeneHubClient, getAdapter } from "@nodeskai/genehub-sdk";
|
|
212
|
+
import { Command as Command3 } from "commander";
|
|
213
|
+
import ora from "ora";
|
|
214
|
+
import { parse } from "yaml";
|
|
215
|
+
var IGNORED_PATTERNS = [".git", "node_modules", ".DS_Store", "__pycache__", ".venv"];
|
|
216
|
+
async function scanDirectory(dirPath) {
|
|
217
|
+
const files = {};
|
|
218
|
+
async function walk(currentPath) {
|
|
219
|
+
const entries = await readdir(currentPath, { withFileTypes: true });
|
|
220
|
+
for (const entry of entries) {
|
|
221
|
+
if (IGNORED_PATTERNS.includes(entry.name)) continue;
|
|
222
|
+
const fullPath = join2(currentPath, entry.name);
|
|
223
|
+
if (entry.isDirectory()) {
|
|
224
|
+
await walk(fullPath);
|
|
225
|
+
} else if (entry.isFile()) {
|
|
226
|
+
const relPath = relative(dirPath, fullPath);
|
|
227
|
+
const content = await readFile2(fullPath, "utf-8");
|
|
228
|
+
files[relPath] = content;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
await walk(dirPath);
|
|
233
|
+
return files;
|
|
234
|
+
}
|
|
235
|
+
async function extractTarGz(buffer, destDir) {
|
|
236
|
+
const { mkdir: mkdir4 } = await import("fs/promises");
|
|
237
|
+
await mkdir4(destDir, { recursive: true });
|
|
238
|
+
const { extract } = await import("tar");
|
|
239
|
+
const tarPath = join2(tmpdir(), `genehub-genome-${Date.now()}.tar.gz`);
|
|
240
|
+
await writeFile2(tarPath, Buffer.from(buffer));
|
|
241
|
+
try {
|
|
242
|
+
await extract({ file: tarPath, cwd: destDir, strip: 1 });
|
|
243
|
+
} finally {
|
|
244
|
+
await rm(tarPath, { force: true });
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
var publishGenomeCommand = new Command3("publish").description("\u53D1\u5E03\u57FA\u56E0\u7EC4\u5230 GeneHub Registry").argument("<path>", "\u57FA\u56E0\u7EC4\u76EE\u5F55\u8DEF\u5F84\uFF08\u5305\u542B genome.yaml\uFF09").action(async (dirPath) => {
|
|
248
|
+
const config = await loadConfig();
|
|
249
|
+
if (!config.token) {
|
|
250
|
+
fail("\u672A\u914D\u7F6E\u8BA4\u8BC1 token");
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
const client = new GeneHubClient({ registryUrl: config.registryUrl, token: config.token });
|
|
254
|
+
const absPath = resolve(dirPath);
|
|
255
|
+
try {
|
|
256
|
+
const yamlPath = join2(absPath, "genome.yaml");
|
|
257
|
+
const raw = await readFile2(yamlPath, "utf-8");
|
|
258
|
+
const parsed = parse(raw);
|
|
259
|
+
if (!parsed.name || !parsed.slug || !parsed.version || !parsed.genes?.length) {
|
|
260
|
+
fail("genome.yaml \u7F3A\u5C11\u5FC5\u586B\u5B57\u6BB5: name, slug, version, genes");
|
|
261
|
+
process.exit(1);
|
|
262
|
+
}
|
|
263
|
+
const scanSpinner = ora("\u626B\u63CF\u57FA\u56E0\u7EC4\u76EE\u5F55...").start();
|
|
264
|
+
const files = await scanDirectory(absPath);
|
|
265
|
+
const fileCount = Object.keys(files).length;
|
|
266
|
+
scanSpinner.succeed(`\u626B\u63CF\u5B8C\u6210: ${fileCount} \u4E2A\u6587\u4EF6`);
|
|
267
|
+
const spinner = ora(`\u53D1\u5E03\u57FA\u56E0\u7EC4 ${parsed.slug}@${parsed.version}...`).start();
|
|
268
|
+
try {
|
|
269
|
+
const genome = await client.publishGenome(parsed, files);
|
|
270
|
+
spinner.succeed("\u53D1\u5E03\u6210\u529F");
|
|
271
|
+
ok(
|
|
272
|
+
`${genome.slug}@${genome.version} \u5DF2\u53D1\u5E03\u5230 GeneHub Registry (${fileCount} \u4E2A\u6587\u4EF6)`
|
|
273
|
+
);
|
|
274
|
+
} catch (err) {
|
|
275
|
+
const isSlugExists = err instanceof Error && err.message.includes("genome_slug_exists");
|
|
276
|
+
if (!isSlugExists) throw err;
|
|
277
|
+
spinner.text = `\u57FA\u56E0\u7EC4 ${parsed.slug} \u5DF2\u5B58\u5728\uFF0C\u53D1\u5E03\u65B0\u7248\u672C ${parsed.version}...`;
|
|
278
|
+
const genome = await client.publishGenomeVersion(
|
|
279
|
+
parsed.slug,
|
|
280
|
+
{ version: parsed.version, genes: parsed.genes, files },
|
|
281
|
+
files
|
|
282
|
+
);
|
|
283
|
+
spinner.succeed("\u53D1\u5E03\u6210\u529F");
|
|
284
|
+
ok(`${genome.slug}@${genome.version} \u65B0\u7248\u672C\u5DF2\u53D1\u5E03 (${fileCount} \u4E2A\u6587\u4EF6)`);
|
|
285
|
+
}
|
|
286
|
+
} catch (err) {
|
|
287
|
+
fail(err instanceof Error ? err.message : String(err));
|
|
288
|
+
process.exit(1);
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
var installGenomeCommand = new Command3("install").description("\u5B89\u88C5\u57FA\u56E0\u7EC4\uFF08\u9012\u5F52\u5B89\u88C5\u6240\u6709\u5F15\u7528\u7684\u57FA\u56E0\uFF09").argument("<slug>", "\u57FA\u56E0\u7EC4\u6807\u8BC6\u7B26\uFF08\u652F\u6301 slug@version \u683C\u5F0F\uFF09").option("-p, --product <product>", "\u6307\u5B9A\u76EE\u6807\u4EA7\u54C1").option("-f, --force", "\u5F3A\u5236\u8986\u76D6\u5DF2\u5B89\u88C5\u57FA\u56E0", false).option("--target <path>", "\u6307\u5B9A\u5B89\u88C5\u76EE\u6807\u8DEF\u5F84").action(async (rawSlug, opts) => {
|
|
292
|
+
const config = await loadConfig();
|
|
293
|
+
const client = new GeneHubClient({ registryUrl: config.registryUrl, token: config.token });
|
|
294
|
+
const atIdx = rawSlug.lastIndexOf("@");
|
|
295
|
+
const slug = atIdx > 0 ? rawSlug.slice(0, atIdx) : rawSlug;
|
|
296
|
+
const version = atIdx > 0 ? rawSlug.slice(atIdx + 1) : void 0;
|
|
297
|
+
const spinner = ora(`\u83B7\u53D6\u57FA\u56E0\u7EC4 ${slug}...`).start();
|
|
298
|
+
try {
|
|
299
|
+
const genome = await client.getGenome(slug);
|
|
300
|
+
spinner.succeed(`\u57FA\u56E0\u7EC4: ${genome.name} v${genome.version} (${genome.genes.length} \u4E2A\u57FA\u56E0)`);
|
|
301
|
+
const adapter = opts.product ? getAdapter(opts.product) : await detectAdapter();
|
|
302
|
+
info(`\u76EE\u6807\u4EA7\u54C1: ${adapter.product}`);
|
|
303
|
+
const resolveSpinner = ora("\u89E3\u6790\u57FA\u56E0\u4F9D\u8D56...").start();
|
|
304
|
+
const resolved = await client.resolveGenome(slug, version);
|
|
305
|
+
resolveSpinner.succeed(`\u89E3\u6790\u5B8C\u6210: ${resolved.genes.length} \u4E2A\u57FA\u56E0\uFF08\u542B\u4F9D\u8D56\uFF09`);
|
|
306
|
+
if (resolved.warnings.length > 0) {
|
|
307
|
+
for (const w of resolved.warnings) warn(w);
|
|
308
|
+
}
|
|
309
|
+
if (resolved.conflicts.length > 0) {
|
|
310
|
+
for (const c of resolved.conflicts) warn(`\u51B2\u7A81: ${c}`);
|
|
311
|
+
}
|
|
312
|
+
let installed = 0;
|
|
313
|
+
let skipped = 0;
|
|
314
|
+
for (const gene of resolved.genes) {
|
|
315
|
+
if (!opts.force && await adapter.isInstalled(gene.slug)) {
|
|
316
|
+
skipped++;
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
const geneSpinner = ora(` \u5B89\u88C5 ${gene.slug}@${gene.version}...`).start();
|
|
320
|
+
try {
|
|
321
|
+
const manifest = await client.getManifest(gene.slug, gene.version);
|
|
322
|
+
let result;
|
|
323
|
+
try {
|
|
324
|
+
const archive = await client.downloadArchive(gene.slug, gene.version);
|
|
325
|
+
const tempDir = join2(tmpdir(), `genehub-install-${gene.slug}-${Date.now()}`);
|
|
326
|
+
await extractTarGz(archive, tempDir);
|
|
327
|
+
if (adapter.installFromDirectory) {
|
|
328
|
+
result = await adapter.installFromDirectory(tempDir, manifest, {
|
|
329
|
+
force: opts.force,
|
|
330
|
+
targetPath: opts.target
|
|
331
|
+
});
|
|
332
|
+
} else {
|
|
333
|
+
result = await adapter.install(manifest, {
|
|
334
|
+
force: opts.force,
|
|
335
|
+
targetPath: opts.target
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
await rm(tempDir, { recursive: true, force: true });
|
|
339
|
+
} catch {
|
|
340
|
+
result = await adapter.install(manifest, {
|
|
341
|
+
force: opts.force,
|
|
342
|
+
targetPath: opts.target
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
geneSpinner.succeed(` ${result.slug}@${result.version}`);
|
|
346
|
+
installed++;
|
|
347
|
+
try {
|
|
348
|
+
await client.reportInstall(gene.slug);
|
|
349
|
+
} catch {
|
|
350
|
+
}
|
|
351
|
+
} catch (err) {
|
|
352
|
+
geneSpinner.fail(
|
|
353
|
+
` ${gene.slug} \u5B89\u88C5\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
ok(`\u57FA\u56E0\u7EC4\u5B89\u88C5\u5B8C\u6210: ${installed} \u5B89\u88C5, ${skipped} \u8DF3\u8FC7`);
|
|
358
|
+
try {
|
|
359
|
+
await client.reportGenomeInstall(slug);
|
|
360
|
+
} catch {
|
|
361
|
+
}
|
|
362
|
+
} catch (err) {
|
|
363
|
+
spinner.fail("\u5B89\u88C5\u5931\u8D25");
|
|
364
|
+
fail(err instanceof Error ? err.message : String(err));
|
|
365
|
+
process.exit(1);
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
var infoGenomeCommand = new Command3("info").description("\u67E5\u770B\u57FA\u56E0\u7EC4\u8BE6\u60C5").argument("<slug>", "\u57FA\u56E0\u7EC4\u6807\u8BC6\u7B26").option("--json", "JSON \u683C\u5F0F\u8F93\u51FA", false).action(async (slug, opts) => {
|
|
369
|
+
const config = await loadConfig();
|
|
370
|
+
const client = new GeneHubClient({ registryUrl: config.registryUrl, token: config.token });
|
|
371
|
+
try {
|
|
372
|
+
const genome = await client.getGenome(slug);
|
|
373
|
+
if (opts.json) {
|
|
374
|
+
console.log(JSON.stringify(genome, null, 2));
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
info(`\u540D\u79F0: ${genome.name}`);
|
|
378
|
+
info(`Slug: ${genome.slug}`);
|
|
379
|
+
info(`\u7248\u672C: ${genome.version}`);
|
|
380
|
+
info(`\u5206\u7C7B: ${genome.category}`);
|
|
381
|
+
info(`\u63CF\u8FF0: ${genome.description || genome.short_description || "(\u65E0)"}`);
|
|
382
|
+
info(`\u5B89\u88C5\u6570: ${genome.install_count}`);
|
|
383
|
+
if (genome.genes.length > 0) {
|
|
384
|
+
info(`
|
|
385
|
+
\u57FA\u56E0\u5217\u8868 (${genome.genes.length}\u4E2A):`);
|
|
386
|
+
table(
|
|
387
|
+
["slug", "\u7248\u672C"],
|
|
388
|
+
genome.genes.map((g) => [g.slug, g.version])
|
|
389
|
+
);
|
|
390
|
+
}
|
|
391
|
+
} catch (err) {
|
|
392
|
+
fail(err instanceof Error ? err.message : String(err));
|
|
393
|
+
process.exit(1);
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
var listGenomeCommand = new Command3("list").description("\u641C\u7D22\u57FA\u56E0\u7EC4").option("-q, --query <keyword>", "\u641C\u7D22\u5173\u952E\u8BCD").option("-c, --category <category>", "\u6309\u5206\u7C7B\u8FC7\u6EE4").option("--json", "JSON \u683C\u5F0F\u8F93\u51FA", false).action(async (opts) => {
|
|
397
|
+
const config = await loadConfig();
|
|
398
|
+
const client = new GeneHubClient({ registryUrl: config.registryUrl, token: config.token });
|
|
399
|
+
try {
|
|
400
|
+
const result = await client.searchGenomes({
|
|
401
|
+
q: opts.query,
|
|
402
|
+
category: opts.category
|
|
403
|
+
});
|
|
404
|
+
if (opts.json) {
|
|
405
|
+
console.log(JSON.stringify(result, null, 2));
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
if (result.items.length === 0) {
|
|
409
|
+
info("\u672A\u627E\u5230\u57FA\u56E0\u7EC4");
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
table(
|
|
413
|
+
["slug", "\u540D\u79F0", "\u7248\u672C", "\u5206\u7C7B", "\u57FA\u56E0\u6570", "\u5B89\u88C5\u6570"],
|
|
414
|
+
result.items.map((g) => [
|
|
415
|
+
g.slug,
|
|
416
|
+
g.name,
|
|
417
|
+
g.version,
|
|
418
|
+
g.category,
|
|
419
|
+
String(g.genes.length),
|
|
420
|
+
String(g.install_count)
|
|
421
|
+
])
|
|
422
|
+
);
|
|
423
|
+
info(`\u5171 ${result.total} \u6761\u7ED3\u679C`);
|
|
424
|
+
} catch (err) {
|
|
425
|
+
fail(err instanceof Error ? err.message : String(err));
|
|
426
|
+
process.exit(1);
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
var genomeCommand = new Command3("genome").description("\u57FA\u56E0\u7EC4\u7BA1\u7406");
|
|
430
|
+
genomeCommand.addCommand(publishGenomeCommand);
|
|
431
|
+
genomeCommand.addCommand(installGenomeCommand);
|
|
432
|
+
genomeCommand.addCommand(infoGenomeCommand);
|
|
433
|
+
genomeCommand.addCommand(listGenomeCommand);
|
|
434
|
+
|
|
88
435
|
// src/commands/init.ts
|
|
89
|
-
import { mkdir as mkdir2, writeFile as
|
|
90
|
-
import { join as
|
|
91
|
-
import { Command as
|
|
436
|
+
import { mkdir as mkdir2, writeFile as writeFile3 } from "fs/promises";
|
|
437
|
+
import { join as join3, resolve as resolve2 } from "path";
|
|
438
|
+
import { Command as Command4 } from "commander";
|
|
92
439
|
import { stringify } from "yaml";
|
|
93
440
|
var TEMPLATE = {
|
|
94
441
|
slug: "my-gene",
|
|
@@ -131,14 +478,14 @@ metadata:
|
|
|
131
478
|
|
|
132
479
|
\u5728\u6B64\u7F16\u5199\u57FA\u56E0\u7684\u6280\u80FD\u63CF\u8FF0...
|
|
133
480
|
`;
|
|
134
|
-
var initCommand = new
|
|
135
|
-
const absPath =
|
|
481
|
+
var initCommand = new Command4("init").description("\u521D\u59CB\u5316\u57FA\u56E0\u6A21\u677F").argument("[path]", "\u76EE\u6807\u76EE\u5F55", ".").action(async (dirPath) => {
|
|
482
|
+
const absPath = resolve2(dirPath);
|
|
136
483
|
try {
|
|
137
484
|
await mkdir2(absPath, { recursive: true });
|
|
138
|
-
const yamlPath =
|
|
139
|
-
await
|
|
140
|
-
const skillPath =
|
|
141
|
-
await
|
|
485
|
+
const yamlPath = join3(absPath, "gene.yaml");
|
|
486
|
+
await writeFile3(yamlPath, stringify(TEMPLATE), "utf-8");
|
|
487
|
+
const skillPath = join3(absPath, "SKILL.md");
|
|
488
|
+
await writeFile3(skillPath, SKILL_TEMPLATE, "utf-8");
|
|
142
489
|
ok(`\u57FA\u56E0\u6A21\u677F\u5DF2\u521B\u5EFA:`);
|
|
143
490
|
info(` ${yamlPath}`);
|
|
144
491
|
info(` ${skillPath}`);
|
|
@@ -150,11 +497,12 @@ var initCommand = new Command2("init").description("\u521D\u59CB\u5316\u57FA\u56
|
|
|
150
497
|
});
|
|
151
498
|
|
|
152
499
|
// src/commands/install.ts
|
|
153
|
-
import {
|
|
154
|
-
import {
|
|
155
|
-
import {
|
|
156
|
-
import {
|
|
157
|
-
import
|
|
500
|
+
import { mkdir as mkdir3, rm as rm2, writeFile as writeFile4 } from "fs/promises";
|
|
501
|
+
import { homedir as homedir2, tmpdir as tmpdir2 } from "os";
|
|
502
|
+
import { join as join4 } from "path";
|
|
503
|
+
import { detectAdapter as detectAdapter2, GeneHubClient as GeneHubClient2, getAdapter as getAdapter2, LearningEngine } from "@nodeskai/genehub-sdk";
|
|
504
|
+
import { Command as Command5 } from "commander";
|
|
505
|
+
import ora2 from "ora";
|
|
158
506
|
function parseSlugVersion(input) {
|
|
159
507
|
const atIdx = input.lastIndexOf("@");
|
|
160
508
|
if (atIdx > 0) {
|
|
@@ -162,27 +510,59 @@ function parseSlugVersion(input) {
|
|
|
162
510
|
}
|
|
163
511
|
return { slug: input };
|
|
164
512
|
}
|
|
165
|
-
|
|
513
|
+
async function extractTarGz2(buffer, destDir) {
|
|
514
|
+
await mkdir3(destDir, { recursive: true });
|
|
515
|
+
const { extract } = await import("tar");
|
|
516
|
+
const tarPath = join4(tmpdir2(), `genehub-${Date.now()}.tar.gz`);
|
|
517
|
+
await writeFile4(tarPath, Buffer.from(buffer));
|
|
518
|
+
try {
|
|
519
|
+
await extract({ file: tarPath, cwd: destDir, strip: 1 });
|
|
520
|
+
} finally {
|
|
521
|
+
await rm2(tarPath, { force: true });
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
var installCommand = new Command5("install").description("\u5B89\u88C5\u57FA\u56E0\u5230\u5F53\u524D Agent \u73AF\u5883").argument("<slug>", "\u57FA\u56E0\u6807\u8BC6\u7B26\uFF08\u652F\u6301 slug@version \u683C\u5F0F\uFF09").option("-p, --product <product>", "\u6307\u5B9A\u76EE\u6807\u4EA7\u54C1\uFF08openclaw / nanobot / generic\uFF09").option("-f, --force", "\u5F3A\u5236\u8986\u76D6\u5DF2\u5B89\u88C5\u7248\u672C", false).option("--target <path>", "\u6307\u5B9A\u5B89\u88C5\u76EE\u6807\u8DEF\u5F84").option("--learn", "\u5B89\u88C5\u540E\u81EA\u52A8\u89E6\u53D1\u6DF1\u5EA6\u5B66\u4E60", false).action(async (rawSlug, opts) => {
|
|
166
525
|
const config = await loadConfig();
|
|
167
|
-
const client = new
|
|
526
|
+
const client = new GeneHubClient2({ registryUrl: config.registryUrl, token: config.token });
|
|
168
527
|
const { slug, version } = parseSlugVersion(rawSlug);
|
|
169
|
-
const spinner =
|
|
528
|
+
const spinner = ora2(`\u83B7\u53D6\u57FA\u56E0 ${slug}${version ? `@${version}` : ""} \u7684 manifest...`).start();
|
|
170
529
|
try {
|
|
171
530
|
const manifest = await client.getManifest(slug, version);
|
|
172
531
|
spinner.succeed(`\u83B7\u53D6 ${manifest.name} v${manifest.version}`);
|
|
173
|
-
const adapter = opts.product ?
|
|
532
|
+
const adapter = opts.product ? getAdapter2(opts.product) : await detectAdapter2();
|
|
174
533
|
info(`\u76EE\u6807\u4EA7\u54C1: ${adapter.product}`);
|
|
175
534
|
if (!opts.force && await adapter.isInstalled(slug)) {
|
|
176
535
|
const installedVer = await adapter.getInstalledVersion(slug);
|
|
177
536
|
warn(`${slug}${installedVer ? ` v${installedVer}` : ""} \u5DF2\u5B89\u88C5\uFF0C\u4F7F\u7528 --force \u8986\u76D6`);
|
|
178
537
|
return;
|
|
179
538
|
}
|
|
180
|
-
const installSpinner =
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
539
|
+
const installSpinner = ora2("\u5B89\u88C5\u4E2D...").start();
|
|
540
|
+
let result;
|
|
541
|
+
let isMultiFile = false;
|
|
542
|
+
try {
|
|
543
|
+
const archive = await client.downloadArchive(slug, version);
|
|
544
|
+
const tempDir = join4(tmpdir2(), `genehub-install-${slug}-${Date.now()}`);
|
|
545
|
+
await extractTarGz2(archive, tempDir);
|
|
546
|
+
isMultiFile = true;
|
|
547
|
+
if (adapter.installFromDirectory) {
|
|
548
|
+
result = await adapter.installFromDirectory(tempDir, manifest, {
|
|
549
|
+
force: opts.force,
|
|
550
|
+
targetPath: opts.target
|
|
551
|
+
});
|
|
552
|
+
} else {
|
|
553
|
+
result = await adapter.install(manifest, {
|
|
554
|
+
force: opts.force,
|
|
555
|
+
targetPath: opts.target
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
await rm2(tempDir, { recursive: true, force: true });
|
|
559
|
+
} catch {
|
|
560
|
+
result = await adapter.install(manifest, {
|
|
561
|
+
force: opts.force,
|
|
562
|
+
targetPath: opts.target
|
|
563
|
+
});
|
|
564
|
+
}
|
|
565
|
+
installSpinner.succeed(isMultiFile ? "\u5B89\u88C5\u5B8C\u6210\uFF08\u591A\u6587\u4EF6\u57FA\u56E0\uFF09" : "\u5B89\u88C5\u5B8C\u6210");
|
|
186
566
|
ok(`${result.slug}@${result.version} \u5B89\u88C5\u6210\u529F`);
|
|
187
567
|
info(`\u6587\u4EF6: ${result.files.join(", ")}`);
|
|
188
568
|
if (result.needsRestart) {
|
|
@@ -196,11 +576,22 @@ var installCommand = new Command3("install").description("\u5B89\u88C5\u57FA\u56
|
|
|
196
576
|
} catch {
|
|
197
577
|
}
|
|
198
578
|
if (opts.learn) {
|
|
199
|
-
const workspaceDir = adapter.product === "openclaw" ?
|
|
579
|
+
const workspaceDir = adapter.product === "openclaw" ? join4(homedir2(), ".openclaw", "workspace") : adapter.product === "nanobot" ? join4(homedir2(), ".nanobot", "workspace") : join4(process.cwd(), ".genehub");
|
|
200
580
|
const engine = new LearningEngine({ workspaceDir, adapter });
|
|
201
|
-
const learnSpinner =
|
|
581
|
+
const learnSpinner = ora2("\u751F\u6210\u5B66\u4E60\u4EFB\u52A1...").start();
|
|
202
582
|
await engine.createLearningTask(manifest);
|
|
203
|
-
learnSpinner.succeed("\u5B66\u4E60\u4EFB\u52A1\u5DF2\u521B\u5EFA
|
|
583
|
+
learnSpinner.succeed("\u5B66\u4E60\u4EFB\u52A1\u5DF2\u521B\u5EFA");
|
|
584
|
+
if (adapter.triggerLearning) {
|
|
585
|
+
const triggerSpinner = ora2("\u89E6\u53D1 bot \u5B66\u4E60...").start();
|
|
586
|
+
try {
|
|
587
|
+
await adapter.triggerLearning("\u68C0\u67E5 learning-tasks/ \u76EE\u5F55\u5E76\u5904\u7406\u5B66\u4E60\u4EFB\u52A1");
|
|
588
|
+
triggerSpinner.succeed("\u5DF2\u89E6\u53D1 bot \u5B66\u4E60\uFF08\u540E\u53F0\u5904\u7406\u4E2D\uFF09");
|
|
589
|
+
} catch {
|
|
590
|
+
triggerSpinner.warn("\u81EA\u52A8\u89E6\u53D1\u5931\u8D25\uFF0CAgent \u5C06\u5728\u4E0B\u6B21\u5BF9\u8BDD\u4E2D\u5904\u7406");
|
|
591
|
+
}
|
|
592
|
+
} else {
|
|
593
|
+
info("Agent \u5C06\u5728\u4E0B\u6B21\u5BF9\u8BDD\u4E2D\u5904\u7406\u5B66\u4E60\u4EFB\u52A1");
|
|
594
|
+
}
|
|
204
595
|
}
|
|
205
596
|
} catch (err) {
|
|
206
597
|
spinner.fail("\u5B89\u88C5\u5931\u8D25");
|
|
@@ -211,28 +602,28 @@ var installCommand = new Command3("install").description("\u5B89\u88C5\u57FA\u56
|
|
|
211
602
|
|
|
212
603
|
// src/commands/learn.ts
|
|
213
604
|
import { homedir as homedir3 } from "os";
|
|
214
|
-
import { join as
|
|
215
|
-
import { detectAdapter as
|
|
216
|
-
import { Command as
|
|
217
|
-
import
|
|
605
|
+
import { join as join5 } from "path";
|
|
606
|
+
import { detectAdapter as detectAdapter3, GeneHubClient as GeneHubClient3, getAdapter as getAdapter3, LearningEngine as LearningEngine2 } from "@nodeskai/genehub-sdk";
|
|
607
|
+
import { Command as Command6 } from "commander";
|
|
608
|
+
import ora3 from "ora";
|
|
218
609
|
function getWorkspaceDir(product) {
|
|
219
610
|
switch (product) {
|
|
220
611
|
case "openclaw":
|
|
221
|
-
return
|
|
612
|
+
return join5(homedir3(), ".openclaw", "workspace");
|
|
222
613
|
case "nanobot":
|
|
223
|
-
return
|
|
614
|
+
return join5(homedir3(), ".nanobot", "workspace");
|
|
224
615
|
default:
|
|
225
|
-
return
|
|
616
|
+
return join5(process.cwd(), ".genehub");
|
|
226
617
|
}
|
|
227
618
|
}
|
|
228
|
-
var learnCommand = new
|
|
619
|
+
var learnCommand = new Command6("learn").description("\u89E6\u53D1\u57FA\u56E0\u6DF1\u5EA6\u5B66\u4E60\uFF08L2\uFF09").argument("<slug>", "\u57FA\u56E0\u6807\u8BC6\u7B26").option("-p, --product <product>", "\u6307\u5B9A\u76EE\u6807\u4EA7\u54C1").option("--check", "\u68C0\u67E5\u5B66\u4E60\u7ED3\u679C\u5E76\u5E94\u7528").action(async (slug, opts) => {
|
|
229
620
|
const config = await loadConfig();
|
|
230
|
-
const client = new
|
|
231
|
-
const adapter = opts.product ?
|
|
621
|
+
const client = new GeneHubClient3({ registryUrl: config.registryUrl, token: config.token });
|
|
622
|
+
const adapter = opts.product ? getAdapter3(opts.product) : await detectAdapter3();
|
|
232
623
|
const workspaceDir = getWorkspaceDir(adapter.product);
|
|
233
624
|
const engine = new LearningEngine2({ workspaceDir, adapter });
|
|
234
625
|
if (opts.check) {
|
|
235
|
-
const spinner2 =
|
|
626
|
+
const spinner2 = ora3("\u68C0\u67E5\u5B66\u4E60\u7ED3\u679C...").start();
|
|
236
627
|
const result = await engine.checkResult(slug);
|
|
237
628
|
if (!result) {
|
|
238
629
|
spinner2.fail("\u672A\u627E\u5230\u5B66\u4E60\u7ED3\u679C");
|
|
@@ -251,7 +642,7 @@ var learnCommand = new Command4("learn").description("\u89E6\u53D1\u57FA\u56E0\u
|
|
|
251
642
|
info(`\u7406\u7531: ${result.reason}`);
|
|
252
643
|
}
|
|
253
644
|
if (result.decision === "learned" && result.content) {
|
|
254
|
-
const skillsDir =
|
|
645
|
+
const skillsDir = join5(workspaceDir, "skills");
|
|
255
646
|
const applied = await engine.applyResult(slug, skillsDir);
|
|
256
647
|
if (applied) {
|
|
257
648
|
ok("\u5DF2\u5C06\u4E2A\u6027\u5316\u7248\u672C\u5E94\u7528\u5230\u6280\u80FD\u76EE\u5F55");
|
|
@@ -259,17 +650,17 @@ var learnCommand = new Command4("learn").description("\u89E6\u53D1\u57FA\u56E0\u
|
|
|
259
650
|
}
|
|
260
651
|
return;
|
|
261
652
|
}
|
|
262
|
-
const spinner =
|
|
653
|
+
const spinner = ora3(`\u83B7\u53D6 ${slug} \u7684 manifest...`).start();
|
|
263
654
|
try {
|
|
264
655
|
const manifest = await client.getManifest(slug);
|
|
265
656
|
spinner.succeed(`\u83B7\u53D6 ${manifest.name} v${manifest.version}`);
|
|
266
657
|
if (!manifest.learning?.objectives?.length && !manifest.learning?.scenarios?.length) {
|
|
267
658
|
warn("\u8BE5\u57FA\u56E0\u6CA1\u6709\u5B9A\u4E49\u5B66\u4E60\u76EE\u6807\u6216\u7EC3\u4E60\u573A\u666F\uFF0C\u5C06\u521B\u5EFA\u57FA\u7840\u5B66\u4E60\u4EFB\u52A1");
|
|
268
659
|
}
|
|
269
|
-
const learnSpinner =
|
|
660
|
+
const learnSpinner = ora3("\u751F\u6210\u5B66\u4E60\u4EFB\u52A1...").start();
|
|
270
661
|
const task = await engine.createLearningTask(manifest);
|
|
271
662
|
learnSpinner.succeed("\u5B66\u4E60\u4EFB\u52A1\u5DF2\u521B\u5EFA");
|
|
272
|
-
ok(`\u4EFB\u52A1\u6587\u4EF6: ${
|
|
663
|
+
ok(`\u4EFB\u52A1\u6587\u4EF6: ${join5(workspaceDir, "learning-tasks", `${slug}.md`)}`);
|
|
273
664
|
info(`\u7ED3\u679C\u8DEF\u5F84: ${task.callback_path}`);
|
|
274
665
|
info("");
|
|
275
666
|
info("\u4E0B\u4E00\u6B65\uFF1A");
|
|
@@ -283,11 +674,11 @@ var learnCommand = new Command4("learn").description("\u89E6\u53D1\u57FA\u56E0\u
|
|
|
283
674
|
});
|
|
284
675
|
|
|
285
676
|
// src/commands/list.ts
|
|
286
|
-
import { detectAdapter as
|
|
287
|
-
import { Command as
|
|
288
|
-
var listCommand = new
|
|
677
|
+
import { detectAdapter as detectAdapter4, getAdapter as getAdapter4 } from "@nodeskai/genehub-sdk";
|
|
678
|
+
import { Command as Command7 } from "commander";
|
|
679
|
+
var listCommand = new Command7("list").description("\u5217\u51FA\u5F53\u524D\u73AF\u5883\u5DF2\u5B89\u88C5\u7684\u57FA\u56E0").option("-p, --product <product>", "\u6307\u5B9A\u76EE\u6807\u4EA7\u54C1").option("--json", "JSON \u683C\u5F0F\u8F93\u51FA", false).action(async (opts) => {
|
|
289
680
|
try {
|
|
290
|
-
const adapter = opts.product ?
|
|
681
|
+
const adapter = opts.product ? getAdapter4(opts.product) : await detectAdapter4();
|
|
291
682
|
const genes = await adapter.list();
|
|
292
683
|
if (opts.json) {
|
|
293
684
|
console.log(JSON.stringify(genes, null, 2));
|
|
@@ -310,31 +701,55 @@ var listCommand = new Command5("list").description("\u5217\u51FA\u5F53\u524D\u73
|
|
|
310
701
|
});
|
|
311
702
|
|
|
312
703
|
// src/commands/publish.ts
|
|
313
|
-
import { readFile as
|
|
314
|
-
import { join as
|
|
315
|
-
import { GeneHubClient as
|
|
704
|
+
import { readdir as readdir2, readFile as readFile3 } from "fs/promises";
|
|
705
|
+
import { join as join6, relative as relative2, resolve as resolve3 } from "path";
|
|
706
|
+
import { GeneHubClient as GeneHubClient4 } from "@nodeskai/genehub-sdk";
|
|
316
707
|
import { GeneManifestSchema } from "@nodeskai/genehub-types";
|
|
317
|
-
import { Command as
|
|
318
|
-
import
|
|
319
|
-
import { parse } from "yaml";
|
|
320
|
-
var
|
|
708
|
+
import { Command as Command8 } from "commander";
|
|
709
|
+
import ora4 from "ora";
|
|
710
|
+
import { parse as parse2 } from "yaml";
|
|
711
|
+
var IGNORED_PATTERNS2 = [".git", "node_modules", ".DS_Store", "__pycache__", ".venv"];
|
|
712
|
+
async function scanDirectory2(dirPath) {
|
|
713
|
+
const files = {};
|
|
714
|
+
async function walk(currentPath) {
|
|
715
|
+
const entries = await readdir2(currentPath, { withFileTypes: true });
|
|
716
|
+
for (const entry of entries) {
|
|
717
|
+
if (IGNORED_PATTERNS2.includes(entry.name)) continue;
|
|
718
|
+
const fullPath = join6(currentPath, entry.name);
|
|
719
|
+
if (entry.isDirectory()) {
|
|
720
|
+
await walk(fullPath);
|
|
721
|
+
} else if (entry.isFile()) {
|
|
722
|
+
const relPath = relative2(dirPath, fullPath);
|
|
723
|
+
const content = await readFile3(fullPath, "utf-8");
|
|
724
|
+
files[relPath] = content;
|
|
725
|
+
}
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
await walk(dirPath);
|
|
729
|
+
return files;
|
|
730
|
+
}
|
|
731
|
+
var publishCommand = new Command8("publish").description("\u53D1\u5E03\u57FA\u56E0\u5230 GeneHub Registry").argument("<path>", "\u57FA\u56E0\u76EE\u5F55\u8DEF\u5F84\uFF08\u5305\u542B gene.yaml\uFF09").action(async (dirPath) => {
|
|
321
732
|
const config = await loadConfig();
|
|
322
733
|
if (!config.token) {
|
|
323
|
-
fail("\u672A\u914D\u7F6E\u8BA4\u8BC1 token
|
|
734
|
+
fail("\u672A\u914D\u7F6E\u8BA4\u8BC1 token");
|
|
735
|
+
info(" \u65B9\u5F0F 1: genehub config set token <token>");
|
|
736
|
+
info(" \u65B9\u5F0F 2: export GENEHUB_TOKEN=<token>");
|
|
324
737
|
process.exit(1);
|
|
325
738
|
}
|
|
326
|
-
const client = new
|
|
327
|
-
const absPath =
|
|
739
|
+
const client = new GeneHubClient4({ registryUrl: config.registryUrl, token: config.token });
|
|
740
|
+
const absPath = resolve3(dirPath);
|
|
328
741
|
try {
|
|
329
|
-
const yamlPath =
|
|
330
|
-
const raw = await
|
|
331
|
-
const parsed =
|
|
332
|
-
const skillMdPath = join5(absPath, "SKILL.md");
|
|
742
|
+
const yamlPath = join6(absPath, "gene.yaml");
|
|
743
|
+
const raw = await readFile3(yamlPath, "utf-8");
|
|
744
|
+
const parsed = parse2(raw);
|
|
333
745
|
if (parsed.skill?.file && !parsed.skill.content) {
|
|
334
746
|
try {
|
|
335
|
-
parsed.skill.content = await
|
|
747
|
+
parsed.skill.content = await readFile3(join6(absPath, parsed.skill.file), "utf-8");
|
|
336
748
|
} catch {
|
|
337
|
-
|
|
749
|
+
try {
|
|
750
|
+
parsed.skill.content = await readFile3(join6(absPath, "SKILL.md"), "utf-8");
|
|
751
|
+
} catch {
|
|
752
|
+
}
|
|
338
753
|
}
|
|
339
754
|
}
|
|
340
755
|
const validation = GeneManifestSchema.safeParse(parsed);
|
|
@@ -345,10 +760,23 @@ var publishCommand = new Command6("publish").description("\u53D1\u5E03\u57FA\u56
|
|
|
345
760
|
}
|
|
346
761
|
process.exit(1);
|
|
347
762
|
}
|
|
348
|
-
const
|
|
349
|
-
const
|
|
763
|
+
const { slug, version } = validation.data;
|
|
764
|
+
const scanSpinner = ora4("\u626B\u63CF\u57FA\u56E0\u76EE\u5F55...").start();
|
|
765
|
+
const files = await scanDirectory2(absPath);
|
|
766
|
+
const fileCount = Object.keys(files).length;
|
|
767
|
+
scanSpinner.succeed(`\u626B\u63CF\u5B8C\u6210: ${fileCount} \u4E2A\u6587\u4EF6`);
|
|
768
|
+
const spinner = ora4(`\u53D1\u5E03 ${slug}@${version} (${fileCount} \u4E2A\u6587\u4EF6)...`).start();
|
|
769
|
+
let gene;
|
|
770
|
+
try {
|
|
771
|
+
gene = await client.publishGene(validation.data, files);
|
|
772
|
+
} catch (err) {
|
|
773
|
+
const isSlugExists = err instanceof Error && err.message.includes("gene_slug_exists");
|
|
774
|
+
if (!isSlugExists) throw err;
|
|
775
|
+
spinner.text = `\u57FA\u56E0 ${slug} \u5DF2\u5B58\u5728\uFF0C\u53D1\u5E03\u65B0\u7248\u672C ${version}...`;
|
|
776
|
+
gene = await client.publishVersion(slug, validation.data, void 0, files);
|
|
777
|
+
}
|
|
350
778
|
spinner.succeed("\u53D1\u5E03\u6210\u529F");
|
|
351
|
-
ok(`${gene.slug}@${gene.version} \u5DF2\u53D1\u5E03\u5230 GeneHub Registry`);
|
|
779
|
+
ok(`${gene.slug}@${gene.version} \u5DF2\u53D1\u5E03\u5230 GeneHub Registry (${fileCount} \u4E2A\u6587\u4EF6)`);
|
|
352
780
|
} catch (err) {
|
|
353
781
|
fail(err instanceof Error ? err.message : String(err));
|
|
354
782
|
process.exit(1);
|
|
@@ -356,11 +784,11 @@ var publishCommand = new Command6("publish").description("\u53D1\u5E03\u57FA\u56
|
|
|
356
784
|
});
|
|
357
785
|
|
|
358
786
|
// src/commands/search.ts
|
|
359
|
-
import { GeneHubClient as
|
|
360
|
-
import { Command as
|
|
361
|
-
var searchCommand = new
|
|
787
|
+
import { GeneHubClient as GeneHubClient5 } from "@nodeskai/genehub-sdk";
|
|
788
|
+
import { Command as Command9 } from "commander";
|
|
789
|
+
var searchCommand = new Command9("search").description("\u641C\u7D22\u57FA\u56E0\u5E93").argument("[keyword]", "\u641C\u7D22\u5173\u952E\u8BCD").option("-c, --category <category>", "\u6309\u5206\u7C7B\u8FC7\u6EE4").option("-t, --tags <tags>", "\u6309\u6807\u7B7E\u8FC7\u6EE4\uFF08\u9017\u53F7\u5206\u9694\uFF09").option("--compat <product>", "\u6309\u517C\u5BB9\u4EA7\u54C1\u8FC7\u6EE4").option("-s, --sort <sort>", "\u6392\u5E8F\u65B9\u5F0F\uFF08newest / popular / rating\uFF09", "newest").option("--page <page>", "\u9875\u7801", "1").option("--json", "JSON \u683C\u5F0F\u8F93\u51FA", false).action(async (keyword, opts) => {
|
|
362
790
|
const config = await loadConfig();
|
|
363
|
-
const client = new
|
|
791
|
+
const client = new GeneHubClient5({ registryUrl: config.registryUrl, token: config.token });
|
|
364
792
|
try {
|
|
365
793
|
const result = await client.searchGenes({
|
|
366
794
|
q: keyword,
|
|
@@ -396,18 +824,323 @@ var searchCommand = new Command7("search").description("\u641C\u7D22\u57FA\u56E0
|
|
|
396
824
|
}
|
|
397
825
|
});
|
|
398
826
|
|
|
827
|
+
// src/commands/template.ts
|
|
828
|
+
import { readdir as readdir3, readFile as readFile4, rm as rm3, writeFile as writeFile5 } from "fs/promises";
|
|
829
|
+
import { tmpdir as tmpdir3 } from "os";
|
|
830
|
+
import { join as join7, relative as relative3, resolve as resolve4 } from "path";
|
|
831
|
+
import { detectAdapter as detectAdapter5, GeneHubClient as GeneHubClient6, getAdapter as getAdapter5 } from "@nodeskai/genehub-sdk";
|
|
832
|
+
import { Command as Command10 } from "commander";
|
|
833
|
+
import ora5 from "ora";
|
|
834
|
+
import { parse as parse3 } from "yaml";
|
|
835
|
+
var IGNORED_PATTERNS3 = [".git", "node_modules", ".DS_Store", "__pycache__", ".venv"];
|
|
836
|
+
async function scanDirectory3(dirPath) {
|
|
837
|
+
const files = {};
|
|
838
|
+
async function walk(currentPath) {
|
|
839
|
+
const entries = await readdir3(currentPath, { withFileTypes: true });
|
|
840
|
+
for (const entry of entries) {
|
|
841
|
+
if (IGNORED_PATTERNS3.includes(entry.name)) continue;
|
|
842
|
+
const fullPath = join7(currentPath, entry.name);
|
|
843
|
+
if (entry.isDirectory()) {
|
|
844
|
+
await walk(fullPath);
|
|
845
|
+
} else if (entry.isFile()) {
|
|
846
|
+
const relPath = relative3(dirPath, fullPath);
|
|
847
|
+
const content = await readFile4(fullPath, "utf-8");
|
|
848
|
+
files[relPath] = content;
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
}
|
|
852
|
+
await walk(dirPath);
|
|
853
|
+
return files;
|
|
854
|
+
}
|
|
855
|
+
async function extractTarGz3(buffer, destDir) {
|
|
856
|
+
const { mkdir: mkdir4 } = await import("fs/promises");
|
|
857
|
+
await mkdir4(destDir, { recursive: true });
|
|
858
|
+
const { extract } = await import("tar");
|
|
859
|
+
const tarPath = join7(tmpdir3(), `genehub-template-${Date.now()}.tar.gz`);
|
|
860
|
+
await writeFile5(tarPath, Buffer.from(buffer));
|
|
861
|
+
try {
|
|
862
|
+
await extract({ file: tarPath, cwd: destDir, strip: 1 });
|
|
863
|
+
} finally {
|
|
864
|
+
await rm3(tarPath, { force: true });
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
var publishTemplateCommand = new Command10("publish").description("\u53D1\u5E03 AI \u5458\u5DE5\u6A21\u677F\u5230 GeneHub Registry").argument("<path>", "\u6A21\u677F\u76EE\u5F55\u8DEF\u5F84\uFF08\u5305\u542B template.yaml\uFF09").action(async (dirPath) => {
|
|
868
|
+
const config = await loadConfig();
|
|
869
|
+
if (!config.token) {
|
|
870
|
+
fail("\u672A\u914D\u7F6E\u8BA4\u8BC1 token");
|
|
871
|
+
process.exit(1);
|
|
872
|
+
}
|
|
873
|
+
const client = new GeneHubClient6({ registryUrl: config.registryUrl, token: config.token });
|
|
874
|
+
const absPath = resolve4(dirPath);
|
|
875
|
+
try {
|
|
876
|
+
const yamlPath = join7(absPath, "template.yaml");
|
|
877
|
+
const raw = await readFile4(yamlPath, "utf-8");
|
|
878
|
+
const parsed = parse3(raw);
|
|
879
|
+
if (!parsed.name || !parsed.slug || !parsed.version) {
|
|
880
|
+
fail("template.yaml \u7F3A\u5C11\u5FC5\u586B\u5B57\u6BB5: name, slug, version");
|
|
881
|
+
process.exit(1);
|
|
882
|
+
}
|
|
883
|
+
if (!parsed.genomes?.length && !parsed.genes?.length) {
|
|
884
|
+
fail("template.yaml \u81F3\u5C11\u9700\u8981\u5F15\u7528\u4E00\u4E2A genome \u6216 gene");
|
|
885
|
+
process.exit(1);
|
|
886
|
+
}
|
|
887
|
+
const scanSpinner = ora5("\u626B\u63CF\u6A21\u677F\u76EE\u5F55...").start();
|
|
888
|
+
const files = await scanDirectory3(absPath);
|
|
889
|
+
const fileCount = Object.keys(files).length;
|
|
890
|
+
scanSpinner.succeed(`\u626B\u63CF\u5B8C\u6210: ${fileCount} \u4E2A\u6587\u4EF6`);
|
|
891
|
+
const spinner = ora5(`\u53D1\u5E03\u6A21\u677F ${parsed.slug}@${parsed.version}...`).start();
|
|
892
|
+
try {
|
|
893
|
+
const template = await client.publishTemplate(parsed, files);
|
|
894
|
+
spinner.succeed("\u53D1\u5E03\u6210\u529F");
|
|
895
|
+
ok(
|
|
896
|
+
`${template.slug}@${template.version} \u5DF2\u53D1\u5E03\u5230 GeneHub Registry (${fileCount} \u4E2A\u6587\u4EF6)`
|
|
897
|
+
);
|
|
898
|
+
} catch (err) {
|
|
899
|
+
const isSlugExists = err instanceof Error && err.message.includes("template_slug_exists");
|
|
900
|
+
if (!isSlugExists) throw err;
|
|
901
|
+
spinner.text = `\u6A21\u677F ${parsed.slug} \u5DF2\u5B58\u5728\uFF0C\u53D1\u5E03\u65B0\u7248\u672C ${parsed.version}...`;
|
|
902
|
+
const template = await client.publishTemplateVersion(
|
|
903
|
+
parsed.slug,
|
|
904
|
+
{
|
|
905
|
+
version: parsed.version,
|
|
906
|
+
genomes: parsed.genomes ?? [],
|
|
907
|
+
genes: parsed.genes,
|
|
908
|
+
files
|
|
909
|
+
},
|
|
910
|
+
files
|
|
911
|
+
);
|
|
912
|
+
spinner.succeed("\u53D1\u5E03\u6210\u529F");
|
|
913
|
+
ok(`${template.slug}@${template.version} \u65B0\u7248\u672C\u5DF2\u53D1\u5E03 (${fileCount} \u4E2A\u6587\u4EF6)`);
|
|
914
|
+
}
|
|
915
|
+
} catch (err) {
|
|
916
|
+
fail(err instanceof Error ? err.message : String(err));
|
|
917
|
+
process.exit(1);
|
|
918
|
+
}
|
|
919
|
+
});
|
|
920
|
+
var installTemplateCommand = new Command10("install").description("\u5B89\u88C5 AI \u5458\u5DE5\u6A21\u677F\uFF08\u9012\u5F52\u5B89\u88C5\u6240\u6709\u57FA\u56E0\u7EC4\u548C\u57FA\u56E0\uFF09").argument("<slug>", "\u6A21\u677F\u6807\u8BC6\u7B26\uFF08\u652F\u6301 slug@version \u683C\u5F0F\uFF09").option("-p, --product <product>", "\u6307\u5B9A\u76EE\u6807\u4EA7\u54C1").option("-f, --force", "\u5F3A\u5236\u8986\u76D6\u5DF2\u5B89\u88C5\u57FA\u56E0", false).option("--target <path>", "\u6307\u5B9A\u5B89\u88C5\u76EE\u6807\u8DEF\u5F84").action(async (rawSlug, opts) => {
|
|
921
|
+
const config = await loadConfig();
|
|
922
|
+
const client = new GeneHubClient6({ registryUrl: config.registryUrl, token: config.token });
|
|
923
|
+
const atIdx = rawSlug.lastIndexOf("@");
|
|
924
|
+
const slug = atIdx > 0 ? rawSlug.slice(0, atIdx) : rawSlug;
|
|
925
|
+
const spinner = ora5(`\u83B7\u53D6\u6A21\u677F ${slug}...`).start();
|
|
926
|
+
try {
|
|
927
|
+
const template = await client.getTemplate(slug);
|
|
928
|
+
spinner.succeed(
|
|
929
|
+
`\u6A21\u677F: ${template.name} v${template.version} (${template.genomes.length} \u57FA\u56E0\u7EC4, ${template.genes.length} \u989D\u5916\u57FA\u56E0)`
|
|
930
|
+
);
|
|
931
|
+
const adapter = opts.product ? getAdapter5(opts.product) : await detectAdapter5();
|
|
932
|
+
info(`\u76EE\u6807\u4EA7\u54C1: ${adapter.product}`);
|
|
933
|
+
const allGeneSlugs = /* @__PURE__ */ new Set();
|
|
934
|
+
let installed = 0;
|
|
935
|
+
let skipped = 0;
|
|
936
|
+
for (const genomeRef of template.genomes) {
|
|
937
|
+
info(`
|
|
938
|
+
\u89E3\u6790\u57FA\u56E0\u7EC4: ${genomeRef.slug}@${genomeRef.version}`);
|
|
939
|
+
try {
|
|
940
|
+
const resolved = await client.resolveGenome(genomeRef.slug, genomeRef.version);
|
|
941
|
+
if (resolved.warnings.length > 0) {
|
|
942
|
+
for (const w of resolved.warnings) warn(w);
|
|
943
|
+
}
|
|
944
|
+
for (const gene of resolved.genes) {
|
|
945
|
+
if (allGeneSlugs.has(gene.slug)) continue;
|
|
946
|
+
allGeneSlugs.add(gene.slug);
|
|
947
|
+
if (!opts.force && await adapter.isInstalled(gene.slug)) {
|
|
948
|
+
skipped++;
|
|
949
|
+
continue;
|
|
950
|
+
}
|
|
951
|
+
const geneSpinner = ora5(` \u5B89\u88C5 ${gene.slug}@${gene.version}...`).start();
|
|
952
|
+
try {
|
|
953
|
+
const manifest = await client.getManifest(gene.slug, gene.version);
|
|
954
|
+
let result;
|
|
955
|
+
try {
|
|
956
|
+
const archive = await client.downloadArchive(gene.slug, gene.version);
|
|
957
|
+
const tempDir = join7(tmpdir3(), `genehub-install-${gene.slug}-${Date.now()}`);
|
|
958
|
+
await extractTarGz3(archive, tempDir);
|
|
959
|
+
if (adapter.installFromDirectory) {
|
|
960
|
+
result = await adapter.installFromDirectory(tempDir, manifest, {
|
|
961
|
+
force: opts.force,
|
|
962
|
+
targetPath: opts.target
|
|
963
|
+
});
|
|
964
|
+
} else {
|
|
965
|
+
result = await adapter.install(manifest, {
|
|
966
|
+
force: opts.force,
|
|
967
|
+
targetPath: opts.target
|
|
968
|
+
});
|
|
969
|
+
}
|
|
970
|
+
await rm3(tempDir, { recursive: true, force: true });
|
|
971
|
+
} catch {
|
|
972
|
+
result = await adapter.install(manifest, {
|
|
973
|
+
force: opts.force,
|
|
974
|
+
targetPath: opts.target
|
|
975
|
+
});
|
|
976
|
+
}
|
|
977
|
+
geneSpinner.succeed(` ${result.slug}@${result.version}`);
|
|
978
|
+
installed++;
|
|
979
|
+
try {
|
|
980
|
+
await client.reportInstall(gene.slug);
|
|
981
|
+
} catch {
|
|
982
|
+
}
|
|
983
|
+
} catch (err) {
|
|
984
|
+
geneSpinner.fail(
|
|
985
|
+
` ${gene.slug} \u5B89\u88C5\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`
|
|
986
|
+
);
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
} catch (err) {
|
|
990
|
+
warn(
|
|
991
|
+
`\u57FA\u56E0\u7EC4 ${genomeRef.slug} \u89E3\u6790\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`
|
|
992
|
+
);
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
if (template.genes.length > 0) {
|
|
996
|
+
info(`
|
|
997
|
+
\u5B89\u88C5\u989D\u5916\u57FA\u56E0 (${template.genes.length}\u4E2A):`);
|
|
998
|
+
for (const geneRef of template.genes) {
|
|
999
|
+
if (allGeneSlugs.has(geneRef.slug)) continue;
|
|
1000
|
+
allGeneSlugs.add(geneRef.slug);
|
|
1001
|
+
if (!opts.force && await adapter.isInstalled(geneRef.slug)) {
|
|
1002
|
+
skipped++;
|
|
1003
|
+
continue;
|
|
1004
|
+
}
|
|
1005
|
+
const geneSpinner = ora5(` \u5B89\u88C5 ${geneRef.slug}@${geneRef.version}...`).start();
|
|
1006
|
+
try {
|
|
1007
|
+
const manifest = await client.getManifest(geneRef.slug, geneRef.version);
|
|
1008
|
+
let result;
|
|
1009
|
+
try {
|
|
1010
|
+
const archive = await client.downloadArchive(geneRef.slug, geneRef.version);
|
|
1011
|
+
const tempDir = join7(tmpdir3(), `genehub-install-${geneRef.slug}-${Date.now()}`);
|
|
1012
|
+
await extractTarGz3(archive, tempDir);
|
|
1013
|
+
if (adapter.installFromDirectory) {
|
|
1014
|
+
result = await adapter.installFromDirectory(tempDir, manifest, {
|
|
1015
|
+
force: opts.force,
|
|
1016
|
+
targetPath: opts.target
|
|
1017
|
+
});
|
|
1018
|
+
} else {
|
|
1019
|
+
result = await adapter.install(manifest, {
|
|
1020
|
+
force: opts.force,
|
|
1021
|
+
targetPath: opts.target
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
await rm3(tempDir, { recursive: true, force: true });
|
|
1025
|
+
} catch {
|
|
1026
|
+
result = await adapter.install(manifest, {
|
|
1027
|
+
force: opts.force,
|
|
1028
|
+
targetPath: opts.target
|
|
1029
|
+
});
|
|
1030
|
+
}
|
|
1031
|
+
geneSpinner.succeed(` ${result.slug}@${result.version}`);
|
|
1032
|
+
installed++;
|
|
1033
|
+
try {
|
|
1034
|
+
await client.reportInstall(geneRef.slug);
|
|
1035
|
+
} catch {
|
|
1036
|
+
}
|
|
1037
|
+
} catch (err) {
|
|
1038
|
+
geneSpinner.fail(
|
|
1039
|
+
` ${geneRef.slug} \u5B89\u88C5\u5931\u8D25: ${err instanceof Error ? err.message : String(err)}`
|
|
1040
|
+
);
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
ok(`\u6A21\u677F\u5B89\u88C5\u5B8C\u6210: ${installed} \u5B89\u88C5, ${skipped} \u8DF3\u8FC7`);
|
|
1045
|
+
try {
|
|
1046
|
+
await client.reportTemplateInstall(slug);
|
|
1047
|
+
} catch {
|
|
1048
|
+
}
|
|
1049
|
+
} catch (err) {
|
|
1050
|
+
spinner.fail("\u5B89\u88C5\u5931\u8D25");
|
|
1051
|
+
fail(err instanceof Error ? err.message : String(err));
|
|
1052
|
+
process.exit(1);
|
|
1053
|
+
}
|
|
1054
|
+
});
|
|
1055
|
+
var infoTemplateCommand = new Command10("info").description("\u67E5\u770B AI \u5458\u5DE5\u6A21\u677F\u8BE6\u60C5").argument("<slug>", "\u6A21\u677F\u6807\u8BC6\u7B26").option("--json", "JSON \u683C\u5F0F\u8F93\u51FA", false).action(async (slug, opts) => {
|
|
1056
|
+
const config = await loadConfig();
|
|
1057
|
+
const client = new GeneHubClient6({ registryUrl: config.registryUrl, token: config.token });
|
|
1058
|
+
try {
|
|
1059
|
+
const template = await client.getTemplate(slug);
|
|
1060
|
+
if (opts.json) {
|
|
1061
|
+
console.log(JSON.stringify(template, null, 2));
|
|
1062
|
+
return;
|
|
1063
|
+
}
|
|
1064
|
+
info(`\u540D\u79F0: ${template.name}`);
|
|
1065
|
+
info(`Slug: ${template.slug}`);
|
|
1066
|
+
info(`\u7248\u672C: ${template.version}`);
|
|
1067
|
+
info(`\u89D2\u8272: ${template.role ?? "(\u65E0)"}`);
|
|
1068
|
+
info(`\u5206\u7C7B: ${template.category}`);
|
|
1069
|
+
info(`\u63CF\u8FF0: ${template.description || template.short_description || "(\u65E0)"}`);
|
|
1070
|
+
info(`\u5B89\u88C5\u6570: ${template.install_count}`);
|
|
1071
|
+
if (template.genomes.length > 0) {
|
|
1072
|
+
info(`
|
|
1073
|
+
\u57FA\u56E0\u7EC4 (${template.genomes.length}\u4E2A):`);
|
|
1074
|
+
table(
|
|
1075
|
+
["slug", "\u7248\u672C"],
|
|
1076
|
+
template.genomes.map((g) => [g.slug, g.version])
|
|
1077
|
+
);
|
|
1078
|
+
}
|
|
1079
|
+
if (template.genes.length > 0) {
|
|
1080
|
+
info(`
|
|
1081
|
+
\u989D\u5916\u57FA\u56E0 (${template.genes.length}\u4E2A):`);
|
|
1082
|
+
table(
|
|
1083
|
+
["slug", "\u7248\u672C"],
|
|
1084
|
+
template.genes.map((g) => [g.slug, g.version])
|
|
1085
|
+
);
|
|
1086
|
+
}
|
|
1087
|
+
} catch (err) {
|
|
1088
|
+
fail(err instanceof Error ? err.message : String(err));
|
|
1089
|
+
process.exit(1);
|
|
1090
|
+
}
|
|
1091
|
+
});
|
|
1092
|
+
var listTemplateCommand = new Command10("list").description("\u641C\u7D22 AI \u5458\u5DE5\u6A21\u677F").option("-q, --query <keyword>", "\u641C\u7D22\u5173\u952E\u8BCD").option("-c, --category <category>", "\u6309\u5206\u7C7B\u8FC7\u6EE4").option("-r, --role <role>", "\u6309\u89D2\u8272\u8FC7\u6EE4").option("--json", "JSON \u683C\u5F0F\u8F93\u51FA", false).action(async (opts) => {
|
|
1093
|
+
const config = await loadConfig();
|
|
1094
|
+
const client = new GeneHubClient6({ registryUrl: config.registryUrl, token: config.token });
|
|
1095
|
+
try {
|
|
1096
|
+
const result = await client.searchTemplates({
|
|
1097
|
+
q: opts.query,
|
|
1098
|
+
category: opts.category,
|
|
1099
|
+
role: opts.role
|
|
1100
|
+
});
|
|
1101
|
+
if (opts.json) {
|
|
1102
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1103
|
+
return;
|
|
1104
|
+
}
|
|
1105
|
+
if (result.items.length === 0) {
|
|
1106
|
+
info("\u672A\u627E\u5230\u6A21\u677F");
|
|
1107
|
+
return;
|
|
1108
|
+
}
|
|
1109
|
+
table(
|
|
1110
|
+
["slug", "\u540D\u79F0", "\u7248\u672C", "\u89D2\u8272", "\u57FA\u56E0\u7EC4", "\u5B89\u88C5\u6570"],
|
|
1111
|
+
result.items.map((t) => [
|
|
1112
|
+
t.slug,
|
|
1113
|
+
t.name,
|
|
1114
|
+
t.version,
|
|
1115
|
+
t.role ?? "-",
|
|
1116
|
+
String(t.genomes.length),
|
|
1117
|
+
String(t.install_count)
|
|
1118
|
+
])
|
|
1119
|
+
);
|
|
1120
|
+
info(`\u5171 ${result.total} \u6761\u7ED3\u679C`);
|
|
1121
|
+
} catch (err) {
|
|
1122
|
+
fail(err instanceof Error ? err.message : String(err));
|
|
1123
|
+
process.exit(1);
|
|
1124
|
+
}
|
|
1125
|
+
});
|
|
1126
|
+
var templateCommand = new Command10("template").description("AI \u5458\u5DE5\u6A21\u677F\u7BA1\u7406");
|
|
1127
|
+
templateCommand.addCommand(publishTemplateCommand);
|
|
1128
|
+
templateCommand.addCommand(installTemplateCommand);
|
|
1129
|
+
templateCommand.addCommand(infoTemplateCommand);
|
|
1130
|
+
templateCommand.addCommand(listTemplateCommand);
|
|
1131
|
+
|
|
399
1132
|
// src/commands/uninstall.ts
|
|
400
|
-
import { detectAdapter as
|
|
401
|
-
import { Command as
|
|
402
|
-
import
|
|
403
|
-
var uninstallCommand = new
|
|
404
|
-
const adapter = opts.product ?
|
|
1133
|
+
import { detectAdapter as detectAdapter6, getAdapter as getAdapter6 } from "@nodeskai/genehub-sdk";
|
|
1134
|
+
import { Command as Command11 } from "commander";
|
|
1135
|
+
import ora6 from "ora";
|
|
1136
|
+
var uninstallCommand = new Command11("uninstall").description("\u4ECE\u5F53\u524D Agent \u73AF\u5883\u5378\u8F7D\u57FA\u56E0").argument("<slug>", "\u57FA\u56E0\u6807\u8BC6\u7B26").option("-p, --product <product>", "\u6307\u5B9A\u76EE\u6807\u4EA7\u54C1\uFF08openclaw / nanobot / generic\uFF09").action(async (slug, opts) => {
|
|
1137
|
+
const adapter = opts.product ? getAdapter6(opts.product) : await detectAdapter6();
|
|
405
1138
|
info(`\u76EE\u6807\u4EA7\u54C1: ${adapter.product}`);
|
|
406
1139
|
if (!await adapter.isInstalled(slug)) {
|
|
407
1140
|
warn(`${slug} \u672A\u5B89\u88C5`);
|
|
408
1141
|
return;
|
|
409
1142
|
}
|
|
410
|
-
const spinner =
|
|
1143
|
+
const spinner = ora6("\u5378\u8F7D\u4E2D...").start();
|
|
411
1144
|
try {
|
|
412
1145
|
const result = await adapter.uninstall(slug);
|
|
413
1146
|
spinner.succeed("\u5378\u8F7D\u5B8C\u6210");
|
|
@@ -424,8 +1157,9 @@ var uninstallCommand = new Command8("uninstall").description("\u4ECE\u5F53\u524D
|
|
|
424
1157
|
});
|
|
425
1158
|
|
|
426
1159
|
// src/index.ts
|
|
427
|
-
var program = new
|
|
1160
|
+
var program = new Command12();
|
|
428
1161
|
program.name("genehub").description("GeneHub CLI - AI \u5458\u5DE5\u57FA\u56E0\u7BA1\u7406\u5DE5\u5177").version("0.1.0");
|
|
1162
|
+
program.addCommand(authCommand);
|
|
429
1163
|
program.addCommand(installCommand);
|
|
430
1164
|
program.addCommand(uninstallCommand);
|
|
431
1165
|
program.addCommand(searchCommand);
|
|
@@ -434,5 +1168,7 @@ program.addCommand(publishCommand);
|
|
|
434
1168
|
program.addCommand(initCommand);
|
|
435
1169
|
program.addCommand(configCommand);
|
|
436
1170
|
program.addCommand(learnCommand);
|
|
1171
|
+
program.addCommand(genomeCommand);
|
|
1172
|
+
program.addCommand(templateCommand);
|
|
437
1173
|
program.parse();
|
|
438
1174
|
//# sourceMappingURL=index.js.map
|