u1s1-cli 1.2.9 → 1.3.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/api.d.ts CHANGED
@@ -64,6 +64,8 @@ export interface ModelsResponse {
64
64
  models: ApiModel[];
65
65
  features: ApiFeatures;
66
66
  announcement?: ApiAnnouncement | null;
67
+ /** Opaque, device-bound canary echoed only by the official signing proxy. */
68
+ clientAttestation?: string;
67
69
  }
68
70
  /** 401:凭证失效(设备被移除/换过钥匙),调用方应引导重新登录而不是继续硬跑。 */
69
71
  export declare class AuthError extends Error {
package/dist/api.js CHANGED
@@ -24,7 +24,13 @@ export async function fetchModels(cfg) {
24
24
  if (!resp.ok)
25
25
  throw new Error(await errorMessageFromResponse(resp, `服务端返回 ${resp.status},稍后再试`));
26
26
  const body = (await resp.json());
27
- return { models: body.data, features: body.features ?? {}, announcement: body.announcement };
27
+ const token = body.client_attestation?.token;
28
+ return {
29
+ models: body.data,
30
+ features: body.features ?? {},
31
+ announcement: body.announcement,
32
+ clientAttestation: typeof token === "string" && token.length <= 1024 ? token : undefined,
33
+ };
28
34
  }
29
35
  /**
30
36
  * 拉取用户在云端配置的自定义模型端点(dashboard「自定义模型端点」卡片)。
@@ -13,11 +13,12 @@ interface SigningProxy {
13
13
  localKey: string;
14
14
  token: string;
15
15
  client: ClientSurface;
16
+ clientAttestation?: string;
16
17
  }
17
18
  export type ClientSurface = "terminal" | "web" | "desktop" | "cloud";
18
19
  /**
19
20
  * pi accepts static provider headers only. Keep it behind a loopback proxy that
20
21
  * replaces the local bearer credential with a fresh DPoP proof per request.
21
22
  */
22
- export declare function ensureSigningProxy(cfg: CliConfig, fallbackClient?: ClientSurface): Promise<SigningProxy>;
23
+ export declare function ensureSigningProxy(cfg: CliConfig, fallbackClient?: ClientSurface, clientAttestation?: string): Promise<SigningProxy>;
23
24
  export {};
@@ -98,12 +98,13 @@ function clientSurface(fallback) {
98
98
  * pi accepts static provider headers only. Keep it behind a loopback proxy that
99
99
  * replaces the local bearer credential with a fresh DPoP proof per request.
100
100
  */
101
- export async function ensureSigningProxy(cfg, fallbackClient = "terminal") {
101
+ export async function ensureSigningProxy(cfg, fallbackClient = "terminal", clientAttestation) {
102
102
  if (!hasDeviceCredential(cfg))
103
103
  throw new Error("当前安装需要重新登录,以创建设备凭证");
104
104
  const client = clientSurface(fallbackClient);
105
105
  const current = signingProxy;
106
- if (current && current.token === cfg.deviceToken && current.client === client)
106
+ if (current && current.token === cfg.deviceToken && current.client === client
107
+ && current.clientAttestation === clientAttestation)
107
108
  return current;
108
109
  const localKey = `local-${randomBytes(32).toString("hex")}`;
109
110
  const upstreamOrigin = new URL(cfg.baseUrl).origin;
@@ -133,6 +134,8 @@ export async function ensureSigningProxy(cfg, fallbackClient = "terminal") {
133
134
  outboundHeaders.set("x-u1s1-client", client);
134
135
  outboundHeaders.set("x-u1s1-version", VERSION);
135
136
  outboundHeaders.set("x-u1s1-platform", `${process.platform}-${process.arch}`);
137
+ if (clientAttestation)
138
+ outboundHeaders.set("x-u1s1-attestation", clientAttestation);
136
139
  const upstream = await authorizedFetch(cfg, target, {
137
140
  method: req.method ?? "GET",
138
141
  headers: outboundHeaders,
@@ -166,6 +169,12 @@ export async function ensureSigningProxy(cfg, fallbackClient = "terminal") {
166
169
  });
167
170
  server.unref();
168
171
  const port = server.address().port;
169
- signingProxy = { baseUrl: `http://127.0.0.1:${port}/v1`, localKey, token: cfg.deviceToken, client };
172
+ signingProxy = {
173
+ baseUrl: `http://127.0.0.1:${port}/v1`,
174
+ localKey,
175
+ token: cfg.deviceToken,
176
+ client,
177
+ clientAttestation,
178
+ };
170
179
  return signingProxy;
171
180
  }
package/dist/index.js CHANGED
@@ -193,7 +193,7 @@ async function runAgent(cfg, args) {
193
193
  // it after the live model list arrives; explicit user choices remain intact.
194
194
  ensureDefaultSettings(MODELS);
195
195
  // pi provider 只支持静态 header;指向本机 signing proxy,由它逐请求附 DPoP proof。
196
- const signing = await ensureSigningProxy(cfg);
196
+ const signing = await ensureSigningProxy(cfg, "terminal", modelsResp?.clientAttestation);
197
197
  const officialCfg = { ...cfg, baseUrl: signing.baseUrl, apiKey: signing.localKey };
198
198
  ensureBrandPrompt(await shellReady);
199
199
  ensureProviderModels(officialCfg);
package/dist/web.js CHANGED
@@ -54,20 +54,22 @@ export async function prepareWebEnv(cfg) {
54
54
  let webFetchRenderEnabled = false;
55
55
  // 老网关没有 /v1/image,image_gen 缺失时按关闭处理,不注册生图工具
56
56
  let imageGenEnabled = false;
57
+ let clientAttestation;
57
58
  const endpointsReady = loadCustomEndpoints(cfg);
58
59
  try {
59
- const { models, features } = await fetchModels(cfg);
60
+ const { models, features, clientAttestation: attestation } = await fetchModels(cfg);
60
61
  setModelsFromApi(models.map(apiModelToDef));
61
62
  webSearchEnabled = features.web_search !== false;
62
63
  webFetchRenderEnabled = features.web_fetch_render === true;
63
64
  imageGenEnabled = features.image_gen === true;
65
+ clientAttestation = attestation;
64
66
  }
65
67
  catch (e) {
66
68
  console.error(" 获取模型列表失败,使用内置列表:", e.message);
67
69
  }
68
70
  await endpointsReady;
69
71
  ensureDefaultSettings(MODELS);
70
- const signing = await ensureSigningProxy(cfg, "desktop");
72
+ const signing = await ensureSigningProxy(cfg, "desktop", clientAttestation);
71
73
  const officialCfg = { ...cfg, baseUrl: signing.baseUrl, apiKey: signing.localKey };
72
74
  webOfficialCfg = officialCfg;
73
75
  const modelsPath = refreshWebModels();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "u1s1-cli",
3
- "version": "1.2.9",
3
+ "version": "1.3.0",
4
4
  "description": "u1s1 — 有一说一,最省心的 AI 编程搭子。终端里用中文说需求,AI 帮你读文件、改代码、跑命令。",
5
5
  "type": "module",
6
6
  "bin": {