@rigelbuild/omp-catalog 18.0.3

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 (117) hide show
  1. package/CHANGELOG.md +1204 -0
  2. package/LICENSE +22 -0
  3. package/README.md +43 -0
  4. package/THIRD-PARTY-NOTICES.txt +22909 -0
  5. package/dist/types/build.d.ts +19 -0
  6. package/dist/types/compat/anthropic.d.ts +18 -0
  7. package/dist/types/compat/apply.d.ts +7 -0
  8. package/dist/types/compat/bedrock.d.ts +3 -0
  9. package/dist/types/compat/devin.d.ts +9 -0
  10. package/dist/types/compat/openai.d.ts +28 -0
  11. package/dist/types/discovery/antigravity.d.ts +70 -0
  12. package/dist/types/discovery/codex.d.ts +36 -0
  13. package/dist/types/discovery/cursor-proto.d.ts +5368 -0
  14. package/dist/types/discovery/cursor.d.ts +23 -0
  15. package/dist/types/discovery/devin-proto.d.ts +1549 -0
  16. package/dist/types/discovery/devin.d.ts +24 -0
  17. package/dist/types/discovery/gemini-cli.d.ts +33 -0
  18. package/dist/types/discovery/gemini.d.ts +25 -0
  19. package/dist/types/discovery/gitlab-duo-workflow.d.ts +30 -0
  20. package/dist/types/discovery/index.d.ts +7 -0
  21. package/dist/types/discovery/openai-compatible.d.ts +89 -0
  22. package/dist/types/discovery/protobuf.d.ts +102 -0
  23. package/dist/types/effort.d.ts +10 -0
  24. package/dist/types/fireworks-model-id.d.ts +23 -0
  25. package/dist/types/hosts.d.ts +169 -0
  26. package/dist/types/identity/bundled.d.ts +3 -0
  27. package/dist/types/identity/classify.d.ts +69 -0
  28. package/dist/types/identity/dialect.d.ts +3 -0
  29. package/dist/types/identity/family.d.ts +239 -0
  30. package/dist/types/identity/id.d.ts +12 -0
  31. package/dist/types/identity/index.d.ts +8 -0
  32. package/dist/types/identity/markers.d.ts +4 -0
  33. package/dist/types/identity/priority.d.ts +1 -0
  34. package/dist/types/identity/reference.d.ts +29 -0
  35. package/dist/types/index.d.ts +19 -0
  36. package/dist/types/model-cache.d.ts +23 -0
  37. package/dist/types/model-manager.d.ts +77 -0
  38. package/dist/types/model-thinking.d.ts +70 -0
  39. package/dist/types/model-tokenizer.d.ts +8 -0
  40. package/dist/types/models.d.ts +14 -0
  41. package/dist/types/openai-pricing.d.ts +17 -0
  42. package/dist/types/provider-models/bundled-references.d.ts +13 -0
  43. package/dist/types/provider-models/cache-provider-id.d.ts +11 -0
  44. package/dist/types/provider-models/descriptor-types.d.ts +76 -0
  45. package/dist/types/provider-models/descriptors.d.ts +505 -0
  46. package/dist/types/provider-models/google.d.ts +29 -0
  47. package/dist/types/provider-models/index.d.ts +7 -0
  48. package/dist/types/provider-models/ollama.d.ts +22 -0
  49. package/dist/types/provider-models/openai-compat.d.ts +646 -0
  50. package/dist/types/provider-models/special.d.ts +53 -0
  51. package/dist/types/types.d.ts +876 -0
  52. package/dist/types/utils.d.ts +20 -0
  53. package/dist/types/variant-collapse.d.ts +137 -0
  54. package/dist/types/wire/alibaba-token-plan.d.ts +22 -0
  55. package/dist/types/wire/codex.d.ts +66 -0
  56. package/dist/types/wire/coreweave.d.ts +12 -0
  57. package/dist/types/wire/gemini-headers.d.ts +62 -0
  58. package/dist/types/wire/github-copilot.d.ts +47 -0
  59. package/dist/types/wire/image-fetchers.d.ts +76 -0
  60. package/package.json +102 -0
  61. package/src/build.ts +103 -0
  62. package/src/compat/anthropic.ts +179 -0
  63. package/src/compat/apply.ts +15 -0
  64. package/src/compat/bedrock.ts +153 -0
  65. package/src/compat/devin.ts +12 -0
  66. package/src/compat/openai.ts +864 -0
  67. package/src/discovery/antigravity.ts +257 -0
  68. package/src/discovery/codex.ts +456 -0
  69. package/src/discovery/cursor-proto.ts +8329 -0
  70. package/src/discovery/cursor.ts +409 -0
  71. package/src/discovery/devin-proto.ts +2037 -0
  72. package/src/discovery/devin.ts +152 -0
  73. package/src/discovery/gemini-cli.ts +198 -0
  74. package/src/discovery/gemini.ts +260 -0
  75. package/src/discovery/gitlab-duo-workflow.ts +859 -0
  76. package/src/discovery/index.ts +7 -0
  77. package/src/discovery/openai-compatible.ts +266 -0
  78. package/src/discovery/protobuf.ts +1073 -0
  79. package/src/effort.ts +18 -0
  80. package/src/fireworks-model-id.ts +50 -0
  81. package/src/hosts.ts +178 -0
  82. package/src/identity/bundled.ts +28 -0
  83. package/src/identity/classify.ts +242 -0
  84. package/src/identity/dialect.ts +42 -0
  85. package/src/identity/family.ts +477 -0
  86. package/src/identity/id.ts +81 -0
  87. package/src/identity/index.ts +8 -0
  88. package/src/identity/markers.ts +49 -0
  89. package/src/identity/priority.ts +59 -0
  90. package/src/identity/reference.ts +167 -0
  91. package/src/index.ts +19 -0
  92. package/src/model-cache.ts +346 -0
  93. package/src/model-manager.ts +683 -0
  94. package/src/model-thinking.ts +925 -0
  95. package/src/model-tokenizer.ts +75 -0
  96. package/src/models.json +304657 -0
  97. package/src/models.json.d.ts +9 -0
  98. package/src/models.ts +108 -0
  99. package/src/openai-pricing.ts +29 -0
  100. package/src/provider-models/bundled-references.ts +87 -0
  101. package/src/provider-models/cache-provider-id.ts +97 -0
  102. package/src/provider-models/descriptor-types.ts +85 -0
  103. package/src/provider-models/descriptors.ts +585 -0
  104. package/src/provider-models/google.ts +117 -0
  105. package/src/provider-models/index.ts +7 -0
  106. package/src/provider-models/ollama.ts +204 -0
  107. package/src/provider-models/openai-compat.ts +6405 -0
  108. package/src/provider-models/special.ts +213 -0
  109. package/src/types.ts +1032 -0
  110. package/src/utils.ts +69 -0
  111. package/src/variant-collapse.ts +1555 -0
  112. package/src/wire/alibaba-token-plan.ts +54 -0
  113. package/src/wire/codex.ts +117 -0
  114. package/src/wire/coreweave.ts +51 -0
  115. package/src/wire/gemini-headers.ts +135 -0
  116. package/src/wire/github-copilot.ts +147 -0
  117. package/src/wire/image-fetchers.ts +159 -0
@@ -0,0 +1,54 @@
1
+ /**
2
+ * International (Singapore) Token Plan endpoint. Default region; keys issued by
3
+ * the international product authenticate only here.
4
+ */
5
+ export const ALIBABA_TOKEN_PLAN_BASE_URL = "https://token-plan.ap-southeast-1.maas.aliyuncs.com/compatible-mode/v1";
6
+ /**
7
+ * China (Beijing) Token Plan endpoint (百炼 Token Plan). Keys are region-locked:
8
+ * a Beijing-issued key is rejected by the international endpoint with
9
+ * `invalid_api_key`, and vice versa (#6682).
10
+ */
11
+ export const ALIBABA_TOKEN_PLAN_CN_BASE_URL = "https://token-plan.cn-beijing.maas.aliyuncs.com/compatible-mode/v1";
12
+
13
+ export interface AlibabaTokenPlanCredential {
14
+ token: string;
15
+ cookie?: string;
16
+ /**
17
+ * Region base URL the key authenticates against. Absent means the default
18
+ * international endpoint ({@link ALIBABA_TOKEN_PLAN_BASE_URL}).
19
+ */
20
+ baseUrl?: string;
21
+ }
22
+
23
+ const TOKEN_PATTERN = /^sk-[A-Za-z0-9._~+/-]+={0,2}$/;
24
+
25
+ export function parseAlibabaTokenPlanCredential(value: string): AlibabaTokenPlanCredential | null {
26
+ const trimmed = value.trim();
27
+ if (!trimmed) return null;
28
+ if (!trimmed.startsWith("{")) return TOKEN_PATTERN.test(trimmed) ? { token: trimmed } : null;
29
+ try {
30
+ const parsed = JSON.parse(trimmed) as { token?: unknown; cookie?: unknown; baseUrl?: unknown };
31
+ if (typeof parsed.token !== "string" || !TOKEN_PATTERN.test(parsed.token.trim())) return null;
32
+ if (parsed.cookie !== undefined && typeof parsed.cookie !== "string") return null;
33
+ if (parsed.baseUrl !== undefined && typeof parsed.baseUrl !== "string") return null;
34
+ const token = parsed.token.trim();
35
+ const cookie = parsed.cookie?.trim();
36
+ const baseUrl = parsed.baseUrl?.trim();
37
+ const credential: AlibabaTokenPlanCredential = { token };
38
+ if (cookie) credential.cookie = cookie;
39
+ if (baseUrl) credential.baseUrl = baseUrl;
40
+ return credential;
41
+ } catch {
42
+ return null;
43
+ }
44
+ }
45
+
46
+ export function serializeAlibabaTokenPlanCredential(token: string, cookie: string, baseUrl?: string): string {
47
+ const trimmedCookie = cookie.trim();
48
+ const trimmedBaseUrl = baseUrl?.trim();
49
+ if (!trimmedCookie && !trimmedBaseUrl) return token;
50
+ const payload: { token: string; cookie?: string; baseUrl?: string } = { token };
51
+ if (trimmedCookie) payload.cookie = trimmedCookie;
52
+ if (trimmedBaseUrl) payload.baseUrl = trimmedBaseUrl;
53
+ return JSON.stringify(payload);
54
+ }
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Constants for OpenAI Codex (ChatGPT OAuth) backend
3
+ */
4
+
5
+ export const CODEX_BASE_URL = "https://chatgpt.com/backend-api";
6
+
7
+ /**
8
+ * Pinned OpenAI Codex client version (corresponds to @openai/codex package version).
9
+ */
10
+ export const CODEX_CLIENT_VERSION = "0.144.1";
11
+
12
+ export const OPENAI_HEADERS = {
13
+ BETA: "OpenAI-Beta",
14
+ /** Codex feature-negotiation header; values identify opt-in wire protocols. */
15
+ CODEX_BETA_FEATURES: "x-codex-beta-features",
16
+ ACCOUNT_ID: "chatgpt-account-id",
17
+ ORIGINATOR: "originator",
18
+ VERSION: "version",
19
+ SESSION_ID: "session_id",
20
+ CONVERSATION_ID: "conversation_id",
21
+ SCOPED_SESSION_ID: "session-id",
22
+ THREAD_ID: "thread-id",
23
+ INSTALLATION_ID: "x-codex-installation-id",
24
+ WINDOW_ID: "x-codex-window-id",
25
+ TURN_METADATA: "x-codex-turn-metadata",
26
+ PARENT_THREAD_ID: "x-codex-parent-thread-id",
27
+ SUBAGENT: "x-openai-subagent",
28
+ /** Responses Lite transport marker (codex-rs `add_responses_lite_header`); value is always `"true"`. */
29
+ RESPONSES_LITE: "x-openai-internal-codex-responses-lite",
30
+ /** DeviceCheck attestation envelope (codex-rs `X_OAI_ATTESTATION_HEADER`); sent on ChatGPT-OAuth requests. */
31
+ ATTESTATION: "x-oai-attestation",
32
+ /** Client-declared data residency for region-pinned enterprise workspaces. */
33
+ RESIDENCY: "x-openai-internal-codex-residency",
34
+ } as const;
35
+
36
+ export const OPENAI_HEADER_VALUES = {
37
+ BETA_RESPONSES: "responses=experimental",
38
+ BETA_RESPONSES_WEBSOCKETS_V2: "responses_websockets=2026-02-06",
39
+ REMOTE_COMPACTION_V2: "remote_compaction_v2",
40
+ ORIGINATOR_CODEX: "pi",
41
+ } as const;
42
+
43
+ export const URL_PATHS = {
44
+ RESPONSES: "/responses",
45
+ CODEX_RESPONSES: "/codex/responses",
46
+ } as const;
47
+
48
+ export const JWT_CLAIM_PATH = "https://api.openai.com/auth" as const;
49
+
50
+ /**
51
+ * Extract account ID from a Codex JWT access token.
52
+ * Returns undefined if the token is not a valid Codex JWT.
53
+ */
54
+ export function getCodexAccountId(accessToken: string): string | undefined {
55
+ try {
56
+ const parts = accessToken.split(".");
57
+ if (parts.length !== 3) return undefined;
58
+ const decoded = Buffer.from(parts[1] ?? "", "base64").toString("utf-8");
59
+ const payload = JSON.parse(decoded) as Record<string, unknown>;
60
+ const auth = payload[JWT_CLAIM_PATH] as { chatgpt_account_id?: string } | undefined;
61
+ return auth?.chatgpt_account_id ?? undefined;
62
+ } catch {
63
+ return undefined;
64
+ }
65
+ }
66
+
67
+ /**
68
+ * Extract the account's data residency from a Codex JWT access token.
69
+ *
70
+ * Enterprise ChatGPT workspaces can be pinned to a region. Such a workspace
71
+ * rejects a Codex request whose egress does not match it — HTTP 401
72
+ * `Workspace is not authorized in this region.` — unless the client declares
73
+ * the residency itself. The token already carries it, so no configuration is
74
+ * needed: `chatgpt_data_residency` is the authoritative claim, with
75
+ * `chatgpt_compute_residency` as the fallback for tokens that only carry that.
76
+ *
77
+ * Returns undefined for a non-JWT token, or for the (common) accounts whose
78
+ * claims omit residency entirely — those workspaces are not region-pinned.
79
+ */
80
+ export function getCodexResidency(accessToken: string): string | undefined {
81
+ try {
82
+ const parts = accessToken.split(".");
83
+ if (parts.length !== 3) return undefined;
84
+ const decoded = Buffer.from(parts[1] ?? "", "base64").toString("utf-8");
85
+ const payload = JSON.parse(decoded) as Record<string, unknown>;
86
+ const auth = payload[JWT_CLAIM_PATH] as
87
+ | { chatgpt_data_residency?: unknown; chatgpt_compute_residency?: unknown }
88
+ | undefined;
89
+ for (const claim of [auth?.chatgpt_data_residency, auth?.chatgpt_compute_residency]) {
90
+ if (typeof claim !== "string") continue;
91
+ const residency = claim.trim();
92
+ if (residency.length > 0) return residency;
93
+ }
94
+ return undefined;
95
+ } catch {
96
+ return undefined;
97
+ }
98
+ }
99
+
100
+ /**
101
+ * Adds the token's workspace residency to Codex request headers without
102
+ * replacing a caller-supplied value.
103
+ */
104
+ export function applyCodexResidencyHeader(headers: Headers | Record<string, string>, accessToken: string): void {
105
+ const headerName = OPENAI_HEADERS.RESIDENCY;
106
+ if (headers instanceof Headers) {
107
+ if (headers.has(headerName)) return;
108
+ const residency = getCodexResidency(accessToken);
109
+ if (residency) headers.set(headerName, residency);
110
+ return;
111
+ }
112
+ for (const configuredName in headers) {
113
+ if (configuredName.toLowerCase() === headerName) return;
114
+ }
115
+ const residency = getCodexResidency(accessToken);
116
+ if (residency) headers[headerName] = residency;
117
+ }
@@ -0,0 +1,51 @@
1
+ export const COREWEAVE_PROJECT_HEADER = "OpenAI-Project" as const;
2
+
3
+ export interface CoreWeaveProjectEnv {
4
+ [key: string]: string | undefined;
5
+ COREWEAVE_PROJECT?: string;
6
+ WANDB_INFERENCE_PROJECT?: string;
7
+ WANDB_ENTITY?: string;
8
+ WANDB_PROJECT?: string;
9
+ }
10
+
11
+ function cleanEnvValue(value: string | undefined): string | undefined {
12
+ const trimmed = value?.trim();
13
+ return trimmed ? trimmed : undefined;
14
+ }
15
+
16
+ export function resolveCoreWeaveProject(env: CoreWeaveProjectEnv): string | undefined {
17
+ const explicitProject = cleanEnvValue(env.COREWEAVE_PROJECT) ?? cleanEnvValue(env.WANDB_INFERENCE_PROJECT);
18
+ if (explicitProject) {
19
+ return explicitProject;
20
+ }
21
+
22
+ const wandbProject = cleanEnvValue(env.WANDB_PROJECT);
23
+ if (!wandbProject) {
24
+ return undefined;
25
+ }
26
+ if (wandbProject.includes("/")) {
27
+ return wandbProject;
28
+ }
29
+
30
+ const wandbEntity = cleanEnvValue(env.WANDB_ENTITY);
31
+ return wandbEntity ? `${wandbEntity}/${wandbProject}` : undefined;
32
+ }
33
+
34
+ export function coreWeaveProjectHeaders(env: CoreWeaveProjectEnv): Record<string, string> | undefined {
35
+ const project = resolveCoreWeaveProject(env);
36
+ return project ? { [COREWEAVE_PROJECT_HEADER]: project } : undefined;
37
+ }
38
+
39
+ export function hasCoreWeaveProjectHeader(headers: Record<string, string>): boolean {
40
+ const normalized = COREWEAVE_PROJECT_HEADER.toLowerCase();
41
+ return Object.entries(headers).some(([header, value]) => header.toLowerCase() === normalized && value.trim() !== "");
42
+ }
43
+
44
+ export function removeBlankCoreWeaveProjectHeaders(headers: Record<string, string>): void {
45
+ const normalized = COREWEAVE_PROJECT_HEADER.toLowerCase();
46
+ for (const [header, value] of Object.entries(headers)) {
47
+ if (header.toLowerCase() === normalized && value.trim() === "") {
48
+ delete headers[header];
49
+ }
50
+ }
51
+ }
@@ -0,0 +1,135 @@
1
+ import type { FetchImpl } from "@rigelbuild/omp-utils";
2
+
3
+ /**
4
+ * Build a User-Agent string that identifies as Gemini CLI to unlock higher rate limits.
5
+ * Uses the same format as the official Gemini CLI (v0.35+):
6
+ * GeminiCLI/VERSION/MODEL (PLATFORM; ARCH; SURFACE)
7
+ */
8
+ export function getGeminiCliUserAgent(modelId = "gemini-3.1-pro-preview"): string {
9
+ const version = process.env.PI_AI_GEMINI_CLI_VERSION || "0.46.0";
10
+ const platform = process.platform === "win32" ? "win32" : process.platform;
11
+ const arch = process.arch === "x64" ? "x64" : process.arch;
12
+ return `GeminiCLI/${version}/${modelId} (${platform}; ${arch}; terminal)`;
13
+ }
14
+
15
+ export const getGeminiCliHeaders = (modelId?: string) => ({
16
+ "User-Agent": getGeminiCliUserAgent(modelId),
17
+ "Client-Metadata": "ideType=IDE_UNSPECIFIED,platform=PLATFORM_UNSPECIFIED,pluginType=GEMINI",
18
+ });
19
+
20
+ /**
21
+ * Antigravity / Cloud Code Assist user agent. Lives in its own file so discovery
22
+ * and usage code can read it without pulling the heavy google-gemini-cli provider
23
+ * (and its @google/genai → google-auth-library dependency chain) into the startup
24
+ * parse graph.
25
+ *
26
+ * Format captured from the real 2.8.0 `antigravity/hub` client:
27
+ * `antigravity/hub/2.8.0 (aidev_client; os_type=darwin; arch=arm64; cl=963137146)`.
28
+ * The backend gates newer models (e.g. gemini-3.7-flash) on the client version,
29
+ * so the version tracks the latest Antigravity release via the update manifest
30
+ * (see {@link ensureAntigravityVersion}) with `DEFAULT_ANTIGRAVITY_VERSION` as
31
+ * the offline fallback. os_type/arch are pinned to the darwin/arm64 reference
32
+ * client the version and manifest are captured from, independent of the host
33
+ * platform. Overrides: PI_AI_ANTIGRAVITY_VERSION / _CL / _OS / _ARCH.
34
+ */
35
+ export const DEFAULT_ANTIGRAVITY_VERSION = "2.8.0";
36
+
37
+ const ANTIGRAVITY_VERSION_MANIFEST_URL =
38
+ "https://antigravity-hub-auto-updater-974169037036.us-central1.run.app/manifest/latest-arm64-mac.yml";
39
+ const ANTIGRAVITY_VERSION_FETCH_TIMEOUT_MS = 5_000;
40
+
41
+ let discoveredAntigravityVersion: string | null = null;
42
+ let antigravityVersionFetch: Promise<void> | null = null;
43
+
44
+ /** Current Antigravity client version: env override → manifest-discovered → pinned fallback. */
45
+ export function getAntigravityVersion(): string {
46
+ return process.env.PI_AI_ANTIGRAVITY_VERSION || discoveredAntigravityVersion || DEFAULT_ANTIGRAVITY_VERSION;
47
+ }
48
+
49
+ /**
50
+ * Extracts the client version from an electron-builder update manifest.
51
+ * Returns null when no well-formed `version:` line is present.
52
+ */
53
+ export function parseAntigravityManifestVersion(yamlText: string): string | null {
54
+ for (const line of yamlText.split(/\r?\n/)) {
55
+ const match = /^\s*version\s*:\s*(?:"([^"]*)"|'([^']*)'|([^\s#]+))\s*(?:#.*)?$/.exec(line);
56
+ if (!match) continue;
57
+ const version = (match[1] ?? match[2] ?? match[3] ?? "").trim();
58
+ return /^\d+\.\d+\.\d+$/.test(version) ? version : null;
59
+ }
60
+ return null;
61
+ }
62
+
63
+ /**
64
+ * Resolves the latest Antigravity release from the official update manifest.
65
+ * Success is cached for the process lifetime; failures are silent (the pinned
66
+ * fallback stays valid) and clear the in-flight cache so a later call retries.
67
+ * Skipped entirely when PI_AI_ANTIGRAVITY_VERSION is set.
68
+ */
69
+ export function ensureAntigravityVersion(fetcher: FetchImpl = fetch, signal?: AbortSignal): Promise<void> {
70
+ if (process.env.PI_AI_ANTIGRAVITY_VERSION || discoveredAntigravityVersion) return Promise.resolve();
71
+ if (antigravityVersionFetch) return antigravityVersionFetch;
72
+
73
+ antigravityVersionFetch = (async () => {
74
+ try {
75
+ const timeoutSignal = AbortSignal.timeout(ANTIGRAVITY_VERSION_FETCH_TIMEOUT_MS);
76
+ const response = await fetcher(ANTIGRAVITY_VERSION_MANIFEST_URL, {
77
+ headers: { "Cache-Control": "no-cache", "User-Agent": "electron-builder" },
78
+ signal: signal ? AbortSignal.any([signal, timeoutSignal]) : timeoutSignal,
79
+ });
80
+ if (response.ok) {
81
+ discoveredAntigravityVersion = parseAntigravityManifestVersion(await response.text());
82
+ }
83
+ } catch {
84
+ // Silent: the pinned fallback remains valid when version discovery fails.
85
+ } finally {
86
+ if (!discoveredAntigravityVersion) antigravityVersionFetch = null;
87
+ }
88
+ })();
89
+ return antigravityVersionFetch;
90
+ }
91
+
92
+ /** Antigravity `User-Agent` header value; rebuilt when the discovered version changes. */
93
+ export function getAntigravityUserAgent(): string {
94
+ const version = getAntigravityVersion();
95
+ // The backend does not validate `cl` (verified live: stale, zero, and absent
96
+ // cl all pass model gating on daily-cloudcode-pa; only the version gates).
97
+ // The update manifest carries no changelist, so the captured value stays.
98
+ const cl = process.env.PI_AI_ANTIGRAVITY_CL || "963137146";
99
+ const os = process.env.PI_AI_ANTIGRAVITY_OS || "darwin";
100
+ const arch = process.env.PI_AI_ANTIGRAVITY_ARCH || "arm64";
101
+ return `antigravity/hub/${version} (aidev_client; os_type=${os}; arch=${arch}; cl=${cl})`;
102
+ }
103
+
104
+ /**
105
+ * Per-wire-id Antigravity Cloud Code Assist request constants, captured from the
106
+ * real `antigravity/hub` client against `daily-cloudcode-pa`. `modelEnum` is the
107
+ * opaque `labels.model_enum` token the client tags each request with — optional
108
+ * because Anthropic-backed wire ids (e.g. `claude-sonnet-4-6`,
109
+ * `claude-opus-4-6-thinking`) are accepted without one; the label is purely
110
+ * telemetry. `maxOutputTokens` is the fixed `generationConfig.maxOutputTokens`
111
+ * the backend enforces regardless of the thinking budget (Claude caps at
112
+ * 64000, Gemini accepts the discovered cap). Keyed by the routed upstream wire
113
+ * id (post effort-routing), not the collapsed logical id. Checkpoint-only ids
114
+ * (e.g. `gemini-3.1-flash-lite`) are intentionally absent — this provider only
115
+ * emits agent requests.
116
+ */
117
+ export interface AntigravityModelWireProfile {
118
+ modelEnum?: string;
119
+ maxOutputTokens: number;
120
+ }
121
+ export const ANTIGRAVITY_MODEL_WIRE_PROFILES: Readonly<Record<string, AntigravityModelWireProfile>> = {
122
+ "gemini-3.5-flash-extra-low": { modelEnum: "MODEL_PLACEHOLDER_M187", maxOutputTokens: 65536 },
123
+ "gemini-3.5-flash-low": { modelEnum: "MODEL_PLACEHOLDER_M20", maxOutputTokens: 65536 },
124
+ "gemini-3-flash-agent": { modelEnum: "MODEL_PLACEHOLDER_M132", maxOutputTokens: 65536 },
125
+ "gemini-3.1-pro-low": { modelEnum: "MODEL_PLACEHOLDER_M36", maxOutputTokens: 65535 },
126
+ "gemini-pro-agent": { modelEnum: "MODEL_PLACEHOLDER_M16", maxOutputTokens: 65535 },
127
+ // Claude on `daily-cloudcode-pa` rejects `maxOutputTokens > 64000` with a
128
+ // 400 (`Request contains an invalid argument`). The model_enum label is
129
+ // untracked for these ids; the backend does not require it.
130
+ "claude-sonnet-4-6": { maxOutputTokens: 64000 },
131
+ "claude-opus-4-6-thinking": { maxOutputTokens: 64000 },
132
+ };
133
+ export function getAntigravityModelWireProfile(wireModelId: string): AntigravityModelWireProfile | undefined {
134
+ return ANTIGRAVITY_MODEL_WIRE_PROFILES[wireModelId];
135
+ }
@@ -0,0 +1,147 @@
1
+ import type { FetchImpl } from "../types";
2
+ import { isRecord } from "../utils";
3
+
4
+ /**
5
+ * GitHub Copilot wire metadata: API-key envelope parsing and endpoint
6
+ * derivation shared by catalog discovery and the pi-ai OAuth flow. The device
7
+ * login / token refresh flow lives in `@oh-my-pi/pi-ai`'s registry.
8
+ */
9
+
10
+ export const COPILOT_USER_AGENT = "opencode/1.3.15" as const;
11
+
12
+ export const OPENCODE_HEADERS = {
13
+ "User-Agent": COPILOT_USER_AGENT,
14
+ } as const;
15
+
16
+ /**
17
+ * Copilot API version sent on `api.githubcopilot.com` requests (`/models`,
18
+ * chat endpoints). Newer versions unlock tiered context metadata: `/models`
19
+ * reports the full long-context window in `capabilities.limits` plus per-tier
20
+ * boundaries/prices under `billing.token_prices.{default,long_context}`.
21
+ * Without it the endpoint serves default-tier limits only (e.g. 264k instead
22
+ * of 1M for Claude Opus). Never send this to `api.github.com` REST endpoints —
23
+ * they validate `X-GitHub-Api-Version` against the REST version vocabulary.
24
+ */
25
+ export const COPILOT_API_VERSION = "2026-06-01" as const;
26
+
27
+ /** Headers for `api.githubcopilot.com` (capi) requests: discovery, chat, policy. */
28
+ export const COPILOT_API_HEADERS = {
29
+ ...OPENCODE_HEADERS,
30
+ "X-GitHub-Api-Version": COPILOT_API_VERSION,
31
+ } as const;
32
+
33
+ type GitHubCopilotApiKeyPayload = {
34
+ token?: unknown;
35
+ enterpriseUrl?: unknown;
36
+ apiEndpoint?: unknown;
37
+ };
38
+
39
+ export type ParsedGitHubCopilotApiKey = {
40
+ accessToken: string;
41
+ enterpriseUrl?: string;
42
+ apiEndpoint?: string;
43
+ };
44
+
45
+ const PUBLIC_GITHUB_HOSTS = new Set(["api.github.com", "github.com", "www.github.com"]);
46
+
47
+ export function isPublicGitHubHost(host: string): boolean {
48
+ return PUBLIC_GITHUB_HOSTS.has(host.trim().toLowerCase());
49
+ }
50
+
51
+ /** Canonical personal-Copilot API host. */
52
+ export const PERSONAL_GITHUB_COPILOT_BASE_URL = "https://api.githubcopilot.com" as const;
53
+
54
+ /** `true` when the resolved base URL is the canonical personal-Copilot host. */
55
+ export function isPersonalGitHubCopilotBaseUrl(baseUrl: string | undefined): boolean {
56
+ return baseUrl === PERSONAL_GITHUB_COPILOT_BASE_URL;
57
+ }
58
+
59
+ export function normalizeGitHubCopilotEnterpriseDomain(input: string | undefined): string | undefined {
60
+ const trimmed = input?.trim();
61
+ if (!trimmed) return undefined;
62
+ const normalized = normalizeDomain(trimmed) ?? trimmed.toLowerCase();
63
+ if (!normalized || isPublicGitHubHost(normalized)) return undefined;
64
+ return normalized;
65
+ }
66
+
67
+ export function normalizeGitHubCopilotApiEndpoint(input: string | undefined): string | undefined {
68
+ const trimmed = input?.trim();
69
+ if (!trimmed?.startsWith("https://")) return undefined;
70
+ try {
71
+ const url = new URL(trimmed);
72
+ if (url.protocol !== "https:" || !url.hostname) return undefined;
73
+ return trimmed.replace(/\/+$/, "");
74
+ } catch {
75
+ return undefined;
76
+ }
77
+ }
78
+ /**
79
+ * Resolve the plan-specific Copilot API endpoint advertised for a GitHub token.
80
+ * Login and raw environment-token discovery share this best-effort probe. Pass
81
+ * a `signal` to bound it against the same discovery deadline as `/models`; a
82
+ * stalled probe otherwise blocks discovery indefinitely.
83
+ */
84
+ export async function discoverGitHubCopilotApiEndpoint(
85
+ token: string,
86
+ fetchImpl: FetchImpl,
87
+ signal?: AbortSignal,
88
+ ): Promise<string | undefined> {
89
+ try {
90
+ const response = await fetchImpl("https://api.github.com/copilot_internal/user", {
91
+ headers: {
92
+ Accept: "application/json",
93
+ Authorization: `token ${token}`,
94
+ ...OPENCODE_HEADERS,
95
+ },
96
+ signal,
97
+ });
98
+ if (!response.ok) return undefined;
99
+ const data: unknown = await response.json();
100
+ if (!isRecord(data) || !isRecord(data.endpoints)) return undefined;
101
+ const endpoint = data.endpoints.api;
102
+ return typeof endpoint === "string" ? normalizeGitHubCopilotApiEndpoint(endpoint) : undefined;
103
+ } catch {
104
+ return undefined;
105
+ }
106
+ }
107
+
108
+ export function parseGitHubCopilotApiKey(apiKeyRaw: string): ParsedGitHubCopilotApiKey {
109
+ try {
110
+ const parsed = JSON.parse(apiKeyRaw) as GitHubCopilotApiKeyPayload;
111
+ if (typeof parsed.token === "string") {
112
+ return {
113
+ accessToken: parsed.token,
114
+ enterpriseUrl:
115
+ typeof parsed.enterpriseUrl === "string"
116
+ ? normalizeGitHubCopilotEnterpriseDomain(parsed.enterpriseUrl)
117
+ : undefined,
118
+ apiEndpoint:
119
+ typeof parsed.apiEndpoint === "string"
120
+ ? normalizeGitHubCopilotApiEndpoint(parsed.apiEndpoint)
121
+ : undefined,
122
+ };
123
+ }
124
+ } catch {}
125
+
126
+ return { accessToken: apiKeyRaw };
127
+ }
128
+
129
+ export function normalizeDomain(input: string): string | null {
130
+ const trimmed = input.trim();
131
+ if (!trimmed) return null;
132
+ try {
133
+ const url = trimmed.includes("://") ? new URL(trimmed) : new URL(`https://${trimmed}`);
134
+ return url.hostname;
135
+ } catch {
136
+ return null;
137
+ }
138
+ }
139
+
140
+ export function getGitHubCopilotBaseUrl(enterpriseDomain?: string): string {
141
+ const normalizedEnterpriseDomain = normalizeGitHubCopilotEnterpriseDomain(enterpriseDomain);
142
+ if (!normalizedEnterpriseDomain) return "https://api.githubcopilot.com";
143
+ const host = normalizedEnterpriseDomain.startsWith("copilot-api.")
144
+ ? normalizedEnterpriseDomain
145
+ : `copilot-api.${normalizedEnterpriseDomain}`;
146
+ return `https://${host}`;
147
+ }
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Identification of provider-side image fetchers.
3
+ *
4
+ * A request may carry an image either inline (base64) or as a URL. With a URL,
5
+ * the provider's backend performs its own server-side GET against that URL, so
6
+ * a local blob server sees an inbound request from vendor infrastructure rather
7
+ * than from the client. This module names those fetchers, letting a blob server
8
+ * attribute an inbound GET to the vendor that issued it — which is how a caller
9
+ * learns where a request actually landed when a router sits between them and
10
+ * the model.
11
+ *
12
+ * NOT an authentication mechanism. Every value here is a request header chosen
13
+ * by the caller and is trivially forged. Authorize blob reads with an
14
+ * unguessable URL (single-use capability token, short TTL) and treat a fetcher
15
+ * match as attribution/telemetry only.
16
+ *
17
+ * Each entry was captured from a live fetch triggered by handing that vendor's
18
+ * API a URL-sourced image.
19
+ */
20
+
21
+ /** Vendor whose infrastructure performed an inbound fetch. */
22
+ export type ImageFetcherVendor = "openai" | "anthropic" | "xai" | "google";
23
+
24
+ /** Registry key for a known fetcher. */
25
+ export type ImageFetcherId =
26
+ | "openai-file-downloader"
27
+ | "anthropic-claude-user"
28
+ | "anthropic-claude-user-preview"
29
+ | "xai-image-api-fetch"
30
+ | "google";
31
+
32
+ /** Request signature of one provider-side fetcher. */
33
+ export interface ImageFetcherIdentity {
34
+ vendor: ImageFetcherVendor;
35
+ /** Human-readable name for logs and UI. */
36
+ label: string;
37
+ /**
38
+ * `User-Agent` contract: an exact string for fetchers that send a fixed
39
+ * value, or a pattern for those embedding a client version.
40
+ */
41
+ userAgent: string | RegExp;
42
+ /**
43
+ * Vendor-proprietary headers observed alongside the agent, used to
44
+ * corroborate a `User-Agent` claim. Generic infrastructure headers
45
+ * (`traceparent`, `x-cloud-trace-context`) are deliberately excluded: they
46
+ * are emitted by unrelated infrastructure and corroborate nothing. Empty
47
+ * means the agent string is the only available signal, so
48
+ * {@link ImageFetcherMatch.corroborated} can never be true for that entry.
49
+ */
50
+ markerHeaders: readonly string[];
51
+ /** API surface the capture came from. */
52
+ observedVia: string;
53
+ /** Operational caveats a blob server should account for. */
54
+ note?: string;
55
+ }
56
+
57
+ /**
58
+ * Known provider-side fetchers.
59
+ *
60
+ * Agent contracts do not overlap — exact strings never collide with the
61
+ * versioned patterns — so lookup order carries no meaning.
62
+ */
63
+ export const IMAGE_FETCHERS: Readonly<Record<ImageFetcherId, ImageFetcherIdentity>> = {
64
+ "openai-file-downloader": {
65
+ vendor: "openai",
66
+ label: "OpenAI File Downloader",
67
+ userAgent: "OpenAI File Downloader",
68
+ markerHeaders: ["openai-internal-smokescreener"],
69
+ observedVia: "chatgpt.com/backend-api/codex/responses, input_image.image_url",
70
+ note: "Issues two near-simultaneous GETs per image; a blob server must treat a duplicate hit as expected rather than as replay.",
71
+ },
72
+ "anthropic-claude-user": {
73
+ vendor: "anthropic",
74
+ label: "Claude image fetcher",
75
+ userAgent: "Claude-User",
76
+ markerHeaders: [],
77
+ observedVia: "api.anthropic.com/v1/messages, image.source.type=url",
78
+ note: "Sends only generic trace headers, so the bare agent string is the sole signal. Distinct from the versioned Claude-User/<version> agent used for links appearing in conversation text.",
79
+ },
80
+ "anthropic-claude-user-preview": {
81
+ vendor: "anthropic",
82
+ label: "Claude link fetcher",
83
+ userAgent: /\bClaude-User\/\d+(?:\.\d+)*\b/,
84
+ markerHeaders: [],
85
+ observedVia:
86
+ "unsolicited fetch after a URL appeared in assistant output; never observed serving an image request",
87
+ note: "Not an image fetcher. Listed so a blob server can separate it from the image path instead of counting it as a provider image fetch.",
88
+ },
89
+ "xai-image-api-fetch": {
90
+ vendor: "xai",
91
+ label: "xAI image API fetch",
92
+ userAgent: /^XaiImageApiFetch\/\d+(?:\.\d+)*\s/,
93
+ markerHeaders: ["x-xaifetchid"],
94
+ observedVia: "api.x.ai/v1/responses, input_image.image_url",
95
+ note: "Sends an image-only `accept` allowlist and rejects any other content type before the model sees the response.",
96
+ },
97
+ google: {
98
+ vendor: "google",
99
+ label: "Google",
100
+ userAgent: "Google",
101
+ markerHeaders: [],
102
+ observedVia: "cloudcode-pa.googleapis.com v1internal:streamGenerateContent, fileData.fileUri",
103
+ note: "Weakest signal of the set: the agent string is a bare vendor name with no version and no proprietary header.",
104
+ },
105
+ };
106
+
107
+ /** Attribution outcome for one inbound request. */
108
+ export interface ImageFetcherMatch {
109
+ id: ImageFetcherId;
110
+ identity: ImageFetcherIdentity;
111
+ /**
112
+ * Whether every proprietary marker header for the matched identity is
113
+ * present. Always false for identities declaring no markers — absence of
114
+ * corroboration is not evidence against the match.
115
+ */
116
+ corroborated: boolean;
117
+ }
118
+
119
+ /** Inbound header bag, as exposed by either `fetch` or a Node-style server. */
120
+ export type InboundHeaders = Headers | Readonly<Record<string, string | readonly string[] | undefined>>;
121
+
122
+ function headerValue(headers: InboundHeaders, name: string): string | undefined {
123
+ if (headers instanceof Headers) return headers.get(name) ?? undefined;
124
+ const direct = headers[name];
125
+ if (direct !== undefined) return Array.isArray(direct) ? direct[0] : (direct as string);
126
+ for (const key in headers) {
127
+ if (key.toLowerCase() !== name) continue;
128
+ const value = headers[key];
129
+ return Array.isArray(value) ? value[0] : (value as string | undefined);
130
+ }
131
+ return undefined;
132
+ }
133
+
134
+ /**
135
+ * Attribute an inbound blob request to a known provider-side fetcher, or
136
+ * `null` when the agent matches none.
137
+ *
138
+ * Matches on `User-Agent` alone and reports marker-header corroboration
139
+ * separately; callers MUST NOT treat either as proof of origin. Gate access on
140
+ * the unguessable URL.
141
+ */
142
+ export function identifyImageFetcher(headers: InboundHeaders): ImageFetcherMatch | null {
143
+ const agent = headerValue(headers, "user-agent");
144
+ if (!agent) return null;
145
+ for (const id in IMAGE_FETCHERS) {
146
+ const identity = IMAGE_FETCHERS[id as ImageFetcherId];
147
+ const { userAgent } = identity;
148
+ const matched = typeof userAgent === "string" ? agent === userAgent : userAgent.test(agent);
149
+ if (!matched) continue;
150
+ return {
151
+ id: id as ImageFetcherId,
152
+ identity,
153
+ corroborated:
154
+ identity.markerHeaders.length > 0 &&
155
+ identity.markerHeaders.every(header => headerValue(headers, header) !== undefined),
156
+ };
157
+ }
158
+ return null;
159
+ }