u1s1-cli-cn 1.18.0 → 1.20.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.
Files changed (43) hide show
  1. package/dist/agents/agent-config.d.ts +1 -1
  2. package/dist/agents/agent-config.js +1 -1
  3. package/dist/agents/claude.d.ts +4 -1
  4. package/dist/agents/claude.js +6 -3
  5. package/dist/agents/codex.d.ts +4 -1
  6. package/dist/agents/codex.js +6 -3
  7. package/dist/agents/custom-models.d.ts +61 -0
  8. package/dist/agents/custom-models.js +175 -0
  9. package/dist/agents/dsh.d.ts +90 -0
  10. package/dist/agents/dsh.js +243 -0
  11. package/dist/agents/launch.d.ts +24 -2
  12. package/dist/agents/launch.js +115 -15
  13. package/dist/agents/models-list.d.ts +65 -0
  14. package/dist/agents/models-list.js +297 -0
  15. package/dist/agents/node-runtime.d.ts +91 -0
  16. package/dist/agents/node-runtime.js +223 -0
  17. package/dist/agents/runtime.d.ts +26 -3
  18. package/dist/agents/runtime.js +50 -14
  19. package/dist/agents/status.d.ts +4 -0
  20. package/dist/agents/status.js +32 -7
  21. package/dist/api.d.ts +2 -0
  22. package/dist/chat-protocol/anthropic.d.ts +29 -0
  23. package/dist/chat-protocol/anthropic.js +484 -0
  24. package/dist/chat-protocol/json.d.ts +20 -0
  25. package/dist/chat-protocol/json.js +22 -0
  26. package/dist/chat-protocol/responses.d.ts +97 -0
  27. package/dist/chat-protocol/responses.js +1076 -0
  28. package/dist/chat-protocol/sse.d.ts +19 -0
  29. package/dist/chat-protocol/sse.js +67 -0
  30. package/dist/chat-protocol/thinking.d.ts +22 -0
  31. package/dist/chat-protocol/thinking.js +58 -0
  32. package/dist/config-command.d.ts +1 -1
  33. package/dist/config-command.js +20 -10
  34. package/dist/config.d.ts +23 -1
  35. package/dist/config.js +52 -1
  36. package/dist/custom-endpoint-proxy.d.ts +92 -0
  37. package/dist/custom-endpoint-proxy.js +461 -0
  38. package/dist/device-auth.d.ts +14 -3
  39. package/dist/device-auth.js +36 -2
  40. package/dist/index.js +12 -3
  41. package/dist/launcher.d.ts +5 -1
  42. package/dist/launcher.js +35 -11
  43. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  import { type RuntimeBrake } from "./runtime.js";
2
- export type GatewayAgentName = "claude-code" | "codex" | "pi";
2
+ export type GatewayAgentName = "claude-code" | "codex" | "dsh" | "pi";
3
3
  export interface AgentModel {
4
4
  id: string;
5
5
  name: string;
@@ -4,7 +4,7 @@
4
4
  * 契约(网关 workers/gateway/src/agent-config.ts):
5
5
  * { agent, enabled, disabled_reason, runtime: { min_version, max_tested_version, blocked_versions },
6
6
  * default_model, small_model, models: [...], settings }
7
- * default_model / small_model 在没有模型时是 null;settings 只在 claude-code 启用时是对象,其余 null。
7
+ * default_model / small_model 在没有模型时是 null;settings 在 claude-code / codex / dsh 启用时是对象(各客户端自己的形状),其余 null。
8
8
  * 响应里没有任何凭证或端点:地址和本地 token 由启动器自己注入。
9
9
  *
10
10
  * 结果处理:
@@ -1,5 +1,6 @@
1
1
  import { type McpLaunchEnv } from "../mcp-server.js";
2
2
  import { type AgentConfig } from "./agent-config.js";
3
+ import { type AgentCustomModel } from "./custom-models.js";
3
4
  import { type RuntimePaths } from "./runtime.js";
4
5
  export { agentsRoot, CLAUDE_TERMS_URL, cliInstallHooks, consentLines, notOpenText, overMaxTestedText, printRuntimeFailure, runtimeErrorText, spawnAgent, } from "./launch.js";
5
6
  export declare const CLAUDE_GATEWAY_AGENT: "claude-code";
@@ -72,7 +73,9 @@ export declare function sanitizeClaudeSettings(settings: Record<string, unknown>
72
73
  * model 用调用方选好的(用户在 u1s1 config 里定的、仍在阵容里的模型),没给时保留网关的,
73
74
  * 网关也没有就用 default_model。
74
75
  */
75
- export declare function claudeSettingsFromConfig(config: AgentConfig, model?: string | null): Record<string, unknown>;
76
+ export declare function claudeSettingsFromConfig(config: AgentConfig, model?: string | null,
77
+ /** 自定义端点模型:接在 /model 列表后面(custom-models.ts withCustomClaudeModels) */
78
+ customModels?: readonly AgentCustomModel[]): Record<string, unknown>;
76
79
  /** 调用方的 --settings 有问题(缺值、读不了、不是 JSON 对象);runClaude 报出来后以非 0 退出。 */
77
80
  export declare class ClaudeSettingsArgError extends Error {
78
81
  name: string;
@@ -22,6 +22,7 @@ import { join, resolve } from "node:path";
22
22
  import { writePrivateFile } from "../config.js";
23
23
  import { lang, tr } from "../i18n.js";
24
24
  import { MCP_ENV_NAMES, MCP_SERVER_NAME, MCP_TRACE_ENV, mcpInjectionDisabled, mcpLaunchEnv, mcpServerCommand, } from "../mcp-server.js";
25
+ import { withCustomClaudeModels } from "./custom-models.js";
25
26
  import { AGENT_PROFILES, agentsRoot, prepareAgentLaunch, runPreparedAgent, } from "./launch.js";
26
27
  import { runtimePaths } from "./runtime.js";
27
28
  import { stripSecretEnv } from "../secret-env.js";
@@ -198,13 +199,15 @@ export function sanitizeClaudeSettings(settings) {
198
199
  * model 用调用方选好的(用户在 u1s1 config 里定的、仍在阵容里的模型),没给时保留网关的,
199
200
  * 网关也没有就用 default_model。
200
201
  */
201
- export function claudeSettingsFromConfig(config, model) {
202
+ export function claudeSettingsFromConfig(config, model,
203
+ /** 自定义端点模型:接在 /model 列表后面(custom-models.ts withCustomClaudeModels) */
204
+ customModels = []) {
202
205
  const out = sanitizeClaudeSettings(config.settings ?? {});
203
206
  if (model)
204
207
  out["model"] = model;
205
208
  else if (out["model"] === undefined && config.defaultModel)
206
209
  out["model"] = config.defaultModel;
207
- return out;
210
+ return withCustomClaudeModels(out, customModels, out["model"]);
208
211
  }
209
212
  // ---------------------------------------------------------------- 调用方的 --settings
210
213
  /** 调用方的 --settings 有问题(缺值、读不了、不是 JSON 对象);runClaude 报出来后以非 0 退出。 */
@@ -385,7 +388,7 @@ export async function runClaude(userArgs) {
385
388
  const mcp = mcpInjectionDisabled()
386
389
  ? undefined
387
390
  : mcpLaunchEnv({ proxyBaseUrl: prepared.proxy.baseUrl, localToken: prepared.proxy.localKey, features: prepared.features, lang: lang() });
388
- const settings = claudeSettingsFromConfig(prepared.config, prepared.model);
391
+ const settings = claudeSettingsFromConfig(prepared.config, prepared.model, prepared.customModels);
389
392
  cleanStaleSettingsFiles(paths.root);
390
393
  const settingsFile = callerSettings.length ? processSettingsFile(paths) : paths.settingsFile;
391
394
  writeClaudeSettings(settingsFile, finalClaudeSettings(settings, callerSettings, !!mcp));
@@ -1,5 +1,6 @@
1
1
  import { type McpLaunchEnv } from "../mcp-server.js";
2
2
  import type { AgentConfig } from "./agent-config.js";
3
+ import { type AgentCustomModel } from "./custom-models.js";
3
4
  import { type RuntimePaths } from "./runtime.js";
4
5
  export declare const CODEX_GATEWAY_AGENT: "codex";
5
6
  /** config.toml 里我们的 provider id(不能用 openai、ollama 等内置保留名)。 */
@@ -98,7 +99,9 @@ export declare function modelCatalogFromConfig(config: AgentConfig): Record<stri
98
99
  */
99
100
  export declare function writeCodexConfig(paths: CodexPaths, config: AgentConfig, now?: () => number, mcp?: CodexMcpServer | null,
100
101
  /** 这次启动用的模型(launch.ts 按 u1s1 config 选好);undefined = 网关默认 */
101
- model?: string | null): string | undefined;
102
+ model?: string | null,
103
+ /** 自定义端点模型:接在网关目录后面(custom-models.ts withCustomCodexCatalog) */
104
+ customModels?: readonly AgentCustomModel[]): string | undefined;
102
105
  /** [mcp_servers.u1s1] 的内容:同一个 node、同一份 CLI;只列要转发的变量名。 */
103
106
  export declare function codexMcpServer(command?: {
104
107
  command: string;
@@ -25,6 +25,7 @@ import { writePrivateFile } from "../config.js";
25
25
  import { lang, tr } from "../i18n.js";
26
26
  import { MCP_ENV_NAMES, MCP_SERVER_NAME, MCP_TRACE_ENV, mcpInjectionDisabled, mcpLaunchEnv, mcpServerCommand, } from "../mcp-server.js";
27
27
  import { stripSecretEnv } from "../secret-env.js";
28
+ import { withCustomCodexCatalog } from "./custom-models.js";
28
29
  import { AGENT_PROFILES, agentsRoot, prepareAgentLaunch, runPreparedAgent } from "./launch.js";
29
30
  import { runtimePaths } from "./runtime.js";
30
31
  export const CODEX_GATEWAY_AGENT = "codex";
@@ -334,9 +335,11 @@ export function modelCatalogFromConfig(config) {
334
335
  */
335
336
  export function writeCodexConfig(paths, config, now = Date.now, mcp = null,
336
337
  /** 这次启动用的模型(launch.ts 按 u1s1 config 选好);undefined = 网关默认 */
337
- model = config.defaultModel) {
338
+ model = config.defaultModel,
339
+ /** 自定义端点模型:接在网关目录后面(custom-models.ts withCustomCodexCatalog) */
340
+ customModels = []) {
338
341
  mkdirSync(paths.configDir, { recursive: true, mode: 0o700 });
339
- const catalog = modelCatalogFromConfig(config);
342
+ const catalog = withCustomCodexCatalog(modelCatalogFromConfig(config), customModels);
340
343
  if (catalog)
341
344
  writePrivateFile(paths.catalogFile, JSON.stringify(catalog) + "\n");
342
345
  else
@@ -382,7 +385,7 @@ export async function runCodex(userArgs) {
382
385
  const mcp = mcpInjectionDisabled()
383
386
  ? undefined
384
387
  : mcpLaunchEnv({ proxyBaseUrl: prepared.proxy.baseUrl, localToken: prepared.proxy.localKey, features: prepared.features, lang: lang() });
385
- const backup = writeCodexConfig(paths, prepared.config, Date.now, mcp ? codexMcpServer() : null, prepared.model);
388
+ const backup = writeCodexConfig(paths, prepared.config, Date.now, mcp ? codexMcpServer() : null, prepared.model, prepared.customModels);
386
389
  if (backup) {
387
390
  console.error(tr(` ${paths.configToml} 里有 u1s1 没法逐项改写的写法,已整份重写;原文件备份在 ${backup}。`, ` ${paths.configToml} had entries u1s1 couldn't update in place, so it was rewritten; the original is saved as ${backup}.`));
388
391
  }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * 自定义端点的模型给 Claude Code / Codex 用(规划 docs/custom-endpoints-all-clients-plan-2026-11-13.md 的 A2)。
3
+ *
4
+ * 客户端里的模型 id 是 `<端点 id>/<模型 id>`(端点 id = ep + 32 位 hex,模型 id 原样,可以再带 `/`),
5
+ * 与 Pi 的 `provider/model` 写法一致;本机签名代理按这个 id 认出自定义模型,在本机转成 Chat Completions
6
+ * 直连端点(custom-endpoint-proxy.ts),不经网关、不计费。网关模型的 id 里没有这种前缀,照旧走网关。
7
+ *
8
+ * 只收 api = openai-completions 的端点(本地路由只会转 Chat Completions)。
9
+ */
10
+ import { type CustomEndpoint, type ModelDef } from "../config.js";
11
+ /** 本地路由认的 id:`ep<32 hex>/<模型 id>`。 */
12
+ export declare const CUSTOM_MODEL_ID_RE: RegExp;
13
+ export declare const CUSTOM_ENDPOINT_API = "openai-completions";
14
+ export interface AgentCustomModel {
15
+ /** 客户端里用的 id(--model / -m / /model 里的值) */
16
+ id: string;
17
+ endpoint: CustomEndpoint;
18
+ model: ModelDef;
19
+ }
20
+ export declare function customModelId(endpointId: string, modelId: string): string;
21
+ /** 端点能不能给 Claude Code / Codex 用:只认 Chat Completions 协议。 */
22
+ export declare function routableEndpoint(ep: Pick<CustomEndpoint, "id" | "api">): boolean;
23
+ /** 某客户端(Claude Code / Codex)可用的自定义模型,端点顺序、端点内模型顺序。A1 的模型列表也用它。 */
24
+ export declare function agentCustomModels(endpoints: readonly CustomEndpoint[]): AgentCustomModel[];
25
+ export type CustomModelLookup =
26
+ /** 不是自定义模型的写法:交给网关 */
27
+ {
28
+ kind: "gateway";
29
+ }
30
+ /** 自定义模型的写法,但端点或模型已不在(删了、改了名):本机直接报错,不发给网关 */
31
+ | {
32
+ kind: "missing";
33
+ endpointId: string;
34
+ modelId: string;
35
+ } | {
36
+ kind: "custom";
37
+ found: AgentCustomModel;
38
+ };
39
+ export declare function lookupCustomModel(endpoints: readonly CustomEndpoint[], requested: unknown): CustomModelLookup;
40
+ /**
41
+ * 把自定义模型接在 /model 列表(modelPicker.options)后面。Claude Code 不认识这些 id,窗口只有一个全局的
42
+ * CLAUDE_CODE_MAX_CONTEXT_TOKENS:启动模型是自定义模型时设成它的窗口,否则不动(不让一个小窗口的本地模型
43
+ * 拖低网关模型的压缩点;会话中途从网关模型换到更小窗口的自定义模型时可能超窗,由端点报错)。
44
+ */
45
+ export declare function withCustomClaudeModels(settings: Record<string, unknown>, customs: readonly AgentCustomModel[], launchModel: string | null | undefined): Record<string, unknown>;
46
+ /**
47
+ * Codex /model 里给自定义模型列的思考档位(本地路由按 chatReasoningFields 转成 reasoning_effort):
48
+ * 带思考契约的按契约(关不掉的不列 none);只标了推理的给 none/low/medium/high,默认 medium;非推理不列。
49
+ */
50
+ export declare function codexCustomReasoning(model: Pick<ModelDef, "reasoning" | "thinking">): {
51
+ levels: string[];
52
+ defaultLevel: string | null;
53
+ };
54
+ /**
55
+ * 自定义模型的 model_catalog 条目。指令模板等 Codex 必填但与模型无关的字段取网关目录的第一条
56
+ * (网关目录每条都带同一份模板);目录为空时没有模板可用,返回 undefined(Codex 对目录外的模型用
57
+ * 兜底元数据,照样能用)。
58
+ */
59
+ export declare function codexCatalogEntry(c: AgentCustomModel, template: Record<string, unknown>, priority: number): Record<string, unknown>;
60
+ /** 网关的 model_catalog + 自定义模型(排在后面)。没有网关目录或目录为空时原样返回。 */
61
+ export declare function withCustomCodexCatalog(catalog: Record<string, unknown> | undefined, customs: readonly AgentCustomModel[]): Record<string, unknown> | undefined;
@@ -0,0 +1,175 @@
1
+ /**
2
+ * 自定义端点的模型给 Claude Code / Codex 用(规划 docs/custom-endpoints-all-clients-plan-2026-11-13.md 的 A2)。
3
+ *
4
+ * 客户端里的模型 id 是 `<端点 id>/<模型 id>`(端点 id = ep + 32 位 hex,模型 id 原样,可以再带 `/`),
5
+ * 与 Pi 的 `provider/model` 写法一致;本机签名代理按这个 id 认出自定义模型,在本机转成 Chat Completions
6
+ * 直连端点(custom-endpoint-proxy.ts),不经网关、不计费。网关模型的 id 里没有这种前缀,照旧走网关。
7
+ *
8
+ * 只收 api = openai-completions 的端点(本地路由只会转 Chat Completions)。
9
+ */
10
+ import { ENDPOINT_ID_RE } from "../config.js";
11
+ /** 本地路由认的 id:`ep<32 hex>/<模型 id>`。 */
12
+ export const CUSTOM_MODEL_ID_RE = /^(ep[0-9a-f]{32})\/(.+)$/;
13
+ export const CUSTOM_ENDPOINT_API = "openai-completions";
14
+ export function customModelId(endpointId, modelId) {
15
+ return `${endpointId}/${modelId}`;
16
+ }
17
+ /** 端点能不能给 Claude Code / Codex 用:只认 Chat Completions 协议。 */
18
+ export function routableEndpoint(ep) {
19
+ return ENDPOINT_ID_RE.test(ep.id) && ep.api === CUSTOM_ENDPOINT_API;
20
+ }
21
+ /** 某客户端(Claude Code / Codex)可用的自定义模型,端点顺序、端点内模型顺序。A1 的模型列表也用它。 */
22
+ export function agentCustomModels(endpoints) {
23
+ const out = [];
24
+ const seen = new Set();
25
+ for (const endpoint of endpoints) {
26
+ if (!routableEndpoint(endpoint))
27
+ continue;
28
+ for (const model of endpoint.models) {
29
+ const id = customModelId(endpoint.id, model.id);
30
+ if (!model.id || seen.has(id))
31
+ continue;
32
+ seen.add(id);
33
+ out.push({ id, endpoint, model });
34
+ }
35
+ }
36
+ return out;
37
+ }
38
+ export function lookupCustomModel(endpoints, requested) {
39
+ if (typeof requested !== "string")
40
+ return { kind: "gateway" };
41
+ const m = CUSTOM_MODEL_ID_RE.exec(requested);
42
+ if (!m)
43
+ return { kind: "gateway" };
44
+ const [, endpointId, modelId] = m;
45
+ const endpoint = endpoints.find((ep) => ep.id === endpointId && routableEndpoint(ep));
46
+ const model = endpoint?.models.find((x) => x.id === modelId);
47
+ if (!endpoint || !model)
48
+ return { kind: "missing", endpointId, modelId };
49
+ return { kind: "custom", found: { id: requested, endpoint, model } };
50
+ }
51
+ // ---------------------------------------------------------------- Claude Code
52
+ function contextLabel(tokens) {
53
+ if (tokens >= 1_000_000)
54
+ return `${Math.round(tokens / 100_000) / 10}M context`;
55
+ return `${Math.round(tokens / 1000)}K context`;
56
+ }
57
+ function customDescription(c) {
58
+ const parts = [];
59
+ if (c.model.contextWindow > 0)
60
+ parts.push(contextLabel(c.model.contextWindow));
61
+ parts.push(`custom endpoint ${c.endpoint.name}`);
62
+ return parts.join(" - ");
63
+ }
64
+ function isPlainObject(value) {
65
+ return !!value && typeof value === "object" && !Array.isArray(value);
66
+ }
67
+ /**
68
+ * 把自定义模型接在 /model 列表(modelPicker.options)后面。Claude Code 不认识这些 id,窗口只有一个全局的
69
+ * CLAUDE_CODE_MAX_CONTEXT_TOKENS:启动模型是自定义模型时设成它的窗口,否则不动(不让一个小窗口的本地模型
70
+ * 拖低网关模型的压缩点;会话中途从网关模型换到更小窗口的自定义模型时可能超窗,由端点报错)。
71
+ */
72
+ export function withCustomClaudeModels(settings, customs, launchModel) {
73
+ if (customs.length === 0)
74
+ return settings;
75
+ const out = { ...settings };
76
+ const picker = isPlainObject(out["modelPicker"]) ? { ...out["modelPicker"] } : {};
77
+ const options = Array.isArray(picker["options"]) ? [...picker["options"]] : [];
78
+ const listed = new Set(options.map((o) => (isPlainObject(o) ? o["model"] : undefined)));
79
+ for (const c of customs) {
80
+ if (listed.has(c.id))
81
+ continue;
82
+ options.push({ model: c.id, label: c.model.name || c.model.id, description: customDescription(c) });
83
+ }
84
+ picker["options"] = options;
85
+ out["modelPicker"] = picker;
86
+ const launched = customs.find((c) => c.id === launchModel);
87
+ if (launched && launched.model.contextWindow > 0) {
88
+ const env = isPlainObject(out["env"]) ? { ...out["env"] } : {};
89
+ env["CLAUDE_CODE_MAX_CONTEXT_TOKENS"] = String(launched.model.contextWindow);
90
+ out["env"] = env;
91
+ }
92
+ return out;
93
+ }
94
+ // ---------------------------------------------------------------- Codex
95
+ const CODEX_EFFORT_TEXT = {
96
+ none: "No reasoning",
97
+ minimal: "Minimal reasoning",
98
+ low: "Fast responses with lighter reasoning",
99
+ medium: "Balanced speed and reasoning depth",
100
+ high: "Deeper reasoning for complex problems",
101
+ xhigh: "Extra deep reasoning",
102
+ };
103
+ /** u1s1 思考档位 → Codex 的 reasoning effort;max 在 Responses 里没有对应档,不列。 */
104
+ const CODEX_EFFORT = {
105
+ off: "none",
106
+ minimal: "minimal",
107
+ low: "low",
108
+ medium: "medium",
109
+ high: "high",
110
+ xhigh: "xhigh",
111
+ };
112
+ /**
113
+ * Codex /model 里给自定义模型列的思考档位(本地路由按 chatReasoningFields 转成 reasoning_effort):
114
+ * 带思考契约的按契约(关不掉的不列 none);只标了推理的给 none/low/medium/high,默认 medium;非推理不列。
115
+ */
116
+ export function codexCustomReasoning(model) {
117
+ if (!model.reasoning)
118
+ return { levels: [], defaultLevel: null };
119
+ const caps = model.thinking;
120
+ if (caps && caps.levels.length) {
121
+ const levels = caps.levels
122
+ .filter((level) => level !== "off" || caps.canDisable)
123
+ .map((level) => CODEX_EFFORT[level])
124
+ .filter((effort) => typeof effort === "string");
125
+ const preferred = CODEX_EFFORT[caps.defaultLevel];
126
+ const defaultLevel = preferred && levels.includes(preferred) ? preferred : levels.filter((e) => e !== "none").at(-1) ?? null;
127
+ return { levels, defaultLevel };
128
+ }
129
+ return { levels: ["none", "low", "medium", "high"], defaultLevel: "medium" };
130
+ }
131
+ /** 与网关 codex-config.ts 一致:模型行没设压缩预算时按 200K 压。 */
132
+ const CODEX_DEFAULT_COMPACT_TOKENS = 200_000;
133
+ /**
134
+ * 自定义模型的 model_catalog 条目。指令模板等 Codex 必填但与模型无关的字段取网关目录的第一条
135
+ * (网关目录每条都带同一份模板);目录为空时没有模板可用,返回 undefined(Codex 对目录外的模型用
136
+ * 兜底元数据,照样能用)。
137
+ */
138
+ export function codexCatalogEntry(c, template, priority) {
139
+ const reasoning = codexCustomReasoning(c.model);
140
+ const levels = reasoning.levels;
141
+ const window = c.model.contextWindow > 0 ? c.model.contextWindow : undefined;
142
+ const entry = {
143
+ ...template,
144
+ slug: c.id,
145
+ display_name: c.model.name || c.model.id,
146
+ description: customDescription(c),
147
+ supported_reasoning_levels: levels.map((effort) => ({ effort, description: CODEX_EFFORT_TEXT[effort] ?? effort })),
148
+ priority,
149
+ input_modalities: c.model.vision ? ["text", "image"] : ["text"],
150
+ };
151
+ delete entry["default_reasoning_level"];
152
+ if (reasoning.defaultLevel)
153
+ entry["default_reasoning_level"] = reasoning.defaultLevel;
154
+ if (window) {
155
+ entry["context_window"] = window;
156
+ entry["max_context_window"] = window;
157
+ // 压缩点:200K 与窗口的 90% 取小(本地模型窗口常比 200K 小)
158
+ entry["auto_compact_token_limit"] = Math.min(CODEX_DEFAULT_COMPACT_TOKENS, Math.floor(window * 0.9));
159
+ }
160
+ return entry;
161
+ }
162
+ /** 网关的 model_catalog + 自定义模型(排在后面)。没有网关目录或目录为空时原样返回。 */
163
+ export function withCustomCodexCatalog(catalog, customs) {
164
+ if (!catalog || customs.length === 0)
165
+ return catalog;
166
+ const models = Array.isArray(catalog["models"]) ? catalog["models"] : [];
167
+ const template = models.find(isPlainObject);
168
+ if (!template)
169
+ return catalog;
170
+ const slugs = new Set(models.map((m) => (isPlainObject(m) ? m["slug"] : undefined)));
171
+ const extra = customs
172
+ .filter((c) => !slugs.has(c.id))
173
+ .map((c, i) => codexCatalogEntry(c, template, models.length + i));
174
+ return { ...catalog, models: [...models, ...extra] };
175
+ }
@@ -0,0 +1,90 @@
1
+ import { type McpLaunchEnv } from "../mcp-server.js";
2
+ import type { AgentConfig } from "./agent-config.js";
3
+ import { type RuntimePaths } from "./runtime.js";
4
+ /** patch 里我们的 provider 名(dsh 的路由名:小写加连字符)。 */
5
+ export declare const DSH_PROVIDER_ID = "u1s1";
6
+ /** provider 的 apiKeyEnv:子进程环境里放本地 token 的变量名。 */
7
+ export declare const DSH_KEY_ENV = "U1S1_LOCAL_KEY";
8
+ /** 签名代理地址(带 /v1),patch 里用 `!!js process.env.U1S1_DSH_BASE_URL` 读。 */
9
+ export declare const DSH_BASE_URL_ENV = "U1S1_DSH_BASE_URL";
10
+ export interface DshPaths extends RuntimePaths {
11
+ /** DSH_HOME */
12
+ configDir: string;
13
+ /** 启动器生成的 --patch 文件 */
14
+ patchFile: string;
15
+ /** 上次成功拉到的 /v1/agents/dsh/config */
16
+ configCache: string;
17
+ }
18
+ export declare function dshPaths(root?: string): DshPaths;
19
+ /**
20
+ * 子进程环境里要去掉的变量(依据 dsh 0.2.2-rc.1 的默认配置树):
21
+ * - DSH_HOME(我们自己设);
22
+ * - DEEPSEEK_API_KEY:官方 API Key 通道和官方网页搜索读它,有了就会绕开 u1s1 直连官方;
23
+ * - 遥测/产品分析的地址与模式(DSH_TELEMETRY_MODE、DSH_TELEMETRY_OTLP_URL、DSH_PRODUCT_ANALYTICS_OTLP_URL、
24
+ * DSH_CLIENT_VERSION),以及换搜索/抓取提供方的 DSH_WEB_SEARCH_PROVIDER、DSH_WEB_FETCH_PROVIDER;
25
+ * - 我们自己的变量:关掉 MCP 注入时也不留用户环境里的旧值。
26
+ * DSH_PERMISSION_MODE(用户自己选的沙箱模式)保留。
27
+ */
28
+ export declare const DSH_ENV_DROP: readonly string[];
29
+ export interface DshLaunchInput {
30
+ baseEnv: NodeJS.ProcessEnv;
31
+ /** 签名代理的一次性本地 token */
32
+ localToken: string;
33
+ /** 签名代理地址,形如 http://127.0.0.1:12345/v1 */
34
+ proxyBaseUrl: string;
35
+ /** DSH_HOME */
36
+ home: string;
37
+ /** u1s1 工具服务要的变量(mcpLaunchEnv);不注入时省略 */
38
+ mcp?: McpLaunchEnv;
39
+ }
40
+ export declare function dshLaunchEnv(input: DshLaunchInput): NodeJS.ProcessEnv;
41
+ /**
42
+ * 用户参数 → dsh 参数。dsh 的语法是 `dsh [--profile] <名字> [启动器选项] [应用参数]`,`--patch` 是启动器选项,
43
+ * 要紧跟在 profile 名后面(第一个它不认识的参数起就是应用参数)。
44
+ * (无参数) → web --patch <文件>
45
+ * --no-open --port 1 → web --patch <文件> --no-open --port 1(以选项开头:默认进网页界面)
46
+ * headless "任务" → headless --patch <文件> "任务"
47
+ * --profile web … → --profile web --patch <文件> …
48
+ * --help / --version → 原样(dsh 自己的帮助和版本)
49
+ * plugin … → 原样(插件管理,不启动 profile)
50
+ * 用户自己再传的 --patch 排在我们的后面,后叠的为准。
51
+ */
52
+ export declare function dshLaunchArgs(patchFile: string, userArgs: readonly string[]): string[];
53
+ /**
54
+ * 网关 settings.models(dsh-config.ts 下发的 dsh 原生形状)→ provider 模型表。没有 settings(老网关)时
55
+ * 退回 models 阵容里的 id、名字、窗口。
56
+ */
57
+ export declare function dshProviderModels(config: Pick<AgentConfig, "settings" | "models">): Array<Record<string, unknown>>;
58
+ export interface DshPatchInput {
59
+ models: Array<Record<string, unknown>>;
60
+ /** 这次启动的默认模型 */
61
+ model: string;
62
+ /** 会话标题等后台小任务用的模型;null = 跟默认模型 */
63
+ smallModel: string | null;
64
+ /** u1s1 工具服务怎么拉起;null = 不注入 */
65
+ mcp: {
66
+ command: string;
67
+ args: string[];
68
+ envVars: string[];
69
+ } | null;
70
+ }
71
+ /**
72
+ * 生成 --patch 文件。dsh 的 patch 是顶层数组:按 id 覆盖条目(config 整段替换、不深合并),`insert` 追加新条目。
73
+ * 文件里没有端点和凭证:baseURL 与 MCP 服务的变量都用 `!!js process.env.…` 在 dsh 进程里读。
74
+ */
75
+ export declare function renderDshPatch(input: DshPatchInput): string;
76
+ /** MCP 服务:同一个 node、同一份 CLI;env 里只列变量名,值由 dsh 从自己的环境转过去。 */
77
+ export declare function dshMcpServer(command?: {
78
+ command: string;
79
+ args: string[];
80
+ }, env?: NodeJS.ProcessEnv): {
81
+ command: string;
82
+ args: string[];
83
+ envVars: string[];
84
+ };
85
+ export declare function writeDshPatch(paths: DshPaths, input: DshPatchInput): void;
86
+ /**
87
+ * `u1s1 dsh [参数…]`。返回子进程的退出码(启动前就失败返回 1)。
88
+ * 是否可用完全由网关配置的 enabled 决定(prepareAgentLaunch)。
89
+ */
90
+ export declare function runDsh(userArgs: string[]): Promise<number>;