@omnicross/core 0.1.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.
Files changed (114) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +57 -0
  3. package/README.md +15 -0
  4. package/dist/ApiKeyPoolService-BmMkau07.d.cts +170 -0
  5. package/dist/ApiKeyPoolService-BmMkau07.d.ts +170 -0
  6. package/dist/ProviderProxy-f_8ziIhW.d.cts +120 -0
  7. package/dist/ProviderProxy-vjt8sQQk.d.ts +120 -0
  8. package/dist/SubscriptionAuthSource-Cr4fVEYY.d.cts +264 -0
  9. package/dist/SubscriptionAuthSource-D89zmiSS.d.ts +264 -0
  10. package/dist/auth/GeminiCodeAssistProjectResolver.cjs +218 -0
  11. package/dist/auth/GeminiCodeAssistProjectResolver.d.cts +68 -0
  12. package/dist/auth/GeminiCodeAssistProjectResolver.d.ts +68 -0
  13. package/dist/auth/GeminiCodeAssistProjectResolver.js +189 -0
  14. package/dist/completion/ApiKeyPoolService.cjs +331 -0
  15. package/dist/completion/ApiKeyPoolService.d.cts +2 -0
  16. package/dist/completion/ApiKeyPoolService.d.ts +2 -0
  17. package/dist/completion/ApiKeyPoolService.js +306 -0
  18. package/dist/completion.cjs +4027 -0
  19. package/dist/completion.d.cts +17 -0
  20. package/dist/completion.d.ts +17 -0
  21. package/dist/completion.js +3983 -0
  22. package/dist/index-BTSmc9Sm.d.ts +645 -0
  23. package/dist/index-DXazdTzZ.d.cts +645 -0
  24. package/dist/index.cjs +10428 -0
  25. package/dist/index.d.cts +128 -0
  26. package/dist/index.d.ts +128 -0
  27. package/dist/index.js +10339 -0
  28. package/dist/outbound-api/subscriptionRegistryPort.cjs +38 -0
  29. package/dist/outbound-api/subscriptionRegistryPort.d.cts +73 -0
  30. package/dist/outbound-api/subscriptionRegistryPort.d.ts +73 -0
  31. package/dist/outbound-api/subscriptionRegistryPort.js +12 -0
  32. package/dist/outbound-api.cjs +5264 -0
  33. package/dist/outbound-api.d.cts +320 -0
  34. package/dist/outbound-api.d.ts +320 -0
  35. package/dist/outbound-api.js +5218 -0
  36. package/dist/pipeline/SubscriptionAuthSource.cjs +131 -0
  37. package/dist/pipeline/SubscriptionAuthSource.d.cts +3 -0
  38. package/dist/pipeline/SubscriptionAuthSource.d.ts +3 -0
  39. package/dist/pipeline/SubscriptionAuthSource.js +103 -0
  40. package/dist/pipeline/SubscriptionAuthStrategy.cjs +18 -0
  41. package/dist/pipeline/SubscriptionAuthStrategy.d.cts +61 -0
  42. package/dist/pipeline/SubscriptionAuthStrategy.d.ts +61 -0
  43. package/dist/pipeline/SubscriptionAuthStrategy.js +0 -0
  44. package/dist/ports/gemini-code-assist-resolver.cjs +38 -0
  45. package/dist/ports/gemini-code-assist-resolver.d.cts +26 -0
  46. package/dist/ports/gemini-code-assist-resolver.d.ts +26 -0
  47. package/dist/ports/gemini-code-assist-resolver.js +12 -0
  48. package/dist/ports.cjs +18 -0
  49. package/dist/ports.d.cts +15 -0
  50. package/dist/ports.d.ts +15 -0
  51. package/dist/ports.js +0 -0
  52. package/dist/provider-proxy/ingress/providerProxyShared.cjs +2958 -0
  53. package/dist/provider-proxy/ingress/providerProxyShared.d.cts +77 -0
  54. package/dist/provider-proxy/ingress/providerProxyShared.d.ts +77 -0
  55. package/dist/provider-proxy/ingress/providerProxyShared.js +2925 -0
  56. package/dist/provider-proxy/matchText.cjs +73 -0
  57. package/dist/provider-proxy/matchText.d.cts +47 -0
  58. package/dist/provider-proxy/matchText.d.ts +47 -0
  59. package/dist/provider-proxy/matchText.js +45 -0
  60. package/dist/provider-proxy/types.cjs +18 -0
  61. package/dist/provider-proxy/types.d.cts +12 -0
  62. package/dist/provider-proxy/types.d.ts +12 -0
  63. package/dist/provider-proxy/types.js +0 -0
  64. package/dist/provider-proxy.cjs +4667 -0
  65. package/dist/provider-proxy.d.cts +69 -0
  66. package/dist/provider-proxy.d.ts +69 -0
  67. package/dist/provider-proxy.js +4636 -0
  68. package/dist/serializeError.cjs +82 -0
  69. package/dist/serializeError.d.cts +24 -0
  70. package/dist/serializeError.d.ts +24 -0
  71. package/dist/serializeError.js +57 -0
  72. package/dist/sse-parser.cjs +456 -0
  73. package/dist/sse-parser.d.cts +143 -0
  74. package/dist/sse-parser.d.ts +143 -0
  75. package/dist/sse-parser.js +430 -0
  76. package/dist/transformer/TransformerChainExecutor.cjs +321 -0
  77. package/dist/transformer/TransformerChainExecutor.d.cts +104 -0
  78. package/dist/transformer/TransformerChainExecutor.d.ts +104 -0
  79. package/dist/transformer/TransformerChainExecutor.js +294 -0
  80. package/dist/transformer/TransformerService.cjs +290 -0
  81. package/dist/transformer/TransformerService.d.cts +138 -0
  82. package/dist/transformer/TransformerService.d.ts +138 -0
  83. package/dist/transformer/TransformerService.js +265 -0
  84. package/dist/transformer/transformers/GeminiCodeAssistTransformer.cjs +1115 -0
  85. package/dist/transformer/transformers/GeminiCodeAssistTransformer.d.cts +102 -0
  86. package/dist/transformer/transformers/GeminiCodeAssistTransformer.d.ts +102 -0
  87. package/dist/transformer/transformers/GeminiCodeAssistTransformer.js +1085 -0
  88. package/dist/transformer/transformers/GeminiTransformer.cjs +1013 -0
  89. package/dist/transformer/transformers/GeminiTransformer.d.cts +70 -0
  90. package/dist/transformer/transformers/GeminiTransformer.d.ts +70 -0
  91. package/dist/transformer/transformers/GeminiTransformer.js +986 -0
  92. package/dist/transformer/transformers/OpenAIResponseTransformer.cjs +538 -0
  93. package/dist/transformer/transformers/OpenAIResponseTransformer.d.cts +53 -0
  94. package/dist/transformer/transformers/OpenAIResponseTransformer.d.ts +53 -0
  95. package/dist/transformer/transformers/OpenAIResponseTransformer.js +513 -0
  96. package/dist/transformer/transformers/OpenCodeGoTransformer.cjs +73 -0
  97. package/dist/transformer/transformers/OpenCodeGoTransformer.d.cts +51 -0
  98. package/dist/transformer/transformers/OpenCodeGoTransformer.d.ts +51 -0
  99. package/dist/transformer/transformers/OpenCodeGoTransformer.js +48 -0
  100. package/dist/transformer/types.cjs +18 -0
  101. package/dist/transformer/types.d.cts +405 -0
  102. package/dist/transformer/types.d.ts +405 -0
  103. package/dist/transformer/types.js +0 -0
  104. package/dist/transformer.cjs +3736 -0
  105. package/dist/transformer.d.cts +33 -0
  106. package/dist/transformer.d.ts +33 -0
  107. package/dist/transformer.js +3712 -0
  108. package/dist/types-CGGrKqC_.d.cts +142 -0
  109. package/dist/types-CbCN2NQP.d.ts +142 -0
  110. package/dist/types-DCzHkhJt.d.ts +467 -0
  111. package/dist/types-DZIQbgp0.d.cts +467 -0
  112. package/dist/usage-event-sink-BX7FE1NL.d.cts +59 -0
  113. package/dist/usage-event-sink-BX7FE1NL.d.ts +59 -0
  114. package/package.json +62 -0
@@ -0,0 +1,218 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/auth/GeminiCodeAssistProjectResolver.ts
21
+ var GeminiCodeAssistProjectResolver_exports = {};
22
+ __export(GeminiCodeAssistProjectResolver_exports, {
23
+ GeminiCodeAssistHandshakeError: () => GeminiCodeAssistHandshakeError,
24
+ GeminiCodeAssistProjectResolver: () => GeminiCodeAssistProjectResolver,
25
+ getGeminiCodeAssistProjectResolver: () => getGeminiCodeAssistProjectResolver
26
+ });
27
+ module.exports = __toCommonJS(GeminiCodeAssistProjectResolver_exports);
28
+
29
+ // src/transformer/transformers/GeminiCodeAssistTransformer.ts
30
+ var DEFAULT_CODE_ASSIST_ENDPOINT = "https://cloudcode-pa.googleapis.com";
31
+ var DEFAULT_CODE_ASSIST_API_VERSION = "v1internal";
32
+ function resolveCodeAssistEndpoint() {
33
+ return (process.env.CODE_ASSIST_ENDPOINT || DEFAULT_CODE_ASSIST_ENDPOINT).replace(/\/+$/, "");
34
+ }
35
+ function resolveCodeAssistApiVersion() {
36
+ return process.env.CODE_ASSIST_API_VERSION || DEFAULT_CODE_ASSIST_API_VERSION;
37
+ }
38
+
39
+ // src/auth/GeminiCodeAssistProjectResolver.ts
40
+ var FREE_TIER_ID = "free-tier";
41
+ var LEGACY_TIER_ID = "legacy-tier";
42
+ var ONBOARD_POLL_INTERVAL_MS = 5e3;
43
+ var ONBOARD_MAX_POLLS = 24;
44
+ var GeminiCodeAssistHandshakeError = class extends Error {
45
+ constructor(message, status, code) {
46
+ super(message);
47
+ this.status = status;
48
+ this.code = code;
49
+ this.name = "GeminiCodeAssistHandshakeError";
50
+ }
51
+ status;
52
+ code;
53
+ };
54
+ var GeminiCodeAssistProjectResolver = class {
55
+ constructor(fetchImpl = (url, init) => fetch(url, init)) {
56
+ this.fetchImpl = fetchImpl;
57
+ }
58
+ fetchImpl;
59
+ /** account access token → resolved project id (undefined = free-tier, no project). */
60
+ cache = /* @__PURE__ */ new Map();
61
+ /** In-flight handshakes so concurrent callers share one round-trip. */
62
+ inflight = /* @__PURE__ */ new Map();
63
+ /** Test/diagnostic helper — clear the cached resolution for an account. */
64
+ clearCache() {
65
+ this.cache.clear();
66
+ this.inflight.clear();
67
+ }
68
+ /**
69
+ * Resolve (and cache) the Code Assist project for the given access token.
70
+ * Runs the handshake at most once per token. Returns `undefined` for a
71
+ * brand-new free-tier account (valid — the envelope sends no project).
72
+ */
73
+ async resolveProject(accessToken) {
74
+ if (this.cache.has(accessToken)) {
75
+ return this.cache.get(accessToken);
76
+ }
77
+ const existing = this.inflight.get(accessToken);
78
+ if (existing) return existing;
79
+ const run = this.runHandshake(accessToken).then((project) => {
80
+ this.cache.set(accessToken, project);
81
+ return project;
82
+ }).finally(() => {
83
+ this.inflight.delete(accessToken);
84
+ });
85
+ this.inflight.set(accessToken, run);
86
+ return run;
87
+ }
88
+ codeAssistUrl(method) {
89
+ return `${resolveCodeAssistEndpoint()}/${resolveCodeAssistApiVersion()}:${method}`;
90
+ }
91
+ /** Seed project id from env; reject a purely-numeric value (project NUMBER). */
92
+ seedProjectId() {
93
+ const seed = process.env.GOOGLE_CLOUD_PROJECT || process.env.GOOGLE_CLOUD_PROJECT_ID || "";
94
+ if (seed && /^\d+$/.test(seed.trim())) return "";
95
+ return seed.trim();
96
+ }
97
+ async runHandshake(accessToken) {
98
+ const seededProject = this.seedProjectId();
99
+ const load = await this.postCodeAssist("loadCodeAssist", accessToken, {
100
+ cloudaicompanionProject: seededProject || void 0,
101
+ metadata: {
102
+ ideType: "IDE_UNSPECIFIED",
103
+ platform: "PLATFORM_UNSPECIFIED",
104
+ pluginType: "GEMINI",
105
+ duetProject: seededProject || void 0
106
+ }
107
+ });
108
+ if (load.currentTier?.id) {
109
+ return load.cloudaicompanionProject || void 0;
110
+ }
111
+ const defaultTier = load.allowedTiers?.find((t) => t.isDefault);
112
+ const tierId = defaultTier?.id ?? LEGACY_TIER_ID;
113
+ const isFreeish = tierId === FREE_TIER_ID || tierId === LEGACY_TIER_ID;
114
+ const onboardProject = isFreeish ? void 0 : seededProject || void 0;
115
+ let lro = await this.postCodeAssist("onboardUser", accessToken, {
116
+ tierId,
117
+ cloudaicompanionProject: onboardProject,
118
+ metadata: {
119
+ ideType: "IDE_UNSPECIFIED",
120
+ platform: "PLATFORM_UNSPECIFIED",
121
+ pluginType: "GEMINI",
122
+ duetProject: onboardProject
123
+ }
124
+ });
125
+ let polls = 0;
126
+ while (!lro.done && polls < ONBOARD_MAX_POLLS) {
127
+ await delay(ONBOARD_POLL_INTERVAL_MS);
128
+ polls++;
129
+ lro = await this.getOperation(accessToken, lro.name);
130
+ }
131
+ if (lro.error) {
132
+ throw new GeminiCodeAssistHandshakeError(
133
+ `Code Assist onboardUser failed: ${lro.error.message ?? "unknown"}`,
134
+ lro.error.code ?? 500
135
+ );
136
+ }
137
+ return lro.response?.cloudaicompanionProject?.id || void 0;
138
+ }
139
+ /** POST a Code Assist method, surfacing the documented hard failures clearly. */
140
+ async postCodeAssist(method, accessToken, body) {
141
+ const url = this.codeAssistUrl(method);
142
+ const res = await this.fetchImpl(url, {
143
+ method: "POST",
144
+ headers: {
145
+ Authorization: `Bearer ${accessToken}`,
146
+ "Content-Type": "application/json"
147
+ },
148
+ body: JSON.stringify(body)
149
+ });
150
+ if (!res.ok) {
151
+ await this.throwForStatus(res, method);
152
+ }
153
+ return await res.json();
154
+ }
155
+ async getOperation(accessToken, name) {
156
+ const url = this.codeAssistUrl("getOperation");
157
+ const res = await this.fetchImpl(url, {
158
+ method: "POST",
159
+ headers: {
160
+ Authorization: `Bearer ${accessToken}`,
161
+ "Content-Type": "application/json"
162
+ },
163
+ body: JSON.stringify({ name })
164
+ });
165
+ if (!res.ok) {
166
+ await this.throwForStatus(res, "getOperation");
167
+ }
168
+ return await res.json();
169
+ }
170
+ /** Map a non-2xx Code Assist response to a clear, surfaced error. */
171
+ async throwForStatus(res, method) {
172
+ let detailCode;
173
+ let detailMessage = "";
174
+ try {
175
+ const errBody = await res.json();
176
+ detailCode = errBody.error?.status;
177
+ detailMessage = errBody.error?.message ?? "";
178
+ } catch {
179
+ }
180
+ if (res.status === 403) {
181
+ throw new GeminiCodeAssistHandshakeError(
182
+ `Code Assist ${method} denied (403 ${detailCode ?? "PERMISSION_DENIED"}): ${detailMessage || "enable the Cloud AI Companion API and grant the required role"}`,
183
+ 403,
184
+ detailCode
185
+ );
186
+ }
187
+ if (res.status === 429) {
188
+ throw new GeminiCodeAssistHandshakeError(
189
+ `Code Assist ${method} rate-limited (429): ${detailMessage || "quota exceeded, retry later"}`,
190
+ 429,
191
+ detailCode
192
+ );
193
+ }
194
+ throw new GeminiCodeAssistHandshakeError(
195
+ `Code Assist ${method} failed (${res.status}): ${detailMessage || res.statusText}`,
196
+ res.status,
197
+ detailCode
198
+ );
199
+ }
200
+ };
201
+ function delay(ms) {
202
+ return new Promise((resolve) => {
203
+ setTimeout(resolve, ms);
204
+ });
205
+ }
206
+ var _resolverSingleton = null;
207
+ function getGeminiCodeAssistProjectResolver() {
208
+ if (!_resolverSingleton) {
209
+ _resolverSingleton = new GeminiCodeAssistProjectResolver();
210
+ }
211
+ return _resolverSingleton;
212
+ }
213
+ // Annotate the CommonJS export names for ESM import in node:
214
+ 0 && (module.exports = {
215
+ GeminiCodeAssistHandshakeError,
216
+ GeminiCodeAssistProjectResolver,
217
+ getGeminiCodeAssistProjectResolver
218
+ });
@@ -0,0 +1,68 @@
1
+ /**
2
+ * GeminiCodeAssistProjectResolver — runs the Google Code Assist project
3
+ * handshake ONCE per account and caches the resolved Cloud AI Companion
4
+ * project id.
5
+ *
6
+ * The gemini SUBSCRIPTION upstream (`cloudcode-pa.googleapis.com`) requires a
7
+ * project id in its generateContent envelope for paid tiers. Brand-new
8
+ * free-tier accounts have NO project (and sending one → Precondition Failed),
9
+ * so `undefined` is a VALID resolved value. The resolution dance mirrors
10
+ * gemini-cli `packages/core/src/code_assist/setup.ts`:
11
+ *
12
+ * 1. seed `projectId = GOOGLE_CLOUD_PROJECT || GOOGLE_CLOUD_PROJECT_ID`
13
+ * (may be empty; a purely-numeric value is rejected — Code Assist wants
14
+ * the human-readable project id, not the number).
15
+ * 2. POST `:loadCodeAssist` with `{ cloudaicompanionProject, metadata }`.
16
+ * - If the response carries `currentTier` → already onboarded; use the
17
+ * response's `cloudaicompanionProject` (may be undefined for free-tier).
18
+ * - Else pick the onboarding tier (first `allowedTiers` with `isDefault`,
19
+ * default `legacy-tier`) and POST `:onboardUser`. For free-tier /
20
+ * legacy-tier the `cloudaicompanionProject` MUST be undefined; for
21
+ * standard-tier include it. `:onboardUser` returns an LRO — poll
22
+ * `:getOperation` until `done`, then read
23
+ * `response.cloudaicompanionProject.id`.
24
+ *
25
+ * Resolution result is cached in-memory keyed by access token (1:1 with the
26
+ * account) so the handshake runs at most once per token. On a documented hard
27
+ * failure (403 SERVICE_DISABLED / PERMISSION_DENIED, 429) we throw a clear
28
+ * error WITHOUT caching, so a transient/permission issue can be retried after
29
+ * the user fixes it.
30
+ *
31
+ * @module @omnicross/core/auth/GeminiCodeAssistProjectResolver
32
+ */
33
+ /** A clear, surfaced error for the documented hard-failure modes. */
34
+ declare class GeminiCodeAssistHandshakeError extends Error {
35
+ readonly status: number;
36
+ readonly code?: string | undefined;
37
+ constructor(message: string, status: number, code?: string | undefined);
38
+ }
39
+ /** Injectable fetch so tests can mock the network without a live endpoint. */
40
+ type FetchLike = (url: string, init: RequestInit) => Promise<Response>;
41
+ declare class GeminiCodeAssistProjectResolver {
42
+ private readonly fetchImpl;
43
+ /** account access token → resolved project id (undefined = free-tier, no project). */
44
+ private readonly cache;
45
+ /** In-flight handshakes so concurrent callers share one round-trip. */
46
+ private readonly inflight;
47
+ constructor(fetchImpl?: FetchLike);
48
+ /** Test/diagnostic helper — clear the cached resolution for an account. */
49
+ clearCache(): void;
50
+ /**
51
+ * Resolve (and cache) the Code Assist project for the given access token.
52
+ * Runs the handshake at most once per token. Returns `undefined` for a
53
+ * brand-new free-tier account (valid — the envelope sends no project).
54
+ */
55
+ resolveProject(accessToken: string): Promise<string | undefined>;
56
+ private codeAssistUrl;
57
+ /** Seed project id from env; reject a purely-numeric value (project NUMBER). */
58
+ private seedProjectId;
59
+ private runHandshake;
60
+ /** POST a Code Assist method, surfacing the documented hard failures clearly. */
61
+ private postCodeAssist;
62
+ private getOperation;
63
+ /** Map a non-2xx Code Assist response to a clear, surfaced error. */
64
+ private throwForStatus;
65
+ }
66
+ declare function getGeminiCodeAssistProjectResolver(): GeminiCodeAssistProjectResolver;
67
+
68
+ export { type FetchLike, GeminiCodeAssistHandshakeError, GeminiCodeAssistProjectResolver, getGeminiCodeAssistProjectResolver };
@@ -0,0 +1,68 @@
1
+ /**
2
+ * GeminiCodeAssistProjectResolver — runs the Google Code Assist project
3
+ * handshake ONCE per account and caches the resolved Cloud AI Companion
4
+ * project id.
5
+ *
6
+ * The gemini SUBSCRIPTION upstream (`cloudcode-pa.googleapis.com`) requires a
7
+ * project id in its generateContent envelope for paid tiers. Brand-new
8
+ * free-tier accounts have NO project (and sending one → Precondition Failed),
9
+ * so `undefined` is a VALID resolved value. The resolution dance mirrors
10
+ * gemini-cli `packages/core/src/code_assist/setup.ts`:
11
+ *
12
+ * 1. seed `projectId = GOOGLE_CLOUD_PROJECT || GOOGLE_CLOUD_PROJECT_ID`
13
+ * (may be empty; a purely-numeric value is rejected — Code Assist wants
14
+ * the human-readable project id, not the number).
15
+ * 2. POST `:loadCodeAssist` with `{ cloudaicompanionProject, metadata }`.
16
+ * - If the response carries `currentTier` → already onboarded; use the
17
+ * response's `cloudaicompanionProject` (may be undefined for free-tier).
18
+ * - Else pick the onboarding tier (first `allowedTiers` with `isDefault`,
19
+ * default `legacy-tier`) and POST `:onboardUser`. For free-tier /
20
+ * legacy-tier the `cloudaicompanionProject` MUST be undefined; for
21
+ * standard-tier include it. `:onboardUser` returns an LRO — poll
22
+ * `:getOperation` until `done`, then read
23
+ * `response.cloudaicompanionProject.id`.
24
+ *
25
+ * Resolution result is cached in-memory keyed by access token (1:1 with the
26
+ * account) so the handshake runs at most once per token. On a documented hard
27
+ * failure (403 SERVICE_DISABLED / PERMISSION_DENIED, 429) we throw a clear
28
+ * error WITHOUT caching, so a transient/permission issue can be retried after
29
+ * the user fixes it.
30
+ *
31
+ * @module @omnicross/core/auth/GeminiCodeAssistProjectResolver
32
+ */
33
+ /** A clear, surfaced error for the documented hard-failure modes. */
34
+ declare class GeminiCodeAssistHandshakeError extends Error {
35
+ readonly status: number;
36
+ readonly code?: string | undefined;
37
+ constructor(message: string, status: number, code?: string | undefined);
38
+ }
39
+ /** Injectable fetch so tests can mock the network without a live endpoint. */
40
+ type FetchLike = (url: string, init: RequestInit) => Promise<Response>;
41
+ declare class GeminiCodeAssistProjectResolver {
42
+ private readonly fetchImpl;
43
+ /** account access token → resolved project id (undefined = free-tier, no project). */
44
+ private readonly cache;
45
+ /** In-flight handshakes so concurrent callers share one round-trip. */
46
+ private readonly inflight;
47
+ constructor(fetchImpl?: FetchLike);
48
+ /** Test/diagnostic helper — clear the cached resolution for an account. */
49
+ clearCache(): void;
50
+ /**
51
+ * Resolve (and cache) the Code Assist project for the given access token.
52
+ * Runs the handshake at most once per token. Returns `undefined` for a
53
+ * brand-new free-tier account (valid — the envelope sends no project).
54
+ */
55
+ resolveProject(accessToken: string): Promise<string | undefined>;
56
+ private codeAssistUrl;
57
+ /** Seed project id from env; reject a purely-numeric value (project NUMBER). */
58
+ private seedProjectId;
59
+ private runHandshake;
60
+ /** POST a Code Assist method, surfacing the documented hard failures clearly. */
61
+ private postCodeAssist;
62
+ private getOperation;
63
+ /** Map a non-2xx Code Assist response to a clear, surfaced error. */
64
+ private throwForStatus;
65
+ }
66
+ declare function getGeminiCodeAssistProjectResolver(): GeminiCodeAssistProjectResolver;
67
+
68
+ export { type FetchLike, GeminiCodeAssistHandshakeError, GeminiCodeAssistProjectResolver, getGeminiCodeAssistProjectResolver };
@@ -0,0 +1,189 @@
1
+ // src/transformer/transformers/GeminiCodeAssistTransformer.ts
2
+ var DEFAULT_CODE_ASSIST_ENDPOINT = "https://cloudcode-pa.googleapis.com";
3
+ var DEFAULT_CODE_ASSIST_API_VERSION = "v1internal";
4
+ function resolveCodeAssistEndpoint() {
5
+ return (process.env.CODE_ASSIST_ENDPOINT || DEFAULT_CODE_ASSIST_ENDPOINT).replace(/\/+$/, "");
6
+ }
7
+ function resolveCodeAssistApiVersion() {
8
+ return process.env.CODE_ASSIST_API_VERSION || DEFAULT_CODE_ASSIST_API_VERSION;
9
+ }
10
+
11
+ // src/auth/GeminiCodeAssistProjectResolver.ts
12
+ var FREE_TIER_ID = "free-tier";
13
+ var LEGACY_TIER_ID = "legacy-tier";
14
+ var ONBOARD_POLL_INTERVAL_MS = 5e3;
15
+ var ONBOARD_MAX_POLLS = 24;
16
+ var GeminiCodeAssistHandshakeError = class extends Error {
17
+ constructor(message, status, code) {
18
+ super(message);
19
+ this.status = status;
20
+ this.code = code;
21
+ this.name = "GeminiCodeAssistHandshakeError";
22
+ }
23
+ status;
24
+ code;
25
+ };
26
+ var GeminiCodeAssistProjectResolver = class {
27
+ constructor(fetchImpl = (url, init) => fetch(url, init)) {
28
+ this.fetchImpl = fetchImpl;
29
+ }
30
+ fetchImpl;
31
+ /** account access token → resolved project id (undefined = free-tier, no project). */
32
+ cache = /* @__PURE__ */ new Map();
33
+ /** In-flight handshakes so concurrent callers share one round-trip. */
34
+ inflight = /* @__PURE__ */ new Map();
35
+ /** Test/diagnostic helper — clear the cached resolution for an account. */
36
+ clearCache() {
37
+ this.cache.clear();
38
+ this.inflight.clear();
39
+ }
40
+ /**
41
+ * Resolve (and cache) the Code Assist project for the given access token.
42
+ * Runs the handshake at most once per token. Returns `undefined` for a
43
+ * brand-new free-tier account (valid — the envelope sends no project).
44
+ */
45
+ async resolveProject(accessToken) {
46
+ if (this.cache.has(accessToken)) {
47
+ return this.cache.get(accessToken);
48
+ }
49
+ const existing = this.inflight.get(accessToken);
50
+ if (existing) return existing;
51
+ const run = this.runHandshake(accessToken).then((project) => {
52
+ this.cache.set(accessToken, project);
53
+ return project;
54
+ }).finally(() => {
55
+ this.inflight.delete(accessToken);
56
+ });
57
+ this.inflight.set(accessToken, run);
58
+ return run;
59
+ }
60
+ codeAssistUrl(method) {
61
+ return `${resolveCodeAssistEndpoint()}/${resolveCodeAssistApiVersion()}:${method}`;
62
+ }
63
+ /** Seed project id from env; reject a purely-numeric value (project NUMBER). */
64
+ seedProjectId() {
65
+ const seed = process.env.GOOGLE_CLOUD_PROJECT || process.env.GOOGLE_CLOUD_PROJECT_ID || "";
66
+ if (seed && /^\d+$/.test(seed.trim())) return "";
67
+ return seed.trim();
68
+ }
69
+ async runHandshake(accessToken) {
70
+ const seededProject = this.seedProjectId();
71
+ const load = await this.postCodeAssist("loadCodeAssist", accessToken, {
72
+ cloudaicompanionProject: seededProject || void 0,
73
+ metadata: {
74
+ ideType: "IDE_UNSPECIFIED",
75
+ platform: "PLATFORM_UNSPECIFIED",
76
+ pluginType: "GEMINI",
77
+ duetProject: seededProject || void 0
78
+ }
79
+ });
80
+ if (load.currentTier?.id) {
81
+ return load.cloudaicompanionProject || void 0;
82
+ }
83
+ const defaultTier = load.allowedTiers?.find((t) => t.isDefault);
84
+ const tierId = defaultTier?.id ?? LEGACY_TIER_ID;
85
+ const isFreeish = tierId === FREE_TIER_ID || tierId === LEGACY_TIER_ID;
86
+ const onboardProject = isFreeish ? void 0 : seededProject || void 0;
87
+ let lro = await this.postCodeAssist("onboardUser", accessToken, {
88
+ tierId,
89
+ cloudaicompanionProject: onboardProject,
90
+ metadata: {
91
+ ideType: "IDE_UNSPECIFIED",
92
+ platform: "PLATFORM_UNSPECIFIED",
93
+ pluginType: "GEMINI",
94
+ duetProject: onboardProject
95
+ }
96
+ });
97
+ let polls = 0;
98
+ while (!lro.done && polls < ONBOARD_MAX_POLLS) {
99
+ await delay(ONBOARD_POLL_INTERVAL_MS);
100
+ polls++;
101
+ lro = await this.getOperation(accessToken, lro.name);
102
+ }
103
+ if (lro.error) {
104
+ throw new GeminiCodeAssistHandshakeError(
105
+ `Code Assist onboardUser failed: ${lro.error.message ?? "unknown"}`,
106
+ lro.error.code ?? 500
107
+ );
108
+ }
109
+ return lro.response?.cloudaicompanionProject?.id || void 0;
110
+ }
111
+ /** POST a Code Assist method, surfacing the documented hard failures clearly. */
112
+ async postCodeAssist(method, accessToken, body) {
113
+ const url = this.codeAssistUrl(method);
114
+ const res = await this.fetchImpl(url, {
115
+ method: "POST",
116
+ headers: {
117
+ Authorization: `Bearer ${accessToken}`,
118
+ "Content-Type": "application/json"
119
+ },
120
+ body: JSON.stringify(body)
121
+ });
122
+ if (!res.ok) {
123
+ await this.throwForStatus(res, method);
124
+ }
125
+ return await res.json();
126
+ }
127
+ async getOperation(accessToken, name) {
128
+ const url = this.codeAssistUrl("getOperation");
129
+ const res = await this.fetchImpl(url, {
130
+ method: "POST",
131
+ headers: {
132
+ Authorization: `Bearer ${accessToken}`,
133
+ "Content-Type": "application/json"
134
+ },
135
+ body: JSON.stringify({ name })
136
+ });
137
+ if (!res.ok) {
138
+ await this.throwForStatus(res, "getOperation");
139
+ }
140
+ return await res.json();
141
+ }
142
+ /** Map a non-2xx Code Assist response to a clear, surfaced error. */
143
+ async throwForStatus(res, method) {
144
+ let detailCode;
145
+ let detailMessage = "";
146
+ try {
147
+ const errBody = await res.json();
148
+ detailCode = errBody.error?.status;
149
+ detailMessage = errBody.error?.message ?? "";
150
+ } catch {
151
+ }
152
+ if (res.status === 403) {
153
+ throw new GeminiCodeAssistHandshakeError(
154
+ `Code Assist ${method} denied (403 ${detailCode ?? "PERMISSION_DENIED"}): ${detailMessage || "enable the Cloud AI Companion API and grant the required role"}`,
155
+ 403,
156
+ detailCode
157
+ );
158
+ }
159
+ if (res.status === 429) {
160
+ throw new GeminiCodeAssistHandshakeError(
161
+ `Code Assist ${method} rate-limited (429): ${detailMessage || "quota exceeded, retry later"}`,
162
+ 429,
163
+ detailCode
164
+ );
165
+ }
166
+ throw new GeminiCodeAssistHandshakeError(
167
+ `Code Assist ${method} failed (${res.status}): ${detailMessage || res.statusText}`,
168
+ res.status,
169
+ detailCode
170
+ );
171
+ }
172
+ };
173
+ function delay(ms) {
174
+ return new Promise((resolve) => {
175
+ setTimeout(resolve, ms);
176
+ });
177
+ }
178
+ var _resolverSingleton = null;
179
+ function getGeminiCodeAssistProjectResolver() {
180
+ if (!_resolverSingleton) {
181
+ _resolverSingleton = new GeminiCodeAssistProjectResolver();
182
+ }
183
+ return _resolverSingleton;
184
+ }
185
+ export {
186
+ GeminiCodeAssistHandshakeError,
187
+ GeminiCodeAssistProjectResolver,
188
+ getGeminiCodeAssistProjectResolver
189
+ };