@armadra/agent 0.6.1 → 0.6.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.
Files changed (40) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/CHANGELOG.zh-CN.md +12 -0
  3. package/dist/agent/session-settings.d.ts +4 -1
  4. package/dist/agent/session-settings.js +7 -2
  5. package/dist/ai/apis/chatgpt-backend.d.ts +7 -2
  6. package/dist/ai/apis/chatgpt-backend.js +12 -4
  7. package/dist/ai/providers/discovered-cache.d.ts +22 -11
  8. package/dist/ai/providers/discovered-cache.js +56 -12
  9. package/dist/ai/providers/registry.js +7 -6
  10. package/dist/ai/types.d.ts +5 -0
  11. package/dist/auth/chatgpt/backend-client.d.ts +11 -2
  12. package/dist/auth/chatgpt/backend-client.js +38 -6
  13. package/dist/auth/chatgpt/cli.d.ts +2 -1
  14. package/dist/auth/chatgpt/cli.js +14 -7
  15. package/dist/auth/chatgpt/follow.d.ts +16 -0
  16. package/dist/auth/chatgpt/follow.js +51 -0
  17. package/dist/auth/chatgpt/presets.d.ts +8 -0
  18. package/dist/auth/chatgpt/presets.js +12 -0
  19. package/dist/auth/testing/fake-oauth.d.ts +4 -1
  20. package/dist/auth/testing/fake-oauth.js +24 -1
  21. package/dist/bundle/ama.cjs +341 -180
  22. package/dist/cli/compose-providers.d.ts +2 -1
  23. package/dist/cli/compose-providers.js +6 -3
  24. package/dist/cli/startup-steps.js +4 -1
  25. package/dist/cli/subcommands/models-discover.d.ts +4 -2
  26. package/dist/cli/subcommands/models-discover.js +40 -8
  27. package/dist/config/json-schema.js +1 -0
  28. package/dist/config/key-docs.js +7 -1
  29. package/dist/config/schema-w6.d.ts +1 -1
  30. package/dist/config/schema-w6.js +2 -1
  31. package/dist/config/types-w6.d.ts +5 -0
  32. package/dist/i18n/catalog.d.ts +7 -0
  33. package/dist/i18n/messages/auth.d.ts +12 -0
  34. package/dist/i18n/messages/auth.js +20 -0
  35. package/dist/i18n/messages/config-keys.d.ts +2 -0
  36. package/dist/i18n/messages/config-keys.js +2 -0
  37. package/dist/i18n/messages/config.d.ts +2 -0
  38. package/docs/en/providers.md +8 -4
  39. package/docs/providers.md +25 -10
  40. package/package.json +1 -1
@@ -9,6 +9,10 @@
9
9
  *
10
10
  * 覆盖:`AMA_CHATGPT_CLIENT_ID` / `AMA_CHATGPT_ISSUER`(测试用)/ `AMA_CHATGPT_BASE_URL` > 配置
11
11
  * `auth.chatgpt.*` > 条目里签发的 client id > 缺省。
12
+ *
13
+ * codex 后端的模型列表按 `client_version` 过滤(每个模型带 `minimal_client_version`,版本过低一个都不给):
14
+ * 发的是 Codex CLI 的版本号而不是 ama 的,`AMA_CHATGPT_CODEX_CLIENT_VERSION` > `auth.chatgpt.codexClientVersion`
15
+ * > `DEFAULT_CODEX_CLIENT_VERSION`。推理请求不带版本(Codex 自己也只在 User-Agent 里带)。
12
16
  */
13
17
  import type { ChatGptAuthConfig, ChatGptFlavor, OAuthAuthEntry } from "../../config/types-w6.js";
14
18
  export declare const CHATGPT_PROVIDER_ID = "chatgpt";
@@ -19,6 +23,8 @@ export declare const SIWC_RESOURCE = "https://api.openai.com/v1";
19
23
  export declare const SIWC_REQUIRED_SCOPE = "chatgpt.tokens.use.direct";
20
24
  export declare const AGENT_NAME_HINT = "ama";
21
25
  export declare const DEFAULT_ORIGINATOR = "codex_cli_rs";
26
+ /** codex 后端 `GET /models?client_version=` 用的 Codex CLI 版本号(模型列表按它过滤)。 */
27
+ export declare const DEFAULT_CODEX_CLIENT_VERSION = "0.160.0";
22
28
  export declare const CHATGPT_BASE_URLS: Readonly<Record<ChatGptFlavor, string>>;
23
29
  export interface AuthorizeInput {
24
30
  redirectUri: string;
@@ -61,6 +67,8 @@ export interface PresetOverrides {
61
67
  port?: number | undefined;
62
68
  }
63
69
  export declare function chatgptBaseUrl(flavor: ChatGptFlavor, env?: Readonly<Record<string, string | undefined>>): string;
70
+ /** codex 模型列表的 `client_version`:环境变量 > 用户级配置 > 缺省。 */
71
+ export declare function codexClientVersion(config?: ChatGptAuthConfig, env?: Readonly<Record<string, string | undefined>>): string;
64
72
  export declare function resolveChatGptPreset(flavor: ChatGptFlavor, overrides?: PresetOverrides): ChatGptPreset;
65
73
  /** 授权 URL(参数顺序固定,便于测试)。 */
66
74
  export declare function authorizeUrl(preset: ChatGptPreset, input: AuthorizeInput): string;
@@ -9,6 +9,10 @@
9
9
  *
10
10
  * 覆盖:`AMA_CHATGPT_CLIENT_ID` / `AMA_CHATGPT_ISSUER`(测试用)/ `AMA_CHATGPT_BASE_URL` > 配置
11
11
  * `auth.chatgpt.*` > 条目里签发的 client id > 缺省。
12
+ *
13
+ * codex 后端的模型列表按 `client_version` 过滤(每个模型带 `minimal_client_version`,版本过低一个都不给):
14
+ * 发的是 Codex CLI 的版本号而不是 ama 的,`AMA_CHATGPT_CODEX_CLIENT_VERSION` > `auth.chatgpt.codexClientVersion`
15
+ * > `DEFAULT_CODEX_CLIENT_VERSION`。推理请求不带版本(Codex 自己也只在 User-Agent 里带)。
12
16
  */
13
17
  export const CHATGPT_PROVIDER_ID = "chatgpt";
14
18
  export const DEFAULT_ISSUER = "https://auth.openai.com";
@@ -18,6 +22,8 @@ export const SIWC_RESOURCE = "https://api.openai.com/v1";
18
22
  export const SIWC_REQUIRED_SCOPE = "chatgpt.tokens.use.direct";
19
23
  export const AGENT_NAME_HINT = "ama";
20
24
  export const DEFAULT_ORIGINATOR = "codex_cli_rs";
25
+ /** codex 后端 `GET /models?client_version=` 用的 Codex CLI 版本号(模型列表按它过滤)。 */
26
+ export const DEFAULT_CODEX_CLIENT_VERSION = "0.160.0";
21
27
  export const CHATGPT_BASE_URLS = {
22
28
  siwc: "https://api.openai.com/v1",
23
29
  codex: "https://chatgpt.com/backend-api/codex",
@@ -29,6 +35,12 @@ function nonEmpty(value) {
29
35
  export function chatgptBaseUrl(flavor, env = process.env) {
30
36
  return nonEmpty(env["AMA_CHATGPT_BASE_URL"]) ?? CHATGPT_BASE_URLS[flavor];
31
37
  }
38
+ /** codex 模型列表的 `client_version`:环境变量 > 用户级配置 > 缺省。 */
39
+ export function codexClientVersion(config, env = process.env) {
40
+ return (nonEmpty(env["AMA_CHATGPT_CODEX_CLIENT_VERSION"]) ??
41
+ nonEmpty(config?.codexClientVersion) ??
42
+ DEFAULT_CODEX_CLIENT_VERSION);
43
+ }
32
44
  export function resolveChatGptPreset(flavor, overrides = {}) {
33
45
  const env = overrides.env ?? process.env;
34
46
  const config = overrides.config;
@@ -4,7 +4,8 @@
4
4
  * OAuth:authorize(记参数、302 回 redirect_uri;SIWC 注册时带签发的 client_id 与 scope)、token(校验 PKCE;
5
5
  * 授权码 / 刷新;刷新轮换,旧 refresh token 再用 → `refresh_token_reused`)、deviceauth、discovery + JWKS
6
6
  * (现场生成 RSA 密钥对)、revoke。后端:`/v1/responses`、`/codex/responses`、`/v1/models`、`/codex/models`、
7
- * `/wham/usage`,按脚本回放。
7
+ * `/wham/usage`,按脚本回放;给了 `codexModels` 时 `/codex/models` 改按 `client_version` 过滤(不带 → 400,
8
+ * 低于条目的 `minimal_client_version` 不给),与真实 codex 后端一致。
8
9
  */
9
10
  export interface RecordedRequest {
10
11
  method: string;
@@ -37,6 +38,8 @@ export interface FakeOAuthOptions {
37
38
  /** 设备码轮询先返回几次 403。 */
38
39
  devicePending?: number;
39
40
  marker?: string;
41
+ /** codex 模型目录(`/codex/models` 按 `client_version` 过滤后返回 `{ models }`)。 */
42
+ codexModels?: Record<string, unknown>[];
40
43
  }
41
44
  export declare class FakeOAuthServer {
42
45
  readonly options: FakeOAuthOptions;
@@ -4,10 +4,22 @@
4
4
  * OAuth:authorize(记参数、302 回 redirect_uri;SIWC 注册时带签发的 client_id 与 scope)、token(校验 PKCE;
5
5
  * 授权码 / 刷新;刷新轮换,旧 refresh token 再用 → `refresh_token_reused`)、deviceauth、discovery + JWKS
6
6
  * (现场生成 RSA 密钥对)、revoke。后端:`/v1/responses`、`/codex/responses`、`/v1/models`、`/codex/models`、
7
- * `/wham/usage`,按脚本回放。
7
+ * `/wham/usage`,按脚本回放;给了 `codexModels` 时 `/codex/models` 改按 `client_version` 过滤(不带 → 400,
8
+ * 低于条目的 `minimal_client_version` 不给),与真实 codex 后端一致。
8
9
  */
9
10
  import { createHash, generateKeyPairSync, randomBytes, sign } from "node:crypto";
10
11
  import { createServer } from "node:http";
12
+ /** `a.b.c` 比较;非数字段按 0。 */
13
+ function compareVersion(a, b) {
14
+ const pa = a.split(".").map((x) => Number.parseInt(x, 10) || 0);
15
+ const pb = b.split(".").map((x) => Number.parseInt(x, 10) || 0);
16
+ for (let i = 0; i < Math.max(pa.length, pb.length); i++) {
17
+ const d = (pa[i] ?? 0) - (pb[i] ?? 0);
18
+ if (d !== 0)
19
+ return d;
20
+ }
21
+ return 0;
22
+ }
11
23
  function b64url(value) {
12
24
  return Buffer.from(JSON.stringify(value)).toString("base64url");
13
25
  }
@@ -212,6 +224,17 @@ export class FakeOAuthServer {
212
224
  code_challenge: "x",
213
225
  });
214
226
  }
227
+ case "/codex/models": {
228
+ const catalog = this.options.codexModels;
229
+ if (catalog === undefined)
230
+ return this.backend(res);
231
+ const version = url.searchParams.get("client_version");
232
+ if (version === null)
233
+ return json(400, { error: { message: "client_version required" } });
234
+ return json(200, {
235
+ models: catalog.filter((m) => compareVersion(version, String(m["minimal_client_version"] ?? "0")) >= 0),
236
+ });
237
+ }
215
238
  case "/revoke":
216
239
  this.revoked.push(params()["token"] ?? "");
217
240
  res.writeHead(200).end();