@helixlife-ai/xiantao 0.1.8 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/commands/plot/download.js +1 -1
- package/dist/commands/plot/run.js +44 -14
- package/dist/lib/download.js +8 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -67,7 +67,7 @@ For machine callers, use `xt tool exec`, keep `--json`, pass an explicit profile
|
|
|
67
67
|
|
|
68
68
|
For the common Xiantao tool product, `tool search`, `tool inspect`, `tool run`, `tool resolve`, and `tool menu` default `toolProductUuid` to `c0b6febb-52dd-4525-970a-61bbe9e263ff`. You can override it with `--toolProductUuid`, `XIANTAO_TOOL_PRODUCT_UUID`, or `~/.config/xtz/config.json` under `xiantao.toolProductUuid`.
|
|
69
69
|
|
|
70
|
-
`xt tool search <keyword>` searches cached or remote tool menus by tool id, title, path, and route. `xt tool inspect <tool_id>` prints resolved metadata together with the dynamic `args_main` schema used by the tool. `xt tool run <tool_id>` runs a Xiantao bioinformatics tool. `xt tool resolve <tool_id>` remains available as the low-level UUID and route lookup. `--demo` reuses the demo file metadata returned by `tool fetch-all`; otherwise `tool run` uploads the local file first and therefore also needs upload auth. Run `xt login` first to refresh both tokens in one step.
|
|
70
|
+
`xt tool search <keyword>` searches cached or remote tool menus by tool id, title, path, and route. `xt tool inspect <tool_id>` prints resolved metadata together with the dynamic `args_main` schema used by the tool. `xt tool run <tool_id>` runs a Xiantao bioinformatics tool. `xt tool resolve <tool_id>` remains available as the low-level UUID and route lookup. `--demo` reuses the demo file metadata returned by `tool fetch-all`; otherwise `tool run` uploads the local file first and therefore also needs upload auth. Run `xt login` first to refresh both tokens in one step. When a tool exposes multiple downloadable results, `--download <path>` uses the output file extension to select the matching artifact.
|
|
71
71
|
|
|
72
72
|
`xt tool run <tool_id> ... --interactive` prompts for dynamic `args_main` values before the final submit, prints reusable `--set` flags, and supports `<` for the previous item plus `/skip` for the current section.
|
|
73
73
|
|
|
@@ -16,7 +16,7 @@ export default class PlotDownload extends XtzCommand {
|
|
|
16
16
|
}),
|
|
17
17
|
output: Flags.string({
|
|
18
18
|
char: 'o',
|
|
19
|
-
description: 'Destination path; defaults to
|
|
19
|
+
description: 'Destination path; defaults to ~/Downloads/<file name>',
|
|
20
20
|
}),
|
|
21
21
|
};
|
|
22
22
|
async run() {
|
|
@@ -135,30 +135,60 @@ export class ToolRunCommandBase extends XtzCommand {
|
|
|
135
135
|
return lines.join('\n');
|
|
136
136
|
}
|
|
137
137
|
async downloadResult(auth, input) {
|
|
138
|
+
if (input.downloads.length > 0) {
|
|
139
|
+
const item = this.pickDownloadItem(input.downloads, input.downloadPath);
|
|
140
|
+
if (!item) {
|
|
141
|
+
throw new XtzError('当前模块返回多个可下载结果,请将 `--download` 路径改为带目标扩展名的文件名,或使用 `--interactive` 选择。');
|
|
142
|
+
}
|
|
143
|
+
const resolved = await resolveXiantaoDownload(auth, {
|
|
144
|
+
item,
|
|
145
|
+
moduleId: input.moduleId,
|
|
146
|
+
uuid: input.uuid,
|
|
147
|
+
});
|
|
148
|
+
return [await downloadToPath(resolved.url, input.downloadPath)];
|
|
149
|
+
}
|
|
138
150
|
if (input.file) {
|
|
139
151
|
return [await downloadToPath(input.file, input.downloadPath)];
|
|
140
152
|
}
|
|
141
|
-
|
|
142
|
-
if (!item) {
|
|
143
|
-
throw new XtzError('当前模块未返回可下载文件。');
|
|
144
|
-
}
|
|
145
|
-
const resolved = await resolveXiantaoDownload(auth, {
|
|
146
|
-
item,
|
|
147
|
-
moduleId: input.moduleId,
|
|
148
|
-
uuid: input.uuid,
|
|
149
|
-
});
|
|
150
|
-
return [await downloadToPath(resolved.url, input.downloadPath)];
|
|
153
|
+
throw new XtzError('当前模块未返回可下载文件。');
|
|
151
154
|
}
|
|
152
|
-
pickDownloadItem(downloads) {
|
|
153
|
-
const
|
|
154
|
-
if (
|
|
155
|
-
|
|
155
|
+
pickDownloadItem(downloads, downloadPath) {
|
|
156
|
+
const expectedDevice = this.inferDownloadDevice(downloadPath);
|
|
157
|
+
if (expectedDevice) {
|
|
158
|
+
const matched = downloads.filter((item) => this.normalizeDevice(item.device) === expectedDevice);
|
|
159
|
+
if (matched.length === 1) {
|
|
160
|
+
return matched[0];
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
const results = downloads.filter((item) => item.endpoint === 'public.output.download-result');
|
|
164
|
+
if (results.length === 1) {
|
|
165
|
+
return results[0];
|
|
156
166
|
}
|
|
157
167
|
if (downloads.length === 1) {
|
|
158
168
|
return downloads[0];
|
|
159
169
|
}
|
|
160
170
|
return undefined;
|
|
161
171
|
}
|
|
172
|
+
inferDownloadDevice(downloadPath) {
|
|
173
|
+
if (!downloadPath) {
|
|
174
|
+
return undefined;
|
|
175
|
+
}
|
|
176
|
+
const extension = downloadPath.trim().split('.').pop()?.toLowerCase();
|
|
177
|
+
if (!extension) {
|
|
178
|
+
return undefined;
|
|
179
|
+
}
|
|
180
|
+
return this.normalizeDevice(extension);
|
|
181
|
+
}
|
|
182
|
+
normalizeDevice(device) {
|
|
183
|
+
const normalized = device.trim().toLowerCase();
|
|
184
|
+
if (normalized === 'tif') {
|
|
185
|
+
return 'tiff';
|
|
186
|
+
}
|
|
187
|
+
if (normalized === 'jpg') {
|
|
188
|
+
return 'jpeg';
|
|
189
|
+
}
|
|
190
|
+
return normalized;
|
|
191
|
+
}
|
|
162
192
|
async downloadSelectedResults(auth, input) {
|
|
163
193
|
const paths = [];
|
|
164
194
|
for (const item of input.downloads) {
|
package/dist/lib/download.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import os from 'node:os';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
+
import { writeFile } from 'node:fs/promises';
|
|
3
4
|
import { XtzError } from './errors.js';
|
|
4
5
|
export async function downloadToPath(url, outputPath) {
|
|
5
6
|
let response;
|
|
@@ -12,18 +13,20 @@ export async function downloadToPath(url, outputPath) {
|
|
|
12
13
|
if (!response.ok) {
|
|
13
14
|
throw new XtzError(`下载失败 (${response.status})`);
|
|
14
15
|
}
|
|
15
|
-
const targetPath =
|
|
16
|
+
const targetPath = outputPath
|
|
17
|
+
? path.resolve(outputPath)
|
|
18
|
+
: inferDownloadPath(url);
|
|
16
19
|
const buffer = Buffer.from(await response.arrayBuffer());
|
|
17
20
|
await writeFile(targetPath, buffer);
|
|
18
21
|
return targetPath;
|
|
19
22
|
}
|
|
20
|
-
function
|
|
23
|
+
function inferDownloadPath(url) {
|
|
21
24
|
try {
|
|
22
25
|
const pathname = new URL(url).pathname;
|
|
23
26
|
const name = decodeURIComponent(path.basename(pathname));
|
|
24
|
-
return name || 'download.bin';
|
|
27
|
+
return path.join(os.homedir(), 'Downloads', name || 'download.bin');
|
|
25
28
|
}
|
|
26
29
|
catch {
|
|
27
|
-
return 'download.bin';
|
|
30
|
+
return path.join(os.homedir(), 'Downloads', 'download.bin');
|
|
28
31
|
}
|
|
29
32
|
}
|