@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 +2 -2
- package/dist/commands/auth/bind.js +53 -0
- package/dist/commands/bind.js +3 -0
- package/dist/lib/constants.js +1 -1
- package/dist/lib/storage.js +37 -3
- package/dist/xtz-help.js +1 -1
- package/package.json +1 -1
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,
|
|
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
|
|
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
|
+
}
|
package/dist/lib/constants.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const DEFAULT_AGENT = '
|
|
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';
|
package/dist/lib/storage.js
CHANGED
|
@@ -135,11 +135,16 @@ export async function readXtzConfig() {
|
|
|
135
135
|
}
|
|
136
136
|
}
|
|
137
137
|
export async function resolveAgent(agentFlag) {
|
|
138
|
-
|
|
139
|
-
|
|
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
|
|
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
|
|
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([
|