u1s1-cli 1.2.2 → 1.2.3

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.
@@ -1,9 +1,15 @@
1
- import { type CliConfig, type CustomEndpoint, type ModelDef } from "./config.js";
1
+ import { type CliConfig, type CustomEndpoint, type ModelDef, type ThinkingLevel } from "./config.js";
2
2
  import type { ShellDoctorResult } from "./shell-doctor.js";
3
3
  /** pi auto-appends <agentDir>/APPEND_SYSTEM.md to its system prompt — our branding hook. */
4
4
  export declare function ensureBrandPrompt(shell: ShellDoctorResult): void;
5
+ /**
6
+ * Seed per-model defaults without overwriting a user's Ctrl+S choice. The
7
+ * marker records values managed by u1s1, so a later Gateway default can update
8
+ * them while a diverged user value becomes user-owned.
9
+ */
10
+ export declare function applyModelThinkingDefaults(settings: Record<string, unknown>, models: ModelDef[]): boolean;
5
11
  /** Defaults that don't overwrite values the user already set. */
6
- export declare function ensureDefaultSettings(): void;
12
+ export declare function ensureDefaultSettings(models?: ModelDef[]): void;
7
13
  /** ≤0.4.0 wrote u1s1-dark/u1s1-light into the pi themes dir; remove them. */
8
14
  export declare function cleanupBrandThemes(): void;
9
15
  /**
@@ -54,10 +60,19 @@ export declare function toProviderModels(models: ModelDef[]): {
54
60
  id: string;
55
61
  name: string;
56
62
  reasoning: boolean;
63
+ thinkingLevelMap?: Partial<Record<ThinkingLevel, string | null>>;
57
64
  input: ("text" | "image")[];
58
65
  cost: ModelDef["cost"];
59
66
  contextWindow: number;
60
67
  maxTokens: number;
68
+ compat?: {
69
+ supportsStore?: boolean;
70
+ supportsDeveloperRole?: boolean;
71
+ supportsReasoningEffort: boolean;
72
+ maxTokensField?: "max_tokens";
73
+ requiresReasoningContentOnAssistantMessages?: boolean;
74
+ thinkingFormat: "openai" | "deepseek" | "qwen";
75
+ };
61
76
  }[];
62
77
  /**
63
78
  * 自定义端点 → pi provider 条目。密钥走环境变量引用(启动器已 set,见
@@ -66,8 +66,52 @@ function bundledPiVersion() {
66
66
  return undefined;
67
67
  }
68
68
  }
69
+ const THINKING_DEFAULTS_MARKER = "u1s1ModelThinkingDefaults";
70
+ function recordValue(value) {
71
+ return typeof value === "object" && value !== null && !Array.isArray(value)
72
+ ? value
73
+ : undefined;
74
+ }
75
+ /**
76
+ * Seed per-model defaults without overwriting a user's Ctrl+S choice. The
77
+ * marker records values managed by u1s1, so a later Gateway default can update
78
+ * them while a diverged user value becomes user-owned.
79
+ */
80
+ export function applyModelThinkingDefaults(settings, models) {
81
+ const defaults = models.filter((model) => model.thinking !== undefined);
82
+ if (defaults.length === 0)
83
+ return false;
84
+ const levels = { ...(recordValue(settings["modelThinkingLevels"]) ?? {}) };
85
+ const managed = { ...(recordValue(settings[THINKING_DEFAULTS_MARKER]) ?? {}) };
86
+ let changed = false;
87
+ for (const model of defaults) {
88
+ const key = `${PROVIDER_ID}/${model.id}`;
89
+ const next = model.thinking.defaultLevel;
90
+ const current = levels[key];
91
+ const previousManaged = managed[key];
92
+ if (current === undefined || current === previousManaged) {
93
+ if (current !== next) {
94
+ levels[key] = next;
95
+ changed = true;
96
+ }
97
+ if (previousManaged !== next) {
98
+ managed[key] = next;
99
+ changed = true;
100
+ }
101
+ }
102
+ else if (previousManaged !== undefined) {
103
+ delete managed[key];
104
+ changed = true;
105
+ }
106
+ }
107
+ if (changed) {
108
+ settings["modelThinkingLevels"] = levels;
109
+ settings[THINKING_DEFAULTS_MARKER] = managed;
110
+ }
111
+ return changed;
112
+ }
69
113
  /** Defaults that don't overwrite values the user already set. */
70
- export function ensureDefaultSettings() {
114
+ export function ensureDefaultSettings(models = []) {
71
115
  mkdirSync(agentDir, { recursive: true });
72
116
  const p = join(agentDir, "settings.json");
73
117
  let settings = {};
@@ -114,6 +158,8 @@ export function ensureDefaultSettings() {
114
158
  settings["autoUpdate"] = true;
115
159
  changed = true;
116
160
  }
161
+ if (applyModelThinkingDefaults(settings, models))
162
+ changed = true;
117
163
  // ≤0.4.0 shipped branded themes and forced them as default; the files are
118
164
  // gone now, so a settings.json still pointing at them must fall back to
119
165
  // pi's default theme.
@@ -333,15 +379,40 @@ export function ensureAuthCredential() {
333
379
  }
334
380
  /** pi provider 条目里的模型形状(models.json 与 registerProvider 共用)。 */
335
381
  export function toProviderModels(models) {
336
- return models.map((m) => ({
337
- id: m.id,
338
- name: m.name,
339
- reasoning: m.reasoning,
340
- input: m.vision ? ["text", "image"] : ["text"],
341
- cost: m.cost,
342
- contextWindow: m.contextWindow,
343
- maxTokens: m.maxTokens,
344
- }));
382
+ return models.map((m) => {
383
+ let thinkingLevelMap;
384
+ let compat;
385
+ if (m.thinking) {
386
+ const supported = new Set(m.thinking.levels);
387
+ thinkingLevelMap = {};
388
+ for (const level of ["off", "minimal", "low", "medium", "high", "xhigh", "max"]) {
389
+ thinkingLevelMap[level] = supported.has(level) ? (m.thinking.levelMap[level] ?? level) : null;
390
+ }
391
+ compat = {
392
+ supportsReasoningEffort: true,
393
+ thinkingFormat: m.thinking.requestFormat,
394
+ ...(m.thinking.requestFormat === "deepseek"
395
+ ? {
396
+ supportsStore: false,
397
+ supportsDeveloperRole: false,
398
+ maxTokensField: "max_tokens",
399
+ requiresReasoningContentOnAssistantMessages: true,
400
+ }
401
+ : {}),
402
+ };
403
+ }
404
+ return {
405
+ id: m.id,
406
+ name: m.name,
407
+ reasoning: m.reasoning,
408
+ thinkingLevelMap,
409
+ input: m.vision ? ["text", "image"] : ["text"],
410
+ cost: m.cost,
411
+ contextWindow: m.contextWindow,
412
+ maxTokens: m.maxTokens,
413
+ compat,
414
+ };
415
+ });
345
416
  }
346
417
  /**
347
418
  * 自定义端点 → pi provider 条目。密钥走环境变量引用(启动器已 set,见
package/dist/api.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type CliConfig } from "./config.js";
1
+ import { type ApiThinkingCapabilities, type CliConfig } from "./config.js";
2
2
  export interface MeResponse {
3
3
  email: string | null;
4
4
  signup_credit_usd?: number;
@@ -35,6 +35,8 @@ export interface ApiModel {
35
35
  id: string;
36
36
  name: string;
37
37
  reasoning: boolean;
38
+ /** Older gateways omit model-specific thinking metadata. */
39
+ thinking?: ApiThinkingCapabilities | null;
38
40
  /** Older gateways omit this field; missing means text-only. */
39
41
  vision?: boolean;
40
42
  context_length: number;
package/dist/config.d.ts CHANGED
@@ -8,14 +8,28 @@ export declare const VERSION: string;
8
8
  export declare function isPortableInstall(): boolean;
9
9
  export declare const DEFAULT_BASE_URL = "https://api.u1s1.io/v1";
10
10
  export declare const PROVIDER_ID = "u1s1";
11
- /**
12
- * Convert an API model response to our internal ModelDef.
13
- * Aliases are derived from the short id; note is left empty (filled by /model command).
14
- */
15
- export declare function apiModelToDef(m: {
11
+ export declare const THINKING_LEVELS: readonly ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
12
+ export type ThinkingLevel = (typeof THINKING_LEVELS)[number];
13
+ export type ThinkingRequestFormat = "openai" | "deepseek" | "qwen";
14
+ export interface ThinkingCapabilities {
15
+ levels: ThinkingLevel[];
16
+ defaultLevel: ThinkingLevel;
17
+ canDisable: boolean;
18
+ levelMap: Partial<Record<ThinkingLevel, string>>;
19
+ requestFormat: ThinkingRequestFormat;
20
+ }
21
+ export interface ApiThinkingCapabilities {
22
+ levels: string[];
23
+ default_level: string;
24
+ can_disable: boolean;
25
+ level_map: Record<string, string>;
26
+ request_format: string;
27
+ }
28
+ interface ApiModelShape {
16
29
  id: string;
17
30
  name: string;
18
31
  reasoning: boolean;
32
+ thinking?: ApiThinkingCapabilities | null;
19
33
  vision?: boolean;
20
34
  context_length: number;
21
35
  max_tokens: number;
@@ -24,13 +38,20 @@ export declare function apiModelToDef(m: {
24
38
  output: number;
25
39
  cache_read: number | null;
26
40
  };
27
- }): ModelDef;
41
+ }
42
+ /**
43
+ * Convert an API model response to our internal ModelDef.
44
+ * Aliases are derived from the short id; note is left empty (filled by /model command).
45
+ */
46
+ export declare function apiModelToDef(m: ApiModelShape): ModelDef;
28
47
  export interface ModelDef {
29
48
  id: string;
30
49
  name: string;
31
50
  /** short aliases accepted by `u1s1 model <name>` */
32
51
  aliases: string[];
33
52
  reasoning: boolean;
53
+ /** Model-specific thinking levels supplied by the Gateway. Missing means legacy pi behavior. */
54
+ thinking?: ThinkingCapabilities;
34
55
  vision: boolean;
35
56
  contextWindow: number;
36
57
  maxTokens: number;
@@ -137,3 +158,4 @@ export declare function saveConfig(cfg: CliConfig): void;
137
158
  export declare function persistPreferredModel(cfg: CliConfig, provider: string, modelId: string): CliConfig;
138
159
  export declare function saveEndpointsCache(endpoints: CustomEndpoint[]): void;
139
160
  export declare function loadEndpointsCache(): CustomEndpoint[];
161
+ export {};
package/dist/config.js CHANGED
@@ -28,16 +28,44 @@ function defaultAliases(id) {
28
28
  const aliases = [short, ...parts.filter((p) => p.length > 1)];
29
29
  return [...new Set(aliases)];
30
30
  }
31
+ export const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
32
+ const THINKING_LEVEL_SET = new Set(THINKING_LEVELS);
33
+ function parseThinkingCapabilities(value) {
34
+ if (!value || !Array.isArray(value.levels))
35
+ return undefined;
36
+ const levels = [...new Set(value.levels)].filter((level) => THINKING_LEVEL_SET.has(level));
37
+ if (levels.length === 0 || !THINKING_LEVEL_SET.has(value.default_level) || !levels.includes(value.default_level)) {
38
+ return undefined;
39
+ }
40
+ const requestFormat = value.request_format;
41
+ if (requestFormat !== "openai" && requestFormat !== "deepseek" && requestFormat !== "qwen")
42
+ return undefined;
43
+ const levelMap = {};
44
+ for (const level of levels) {
45
+ const mapped = value.level_map?.[level];
46
+ if (typeof mapped === "string" && mapped)
47
+ levelMap[level] = mapped;
48
+ }
49
+ return {
50
+ levels,
51
+ defaultLevel: value.default_level,
52
+ canDisable: value.can_disable === true && levels.includes("off"),
53
+ levelMap,
54
+ requestFormat,
55
+ };
56
+ }
31
57
  /**
32
58
  * Convert an API model response to our internal ModelDef.
33
59
  * Aliases are derived from the short id; note is left empty (filled by /model command).
34
60
  */
35
61
  export function apiModelToDef(m) {
62
+ const thinking = parseThinkingCapabilities(m.thinking);
36
63
  return {
37
64
  id: m.id,
38
65
  name: m.name,
39
66
  aliases: defaultAliases(m.id),
40
- reasoning: m.reasoning,
67
+ reasoning: m.reasoning || thinking !== undefined,
68
+ thinking,
41
69
  vision: m.vision === true,
42
70
  contextWindow: m.context_length,
43
71
  maxTokens: m.max_tokens,
@@ -55,7 +83,14 @@ export const MODELS = [
55
83
  id: "deepseek-v4-flash",
56
84
  name: "DeepSeek V4 Flash (u1s1)",
57
85
  aliases: ["deepseek", "flash", "v4-flash"],
58
- reasoning: false,
86
+ reasoning: true,
87
+ thinking: {
88
+ levels: ["off", "low", "high", "max"],
89
+ defaultLevel: "high",
90
+ canDisable: true,
91
+ levelMap: { off: "none", low: "low", high: "high", max: "max" },
92
+ requestFormat: "deepseek",
93
+ },
59
94
  vision: false,
60
95
  contextWindow: 1_048_576,
61
96
  maxTokens: 384_000,
package/dist/index.js CHANGED
@@ -189,6 +189,9 @@ async function runAgent(cfg, args) {
189
189
  setAnnouncement(announcement);
190
190
  }
191
191
  await endpointsReady;
192
+ // Gateway metadata owns each official model's default thinking level. Seed
193
+ // it after the live model list arrives; explicit user choices remain intact.
194
+ ensureDefaultSettings(MODELS);
192
195
  // pi provider 只支持静态 header;指向本机 signing proxy,由它逐请求附 DPoP proof。
193
196
  const signing = await ensureSigningProxy(cfg);
194
197
  const officialCfg = { ...cfg, baseUrl: signing.baseUrl, apiKey: signing.localKey };
package/dist/login.js CHANGED
@@ -152,8 +152,8 @@ export async function login(keyArg) {
152
152
  // 新网关且免费包可领/可续,登录成功时顺手提醒
153
153
  quotaNote =
154
154
  me.free_claim === "first"
155
- ? "免费用量包还没领,去 https://u1s1.io/dashboard 点「领取」(首月每天 1 亿 Token)"
156
- : "免费用量包到期了,去 https://u1s1.io/dashboard 续领(每天 3000 万 Token)";
155
+ ? "有免费用量包可领,去 https://u1s1.io/dashboard 查看"
156
+ : "免费用量包状态有更新,去 https://u1s1.io/dashboard 查看";
157
157
  }
158
158
  else if (tpu > 0) {
159
159
  const tok = (usd) => {
package/dist/usage.js CHANGED
@@ -42,6 +42,8 @@ export async function usage() {
42
42
  free_first: "首月免费包",
43
43
  free_yearly: "年度免费包",
44
44
  invite: "邀请赠送",
45
+ new_user: "新用户赠送",
46
+ payment_delay_gift: "临时加量包",
45
47
  topup_daily: "每日加量包",
46
48
  admin_grant: "官方赠送",
47
49
  };
@@ -71,9 +73,11 @@ export async function usage() {
71
73
  const label = ((PKG_LABEL[p.kind] ?? p.kind) + (p.count > 1 ? ` ×${p.count}` : "")).padEnd(5, " ");
72
74
  const per = daily ? "/天" : "";
73
75
  console.log(` ${label} 还剩 ${fmtTokensCn(p.remaining)} / ${fmtTokensCn(total)}${per} ${bar(ratio)}`);
74
- const scopeNote = p.kind === "invite"
76
+ const scopeNote = p.kind === "login_checkin"
75
77
  ? "仅限 u1s1 客户端使用 · 全模型可用"
76
- : p.scope === "free" ? "仅默认模型和搜索 · 0 点恢复" : "全模型可用";
78
+ : p.scope === "free"
79
+ ? "免费包适用模型 · 0 点恢复"
80
+ : "仅限 u1s1 客户端使用 · 免费包适用模型";
77
81
  console.log(` ${scopeNote} · ${p.expires_at ? `${p.expires_at.slice(0, 10)} 到期` : "永不过期"}`);
78
82
  }
79
83
  if (me.bonus_balance_usd > 0) {
@@ -82,15 +86,8 @@ export async function usage() {
82
86
  }
83
87
  console.log(` 本月已用 ${tpu > 0 ? `约 ${fmtTokensCn(me.mtd_usd * tpu)} Token($${me.mtd_usd.toFixed(2)})` : `$${me.mtd_usd.toFixed(2)}`}`);
84
88
  console.log("");
85
- if (me.free_claim === "first") {
86
- console.log(" 你有免费用量包没领:首月每天 1 亿 Token,去 https://u1s1.io/dashboard 点「领取」");
87
- }
88
- else if (me.free_claim === "renew") {
89
- console.log(" → 免费用量包到期了,去 https://u1s1.io/dashboard 续领:每天 3000 万 Token,一年有效");
90
- }
91
- else {
92
- console.log(" 免费包每天 0 点恢复;邀请礼包需受邀人手动领取,仅限 u1s1 客户端 → https://u1s1.io/dashboard");
93
- }
89
+ console.log(" 免费用量包覆盖 DeepSeek V4 Flash / Vision、GLM-5.3 Flash、Qwen3.8 Flash 和联网搜索。");
90
+ console.log(" DeepSeek V4 Pro 仅可使用余额 https://u1s1.io/dashboard");
94
91
  console.log(" Token 数按默认模型单价折算,为约数;用更贵的模型时消耗更快。");
95
92
  console.log("");
96
93
  return;
package/dist/web.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { mkdirSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { cleanupBrandThemes, endpointKeyEnv, ensureAuthCredential, ensureBrandPrompt, ensureWorkflowPromptTemplate, ensureDefaultSettings, ensureProviderModels, scrubForeignProviderEnv, writeAttributionExtension, writeWebToolsExtension, } from "./agent-setup.js";
4
- import { agentDir, apiModelToDef, resolvePreferredModel, setModelsFromApi, u1s1Dir, writeAgentDefaultModel, } from "./config.js";
4
+ import { agentDir, apiModelToDef, MODELS, resolvePreferredModel, setModelsFromApi, u1s1Dir, writeAgentDefaultModel, } from "./config.js";
5
5
  import { ensureSearchTools } from "./search-tools.js";
6
6
  import { ensureUsableShell } from "./shell-doctor.js";
7
7
  import { fetchModels, loadCustomEndpoints } from "./api.js";
@@ -38,6 +38,7 @@ export async function prepareWebEnv(cfg) {
38
38
  console.error(" 获取模型列表失败,使用内置列表:", e.message);
39
39
  }
40
40
  await endpointsReady;
41
+ ensureDefaultSettings(MODELS);
41
42
  const signing = await ensureSigningProxy(cfg, "desktop");
42
43
  const officialCfg = { ...cfg, baseUrl: signing.baseUrl, apiKey: signing.localKey };
43
44
  ensureBrandPrompt(await shellReady);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "u1s1-cli",
3
- "version": "1.2.2",
3
+ "version": "1.2.3",
4
4
  "description": "u1s1 — 有一说一,最省心的 AI 编程搭子。终端里用中文说需求,AI 帮你读文件、改代码、跑命令。",
5
5
  "type": "module",
6
6
  "bin": {