u1s1-cli 1.14.5 → 1.15.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/agent-setup.d.ts +7 -0
- package/dist/agent-setup.js +14 -0
- package/dist/agents/agent-config.d.ts +83 -0
- package/dist/agents/agent-config.js +154 -0
- package/dist/agents/claude.d.ts +80 -0
- package/dist/agents/claude.js +233 -0
- package/dist/agents/codex.d.ts +111 -0
- package/dist/agents/codex.js +392 -0
- package/dist/agents/launch.d.ts +109 -0
- package/dist/agents/launch.js +444 -0
- package/dist/agents/pi.d.ts +9 -0
- package/dist/agents/pi.js +562 -0
- package/dist/agents/prefs.d.ts +35 -0
- package/dist/agents/prefs.js +96 -0
- package/dist/agents/runtime.d.ts +354 -0
- package/dist/agents/runtime.js +884 -0
- package/dist/agents/status.d.ts +20 -0
- package/dist/agents/status.js +241 -0
- package/dist/api.js +2 -1
- package/dist/config-command.d.ts +18 -0
- package/dist/config-command.js +335 -0
- package/dist/config.d.ts +17 -0
- package/dist/config.js +35 -1
- package/dist/device-auth.d.ts +39 -17
- package/dist/device-auth.js +61 -72
- package/dist/embed.d.ts +13 -0
- package/dist/embed.js +15 -0
- package/dist/gateway-route.d.ts +57 -0
- package/dist/gateway-route.js +157 -0
- package/dist/index.js +22 -556
- package/dist/launcher.d.ts +48 -0
- package/dist/launcher.js +235 -0
- package/dist/login.js +3 -2
- package/dist/mcp-server.d.ts +105 -0
- package/dist/mcp-server.js +449 -0
- package/dist/pi-ui-text.d.ts +57 -0
- package/dist/pi-ui-text.js +148 -0
- package/dist/search-tools.js +2 -1
- package/dist/usage.js +2 -2
- package/package.json +3 -2
- package/scripts/p0-claude-code/README.md +39 -0
- package/scripts/p0-claude-code/launch-claude.ts +94 -0
- package/scripts/p0-claude-code/replay.ts +74 -0
- package/scripts/p0-claude-code/seed-local.mjs +145 -0
- package/scripts/p0-claude-code/tap.mjs +105 -0
- package/scripts/patch-pi.js +76 -55
- package/scripts/pi-patch-report.mjs +224 -0
- package/scripts/pi-upgrade.mjs +829 -0
- package/scripts/render-regression.mjs +67 -5
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { type McpLaunchEnv } from "../mcp-server.js";
|
|
2
|
+
import type { AgentConfig } from "./agent-config.js";
|
|
3
|
+
import { type RuntimePaths } from "./runtime.js";
|
|
4
|
+
export declare const CODEX_GATEWAY_AGENT: "codex";
|
|
5
|
+
/** config.toml 里我们的 provider id(不能用 openai、ollama 等内置保留名)。 */
|
|
6
|
+
export declare const CODEX_PROVIDER_ID = "u1s1";
|
|
7
|
+
/** provider 的 env_key:子进程环境里放本地 token 的变量名。 */
|
|
8
|
+
export declare const CODEX_KEY_ENV = "U1S1_LOCAL_KEY";
|
|
9
|
+
export interface CodexPaths extends RuntimePaths {
|
|
10
|
+
/** CODEX_HOME */
|
|
11
|
+
configDir: string;
|
|
12
|
+
/** CODEX_HOME/config.toml */
|
|
13
|
+
configToml: string;
|
|
14
|
+
/** 网关下发的模型目录,原样写成文件给 model_catalog_json 用 */
|
|
15
|
+
catalogFile: string;
|
|
16
|
+
/** 上次成功拉到的 /v1/agents/codex/config */
|
|
17
|
+
configCache: string;
|
|
18
|
+
}
|
|
19
|
+
export declare function codexPaths(root?: string): CodexPaths;
|
|
20
|
+
/**
|
|
21
|
+
* 子进程环境里要去掉的变量(依据 Codex 0.167.2 源码):
|
|
22
|
+
* - 会换掉配置/状态目录的:CODEX_HOME(我们自己设)、CODEX_SQLITE_HOME(状态库改放别处,
|
|
23
|
+
* 可能是用户自己的目录);
|
|
24
|
+
* - 别的凭证:OPENAI_API_KEY、CODEX_API_KEY(`codex exec` 读它当 API Key 登录)、CODEX_ACCESS_TOKEN;
|
|
25
|
+
* 我们的 provider 只用 env_key,但有了它们 Codex 会以为已登录官方账号,去做相应的事;
|
|
26
|
+
* - 改官方 provider 地址的 OPENAI_BASE_URL(老版本读,0.167.2 只在它的网络代理里用);
|
|
27
|
+
* - 改请求形状的:CODEX_INTERNAL_ORIGINATOR_OVERRIDE 会换掉 originator 头和 User-Agent 前缀,
|
|
28
|
+
* 网关的客户端判定看这两样;
|
|
29
|
+
* - 「由某包管理器管理」的标记(CODEX_MANAGED_BY_*、CODEX_MANAGED_PACKAGE_ROOT):会让它提示用
|
|
30
|
+
* npm/bun 等升级,更新由启动器管。
|
|
31
|
+
* 只影响这一次启动的子进程环境,用户自己的 shell 与配置不动。
|
|
32
|
+
*/
|
|
33
|
+
export declare const CODEX_ENV_DROP: readonly string[];
|
|
34
|
+
export interface CodexLaunchInput {
|
|
35
|
+
baseEnv: NodeJS.ProcessEnv;
|
|
36
|
+
/** 签名代理的一次性本地 token */
|
|
37
|
+
localToken: string;
|
|
38
|
+
/** CODEX_HOME(须已存在,否则 Codex 报错退出) */
|
|
39
|
+
home: string;
|
|
40
|
+
/** u1s1 工具服务要的变量(mcpLaunchEnv);不注入时省略 */
|
|
41
|
+
mcp?: McpLaunchEnv;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* 本机代理地址不走用户的 HTTP(S)_PROXY:0.167.2 实测,设了 HTTP_PROXY / HTTPS_PROXY 又没有 NO_PROXY 时,
|
|
45
|
+
* Codex 把发往 http://127.0.0.1:<端口>/v1/responses 的请求也交给那个代理(连同本地 token),
|
|
46
|
+
* 代理不在本机就直接失败。用户原有的 NO_PROXY 条目保留,只补上 127.0.0.1 和 localhost。
|
|
47
|
+
*/
|
|
48
|
+
export declare function withLoopbackNoProxy(value: string | undefined): string;
|
|
49
|
+
export declare function codexLaunchEnv(input: CodexLaunchInput): NodeJS.ProcessEnv;
|
|
50
|
+
/**
|
|
51
|
+
* 启动器的 -c 覆盖放在用户参数前面:用户自己传的 -c / -m / --profile 照样生效(同名 -c 以后出现的为准)。
|
|
52
|
+
* @param proxyOrigin 签名代理的 origin,形如 http://127.0.0.1:12345(不带 /v1)
|
|
53
|
+
*/
|
|
54
|
+
export declare function codexLaunchArgs(proxyOrigin: string, userArgs: readonly string[]): string[];
|
|
55
|
+
/**
|
|
56
|
+
* 启动器管的顶层键:每次启动重写;不在这里的键(包括 Codex 自己写的项目信任)原样保留。
|
|
57
|
+
* model 每次都写成「用户在 u1s1 config 里定的模型(仍在阵容里时),否则网关默认」:Codex 里用 /model 换的
|
|
58
|
+
* 模型只管那一次会话,和 u1s1 claude 一致(那边 --settings 的 model 也盖过客户端里 /model 的选择)。
|
|
59
|
+
*/
|
|
60
|
+
export declare const CODEX_MANAGED_KEYS: readonly ["model", "model_provider", "check_for_update_on_startup", "cli_auth_credentials_store", "model_catalog_json"];
|
|
61
|
+
/**
|
|
62
|
+
* 跟着模型走的顶层键:Codex 的 /model 把它们和 model 一起写进 config.toml。启动器把 model 换掉时
|
|
63
|
+
* 一并去掉(回到新模型自己的默认),免得为别的模型选的思考强度带到新模型上;model 没变就保留。
|
|
64
|
+
*/
|
|
65
|
+
export declare const CODEX_MODEL_BOUND_KEYS: readonly ["model_reasoning_effort"];
|
|
66
|
+
export interface CodexManagedSettings {
|
|
67
|
+
model: string | null;
|
|
68
|
+
/** model_catalog_json 的绝对路径;null = 不写这一项 */
|
|
69
|
+
catalogFile: string | null;
|
|
70
|
+
/** u1s1 工具服务怎么拉起;null / 省略 = 不注入(原有的 [mcp_servers.u1s1] 也去掉) */
|
|
71
|
+
mcp?: CodexMcpServer | null;
|
|
72
|
+
}
|
|
73
|
+
export interface CodexMcpServer {
|
|
74
|
+
command: string;
|
|
75
|
+
args: string[];
|
|
76
|
+
/** 从 Codex 环境转给服务的变量名(Codex 默认只转 HOME、PATH 等少数几个) */
|
|
77
|
+
envVars: string[];
|
|
78
|
+
}
|
|
79
|
+
export interface RenderedCodexConfig {
|
|
80
|
+
text: string;
|
|
81
|
+
/** 原文件里有没法逐行安全改写的写法:原文件要先备份,再整份重写 */
|
|
82
|
+
replaced: boolean;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* 生成新的 config.toml。保守做法:不解析整份 TOML,只逐行处理我们管的东西——
|
|
86
|
+
* - 顶层(第一个表头之前)的受管键整行去掉,换成新值,放在文件最前面;
|
|
87
|
+
* - `[model_providers.u1s1]`、`[mcp_servers.u1s1]` 及其子表整段去掉,换成新的放在最后
|
|
88
|
+
* (例外:用户对 u1s1 工具的审批选择 `[mcp_servers.u1s1.tools.*]`,注入时跟在新表后面保留);
|
|
89
|
+
* - 其余每一行(用户加的键、别的表、注释、Codex 自己写的项目信任等)原样保留,顺序不变。
|
|
90
|
+
* 遇到没法逐行安全改写的写法(受管键是跨行值、在顶层或 [model_providers] 表里用点号/内联表写我们的
|
|
91
|
+
* provider)就整份重写,由调用方先把原文件备份。
|
|
92
|
+
*/
|
|
93
|
+
export declare function renderCodexConfig(existing: string | undefined, managed: CodexManagedSettings): RenderedCodexConfig;
|
|
94
|
+
/** 网关 settings.model_catalog:必须是对象才写;CLI 不解析、不改它的内容。 */
|
|
95
|
+
export declare function modelCatalogFromConfig(config: AgentConfig): Record<string, unknown> | undefined;
|
|
96
|
+
/**
|
|
97
|
+
* 写 model-catalog.json 与 config.toml。返回原 config.toml 被整份重写时的备份路径(给用户一句说明)。
|
|
98
|
+
*/
|
|
99
|
+
export declare function writeCodexConfig(paths: CodexPaths, config: AgentConfig, now?: () => number, mcp?: CodexMcpServer | null,
|
|
100
|
+
/** 这次启动用的模型(launch.ts 按 u1s1 config 选好);undefined = 网关默认 */
|
|
101
|
+
model?: string | null): string | undefined;
|
|
102
|
+
/** [mcp_servers.u1s1] 的内容:同一个 node、同一份 CLI;只列要转发的变量名。 */
|
|
103
|
+
export declare function codexMcpServer(command?: {
|
|
104
|
+
command: string;
|
|
105
|
+
args: string[];
|
|
106
|
+
}, env?: NodeJS.ProcessEnv): CodexMcpServer;
|
|
107
|
+
/**
|
|
108
|
+
* `u1s1 codex [参数…]`。返回子进程的退出码(启动前就失败返回 1)。
|
|
109
|
+
* 是否可用完全由网关配置的 enabled 决定(prepareAgentLaunch)。
|
|
110
|
+
*/
|
|
111
|
+
export declare function runCodex(userArgs: string[]): Promise<number>;
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `u1s1 codex`:以子进程启动官方原版 Codex CLI,经本机签名代理用设备凭证访问网关。
|
|
3
|
+
*
|
|
4
|
+
* 注入(规划 4.4,对着 Codex 0.167.2 的源码与实跑核对过):
|
|
5
|
+
* CODEX_HOME=~/.u1s1/agents/codex/home 配置、会话、历史、日志都在这里,与用户自己的 ~/.codex 互不影响
|
|
6
|
+
* home/config.toml 启动器每次重写受管的几项(见 renderCodexConfig),其余原样保留
|
|
7
|
+
* U1S1_LOCAL_KEY=<本地 token> [model_providers.u1s1] 的 env_key 读它,发 Authorization: Bearer
|
|
8
|
+
* -c model_providers.u1s1.base_url="http://127.0.0.1:<端口>/v1" 端口每次随机,用命令行覆盖
|
|
9
|
+
* -c model_provider="u1s1" 命令行一层的优先级高于仓库里的 .codex/config.toml
|
|
10
|
+
* -c features.daemon_auto_start=false 不用跨会话常驻的共享后台服务(见 codexLaunchArgs)
|
|
11
|
+
* -c analytics.enabled=false 不向官方上报使用指标
|
|
12
|
+
* -c features.plugins=false 不做插件同步(插件带来的 MCP 工具、/plugins 命令随之不可用)
|
|
13
|
+
* NO_PROXY / no_proxy 补上 127.0.0.1、localhost 否则设了 HTTP(S)_PROXY 时连本机代理也走用户代理
|
|
14
|
+
* [mcp_servers.u1s1] u1s1 工具服务(搜索、读网页、生图,见 mcp-server.ts);env_vars 只列变量名,
|
|
15
|
+
* 由 Codex 从自己的环境转给服务,代理地址和本地 token 不落盘
|
|
16
|
+
*
|
|
17
|
+
* provider 不设 requires_openai_auth(默认 false),所以不出现登录页;cli_auth_credentials_store="file"
|
|
18
|
+
* 不碰系统钥匙串。真实凭证(设备令牌、设备私钥)只留在启动器进程里:子进程环境从 stripSecretEnv 起步、
|
|
19
|
+
* 再显式写入上面几项;本地 token 只对本进程的签名代理有效,进程退出即作废。
|
|
20
|
+
* 不改二进制,不删除也不禁用它自带的登录方式(`codex login` 照常可用,凭据落在上面的 CODEX_HOME)。
|
|
21
|
+
*/
|
|
22
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, rmSync } from "node:fs";
|
|
23
|
+
import { join } from "node:path";
|
|
24
|
+
import { writePrivateFile } from "../config.js";
|
|
25
|
+
import { lang, tr } from "../i18n.js";
|
|
26
|
+
import { MCP_ENV_NAMES, MCP_SERVER_NAME, MCP_TRACE_ENV, mcpInjectionDisabled, mcpLaunchEnv, mcpServerCommand, } from "../mcp-server.js";
|
|
27
|
+
import { stripSecretEnv } from "../secret-env.js";
|
|
28
|
+
import { AGENT_PROFILES, agentsRoot, prepareAgentLaunch, runPreparedAgent } from "./launch.js";
|
|
29
|
+
import { runtimePaths } from "./runtime.js";
|
|
30
|
+
export const CODEX_GATEWAY_AGENT = "codex";
|
|
31
|
+
/** config.toml 里我们的 provider id(不能用 openai、ollama 等内置保留名)。 */
|
|
32
|
+
export const CODEX_PROVIDER_ID = "u1s1";
|
|
33
|
+
/** provider 的 env_key:子进程环境里放本地 token 的变量名。 */
|
|
34
|
+
export const CODEX_KEY_ENV = "U1S1_LOCAL_KEY";
|
|
35
|
+
export function codexPaths(root = agentsRoot()) {
|
|
36
|
+
const base = runtimePaths(root, "codex");
|
|
37
|
+
const home = join(base.root, "home");
|
|
38
|
+
return {
|
|
39
|
+
...base,
|
|
40
|
+
configDir: home,
|
|
41
|
+
configToml: join(home, "config.toml"),
|
|
42
|
+
catalogFile: join(base.root, "model-catalog.json"),
|
|
43
|
+
configCache: join(base.root, "agent-config.json"),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* 子进程环境里要去掉的变量(依据 Codex 0.167.2 源码):
|
|
48
|
+
* - 会换掉配置/状态目录的:CODEX_HOME(我们自己设)、CODEX_SQLITE_HOME(状态库改放别处,
|
|
49
|
+
* 可能是用户自己的目录);
|
|
50
|
+
* - 别的凭证:OPENAI_API_KEY、CODEX_API_KEY(`codex exec` 读它当 API Key 登录)、CODEX_ACCESS_TOKEN;
|
|
51
|
+
* 我们的 provider 只用 env_key,但有了它们 Codex 会以为已登录官方账号,去做相应的事;
|
|
52
|
+
* - 改官方 provider 地址的 OPENAI_BASE_URL(老版本读,0.167.2 只在它的网络代理里用);
|
|
53
|
+
* - 改请求形状的:CODEX_INTERNAL_ORIGINATOR_OVERRIDE 会换掉 originator 头和 User-Agent 前缀,
|
|
54
|
+
* 网关的客户端判定看这两样;
|
|
55
|
+
* - 「由某包管理器管理」的标记(CODEX_MANAGED_BY_*、CODEX_MANAGED_PACKAGE_ROOT):会让它提示用
|
|
56
|
+
* npm/bun 等升级,更新由启动器管。
|
|
57
|
+
* 只影响这一次启动的子进程环境,用户自己的 shell 与配置不动。
|
|
58
|
+
*/
|
|
59
|
+
export const CODEX_ENV_DROP = [
|
|
60
|
+
"CODEX_HOME",
|
|
61
|
+
"CODEX_SQLITE_HOME",
|
|
62
|
+
"OPENAI_API_KEY",
|
|
63
|
+
"OPENAI_BASE_URL",
|
|
64
|
+
"CODEX_API_KEY",
|
|
65
|
+
"CODEX_ACCESS_TOKEN",
|
|
66
|
+
"CODEX_INTERNAL_ORIGINATOR_OVERRIDE",
|
|
67
|
+
"CODEX_MANAGED_BY_NPM",
|
|
68
|
+
"CODEX_MANAGED_BY_BUN",
|
|
69
|
+
"CODEX_MANAGED_BY_PNPM",
|
|
70
|
+
"CODEX_MANAGED_BY_VITE_PLUS",
|
|
71
|
+
"CODEX_MANAGED_PACKAGE_ROOT",
|
|
72
|
+
CODEX_KEY_ENV,
|
|
73
|
+
// u1s1 工具服务的变量只由启动器设;关掉注入时也不留用户环境里的旧值
|
|
74
|
+
...MCP_ENV_NAMES.filter((name) => name !== "U1S1_LANG"),
|
|
75
|
+
];
|
|
76
|
+
/**
|
|
77
|
+
* 本机代理地址不走用户的 HTTP(S)_PROXY:0.167.2 实测,设了 HTTP_PROXY / HTTPS_PROXY 又没有 NO_PROXY 时,
|
|
78
|
+
* Codex 把发往 http://127.0.0.1:<端口>/v1/responses 的请求也交给那个代理(连同本地 token),
|
|
79
|
+
* 代理不在本机就直接失败。用户原有的 NO_PROXY 条目保留,只补上 127.0.0.1 和 localhost。
|
|
80
|
+
*/
|
|
81
|
+
export function withLoopbackNoProxy(value) {
|
|
82
|
+
const entries = (value ?? "").split(",").map((e) => e.trim()).filter(Boolean);
|
|
83
|
+
for (const host of ["127.0.0.1", "localhost"])
|
|
84
|
+
if (!entries.includes(host))
|
|
85
|
+
entries.push(host);
|
|
86
|
+
return entries.join(",");
|
|
87
|
+
}
|
|
88
|
+
export function codexLaunchEnv(input) {
|
|
89
|
+
const env = stripSecretEnv(input.baseEnv);
|
|
90
|
+
for (const key of CODEX_ENV_DROP)
|
|
91
|
+
delete env[key];
|
|
92
|
+
env["CODEX_HOME"] = input.home;
|
|
93
|
+
env[CODEX_KEY_ENV] = input.localToken;
|
|
94
|
+
if (input.mcp)
|
|
95
|
+
Object.assign(env, input.mcp);
|
|
96
|
+
const noProxy = withLoopbackNoProxy([env["NO_PROXY"], env["no_proxy"]].filter(Boolean).join(","));
|
|
97
|
+
env["NO_PROXY"] = noProxy;
|
|
98
|
+
env["no_proxy"] = noProxy;
|
|
99
|
+
return env;
|
|
100
|
+
}
|
|
101
|
+
/** -c 的值按 TOML 解析;用 JSON 字符串字面量(也是合法的 TOML 基本字符串)。 */
|
|
102
|
+
function tomlString(value) {
|
|
103
|
+
return JSON.stringify(value);
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* 启动器的 -c 覆盖放在用户参数前面:用户自己传的 -c / -m / --profile 照样生效(同名 -c 以后出现的为准)。
|
|
107
|
+
* @param proxyOrigin 签名代理的 origin,形如 http://127.0.0.1:12345(不带 /v1)
|
|
108
|
+
*/
|
|
109
|
+
export function codexLaunchArgs(proxyOrigin, userArgs) {
|
|
110
|
+
if (!/^http:\/\/127\.0\.0\.1:\d+$/.test(proxyOrigin)) {
|
|
111
|
+
throw new Error(`proxy origin must be a loopback origin without a path: ${proxyOrigin}`);
|
|
112
|
+
}
|
|
113
|
+
return [
|
|
114
|
+
"-c", `model_providers.${CODEX_PROVIDER_ID}.base_url=${tomlString(`${proxyOrigin}/v1`)}`,
|
|
115
|
+
"-c", `model_provider=${tomlString(CODEX_PROVIDER_ID)}`,
|
|
116
|
+
// 不起/不连「共享后台服务」:那是跨会话常驻的进程,会带着上一次的端口和本地 token;有 -c 时
|
|
117
|
+
// 它本来也会退回进程内模式,只是每次启动都弹一条警告(0.167.2 实测)
|
|
118
|
+
"-c", "features.daemon_auto_start=false",
|
|
119
|
+
// 关掉直连官方的两类后台请求:指标上报、插件同步(0.167.2 实测关掉后工具调用、子代理、/compact、
|
|
120
|
+
// /model 都正常;交互界面启动时拉一次公告没有公开开关,保留)
|
|
121
|
+
"-c", "analytics.enabled=false",
|
|
122
|
+
"-c", "features.plugins=false",
|
|
123
|
+
...userArgs,
|
|
124
|
+
];
|
|
125
|
+
}
|
|
126
|
+
// ---------------------------------------------------------------- config.toml
|
|
127
|
+
/**
|
|
128
|
+
* 启动器管的顶层键:每次启动重写;不在这里的键(包括 Codex 自己写的项目信任)原样保留。
|
|
129
|
+
* model 每次都写成「用户在 u1s1 config 里定的模型(仍在阵容里时),否则网关默认」:Codex 里用 /model 换的
|
|
130
|
+
* 模型只管那一次会话,和 u1s1 claude 一致(那边 --settings 的 model 也盖过客户端里 /model 的选择)。
|
|
131
|
+
*/
|
|
132
|
+
export const CODEX_MANAGED_KEYS = [
|
|
133
|
+
"model",
|
|
134
|
+
"model_provider",
|
|
135
|
+
"check_for_update_on_startup",
|
|
136
|
+
"cli_auth_credentials_store",
|
|
137
|
+
"model_catalog_json",
|
|
138
|
+
];
|
|
139
|
+
/**
|
|
140
|
+
* 跟着模型走的顶层键:Codex 的 /model 把它们和 model 一起写进 config.toml。启动器把 model 换掉时
|
|
141
|
+
* 一并去掉(回到新模型自己的默认),免得为别的模型选的思考强度带到新模型上;model 没变就保留。
|
|
142
|
+
*/
|
|
143
|
+
export const CODEX_MODEL_BOUND_KEYS = ["model_reasoning_effort"];
|
|
144
|
+
const MANAGED_HEADER = [
|
|
145
|
+
"# u1s1: the keys right below this comment and the [model_providers.u1s1] and [mcp_servers.u1s1] tables",
|
|
146
|
+
"# are rewritten every time u1s1 codex starts; everything else in this file is kept as is.",
|
|
147
|
+
];
|
|
148
|
+
/** 旧版的说明行(只提 provider 表),重写时一并去掉 */
|
|
149
|
+
const LEGACY_MANAGED_HEADER = [
|
|
150
|
+
"# u1s1: the keys right below this comment and the [model_providers.u1s1] table are rewritten",
|
|
151
|
+
"# every time u1s1 codex starts; everything else in this file is kept as is.",
|
|
152
|
+
];
|
|
153
|
+
const LEGACY_MANAGED_MARK = "# u1s1:";
|
|
154
|
+
const HEADER_RE = /^\s*\[\[?\s*([^\]]*?)\s*\]\]?\s*(?:#.*)?$/;
|
|
155
|
+
/** 表头名规范化:去掉各段的引号与空白(`model_providers."u1s1"` → `model_providers.u1s1`)。 */
|
|
156
|
+
function tableName(raw) {
|
|
157
|
+
return raw.split(".").map((part) => part.trim().replace(/^"(.*)"$/, "$1").replace(/^'(.*)'$/, "$1")).join(".");
|
|
158
|
+
}
|
|
159
|
+
function keyOf(line) {
|
|
160
|
+
const m = /^\s*("([^"]*)"|'([^']*)'|[A-Za-z0-9_.-]+)\s*=/.exec(line);
|
|
161
|
+
if (!m)
|
|
162
|
+
return undefined;
|
|
163
|
+
return m[2] ?? m[3] ?? m[1].split(".").map((p) => p.trim()).join(".");
|
|
164
|
+
}
|
|
165
|
+
/** `key = "值"` / `key = '值'` 的字符串值;别的写法返回 undefined。 */
|
|
166
|
+
function stringValueOf(line) {
|
|
167
|
+
const m = /=\s*(?:"((?:[^"\\]|\\.)*)"|'([^']*)')\s*(?:#.*)?$/.exec(line);
|
|
168
|
+
if (!m)
|
|
169
|
+
return undefined;
|
|
170
|
+
if (m[2] !== undefined)
|
|
171
|
+
return m[2];
|
|
172
|
+
try {
|
|
173
|
+
return JSON.parse(`"${m[1]}"`);
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
return undefined;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
/** 值是不是在这一行内结束的简单值(多行字符串、跨行数组/内联表都不算)。 */
|
|
180
|
+
function singleLineValue(line) {
|
|
181
|
+
const value = line.slice(line.indexOf("=") + 1).trim();
|
|
182
|
+
return !/^("""|'''|\[|\{)/.test(value);
|
|
183
|
+
}
|
|
184
|
+
function providerTable() {
|
|
185
|
+
return [
|
|
186
|
+
`[model_providers.${CODEX_PROVIDER_ID}]`,
|
|
187
|
+
`name = ${tomlString("u1s1")}`,
|
|
188
|
+
// 真正的地址每次由 -c 覆盖;直接用这个目录起 codex 时连不上,不会把请求发到别处
|
|
189
|
+
`base_url = ${tomlString("http://127.0.0.1:0/v1")}`,
|
|
190
|
+
`env_key = ${tomlString(CODEX_KEY_ENV)}`,
|
|
191
|
+
`env_key_instructions = ${tomlString(tr("请用 u1s1 codex 启动。", "Start it with u1s1 codex."))}`,
|
|
192
|
+
`wire_api = ${tomlString("responses")}`,
|
|
193
|
+
"requires_openai_auth = false",
|
|
194
|
+
];
|
|
195
|
+
}
|
|
196
|
+
function mcpTable(server) {
|
|
197
|
+
return [
|
|
198
|
+
`[mcp_servers.${MCP_SERVER_NAME}]`,
|
|
199
|
+
`command = ${tomlString(server.command)}`,
|
|
200
|
+
`args = [${server.args.map(tomlString).join(", ")}]`,
|
|
201
|
+
// 超时用 Codex 的默认值(0.167.2 源码:启动 30 秒、单次调用 300 秒;生图最长约 150 秒加下载,够用)
|
|
202
|
+
`env_vars = [${server.envVars.map(tomlString).join(", ")}]`,
|
|
203
|
+
];
|
|
204
|
+
}
|
|
205
|
+
/** 受管的表:我们整段重写的 `[model_providers.u1s1]`、`[mcp_servers.u1s1]` 及其子表。 */
|
|
206
|
+
const MANAGED_TABLES = [`model_providers.${CODEX_PROVIDER_ID}`, `mcp_servers.${MCP_SERVER_NAME}`];
|
|
207
|
+
function isManagedTable(section) {
|
|
208
|
+
return MANAGED_TABLES.some((t) => section === t || section.startsWith(`${t}.`));
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* `[mcp_servers.u1s1.tools.<工具>]`:用户在 Codex 里对某个工具选「总是允许」时,Codex 写在这里
|
|
212
|
+
* (0.167.2 实测写 `approval_mode = "approve"`)。注入时原样保留、放在我们的表后面;不注入时去掉
|
|
213
|
+
* (只剩子表没有 command,Codex 会当成配置不完整)。
|
|
214
|
+
*/
|
|
215
|
+
function isMcpToolTable(section) {
|
|
216
|
+
return section.startsWith(`mcp_servers.${MCP_SERVER_NAME}.tools.`);
|
|
217
|
+
}
|
|
218
|
+
function managedTopLevel(model) {
|
|
219
|
+
const lines = [...MANAGED_HEADER];
|
|
220
|
+
if (model.model)
|
|
221
|
+
lines.push(`model = ${tomlString(model.model)}`);
|
|
222
|
+
lines.push(`model_provider = ${tomlString(CODEX_PROVIDER_ID)}`,
|
|
223
|
+
// 更新由启动器管(max_tested_version 以内),不让它自己查、也不弹升级提示
|
|
224
|
+
"check_for_update_on_startup = false",
|
|
225
|
+
// 凭据(如果用户自己 codex login)写在 CODEX_HOME 的文件里,不碰系统钥匙串
|
|
226
|
+
`cli_auth_credentials_store = ${tomlString("file")}`);
|
|
227
|
+
if (model.catalogFile)
|
|
228
|
+
lines.push(`model_catalog_json = ${tomlString(model.catalogFile)}`);
|
|
229
|
+
return lines;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* 生成新的 config.toml。保守做法:不解析整份 TOML,只逐行处理我们管的东西——
|
|
233
|
+
* - 顶层(第一个表头之前)的受管键整行去掉,换成新值,放在文件最前面;
|
|
234
|
+
* - `[model_providers.u1s1]`、`[mcp_servers.u1s1]` 及其子表整段去掉,换成新的放在最后
|
|
235
|
+
* (例外:用户对 u1s1 工具的审批选择 `[mcp_servers.u1s1.tools.*]`,注入时跟在新表后面保留);
|
|
236
|
+
* - 其余每一行(用户加的键、别的表、注释、Codex 自己写的项目信任等)原样保留,顺序不变。
|
|
237
|
+
* 遇到没法逐行安全改写的写法(受管键是跨行值、在顶层或 [model_providers] 表里用点号/内联表写我们的
|
|
238
|
+
* provider)就整份重写,由调用方先把原文件备份。
|
|
239
|
+
*/
|
|
240
|
+
export function renderCodexConfig(existing, managed) {
|
|
241
|
+
const top = managedTopLevel(managed);
|
|
242
|
+
const table = [...providerTable(), ...(managed.mcp ? ["", ...mcpTable(managed.mcp)] : [])];
|
|
243
|
+
const fresh = () => [...top, "", ...table, ""].join("\n");
|
|
244
|
+
if (existing === undefined || existing.trim() === "")
|
|
245
|
+
return { text: fresh(), replaced: false };
|
|
246
|
+
const managedKeys = new Set(CODEX_MANAGED_KEYS);
|
|
247
|
+
const modelBound = new Set(CODEX_MODEL_BOUND_KEYS);
|
|
248
|
+
// 原文件顶层的 model;和这次要写的不同(或读不出)时,跟着模型走的键一起去掉
|
|
249
|
+
let previousModel;
|
|
250
|
+
for (const line of existing.replace(/\r\n/g, "\n").split("\n")) {
|
|
251
|
+
const header = HEADER_RE.exec(line);
|
|
252
|
+
if (header && !/^\s*#/.test(line))
|
|
253
|
+
break;
|
|
254
|
+
if (keyOf(line) === "model")
|
|
255
|
+
previousModel = stringValueOf(line);
|
|
256
|
+
}
|
|
257
|
+
const modelChanged = previousModel !== (managed.model ?? undefined);
|
|
258
|
+
const keptTop = [];
|
|
259
|
+
const keptTables = [];
|
|
260
|
+
const keptMcpTools = [];
|
|
261
|
+
let section = null; // null = 顶层
|
|
262
|
+
let skipping = false;
|
|
263
|
+
let inMcpTools = false;
|
|
264
|
+
let unsafe = false;
|
|
265
|
+
for (const line of existing.replace(/\r\n/g, "\n").split("\n")) {
|
|
266
|
+
const header = HEADER_RE.exec(line);
|
|
267
|
+
if (header && !/^\s*#/.test(line)) {
|
|
268
|
+
section = tableName(header[1] ?? "");
|
|
269
|
+
inMcpTools = isMcpToolTable(section);
|
|
270
|
+
skipping = !inMcpTools && isManagedTable(section);
|
|
271
|
+
if (inMcpTools)
|
|
272
|
+
keptMcpTools.push(line);
|
|
273
|
+
else if (!skipping)
|
|
274
|
+
keptTables.push(line);
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
if (skipping)
|
|
278
|
+
continue;
|
|
279
|
+
if (inMcpTools) {
|
|
280
|
+
keptMcpTools.push(line);
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
const key = keyOf(line);
|
|
284
|
+
if (section === null) {
|
|
285
|
+
if (line.startsWith(LEGACY_MANAGED_MARK) || MANAGED_HEADER.includes(line) || LEGACY_MANAGED_HEADER.includes(line))
|
|
286
|
+
continue;
|
|
287
|
+
if (key && managedKeys.has(key)) {
|
|
288
|
+
if (!singleLineValue(line))
|
|
289
|
+
unsafe = true;
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
if (key && modelChanged && modelBound.has(key) && singleLineValue(line))
|
|
293
|
+
continue;
|
|
294
|
+
if (key && (key === "model_providers" || key.startsWith("model_providers.")))
|
|
295
|
+
unsafe = true;
|
|
296
|
+
if (key && (key === "mcp_servers" || key.startsWith(`mcp_servers.${MCP_SERVER_NAME}`)))
|
|
297
|
+
unsafe = true;
|
|
298
|
+
keptTop.push(line);
|
|
299
|
+
}
|
|
300
|
+
else {
|
|
301
|
+
if (section === "model_providers" && key && (key === CODEX_PROVIDER_ID || key.startsWith(`${CODEX_PROVIDER_ID}.`)))
|
|
302
|
+
unsafe = true;
|
|
303
|
+
if (section === "mcp_servers" && key && (key === MCP_SERVER_NAME || key.startsWith(`${MCP_SERVER_NAME}.`)))
|
|
304
|
+
unsafe = true;
|
|
305
|
+
keptTables.push(line);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
if (unsafe)
|
|
309
|
+
return { text: fresh(), replaced: true };
|
|
310
|
+
const trimBlank = (lines) => {
|
|
311
|
+
while (lines.length && lines[0].trim() === "")
|
|
312
|
+
lines.shift();
|
|
313
|
+
while (lines.length && lines[lines.length - 1].trim() === "")
|
|
314
|
+
lines.pop();
|
|
315
|
+
return lines;
|
|
316
|
+
};
|
|
317
|
+
const parts = [top.join("\n")];
|
|
318
|
+
if (trimBlank(keptTop).length)
|
|
319
|
+
parts.push(keptTop.join("\n"));
|
|
320
|
+
if (trimBlank(keptTables).length)
|
|
321
|
+
parts.push(keptTables.join("\n"));
|
|
322
|
+
parts.push(table.join("\n"));
|
|
323
|
+
if (managed.mcp && trimBlank(keptMcpTools).length)
|
|
324
|
+
parts.push(keptMcpTools.join("\n"));
|
|
325
|
+
return { text: parts.join("\n\n") + "\n", replaced: false };
|
|
326
|
+
}
|
|
327
|
+
/** 网关 settings.model_catalog:必须是对象才写;CLI 不解析、不改它的内容。 */
|
|
328
|
+
export function modelCatalogFromConfig(config) {
|
|
329
|
+
const catalog = config.settings?.["model_catalog"];
|
|
330
|
+
return catalog && typeof catalog === "object" && !Array.isArray(catalog) ? catalog : undefined;
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* 写 model-catalog.json 与 config.toml。返回原 config.toml 被整份重写时的备份路径(给用户一句说明)。
|
|
334
|
+
*/
|
|
335
|
+
export function writeCodexConfig(paths, config, now = Date.now, mcp = null,
|
|
336
|
+
/** 这次启动用的模型(launch.ts 按 u1s1 config 选好);undefined = 网关默认 */
|
|
337
|
+
model = config.defaultModel) {
|
|
338
|
+
mkdirSync(paths.configDir, { recursive: true, mode: 0o700 });
|
|
339
|
+
const catalog = modelCatalogFromConfig(config);
|
|
340
|
+
if (catalog)
|
|
341
|
+
writePrivateFile(paths.catalogFile, JSON.stringify(catalog) + "\n");
|
|
342
|
+
else
|
|
343
|
+
rmSync(paths.catalogFile, { force: true });
|
|
344
|
+
let existing;
|
|
345
|
+
try {
|
|
346
|
+
existing = readFileSync(paths.configToml, "utf8");
|
|
347
|
+
}
|
|
348
|
+
catch {
|
|
349
|
+
existing = undefined;
|
|
350
|
+
}
|
|
351
|
+
const rendered = renderCodexConfig(existing, {
|
|
352
|
+
model,
|
|
353
|
+
catalogFile: catalog ? paths.catalogFile : null,
|
|
354
|
+
mcp,
|
|
355
|
+
});
|
|
356
|
+
let backup;
|
|
357
|
+
if (rendered.replaced && existing !== undefined && existsSync(paths.configToml)) {
|
|
358
|
+
backup = `${paths.configToml}.${now()}.bak`;
|
|
359
|
+
renameSync(paths.configToml, backup);
|
|
360
|
+
}
|
|
361
|
+
if (rendered.text !== existing)
|
|
362
|
+
writePrivateFile(paths.configToml, rendered.text);
|
|
363
|
+
return backup;
|
|
364
|
+
}
|
|
365
|
+
/** [mcp_servers.u1s1] 的内容:同一个 node、同一份 CLI;只列要转发的变量名。 */
|
|
366
|
+
export function codexMcpServer(command = mcpServerCommand(), env = process.env) {
|
|
367
|
+
const envVars = [...MCP_ENV_NAMES];
|
|
368
|
+
if (env[MCP_TRACE_ENV])
|
|
369
|
+
envVars.push(MCP_TRACE_ENV);
|
|
370
|
+
return { ...command, envVars };
|
|
371
|
+
}
|
|
372
|
+
// ---------------------------------------------------------------- run
|
|
373
|
+
/**
|
|
374
|
+
* `u1s1 codex [参数…]`。返回子进程的退出码(启动前就失败返回 1)。
|
|
375
|
+
* 是否可用完全由网关配置的 enabled 决定(prepareAgentLaunch)。
|
|
376
|
+
*/
|
|
377
|
+
export async function runCodex(userArgs) {
|
|
378
|
+
const paths = codexPaths();
|
|
379
|
+
const prepared = await prepareAgentLaunch(AGENT_PROFILES.codex, paths);
|
|
380
|
+
if (typeof prepared === "number")
|
|
381
|
+
return prepared;
|
|
382
|
+
const mcp = mcpInjectionDisabled()
|
|
383
|
+
? undefined
|
|
384
|
+
: 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);
|
|
386
|
+
if (backup) {
|
|
387
|
+
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
|
+
}
|
|
389
|
+
const env = codexLaunchEnv({ baseEnv: process.env, localToken: prepared.proxy.localKey, home: paths.configDir, mcp });
|
|
390
|
+
const args = codexLaunchArgs(new URL(prepared.proxy.baseUrl).origin, userArgs);
|
|
391
|
+
return runPreparedAgent({ agent: "codex", paths, prepared, args, env, userArgs });
|
|
392
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 官方原版客户端(`u1s1 claude` / `u1s1 codex`)共用的启动流程与文案:
|
|
3
|
+
* 登录 → 经签名代理拉网关配置(开关、刹车、阵容)→ 找到/下载原版 → 起声明了客户端的签名代理 →
|
|
4
|
+
* 由各客户端模块写配置、拼环境 → 子进程继承终端 → 透传退出码;顺带每天查一次新版。
|
|
5
|
+
*
|
|
6
|
+
* 各客户端只提供 AgentProfile(名字、条款、路径)和「怎么注入」,下载、刹车、文案都在这里一份。
|
|
7
|
+
*/
|
|
8
|
+
import { spawn } from "node:child_process";
|
|
9
|
+
import { type ApiFeatures } from "../api.js";
|
|
10
|
+
import { type SiteId } from "../site.js";
|
|
11
|
+
import { type AgentConfig, type GatewayAgentName } from "./agent-config.js";
|
|
12
|
+
import { RuntimePlanError, type DownloadInfo, type EnsuredRuntime, type InstallHooks, type ManagedAgent, type RuntimePaths, type SkippedRuntime } from "./runtime.js";
|
|
13
|
+
export declare function agentsRoot(): string;
|
|
14
|
+
export interface AgentProfile {
|
|
15
|
+
/** 子命令名,也是 ~/.u1s1/agents 下的目录名 */
|
|
16
|
+
agent: ManagedAgent;
|
|
17
|
+
/** 网关里的客户端名(/v1/agents/:agent/config、x-u1s1-agent) */
|
|
18
|
+
gatewayAgent: Extract<GatewayAgentName, "claude-code" | "codex">;
|
|
19
|
+
/** 产品名(只作事实陈述,如「官方 Claude Code」) */
|
|
20
|
+
product: string;
|
|
21
|
+
/** 下载前提示里的许可/条款一行 */
|
|
22
|
+
termsLine: () => string;
|
|
23
|
+
/** 下载前提示里的安装位置一行 */
|
|
24
|
+
locationLine: () => string;
|
|
25
|
+
}
|
|
26
|
+
export declare const CLAUDE_TERMS_URL = "https://code.claude.com/docs/en/legal-and-compliance";
|
|
27
|
+
export declare const CODEX_LICENSE_URL = "https://github.com/openai/codex/blob/main/LICENSE";
|
|
28
|
+
export declare const AGENT_PROFILES: Record<ManagedAgent, AgentProfile>;
|
|
29
|
+
/**
|
|
30
|
+
* 请求本机签名代理(http://127.0.0.1:<端口>)专用:直连,不走用户的 HTTP(S)_PROXY。
|
|
31
|
+
* 全局 dispatcher 认 HTTP_PROXY,用户没在 NO_PROXY 里写 127.0.0.1 时,连本机代理的请求也会交给
|
|
32
|
+
* 那个代理;远端代理连不到用户本机,首次启动(还没有配置缓存)就报「拿不到启动配置」。
|
|
33
|
+
* 签名代理到网关那一跳不受影响,照旧按用户的代理设置走。
|
|
34
|
+
*/
|
|
35
|
+
export declare function loopbackFetch(url: string, init: RequestInit): Promise<Response>;
|
|
36
|
+
/**
|
|
37
|
+
* 起子进程、继承终端、等它退出、透传退出码。
|
|
38
|
+
*
|
|
39
|
+
* 信号:Ctrl+C / Ctrl+\ 由终端直接发给整个前台进程组,子进程自己会收到;启动器只要不被它们
|
|
40
|
+
* 打死就行,不再转发(转发会让子进程收到两次,交互界面里「按两次 Ctrl+C 退出」就成了按一次)。
|
|
41
|
+
* SIGTERM / SIGHUP 通常只发给启动器(kill、关窗口),转发给子进程。
|
|
42
|
+
* 签名代理的监听是 unref 的:必须等子进程退出后再返回。
|
|
43
|
+
*/
|
|
44
|
+
export declare function spawnAgent(bin: string, args: readonly string[], env: NodeJS.ProcessEnv, deps?: {
|
|
45
|
+
spawn?: typeof spawn;
|
|
46
|
+
proc?: Pick<NodeJS.Process, "on" | "off">;
|
|
47
|
+
}): Promise<number>;
|
|
48
|
+
export declare function consentLines(info: DownloadInfo): string[];
|
|
49
|
+
export declare function cliInstallHooks(agent?: ManagedAgent): InstallHooks;
|
|
50
|
+
/** 已有的某份客户端高于 max_tested、这次不用它。 */
|
|
51
|
+
export declare function overMaxTestedText(s: SkippedRuntime, agent?: ManagedAgent): string;
|
|
52
|
+
/** ensureAgentRuntime 抛出的错误 → 给用户的几行说明。不认识的错误返回 false(调用方再抛)。 */
|
|
53
|
+
export declare function printRuntimeFailure(e: unknown, agent?: ManagedAgent): boolean;
|
|
54
|
+
export declare function notOpenText(): string;
|
|
55
|
+
export declare function runtimeErrorText(e: RuntimePlanError, agent?: ManagedAgent): string;
|
|
56
|
+
/** 运行时的两条附带提示:pin 被忽略、更新没装上。 */
|
|
57
|
+
export declare function printRuntimeNotes(runtime: EnsuredRuntime, agent: ManagedAgent): void;
|
|
58
|
+
export interface LaunchPaths extends RuntimePaths {
|
|
59
|
+
/** 客户端自己的配置目录(CLAUDE_CONFIG_DIR / CODEX_HOME);探测版本时也指到这里 */
|
|
60
|
+
configDir: string;
|
|
61
|
+
/** 上次成功拉到的 /v1/agents/:agent/config */
|
|
62
|
+
configCache: string;
|
|
63
|
+
}
|
|
64
|
+
export interface PreparedLaunch {
|
|
65
|
+
config: AgentConfig;
|
|
66
|
+
/** 这次启动用的模型:用户在 u1s1 config 里选的(仍在阵容里时),否则网关默认 */
|
|
67
|
+
model: string | null;
|
|
68
|
+
/** u1s1 config 的 <客户端>.auto-update;关着时不做每日查新 */
|
|
69
|
+
autoUpdate: boolean;
|
|
70
|
+
runtime: EnsuredRuntime;
|
|
71
|
+
/** 声明了客户端名与版本的签名代理 */
|
|
72
|
+
proxy: {
|
|
73
|
+
baseUrl: string;
|
|
74
|
+
localKey: string;
|
|
75
|
+
};
|
|
76
|
+
site: SiteId;
|
|
77
|
+
/** /v1/models 的能力开关(搜索、云端渲染、生图);没拉到时为 undefined。MCP 工具服务按它决定提供哪些工具 */
|
|
78
|
+
features: ApiFeatures | undefined;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* 子进程启动前的公共部分。返回 number = 启动前就失败的退出码(已向用户说明)。
|
|
82
|
+
* 是否可用完全由网关配置的 enabled 决定:停用时显示 disabled_reason,不下载、不启动。
|
|
83
|
+
*/
|
|
84
|
+
export declare function prepareAgentLaunch(profile: AgentProfile, paths: LaunchPaths): Promise<PreparedLaunch | number>;
|
|
85
|
+
/** 用户在 u1s1 config 里选的模型不在当前阵容里(只提示一次,见 prefs.ts)。 */
|
|
86
|
+
export declare function modelMissingText(agent: ManagedAgent, missing: string, fallback: string | null): string;
|
|
87
|
+
/** configDir 下这次运行(since 之后)写过的最新会话的 id。 */
|
|
88
|
+
export declare function latestSessionId(agent: ManagedAgent, configDir: string, since: number): string | undefined;
|
|
89
|
+
/**
|
|
90
|
+
* 只在交互界面正常退出后提示:脚本调用(codex exec、claude -p)客户端自己也不打印续接提示,
|
|
91
|
+
* 我们也不加。
|
|
92
|
+
*/
|
|
93
|
+
export declare function wantsResumeHint(agent: ManagedAgent, userArgs: readonly string[], interactive: boolean): boolean;
|
|
94
|
+
export declare function resumeHintText(agent: ManagedAgent, id: string): string;
|
|
95
|
+
/**
|
|
96
|
+
* 起子进程并等它退出;同时每天最多查一次新版(只管启动器自己装的那份),下次启动前更新,不挡启动。
|
|
97
|
+
* 用户关了自动更新(u1s1 config)就不查。
|
|
98
|
+
*/
|
|
99
|
+
export declare function runPreparedAgent(input: {
|
|
100
|
+
agent: ManagedAgent;
|
|
101
|
+
paths: RuntimePaths & {
|
|
102
|
+
configDir?: string;
|
|
103
|
+
};
|
|
104
|
+
/** 用户自己的参数(不含启动器加的);判断要不要补续接提示 */
|
|
105
|
+
userArgs?: readonly string[];
|
|
106
|
+
prepared: PreparedLaunch;
|
|
107
|
+
args: readonly string[];
|
|
108
|
+
env: NodeJS.ProcessEnv;
|
|
109
|
+
}): Promise<number>;
|