@guangnao/agent-cli 1.1.4 → 1.1.6
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 +3 -1
- package/dist/cli.js +123 -21
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,9 +26,11 @@ npm i -g @guangnao/agent-cli # 全局安装,得到 gcli 命令(别名 a
|
|
|
26
26
|
|
|
27
27
|
```sh
|
|
28
28
|
gcli
|
|
29
|
-
# → 选服务商(Guangnao / DeepSeek / OpenAI / Anthropic / Gemini)→ 贴 Key → 自动列模型并选 → 进入交互
|
|
29
|
+
# → 选服务商(Guangnao / DeepSeek / OpenAI / Anthropic / Gemini / Custom)→ 贴 Key → 自动列模型并选 → 进入交互
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
+
> 选 **Custom** 可接任意自建端点:先选协议(OpenAI 兼容 / Anthropic 兼容)→ 填 baseURL + Key + 模型即可。
|
|
33
|
+
|
|
32
34
|
配置存到 `~/.agentos/config.json`,下次直接进,无需重填。
|
|
33
35
|
|
|
34
36
|
**方式二:环境变量** —— 适合脚本 / CI / 固定配置(**环境变量优先于已保存配置**):
|
package/dist/cli.js
CHANGED
|
@@ -2950,27 +2950,44 @@ function parseImageUrl(url) {
|
|
|
2950
2950
|
var isDataImage = (r) => "base64" in r;
|
|
2951
2951
|
|
|
2952
2952
|
// src/llm/anthropic.ts
|
|
2953
|
+
var noTemperature = /* @__PURE__ */ new Set();
|
|
2954
|
+
var TEMP_DEPRECATED = /temperature.{0,40}deprecated|deprecated.{0,40}temperature/i;
|
|
2953
2955
|
var anthropic = (cfg2) => {
|
|
2954
2956
|
const base2 = (cfg2.baseUrl ?? "https://api.anthropic.com/v1").replace(/\/$/, "");
|
|
2955
2957
|
const headers = { "content-type": "application/json", "x-api-key": cfg2.apiKey, "anthropic-version": "2023-06-01" };
|
|
2956
2958
|
return async (req) => {
|
|
2957
2959
|
const system = req.messages.filter((m) => m.role === "system").map((m) => m.content).join("\n");
|
|
2958
|
-
const
|
|
2960
|
+
const mkBody = (withTemp) => ({
|
|
2959
2961
|
model: cfg2.model,
|
|
2960
2962
|
max_tokens: req.maxTokens ?? 4096,
|
|
2961
2963
|
// Anthropic requires this
|
|
2962
|
-
temperature: cfg2.temperature ?? 0,
|
|
2964
|
+
...withTemp ? { temperature: cfg2.temperature ?? 0 } : {},
|
|
2965
|
+
// newest models deprecated it → omit (see below)
|
|
2963
2966
|
...system ? { system } : {},
|
|
2964
2967
|
messages: toMsgs(req.messages),
|
|
2965
2968
|
...req.tools?.length ? { tools: req.tools.map(toTool2) } : {}
|
|
2969
|
+
});
|
|
2970
|
+
const run2 = (withTemp) => {
|
|
2971
|
+
const body = mkBody(withTemp);
|
|
2972
|
+
if (req.onDelta) return stream2(base2, headers, body, req.onDelta, req.signal);
|
|
2973
|
+
return blocking(base2, headers, body, req.signal);
|
|
2966
2974
|
};
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2975
|
+
try {
|
|
2976
|
+
return await run2(!noTemperature.has(cfg2.model));
|
|
2977
|
+
} catch (e) {
|
|
2978
|
+
if (!noTemperature.has(cfg2.model) && TEMP_DEPRECATED.test(String(e.message))) {
|
|
2979
|
+
noTemperature.add(cfg2.model);
|
|
2980
|
+
return await run2(false);
|
|
2981
|
+
}
|
|
2982
|
+
throw e;
|
|
2983
|
+
}
|
|
2972
2984
|
};
|
|
2973
2985
|
};
|
|
2986
|
+
async function blocking(base2, headers, body, signal) {
|
|
2987
|
+
const res = await fetch(`${base2}/messages`, { method: "POST", headers, body: JSON.stringify(body), signal });
|
|
2988
|
+
if (!res.ok) throw new Error(`llm ${res.status}: ${(await res.text()).slice(0, 300)}`);
|
|
2989
|
+
return fromReply(await res.json());
|
|
2990
|
+
}
|
|
2974
2991
|
function fromReply(data) {
|
|
2975
2992
|
const blocks = data.content ?? [];
|
|
2976
2993
|
const text2 = blocks.filter((b) => b.type === "text").map((b) => b.text).join("") || void 0;
|
|
@@ -3175,6 +3192,22 @@ var PROVIDER_LABEL = {
|
|
|
3175
3192
|
google: "Gemini"
|
|
3176
3193
|
};
|
|
3177
3194
|
var labelOf = (provider2) => PROVIDER_LABEL[provider2.toLowerCase()] ?? provider2;
|
|
3195
|
+
var VENDOR_PREFIX = [
|
|
3196
|
+
["claude", "Anthropic"],
|
|
3197
|
+
["gpt", "OpenAI"],
|
|
3198
|
+
["codex", "OpenAI"],
|
|
3199
|
+
["chatgpt", "OpenAI"],
|
|
3200
|
+
["o1", "OpenAI"],
|
|
3201
|
+
["o3", "OpenAI"],
|
|
3202
|
+
["o4", "OpenAI"],
|
|
3203
|
+
["gemini", "Gemini"],
|
|
3204
|
+
["deepseek", "DeepSeek"]
|
|
3205
|
+
];
|
|
3206
|
+
function modelProviderLabel(modelId, fallbackProvider) {
|
|
3207
|
+
const id = modelId.toLowerCase();
|
|
3208
|
+
for (const [prefix, vendor] of VENDOR_PREFIX) if (id.startsWith(prefix)) return vendor;
|
|
3209
|
+
return labelOf(fallbackProvider);
|
|
3210
|
+
}
|
|
3178
3211
|
var defaultModel = {
|
|
3179
3212
|
gapi: "gmodel-pro",
|
|
3180
3213
|
gmodel: "gmodel-pro",
|
|
@@ -3247,7 +3280,9 @@ async function listModels(provider2, cfg2) {
|
|
|
3247
3280
|
return (d2.models ?? []).filter((m) => m.supportedGenerationMethods?.includes("generateContent")).map((m) => (m.name ?? "").replace(/^models\//, "")).filter(Boolean);
|
|
3248
3281
|
}
|
|
3249
3282
|
if (p === "anthropic" || p === "claude") {
|
|
3250
|
-
const
|
|
3283
|
+
const base3 = cfg2.baseUrl?.replace(/\/$/, "");
|
|
3284
|
+
const url = base3 ? `${base3}/models` : "https://api.anthropic.com/v1/models";
|
|
3285
|
+
const r2 = await fetch(url, { headers: { "x-api-key": cfg2.apiKey, authorization: `Bearer ${cfg2.apiKey}`, "anthropic-version": "2023-06-01" } });
|
|
3251
3286
|
if (!r2.ok) return [];
|
|
3252
3287
|
const d2 = await r2.json();
|
|
3253
3288
|
return (d2.data ?? []).map((m) => m.id ?? "").filter(Boolean);
|
|
@@ -9128,6 +9163,45 @@ var PromptCancelledError = class extends Error {
|
|
|
9128
9163
|
|
|
9129
9164
|
// ../agent-ui/src/prompt/text.ts
|
|
9130
9165
|
init_color();
|
|
9166
|
+
import { createInterface } from "readline";
|
|
9167
|
+
async function textPrompt(question, opts = {}) {
|
|
9168
|
+
const { default: defaultVal, placeholder, validate, transform } = opts;
|
|
9169
|
+
const hint = defaultVal ? c.meta(` [${defaultVal}]`) : placeholder ? c.meta(` (${placeholder})`) : "";
|
|
9170
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
9171
|
+
let _cancelReject;
|
|
9172
|
+
const cancelPromise = new Promise((_, reject) => {
|
|
9173
|
+
_cancelReject = reject;
|
|
9174
|
+
});
|
|
9175
|
+
rl.on("SIGINT", () => {
|
|
9176
|
+
rl.close();
|
|
9177
|
+
process.stdout.write("\n");
|
|
9178
|
+
_cancelReject(new PromptCancelledError());
|
|
9179
|
+
});
|
|
9180
|
+
try {
|
|
9181
|
+
for (; ; ) {
|
|
9182
|
+
const answer = await Promise.race([
|
|
9183
|
+
new Promise((res) => rl.question(`${c.user("?")} ${question}${hint}: `, res)),
|
|
9184
|
+
cancelPromise
|
|
9185
|
+
]);
|
|
9186
|
+
const raw = answer.trim() || (defaultVal ?? "");
|
|
9187
|
+
const value = transform ? transform(raw) : raw;
|
|
9188
|
+
if (validate) {
|
|
9189
|
+
const result = validate(value);
|
|
9190
|
+
if (result !== true) {
|
|
9191
|
+
const msg = typeof result === "string" ? result : "\u8F93\u5165\u65E0\u6548\uFF0C\u8BF7\u91CD\u8BD5";
|
|
9192
|
+
process.stdout.write(` ${c.fail("\u2717")} ${c.dim(msg)}
|
|
9193
|
+
`);
|
|
9194
|
+
continue;
|
|
9195
|
+
}
|
|
9196
|
+
}
|
|
9197
|
+
rl.close();
|
|
9198
|
+
return value;
|
|
9199
|
+
}
|
|
9200
|
+
} catch (e) {
|
|
9201
|
+
rl.close();
|
|
9202
|
+
throw e;
|
|
9203
|
+
}
|
|
9204
|
+
}
|
|
9131
9205
|
|
|
9132
9206
|
// ../agent-ui/src/prompt/select.ts
|
|
9133
9207
|
init_color();
|
|
@@ -9913,7 +9987,7 @@ import { statSync as statSync6, readFileSync as readFileSync14, unlinkSync as un
|
|
|
9913
9987
|
// package.json
|
|
9914
9988
|
var package_default = {
|
|
9915
9989
|
name: "@guangnao/agent-cli",
|
|
9916
|
-
version: "1.1.
|
|
9990
|
+
version: "1.1.6",
|
|
9917
9991
|
description: "AgentOS terminal CLI \u2014 drive a real LLM agent on the microkernel; strongest-UX shell, advanced agent core",
|
|
9918
9992
|
type: "module",
|
|
9919
9993
|
bin: {
|
|
@@ -11636,7 +11710,10 @@ ${c.dim(cut(p.system, 300))}`);
|
|
|
11636
11710
|
return;
|
|
11637
11711
|
}
|
|
11638
11712
|
const choice = await this.dock.requestSplitSelect(
|
|
11639
|
-
opts.map((o) =>
|
|
11713
|
+
opts.map((o) => {
|
|
11714
|
+
const vendor = modelProviderLabel(o.model, o.provider);
|
|
11715
|
+
return { label: o.model, hint: vendor, value: `${o.provider}|${o.model}`, detail: `provider: ${vendor}${`${vendor} \xB7 ${o.model}` === m.current ? " (current)" : ""}` };
|
|
11716
|
+
}),
|
|
11640
11717
|
"switch model"
|
|
11641
11718
|
);
|
|
11642
11719
|
if (choice) this.switchModel(choice);
|
|
@@ -11644,12 +11721,13 @@ ${c.dim(cut(p.system, 300))}`);
|
|
|
11644
11721
|
switchModel(v) {
|
|
11645
11722
|
const sep = v.indexOf("|"), provider2 = v.slice(0, sep), model2 = v.slice(sep + 1);
|
|
11646
11723
|
this.chat = this.opts.modelMenu.build(provider2, model2);
|
|
11647
|
-
this.opts.modelMenu.
|
|
11724
|
+
this.opts.modelMenu.persist?.(provider2, model2);
|
|
11725
|
+
this.opts.modelMenu.current = `${modelProviderLabel(model2, provider2)} \xB7 ${model2}`;
|
|
11648
11726
|
const history = this.convo?.dump();
|
|
11649
11727
|
this.convo?.close();
|
|
11650
11728
|
this.convo = void 0;
|
|
11651
11729
|
if (history?.length) this.ensureConvo(history);
|
|
11652
|
-
this.note(`${c.done("model")} ${c.dim("\u2192")} ${c.bold(model2)} ${c.dim("(" +
|
|
11730
|
+
this.note(`${c.done("model")} ${c.dim("\u2192")} ${c.bold(model2)} ${c.dim("(" + modelProviderLabel(model2, provider2) + ")")}`);
|
|
11653
11731
|
this.dock.setStatusLine(this.statusLine());
|
|
11654
11732
|
}
|
|
11655
11733
|
/** /login — re-run the first-run setup wizard (pick provider → paste key → pick model), exactly the flow a
|
|
@@ -12063,7 +12141,8 @@ var PROVIDERS = [
|
|
|
12063
12141
|
{ label: "DeepSeek", value: "deepseek", hint: "api.deepseek.com \xB7 cheap & capable" },
|
|
12064
12142
|
{ label: "OpenAI", value: "openai", hint: "api.openai.com" },
|
|
12065
12143
|
{ label: "Anthropic \xB7 Claude", value: "anthropic", hint: "api.anthropic.com" },
|
|
12066
|
-
{ label: "Google \xB7 Gemini", value: "gemini", hint: "generativelanguage.googleapis.com" }
|
|
12144
|
+
{ label: "Google \xB7 Gemini", value: "gemini", hint: "generativelanguage.googleapis.com" },
|
|
12145
|
+
{ label: "Custom", value: "custom", hint: "your own baseURL + key \xB7 OpenAI- or Anthropic-compatible" }
|
|
12067
12146
|
];
|
|
12068
12147
|
var KEY_URL = {
|
|
12069
12148
|
guangnao: "https://guangnao.com",
|
|
@@ -12089,9 +12168,24 @@ ${c.dim(" Connect an LLM provider to begin \u2014 your key is saved to ~/.agent
|
|
|
12089
12168
|
|
|
12090
12169
|
`);
|
|
12091
12170
|
try {
|
|
12092
|
-
const
|
|
12093
|
-
if (
|
|
12094
|
-
|
|
12171
|
+
const choice = await selectPrompt("Provider \u2191\u2193 select \xB7 Enter", { options: PROVIDERS });
|
|
12172
|
+
if (choice == null) return null;
|
|
12173
|
+
let provider2 = choice;
|
|
12174
|
+
let baseUrl2;
|
|
12175
|
+
if (choice === "custom") {
|
|
12176
|
+
const proto = await selectPrompt("Wire protocol \u2191\u2193 select \xB7 Enter", { options: [
|
|
12177
|
+
{ label: "OpenAI-compatible", value: "openai", hint: "POST {baseURL}/chat/completions" },
|
|
12178
|
+
{ label: "Anthropic-compatible", value: "anthropic", hint: "POST {baseURL}/messages" }
|
|
12179
|
+
] });
|
|
12180
|
+
if (proto == null) return null;
|
|
12181
|
+
provider2 = proto;
|
|
12182
|
+
baseUrl2 = (await textPrompt("Base URL", {
|
|
12183
|
+
placeholder: "https://host/v1",
|
|
12184
|
+
transform: (v) => v.trim(),
|
|
12185
|
+
validate: (v) => /^https?:\/\/.+/.test(v.trim()) || "enter a full URL, e.g. https://host/v1"
|
|
12186
|
+
})).trim();
|
|
12187
|
+
}
|
|
12188
|
+
const label2 = choice === "custom" ? `Custom \xB7 ${provider2}` : PROVIDERS.find((p) => p.value === provider2).label;
|
|
12095
12189
|
if (KEY_URL[provider2]) out(`${c.dim(" Get a key at " + KEY_URL[provider2])}
|
|
12096
12190
|
`);
|
|
12097
12191
|
for (; ; ) {
|
|
@@ -12104,7 +12198,7 @@ ${c.dim(" Connect an LLM provider to begin \u2014 your key is saved to ~/.agent
|
|
|
12104
12198
|
const fallback = defaultModel[provider2] ?? "";
|
|
12105
12199
|
let models = [];
|
|
12106
12200
|
try {
|
|
12107
|
-
models = await listModels(provider2, { apiKey: apiKey2, model: fallback });
|
|
12201
|
+
models = await listModels(provider2, { apiKey: apiKey2, model: fallback, baseUrl: baseUrl2 });
|
|
12108
12202
|
} catch {
|
|
12109
12203
|
}
|
|
12110
12204
|
if (!models.length) out(`${c.warning(" Could not list models")} ${c.dim("(unverified key or no catalog \u2014 saving anyway; switch later with /models).")}
|
|
@@ -12119,9 +12213,11 @@ ${c.dim(" Connect an LLM provider to begin \u2014 your key is saved to ~/.agent
|
|
|
12119
12213
|
});
|
|
12120
12214
|
if (sel == null) return null;
|
|
12121
12215
|
model2 = sel;
|
|
12216
|
+
} else if (baseUrl2) {
|
|
12217
|
+
model2 = (await textPrompt("Model name", { transform: (v) => v.trim(), validate: (v) => !!v.trim() || "model name required" })).trim();
|
|
12122
12218
|
}
|
|
12123
|
-
saveConfig({ provider: provider2, apiKey: apiKey2, model: model2 });
|
|
12124
|
-
out(`${c.done(" \u2713 Saved")} ${c.dim("\xB7 " + provider2 + " \xB7 " + model2)}
|
|
12219
|
+
saveConfig({ provider: provider2, apiKey: apiKey2, model: model2, baseUrl: baseUrl2 });
|
|
12220
|
+
out(`${c.done(" \u2713 Saved")} ${c.dim("\xB7 " + provider2 + " \xB7 " + model2 + (baseUrl2 ? " \xB7 " + baseUrl2 : ""))}
|
|
12125
12221
|
|
|
12126
12222
|
`);
|
|
12127
12223
|
return { provider: provider2, apiKey: apiKey2, model: model2 };
|
|
@@ -12200,8 +12296,14 @@ var keyOf = (p) => ({
|
|
|
12200
12296
|
function modelMenu() {
|
|
12201
12297
|
const keyed = ["gapi", "deepseek", "openai", "openai-responses", "gemini", "anthropic"].filter((p) => keyOf(p));
|
|
12202
12298
|
return {
|
|
12203
|
-
current: `${
|
|
12299
|
+
current: `${modelProviderLabel(model, provider)} \xB7 ${model}`,
|
|
12204
12300
|
build: (p, m) => makeChat(p, { apiKey: keyOf(p) ?? apiKey, model: m, baseUrl }),
|
|
12301
|
+
persist: (p, m) => {
|
|
12302
|
+
provider = p;
|
|
12303
|
+
model = m;
|
|
12304
|
+
saveConfig({ provider: p, model: m });
|
|
12305
|
+
},
|
|
12306
|
+
// /models choice survives restart (apiKey/baseUrl already saved)
|
|
12205
12307
|
async list() {
|
|
12206
12308
|
const lists = await Promise.all(keyed.map(async (p) => {
|
|
12207
12309
|
const ids = await listModels(p, { apiKey: keyOf(p) ?? apiKey, model: defaultModel[p] ?? model, baseUrl });
|
|
@@ -12220,7 +12322,7 @@ async function relogin() {
|
|
|
12220
12322
|
model = r.model;
|
|
12221
12323
|
baseUrl = process.env.LLM_BASE_URL ?? loadConfig().baseUrl;
|
|
12222
12324
|
buildChats();
|
|
12223
|
-
return { chat: rawChat, cheapChat, label: `${
|
|
12325
|
+
return { chat: rawChat, cheapChat, label: `${modelProviderLabel(model, provider)} \xB7 ${model}` };
|
|
12224
12326
|
}
|
|
12225
12327
|
async function main() {
|
|
12226
12328
|
if (!apiKey && !process.env.AGENTOS_OFFLINE) {
|