@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.
Files changed (3) hide show
  1. package/README.md +3 -1
  2. package/dist/cli.js +123 -21
  3. 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 body = {
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
- if (req.onDelta) return stream2(base2, headers, body, req.onDelta, req.signal);
2968
- const res = await fetch(`${base2}/messages`, { method: "POST", headers, body: JSON.stringify(body), signal: req.signal });
2969
- if (!res.ok) throw new Error(`llm ${res.status}: ${(await res.text()).slice(0, 300)}`);
2970
- const data = await res.json();
2971
- return fromReply(data);
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 r2 = await fetch("https://api.anthropic.com/v1/models", { headers: { "x-api-key": cfg2.apiKey, "anthropic-version": "2023-06-01" } });
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.4",
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) => ({ label: o.model, hint: labelOf(o.provider), value: `${o.provider}|${o.model}`, detail: `provider: ${labelOf(o.provider)}${`${labelOf(o.provider)} \xB7 ${o.model}` === m.current ? " (current)" : ""}` })),
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.current = `${labelOf(provider2)} \xB7 ${model2}`;
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("(" + labelOf(provider2) + ")")}`);
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 provider2 = await selectPrompt("Provider \u2191\u2193 select \xB7 Enter", { options: PROVIDERS });
12093
- if (provider2 == null) return null;
12094
- const label2 = PROVIDERS.find((p) => p.value === provider2).label;
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: `${labelOf(provider)} \xB7 ${model}`,
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: `${labelOf(provider)} \xB7 ${model}` };
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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guangnao/agent-cli",
3
- "version": "1.1.4",
3
+ "version": "1.1.6",
4
4
  "description": "AgentOS terminal CLI — drive a real LLM agent on the microkernel; strongest-UX shell, advanced agent core",
5
5
  "type": "module",
6
6
  "bin": {