@sema-agent/client-core 0.11.17 → 0.11.18
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/README.md +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +8 -0
- package/dist/model/catalog.d.ts +158 -0
- package/dist/model/catalog.js +224 -0
- package/dist/model/providerPresets.d.ts +11 -0
- package/dist/model/providerPresets.json +31 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ Renamed from **`@sema-agent/wire-cc-adapter`** (0.1.x, deprecated — see *Migra
|
|
|
23
23
|
|
|
24
24
|
## Scope
|
|
25
25
|
|
|
26
|
-
**Version:** 0.11.
|
|
26
|
+
**Version:** 0.11.18
|
|
27
27
|
|
|
28
28
|
- **Today** — the adapter seam, the whole `adapt()` pipeline (all 14 A-layer arms plus the
|
|
29
29
|
B/D/E tool-card layers), the notification/caps/model families, the adapter kernel (stream driver
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -302,3 +302,11 @@ export * from './agentSession/contract.js';
|
|
|
302
302
|
// 两源(assistant.tasks 跨 session 调度总览 + fleet 常驻 SSE 快照)独立拉取独立降级的归一投影;
|
|
303
303
|
// 两张 status 映射表 never 卫兵穷举;'not-configured' 判别姿势见文件头注。
|
|
304
304
|
export * from './agentSession/backgroundView.js';
|
|
305
|
+
// ── design/160 需求②:模型 provider 目录公用化(2026-07-31)──────────────────────────────────
|
|
306
|
+
// 三层源解析(线上 URL 可配 → 包内 providerPresets 兜底 → 用户自定义覆盖)+ **来源标注**。
|
|
307
|
+
// 🔴 零 Node、零全局 fetch:线上腿的取数走注入的 `CatalogFetchJson` 端口,宿主没给 ⇒ 线上腿
|
|
308
|
+
// 整条不启用(`online.reason = 'no-fetch-port'`),与 host.ts 的缺席语义同一条纪律。
|
|
309
|
+
// 🔴 本文件零默认 URL:目录仓位候裁(设计档待裁点 3),机制先行、地址由调用方传。
|
|
310
|
+
// 🔴 绝不半解析:schemaVersion 超区间/任一行形状坏 ⇒ 整份弃用走兜底(端渲「内置版本(离线)」
|
|
311
|
+
// 靠 `source` + `online.reason`,所以「静默用兜底」在本层是可观测的)。
|
|
312
|
+
export * from './model/catalog.js';
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* catalog.ts — **模型 provider 目录的三层源解析**(design/160 需求②,2026-07-31)。
|
|
3
|
+
*
|
|
4
|
+
* ## 它解决什么
|
|
5
|
+
*
|
|
6
|
+
* `providerPresets.json` 是**编译进包的静态表**:加一家 provider、改一个 model 的 cap,都得发一次
|
|
7
|
+
* client-core。而三端(CLI / web / 桌面)要的是同一份目录、可在线更新、离线仍能用。本模块把
|
|
8
|
+
* 设计档 §需求② 的三层源做成一个**纯解析器**:
|
|
9
|
+
*
|
|
10
|
+
* ```
|
|
11
|
+
* ① 线上目录(URL 由调用方传入 —— 本文件零默认地址,见下「为什么不写死 URL」)
|
|
12
|
+
* ↓ 拉取失败 / 离线 / 校验不过(整份弃用)
|
|
13
|
+
* ② 包内兜底 = PROVIDER_PRESETS(零改动即兜底)
|
|
14
|
+
* ↓
|
|
15
|
+
* ③ 用户本地覆盖(自定义 provider,合并语义:同 id 用户 > 线上 > 包内)
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* ## 四条设计纪律(每条都在 pure 门 MC 段有断言)
|
|
19
|
+
*
|
|
20
|
+
* 1. **零 Node、零自发网络**:本文件不 import 任何 `node:*`,也**不碰全局 `fetch`** ——
|
|
21
|
+
* 网络能力由调用方注入 `fetchJson` 端口。宿主没给 ⇒ 线上腿整条不启用(`reason: 'no-fetch-port'`),
|
|
22
|
+
* 而不是「偷偷换个方式做」(与 host.ts 的缺席语义同一条纪律)。
|
|
23
|
+
* ⇒ 桌面渲染进程 / 浏览器 / TUI 用同一份代码,各自注入自己的传输。
|
|
24
|
+
* 2. **绝不半解析**(设计档明写):`schemaVersion` 超出支持区间、`providers` 不是数组、
|
|
25
|
+
* **任何一行**形状不合格 ⇒ **整份**线上目录弃用走兜底。半解析 = 用户拿到一张自己也说不清
|
|
26
|
+
* 是哪一版的表。
|
|
27
|
+
* 3. **来源标注是产品位不是调试位**:返回值恒带 `source` 与 `online.{ok,reason}`。端据此渲
|
|
28
|
+
* 「目录为内置版本(离线)」——「静默用兜底」在本层必须是**可观测**的,否则用户会以为看到的
|
|
29
|
+
* 是最新表([honest-absence-not-fabricated-zero])。
|
|
30
|
+
* 4. **诚实缺席**:`staleness` 只在真吃到线上目录时在场;`ageMs` 只在调用方给了 `nowMs`
|
|
31
|
+
* 且 `generatedAt` 可解析时在场。不知道就是键缺席,绝不 `?? 0`,也绝不偷读时钟
|
|
32
|
+
* (时钟是宿主资产,与 `AdapterContext.now` 同一条口径)。
|
|
33
|
+
*
|
|
34
|
+
* ## 为什么不写死线上 URL
|
|
35
|
+
*
|
|
36
|
+
* 目录仓位(设计档待裁点 3:`sema-agent/model-catalog` 公开仓 or release asset)**候 clay 裁**。
|
|
37
|
+
* 机制先行、地址后填:URL 由调用方从配置/settings 传入。本文件里出现任何默认地址,都会让
|
|
38
|
+
* 「还没定的仓位」提前变成事实契约。https-only 是硬校验(目录是会被下载并影响**出站地址**的
|
|
39
|
+
* 数据,明文信道下可被改写成钓鱼网关)。
|
|
40
|
+
*
|
|
41
|
+
* ## 本批不做(留口注释,别当遗漏)
|
|
42
|
+
*
|
|
43
|
+
* · **缓存与 ETag**:`~/.sema/cache/model-catalog.json` + ETag/304 + 新鲜窗 + `localGeneratedAt`
|
|
44
|
+
* 防降级(设计档 pi 调研节)。落盘是宿主能力(fs 在 host.ts 的 `FsPort`),所以缓存层应当是
|
|
45
|
+
* **本模块之上**的一层:调用方拿 `resolveModelCatalog` 的结果自行落盘,并把上次的 `generatedAt`
|
|
46
|
+
* 与新拉到的比较后决定是否替换。⇒ 加缓存时**不需要**改本文件的形状,只需在 `fetchJson` 注入
|
|
47
|
+
* 处做 ETag,并在外层加一个「旧远端压不过新包」的比较。
|
|
48
|
+
* · **签名 / sha256**:同上,属传输层与目录产出方的约定(设计档待裁点 3)。
|
|
49
|
+
* · **`oauth` / `subscribe` 字段**:设计档待裁点 3b。本批只加纯链接位 `consoleUrl`/`signupUrl`
|
|
50
|
+
* (见 providerPresets.ts),授权码流/设备码流一律不进。
|
|
51
|
+
*
|
|
52
|
+
* ## probe 复用面评估(设计档 §需求②-C「复用面」;只评估不搬,搬迁下一批做)
|
|
53
|
+
*
|
|
54
|
+
* 桌面端 onboard 要「选 provider → 填 key → 探活」,探活那一步现在只在壳树。按
|
|
55
|
+
* [multiclient-migration-batch-discipline](模块级状态与读写口同包)逐个评估,结论如下 ——
|
|
56
|
+
* 这是下一批的搬迁清单,坐标为壳树 `src/sema/onboard/`:
|
|
57
|
+
*
|
|
58
|
+
* | 文件 | 结论 | 依据 |
|
|
59
|
+
* |---|---|---|
|
|
60
|
+
* | `probePlan.ts`(139 行) | ✅ **整搬,零改造** | 零 import、零模块级状态、纯函数四件(`buildVerdictRequest` / `extractApiError` / `looksLikeModelError` / `judgeMessagesProbe`)。判决表本身就是「三端必须一致」的那部分。 |
|
|
61
|
+
* | `probeProvider.ts`(223 行) | ⚠️ **拆搬**:判定+请求编排进包、传输与 TLS 留端 | ①裸 `fetch`/`AbortController`/`setTimeout` ⇒ 改注入 `fetchImpl`(本包既有姿势:`limitsWire`/`detachWire`/`liveInitToolFace` 的 `opts.fetchImpl`);②`ensureProxyBootstrap()` 动态 import 壳的 `utils/proxy.js`(`configureGlobalAgents`)= **Node undici 专属**,浏览器无此概念 ⇒ 留端,做成可选的「拨号前置钩子」端口;③`isCertError`/`applySystemTlsTrust`(壳 `tlsTrust.ts`,读系统钥匙串 + 设 `NODE_EXTRA_CA_CERTS`)= 纯 Node ⇒ 留端,做成可选的「一次性重试钩子」;④`nearestListed()` 与 `unwrapFetchError()` 是纯函数 ⇒ 直接进包。搬后包内出口 ≈ `probeProvider({ baseUrl, api, apiKey, modelId }, { fetchImpl, beforeDial?, retryOnCertError? })`。 |
|
|
62
|
+
* | `detectSources.ts`(524 行) | ❌ **本批不搬** | 它整个是**宿主文件系统扫描器**:`node:fs`(existsSync/readdirSync/readFileSync/statSync)+ `node:os` homedir + `node:path` + `process.env` + 两个外部解析器(`jsonc-parser`/`yaml`)+ 壳内 `ccConfigFallback`。可搬的只有「读到的配置 → `ImportableProvider` 的分类/归一逻辑」那一小半,而它与扫描代码是逐行交织的。⇒ 要搬得先在壳里把「扫描(IO)」与「分类(纯)」拆开,是独立一批的量。**桌面端不阻塞**:桌面 onboard 的导入源探测本就该是主进程的活(与 CLI 同为 Node 侧),复用价值远低于 probe 两件。 |
|
|
63
|
+
* | `onboardZeroModel.ts`(2.8K) | 🔶 **顺路可搬** | 已有独立单测(`onboardZeroModel.test.ts`),看形状是纯判定;搬前需确认零壳态依赖。 |
|
|
64
|
+
* | `applyOnboard.ts` / `OnboardWizard.tsx` / `BigStepTitle.tsx` / `ccSwitchReader.ts` | ❌ 属端 | 落盘编排 / Ink 渲染 / 读 cc-switch sqlite —— 宿主辖区,桌面端各写各的渲染器(设计档 §C 与 pi 的 `AuthInteraction` seam 同一心智)。 |
|
|
65
|
+
*
|
|
66
|
+
* 搬迁批建议顺序:`probePlan`(零风险打头,先让包里有判决表)→ `probeProvider` 拆搬(带
|
|
67
|
+
* fetchImpl 注入 + 两个可选钩子端口)→ `onboardZeroModel` → detectSources 拆分单列一批。
|
|
68
|
+
*/
|
|
69
|
+
import { type ProviderPreset } from './providerPresets.js';
|
|
70
|
+
/** 支持的目录 schemaVersion 区间(闭区间)。产目录那一方按这个区间发版。 */
|
|
71
|
+
export declare const CATALOG_SCHEMA_VERSION_MIN = 1;
|
|
72
|
+
export declare const CATALOG_SCHEMA_VERSION_MAX = 1;
|
|
73
|
+
/** 线上目录文档的信封形。`providers` 与包内表**同形**(ProviderPreset),所以两层可直接按 id 合并。 */
|
|
74
|
+
export interface OnlineCatalogDoc {
|
|
75
|
+
schemaVersion: number;
|
|
76
|
+
/** 目录生成时刻(ISO8601)。用于端渲新鲜度;不可解析也不弃用整份(它不是契约位)。 */
|
|
77
|
+
generatedAt: string;
|
|
78
|
+
providers: ProviderPreset[];
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* 结果的来源标注 = **最高优先级的在场层**:
|
|
82
|
+
* · `merged` —— 用户覆盖层真的贡献了行(不管线上腿成没成);
|
|
83
|
+
* · `online` —— 线上目录被接受且无用户覆盖;
|
|
84
|
+
* · `bundled` —— 只有包内表(离线 / 线上被拒 / 没配 URL)。
|
|
85
|
+
*/
|
|
86
|
+
export type CatalogSource = 'online' | 'bundled' | 'merged';
|
|
87
|
+
/** 线上腿被拒的**具体**理由(端可据此分诊:是没配、是离线、还是目录本身坏了)。 */
|
|
88
|
+
export type CatalogRejectReason = 'no-online-url' | 'no-fetch-port' | 'insecure-url' | 'fetch-failed' | 'payload-not-object' | 'schema-version-missing' | 'schema-version-unsupported' | 'generated-at-missing' | 'providers-not-array' | 'provider-row-invalid';
|
|
89
|
+
/** 线上腿的结局。`ok: true` 时不带 `reason`(枚举互斥)。 */
|
|
90
|
+
export interface CatalogOnlineOutcome {
|
|
91
|
+
ok: boolean;
|
|
92
|
+
reason?: CatalogRejectReason;
|
|
93
|
+
/**
|
|
94
|
+
* 拒绝细节(人话,给分诊用)。🔴 只放判决相关的短语与被截断的错误 message,
|
|
95
|
+
* **绝不回显载荷本身**([credential-error-path-leak]:错误路径是凭证外泄的经典入口;
|
|
96
|
+
* 目录 URL 本身是公开物,但宿主注入的 fetchJson 可能带鉴权头,所以异常只取 message 前段)。
|
|
97
|
+
*/
|
|
98
|
+
detail?: string;
|
|
99
|
+
}
|
|
100
|
+
/** 线上目录的新鲜度。只在真吃到线上目录时在场。 */
|
|
101
|
+
export interface CatalogStaleness {
|
|
102
|
+
/** 目录自述的生成时刻,原样透传(烂串也原样给端,由端决定怎么说)。 */
|
|
103
|
+
generatedAt: string;
|
|
104
|
+
/** 距今毫秒。只在调用方给了 `nowMs` 且 `generatedAt` 可解析时在场 —— 不知道就键缺席。 */
|
|
105
|
+
ageMs?: number;
|
|
106
|
+
}
|
|
107
|
+
export interface ModelCatalogResult {
|
|
108
|
+
/** 合并后的 provider 表(新数组;包内表绝不被就地改写)。 */
|
|
109
|
+
providers: ProviderPreset[];
|
|
110
|
+
source: CatalogSource;
|
|
111
|
+
staleness?: CatalogStaleness;
|
|
112
|
+
online: CatalogOnlineOutcome;
|
|
113
|
+
/** 用户覆盖层真正贡献的 provider id(替换的 + 新增的)。无覆盖 ⇒ 空数组。 */
|
|
114
|
+
overrideIds: string[];
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* 注入式取 JSON 端口 —— **本包唯一的线上目录取数口**。
|
|
118
|
+
* 约定:2xx ⇒ resolve 已解析的 JSON;其它一切(网络错 / 非 2xx / 解析失败)⇒ reject。
|
|
119
|
+
* ETag/304、超时、代理、鉴权头全归宿主(它们各端形态不同,见文件头「本批不做」)。
|
|
120
|
+
*/
|
|
121
|
+
export type CatalogFetchJson = (url: string) => Promise<unknown>;
|
|
122
|
+
export interface ResolveModelCatalogOptions {
|
|
123
|
+
/** 线上目录 URL。缺席 ⇒ 线上腿不启用(本包零默认地址)。必须是 https。 */
|
|
124
|
+
onlineUrl?: string;
|
|
125
|
+
/** 取数口。缺席 ⇒ 线上腿不启用(本包绝不自己碰 fetch)。 */
|
|
126
|
+
fetchJson?: CatalogFetchJson;
|
|
127
|
+
/** 用户本地覆盖(自定义 provider)。同 id 恒赢线上与包内。 */
|
|
128
|
+
overrides?: readonly ProviderPreset[];
|
|
129
|
+
/** 当前时刻(宿主时钟)。缺席 ⇒ 不算 ageMs(而不是自己读 Date.now)。 */
|
|
130
|
+
nowMs?: number;
|
|
131
|
+
}
|
|
132
|
+
/** schemaVersion 是否落在支持区间内(非整数一律否)。 */
|
|
133
|
+
export declare function isSupportedCatalogSchemaVersion(v: unknown): boolean;
|
|
134
|
+
export type ValidateCatalogResult = {
|
|
135
|
+
ok: true;
|
|
136
|
+
doc: OnlineCatalogDoc;
|
|
137
|
+
} | {
|
|
138
|
+
ok: false;
|
|
139
|
+
reason: CatalogRejectReason;
|
|
140
|
+
detail?: string;
|
|
141
|
+
};
|
|
142
|
+
/**
|
|
143
|
+
* 校验线上载荷 —— **纯同步**,端可以单用(比如「刷新目录」按钮先本地校验再落盘)。
|
|
144
|
+
* 🔴 全或无:任何一条不合格 ⇒ 整份拒,绝不半解析。
|
|
145
|
+
*/
|
|
146
|
+
export declare function validateOnlineCatalog(payload: unknown): ValidateCatalogResult;
|
|
147
|
+
/**
|
|
148
|
+
* 按 id 分层合并:**后来的层赢**,行序 = 首见序(包内序稳定在前,新家按层依次追加)。
|
|
149
|
+
* 纯函数,端可以单用(比如只合并「包内 + 用户覆盖」的离线路径)。
|
|
150
|
+
*/
|
|
151
|
+
export declare function mergeProviderLayers<T extends {
|
|
152
|
+
id: string;
|
|
153
|
+
}>(...layers: readonly (readonly T[])[]): T[];
|
|
154
|
+
/**
|
|
155
|
+
* 解析出一份可用的 provider 目录 —— 三层源 + 来源标注。**永不 reject**:线上腿的任何失败都
|
|
156
|
+
* 收敛成 `online.ok === false` 并回落包内表(离线可用是硬需求)。
|
|
157
|
+
*/
|
|
158
|
+
export declare function resolveModelCatalog(opts?: ResolveModelCatalogOptions): Promise<ModelCatalogResult>;
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* catalog.ts — **模型 provider 目录的三层源解析**(design/160 需求②,2026-07-31)。
|
|
3
|
+
*
|
|
4
|
+
* ## 它解决什么
|
|
5
|
+
*
|
|
6
|
+
* `providerPresets.json` 是**编译进包的静态表**:加一家 provider、改一个 model 的 cap,都得发一次
|
|
7
|
+
* client-core。而三端(CLI / web / 桌面)要的是同一份目录、可在线更新、离线仍能用。本模块把
|
|
8
|
+
* 设计档 §需求② 的三层源做成一个**纯解析器**:
|
|
9
|
+
*
|
|
10
|
+
* ```
|
|
11
|
+
* ① 线上目录(URL 由调用方传入 —— 本文件零默认地址,见下「为什么不写死 URL」)
|
|
12
|
+
* ↓ 拉取失败 / 离线 / 校验不过(整份弃用)
|
|
13
|
+
* ② 包内兜底 = PROVIDER_PRESETS(零改动即兜底)
|
|
14
|
+
* ↓
|
|
15
|
+
* ③ 用户本地覆盖(自定义 provider,合并语义:同 id 用户 > 线上 > 包内)
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* ## 四条设计纪律(每条都在 pure 门 MC 段有断言)
|
|
19
|
+
*
|
|
20
|
+
* 1. **零 Node、零自发网络**:本文件不 import 任何 `node:*`,也**不碰全局 `fetch`** ——
|
|
21
|
+
* 网络能力由调用方注入 `fetchJson` 端口。宿主没给 ⇒ 线上腿整条不启用(`reason: 'no-fetch-port'`),
|
|
22
|
+
* 而不是「偷偷换个方式做」(与 host.ts 的缺席语义同一条纪律)。
|
|
23
|
+
* ⇒ 桌面渲染进程 / 浏览器 / TUI 用同一份代码,各自注入自己的传输。
|
|
24
|
+
* 2. **绝不半解析**(设计档明写):`schemaVersion` 超出支持区间、`providers` 不是数组、
|
|
25
|
+
* **任何一行**形状不合格 ⇒ **整份**线上目录弃用走兜底。半解析 = 用户拿到一张自己也说不清
|
|
26
|
+
* 是哪一版的表。
|
|
27
|
+
* 3. **来源标注是产品位不是调试位**:返回值恒带 `source` 与 `online.{ok,reason}`。端据此渲
|
|
28
|
+
* 「目录为内置版本(离线)」——「静默用兜底」在本层必须是**可观测**的,否则用户会以为看到的
|
|
29
|
+
* 是最新表([honest-absence-not-fabricated-zero])。
|
|
30
|
+
* 4. **诚实缺席**:`staleness` 只在真吃到线上目录时在场;`ageMs` 只在调用方给了 `nowMs`
|
|
31
|
+
* 且 `generatedAt` 可解析时在场。不知道就是键缺席,绝不 `?? 0`,也绝不偷读时钟
|
|
32
|
+
* (时钟是宿主资产,与 `AdapterContext.now` 同一条口径)。
|
|
33
|
+
*
|
|
34
|
+
* ## 为什么不写死线上 URL
|
|
35
|
+
*
|
|
36
|
+
* 目录仓位(设计档待裁点 3:`sema-agent/model-catalog` 公开仓 or release asset)**候 clay 裁**。
|
|
37
|
+
* 机制先行、地址后填:URL 由调用方从配置/settings 传入。本文件里出现任何默认地址,都会让
|
|
38
|
+
* 「还没定的仓位」提前变成事实契约。https-only 是硬校验(目录是会被下载并影响**出站地址**的
|
|
39
|
+
* 数据,明文信道下可被改写成钓鱼网关)。
|
|
40
|
+
*
|
|
41
|
+
* ## 本批不做(留口注释,别当遗漏)
|
|
42
|
+
*
|
|
43
|
+
* · **缓存与 ETag**:`~/.sema/cache/model-catalog.json` + ETag/304 + 新鲜窗 + `localGeneratedAt`
|
|
44
|
+
* 防降级(设计档 pi 调研节)。落盘是宿主能力(fs 在 host.ts 的 `FsPort`),所以缓存层应当是
|
|
45
|
+
* **本模块之上**的一层:调用方拿 `resolveModelCatalog` 的结果自行落盘,并把上次的 `generatedAt`
|
|
46
|
+
* 与新拉到的比较后决定是否替换。⇒ 加缓存时**不需要**改本文件的形状,只需在 `fetchJson` 注入
|
|
47
|
+
* 处做 ETag,并在外层加一个「旧远端压不过新包」的比较。
|
|
48
|
+
* · **签名 / sha256**:同上,属传输层与目录产出方的约定(设计档待裁点 3)。
|
|
49
|
+
* · **`oauth` / `subscribe` 字段**:设计档待裁点 3b。本批只加纯链接位 `consoleUrl`/`signupUrl`
|
|
50
|
+
* (见 providerPresets.ts),授权码流/设备码流一律不进。
|
|
51
|
+
*
|
|
52
|
+
* ## probe 复用面评估(设计档 §需求②-C「复用面」;只评估不搬,搬迁下一批做)
|
|
53
|
+
*
|
|
54
|
+
* 桌面端 onboard 要「选 provider → 填 key → 探活」,探活那一步现在只在壳树。按
|
|
55
|
+
* [multiclient-migration-batch-discipline](模块级状态与读写口同包)逐个评估,结论如下 ——
|
|
56
|
+
* 这是下一批的搬迁清单,坐标为壳树 `src/sema/onboard/`:
|
|
57
|
+
*
|
|
58
|
+
* | 文件 | 结论 | 依据 |
|
|
59
|
+
* |---|---|---|
|
|
60
|
+
* | `probePlan.ts`(139 行) | ✅ **整搬,零改造** | 零 import、零模块级状态、纯函数四件(`buildVerdictRequest` / `extractApiError` / `looksLikeModelError` / `judgeMessagesProbe`)。判决表本身就是「三端必须一致」的那部分。 |
|
|
61
|
+
* | `probeProvider.ts`(223 行) | ⚠️ **拆搬**:判定+请求编排进包、传输与 TLS 留端 | ①裸 `fetch`/`AbortController`/`setTimeout` ⇒ 改注入 `fetchImpl`(本包既有姿势:`limitsWire`/`detachWire`/`liveInitToolFace` 的 `opts.fetchImpl`);②`ensureProxyBootstrap()` 动态 import 壳的 `utils/proxy.js`(`configureGlobalAgents`)= **Node undici 专属**,浏览器无此概念 ⇒ 留端,做成可选的「拨号前置钩子」端口;③`isCertError`/`applySystemTlsTrust`(壳 `tlsTrust.ts`,读系统钥匙串 + 设 `NODE_EXTRA_CA_CERTS`)= 纯 Node ⇒ 留端,做成可选的「一次性重试钩子」;④`nearestListed()` 与 `unwrapFetchError()` 是纯函数 ⇒ 直接进包。搬后包内出口 ≈ `probeProvider({ baseUrl, api, apiKey, modelId }, { fetchImpl, beforeDial?, retryOnCertError? })`。 |
|
|
62
|
+
* | `detectSources.ts`(524 行) | ❌ **本批不搬** | 它整个是**宿主文件系统扫描器**:`node:fs`(existsSync/readdirSync/readFileSync/statSync)+ `node:os` homedir + `node:path` + `process.env` + 两个外部解析器(`jsonc-parser`/`yaml`)+ 壳内 `ccConfigFallback`。可搬的只有「读到的配置 → `ImportableProvider` 的分类/归一逻辑」那一小半,而它与扫描代码是逐行交织的。⇒ 要搬得先在壳里把「扫描(IO)」与「分类(纯)」拆开,是独立一批的量。**桌面端不阻塞**:桌面 onboard 的导入源探测本就该是主进程的活(与 CLI 同为 Node 侧),复用价值远低于 probe 两件。 |
|
|
63
|
+
* | `onboardZeroModel.ts`(2.8K) | 🔶 **顺路可搬** | 已有独立单测(`onboardZeroModel.test.ts`),看形状是纯判定;搬前需确认零壳态依赖。 |
|
|
64
|
+
* | `applyOnboard.ts` / `OnboardWizard.tsx` / `BigStepTitle.tsx` / `ccSwitchReader.ts` | ❌ 属端 | 落盘编排 / Ink 渲染 / 读 cc-switch sqlite —— 宿主辖区,桌面端各写各的渲染器(设计档 §C 与 pi 的 `AuthInteraction` seam 同一心智)。 |
|
|
65
|
+
*
|
|
66
|
+
* 搬迁批建议顺序:`probePlan`(零风险打头,先让包里有判决表)→ `probeProvider` 拆搬(带
|
|
67
|
+
* fetchImpl 注入 + 两个可选钩子端口)→ `onboardZeroModel` → detectSources 拆分单列一批。
|
|
68
|
+
*/
|
|
69
|
+
import { PROVIDER_PRESETS } from './providerPresets.js';
|
|
70
|
+
/** 支持的目录 schemaVersion 区间(闭区间)。产目录那一方按这个区间发版。 */
|
|
71
|
+
export const CATALOG_SCHEMA_VERSION_MIN = 1;
|
|
72
|
+
export const CATALOG_SCHEMA_VERSION_MAX = 1;
|
|
73
|
+
/** schemaVersion 是否落在支持区间内(非整数一律否)。 */
|
|
74
|
+
export function isSupportedCatalogSchemaVersion(v) {
|
|
75
|
+
return (typeof v === 'number' &&
|
|
76
|
+
Number.isInteger(v) &&
|
|
77
|
+
v >= CATALOG_SCHEMA_VERSION_MIN &&
|
|
78
|
+
v <= CATALOG_SCHEMA_VERSION_MAX);
|
|
79
|
+
}
|
|
80
|
+
function isProviderRow(v) {
|
|
81
|
+
if (v === null || typeof v !== 'object' || Array.isArray(v))
|
|
82
|
+
return false;
|
|
83
|
+
const r = v;
|
|
84
|
+
return (typeof r.id === 'string' &&
|
|
85
|
+
r.id.length > 0 &&
|
|
86
|
+
typeof r.name === 'string' &&
|
|
87
|
+
typeof r.baseUrl === 'string' &&
|
|
88
|
+
(r.api === 'anthropic-messages' || r.api === 'openai-completions') &&
|
|
89
|
+
typeof r.authEnv === 'string' &&
|
|
90
|
+
Array.isArray(r.models));
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* 校验线上载荷 —— **纯同步**,端可以单用(比如「刷新目录」按钮先本地校验再落盘)。
|
|
94
|
+
* 🔴 全或无:任何一条不合格 ⇒ 整份拒,绝不半解析。
|
|
95
|
+
*/
|
|
96
|
+
export function validateOnlineCatalog(payload) {
|
|
97
|
+
if (payload === null || typeof payload !== 'object' || Array.isArray(payload)) {
|
|
98
|
+
return { ok: false, reason: 'payload-not-object', detail: 'catalog payload is not a JSON object' };
|
|
99
|
+
}
|
|
100
|
+
const doc = payload;
|
|
101
|
+
if (typeof doc.schemaVersion !== 'number' || !Number.isInteger(doc.schemaVersion)) {
|
|
102
|
+
return { ok: false, reason: 'schema-version-missing', detail: 'schemaVersion missing or not an integer' };
|
|
103
|
+
}
|
|
104
|
+
if (!isSupportedCatalogSchemaVersion(doc.schemaVersion)) {
|
|
105
|
+
return {
|
|
106
|
+
ok: false,
|
|
107
|
+
reason: 'schema-version-unsupported',
|
|
108
|
+
detail: `schemaVersion ${doc.schemaVersion} outside supported range ${CATALOG_SCHEMA_VERSION_MIN}..${CATALOG_SCHEMA_VERSION_MAX}`,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
if (typeof doc.generatedAt !== 'string' || doc.generatedAt.length === 0) {
|
|
112
|
+
return { ok: false, reason: 'generated-at-missing', detail: 'generatedAt missing' };
|
|
113
|
+
}
|
|
114
|
+
if (!Array.isArray(doc.providers)) {
|
|
115
|
+
return { ok: false, reason: 'providers-not-array', detail: 'providers is not an array' };
|
|
116
|
+
}
|
|
117
|
+
const bad = doc.providers.findIndex((row) => !isProviderRow(row));
|
|
118
|
+
if (bad >= 0) {
|
|
119
|
+
return {
|
|
120
|
+
ok: false,
|
|
121
|
+
reason: 'provider-row-invalid',
|
|
122
|
+
// 只报**位置**不报内容:载荷是外部数据,回显 = 把不可信内容带进日志面。
|
|
123
|
+
detail: `provider row #${bad} failed the shape gate (whole document rejected — no partial parse)`,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
ok: true,
|
|
128
|
+
doc: {
|
|
129
|
+
schemaVersion: doc.schemaVersion,
|
|
130
|
+
generatedAt: doc.generatedAt,
|
|
131
|
+
providers: doc.providers,
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* 按 id 分层合并:**后来的层赢**,行序 = 首见序(包内序稳定在前,新家按层依次追加)。
|
|
137
|
+
* 纯函数,端可以单用(比如只合并「包内 + 用户覆盖」的离线路径)。
|
|
138
|
+
*/
|
|
139
|
+
export function mergeProviderLayers(...layers) {
|
|
140
|
+
const order = [];
|
|
141
|
+
const byId = new Map();
|
|
142
|
+
for (const layer of layers) {
|
|
143
|
+
for (const row of layer) {
|
|
144
|
+
if (!row || typeof row.id !== 'string' || row.id.length === 0)
|
|
145
|
+
continue;
|
|
146
|
+
if (!byId.has(row.id))
|
|
147
|
+
order.push(row.id);
|
|
148
|
+
byId.set(row.id, row);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
const out = [];
|
|
152
|
+
for (const id of order) {
|
|
153
|
+
const row = byId.get(id);
|
|
154
|
+
if (row !== undefined)
|
|
155
|
+
out.push(row);
|
|
156
|
+
}
|
|
157
|
+
return out;
|
|
158
|
+
}
|
|
159
|
+
/** https-only(目录会影响出站地址,明文信道可被改写成钓鱼网关)。 */
|
|
160
|
+
function isHttpsUrl(u) {
|
|
161
|
+
return /^https:\/\/\S+$/i.test(u.trim());
|
|
162
|
+
}
|
|
163
|
+
/** 异常 → 短 detail(只取 message 前段,绝不带栈、绝不带载荷)。 */
|
|
164
|
+
function shortError(e) {
|
|
165
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
166
|
+
return msg.slice(0, 160);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* 解析出一份可用的 provider 目录 —— 三层源 + 来源标注。**永不 reject**:线上腿的任何失败都
|
|
170
|
+
* 收敛成 `online.ok === false` 并回落包内表(离线可用是硬需求)。
|
|
171
|
+
*/
|
|
172
|
+
export async function resolveModelCatalog(opts) {
|
|
173
|
+
const overrides = opts?.overrides ?? [];
|
|
174
|
+
let onlineDoc = null;
|
|
175
|
+
let online;
|
|
176
|
+
const url = opts?.onlineUrl?.trim();
|
|
177
|
+
if (!url) {
|
|
178
|
+
online = { ok: false, reason: 'no-online-url', detail: 'no online catalog URL configured' };
|
|
179
|
+
}
|
|
180
|
+
else if (typeof opts?.fetchJson !== 'function') {
|
|
181
|
+
online = { ok: false, reason: 'no-fetch-port', detail: 'host did not install a fetchJson port' };
|
|
182
|
+
}
|
|
183
|
+
else if (!isHttpsUrl(url)) {
|
|
184
|
+
// 🔴 校验在拨号**之前**:非 https 连请求都不发。
|
|
185
|
+
online = { ok: false, reason: 'insecure-url', detail: 'online catalog URL must be https' };
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
// 单发不重试:重试/超时预算属宿主传输层(注入的 fetchJson 自己带)。
|
|
189
|
+
let payload;
|
|
190
|
+
let dialFailure = null;
|
|
191
|
+
try {
|
|
192
|
+
payload = await opts.fetchJson(url);
|
|
193
|
+
}
|
|
194
|
+
catch (e) {
|
|
195
|
+
dialFailure = { ok: false, reason: 'fetch-failed', detail: shortError(e) };
|
|
196
|
+
}
|
|
197
|
+
if (dialFailure !== null) {
|
|
198
|
+
online = dialFailure;
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
const v = validateOnlineCatalog(payload);
|
|
202
|
+
if (v.ok) {
|
|
203
|
+
onlineDoc = v.doc;
|
|
204
|
+
online = { ok: true };
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
online = { ok: false, reason: v.reason, ...(v.detail !== undefined ? { detail: v.detail } : {}) };
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
const providers = mergeProviderLayers(PROVIDER_PRESETS, onlineDoc?.providers ?? [], overrides);
|
|
212
|
+
const overrideIds = mergeProviderLayers(overrides).map((p) => p.id);
|
|
213
|
+
const source = overrideIds.length > 0 ? 'merged' : onlineDoc !== null ? 'online' : 'bundled';
|
|
214
|
+
const result = { providers, source, online, overrideIds };
|
|
215
|
+
if (onlineDoc !== null) {
|
|
216
|
+
const parsed = Date.parse(onlineDoc.generatedAt);
|
|
217
|
+
const ageMs = opts?.nowMs !== undefined && Number.isFinite(parsed) ? opts.nowMs - parsed : undefined;
|
|
218
|
+
result.staleness = {
|
|
219
|
+
generatedAt: onlineDoc.generatedAt,
|
|
220
|
+
...(ageMs !== undefined ? { ageMs } : {}),
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
return result;
|
|
224
|
+
}
|
|
@@ -14,6 +14,17 @@ export type ProviderPreset = {
|
|
|
14
14
|
models: PresetModel[];
|
|
15
15
|
/** Aggregator gateways (OpenRouter): model id is hand-typed, ctx/maxtok via family table. */
|
|
16
16
|
modelViaFamily?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* 🆕 design/160 需求②-B(2026-07-31):**去哪拿 key** 的控制台页(纯链接,离线也能显示)。
|
|
19
|
+
*
|
|
20
|
+
* 🔴 **诚实缺席**:只填能可靠确认的官网控制台地址;拿不准的家**不填**,绝不编 URL —— 这个字段
|
|
21
|
+
* 会被 onboard 直接拿去开浏览器,编一个 = 把用户送到错误的地方。本地/自建家(ollama /
|
|
22
|
+
* lmstudio / vllm / sglang / litellm)按定义没有控制台页,缺席是正确答案不是遗漏。
|
|
23
|
+
* 设计档待裁点 3b:`oauth` / `subscribe` 两类**本批不加**(授权码流/设备码流单独排)。
|
|
24
|
+
*/
|
|
25
|
+
consoleUrl?: string;
|
|
26
|
+
/** 没账号时的注册入口。与 `consoleUrl` 同页的家不重复填(缺席 = 用 consoleUrl 那条)。 */
|
|
27
|
+
signupUrl?: string;
|
|
17
28
|
};
|
|
18
29
|
export type ModelFamily = {
|
|
19
30
|
id: string;
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
"baseUrl": "https://api.anthropic.com",
|
|
7
7
|
"api": "anthropic-messages",
|
|
8
8
|
"authEnv": "ANTHROPIC_API_KEY",
|
|
9
|
+
"consoleUrl": "https://console.anthropic.com/settings/keys",
|
|
10
|
+
"signupUrl": "https://console.anthropic.com/",
|
|
9
11
|
"models": [
|
|
10
12
|
{
|
|
11
13
|
"id": "claude-fable-5",
|
|
@@ -53,6 +55,8 @@
|
|
|
53
55
|
"baseUrl": "https://api.openai.com/v1",
|
|
54
56
|
"api": "openai-completions",
|
|
55
57
|
"authEnv": "OPENAI_API_KEY",
|
|
58
|
+
"consoleUrl": "https://platform.openai.com/api-keys",
|
|
59
|
+
"signupUrl": "https://platform.openai.com/signup",
|
|
56
60
|
"models": [
|
|
57
61
|
{
|
|
58
62
|
"id": "gpt-5.5",
|
|
@@ -106,6 +110,8 @@
|
|
|
106
110
|
"baseUrl": "https://api.deepseek.com/v1",
|
|
107
111
|
"api": "openai-completions",
|
|
108
112
|
"authEnv": "DEEPSEEK_API_KEY",
|
|
113
|
+
"consoleUrl": "https://platform.deepseek.com/api_keys",
|
|
114
|
+
"signupUrl": "https://platform.deepseek.com/sign_up",
|
|
109
115
|
"models": [
|
|
110
116
|
{
|
|
111
117
|
"id": "deepseek-v4-pro",
|
|
@@ -137,6 +143,7 @@
|
|
|
137
143
|
"baseUrl": "https://dashscope.aliyuncs.com/compatible-mode/v1",
|
|
138
144
|
"api": "openai-completions",
|
|
139
145
|
"authEnv": "DASHSCOPE_API_KEY",
|
|
146
|
+
"consoleUrl": "https://bailian.console.aliyun.com/",
|
|
140
147
|
"models": [
|
|
141
148
|
{
|
|
142
149
|
"id": "qwen3.5-plus",
|
|
@@ -179,6 +186,7 @@
|
|
|
179
186
|
"baseUrl": "https://api.moonshot.cn/v1",
|
|
180
187
|
"api": "openai-completions",
|
|
181
188
|
"authEnv": "MOONSHOT_API_KEY",
|
|
189
|
+
"consoleUrl": "https://platform.moonshot.cn/console/api-keys",
|
|
182
190
|
"models": [
|
|
183
191
|
{
|
|
184
192
|
"id": "kimi-k3",
|
|
@@ -220,6 +228,7 @@
|
|
|
220
228
|
"baseUrl": "https://open.bigmodel.cn/api/paas/v4",
|
|
221
229
|
"api": "openai-completions",
|
|
222
230
|
"authEnv": "ZHIPU_API_KEY",
|
|
231
|
+
"consoleUrl": "https://open.bigmodel.cn/usercenter/apikeys",
|
|
223
232
|
"models": [
|
|
224
233
|
{
|
|
225
234
|
"id": "glm-5.2",
|
|
@@ -267,6 +276,7 @@
|
|
|
267
276
|
"baseUrl": "https://api.z.ai/api/paas/v4",
|
|
268
277
|
"api": "openai-completions",
|
|
269
278
|
"authEnv": "ZAI_API_KEY",
|
|
279
|
+
"consoleUrl": "https://z.ai/manage-apikey/apikey-list",
|
|
270
280
|
"models": [
|
|
271
281
|
{
|
|
272
282
|
"id": "glm-5.2",
|
|
@@ -309,6 +319,7 @@
|
|
|
309
319
|
"baseUrl": "https://api.minimax.io/anthropic",
|
|
310
320
|
"api": "anthropic-messages",
|
|
311
321
|
"authEnv": "MINIMAX_API_KEY",
|
|
322
|
+
"consoleUrl": "https://platform.minimax.io/",
|
|
312
323
|
"models": [
|
|
313
324
|
{
|
|
314
325
|
"id": "MiniMax-M3",
|
|
@@ -335,6 +346,7 @@
|
|
|
335
346
|
"baseUrl": "https://api.minimaxi.com/anthropic",
|
|
336
347
|
"api": "anthropic-messages",
|
|
337
348
|
"authEnv": "MINIMAX_CN_API_KEY",
|
|
349
|
+
"consoleUrl": "https://platform.minimaxi.com/",
|
|
338
350
|
"models": [
|
|
339
351
|
{
|
|
340
352
|
"id": "MiniMax-M3",
|
|
@@ -355,6 +367,7 @@
|
|
|
355
367
|
"baseUrl": "https://api.moonshot.ai/v1",
|
|
356
368
|
"api": "openai-completions",
|
|
357
369
|
"authEnv": "MOONSHOT_API_KEY",
|
|
370
|
+
"consoleUrl": "https://platform.moonshot.ai/console/api-keys",
|
|
358
371
|
"models": [
|
|
359
372
|
{
|
|
360
373
|
"id": "kimi-k2.6",
|
|
@@ -401,6 +414,7 @@
|
|
|
401
414
|
"baseUrl": "https://qianfan.baidubce.com/v2",
|
|
402
415
|
"api": "openai-completions",
|
|
403
416
|
"authEnv": "QIANFAN_API_KEY",
|
|
417
|
+
"consoleUrl": "https://console.bce.baidu.com/qianfan/",
|
|
404
418
|
"models": [
|
|
405
419
|
{
|
|
406
420
|
"id": "ernie-5.0-thinking-preview",
|
|
@@ -421,6 +435,7 @@
|
|
|
421
435
|
"baseUrl": "https://ark.cn-beijing.volces.com/api/v3",
|
|
422
436
|
"api": "openai-completions",
|
|
423
437
|
"authEnv": "VOLCANO_ENGINE_API_KEY",
|
|
438
|
+
"consoleUrl": "https://console.volcengine.com/ark",
|
|
424
439
|
"models": [
|
|
425
440
|
{
|
|
426
441
|
"id": "doubao-seed-code-preview-251028",
|
|
@@ -504,6 +519,8 @@
|
|
|
504
519
|
"baseUrl": "https://openrouter.ai/api/v1",
|
|
505
520
|
"api": "openai-completions",
|
|
506
521
|
"authEnv": "OPENROUTER_API_KEY",
|
|
522
|
+
"consoleUrl": "https://openrouter.ai/keys",
|
|
523
|
+
"signupUrl": "https://openrouter.ai/",
|
|
507
524
|
"models": [
|
|
508
525
|
{
|
|
509
526
|
"id": "openrouter/auto",
|
|
@@ -525,6 +542,7 @@
|
|
|
525
542
|
"baseUrl": "https://api.x.ai/v1",
|
|
526
543
|
"api": "openai-completions",
|
|
527
544
|
"authEnv": "XAI_API_KEY",
|
|
545
|
+
"consoleUrl": "https://console.x.ai/",
|
|
528
546
|
"models": [
|
|
529
547
|
{
|
|
530
548
|
"id": "grok-4.3",
|
|
@@ -552,6 +570,7 @@
|
|
|
552
570
|
"baseUrl": "https://api.mistral.ai/v1",
|
|
553
571
|
"api": "openai-completions",
|
|
554
572
|
"authEnv": "MISTRAL_API_KEY",
|
|
573
|
+
"consoleUrl": "https://console.mistral.ai/api-keys",
|
|
555
574
|
"models": [
|
|
556
575
|
{
|
|
557
576
|
"id": "mistral-large-latest",
|
|
@@ -589,6 +608,7 @@
|
|
|
589
608
|
"baseUrl": "https://api.groq.com/openai/v1",
|
|
590
609
|
"api": "openai-completions",
|
|
591
610
|
"authEnv": "GROQ_API_KEY",
|
|
611
|
+
"consoleUrl": "https://console.groq.com/keys",
|
|
592
612
|
"models": [
|
|
593
613
|
{
|
|
594
614
|
"id": "groq/compound",
|
|
@@ -615,6 +635,7 @@
|
|
|
615
635
|
"baseUrl": "https://api.together.xyz/v1",
|
|
616
636
|
"api": "openai-completions",
|
|
617
637
|
"authEnv": "TOGETHER_API_KEY",
|
|
638
|
+
"consoleUrl": "https://api.together.xyz/settings/api-keys",
|
|
618
639
|
"models": [
|
|
619
640
|
{
|
|
620
641
|
"id": "moonshotai/Kimi-K2.6",
|
|
@@ -645,6 +666,7 @@
|
|
|
645
666
|
"baseUrl": "https://api.fireworks.ai/inference/v1",
|
|
646
667
|
"api": "openai-completions",
|
|
647
668
|
"authEnv": "FIREWORKS_API_KEY",
|
|
669
|
+
"consoleUrl": "https://fireworks.ai/account/api-keys",
|
|
648
670
|
"models": [
|
|
649
671
|
{
|
|
650
672
|
"id": "accounts/fireworks/models/kimi-k2p6",
|
|
@@ -691,6 +713,7 @@
|
|
|
691
713
|
"baseUrl": "https://ark.ap-southeast.bytepluses.com/api/v3",
|
|
692
714
|
"api": "openai-completions",
|
|
693
715
|
"authEnv": "BYTEPLUS_API_KEY",
|
|
716
|
+
"consoleUrl": "https://console.byteplus.com/",
|
|
694
717
|
"models": [
|
|
695
718
|
{
|
|
696
719
|
"id": "seed-1-8-251228",
|
|
@@ -717,6 +740,7 @@
|
|
|
717
740
|
"baseUrl": "https://api.cerebras.ai/v1",
|
|
718
741
|
"api": "openai-completions",
|
|
719
742
|
"authEnv": "CEREBRAS_API_KEY",
|
|
743
|
+
"consoleUrl": "https://cloud.cerebras.ai/",
|
|
720
744
|
"models": [
|
|
721
745
|
{
|
|
722
746
|
"id": "zai-glm-4.7",
|
|
@@ -775,6 +799,7 @@
|
|
|
775
799
|
"baseUrl": "https://api.cohere.ai/compatibility/v1",
|
|
776
800
|
"api": "openai-completions",
|
|
777
801
|
"authEnv": "COHERE_API_KEY",
|
|
802
|
+
"consoleUrl": "https://dashboard.cohere.com/api-keys",
|
|
778
803
|
"models": [
|
|
779
804
|
{
|
|
780
805
|
"id": "command-a-03-2025",
|
|
@@ -790,6 +815,7 @@
|
|
|
790
815
|
"baseUrl": "https://api.deepinfra.com/v1/openai",
|
|
791
816
|
"api": "openai-completions",
|
|
792
817
|
"authEnv": "DEEPINFRA_API_KEY",
|
|
818
|
+
"consoleUrl": "https://deepinfra.com/dash/api_keys",
|
|
793
819
|
"models": [
|
|
794
820
|
{
|
|
795
821
|
"id": "deepseek-ai/DeepSeek-V4-Flash",
|
|
@@ -842,6 +868,8 @@
|
|
|
842
868
|
"baseUrl": "https://router.huggingface.co/v1",
|
|
843
869
|
"api": "openai-completions",
|
|
844
870
|
"authEnv": "HF_TOKEN",
|
|
871
|
+
"consoleUrl": "https://huggingface.co/settings/tokens",
|
|
872
|
+
"signupUrl": "https://huggingface.co/join",
|
|
845
873
|
"models": [
|
|
846
874
|
{
|
|
847
875
|
"id": "deepseek-ai/DeepSeek-V3.1",
|
|
@@ -947,6 +975,7 @@
|
|
|
947
975
|
"baseUrl": "https://integrate.api.nvidia.com/v1",
|
|
948
976
|
"api": "openai-completions",
|
|
949
977
|
"authEnv": "NVIDIA_API_KEY",
|
|
978
|
+
"consoleUrl": "https://build.nvidia.com/",
|
|
950
979
|
"models": [
|
|
951
980
|
{
|
|
952
981
|
"id": "nvidia/nemotron-3-ultra-550b-a55b",
|
|
@@ -1064,6 +1093,7 @@
|
|
|
1064
1093
|
"baseUrl": "https://api.stepfun.com/v1",
|
|
1065
1094
|
"api": "openai-completions",
|
|
1066
1095
|
"authEnv": "STEPFUN_API_KEY",
|
|
1096
|
+
"consoleUrl": "https://platform.stepfun.com/",
|
|
1067
1097
|
"models": [
|
|
1068
1098
|
{
|
|
1069
1099
|
"id": "step-3.5-flash",
|
|
@@ -1143,6 +1173,7 @@
|
|
|
1143
1173
|
"baseUrl": "https://ai-gateway.vercel.sh",
|
|
1144
1174
|
"api": "anthropic-messages",
|
|
1145
1175
|
"authEnv": "AI_GATEWAY_API_KEY",
|
|
1176
|
+
"consoleUrl": "https://vercel.com/dashboard",
|
|
1146
1177
|
"models": [
|
|
1147
1178
|
{
|
|
1148
1179
|
"id": "anthropic/claude-opus-4.6",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sema-agent/client-core",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.18",
|
|
4
4
|
"description": "Client-side session runtime shared by every sema human client (TUI / web / desktop): sema wire frames (AgentEvent) -> CC session vocabulary (SDKMessage) with dual-plane output (transcript/chrome), deterministic transcript ids, lane discipline as a type, and the notification/dedup ledgers. Every CC-skin shape is collected here so the wire itself stays neutral. Blackboard [1832] design axioms; [1651]/[1652]/[1653] signed seam design. Renamed from @sema-agent/wire-cc-adapter (0.1.x).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|