@helixlife-ai/xiantao 0.1.6 → 0.1.8

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 CHANGED
@@ -6,7 +6,7 @@ Install with `npm i -g @helixlife-ai/xiantao` and run it as `xt`.
6
6
 
7
7
  `tool` is the primary product topic for bioinformatics tools. `plot` is kept as a compatibility entrypoint for the current command set and prints a migration warning when executed.
8
8
 
9
- `--profile` is the primary profile selector for token lookup and config isolation, for example `default` or `openclaw`. `--agent` is kept as a compatibility alias.
9
+ `--profile` is the primary profile selector for token lookup and config isolation. If it is omitted, the CLI resolves the profile from `AGENT_NAME`, then the host runtime (`cursor`, `opencode`, `openclaw`), then `~/.config/xtz/config.json`, and finally defaults to `opencode`. `--agent` is kept as a compatibility alias.
10
10
 
11
11
  `login`, `status`, and `logout` are the primary auth entrypoints. `auth ...` is kept as a compatibility topic and prints a migration warning when executed.
12
12
 
@@ -34,7 +34,7 @@ This starts a menu-driven path for choosing a tool, selecting demo data or a loc
34
34
  ### Agent Automation
35
35
 
36
36
  ```bash
37
- xt tool exec violin_flat --file ./data.xlsx --profile openclaw --json
37
+ xt tool exec violin_flat --file ./data.xlsx --profile opencode --json
38
38
  ```
39
39
 
40
40
  For machine callers, use `xt tool exec`, keep `--json`, pass an explicit profile with `--profile`, and avoid interactive-only flows.
@@ -0,0 +1,53 @@
1
+ import { Flags } from '@oclif/core';
2
+ import { XtzCommand } from '../../base-command.js';
3
+ import { loginAuth, waitForAuthorizedToken } from '../../lib/auth.js';
4
+ import { globalFlags } from '../../lib/flags.js';
5
+ import { bindUserWithXiantaoToken } from '../../lib/xiantao-auth.js';
6
+ import { getTokenPath, getXiantaoTokenPath, loadXiantaoToken, saveToken } from '../../lib/storage.js';
7
+ export default class AuthBind extends XtzCommand {
8
+ static description = 'Bind login remotely with a Xiantao web token without the localhost callback flow';
9
+ static flags = {
10
+ ...globalFlags,
11
+ 'auth-key': Flags.string({
12
+ char: 'k',
13
+ description: 'Existing auth key returned by the upload authorization flow',
14
+ }),
15
+ 'token-key': Flags.string({
16
+ description: 'Existing upload token key paired with --auth-key',
17
+ }),
18
+ 'xiantao-token': Flags.string({
19
+ description: 'Bearer token for api.helixlife.net xiantao APIs; falls back to XIANTAO_TOKEN or the stored Xiantao web token',
20
+ }),
21
+ };
22
+ async run() {
23
+ this.warnIfLegacyAuthCommand();
24
+ const { flags } = await this.parse(AuthBind);
25
+ const agent = await this.getProfile(flags.profile);
26
+ const token = flags['xiantao-token']?.trim() || process.env.XIANTAO_TOKEN?.trim() || await loadXiantaoToken(agent);
27
+ if (!token) {
28
+ this.error(`未找到 ${agent} 的仙桃网页 token,请传 --xiantao-token、设置 XIANTAO_TOKEN,或先准备 ${getXiantaoTokenPath(agent)}`, { exit: 1 });
29
+ }
30
+ const auth = flags['auth-key']
31
+ ? {
32
+ authKey: flags['auth-key'],
33
+ token: flags['token-key']?.trim(),
34
+ tokenPath: getTokenPath(agent),
35
+ }
36
+ : await loginAuth(agent, false);
37
+ if (!auth.token) {
38
+ this.error('传 --auth-key 时必须同时传 --token-key', { exit: 1 });
39
+ }
40
+ if (flags['auth-key']) {
41
+ await saveToken(agent, auth.token);
42
+ }
43
+ const result = await bindUserWithXiantaoToken(agent, auth.authKey, token);
44
+ await waitForAuthorizedToken(auth.token);
45
+ this.print(flags, {
46
+ agent,
47
+ auth_key: auth.authKey,
48
+ bound: true,
49
+ token_path: auth.tokenPath,
50
+ xiantao_token_path: result.tokenPath,
51
+ }, `profile: ${agent}\nbound: true\nauth_key: ${auth.authKey}\ntoken: ${auth.tokenPath}\nxiantao_token: ${result.tokenPath}`);
52
+ }
53
+ }
@@ -0,0 +1,3 @@
1
+ import AuthBind from './auth/bind.js';
2
+ export default class Bind extends AuthBind {
3
+ }
@@ -1,4 +1,4 @@
1
- export const DEFAULT_AGENT = 'openclaw';
1
+ export const DEFAULT_AGENT = 'opencode';
2
2
  export const XTZ_CONFIG_PATH = '.config/xtz/config.json';
3
3
  export const AUTH_URL_ENDPOINT = 'https://api.helixlife.net/api/v1/user/skills/auth_url';
4
4
  export const AUTH_BIND_ENDPOINT = 'https://api.helixlife.net/api/v1/user/skills/bind_user';
@@ -135,11 +135,16 @@ export async function readXtzConfig() {
135
135
  }
136
136
  }
137
137
  export async function resolveAgent(agentFlag) {
138
- if (agentFlag) {
139
- return agentFlag;
138
+ const cliAgent = agentFlag?.trim();
139
+ if (cliAgent) {
140
+ return cliAgent;
141
+ }
142
+ const environmentAgent = resolveAgentFromEnvironment();
143
+ if (environmentAgent) {
144
+ return environmentAgent;
140
145
  }
141
146
  const config = await readXtzConfig();
142
- return config.agent ?? DEFAULT_AGENT;
147
+ return config.agent?.trim() || DEFAULT_AGENT;
143
148
  }
144
149
  export async function resolveXiantaoToolProductUuid(toolProductUuidFlag) {
145
150
  if (toolProductUuidFlag?.trim()) {
@@ -184,3 +189,32 @@ async function readJsonFile(filePath) {
184
189
  function isNodeError(error) {
185
190
  return error instanceof Error && 'code' in error;
186
191
  }
192
+ function resolveAgentFromEnvironment() {
193
+ const agentName = process.env.AGENT_NAME?.trim();
194
+ if (agentName) {
195
+ return agentName;
196
+ }
197
+ if (isCursorEnvironment()) {
198
+ return 'cursor';
199
+ }
200
+ if (isNamedEnvironment('OPENCODE')) {
201
+ return 'opencode';
202
+ }
203
+ if (isNamedEnvironment('OPENCLAW')) {
204
+ return 'openclaw';
205
+ }
206
+ return undefined;
207
+ }
208
+ function isCursorEnvironment() {
209
+ return Boolean(process.env.CURSOR_TRACE_ID?.trim()
210
+ || process.env.CURSOR_SESSION_ID?.trim()
211
+ || process.env.CURSOR_AGENT?.trim()
212
+ || process.env.VSCODE_GIT_ASKPASS_NODE?.includes('Cursor')
213
+ || process.env.TERM_PROGRAM?.toLowerCase() === 'cursor');
214
+ }
215
+ function isNamedEnvironment(name) {
216
+ return Boolean(process.env[name]?.trim()
217
+ || process.env[`${name}_AGENT`]?.trim()
218
+ || process.env[`${name}_SESSION_ID`]?.trim()
219
+ || process.env.TERM_PROGRAM?.toLowerCase() === name.toLowerCase());
220
+ }
package/dist/xtz-help.js CHANGED
@@ -6,7 +6,7 @@ export default class XtzHelp extends Help {
6
6
  this.log(this.section('MAIN FLOWS', [
7
7
  ['First use', '$ xt login\n$ xt tool search violin\n$ xt tool inspect violin_flat\n$ xt tool run violin_flat ./data.xlsx'],
8
8
  ['Interactive tool run', '$ xt tool run --interactive'],
9
- ['Agent automation', '$ xt tool:prepare violin_flat --file ./data.xlsx --profile openclaw --json\n$ xt tool exec violin_flat --file ./data.xlsx --profile openclaw --json'],
9
+ ['Agent automation', '$ xt tool:prepare violin_flat --file ./data.xlsx --profile opencode --json\n$ xt tool exec violin_flat --file ./data.xlsx --profile opencode --json'],
10
10
  ]));
11
11
  this.log('');
12
12
  this.log(this.section('COMMON COMMANDS', this.renderCommandItems([
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@helixlife-ai/xiantao",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "CLI for Xiantao bioinformatics tools",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",