u1s1-cli 1.14.6 → 1.15.1

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 (44) hide show
  1. package/dist/agent-setup.d.ts +7 -0
  2. package/dist/agent-setup.js +14 -0
  3. package/dist/agents/agent-config.d.ts +83 -0
  4. package/dist/agents/agent-config.js +154 -0
  5. package/dist/agents/claude.d.ts +113 -0
  6. package/dist/agents/claude.js +411 -0
  7. package/dist/agents/codex.d.ts +111 -0
  8. package/dist/agents/codex.js +392 -0
  9. package/dist/agents/launch.d.ts +109 -0
  10. package/dist/agents/launch.js +444 -0
  11. package/dist/agents/pi.d.ts +9 -0
  12. package/dist/agents/pi.js +562 -0
  13. package/dist/agents/prefs.d.ts +35 -0
  14. package/dist/agents/prefs.js +96 -0
  15. package/dist/agents/runtime.d.ts +354 -0
  16. package/dist/agents/runtime.js +884 -0
  17. package/dist/agents/status.d.ts +20 -0
  18. package/dist/agents/status.js +241 -0
  19. package/dist/config-command.d.ts +18 -0
  20. package/dist/config-command.js +335 -0
  21. package/dist/config.d.ts +17 -0
  22. package/dist/config.js +35 -1
  23. package/dist/device-auth.d.ts +37 -1
  24. package/dist/device-auth.js +43 -8
  25. package/dist/embed.d.ts +13 -0
  26. package/dist/embed.js +15 -0
  27. package/dist/index.js +22 -556
  28. package/dist/launcher.d.ts +48 -0
  29. package/dist/launcher.js +235 -0
  30. package/dist/mcp-server.d.ts +105 -0
  31. package/dist/mcp-server.js +449 -0
  32. package/dist/pi-ui-text.d.ts +57 -0
  33. package/dist/pi-ui-text.js +148 -0
  34. package/dist/usage.js +2 -2
  35. package/package.json +1 -1
  36. package/scripts/p0-claude-code/README.md +39 -0
  37. package/scripts/p0-claude-code/launch-claude.ts +94 -0
  38. package/scripts/p0-claude-code/replay.ts +74 -0
  39. package/scripts/p0-claude-code/seed-local.mjs +145 -0
  40. package/scripts/p0-claude-code/tap.mjs +105 -0
  41. package/scripts/patch-pi.js +76 -55
  42. package/scripts/pi-patch-report.mjs +224 -0
  43. package/scripts/pi-upgrade.mjs +829 -0
  44. package/scripts/render-regression.mjs +67 -5
package/dist/config.js CHANGED
@@ -259,8 +259,11 @@ export function findModel(query) {
259
259
  }
260
260
  return undefined;
261
261
  }
262
+ export function isDefaultAgent(value) {
263
+ return value === "pi" || value === "claude" || value === "codex";
264
+ }
262
265
  export const u1s1Dir = join(homedir(), ".u1s1");
263
- const configFile = join(u1s1Dir, "config.json");
266
+ export const configFile = join(u1s1Dir, "config.json");
264
267
  /** pi keeps auth/models/settings/sessions under this dir — isolated from any real pi install. */
265
268
  export const agentDir = join(u1s1Dir, "agent");
266
269
  export const agentSettingsFile = join(agentDir, "settings.json");
@@ -368,6 +371,7 @@ export function configFromFile(file, env = process.env, currentCnApiBase = SITES
368
371
  model: file.model,
369
372
  modelProvider: typeof file.modelProvider === "string" ? file.modelProvider : undefined,
370
373
  email: typeof file.email === "string" && file.email ? file.email : undefined,
374
+ ...(isDefaultAgent(file.defaultAgent) ? { defaultAgent: file.defaultAgent } : {}),
371
375
  };
372
376
  }
373
377
  /**
@@ -442,6 +446,36 @@ export function saveConfig(cfg) {
442
446
  resetLang();
443
447
  }
444
448
  /** Persist the user's preferred model to both stores. */
449
+ /**
450
+ * 只改 config.json 里的 defaultAgent 一个键,其余原样保留(还没登录、文件不存在时也能写)。
451
+ * 不经 saveConfig:那会把默认 baseUrl 等生效值一并写死进文件。
452
+ */
453
+ export function saveDefaultAgent(agent) {
454
+ updateRawConfigFile((file) => {
455
+ if (agent === undefined)
456
+ delete file["defaultAgent"];
457
+ else
458
+ file["defaultAgent"] = agent;
459
+ });
460
+ }
461
+ /**
462
+ * 只改 config.json 里 mutate 碰到的键,其余原样保留(不经 saveConfig,理由同 saveDefaultAgent)。
463
+ * 文件在但读不出来时不覆盖(里面可能有登录凭据),抛错让用户先修好。
464
+ */
465
+ export function updateRawConfigFile(mutate) {
466
+ const raw = readJsonFile(configFile);
467
+ if (existsSync(configFile) && (!raw || typeof raw !== "object" || Array.isArray(raw))) {
468
+ throw new Error(tr(`${configFile} 格式不正确,没有改动它。`, `${configFile} is malformed; it was left unchanged.`));
469
+ }
470
+ const file = { ...raw };
471
+ mutate(file);
472
+ writePrivateFile(configFile, JSON.stringify(file, null, 2) + "\n");
473
+ }
474
+ /** config.json 的原始内容(不叠加环境变量);不存在或解析失败为 undefined。 */
475
+ export function readRawConfigFile() {
476
+ const raw = readJsonFile(configFile);
477
+ return raw && typeof raw === "object" && !Array.isArray(raw) ? raw : undefined;
478
+ }
445
479
  export function persistPreferredModel(cfg, provider, modelId) {
446
480
  const next = { ...cfg, model: modelId, modelProvider: provider };
447
481
  saveConfig(next);
@@ -50,8 +50,44 @@ interface SigningProxy {
50
50
  localKey: string;
51
51
  token: string;
52
52
  client: ClientSurface;
53
+ agent?: LaunchedAgent;
53
54
  attestation: AttestationHolder;
54
55
  }
56
+ /**
57
+ * Which client the launcher started behind this proxy. Sent as `x-u1s1-agent` /
58
+ * `x-u1s1-agent-version` so the Gateway knows which request shape to check
59
+ * (workers/gateway/src/launcher-agents.ts). The headers carry no authority of
60
+ * their own: the Gateway still requires the DPoP proof and a valid attestation.
61
+ */
62
+ export interface LaunchedAgent {
63
+ name: "pi" | "claude-code" | "codex";
64
+ /** The client's own version (e.g. Claude Code `2.1.322`), not the CLI's. */
65
+ version?: string;
66
+ }
67
+ export declare const AGENT_HEADER = "x-u1s1-agent";
68
+ export declare const AGENT_VERSION_HEADER = "x-u1s1-agent-version";
69
+ /**
70
+ * With a declared agent the proxy owns both headers: whatever the child sent is
71
+ * replaced (or dropped when no version is declared). Without one the request
72
+ * passes through exactly as before this option existed.
73
+ */
74
+ export declare function applyAgentHeaders(headers: Headers, agent: LaunchedAgent | undefined): void;
75
+ /**
76
+ * Errors the proxy produces itself (wrong local token, path outside /v1, body too large) are final:
77
+ * retrying cannot fix them. Codex retries 401/403/413 like transient failures (five times) and then
78
+ * shows the raw JSON, so for a declared `codex` child they become 400 with `error.code:
79
+ * "invalid_prompt"`, which Codex reports once and displays as `error.message`. Other clients keep
80
+ * the original status codes.
81
+ */
82
+ export declare function localFinalError(agent: LaunchedAgent | undefined, status: number, message: string): {
83
+ status: number;
84
+ body: {
85
+ error: {
86
+ message: string;
87
+ code?: string;
88
+ };
89
+ };
90
+ };
55
91
  /** Attach a fresh (self-healing) attestation header to an outbound proxy request. */
56
92
  export declare function attachAttestationHeader(holder: AttestationHolder, headers: Headers): Promise<void>;
57
93
  export type ClientSurface = "terminal" | "web" | "desktop";
@@ -62,4 +98,4 @@ export declare function onGatewayFetchFailure(listener: ((failure: GatewayFetchF
62
98
  * pi accepts static provider headers only. Keep it behind a loopback proxy that
63
99
  * replaces the local bearer credential with a fresh DPoP proof per request.
64
100
  */
65
- export declare function ensureSigningProxy(cfg: CliConfig, fallbackClient?: ClientSurface, attestationSource?: ClientAttestationSource): Promise<SigningProxy>;
101
+ export declare function ensureSigningProxy(cfg: CliConfig, fallbackClient?: ClientSurface, attestationSource?: ClientAttestationSource, agent?: LaunchedAgent): Promise<SigningProxy>;
@@ -130,6 +130,37 @@ export async function readSigningProxyRequestBody(request, maxBytes = SIGNING_PR
130
130
  }
131
131
  return chunks.length ? Buffer.concat(chunks, total) : undefined;
132
132
  }
133
+ export const AGENT_HEADER = "x-u1s1-agent";
134
+ export const AGENT_VERSION_HEADER = "x-u1s1-agent-version";
135
+ function sameAgent(a, b) {
136
+ return a?.name === b?.name && a?.version === b?.version;
137
+ }
138
+ /**
139
+ * With a declared agent the proxy owns both headers: whatever the child sent is
140
+ * replaced (or dropped when no version is declared). Without one the request
141
+ * passes through exactly as before this option existed.
142
+ */
143
+ export function applyAgentHeaders(headers, agent) {
144
+ if (!agent)
145
+ return;
146
+ headers.set(AGENT_HEADER, agent.name);
147
+ if (agent.version)
148
+ headers.set(AGENT_VERSION_HEADER, agent.version);
149
+ else
150
+ headers.delete(AGENT_VERSION_HEADER);
151
+ }
152
+ /**
153
+ * Errors the proxy produces itself (wrong local token, path outside /v1, body too large) are final:
154
+ * retrying cannot fix them. Codex retries 401/403/413 like transient failures (five times) and then
155
+ * shows the raw JSON, so for a declared `codex` child they become 400 with `error.code:
156
+ * "invalid_prompt"`, which Codex reports once and displays as `error.message`. Other clients keep
157
+ * the original status codes.
158
+ */
159
+ export function localFinalError(agent, status, message) {
160
+ if (agent?.name === "codex")
161
+ return { status: 400, body: { error: { message, code: "invalid_prompt" } } };
162
+ return { status, body: { error: { message } } };
163
+ }
133
164
  // Refresh once we are within a day of the server-side 7-day expiry; back off for
134
165
  // a while after a failed refresh so a flaky Gateway is not hammered per request.
135
166
  const ATTESTATION_REFRESH_MARGIN_MS = 24 * 60 * 60 * 1000;
@@ -222,12 +253,12 @@ export function onGatewayFetchFailure(listener) {
222
253
  * pi accepts static provider headers only. Keep it behind a loopback proxy that
223
254
  * replaces the local bearer credential with a fresh DPoP proof per request.
224
255
  */
225
- export async function ensureSigningProxy(cfg, fallbackClient = "terminal", attestationSource) {
256
+ export async function ensureSigningProxy(cfg, fallbackClient = "terminal", attestationSource, agent) {
226
257
  if (!hasDeviceCredential(cfg))
227
258
  throw new Error(tr("当前安装需要重新登录,以创建设备凭证", "Please sign in again to create a device credential for this install."));
228
259
  const client = clientSurface(fallbackClient);
229
260
  const current = signingProxy;
230
- if (current && current.token === cfg.deviceToken && current.client === client) {
261
+ if (current && current.token === cfg.deviceToken && current.client === client && sameAgent(current.agent, agent)) {
231
262
  updateAttestationHolder(current.attestation, attestationSource);
232
263
  return current;
233
264
  }
@@ -235,19 +266,22 @@ export async function ensureSigningProxy(cfg, fallbackClient = "terminal", attes
235
266
  updateAttestationHolder(attestation, attestationSource);
236
267
  const localKey = `local-${randomBytes(32).toString("hex")}`;
237
268
  const upstreamOrigin = new URL(cfg.baseUrl).origin;
269
+ const localError = (res, status, message) => {
270
+ const final = localFinalError(agent, status, message);
271
+ res.writeHead(final.status, { "content-type": "application/json" });
272
+ res.end(JSON.stringify(final.body));
273
+ };
238
274
  const server = createServer(async (req, res) => {
239
275
  try {
240
276
  if (req.headers.authorization !== `Bearer ${localKey}` || !req.url) {
241
- res.writeHead(401, { "content-type": "application/json" });
242
- res.end(JSON.stringify({ error: { message: "local signing proxy authentication failed" } }));
277
+ localError(res, 401, "local signing proxy authentication failed");
243
278
  return;
244
279
  }
245
280
  // Only proxy the gateway namespace. Absolute-form request targets must not
246
281
  // be allowed to override the configured upstream origin and steal a proof.
247
282
  const localUrl = new URL(req.url, "http://127.0.0.1");
248
283
  if (!localUrl.pathname.startsWith("/v1/")) {
249
- res.writeHead(403, { "content-type": "application/json" });
250
- res.end(JSON.stringify({ error: { message: "signing proxy only serves /v1/*" } }));
284
+ localError(res, 403, "signing proxy only serves /v1/*");
251
285
  return;
252
286
  }
253
287
  const upstreamAbort = new AbortController();
@@ -266,6 +300,7 @@ export async function ensureSigningProxy(cfg, fallbackClient = "terminal", attes
266
300
  outboundHeaders.set("x-u1s1-client", client);
267
301
  outboundHeaders.set("x-u1s1-version", VERSION);
268
302
  outboundHeaders.set("x-u1s1-platform", `${process.platform}-${process.arch}`);
303
+ applyAgentHeaders(outboundHeaders, agent);
269
304
  await attachAttestationHeader(attestation, outboundHeaders);
270
305
  // 中国站主域建连失败时 authorizedFetch 会换到大陆加速入口,和启动时的请求共用选路状态
271
306
  const upstream = await authorizedFetch(cfg, new URL(pathAndQuery, upstreamOrigin).toString(), {
@@ -288,8 +323,7 @@ export async function ensureSigningProxy(cfg, fallbackClient = "terminal", attes
288
323
  catch (error) {
289
324
  if (error instanceof SigningProxyRequestTooLargeError && !res.destroyed) {
290
325
  req.resume();
291
- res.writeHead(413, { "content-type": "application/json" });
292
- res.end(JSON.stringify({ error: { message: "local signing proxy request body is too large" } }));
326
+ localError(res, 413, "local signing proxy request body is too large");
293
327
  return;
294
328
  }
295
329
  if (res.destroyed)
@@ -322,6 +356,7 @@ export async function ensureSigningProxy(cfg, fallbackClient = "terminal", attes
322
356
  localKey,
323
357
  token: cfg.deviceToken,
324
358
  client,
359
+ ...(agent ? { agent: { ...agent } } : {}),
325
360
  attestation,
326
361
  };
327
362
  return signingProxy;
package/dist/embed.d.ts CHANGED
@@ -11,3 +11,16 @@ export { applyWebUiBranding } from "./webui-brand.js";
11
11
  export { DASHBOARD_URL } from "./brand.js";
12
12
  export { SITES, siteForBaseUrl, siteLinks, type Site, type SiteId, type SiteLinks } from "./site.js";
13
13
  export { hasNudge, markNudge, readNudges, type NudgeKey } from "./nudges.js";
14
+ export { ensureAgentRuntime, RuntimeInstallError, RuntimePlanError, type DownloadInfo, type EnsureRuntimeOptions, type EnsuredRuntime, type InstallHooks, type RuntimeBrake, type SkippedRuntime, } from "./agents/runtime.js";
15
+ export { agentLaunchEnv, claudeLaunchArgs, claudePaths, type ClaudeLaunchInput } from "./agents/claude.js";
16
+ export { codexLaunchArgs, codexLaunchEnv, codexPaths, writeCodexConfig, type CodexLaunchInput, type CodexPaths, } from "./agents/codex.js";
17
+ export { loadAgentConfig, loadPublicAgentRuntime, type AgentConfig, type AgentConfigResult, type PublicAgentRuntimeResult, } from "./agents/agent-config.js";
18
+ export { bestInstallTarget, checkBrake, compareVersions, dailyUpdateCheck, downloadSize, fetchLatestVersion, fetchPackageMeta, findNpm, findOnPath, installedVersions, isManagedAgent, MANAGED_AGENTS, managedBinPath, NO_BRAKE, normalizeVersion, npmRegistryFor, planRuntime, platformPackageRef, probeVersion, readRuntimeState, runtimeSpec, type BrakeVerdict, type FoundRuntime, type ManagedAgent, type NpmCommand, type PackageMeta, type RuntimePlan, type RuntimeState, } from "./agents/runtime.js";
19
+ export { AGENT_PROFILES, agentsRoot, CLAUDE_TERMS_URL, CODEX_LICENSE_URL, notOpenText, overMaxTestedText, runtimeErrorText, type LaunchPaths, } from "./agents/launch.js";
20
+ export { launchPaths } from "./agents/status.js";
21
+ export { parseAgentConfig, readConfigCache, type AgentModel, type GatewayAgentName } from "./agents/agent-config.js";
22
+ export { chooseAgentModel, isModelId, readAgentPrefs, saveAgentPrefs, type AgentPrefs } from "./agents/prefs.js";
23
+ export { CONFIG_KEYS, isConfigKey, readConfigValue, writeConfigValue, type ConfigKey, type ConfigValue, } from "./config-command.js";
24
+ export { isDefaultAgent, readRawConfigFile, saveDefaultAgent, type DefaultAgent } from "./config.js";
25
+ export { isExistingUser } from "./launcher.js";
26
+ export { gatewayFetch } from "./gateway-route.js";
package/dist/embed.js CHANGED
@@ -11,3 +11,18 @@ export { applyWebUiBranding } from "./webui-brand.js";
11
11
  export { DASHBOARD_URL } from "./brand.js";
12
12
  export { SITES, siteForBaseUrl, siteLinks } from "./site.js";
13
13
  export { hasNudge, markNudge, readNudges } from "./nudges.js";
14
+ // 启动器:桌面端复用同一套运行时下载/更新/刹车与子进程注入逻辑
15
+ export { ensureAgentRuntime, RuntimeInstallError, RuntimePlanError, } from "./agents/runtime.js";
16
+ export { agentLaunchEnv, claudeLaunchArgs, claudePaths } from "./agents/claude.js";
17
+ export { codexLaunchArgs, codexLaunchEnv, codexPaths, writeCodexConfig, } from "./agents/codex.js";
18
+ export { loadAgentConfig, loadPublicAgentRuntime, } from "./agents/agent-config.js";
19
+ // 启动器(桌面端服务层):状态、刹车、偏好与 `u1s1 config` 读写都走 CLI 的同一份实现
20
+ export { bestInstallTarget, checkBrake, compareVersions, dailyUpdateCheck, downloadSize, fetchLatestVersion, fetchPackageMeta, findNpm, findOnPath, installedVersions, isManagedAgent, MANAGED_AGENTS, managedBinPath, NO_BRAKE, normalizeVersion, npmRegistryFor, planRuntime, platformPackageRef, probeVersion, readRuntimeState, runtimeSpec, } from "./agents/runtime.js";
21
+ export { AGENT_PROFILES, agentsRoot, CLAUDE_TERMS_URL, CODEX_LICENSE_URL, notOpenText, overMaxTestedText, runtimeErrorText, } from "./agents/launch.js";
22
+ export { launchPaths } from "./agents/status.js";
23
+ export { parseAgentConfig, readConfigCache } from "./agents/agent-config.js";
24
+ export { chooseAgentModel, isModelId, readAgentPrefs, saveAgentPrefs } from "./agents/prefs.js";
25
+ export { CONFIG_KEYS, isConfigKey, readConfigValue, writeConfigValue, } from "./config-command.js";
26
+ export { isDefaultAgent, readRawConfigFile, saveDefaultAgent } from "./config.js";
27
+ export { isExistingUser } from "./launcher.js";
28
+ export { gatewayFetch } from "./gateway-route.js";