@fastthree-ai/cli 0.0.0-stage → 0.2.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.
- package/dist/api.js +133 -0
- package/dist/api.js.map +1 -0
- package/dist/commands/account.js +15 -0
- package/dist/commands/account.js.map +1 -0
- package/dist/commands/chat.js +53 -0
- package/dist/commands/chat.js.map +1 -0
- package/dist/commands/config.js +104 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/draw.js +48 -0
- package/dist/commands/draw.js.map +1 -0
- package/dist/commands/models.js +62 -0
- package/dist/commands/models.js.map +1 -0
- package/dist/commands/task.js +11 -0
- package/dist/commands/task.js.map +1 -0
- package/dist/commands/upload.js +11 -0
- package/dist/commands/upload.js.map +1 -0
- package/dist/config.js +61 -0
- package/dist/config.js.map +1 -0
- package/dist/index.js +33 -0
- package/dist/index.js.map +1 -0
- package/package.json +36 -6
- package/README.md +0 -3
package/dist/api.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP 客户端 + SSE 流式解析。
|
|
3
|
+
*
|
|
4
|
+
* 后端响应包络(OpenApiController 系)是 CommonResult { code, message, data },
|
|
5
|
+
* HTTP 永远 200,业务错误用 code 字段表达 —— 所以这里把 code != 0/200 视为失败。
|
|
6
|
+
*
|
|
7
|
+
* LLM 的 /chat/completions 走 OpenAI 兼容格式,不走 CommonResult 包络,
|
|
8
|
+
* 故 streamChat 直接读 SSE data 行不做包络剥离。
|
|
9
|
+
*/
|
|
10
|
+
import { resolveBaseUrl, requireToken } from "./config.js";
|
|
11
|
+
export class ApiError extends Error {
|
|
12
|
+
code;
|
|
13
|
+
constructor(code, message) {
|
|
14
|
+
super(message);
|
|
15
|
+
this.code = code;
|
|
16
|
+
this.name = "ApiError";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
function buildUrl(path, baseUrlOverride) {
|
|
20
|
+
return `${resolveBaseUrl(baseUrlOverride)}${path}`;
|
|
21
|
+
}
|
|
22
|
+
function authHeaders(token) {
|
|
23
|
+
const t = token || requireToken();
|
|
24
|
+
return {
|
|
25
|
+
Authorization: `Bearer ${t}`,
|
|
26
|
+
"Content-Type": "application/json",
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/** 调用 GET/POST/PUT/DELETE,自动解 CommonResult 包络 */
|
|
30
|
+
export async function request(method, path, options = {}) {
|
|
31
|
+
const url = buildUrl(path, options.baseUrlOverride);
|
|
32
|
+
const headers = { ...authHeaders(options.token), ...(options.headers || {}) };
|
|
33
|
+
const init = { method, headers };
|
|
34
|
+
if (options.body !== undefined && method !== "GET" && method !== "HEAD") {
|
|
35
|
+
init.body = typeof options.body === "string" ? options.body : JSON.stringify(options.body);
|
|
36
|
+
}
|
|
37
|
+
const resp = await fetch(url, init);
|
|
38
|
+
const text = await resp.text();
|
|
39
|
+
let payload = null;
|
|
40
|
+
try {
|
|
41
|
+
payload = text ? JSON.parse(text) : null;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
throw new ApiError(resp.status, `响应解析失败: ${text.slice(0, 200)}`);
|
|
45
|
+
}
|
|
46
|
+
// 后端惯例:HTTP 永远 200,业务错误用 code 字段表示
|
|
47
|
+
if (payload && typeof payload.code === "number" && payload.code !== 0 && payload.code !== 200) {
|
|
48
|
+
throw new ApiError(payload.code, payload.message || `请求失败 (code=${payload.code})`);
|
|
49
|
+
}
|
|
50
|
+
if (!payload || payload.data === undefined) {
|
|
51
|
+
throw new ApiError(resp.status, `响应格式异常: ${text.slice(0, 200)}`);
|
|
52
|
+
}
|
|
53
|
+
return payload.data;
|
|
54
|
+
}
|
|
55
|
+
/** 开放 API 上传:multipart/form-data,file 字段;返回可直接填入出图参数的 URL */
|
|
56
|
+
export async function uploadFile(filePath, token, baseUrlOverride) {
|
|
57
|
+
const { readFile } = await import("node:fs/promises");
|
|
58
|
+
const { basename } = await import("node:path");
|
|
59
|
+
const buf = await readFile(filePath);
|
|
60
|
+
const filename = basename(filePath);
|
|
61
|
+
const form = new FormData();
|
|
62
|
+
form.append("file", new Blob([buf]), filename);
|
|
63
|
+
const url = buildUrl("/api/open/v1/upload", baseUrlOverride);
|
|
64
|
+
const resp = await fetch(url, {
|
|
65
|
+
method: "POST",
|
|
66
|
+
headers: { Authorization: `Bearer ${token || requireToken()}` },
|
|
67
|
+
body: form,
|
|
68
|
+
});
|
|
69
|
+
const text = await resp.text();
|
|
70
|
+
let payload = null;
|
|
71
|
+
try {
|
|
72
|
+
payload = text ? JSON.parse(text) : null;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
throw new ApiError(resp.status, `响应解析失败: ${text.slice(0, 200)}`);
|
|
76
|
+
}
|
|
77
|
+
if (!payload || (payload.code !== 0 && payload.code !== 200)) {
|
|
78
|
+
throw new ApiError(payload?.code ?? resp.status, payload?.message || "上传失败");
|
|
79
|
+
}
|
|
80
|
+
return payload.data;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* 流式调用 /chat/completions,逐 chunk 回调。
|
|
84
|
+
* 解析规则:SSE 事件以空行分隔,data: 行 JSON parse;[DONE] 结束。
|
|
85
|
+
*/
|
|
86
|
+
export async function streamChat(body, onChunk, signal) {
|
|
87
|
+
const url = buildUrl("/api/open/v1/chat/completions");
|
|
88
|
+
const resp = await fetch(url, {
|
|
89
|
+
method: "POST",
|
|
90
|
+
headers: { ...authHeaders(), Accept: "text/event-stream" },
|
|
91
|
+
body: JSON.stringify({ ...body, stream: true }),
|
|
92
|
+
signal,
|
|
93
|
+
});
|
|
94
|
+
const contentType = resp.headers.get("content-type") || "";
|
|
95
|
+
if (!contentType.includes("event-stream")) {
|
|
96
|
+
const text = await resp.text();
|
|
97
|
+
throw new ApiError(resp.status, `流式响应失败 (content-type=${contentType}): ${text.slice(0, 200)}`);
|
|
98
|
+
}
|
|
99
|
+
if (!resp.body)
|
|
100
|
+
throw new Error("响应没有 body 流");
|
|
101
|
+
const reader = resp.body.getReader();
|
|
102
|
+
const decoder = new TextDecoder("utf-8");
|
|
103
|
+
let buffer = "";
|
|
104
|
+
while (true) {
|
|
105
|
+
const { done, value } = await reader.read();
|
|
106
|
+
if (done)
|
|
107
|
+
break;
|
|
108
|
+
buffer += decoder.decode(value, { stream: true });
|
|
109
|
+
let sep;
|
|
110
|
+
while ((sep = buffer.indexOf("\n\n")) >= 0) {
|
|
111
|
+
const rawEvent = buffer.slice(0, sep);
|
|
112
|
+
buffer = buffer.slice(sep + 2);
|
|
113
|
+
const lines = rawEvent.split("\n");
|
|
114
|
+
let dataLine = "";
|
|
115
|
+
for (const line of lines) {
|
|
116
|
+
if (line.startsWith("data:")) {
|
|
117
|
+
dataLine += line.slice(5).trim();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
if (!dataLine)
|
|
121
|
+
continue;
|
|
122
|
+
if (dataLine === "[DONE]")
|
|
123
|
+
return;
|
|
124
|
+
try {
|
|
125
|
+
onChunk(JSON.parse(dataLine));
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
// 跳过无法解析的行
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=api.js.map
|
package/dist/api.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { request } from "../api.js";
|
|
2
|
+
export function registerAccountCommands(program) {
|
|
3
|
+
program
|
|
4
|
+
.command("account")
|
|
5
|
+
.description("查询账户算力/积分/会员档位")
|
|
6
|
+
.action(async () => {
|
|
7
|
+
const data = await request("GET", "/api/open/v1/account");
|
|
8
|
+
console.log(`用户ID: ${data.userId}`);
|
|
9
|
+
console.log(`用户名: ${data.username}`);
|
|
10
|
+
console.log(`积分: ${data.points}`);
|
|
11
|
+
console.log(`会员档位: ${data.membershipTier ?? "-"}`);
|
|
12
|
+
console.log(`等级: ${data.level ?? "-"}`);
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=account.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"account.js","sourceRoot":"","sources":["../../src/commands/account.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUpC,MAAM,UAAU,uBAAuB,CAAC,OAAgB;IACtD,OAAO;SACJ,OAAO,CAAC,SAAS,CAAC;SAClB,WAAW,CAAC,gBAAgB,CAAC;SAC7B,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,MAAM,IAAI,GAAG,MAAM,OAAO,CAAc,KAAK,EAAE,sBAAsB,CAAC,CAAC;QACvE,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QACtC,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QACvC,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;QACtC,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,cAAc,IAAI,GAAG,EAAE,CAAC,CAAC;QACnD,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,CAAC,KAAK,IAAI,GAAG,EAAE,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { streamChat } from "../api.js";
|
|
2
|
+
export function registerChatCommands(program) {
|
|
3
|
+
program
|
|
4
|
+
.command("chat <model>")
|
|
5
|
+
.description("与 LLM 模型聊天(流式输出到 stdout)")
|
|
6
|
+
.option("-m, --message <text>", "单条消息(不进交互模式)")
|
|
7
|
+
.option("--max-tokens <n>", "max_tokens", "1024")
|
|
8
|
+
.action(async (model, opts) => {
|
|
9
|
+
const maxTokens = Number(opts.maxTokens || "1024");
|
|
10
|
+
if (opts.message) {
|
|
11
|
+
const messages = [{ role: "user", content: opts.message }];
|
|
12
|
+
await streamChat({ model, messages, max_tokens: maxTokens }, (chunk) => {
|
|
13
|
+
const delta = chunk.choices?.[0]?.delta?.content;
|
|
14
|
+
if (delta)
|
|
15
|
+
process.stdout.write(delta);
|
|
16
|
+
});
|
|
17
|
+
process.stdout.write("\n");
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
// 交互模式
|
|
21
|
+
const { createInterface } = await import("node:readline");
|
|
22
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
23
|
+
const messages = [];
|
|
24
|
+
console.error(`已进入交互模式(model=${model})。输入 :q 退出。`);
|
|
25
|
+
try {
|
|
26
|
+
while (true) {
|
|
27
|
+
const line = await new Promise((resolve) => rl.question("> ", resolve));
|
|
28
|
+
const trimmed = line.trim();
|
|
29
|
+
if (!trimmed)
|
|
30
|
+
continue;
|
|
31
|
+
if (trimmed === ":q" || trimmed === ":quit")
|
|
32
|
+
break;
|
|
33
|
+
messages.push({ role: "user", content: trimmed });
|
|
34
|
+
// 拼接 delta 写回 messages 作为 assistant 回包,保证下一轮上下文完整
|
|
35
|
+
let assistant = "";
|
|
36
|
+
await streamChat({ model, messages, max_tokens: maxTokens }, (chunk) => {
|
|
37
|
+
const delta = chunk.choices?.[0]?.delta?.content;
|
|
38
|
+
if (delta) {
|
|
39
|
+
assistant += delta;
|
|
40
|
+
process.stdout.write(delta);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
process.stdout.write("\n");
|
|
44
|
+
if (assistant)
|
|
45
|
+
messages.push({ role: "assistant", content: assistant });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
rl.close();
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=chat.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chat.js","sourceRoot":"","sources":["../../src/commands/chat.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import * as readline from "node:readline/promises";
|
|
2
|
+
import { stdin as input, stdout as output } from "node:process";
|
|
3
|
+
import { loadConfig, saveConfig, resolveBaseUrl, requireToken } from "../config.js";
|
|
4
|
+
import { request, ApiError } from "../api.js";
|
|
5
|
+
export function registerConfigCommands(program) {
|
|
6
|
+
const cfg = program.command("config").description("管理本地配置(base_url / token)");
|
|
7
|
+
cfg.command("set-token <token>")
|
|
8
|
+
.description("保存 API Token(Authorization: Bearer 的凭据)")
|
|
9
|
+
.action((token) => {
|
|
10
|
+
const cur = loadConfig();
|
|
11
|
+
saveConfig({ ...cur, token });
|
|
12
|
+
console.log("✓ Token 已保存到 ~/.ft/config.json");
|
|
13
|
+
});
|
|
14
|
+
cfg.command("set-base-url <url>")
|
|
15
|
+
.description("设置后端 base URL(默认 https://ai-api.fastthree.com)")
|
|
16
|
+
.action((url) => {
|
|
17
|
+
const cur = loadConfig();
|
|
18
|
+
saveConfig({ ...cur, baseUrl: url });
|
|
19
|
+
console.log(`✓ baseUrl 已更新为 ${url}`);
|
|
20
|
+
});
|
|
21
|
+
cfg.command("show")
|
|
22
|
+
.description("查看当前配置(token 脱敏显示)")
|
|
23
|
+
.action(() => {
|
|
24
|
+
const cur = loadConfig();
|
|
25
|
+
const masked = cur.token
|
|
26
|
+
? `${cur.token.slice(0, 6)}***${cur.token.slice(-4)}`
|
|
27
|
+
: "(未设置)";
|
|
28
|
+
console.log(`baseUrl: ${cur.baseUrl}`);
|
|
29
|
+
console.log(`token: ${masked}`);
|
|
30
|
+
});
|
|
31
|
+
cfg.command("verify")
|
|
32
|
+
.description("调 /account 健康检查,验证 token 与连通性")
|
|
33
|
+
.action(async () => {
|
|
34
|
+
await verifyAndPrint();
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* ft configure — 交互式配置入口。
|
|
39
|
+
*
|
|
40
|
+
* 不带参数:提示输入 token + base-url,保存。
|
|
41
|
+
* --verify:保存后调 /account 验证(也可单独对已存配置运行 ft configure --verify)。
|
|
42
|
+
*/
|
|
43
|
+
export function registerConfigureCommand(program) {
|
|
44
|
+
program
|
|
45
|
+
.command("configure")
|
|
46
|
+
.description("交互式配置 token 与 base-url(--verify 同时做健康检查)")
|
|
47
|
+
.option("--verify", "保存后(或对已存配置)调 /account 验证")
|
|
48
|
+
.action(async (opts) => {
|
|
49
|
+
const cur = loadConfig();
|
|
50
|
+
let next = cur;
|
|
51
|
+
// 仅当 stdin 是 TTY 时走交互;非 TTY(如管道/CI)跳过提示,直接对已存配置做 --verify
|
|
52
|
+
if (process.stdin.isTTY) {
|
|
53
|
+
const rl = readline.createInterface({ input, output });
|
|
54
|
+
try {
|
|
55
|
+
const tokenInput = await rl.question(`API Token [${maskToken(cur.token)}]: `);
|
|
56
|
+
const baseUrlInput = await rl.question(`base URL [${cur.baseUrl}]: `);
|
|
57
|
+
next = {
|
|
58
|
+
token: tokenInput.trim() || cur.token,
|
|
59
|
+
baseUrl: baseUrlInput.trim() || cur.baseUrl,
|
|
60
|
+
};
|
|
61
|
+
if (next.token !== cur.token || next.baseUrl !== cur.baseUrl) {
|
|
62
|
+
saveConfig(next);
|
|
63
|
+
console.log("✓ 配置已保存到 ~/.ft/config.json");
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
console.log("(配置未变更)");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
rl.close();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (opts.verify) {
|
|
74
|
+
await verifyAndPrint();
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
/** 调 /account 健康检查,成功打印账户信息,失败打印错误并 exit 1。 */
|
|
79
|
+
async function verifyAndPrint() {
|
|
80
|
+
try {
|
|
81
|
+
requireToken();
|
|
82
|
+
const data = await request("GET", "/api/open/v1/account");
|
|
83
|
+
console.log("✓ 验证通过");
|
|
84
|
+
console.log(JSON.stringify(data, null, 2));
|
|
85
|
+
}
|
|
86
|
+
catch (e) {
|
|
87
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
88
|
+
if (e instanceof ApiError) {
|
|
89
|
+
console.error(`✗ 验证失败 (code=${e.code}): ${msg}`);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
console.error(`✗ 验证失败: ${msg}`);
|
|
93
|
+
}
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function maskToken(token) {
|
|
98
|
+
if (!token)
|
|
99
|
+
return "(未设置)";
|
|
100
|
+
return `${token.slice(0, 6)}***${token.slice(-4)}`;
|
|
101
|
+
}
|
|
102
|
+
// 兼容旧调用方:resolveBaseUrl 仍可从这里 re-export
|
|
103
|
+
export { resolveBaseUrl };
|
|
104
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,QAAQ,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,KAAK,IAAI,KAAK,EAAE,MAAM,IAAI,MAAM,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACpF,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE9C,MAAM,UAAU,sBAAsB,CAAC,OAAgB;IACrD,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,CAAC,0BAA0B,CAAC,CAAC;IAE9E,GAAG,CAAC,OAAO,CAAC,mBAAmB,CAAC;SAC7B,WAAW,CAAC,yCAAyC,CAAC;SACtD,MAAM,CAAC,CAAC,KAAa,EAAE,EAAE;QACxB,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;QACzB,UAAU,CAAC,EAAE,GAAG,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,gCAAgC,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEL,GAAG,CAAC,OAAO,CAAC,oBAAoB,CAAC;SAC9B,WAAW,CAAC,gDAAgD,CAAC;SAC7D,MAAM,CAAC,CAAC,GAAW,EAAE,EAAE;QACtB,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;QACzB,UAAU,CAAC,EAAE,GAAG,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC;QACrC,OAAO,CAAC,GAAG,CAAC,kBAAkB,GAAG,EAAE,CAAC,CAAC;IACvC,CAAC,CAAC,CAAC;IAEL,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC;SAChB,WAAW,CAAC,oBAAoB,CAAC;SACjC,MAAM,CAAC,GAAG,EAAE;QACX,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;QACzB,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK;YACtB,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE;YACrD,CAAC,CAAC,OAAO,CAAC;QACZ,OAAO,CAAC,GAAG,CAAC,YAAY,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACvC,OAAO,CAAC,GAAG,CAAC,YAAY,MAAM,EAAE,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEL,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC;SAClB,WAAW,CAAC,+BAA+B,CAAC;SAC5C,MAAM,CAAC,KAAK,IAAI,EAAE;QACjB,MAAM,cAAc,EAAE,CAAC;IACzB,CAAC,CAAC,CAAC;AACP,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,wBAAwB,CAAC,OAAgB;IACvD,OAAO;SACJ,OAAO,CAAC,WAAW,CAAC;SACpB,WAAW,CAAC,0CAA0C,CAAC;SACvD,MAAM,CAAC,UAAU,EAAE,0BAA0B,CAAC;SAC9C,MAAM,CAAC,KAAK,EAAE,IAA0B,EAAE,EAAE;QAC3C,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;QACzB,IAAI,IAAI,GAAG,GAAG,CAAC;QACf,0DAA0D;QAC1D,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;YACxB,MAAM,EAAE,GAAG,QAAQ,CAAC,eAAe,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;YACvD,IAAI,CAAC;gBACH,MAAM,UAAU,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,cAAc,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;gBAC9E,MAAM,YAAY,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,aAAa,GAAG,CAAC,OAAO,KAAK,CAAC,CAAC;gBACtE,IAAI,GAAG;oBACL,KAAK,EAAE,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,CAAC,KAAK;oBACrC,OAAO,EAAE,YAAY,CAAC,IAAI,EAAE,IAAI,GAAG,CAAC,OAAO;iBAC5C,CAAC;gBACF,IAAI,IAAI,CAAC,KAAK,KAAK,GAAG,CAAC,KAAK,IAAI,IAAI,CAAC,OAAO,KAAK,GAAG,CAAC,OAAO,EAAE,CAAC;oBAC7D,UAAU,CAAC,IAAI,CAAC,CAAC;oBACjB,OAAO,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;gBAC5C,CAAC;qBAAM,CAAC;oBACN,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;gBACzB,CAAC;YACH,CAAC;oBAAS,CAAC;gBACT,EAAE,CAAC,KAAK,EAAE,CAAC;YACb,CAAC;QACH,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,MAAM,cAAc,EAAE,CAAC;QACzB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC;AAED,+CAA+C;AAC/C,KAAK,UAAU,cAAc;IAC3B,IAAI,CAAC;QACH,YAAY,EAAE,CAAC;QACf,MAAM,IAAI,GAAG,MAAM,OAAO,CAA0B,KAAK,EAAE,sBAAsB,CAAC,CAAC;QACnF,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QACtB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC7C,CAAC;IAAC,OAAO,CAAU,EAAE,CAAC;QACpB,MAAM,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACvD,IAAI,CAAC,YAAY,QAAQ,EAAE,CAAC;YAC1B,OAAO,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC,IAAI,MAAM,GAAG,EAAE,CAAC,CAAC;QACnD,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,KAAK,CAAC,WAAW,GAAG,EAAE,CAAC,CAAC;QAClC,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAC,KAAa;IAC9B,IAAI,CAAC,KAAK;QAAE,OAAO,OAAO,CAAC;IAC3B,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AACrD,CAAC;AAED,wCAAwC;AACxC,OAAO,EAAE,cAAc,EAAE,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { request } from "../api.js";
|
|
2
|
+
function parseKV(list) {
|
|
3
|
+
const out = {};
|
|
4
|
+
for (const kv of list) {
|
|
5
|
+
const i = kv.indexOf("=");
|
|
6
|
+
if (i < 0)
|
|
7
|
+
continue;
|
|
8
|
+
const k = kv.slice(0, i).trim();
|
|
9
|
+
let v = kv.slice(i + 1).trim();
|
|
10
|
+
if (v === "true")
|
|
11
|
+
v = true;
|
|
12
|
+
else if (v === "false")
|
|
13
|
+
v = false;
|
|
14
|
+
else if (/^-?\d+(\.\d+)?$/.test(v))
|
|
15
|
+
v = Number(v);
|
|
16
|
+
else {
|
|
17
|
+
try {
|
|
18
|
+
v = JSON.parse(v);
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
// 保持字符串
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
out[k] = v;
|
|
25
|
+
}
|
|
26
|
+
return out;
|
|
27
|
+
}
|
|
28
|
+
export function registerDrawCommands(program) {
|
|
29
|
+
program
|
|
30
|
+
.command("draw <apiKey>")
|
|
31
|
+
.description("提交出图任务。apiKey 见 ft models draw;模型参数用 key=value 形式传入")
|
|
32
|
+
.option("-p, --param <kv...>", "模型参数,可重复(如 -p prompt=cat -p image=http://...)")
|
|
33
|
+
.option("--output-tier <tier>", "出图档位(fast/plus/ultra/max,可选)")
|
|
34
|
+
.option("--callback-url <url>", "回调 URL(可选,需同时提供 --callback-secret)")
|
|
35
|
+
.option("--callback-secret <secret>", "回调密钥(16~256 字符)")
|
|
36
|
+
.action(async (apiKey, opts) => {
|
|
37
|
+
const body = { ...parseKV(opts.param || []) };
|
|
38
|
+
if (opts.outputTier)
|
|
39
|
+
body.output_tier = opts.outputTier;
|
|
40
|
+
if (opts.callbackUrl)
|
|
41
|
+
body.callback_url = opts.callbackUrl;
|
|
42
|
+
if (opts.callbackSecret)
|
|
43
|
+
body.callback_secret = opts.callbackSecret;
|
|
44
|
+
const data = await request("POST", `/api/open/v1/tasks/${encodeURIComponent(apiKey)}`, { body });
|
|
45
|
+
console.log(JSON.stringify(data, null, 2));
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=draw.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"draw.js","sourceRoot":"","sources":["../../src/commands/draw.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAOpC,SAAS,OAAO,CAAC,IAAc;IAC7B,MAAM,GAAG,GAA4B,EAAE,CAAC;IACxC,KAAK,MAAM,EAAE,IAAI,IAAI,EAAE,CAAC;QACtB,MAAM,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC1B,IAAI,CAAC,GAAG,CAAC;YAAE,SAAS;QACpB,MAAM,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAChC,IAAI,CAAC,GAAY,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QACxC,IAAI,CAAC,KAAK,MAAM;YAAE,CAAC,GAAG,IAAI,CAAC;aACtB,IAAI,CAAC,KAAK,OAAO;YAAE,CAAC,GAAG,KAAK,CAAC;aAC7B,IAAI,iBAAiB,CAAC,IAAI,CAAC,CAAW,CAAC;YAAE,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;aACvD,CAAC;YACJ,IAAI,CAAC;gBACH,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAW,CAAC,CAAC;YAC9B,CAAC;YAAC,MAAM,CAAC;gBACP,QAAQ;YACV,CAAC;QACH,CAAC;QACD,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IACb,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,OAAgB;IACnD,OAAO;SACJ,OAAO,CAAC,eAAe,CAAC;SACxB,WAAW,CAAC,qDAAqD,CAAC;SAClE,MAAM,CAAC,qBAAqB,EAAE,+CAA+C,CAAC;SAC9E,MAAM,CAAC,sBAAsB,EAAE,8BAA8B,CAAC;SAC9D,MAAM,CAAC,sBAAsB,EAAE,oCAAoC,CAAC;SACpE,MAAM,CAAC,4BAA4B,EAAE,iBAAiB,CAAC;SACvD,MAAM,CACL,KAAK,EACH,MAAc,EACd,IAKC,EACD,EAAE;QACF,MAAM,IAAI,GAA4B,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,CAAC;QACvE,IAAI,IAAI,CAAC,UAAU;YAAE,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,UAAU,CAAC;QACxD,IAAI,IAAI,CAAC,WAAW;YAAE,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,WAAW,CAAC;QAC3D,IAAI,IAAI,CAAC,cAAc;YAAE,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,cAAc,CAAC;QACpE,MAAM,IAAI,GAAG,MAAM,OAAO,CACxB,MAAM,EACN,sBAAsB,kBAAkB,CAAC,MAAM,CAAC,EAAE,EAClD,EAAE,IAAI,EAAE,CACT,CAAC;QACF,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC7C,CAAC,CACF,CAAC;AACN,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { request } from "../api.js";
|
|
2
|
+
/**
|
|
3
|
+
* 与 OpenApiController.ALLOWED_OPEN_API_KEYS 保持一致。
|
|
4
|
+
* 后端白名单变化时这里也要同步——它是「入口白名单」,前端手册 docsModel.ts
|
|
5
|
+
* 的 PUBLIC_API_KEYS 是「手册白名单」,二者口径不同,本表只覆盖允许直接点名的 key。
|
|
6
|
+
*/
|
|
7
|
+
const DRAW_MODELS = [
|
|
8
|
+
{ key: "matting", desc: "抠图" },
|
|
9
|
+
{ key: "extract", desc: "图案提取(主档)" },
|
|
10
|
+
{ key: "extract-fast", desc: "图案提取(快速档)" },
|
|
11
|
+
{ key: "extract-ultra", desc: "图案提取(超清档)" },
|
|
12
|
+
{ key: "image-edit", desc: "图像编辑(主档)" },
|
|
13
|
+
{ key: "image-edit-fast", desc: "图像编辑(快速档)" },
|
|
14
|
+
{ key: "image-edit-ultra", desc: "图像编辑(超清档)" },
|
|
15
|
+
{ key: "image-edit-2.5", desc: "图像编辑 2.5(主档)" },
|
|
16
|
+
{ key: "image-edit-2.5-fast", desc: "图像编辑 2.5(快速档)" },
|
|
17
|
+
{ key: "image-edit-2.5-plus", desc: "图像编辑 2.5(增强档)" },
|
|
18
|
+
{ key: "image-edit-2.5-ultra", desc: "图像编辑 2.5(超清档)" },
|
|
19
|
+
{ key: "image-edit-2.5-max", desc: "图像编辑 2.5(顶配档)" },
|
|
20
|
+
{ key: "seamless-tile", desc: "无缝拼贴" },
|
|
21
|
+
{ key: "seamless-tile-fast", desc: "无缝拼贴(快速档)" },
|
|
22
|
+
{ key: "pattern-extract-rh", desc: "图案提取 RH" },
|
|
23
|
+
{ key: "upscale", desc: "超分辨率放大" },
|
|
24
|
+
{ key: "image-expand", desc: "扩图" },
|
|
25
|
+
{ key: "nano banana 2", desc: "Nano Banana 2" },
|
|
26
|
+
{ key: "nano banana pro", desc: "Nano Banana Pro" },
|
|
27
|
+
{ key: "nano-banana-lite", desc: "Nano Banana Lite" },
|
|
28
|
+
{ key: "turbo", desc: "Turbo" },
|
|
29
|
+
{ key: "cut-pieces", desc: "裁片提取(按片计费)" },
|
|
30
|
+
];
|
|
31
|
+
export function registerModelsCommands(program) {
|
|
32
|
+
const m = program.command("models").description("查询模型列表");
|
|
33
|
+
// 不带子命令时直接显示 help:commander 默认对"有子命令但未命中"的调用静默退出,
|
|
34
|
+
// 用户会以为命令坏了。显式给出子命令引导。
|
|
35
|
+
m.action(() => {
|
|
36
|
+
console.log("用法: ft models draw 列出可调用的出图模型 api_key");
|
|
37
|
+
console.log(" ft models llm 列出可用的 LLM 模型\n");
|
|
38
|
+
m.help({ error: false });
|
|
39
|
+
});
|
|
40
|
+
m.command("draw")
|
|
41
|
+
.description("列出可调用的出图模型 api_key(作为 ft draw <apiKey> 的参数)")
|
|
42
|
+
.action(() => {
|
|
43
|
+
console.log("出图模型 api_key(用于 ft draw <apiKey>):\n");
|
|
44
|
+
for (const it of DRAW_MODELS) {
|
|
45
|
+
console.log(` ${it.key.padEnd(22)} ${it.desc}`);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
m.command("llm")
|
|
49
|
+
.description("查询 LLM 模型(GET /api/open/v1/models)")
|
|
50
|
+
.action(async () => {
|
|
51
|
+
const data = await request("GET", "/api/open/v1/models");
|
|
52
|
+
if (!data?.data?.length) {
|
|
53
|
+
console.log("(无可用 LLM 模型)");
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
console.log("LLM 模型:\n");
|
|
57
|
+
for (const it of data.data) {
|
|
58
|
+
console.log(` ${it.id}`);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=models.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"models.js","sourceRoot":"","sources":["../../src/commands/models.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { request } from "../api.js";
|
|
2
|
+
export function registerTaskCommands(program) {
|
|
3
|
+
program
|
|
4
|
+
.command("task <id>")
|
|
5
|
+
.description("按任务 ID 查询状态与结果(GET /api/open/v1/tasks/{id})")
|
|
6
|
+
.action(async (id) => {
|
|
7
|
+
const data = await request("GET", `/api/open/v1/tasks/${encodeURIComponent(id)}`);
|
|
8
|
+
console.log(JSON.stringify(data, null, 2));
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=task.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task.js","sourceRoot":"","sources":["../../src/commands/task.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAUpC,MAAM,UAAU,oBAAoB,CAAC,OAAgB;IACnD,OAAO;SACJ,OAAO,CAAC,WAAW,CAAC;SACpB,WAAW,CAAC,6CAA6C,CAAC;SAC1D,MAAM,CAAC,KAAK,EAAE,EAAU,EAAE,EAAE;QAC3B,MAAM,IAAI,GAAG,MAAM,OAAO,CACxB,KAAK,EACL,sBAAsB,kBAAkB,CAAC,EAAE,CAAC,EAAE,CAC/C,CAAC;QACF,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;AACP,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { uploadFile } from "../api.js";
|
|
2
|
+
export function registerUploadCommands(program) {
|
|
3
|
+
program
|
|
4
|
+
.command("upload <file>")
|
|
5
|
+
.description("上传本地图片到开放 API,返回可直接填入出图参数的 URL")
|
|
6
|
+
.action(async (file) => {
|
|
7
|
+
const url = await uploadFile(file);
|
|
8
|
+
console.log(url);
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=upload.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"upload.js","sourceRoot":"","sources":["../../src/commands/upload.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAEvC,MAAM,UAAU,sBAAsB,CAAC,OAAgB;IACrD,OAAO;SACJ,OAAO,CAAC,eAAe,CAAC;SACxB,WAAW,CAAC,gCAAgC,CAAC;SAC7C,MAAM,CAAC,KAAK,EAAE,IAAY,EAAE,EAAE;QAC7B,MAAM,GAAG,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC;QACnC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC,CAAC,CAAC;AACP,CAAC"}
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 本地配置读写:base_url + token,落在 ~/.ft/config.json。
|
|
3
|
+
*
|
|
4
|
+
* 文件权限 0o600 — token 是凭据,不该让本机其他用户读到。
|
|
5
|
+
*
|
|
6
|
+
* 环境变量优先级高于配置文件,方便 CI/容器场景一次性注入:
|
|
7
|
+
* FT_API_TOKEN — 覆盖 config.token
|
|
8
|
+
* FT_API_BASE_URL — 覆盖 config.baseUrl(与 ft-mcp 同名环境变量对齐)
|
|
9
|
+
*/
|
|
10
|
+
import { homedir } from "node:os";
|
|
11
|
+
import { join } from "node:path";
|
|
12
|
+
import { mkdirSync, readFileSync, writeFileSync, existsSync } from "node:fs";
|
|
13
|
+
const CONFIG_DIR = join(homedir(), ".ft");
|
|
14
|
+
const CONFIG_FILE = join(CONFIG_DIR, "config.json");
|
|
15
|
+
/** 与 Nginx conf.d/fastthree.conf 里的对外 API 域名一致 */
|
|
16
|
+
const DEFAULT_BASE_URL = "https://ai-api.fastthree.com";
|
|
17
|
+
export function loadConfig() {
|
|
18
|
+
// 环境变量优先于配置文件
|
|
19
|
+
const envToken = process.env.FT_API_TOKEN;
|
|
20
|
+
const envBaseUrl = process.env.FT_API_BASE_URL;
|
|
21
|
+
if (!existsSync(CONFIG_FILE)) {
|
|
22
|
+
return {
|
|
23
|
+
baseUrl: envBaseUrl || DEFAULT_BASE_URL,
|
|
24
|
+
token: envToken || "",
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
const raw = readFileSync(CONFIG_FILE, "utf8");
|
|
29
|
+
const parsed = JSON.parse(raw);
|
|
30
|
+
return {
|
|
31
|
+
baseUrl: envBaseUrl || parsed.baseUrl || DEFAULT_BASE_URL,
|
|
32
|
+
token: envToken || parsed.token || "",
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return {
|
|
37
|
+
baseUrl: envBaseUrl || DEFAULT_BASE_URL,
|
|
38
|
+
token: envToken || "",
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export function saveConfig(cfg) {
|
|
43
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
44
|
+
writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2), { mode: 0o600 });
|
|
45
|
+
}
|
|
46
|
+
/** 调用任何需要鉴权的端点前都先取 token;没配就报错并指引用户去 set-token */
|
|
47
|
+
export function requireToken() {
|
|
48
|
+
const { token } = loadConfig();
|
|
49
|
+
if (!token) {
|
|
50
|
+
throw new Error("未配置 API Token。可通过以下方式之一配置:\n" +
|
|
51
|
+
" 1. 环境变量: export FT_API_TOKEN=<token>\n" +
|
|
52
|
+
" 2. 配置文件: ft config set-token <token>");
|
|
53
|
+
}
|
|
54
|
+
return token;
|
|
55
|
+
}
|
|
56
|
+
export function resolveBaseUrl(override) {
|
|
57
|
+
const envBaseUrl = process.env.FT_API_BASE_URL;
|
|
58
|
+
const { baseUrl } = loadConfig();
|
|
59
|
+
return (override || envBaseUrl || baseUrl || DEFAULT_BASE_URL).replace(/\/$/, "");
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAO7E,MAAM,UAAU,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,KAAK,CAAC,CAAC;AAC1C,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;AAEpD,kDAAkD;AAClD,MAAM,gBAAgB,GAAG,8BAA8B,CAAC;AAExD,MAAM,UAAU,UAAU;IACxB,cAAc;IACd,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;IAC1C,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IAC/C,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC7B,OAAO;YACL,OAAO,EAAE,UAAU,IAAI,gBAAgB;YACvC,KAAK,EAAE,QAAQ,IAAI,EAAE;SACtB,CAAC;IACJ,CAAC;IACD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;QAC9C,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAsB,CAAC;QACpD,OAAO;YACL,OAAO,EAAE,UAAU,IAAI,MAAM,CAAC,OAAO,IAAI,gBAAgB;YACzD,KAAK,EAAE,QAAQ,IAAI,MAAM,CAAC,KAAK,IAAI,EAAE;SACtC,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,OAAO;YACL,OAAO,EAAE,UAAU,IAAI,gBAAgB;YACvC,KAAK,EAAE,QAAQ,IAAI,EAAE;SACtB,CAAC;IACJ,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,GAAa;IACtC,SAAS,CAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3C,aAAa,CAAC,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AAC5E,CAAC;AAED,kDAAkD;AAClD,MAAM,UAAU,YAAY;IAC1B,MAAM,EAAE,KAAK,EAAE,GAAG,UAAU,EAAE,CAAC;IAC/B,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,KAAK,CACb,8BAA8B;YAC5B,0CAA0C;YAC1C,wCAAwC,CAC3C,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,QAAiB;IAC9C,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IAC/C,MAAM,EAAE,OAAO,EAAE,GAAG,UAAU,EAAE,CAAC;IACjC,OAAO,CAAC,QAAQ,IAAI,UAAU,IAAI,OAAO,IAAI,gBAAgB,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;AACpF,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* ft — FastThree 命令行工具入口
|
|
4
|
+
*
|
|
5
|
+
* 封装 /api/open/v1 开放 REST API:配置 token、查询账户、列模型、
|
|
6
|
+
* 上传图片、提交出图任务、查询任务、与 LLM 聊天(流式)。
|
|
7
|
+
*/
|
|
8
|
+
import { program } from "commander";
|
|
9
|
+
import { registerConfigCommands, registerConfigureCommand } from "./commands/config.js";
|
|
10
|
+
import { registerAccountCommands } from "./commands/account.js";
|
|
11
|
+
import { registerModelsCommands } from "./commands/models.js";
|
|
12
|
+
import { registerUploadCommands } from "./commands/upload.js";
|
|
13
|
+
import { registerDrawCommands } from "./commands/draw.js";
|
|
14
|
+
import { registerTaskCommands } from "./commands/task.js";
|
|
15
|
+
import { registerChatCommands } from "./commands/chat.js";
|
|
16
|
+
program
|
|
17
|
+
.name("ft")
|
|
18
|
+
.description("FastThree 命令行工具 — 封装 /api/open/v1 开放 REST API")
|
|
19
|
+
.version("0.2.0");
|
|
20
|
+
registerConfigureCommand(program);
|
|
21
|
+
registerConfigCommands(program);
|
|
22
|
+
registerAccountCommands(program);
|
|
23
|
+
registerModelsCommands(program);
|
|
24
|
+
registerUploadCommands(program);
|
|
25
|
+
registerDrawCommands(program);
|
|
26
|
+
registerTaskCommands(program);
|
|
27
|
+
registerChatCommands(program);
|
|
28
|
+
program.parseAsync(process.argv).catch((err) => {
|
|
29
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
30
|
+
console.error(msg);
|
|
31
|
+
process.exit(1);
|
|
32
|
+
});
|
|
33
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;GAKG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,sBAAsB,EAAE,wBAAwB,EAAE,MAAM,sBAAsB,CAAC;AACxF,OAAO,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAC9D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAE1D,OAAO;KACJ,IAAI,CAAC,IAAI,CAAC;KACV,WAAW,CAAC,+CAA+C,CAAC;KAC5D,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,wBAAwB,CAAC,OAAO,CAAC,CAAC;AAClC,sBAAsB,CAAC,OAAO,CAAC,CAAC;AAChC,uBAAuB,CAAC,OAAO,CAAC,CAAC;AACjC,sBAAsB,CAAC,OAAO,CAAC,CAAC;AAChC,sBAAsB,CAAC,OAAO,CAAC,CAAC;AAChC,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAC9B,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAC9B,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAE9B,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAY,EAAE,EAAE;IACtD,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAC7D,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,36 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@fastthree-ai/cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "@fastthree-ai/cli",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "FastThree 命令行工具,封装 /api/open/v1 开放 REST API",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"ft": "dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "tsx watch src/index.ts",
|
|
13
|
+
"start": "node dist/index.js",
|
|
14
|
+
"clean": "rimraf dist",
|
|
15
|
+
"prepublishOnly": "npm run clean && npm run build"
|
|
16
|
+
},
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=18"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"README.md"
|
|
23
|
+
],
|
|
24
|
+
"publishConfig": {
|
|
25
|
+
"access": "public"
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"commander": "^12.1.0"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/node": "^20.11.0",
|
|
32
|
+
"rimraf": "^5.0.5",
|
|
33
|
+
"tsx": "^4.7.0",
|
|
34
|
+
"typescript": "^5.3.0"
|
|
35
|
+
}
|
|
36
|
+
}
|
package/README.md
DELETED