u1s1-cli 1.15.0 → 1.15.2
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/agents/claude.d.ts +33 -0
- package/dist/agents/claude.js +206 -28
- package/dist/agents/pi.js +3 -5
- package/dist/config.d.ts +20 -4
- package/dist/config.js +41 -8
- package/dist/index.js +3 -1
- package/dist/update.d.ts +15 -1
- package/dist/update.js +41 -7
- package/package.json +4 -2
package/dist/agents/claude.d.ts
CHANGED
|
@@ -65,15 +65,48 @@ export declare function withMcpPermissions(settings: Record<string, unknown>): R
|
|
|
65
65
|
* --settings 的优先级高于项目设置,在这里写回默认值。
|
|
66
66
|
*/
|
|
67
67
|
export declare const CLAUDE_SETTINGS_ENV_PINNED: Readonly<Record<string, string>>;
|
|
68
|
+
/** 去掉连接/凭证相关项(SETTINGS_FORBIDDEN、env 里的 SETTINGS_ENV_FORBIDDEN),钉上 CLAUDE_SETTINGS_ENV_PINNED。 */
|
|
69
|
+
export declare function sanitizeClaudeSettings(settings: Record<string, unknown>): Record<string, unknown>;
|
|
68
70
|
/**
|
|
69
71
|
* 网关的 settings → 写给 --settings 的对象:去掉连接/凭证相关项,钉上 CLAUDE_SETTINGS_ENV_PINNED;
|
|
70
72
|
* model 用调用方选好的(用户在 u1s1 config 里定的、仍在阵容里的模型),没给时保留网关的,
|
|
71
73
|
* 网关也没有就用 default_model。
|
|
72
74
|
*/
|
|
73
75
|
export declare function claudeSettingsFromConfig(config: AgentConfig, model?: string | null): Record<string, unknown>;
|
|
76
|
+
/** 调用方的 --settings 有问题(缺值、读不了、不是 JSON 对象);runClaude 报出来后以非 0 退出。 */
|
|
77
|
+
export declare class ClaudeSettingsArgError extends Error {
|
|
78
|
+
name: string;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Claude Code 的 --settings 只认最后一个(2.1.323 实测),用户参数里再带一个就把启动器的整份设置盖掉。
|
|
82
|
+
* 所以从用户参数里把它们全抽出来(`--settings <值>` 与 `--settings=<值>`,按出现顺序;`--` 之后不动),
|
|
83
|
+
* 由启动器合并进自己写的那份。值可以是文件路径,也可以是 JSON 字符串(与 Claude Code 一样)。
|
|
84
|
+
*/
|
|
85
|
+
export declare function extractSettingsArgs(userArgs: readonly string[]): {
|
|
86
|
+
rest: string[];
|
|
87
|
+
sources: string[];
|
|
88
|
+
};
|
|
89
|
+
/** 读一个 --settings 的值:以 `{` 开头按 JSON 字符串解析,否则当文件路径(相对 cwd)。必须是 JSON 对象。 */
|
|
90
|
+
export declare function loadCallerSettings(source: string, cwd?: string): Record<string, unknown>;
|
|
91
|
+
/**
|
|
92
|
+
* 把调用方的设置合并进启动器的:对象逐层合并;hooks 每个事件的数组拼接;其余数组拼接去重;
|
|
93
|
+
* 标量(含 model、statusLine 这类整项)以调用方为准。合并后调用方还要再过 sanitizeClaudeSettings 与
|
|
94
|
+
* withMcpPermissions,不能借此换凭证、换地址、改请求形状。
|
|
95
|
+
*/
|
|
96
|
+
export declare function mergeClaudeSettings(base: Record<string, unknown>, caller: Record<string, unknown>): Record<string, unknown>;
|
|
97
|
+
/** 启动器的设置 + 调用方的设置(按顺序)→ 清洗 → MCP 权限:写进 --settings 文件的最终内容。 */
|
|
98
|
+
export declare function finalClaudeSettings(launcher: Record<string, unknown>, callers: readonly Record<string, unknown>[], withMcp: boolean): Record<string, unknown>;
|
|
74
99
|
export declare function writeClaudeSettings(file: string, settings: Record<string, unknown>): void;
|
|
75
100
|
/** 桌面端复用:同一份注入逻辑(embed.ts 导出)。 */
|
|
76
101
|
export declare function agentLaunchEnv(agent: "claude", input: ClaudeLaunchInput): NodeJS.ProcessEnv;
|
|
102
|
+
/**
|
|
103
|
+
* 带调用方 --settings 时,合并结果写进这个进程自己的文件:Claude Code 会监视 --settings 文件、改了就重读,
|
|
104
|
+
* 几个进程共用一份的话,后起的会把先起的那个的 hooks(调用方按会话区分的回调)换成自己的。
|
|
105
|
+
* 没有调用方 --settings 时内容只取决于网关配置,仍用共用的 settingsFile。
|
|
106
|
+
*/
|
|
107
|
+
export declare function processSettingsFile(paths: Pick<ClaudePaths, "root">, pid?: number): string;
|
|
108
|
+
/** 删掉上次没来得及清理(进程被强杀)的每进程设置文件;进程还在的不动。 */
|
|
109
|
+
export declare function cleanStaleSettingsFiles(root: string, alive?: (pid: number) => boolean): void;
|
|
77
110
|
/**
|
|
78
111
|
* `u1s1 claude [参数…]`。返回子进程的退出码(启动前就失败返回 1)。
|
|
79
112
|
*/
|
package/dist/agents/claude.js
CHANGED
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
* ANTHROPIC_AUTH_TOKEN=<本地 token> 代理只认 Bearer;不能用 ANTHROPIC_API_KEY(发 x-api-key)
|
|
8
8
|
* DISABLE_AUTOUPDATER=1 更新由启动器管(runtime.ts)
|
|
9
9
|
* --settings <启动器写出的文件> 网关下发的模型阵容等;文件里没有任何凭证
|
|
10
|
+
* 调用方自己传的 --settings(文件或 JSON,可多个)不转给客户端,
|
|
11
|
+
* 按顺序合并进这份文件再清洗(Claude Code 只认最后一个 --settings),
|
|
12
|
+
* 见 extractSettingsArgs / mergeClaudeSettings
|
|
10
13
|
* --mcp-config <启动器写出的文件> u1s1 工具服务(搜索、读网页、生图,见 mcp-server.ts);
|
|
11
14
|
* 文件里只引用环境变量名,代理地址和本地 token 的值在子进程环境里
|
|
12
15
|
*
|
|
@@ -14,10 +17,10 @@
|
|
|
14
17
|
* 再显式写入上面几项;本地 token 只对本进程的签名代理有效,进程退出即作废。
|
|
15
18
|
* 不改二进制,不删除也不禁用它自带的登录方式。
|
|
16
19
|
*/
|
|
17
|
-
import { rmSync } from "node:fs";
|
|
18
|
-
import { join } from "node:path";
|
|
20
|
+
import { readdirSync, readFileSync, rmSync } from "node:fs";
|
|
21
|
+
import { join, resolve } from "node:path";
|
|
19
22
|
import { writePrivateFile } from "../config.js";
|
|
20
|
-
import { lang } from "../i18n.js";
|
|
23
|
+
import { lang, tr } from "../i18n.js";
|
|
21
24
|
import { MCP_ENV_NAMES, MCP_SERVER_NAME, MCP_TRACE_ENV, mcpInjectionDisabled, mcpLaunchEnv, mcpServerCommand, } from "../mcp-server.js";
|
|
22
25
|
import { AGENT_PROFILES, agentsRoot, prepareAgentLaunch, runPreparedAgent, } from "./launch.js";
|
|
23
26
|
import { runtimePaths } from "./runtime.js";
|
|
@@ -145,13 +148,17 @@ export function withMcpPermissions(settings) {
|
|
|
145
148
|
permissions["ask"] = mergeRules(permissions["ask"], CLAUDE_MCP_ASK_TOOLS);
|
|
146
149
|
return { ...settings, permissions };
|
|
147
150
|
}
|
|
148
|
-
/**
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
"
|
|
151
|
+
/**
|
|
152
|
+
* settings.env 里绝不能出现的键(契约上网关也不会发,这里再兜一层;调用方的 --settings 合并进来后同样过这一遍):
|
|
153
|
+
* CLAUDE_ENV_DROP 里换凭证、换地址、换云厂商、改 u1s1 工具服务连接的那些。模型变量可以由调用方改
|
|
154
|
+
* (等同于改 model);两个请求形状变量另由 CLAUDE_SETTINGS_ENV_PINNED 钉回默认值。
|
|
155
|
+
*/
|
|
156
|
+
const SETTINGS_ENV_ALLOWED_FROM_DROP = new Set([
|
|
157
|
+
"ANTHROPIC_MODEL",
|
|
158
|
+
"ANTHROPIC_SMALL_FAST_MODEL",
|
|
159
|
+
"ANTHROPIC_DEFAULT_OPUS_MODEL",
|
|
160
|
+
"ANTHROPIC_DEFAULT_SONNET_MODEL",
|
|
161
|
+
"ANTHROPIC_DEFAULT_HAIKU_MODEL",
|
|
155
162
|
]);
|
|
156
163
|
/** 会让客户端去别处取凭证的设置项。 */
|
|
157
164
|
const SETTINGS_FORBIDDEN = new Set(["apiKeyHelper"]);
|
|
@@ -165,35 +172,149 @@ export const CLAUDE_SETTINGS_ENV_PINNED = {
|
|
|
165
172
|
CLAUDE_CODE_ATTRIBUTION_HEADER: "1",
|
|
166
173
|
CLAUDE_CODE_EXTRA_BODY: "{}",
|
|
167
174
|
};
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
*/
|
|
173
|
-
export function
|
|
175
|
+
const SETTINGS_ENV_FORBIDDEN = new Set(CLAUDE_ENV_DROP.filter((key) => !SETTINGS_ENV_ALLOWED_FROM_DROP.has(key) && !(key in CLAUDE_SETTINGS_ENV_PINNED)));
|
|
176
|
+
function isPlainObject(value) {
|
|
177
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
178
|
+
}
|
|
179
|
+
/** 去掉连接/凭证相关项(SETTINGS_FORBIDDEN、env 里的 SETTINGS_ENV_FORBIDDEN),钉上 CLAUDE_SETTINGS_ENV_PINNED。 */
|
|
180
|
+
export function sanitizeClaudeSettings(settings) {
|
|
174
181
|
const out = {};
|
|
175
|
-
for (const [key, value] of Object.entries(
|
|
182
|
+
for (const [key, value] of Object.entries(settings)) {
|
|
176
183
|
if (!SETTINGS_FORBIDDEN.has(key))
|
|
177
184
|
out[key] = value;
|
|
178
185
|
}
|
|
179
|
-
const
|
|
180
|
-
if (
|
|
181
|
-
const env
|
|
182
|
-
for (const [key, value] of Object.entries(rawEnv)) {
|
|
186
|
+
const env = {};
|
|
187
|
+
if (isPlainObject(out["env"])) {
|
|
188
|
+
for (const [key, value] of Object.entries(out["env"])) {
|
|
183
189
|
if (!SETTINGS_ENV_FORBIDDEN.has(key))
|
|
184
190
|
env[key] = value;
|
|
185
191
|
}
|
|
186
|
-
out["env"] = { ...env, ...CLAUDE_SETTINGS_ENV_PINNED };
|
|
187
|
-
}
|
|
188
|
-
else {
|
|
189
|
-
out["env"] = { ...CLAUDE_SETTINGS_ENV_PINNED };
|
|
190
192
|
}
|
|
193
|
+
out["env"] = { ...env, ...CLAUDE_SETTINGS_ENV_PINNED };
|
|
194
|
+
return out;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* 网关的 settings → 写给 --settings 的对象:去掉连接/凭证相关项,钉上 CLAUDE_SETTINGS_ENV_PINNED;
|
|
198
|
+
* model 用调用方选好的(用户在 u1s1 config 里定的、仍在阵容里的模型),没给时保留网关的,
|
|
199
|
+
* 网关也没有就用 default_model。
|
|
200
|
+
*/
|
|
201
|
+
export function claudeSettingsFromConfig(config, model) {
|
|
202
|
+
const out = sanitizeClaudeSettings(config.settings ?? {});
|
|
191
203
|
if (model)
|
|
192
204
|
out["model"] = model;
|
|
193
205
|
else if (out["model"] === undefined && config.defaultModel)
|
|
194
206
|
out["model"] = config.defaultModel;
|
|
195
207
|
return out;
|
|
196
208
|
}
|
|
209
|
+
// ---------------------------------------------------------------- 调用方的 --settings
|
|
210
|
+
/** 调用方的 --settings 有问题(缺值、读不了、不是 JSON 对象);runClaude 报出来后以非 0 退出。 */
|
|
211
|
+
export class ClaudeSettingsArgError extends Error {
|
|
212
|
+
name = "ClaudeSettingsArgError";
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Claude Code 的 --settings 只认最后一个(2.1.323 实测),用户参数里再带一个就把启动器的整份设置盖掉。
|
|
216
|
+
* 所以从用户参数里把它们全抽出来(`--settings <值>` 与 `--settings=<值>`,按出现顺序;`--` 之后不动),
|
|
217
|
+
* 由启动器合并进自己写的那份。值可以是文件路径,也可以是 JSON 字符串(与 Claude Code 一样)。
|
|
218
|
+
*/
|
|
219
|
+
export function extractSettingsArgs(userArgs) {
|
|
220
|
+
const rest = [];
|
|
221
|
+
const sources = [];
|
|
222
|
+
for (let i = 0; i < userArgs.length; i++) {
|
|
223
|
+
const arg = userArgs[i];
|
|
224
|
+
if (arg === "--") {
|
|
225
|
+
rest.push(...userArgs.slice(i));
|
|
226
|
+
break;
|
|
227
|
+
}
|
|
228
|
+
if (arg === "--settings") {
|
|
229
|
+
const value = userArgs[i + 1];
|
|
230
|
+
if (value === undefined || value === "")
|
|
231
|
+
throw new ClaudeSettingsArgError(tr("--settings 后面缺少文件路径或 JSON。", "--settings needs a file path or a JSON string."));
|
|
232
|
+
sources.push(value);
|
|
233
|
+
i++;
|
|
234
|
+
}
|
|
235
|
+
else if (arg.startsWith("--settings=")) {
|
|
236
|
+
const value = arg.slice("--settings=".length);
|
|
237
|
+
if (value === "")
|
|
238
|
+
throw new ClaudeSettingsArgError(tr("--settings= 后面缺少文件路径或 JSON。", "--settings= needs a file path or a JSON string."));
|
|
239
|
+
sources.push(value);
|
|
240
|
+
}
|
|
241
|
+
else {
|
|
242
|
+
rest.push(arg);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return { rest, sources };
|
|
246
|
+
}
|
|
247
|
+
/** 读一个 --settings 的值:以 `{` 开头按 JSON 字符串解析,否则当文件路径(相对 cwd)。必须是 JSON 对象。 */
|
|
248
|
+
export function loadCallerSettings(source, cwd = process.cwd()) {
|
|
249
|
+
const inline = source.trimStart().startsWith("{");
|
|
250
|
+
let text;
|
|
251
|
+
if (inline) {
|
|
252
|
+
text = source;
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
const file = resolve(cwd, source);
|
|
256
|
+
try {
|
|
257
|
+
text = readFileSync(file, "utf8");
|
|
258
|
+
}
|
|
259
|
+
catch (e) {
|
|
260
|
+
const reason = e.code ?? e.message;
|
|
261
|
+
throw new ClaudeSettingsArgError(tr(`读不了 --settings 文件 ${file}(${reason})。`, `Couldn't read the --settings file ${file} (${reason}).`));
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
const what = inline ? tr("--settings 的 JSON", "The --settings JSON") : tr(`--settings 文件 ${resolve(cwd, source)}`, `The --settings file ${resolve(cwd, source)}`);
|
|
265
|
+
let parsed;
|
|
266
|
+
try {
|
|
267
|
+
parsed = JSON.parse(text);
|
|
268
|
+
}
|
|
269
|
+
catch (e) {
|
|
270
|
+
throw new ClaudeSettingsArgError(tr(`${what} 不是合法的 JSON:${e.message}`, `${what} is not valid JSON: ${e.message}`));
|
|
271
|
+
}
|
|
272
|
+
if (!isPlainObject(parsed))
|
|
273
|
+
throw new ClaudeSettingsArgError(tr(`${what} 必须是一个 JSON 对象。`, `${what} must be a JSON object.`));
|
|
274
|
+
return parsed;
|
|
275
|
+
}
|
|
276
|
+
function mergeArrays(base, extra, dedupe) {
|
|
277
|
+
if (!dedupe)
|
|
278
|
+
return [...base, ...extra];
|
|
279
|
+
const out = [...base];
|
|
280
|
+
const seen = new Set(out.map((item) => JSON.stringify(item)));
|
|
281
|
+
for (const item of extra) {
|
|
282
|
+
const key = JSON.stringify(item);
|
|
283
|
+
if (!seen.has(key)) {
|
|
284
|
+
seen.add(key);
|
|
285
|
+
out.push(item);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return out;
|
|
289
|
+
}
|
|
290
|
+
function mergeValue(base, extra, path) {
|
|
291
|
+
if (isPlainObject(base) && isPlainObject(extra)) {
|
|
292
|
+
const out = { ...base };
|
|
293
|
+
for (const [key, value] of Object.entries(extra)) {
|
|
294
|
+
if (key === "__proto__")
|
|
295
|
+
continue;
|
|
296
|
+
out[key] = Object.hasOwn(out, key) ? mergeValue(out[key], value, [...path, key]) : value;
|
|
297
|
+
}
|
|
298
|
+
return out;
|
|
299
|
+
}
|
|
300
|
+
// hooks.<事件> 按原样拼接(调用方的回调排在后面);其余数组(permissions.allow/ask/deny 等)拼接去重
|
|
301
|
+
if (Array.isArray(base) && Array.isArray(extra))
|
|
302
|
+
return mergeArrays(base, extra, !(path.length === 2 && path[0] === "hooks"));
|
|
303
|
+
return extra;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* 把调用方的设置合并进启动器的:对象逐层合并;hooks 每个事件的数组拼接;其余数组拼接去重;
|
|
307
|
+
* 标量(含 model、statusLine 这类整项)以调用方为准。合并后调用方还要再过 sanitizeClaudeSettings 与
|
|
308
|
+
* withMcpPermissions,不能借此换凭证、换地址、改请求形状。
|
|
309
|
+
*/
|
|
310
|
+
export function mergeClaudeSettings(base, caller) {
|
|
311
|
+
return mergeValue(base, caller, []);
|
|
312
|
+
}
|
|
313
|
+
/** 启动器的设置 + 调用方的设置(按顺序)→ 清洗 → MCP 权限:写进 --settings 文件的最终内容。 */
|
|
314
|
+
export function finalClaudeSettings(launcher, callers, withMcp) {
|
|
315
|
+
const merged = sanitizeClaudeSettings(callers.reduce(mergeClaudeSettings, launcher));
|
|
316
|
+
return withMcp ? withMcpPermissions(merged) : merged;
|
|
317
|
+
}
|
|
197
318
|
export function writeClaudeSettings(file, settings) {
|
|
198
319
|
writePrivateFile(file, JSON.stringify(settings, null, 2) + "\n");
|
|
199
320
|
}
|
|
@@ -204,10 +325,59 @@ export function agentLaunchEnv(agent, input) {
|
|
|
204
325
|
return claudeLaunchEnv(input);
|
|
205
326
|
}
|
|
206
327
|
// ---------------------------------------------------------------- run
|
|
328
|
+
const PROCESS_SETTINGS = /^settings-(\d+)\.json$/;
|
|
329
|
+
/**
|
|
330
|
+
* 带调用方 --settings 时,合并结果写进这个进程自己的文件:Claude Code 会监视 --settings 文件、改了就重读,
|
|
331
|
+
* 几个进程共用一份的话,后起的会把先起的那个的 hooks(调用方按会话区分的回调)换成自己的。
|
|
332
|
+
* 没有调用方 --settings 时内容只取决于网关配置,仍用共用的 settingsFile。
|
|
333
|
+
*/
|
|
334
|
+
export function processSettingsFile(paths, pid = process.pid) {
|
|
335
|
+
return join(paths.root, `settings-${pid}.json`);
|
|
336
|
+
}
|
|
337
|
+
function pidAlive(pid) {
|
|
338
|
+
try {
|
|
339
|
+
process.kill(pid, 0);
|
|
340
|
+
return true;
|
|
341
|
+
}
|
|
342
|
+
catch (e) {
|
|
343
|
+
return e.code === "EPERM";
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
/** 删掉上次没来得及清理(进程被强杀)的每进程设置文件;进程还在的不动。 */
|
|
347
|
+
export function cleanStaleSettingsFiles(root, alive = pidAlive) {
|
|
348
|
+
let names;
|
|
349
|
+
try {
|
|
350
|
+
names = readdirSync(root);
|
|
351
|
+
}
|
|
352
|
+
catch {
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
for (const name of names) {
|
|
356
|
+
const m = PROCESS_SETTINGS.exec(name);
|
|
357
|
+
if (m && !alive(Number(m[1])))
|
|
358
|
+
rmSync(join(root, name), { force: true });
|
|
359
|
+
}
|
|
360
|
+
}
|
|
207
361
|
/**
|
|
208
362
|
* `u1s1 claude [参数…]`。返回子进程的退出码(启动前就失败返回 1)。
|
|
209
363
|
*/
|
|
210
364
|
export async function runClaude(userArgs) {
|
|
365
|
+
// 调用方的 --settings 先读出来:有错在登录、联网之前就报。只查版本的快速路径原样不动。
|
|
366
|
+
let launchArgs = userArgs;
|
|
367
|
+
let callerSettings = [];
|
|
368
|
+
if (!isBareVersionQuery(userArgs)) {
|
|
369
|
+
try {
|
|
370
|
+
const { rest, sources } = extractSettingsArgs(userArgs);
|
|
371
|
+
launchArgs = rest;
|
|
372
|
+
callerSettings = sources.map((source) => loadCallerSettings(source));
|
|
373
|
+
}
|
|
374
|
+
catch (e) {
|
|
375
|
+
if (!(e instanceof ClaudeSettingsArgError))
|
|
376
|
+
throw e;
|
|
377
|
+
console.error(e.message);
|
|
378
|
+
return 1;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
211
381
|
const paths = claudePaths();
|
|
212
382
|
const prepared = await prepareAgentLaunch(AGENT_PROFILES.claude, paths);
|
|
213
383
|
if (typeof prepared === "number")
|
|
@@ -216,7 +386,9 @@ export async function runClaude(userArgs) {
|
|
|
216
386
|
? undefined
|
|
217
387
|
: mcpLaunchEnv({ proxyBaseUrl: prepared.proxy.baseUrl, localToken: prepared.proxy.localKey, features: prepared.features, lang: lang() });
|
|
218
388
|
const settings = claudeSettingsFromConfig(prepared.config, prepared.model);
|
|
219
|
-
|
|
389
|
+
cleanStaleSettingsFiles(paths.root);
|
|
390
|
+
const settingsFile = callerSettings.length ? processSettingsFile(paths) : paths.settingsFile;
|
|
391
|
+
writeClaudeSettings(settingsFile, finalClaudeSettings(settings, callerSettings, !!mcp));
|
|
220
392
|
if (mcp)
|
|
221
393
|
writePrivateFile(paths.mcpConfigFile, JSON.stringify(claudeMcpConfig(), null, 2) + "\n");
|
|
222
394
|
else
|
|
@@ -228,6 +400,12 @@ export async function runClaude(userArgs) {
|
|
|
228
400
|
configDir: paths.configDir,
|
|
229
401
|
mcp,
|
|
230
402
|
});
|
|
231
|
-
const args = claudeLaunchArgs(
|
|
232
|
-
|
|
403
|
+
const args = claudeLaunchArgs(settingsFile, launchArgs, mcp ? paths.mcpConfigFile : undefined);
|
|
404
|
+
try {
|
|
405
|
+
return await runPreparedAgent({ agent: "claude", paths, prepared, args, env, userArgs });
|
|
406
|
+
}
|
|
407
|
+
finally {
|
|
408
|
+
if (settingsFile !== paths.settingsFile)
|
|
409
|
+
rmSync(settingsFile, { force: true });
|
|
410
|
+
}
|
|
233
411
|
}
|
package/dist/agents/pi.js
CHANGED
|
@@ -20,7 +20,7 @@ import { deployHintText, markNudge, shouldShowDeployHint } from "../nudges.js";
|
|
|
20
20
|
import { createGatewayTelemetry } from "../telemetry.js";
|
|
21
21
|
import { installCommand, siteLinks, siteOf } from "../site.js";
|
|
22
22
|
import { lang, tr } from "../i18n.js";
|
|
23
|
-
|
|
23
|
+
import { globalUpdateCommand, latestVersionUrl } from "../update.js";
|
|
24
24
|
/** 启动时检测到的可自动安装的新版;TUI 退出后才装(见 installPendingUpdate)。 */
|
|
25
25
|
let pendingUpdate;
|
|
26
26
|
/**
|
|
@@ -36,7 +36,7 @@ let pendingUpdate;
|
|
|
36
36
|
async function checkForUpdate() {
|
|
37
37
|
let latest;
|
|
38
38
|
try {
|
|
39
|
-
const url =
|
|
39
|
+
const url = latestVersionUrl();
|
|
40
40
|
const res = await fetch(url, {
|
|
41
41
|
headers: { Accept: "application/json" },
|
|
42
42
|
signal: AbortSignal.timeout(5_000),
|
|
@@ -82,9 +82,7 @@ async function checkForUpdate() {
|
|
|
82
82
|
: userAgent.startsWith("yarn") ? "yarn"
|
|
83
83
|
: userAgent.startsWith("bun") ? "bun"
|
|
84
84
|
: "npm";
|
|
85
|
-
const installCmd = pm
|
|
86
|
-
? `npm install -g --loglevel=error ${PACKAGE_NAME}@latest`
|
|
87
|
-
: `${pm} add -g ${PACKAGE_NAME}@latest`;
|
|
85
|
+
const installCmd = globalUpdateCommand(pm);
|
|
88
86
|
pendingUpdate = { latest, installCmd };
|
|
89
87
|
setUpdateNotice(tr(`⬆ 发现新版 v${latest},退出后自动更新`, `⬆ v${latest} available, will update on exit`));
|
|
90
88
|
}
|
package/dist/config.d.ts
CHANGED
|
@@ -1,13 +1,23 @@
|
|
|
1
1
|
import { type webcrypto } from "node:crypto";
|
|
2
|
-
import { type Site } from "./site.js";
|
|
2
|
+
import { type Site, type SiteId } from "./site.js";
|
|
3
3
|
export declare const VERSION: string;
|
|
4
|
+
/**
|
|
5
|
+
* npm 上同一份构建发两个包(scripts/make-cli-npm-packages.mjs):u1s1-cli 默认国际站,
|
|
6
|
+
* u1s1-cli-cn 默认中国站,只差 package.json 的 name 和 u1s1.defaultSite。仓库、便携包、
|
|
7
|
+
* 桌面版内嵌的 package.json 不带标记,按中国站(老行为)。升级/查新版都用自己的包名。
|
|
8
|
+
*/
|
|
9
|
+
export declare const PACKAGE_NAME: string;
|
|
10
|
+
export declare const PACKAGE_DEFAULT_SITE: SiteId;
|
|
4
11
|
/**
|
|
5
12
|
* 便携包安装(install.sh / install.ps1):包根旁边带自己的 node 运行时,
|
|
6
13
|
* npm 更新碰不到这份拷贝,升级只能整包重装。npm 全局安装的包在 node_modules
|
|
7
14
|
* 下,按父目录名先排除,避免撞上恰好叫 node 的目录误判。
|
|
8
15
|
*/
|
|
9
16
|
export declare function isPortableInstall(): boolean;
|
|
10
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* 本机还没有 ~/.u1s1/config.json 时连的站(包标记决定)。配置文件已存在但没写 baseUrl 的
|
|
19
|
+
* 一律是中国站:中国站历来不写 baseUrl,翻默认站不能把存量用户和他们的凭证带去国际站。
|
|
20
|
+
*/
|
|
11
21
|
export declare const DEFAULT_BASE_URL: string;
|
|
12
22
|
export declare const PROVIDER_ID = "u1s1";
|
|
13
23
|
export declare const THINKING_LEVELS: readonly ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
@@ -225,12 +235,18 @@ export declare function resolvePreferredModel(cfg: Pick<CliConfig, "model" | "mo
|
|
|
225
235
|
export declare function loadConfig(): CliConfig;
|
|
226
236
|
/** 只看配置文件、不叠加环境变量覆盖的配置(判断「文件里存的是哪个站」用)。 */
|
|
227
237
|
export declare function loadFileConfig(): CliConfig;
|
|
238
|
+
/**
|
|
239
|
+
* 配置文件在、却没写 baseUrl(老中国站用户)→ 显式写成中国站固定下来,以后换装
|
|
240
|
+
* 默认国际站的包也不会把人带走。只补这一个字段,其它原样保留;读不懂的文件不碰。
|
|
241
|
+
*/
|
|
242
|
+
export declare function pinLegacySiteBaseUrl(): void;
|
|
228
243
|
/**
|
|
229
244
|
* 配置文件内容 + 环境变量 → 生效配置。环境变量优先于文件;文件里的历代中国站
|
|
230
245
|
* API 根换成当前中国站 apiBase(migrateStoredBaseUrl),下次 saveConfig 时落盘。
|
|
231
|
-
*
|
|
246
|
+
* 都没有 baseUrl 时用 fallback 站(见 fallbackSite)。
|
|
247
|
+
* env / currentCnApiBase / fallback 可注入,供测试模拟 P1 换主域后的迁移和不同包的默认站。
|
|
232
248
|
*/
|
|
233
|
-
export declare function configFromFile(file: Partial<CliConfig>, env?: NodeJS.ProcessEnv, currentCnApiBase?: string): CliConfig;
|
|
249
|
+
export declare function configFromFile(file: Partial<CliConfig>, env?: NodeJS.ProcessEnv, currentCnApiBase?: string, fallback?: SiteId): CliConfig;
|
|
234
250
|
/**
|
|
235
251
|
* 当前生效站点(按 loadConfig().baseUrl 判定,含 U1S1_BASE_URL 覆盖)。手里没有 cfg 的
|
|
236
252
|
* 文案/链接(模块级提示、生成的扩展、错误人话化)用它;有 cfg 的地方直接 siteOf(cfg)。
|
package/dist/config.js
CHANGED
|
@@ -7,7 +7,15 @@ import { fileURLToPath } from "node:url";
|
|
|
7
7
|
import { resetLang, setLangResolver, tr } from "./i18n.js";
|
|
8
8
|
import { migrateStoredBaseUrl, SITES, siteOf } from "./site.js";
|
|
9
9
|
const require = createRequire(import.meta.url);
|
|
10
|
-
|
|
10
|
+
const pkg = require("../package.json");
|
|
11
|
+
export const VERSION = pkg.version;
|
|
12
|
+
/**
|
|
13
|
+
* npm 上同一份构建发两个包(scripts/make-cli-npm-packages.mjs):u1s1-cli 默认国际站,
|
|
14
|
+
* u1s1-cli-cn 默认中国站,只差 package.json 的 name 和 u1s1.defaultSite。仓库、便携包、
|
|
15
|
+
* 桌面版内嵌的 package.json 不带标记,按中国站(老行为)。升级/查新版都用自己的包名。
|
|
16
|
+
*/
|
|
17
|
+
export const PACKAGE_NAME = typeof pkg.name === "string" && /^[a-z0-9][a-z0-9._-]*$/.test(pkg.name) ? pkg.name : "u1s1-cli";
|
|
18
|
+
export const PACKAGE_DEFAULT_SITE = pkg.u1s1?.defaultSite === "intl" ? "intl" : "cn";
|
|
11
19
|
/**
|
|
12
20
|
* 便携包安装(install.sh / install.ps1):包根旁边带自己的 node 运行时,
|
|
13
21
|
* npm 更新碰不到这份拷贝,升级只能整包重装。npm 全局安装的包在 node_modules
|
|
@@ -22,8 +30,11 @@ export function isPortableInstall() {
|
|
|
22
30
|
: join(pkgRoot, "..", "node", "bin", "node");
|
|
23
31
|
return existsSync(portableNode);
|
|
24
32
|
}
|
|
25
|
-
/**
|
|
26
|
-
|
|
33
|
+
/**
|
|
34
|
+
* 本机还没有 ~/.u1s1/config.json 时连的站(包标记决定)。配置文件已存在但没写 baseUrl 的
|
|
35
|
+
* 一律是中国站:中国站历来不写 baseUrl,翻默认站不能把存量用户和他们的凭证带去国际站。
|
|
36
|
+
*/
|
|
37
|
+
export const DEFAULT_BASE_URL = SITES[PACKAGE_DEFAULT_SITE].apiBase;
|
|
27
38
|
export const PROVIDER_ID = "u1s1";
|
|
28
39
|
/**
|
|
29
40
|
* Make a short alias from a model id by stripping common prefixes:厂商前缀
|
|
@@ -336,19 +347,39 @@ export function resolvePreferredModel(cfg) {
|
|
|
336
347
|
}
|
|
337
348
|
return { provider: PROVIDER_ID, id: defaultModelId() };
|
|
338
349
|
}
|
|
350
|
+
/** 配置里没写 baseUrl 时按哪个站:没有配置文件 → 包的默认站;有文件(老中国站用户)→ 中国站。 */
|
|
351
|
+
function fallbackSite() {
|
|
352
|
+
return existsSync(configFile) ? "cn" : PACKAGE_DEFAULT_SITE;
|
|
353
|
+
}
|
|
339
354
|
export function loadConfig() {
|
|
340
|
-
return configFromFile((readJsonFile(configFile) ?? {}));
|
|
355
|
+
return configFromFile((readJsonFile(configFile) ?? {}), process.env, SITES.cn.apiBase, fallbackSite());
|
|
341
356
|
}
|
|
342
357
|
/** 只看配置文件、不叠加环境变量覆盖的配置(判断「文件里存的是哪个站」用)。 */
|
|
343
358
|
export function loadFileConfig() {
|
|
344
|
-
return configFromFile((readJsonFile(configFile) ?? {}), {});
|
|
359
|
+
return configFromFile((readJsonFile(configFile) ?? {}), {}, SITES.cn.apiBase, fallbackSite());
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* 配置文件在、却没写 baseUrl(老中国站用户)→ 显式写成中国站固定下来,以后换装
|
|
363
|
+
* 默认国际站的包也不会把人带走。只补这一个字段,其它原样保留;读不懂的文件不碰。
|
|
364
|
+
*/
|
|
365
|
+
export function pinLegacySiteBaseUrl() {
|
|
366
|
+
const file = readJsonFile(configFile);
|
|
367
|
+
if (!file || Array.isArray(file) || file.baseUrl)
|
|
368
|
+
return;
|
|
369
|
+
try {
|
|
370
|
+
writePrivateFile(configFile, JSON.stringify({ ...file, baseUrl: SITES.cn.apiBase }, null, 2) + "\n");
|
|
371
|
+
}
|
|
372
|
+
catch {
|
|
373
|
+
// 写不进去也照样按中国站判定(fallbackSite),下次启动再试
|
|
374
|
+
}
|
|
345
375
|
}
|
|
346
376
|
/**
|
|
347
377
|
* 配置文件内容 + 环境变量 → 生效配置。环境变量优先于文件;文件里的历代中国站
|
|
348
378
|
* API 根换成当前中国站 apiBase(migrateStoredBaseUrl),下次 saveConfig 时落盘。
|
|
349
|
-
*
|
|
379
|
+
* 都没有 baseUrl 时用 fallback 站(见 fallbackSite)。
|
|
380
|
+
* env / currentCnApiBase / fallback 可注入,供测试模拟 P1 换主域后的迁移和不同包的默认站。
|
|
350
381
|
*/
|
|
351
|
-
export function configFromFile(file, env = process.env, currentCnApiBase = SITES.cn.apiBase) {
|
|
382
|
+
export function configFromFile(file, env = process.env, currentCnApiBase = SITES.cn.apiBase, fallback = "cn") {
|
|
352
383
|
const envJwk = (name) => {
|
|
353
384
|
const raw = env[name];
|
|
354
385
|
if (!raw)
|
|
@@ -366,7 +397,9 @@ export function configFromFile(file, env = process.env, currentCnApiBase = SITES
|
|
|
366
397
|
deviceId: file.deviceId,
|
|
367
398
|
devicePrivateJwk: envJwk("U1S1_DEVICE_PRIVATE_JWK") || file.devicePrivateJwk,
|
|
368
399
|
devicePublicJwk: envJwk("U1S1_DEVICE_PUBLIC_JWK") || file.devicePublicJwk,
|
|
369
|
-
baseUrl: env["U1S1_BASE_URL"]
|
|
400
|
+
baseUrl: env["U1S1_BASE_URL"]
|
|
401
|
+
|| migrateStoredBaseUrl(file.baseUrl, currentCnApiBase)
|
|
402
|
+
|| (fallback === "intl" ? SITES.intl.apiBase : currentCnApiBase),
|
|
370
403
|
// 有效性不在这里裁决:端点列表可能还没拉回来,交给 resolvePreferredModel
|
|
371
404
|
model: file.model,
|
|
372
405
|
modelProvider: typeof file.modelProvider === "string" ? file.modelProvider : undefined,
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { printConsoleBanner } from "./brand.js";
|
|
3
|
-
import { activeSite, loadConfig, readSettings, VERSION } from "./config.js";
|
|
3
|
+
import { activeSite, loadConfig, pinLegacySiteBaseUrl, readSettings, VERSION } from "./config.js";
|
|
4
4
|
import { siteLinks, siteOf } from "./site.js";
|
|
5
5
|
import { isEnglish, tr } from "./i18n.js";
|
|
6
6
|
import { isLauncherWord, launcherHelpLines, launcherHelpRows, runLauncher } from "./launcher.js";
|
|
@@ -229,6 +229,8 @@ function rejectMistypedCommand(cmd, argCount) {
|
|
|
229
229
|
return true;
|
|
230
230
|
}
|
|
231
231
|
async function run() {
|
|
232
|
+
// 老中国站配置(有文件没 baseUrl)先钉成中国站,再做任何按站点的判断
|
|
233
|
+
pinLegacySiteBaseUrl();
|
|
232
234
|
const args = process.argv.slice(2);
|
|
233
235
|
const cmd = args[0];
|
|
234
236
|
const rest = args.slice(1);
|
package/dist/update.d.ts
CHANGED
|
@@ -1,7 +1,21 @@
|
|
|
1
|
+
import { PACKAGE_NAME } from "./config.js";
|
|
1
2
|
export declare const VERSION: string;
|
|
2
|
-
export
|
|
3
|
+
export { PACKAGE_NAME };
|
|
4
|
+
/** 中国站的 npm 包名;u1s1-cli 自 1.15 起是国际站的包(见 config.ts PACKAGE_NAME)。 */
|
|
5
|
+
export declare const CN_PACKAGE_NAME = "u1s1-cli-cn";
|
|
3
6
|
/** Detect the package manager that installed u1s1. */
|
|
4
7
|
export declare function detectPackageManager(): string;
|
|
8
|
+
/** npm registry 上本包 latest 的元数据地址(两个包各查各的)。 */
|
|
9
|
+
export declare function latestVersionUrl(packageName?: string): string;
|
|
10
|
+
/** 全局升级本包的包管理器参数(execFileSync 用,不经 shell)。 */
|
|
11
|
+
export declare function globalUpdateArgs(pm: string, packageName?: string): string[];
|
|
12
|
+
/** 同上,拼成一行命令(退出后自动更新、给用户看的提示用)。 */
|
|
13
|
+
export declare function globalUpdateCommand(pm: string, packageName?: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* 装的是默认国际站的 u1s1-cli、连的却是中国站(老用户):提示改装 u1s1-cli-cn,只提示不自动换包。
|
|
16
|
+
* 两个包都提供 u1s1 命令,要先卸再装,否则 npm 报 bin 冲突。返回要打印的行;不需要提示时为空。
|
|
17
|
+
*/
|
|
18
|
+
export declare function cnPackageHint(pm: string, siteId: string, packageName?: string, defaultSite?: string): string[];
|
|
5
19
|
/** Fetch the latest published version from npm registry. */
|
|
6
20
|
export declare function getLatestVersion(): Promise<string | undefined>;
|
|
7
21
|
export declare function compareVersions(a: string, b: string): number;
|
package/dist/update.js
CHANGED
|
@@ -5,14 +5,16 @@ import { createRequire } from "node:module";
|
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
6
|
import { dirname, join } from "node:path";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
|
-
import { activeSite, isPortableInstall } from "./config.js";
|
|
8
|
+
import { activeSite, isPortableInstall, PACKAGE_DEFAULT_SITE, PACKAGE_NAME } from "./config.js";
|
|
9
9
|
import { tr } from "./i18n.js";
|
|
10
10
|
import { installCommand } from "./site.js";
|
|
11
11
|
import { readJsonResponseCapped, readResponseTextCapped } from "./api.js";
|
|
12
12
|
const require = createRequire(import.meta.url);
|
|
13
13
|
const pkg = require("../package.json");
|
|
14
14
|
export const VERSION = pkg.version;
|
|
15
|
-
export
|
|
15
|
+
export { PACKAGE_NAME };
|
|
16
|
+
/** 中国站的 npm 包名;u1s1-cli 自 1.15 起是国际站的包(见 config.ts PACKAGE_NAME)。 */
|
|
17
|
+
export const CN_PACKAGE_NAME = "u1s1-cli-cn";
|
|
16
18
|
const MAX_NPM_METADATA_BYTES = 64 * 1024;
|
|
17
19
|
const MAX_INSTALL_SCRIPT_BYTES = 1024 * 1024;
|
|
18
20
|
function publishedVersion(value) {
|
|
@@ -39,9 +41,39 @@ export function detectPackageManager() {
|
|
|
39
41
|
return "bun";
|
|
40
42
|
return "npm";
|
|
41
43
|
}
|
|
44
|
+
/** npm registry 上本包 latest 的元数据地址(两个包各查各的)。 */
|
|
45
|
+
export function latestVersionUrl(packageName = PACKAGE_NAME) {
|
|
46
|
+
return `https://registry.npmjs.org/${packageName}/latest`;
|
|
47
|
+
}
|
|
48
|
+
/** 全局升级本包的包管理器参数(execFileSync 用,不经 shell)。 */
|
|
49
|
+
export function globalUpdateArgs(pm, packageName = PACKAGE_NAME) {
|
|
50
|
+
return pm === "npm"
|
|
51
|
+
? ["install", "-g", "--loglevel=error", `${packageName}@latest`]
|
|
52
|
+
: ["add", "-g", `${packageName}@latest`];
|
|
53
|
+
}
|
|
54
|
+
/** 同上,拼成一行命令(退出后自动更新、给用户看的提示用)。 */
|
|
55
|
+
export function globalUpdateCommand(pm, packageName = PACKAGE_NAME) {
|
|
56
|
+
return [pm, ...globalUpdateArgs(pm, packageName)].join(" ");
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* 装的是默认国际站的 u1s1-cli、连的却是中国站(老用户):提示改装 u1s1-cli-cn,只提示不自动换包。
|
|
60
|
+
* 两个包都提供 u1s1 命令,要先卸再装,否则 npm 报 bin 冲突。返回要打印的行;不需要提示时为空。
|
|
61
|
+
*/
|
|
62
|
+
export function cnPackageHint(pm, siteId, packageName = PACKAGE_NAME, defaultSite = PACKAGE_DEFAULT_SITE) {
|
|
63
|
+
if (siteId !== "cn" || defaultSite !== "intl" || packageName === CN_PACKAGE_NAME)
|
|
64
|
+
return [];
|
|
65
|
+
const remove = pm === "npm" ? `npm uninstall -g ${packageName}` : `${pm} remove -g ${packageName}`;
|
|
66
|
+
const add = pm === "npm" ? `npm install -g ${CN_PACKAGE_NAME}` : `${pm} add -g ${CN_PACKAGE_NAME}`;
|
|
67
|
+
return [
|
|
68
|
+
tr(`提示:你连的是中国站。npm 包 ${packageName} 现在默认国际站,中国站专用包是 ${CN_PACKAGE_NAME},建议改装(登录状态和设置都保留):`, `Note: you're on the China site. The npm package ${packageName} now defaults to the international site; the China site package is ${CN_PACKAGE_NAME}. We suggest switching (your sign-in and settings are kept):`),
|
|
69
|
+
` ${remove}`,
|
|
70
|
+
` ${add}`,
|
|
71
|
+
tr(" 不改装也能照常用,当前仍连中国站。", " You can keep using this package; it still connects to the China site."),
|
|
72
|
+
];
|
|
73
|
+
}
|
|
42
74
|
/** Fetch the latest published version from npm registry. */
|
|
43
75
|
export async function getLatestVersion() {
|
|
44
|
-
const url =
|
|
76
|
+
const url = latestVersionUrl();
|
|
45
77
|
try {
|
|
46
78
|
const res = await fetch(url, {
|
|
47
79
|
headers: { Accept: "application/json" },
|
|
@@ -211,6 +243,11 @@ async function portableSelfUpdate(latest) {
|
|
|
211
243
|
}
|
|
212
244
|
}
|
|
213
245
|
export async function update() {
|
|
246
|
+
if (!isPortableInstall()) {
|
|
247
|
+
const hint = cnPackageHint(detectPackageManager(), activeSite().id);
|
|
248
|
+
if (hint.length)
|
|
249
|
+
console.log(hint.join("\n") + "\n");
|
|
250
|
+
}
|
|
214
251
|
console.log(tr("正在检查更新…", "Checking for updates…"));
|
|
215
252
|
const latest = await getLatestVersion();
|
|
216
253
|
if (!latest) {
|
|
@@ -246,10 +283,7 @@ export async function update() {
|
|
|
246
283
|
console.log(tr(`正在用 ${pm} 更新 ${PACKAGE_NAME}…`, `Updating ${PACKAGE_NAME} with ${pm}…`));
|
|
247
284
|
try {
|
|
248
285
|
// npm 压掉 EBADENGINE 等警告墙,对新手只有噪音;出错时 error 仍会显示
|
|
249
|
-
|
|
250
|
-
? ["install", "-g", "--loglevel=error", `${PACKAGE_NAME}@latest`]
|
|
251
|
-
: ["add", "-g", `${PACKAGE_NAME}@latest`];
|
|
252
|
-
execFileSync(pm, args, { stdio: "inherit" });
|
|
286
|
+
execFileSync(pm, globalUpdateArgs(pm), { stdio: "inherit" });
|
|
253
287
|
console.log(tr(`\n✅ 已更新到 v${latest},重启 u1s1 后生效。`, `\n✅ Updated to v${latest}. Restart u1s1 to use it.`));
|
|
254
288
|
}
|
|
255
289
|
catch {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "u1s1-cli",
|
|
3
|
-
"version": "1.15.
|
|
3
|
+
"version": "1.15.2",
|
|
4
4
|
"description": "u1s1 — 有一说一,最省心的 AI 编程搭子。终端里用中文说需求,AI 帮你读文件、改代码、跑命令。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -25,7 +25,6 @@
|
|
|
25
25
|
"build": "tsc",
|
|
26
26
|
"dev": "tsx src/index.ts",
|
|
27
27
|
"typecheck": "tsc --noEmit",
|
|
28
|
-
"prepublishOnly": "npm run build && npm test",
|
|
29
28
|
"test": "U1S1_LANG=zh tsx --test test/*.test.ts && U1S1_LANG=zh node scripts/render-regression.mjs",
|
|
30
29
|
"postinstall": "node scripts/patch-pi.js",
|
|
31
30
|
"package": "bash ../../scripts/package.sh",
|
|
@@ -56,5 +55,8 @@
|
|
|
56
55
|
"@types/node": "^22.20.1",
|
|
57
56
|
"tsx": "^4.23.12",
|
|
58
57
|
"typescript": "^5.9.3"
|
|
58
|
+
},
|
|
59
|
+
"u1s1": {
|
|
60
|
+
"defaultSite": "cn"
|
|
59
61
|
}
|
|
60
62
|
}
|