dsh-antigravity-auth 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/CHANGELOG.md +28 -0
  2. package/LICENSE +21 -0
  3. package/README.md +168 -0
  4. package/README.zh.md +159 -0
  5. package/cordis.patch.yml +12 -0
  6. package/lib/capability-lifecycle--tgGKajs.js +1896 -0
  7. package/lib/client.cjs +2123 -0
  8. package/lib/client.cjs.map +1 -0
  9. package/lib/image.js +564 -0
  10. package/lib/index.js +176 -0
  11. package/lib/invariant.js +2 -0
  12. package/lib/live-gate-runner.js +542 -0
  13. package/lib/live-gates.js +97 -0
  14. package/lib/llm-adapter.js +916 -0
  15. package/lib/media-admission.js +206 -0
  16. package/lib/private-failure-B9vzUkQ7.js +23 -0
  17. package/lib/private-transport-urF138qW.js +641 -0
  18. package/lib/private-transport.js +2 -0
  19. package/lib/project-context.js +133 -0
  20. package/lib/quota.js +283 -0
  21. package/lib/replay.js +156 -0
  22. package/lib/rpc-contract-BFWfdKKs.js +491 -0
  23. package/lib/rpc-contract.js +2 -0
  24. package/lib/safe-text-AlEyN8q_.js +12 -0
  25. package/lib/search.js +259 -0
  26. package/lib/status-D0-em3Ru.js +125 -0
  27. package/lib/types/auth-service.d.ts +64 -0
  28. package/lib/types/auth-service.d.ts.map +1 -0
  29. package/lib/types/auth-store.d.ts +45 -0
  30. package/lib/types/auth-store.d.ts.map +1 -0
  31. package/lib/types/bootstrap-service.d.ts +6 -0
  32. package/lib/types/bootstrap-service.d.ts.map +1 -0
  33. package/lib/types/capability-gates.d.ts +19 -0
  34. package/lib/types/capability-gates.d.ts.map +1 -0
  35. package/lib/types/capability-lifecycle.d.ts +26 -0
  36. package/lib/types/capability-lifecycle.d.ts.map +1 -0
  37. package/lib/types/client/AntigravityAuthSettings.d.ts +19 -0
  38. package/lib/types/client/AntigravityAuthSettings.d.ts.map +1 -0
  39. package/lib/types/client/index.d.ts +18 -0
  40. package/lib/types/client/index.d.ts.map +1 -0
  41. package/lib/types/client/locales.d.ts +121 -0
  42. package/lib/types/client/locales.d.ts.map +1 -0
  43. package/lib/types/client/styles.d.ts +4 -0
  44. package/lib/types/client/styles.d.ts.map +1 -0
  45. package/lib/types/credential-coordinator.d.ts +84 -0
  46. package/lib/types/credential-coordinator.d.ts.map +1 -0
  47. package/lib/types/image.d.ts +39 -0
  48. package/lib/types/image.d.ts.map +1 -0
  49. package/lib/types/index.d.ts +21 -0
  50. package/lib/types/index.d.ts.map +1 -0
  51. package/lib/types/invariant.d.ts +3 -0
  52. package/lib/types/invariant.d.ts.map +1 -0
  53. package/lib/types/live-gate-attachments.d.ts +6 -0
  54. package/lib/types/live-gate-attachments.d.ts.map +1 -0
  55. package/lib/types/live-gate-auth.d.ts +5 -0
  56. package/lib/types/live-gate-auth.d.ts.map +1 -0
  57. package/lib/types/live-gate-image.d.ts +5 -0
  58. package/lib/types/live-gate-image.d.ts.map +1 -0
  59. package/lib/types/live-gate-llm.d.ts +10 -0
  60. package/lib/types/live-gate-llm.d.ts.map +1 -0
  61. package/lib/types/live-gate-outcome.d.ts +8 -0
  62. package/lib/types/live-gate-outcome.d.ts.map +1 -0
  63. package/lib/types/live-gate-runner.d.ts +15 -0
  64. package/lib/types/live-gate-runner.d.ts.map +1 -0
  65. package/lib/types/live-gate-search.d.ts +5 -0
  66. package/lib/types/live-gate-search.d.ts.map +1 -0
  67. package/lib/types/live-gate-video.d.ts +8 -0
  68. package/lib/types/live-gate-video.d.ts.map +1 -0
  69. package/lib/types/live-gates.d.ts +25 -0
  70. package/lib/types/live-gates.d.ts.map +1 -0
  71. package/lib/types/llm-adapter.d.ts +65 -0
  72. package/lib/types/llm-adapter.d.ts.map +1 -0
  73. package/lib/types/login-types.d.ts +36 -0
  74. package/lib/types/login-types.d.ts.map +1 -0
  75. package/lib/types/media-admission.d.ts +54 -0
  76. package/lib/types/media-admission.d.ts.map +1 -0
  77. package/lib/types/model-catalog.d.ts +19 -0
  78. package/lib/types/model-catalog.d.ts.map +1 -0
  79. package/lib/types/oauth-flow.d.ts +87 -0
  80. package/lib/types/oauth-flow.d.ts.map +1 -0
  81. package/lib/types/private-failure.d.ts +6 -0
  82. package/lib/types/private-failure.d.ts.map +1 -0
  83. package/lib/types/private-transport-error.d.ts +13 -0
  84. package/lib/types/private-transport-error.d.ts.map +1 -0
  85. package/lib/types/private-transport.d.ts +48 -0
  86. package/lib/types/private-transport.d.ts.map +1 -0
  87. package/lib/types/project-context.d.ts +30 -0
  88. package/lib/types/project-context.d.ts.map +1 -0
  89. package/lib/types/quota.d.ts +43 -0
  90. package/lib/types/quota.d.ts.map +1 -0
  91. package/lib/types/raw-http.d.ts +11 -0
  92. package/lib/types/raw-http.d.ts.map +1 -0
  93. package/lib/types/replay.d.ts +30 -0
  94. package/lib/types/replay.d.ts.map +1 -0
  95. package/lib/types/rpc-contract.d.ts +33 -0
  96. package/lib/types/rpc-contract.d.ts.map +1 -0
  97. package/lib/types/rpc-vocabulary.d.ts +7 -0
  98. package/lib/types/rpc-vocabulary.d.ts.map +1 -0
  99. package/lib/types/rpc.d.ts +11 -0
  100. package/lib/types/rpc.d.ts.map +1 -0
  101. package/lib/types/safe-text.d.ts +3 -0
  102. package/lib/types/safe-text.d.ts.map +1 -0
  103. package/lib/types/search.d.ts +42 -0
  104. package/lib/types/search.d.ts.map +1 -0
  105. package/lib/types/status.d.ts +62 -0
  106. package/lib/types/status.d.ts.map +1 -0
  107. package/lib/types/video.d.ts +42 -0
  108. package/lib/types/video.d.ts.map +1 -0
  109. package/lib/types/wire-identity.d.ts +49 -0
  110. package/lib/types/wire-identity.d.ts.map +1 -0
  111. package/lib/video.js +320 -0
  112. package/lib/wire-identity.js +162 -0
  113. package/package.json +189 -0
  114. package/scripts/live-gates.mjs +4 -0
package/lib/client.cjs ADDED
@@ -0,0 +1,2123 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "dsh-antigravity-auth",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let react = require("react");
8
+ let react_jsx_runtime = require("react/jsx-runtime");
9
+ //#region src/status.ts
10
+ const CAPABILITY_ROW_IDS = [
11
+ "auth-llm",
12
+ "search",
13
+ "image",
14
+ "video"
15
+ ];
16
+ Object.freeze(CAPABILITY_ROW_IDS.map((id) => Object.freeze({
17
+ id,
18
+ state: "disabled",
19
+ reasonCode: "unauthenticated"
20
+ })));
21
+ //#endregion
22
+ //#region src/login-types.ts
23
+ /** Browser-safe login state shared by the Host OAuth flow and settings RPC. */
24
+ const LOGIN_PHASES = [
25
+ "idle",
26
+ "pending",
27
+ "success",
28
+ "cancelled",
29
+ "expired",
30
+ "port-conflict",
31
+ "failed"
32
+ ];
33
+ const LOGIN_ERROR_CODES = [
34
+ "invalid-method",
35
+ "invalid-path",
36
+ "invalid-host",
37
+ "duplicate-parameter",
38
+ "invalid-parameters",
39
+ "missing-state",
40
+ "state-mismatch",
41
+ "missing-code",
42
+ "oauth-error",
43
+ "no-pending-flow",
44
+ "expired",
45
+ "cancelled",
46
+ "port-conflict",
47
+ "token-exchange-failed",
48
+ "project-unavailable",
49
+ "project-authentication-failed",
50
+ "project-forbidden",
51
+ "project-rate-limited",
52
+ "project-offline",
53
+ "project-malformed",
54
+ "project-protocol-drift",
55
+ "project-validation-failed",
56
+ "persistence-failed",
57
+ "credential-conflict",
58
+ "invalid-callback-url",
59
+ "risk-acknowledgement-required",
60
+ "internal"
61
+ ];
62
+ function isLoginPhase(value) {
63
+ return typeof value === "string" && LOGIN_PHASES.includes(value);
64
+ }
65
+ function isLoginErrorCode(value) {
66
+ return typeof value === "string" && LOGIN_ERROR_CODES.includes(value);
67
+ }
68
+ //#endregion
69
+ //#region src/model-catalog.ts
70
+ /** Browser-safe advisory model-catalog state shared by Host RPC and settings. */
71
+ const ANTIGRAVITY_MODEL_CATALOG_STATES = [
72
+ "snapshot",
73
+ "live-available",
74
+ "refresh-failed",
75
+ "protocol-drift"
76
+ ];
77
+ //#endregion
78
+ //#region src/rpc-vocabulary.ts
79
+ /** Closed, browser-safe RPC error vocabulary shared by Host and client. */
80
+ const OPERATION_ERROR_CODES = [
81
+ "invalid-grant",
82
+ "network",
83
+ "timeout",
84
+ "rate-limited",
85
+ "server-error",
86
+ "http-error",
87
+ "invalid-response",
88
+ "conflict",
89
+ "storage"
90
+ ];
91
+ const SAFE_RPC_ERROR_CODES = [
92
+ "bad-request",
93
+ ...LOGIN_ERROR_CODES,
94
+ ...OPERATION_ERROR_CODES
95
+ ];
96
+ const SAFE_RPC_ERROR_MESSAGES = Object.freeze({
97
+ "bad-request": "antigravity-auth: invalid request",
98
+ cancelled: "The operation was cancelled",
99
+ "risk-acknowledgement-required": "Risk acknowledgement is required before login",
100
+ "port-conflict": "The fixed OAuth callback port is already in use",
101
+ "invalid-method": "The OAuth callback method is not accepted",
102
+ "invalid-path": "The OAuth callback path is not accepted",
103
+ "invalid-host": "The OAuth callback host is not accepted",
104
+ "duplicate-parameter": "The OAuth callback contains duplicate parameters",
105
+ "invalid-parameters": "The OAuth callback parameters are not accepted",
106
+ "missing-state": "The OAuth callback state is missing",
107
+ "state-mismatch": "The OAuth callback state was not accepted",
108
+ "missing-code": "The OAuth callback code is missing",
109
+ "oauth-error": "The OAuth provider rejected authorization",
110
+ expired: "The OAuth login expired",
111
+ "no-pending-flow": "There is no pending OAuth login",
112
+ "project-unavailable": "No usable project is available for this account",
113
+ "project-authentication-failed": "The Antigravity project probe requires authentication",
114
+ "project-forbidden": "The Antigravity project probe was forbidden",
115
+ "project-rate-limited": "The Antigravity project probe is rate-limited",
116
+ "project-offline": "The Antigravity project probe is offline",
117
+ "project-malformed": "The Antigravity project response was malformed",
118
+ "project-protocol-drift": "The Antigravity project protocol changed",
119
+ "project-validation-failed": "Project validation failed",
120
+ "credential-conflict": "The login changed while it was completing",
121
+ "persistence-failed": "The login could not be saved",
122
+ "token-exchange-failed": "The authorization code could not be exchanged",
123
+ "invalid-callback-url": "The callback URL is invalid",
124
+ "invalid-grant": "The account must be authenticated again",
125
+ network: "The operation could not reach the provider",
126
+ timeout: "The operation timed out",
127
+ "rate-limited": "The operation is rate-limited",
128
+ "server-error": "The provider is unavailable",
129
+ "http-error": "The provider rejected the operation",
130
+ "invalid-response": "The provider response was not accepted",
131
+ conflict: "The account changed while the operation was running",
132
+ storage: "The local account state could not be updated"
133
+ });
134
+ function isSafeRpcErrorCode(value) {
135
+ return typeof value === "string" && SAFE_RPC_ERROR_CODES.includes(value);
136
+ }
137
+ function safeRpcErrorMessage(code) {
138
+ return SAFE_RPC_ERROR_MESSAGES[code] ?? "antigravity-auth: operation failed";
139
+ }
140
+ //#endregion
141
+ //#region src/safe-text.ts
142
+ /** Shared bounded text predicate for Host trust-boundary values. */
143
+ function isBoundedSafeText(value, maxLength, minLength = 1) {
144
+ if (typeof value !== "string" || !Number.isSafeInteger(maxLength) || !Number.isSafeInteger(minLength) || minLength < 0 || maxLength < minLength || value.length < minLength || value.length > maxLength) return false;
145
+ for (let index = 0; index < value.length; index += 1) {
146
+ const codePoint = value.charCodeAt(index);
147
+ if (codePoint < 32 || codePoint === 127) return false;
148
+ }
149
+ return true;
150
+ }
151
+ //#endregion
152
+ //#region src/rpc-contract.ts
153
+ const ANTIGRAVITY_AUTH_RPC_CHANNEL = "/antigravity-auth";
154
+ /** Build the browser face over the plugin-owned loopback channel. */
155
+ function createAntigravityAuthRpcClient(rpc) {
156
+ return {
157
+ status: (signal) => callValidated(rpc, "status", {}, signal, (value) => {
158
+ const status = parseStatusResult(value);
159
+ return status === void 0 ? void 0 : { status };
160
+ }),
161
+ acknowledgeRisk: (signal) => callValidated(rpc, "acknowledge-risk", { acknowledge: true }, signal, parseAcknowledgementResult),
162
+ login: (signal) => callValidated(rpc, "login", {}, signal, parseLoginResult),
163
+ cancelLogin: (signal) => callValidated(rpc, "cancel", {}, signal, parseActionResult),
164
+ logout: (signal) => callValidated(rpc, "logout", {}, signal, parseLogoutResult),
165
+ revoke: (signal) => callValidated(rpc, "revoke", { confirmed: true }, signal, parseRevokeResult),
166
+ models: (signal, force = false) => callValidated(rpc, "models", { force }, signal, parseModelCatalogResult),
167
+ usage: (signal, force = false) => callValidated(rpc, "usage", { force }, signal, parseUsageResult)
168
+ };
169
+ }
170
+ async function callValidated(rpc, endpoint, payload, signal, parse) {
171
+ let result;
172
+ try {
173
+ result = await rpc.call(ANTIGRAVITY_AUTH_RPC_CHANNEL, endpoint, payload, signal);
174
+ } catch {
175
+ return invalidResponse(endpoint);
176
+ }
177
+ if (!result.ok) return sanitizeFailure(result, endpoint);
178
+ const value = parse(result.value);
179
+ return value === void 0 ? invalidResponse(endpoint) : {
180
+ ok: true,
181
+ value
182
+ };
183
+ }
184
+ function sanitizeFailure(result, endpoint) {
185
+ const error = result.error;
186
+ if (!isRecord$1(error) || !hasExactKeys(error, [
187
+ "code",
188
+ "message",
189
+ "details"
190
+ ]) || !isSafeRpcErrorCode(error.code) || typeof error.message !== "string" || !isBoundedSafeText(error.message, 512) || !isSafeErrorDetails(error.details)) return invalidResponse(endpoint);
191
+ return {
192
+ ok: false,
193
+ error: {
194
+ code: error.code,
195
+ message: safeRpcErrorMessage(error.code),
196
+ details: {}
197
+ }
198
+ };
199
+ }
200
+ function isSafeErrorDetails(value) {
201
+ if (!isRecord$1(value)) return false;
202
+ if (Object.keys(value).length === 0) return true;
203
+ return hasExactKeys(value, ["issues"]) && Array.isArray(value.issues) && value.issues.length === 0;
204
+ }
205
+ /** Parse a closed, value-safe advisory model catalog received by the browser. */
206
+ function parseModelCatalogResult(value) {
207
+ if (!isRecord$1(value) || !hasExactKeys(value, [
208
+ "state",
209
+ "models",
210
+ ...value.checkedAt === void 0 ? [] : ["checkedAt"]
211
+ ]) || !ANTIGRAVITY_MODEL_CATALOG_STATES.includes(value.state) || !Array.isArray(value.models) || value.models.length === 0 || value.models.length > 64 || value.checkedAt !== void 0 && !isIsoTime(value.checkedAt)) return void 0;
212
+ const models = [];
213
+ const ids = /* @__PURE__ */ new Set();
214
+ for (const model of value.models) {
215
+ if (!isRecord$1(model) || !hasExactKeys(model, [
216
+ "id",
217
+ "name",
218
+ "state"
219
+ ]) || !isBoundedSafeText(model.id, 256) || !isBoundedSafeText(model.name, 256) || model.state !== "snapshot" && model.state !== "live-available" && model.state !== "unavailable" || ids.has(model.id)) return void 0;
220
+ if (value.state === "live-available" ? model.state === "snapshot" : model.state !== "snapshot") return void 0;
221
+ ids.add(model.id);
222
+ models.push({
223
+ id: model.id,
224
+ name: model.name,
225
+ state: model.state
226
+ });
227
+ }
228
+ return {
229
+ state: value.state,
230
+ models,
231
+ ...typeof value.checkedAt === "string" ? { checkedAt: value.checkedAt } : {}
232
+ };
233
+ }
234
+ /** Parse a value-safe, normalized quota envelope received by the browser. */
235
+ function parseUsageResult(value) {
236
+ if (!isRecord$1(value) || !hasExactKeys(value, [
237
+ "state",
238
+ ...value.checkedAt === void 0 ? [] : ["checkedAt"],
239
+ ...value.groups === void 0 ? [] : ["groups"]
240
+ ])) return void 0;
241
+ if (!isQuotaState(value.state)) return void 0;
242
+ if (value.checkedAt !== void 0 && !isIsoTime(value.checkedAt)) return void 0;
243
+ if (value.state === "available" && value.groups === void 0) return void 0;
244
+ if (value.groups !== void 0) {
245
+ if (!Array.isArray(value.groups) || value.groups.length === 0 || value.groups.length > 2) return void 0;
246
+ const groups = [];
247
+ const groupNames = /* @__PURE__ */ new Set();
248
+ for (const rawGroup of value.groups) {
249
+ if (!isRecord$1(rawGroup) || !hasExactKeys(rawGroup, [
250
+ "group",
251
+ "modelCount",
252
+ "windows"
253
+ ]) || rawGroup.group !== "gemini" && rawGroup.group !== "non-gemini" || !Number.isSafeInteger(rawGroup.modelCount) || rawGroup.modelCount < 0 || !Array.isArray(rawGroup.windows)) return void 0;
254
+ if (groupNames.has(rawGroup.group)) return void 0;
255
+ groupNames.add(rawGroup.group);
256
+ const modelCount = rawGroup.modelCount;
257
+ if (rawGroup.windows.length === 0 || rawGroup.windows.length > 2) return void 0;
258
+ const windows = [];
259
+ const windowKinds = /* @__PURE__ */ new Set();
260
+ for (const rawWindow of rawGroup.windows) {
261
+ if (!isRecord$1(rawWindow) || !hasExactKeys(rawWindow, [
262
+ "window",
263
+ "remainingFraction",
264
+ "resetTime"
265
+ ]) || rawWindow.window !== "5h" && rawWindow.window !== "weekly" || windowKinds.has(rawWindow.window) || typeof rawWindow.remainingFraction !== "number" || !Number.isFinite(rawWindow.remainingFraction) || rawWindow.remainingFraction < 0 || rawWindow.remainingFraction > 1 || !isIsoTime(rawWindow.resetTime)) return void 0;
266
+ windowKinds.add(rawWindow.window);
267
+ windows.push({
268
+ window: rawWindow.window,
269
+ remainingFraction: rawWindow.remainingFraction,
270
+ resetTime: rawWindow.resetTime
271
+ });
272
+ }
273
+ groups.push({
274
+ group: rawGroup.group,
275
+ modelCount,
276
+ windows
277
+ });
278
+ }
279
+ return {
280
+ state: value.state,
281
+ ...typeof value.checkedAt === "string" ? { checkedAt: value.checkedAt } : {},
282
+ groups
283
+ };
284
+ }
285
+ return {
286
+ state: value.state,
287
+ ...typeof value.checkedAt === "string" ? { checkedAt: value.checkedAt } : {}
288
+ };
289
+ }
290
+ /** Parse the closed status envelope received by the browser. */
291
+ function parseStatusResult(value) {
292
+ if (!isRecord$1(value) || !hasExactKeys(value, ["status"])) return void 0;
293
+ return parseStatus(value.status);
294
+ }
295
+ function parseStatus(value) {
296
+ if (!isRecord$1(value) || !hasAllowedKeys(value, [
297
+ "pluginId",
298
+ "phase",
299
+ "privateSelfUse",
300
+ "singleAccount",
301
+ "riskAcknowledgementRequired",
302
+ "riskAcknowledged",
303
+ "login",
304
+ "credential",
305
+ "revoke",
306
+ "capabilities"
307
+ ]) || value.pluginId !== "dsh-antigravity-auth" || value.phase !== "bootstrap" || value.privateSelfUse !== true || value.singleAccount !== true || value.riskAcknowledgementRequired !== true || typeof value.riskAcknowledged !== "boolean" || !Array.isArray(value.capabilities)) return void 0;
308
+ const login = parseLoginStatus(value.login);
309
+ if (login === void 0) return void 0;
310
+ const credential = value.credential === void 0 ? void 0 : parseCredentialStatus(value.credential);
311
+ if (value.credential !== void 0 && credential === void 0) return void 0;
312
+ const revoke = value.revoke === void 0 ? void 0 : parseRevokeStatus(value.revoke);
313
+ if (value.revoke !== void 0 && revoke === void 0) return void 0;
314
+ const seen = /* @__PURE__ */ new Set();
315
+ const capabilities = [];
316
+ for (const capability of value.capabilities) {
317
+ const parsed = parseCapability(capability);
318
+ if (parsed === void 0 || seen.has(parsed.id)) return void 0;
319
+ seen.add(parsed.id);
320
+ capabilities.push(parsed);
321
+ }
322
+ if (seen.size !== CAPABILITY_ROW_IDS.length || CAPABILITY_ROW_IDS.some((id) => !seen.has(id))) return void 0;
323
+ return {
324
+ pluginId: "dsh-antigravity-auth",
325
+ phase: "bootstrap",
326
+ privateSelfUse: true,
327
+ singleAccount: true,
328
+ riskAcknowledgementRequired: true,
329
+ riskAcknowledged: value.riskAcknowledged,
330
+ login,
331
+ ...credential === void 0 ? {} : { credential },
332
+ ...revoke === void 0 ? {} : { revoke },
333
+ capabilities
334
+ };
335
+ }
336
+ function parseCredentialStatus(value) {
337
+ if (!isRecord$1(value) || typeof value.state !== "string" || !isCredentialState(value.state) || typeof value.configured !== "boolean" || !hasAllowedKeys(value, [
338
+ "state",
339
+ "configured",
340
+ "expiresAt",
341
+ "lastRefreshAt",
342
+ "errorCode"
343
+ ])) return void 0;
344
+ if (value.expiresAt !== void 0 && !isIsoTime(value.expiresAt)) return void 0;
345
+ if (value.lastRefreshAt !== void 0 && !isIsoTime(value.lastRefreshAt)) return void 0;
346
+ if (value.errorCode !== void 0 && (typeof value.errorCode !== "string" || !isCredentialErrorCode(value.errorCode))) return void 0;
347
+ return {
348
+ state: value.state,
349
+ configured: value.configured,
350
+ ...typeof value.expiresAt === "string" ? { expiresAt: value.expiresAt } : {},
351
+ ...typeof value.lastRefreshAt === "string" ? { lastRefreshAt: value.lastRefreshAt } : {},
352
+ ...typeof value.errorCode === "string" ? { errorCode: value.errorCode } : {}
353
+ };
354
+ }
355
+ function parseRevokeStatus(value) {
356
+ if (!isRecord$1(value) || typeof value.state !== "string" || !isRevokeState(value.state) || !hasAllowedKeys(value, ["state", "errorCode"])) return void 0;
357
+ if (value.errorCode !== void 0 && (typeof value.errorCode !== "string" || !isRevokeErrorCode(value.errorCode))) return void 0;
358
+ return {
359
+ state: value.state,
360
+ ...typeof value.errorCode === "string" ? { errorCode: value.errorCode } : {}
361
+ };
362
+ }
363
+ function parseLoginStatus(value) {
364
+ if (!isRecord$1(value) || typeof value.phase !== "string" || !isLoginPhase(value.phase) || typeof value.configured !== "boolean" || typeof value.projectAvailable !== "boolean") return void 0;
365
+ const allowed = [
366
+ "phase",
367
+ "configured",
368
+ "projectAvailable",
369
+ "authorizationUrl",
370
+ "expiresAt",
371
+ "maskedEmail",
372
+ "errorCode"
373
+ ];
374
+ if (Object.keys(value).some((key) => !allowed.includes(key))) return void 0;
375
+ if (value.authorizationUrl !== void 0 && !isSafeAuthorizationUrl(value.authorizationUrl)) return void 0;
376
+ if (value.expiresAt !== void 0 && !isIsoTime(value.expiresAt)) return void 0;
377
+ if (value.maskedEmail !== void 0 && (typeof value.maskedEmail !== "string" || !isMaskedEmail(value.maskedEmail))) return void 0;
378
+ if (value.errorCode !== void 0 && !isLoginErrorCode(value.errorCode)) return void 0;
379
+ if (isErrorPhase(value.phase) ? typeof value.errorCode !== "string" : value.errorCode !== void 0) return void 0;
380
+ if (value.phase === "pending" ? typeof value.authorizationUrl !== "string" || typeof value.expiresAt !== "string" : value.authorizationUrl !== void 0) return void 0;
381
+ return {
382
+ phase: value.phase,
383
+ configured: value.configured,
384
+ projectAvailable: value.projectAvailable,
385
+ ...typeof value.authorizationUrl === "string" ? { authorizationUrl: value.authorizationUrl } : {},
386
+ ...typeof value.expiresAt === "string" ? { expiresAt: value.expiresAt } : {},
387
+ ...typeof value.maskedEmail === "string" ? { maskedEmail: value.maskedEmail } : {},
388
+ ...typeof value.errorCode === "string" ? { errorCode: value.errorCode } : {}
389
+ };
390
+ }
391
+ function parseCapability(value) {
392
+ if (!isRecord$1(value) || !hasExactKeys(value, [
393
+ "id",
394
+ "state",
395
+ "reasonCode"
396
+ ])) return void 0;
397
+ if (!isCapabilityRowId(value.id) || !isCapabilityGateState(value.state) || !isCapabilityReasonCode(value.reasonCode)) return;
398
+ return {
399
+ id: value.id,
400
+ state: value.state,
401
+ reasonCode: value.reasonCode
402
+ };
403
+ }
404
+ function parseAcknowledgementResult(value) {
405
+ return isRecord$1(value) && hasExactKeys(value, ["acknowledged"]) && value.acknowledged === true ? { acknowledged: true } : void 0;
406
+ }
407
+ function parseLoginResult(value) {
408
+ return isRecord$1(value) && hasExactKeys(value, [
409
+ "started",
410
+ "phase",
411
+ "authorizationUrl",
412
+ "expiresAt"
413
+ ]) && value.started === true && value.phase === "pending" && isSafeAuthorizationUrl(value.authorizationUrl) && isIsoTime(value.expiresAt) ? {
414
+ started: true,
415
+ phase: "pending",
416
+ authorizationUrl: value.authorizationUrl,
417
+ expiresAt: value.expiresAt
418
+ } : void 0;
419
+ }
420
+ function parseActionResult(value) {
421
+ if (!isRecord$1(value) || typeof value.phase !== "string" || !isLoginPhase(value.phase)) return void 0;
422
+ if (Object.keys(value).some((key) => key !== "phase" && key !== "errorCode")) return void 0;
423
+ if (value.errorCode !== void 0 && !isLoginErrorCode(value.errorCode)) return void 0;
424
+ return {
425
+ phase: value.phase,
426
+ ...typeof value.errorCode === "string" ? { errorCode: value.errorCode } : {}
427
+ };
428
+ }
429
+ function parseLogoutResult(value) {
430
+ return isRecord$1(value) && hasExactKeys(value, ["state"]) && value.state === "logged-out" ? { state: "logged-out" } : void 0;
431
+ }
432
+ function parseRevokeResult(value) {
433
+ if (!isRecord$1(value) || typeof value.state !== "string") return void 0;
434
+ if (value.state === "confirmation-required" || value.state === "revoked" || value.state === "logged-out" || value.state === "superseded") return hasExactKeys(value, ["state"]) ? { state: value.state } : void 0;
435
+ if (value.state === "failed" && hasExactKeys(value, ["state", "errorCode"]) && typeof value.errorCode === "string" && isRevokeErrorCode(value.errorCode)) return {
436
+ state: "failed",
437
+ errorCode: value.errorCode
438
+ };
439
+ }
440
+ function isSafeAuthorizationUrl(value) {
441
+ if (typeof value !== "string" || value.length === 0 || value.length > 8192) return false;
442
+ try {
443
+ const parsed = new URL(value);
444
+ const allowed = /* @__PURE__ */ new Set([
445
+ "client_id",
446
+ "response_type",
447
+ "redirect_uri",
448
+ "scope",
449
+ "code_challenge",
450
+ "code_challenge_method",
451
+ "state",
452
+ "access_type",
453
+ "prompt"
454
+ ]);
455
+ for (const [key, parameter] of parsed.searchParams) if (!allowed.has(key) || parsed.searchParams.getAll(key).length !== 1 || !isBoundedSafeText(parameter, 4096)) return false;
456
+ const state = parsed.searchParams.get("state");
457
+ const clientId = parsed.searchParams.get("client_id");
458
+ const challenge = parsed.searchParams.get("code_challenge");
459
+ return parsed.protocol === "https:" && parsed.hostname === "accounts.google.com" && parsed.port === "" && parsed.username === "" && parsed.password === "" && parsed.pathname === "/o/oauth2/v2/auth" && parsed.hash.length === 0 && typeof state === "string" && /^[A-Za-z0-9_-]{43}$/u.test(state) && (clientId === null || clientId.length <= 256 && clientId.endsWith(".apps.googleusercontent.com")) && (challenge === null || /^[A-Za-z0-9_-]{43}$/u.test(challenge)) && (parsed.searchParams.get("response_type") === null || parsed.searchParams.get("response_type") === "code") && (parsed.searchParams.get("redirect_uri") === null || parsed.searchParams.get("redirect_uri") === "http://localhost:51121/oauth-callback") && (parsed.searchParams.get("code_challenge_method") === null || parsed.searchParams.get("code_challenge_method") === "S256") && (parsed.searchParams.get("access_type") === null || parsed.searchParams.get("access_type") === "offline") && (parsed.searchParams.get("prompt") === null || parsed.searchParams.get("prompt") === "consent");
460
+ } catch {
461
+ return false;
462
+ }
463
+ }
464
+ function isMaskedEmail(value) {
465
+ return isBoundedSafeText(value, 256) && /^.[*]{3}[^@]*@[^@\s]+$/u.test(value);
466
+ }
467
+ function isErrorPhase(value) {
468
+ return value === "cancelled" || value === "expired" || value === "port-conflict" || value === "failed";
469
+ }
470
+ function isQuotaState(value) {
471
+ return value === "available" || value === "unauthenticated" || value === "forbidden" || value === "rate-limited" || value === "offline" || value === "timeout" || value === "protocol-drift";
472
+ }
473
+ function isIsoTime(value) {
474
+ if (typeof value !== "string" || value.length > 64) return false;
475
+ const timestamp = Date.parse(value);
476
+ return Number.isFinite(timestamp) && new Date(timestamp).toISOString() === value;
477
+ }
478
+ function isCapabilityRowId(value) {
479
+ return typeof value === "string" && CAPABILITY_ROW_IDS.includes(value);
480
+ }
481
+ function isCapabilityGateState(value) {
482
+ return value === "available" || value === "disabled" || value === "poc-pending" || value === "protocol-drift";
483
+ }
484
+ function isCapabilityReasonCode(value) {
485
+ return value === "gate-not-run" || value === "project-unavailable" || value === "capability-ready" || value === "unauthenticated" || value === "rate-limited" || value === "cancelled" || value === "gate-0-failed" || value === "gate-failed" || value === "unsupported-video" || value === "protocol-drift";
486
+ }
487
+ function isCredentialState(value) {
488
+ return value === "logged-out" || value === "logged-in" || value === "refreshing" || value === "refresh-failed" || value === "re-login-required";
489
+ }
490
+ function isCredentialErrorCode(value) {
491
+ return value === "invalid-grant" || value === "network" || value === "timeout" || value === "rate-limited" || value === "server-error" || value === "http-error" || value === "invalid-response" || value === "conflict" || value === "storage" || value === "cancelled";
492
+ }
493
+ function isRevokeState(value) {
494
+ return value === "idle" || value === "pending" || value === "confirmation-required" || value === "revoked" || value === "logged-out" || value === "failed" || value === "superseded";
495
+ }
496
+ function isRevokeErrorCode(value) {
497
+ return value === "network" || value === "timeout" || value === "rate-limited" || value === "server-error" || value === "http-error" || value === "invalid-response" || value === "storage";
498
+ }
499
+ function hasAllowedKeys(value, allowed) {
500
+ return Object.keys(value).every((key) => allowed.includes(key));
501
+ }
502
+ function hasExactKeys(value, expected) {
503
+ const keys = Object.keys(value).sort();
504
+ const sortedExpected = [...expected].sort();
505
+ return keys.length === sortedExpected.length && keys.every((key, index) => key === sortedExpected[index]);
506
+ }
507
+ function invalidResponse(endpoint) {
508
+ return {
509
+ ok: false,
510
+ error: {
511
+ code: "internal",
512
+ message: `antigravity-auth: invalid ${endpoint} response from Host`,
513
+ details: {}
514
+ }
515
+ };
516
+ }
517
+ function isRecord$1(value) {
518
+ return typeof value === "object" && value !== null && !Array.isArray(value);
519
+ }
520
+ //#endregion
521
+ //#region src/client/styles.ts
522
+ /** Embedded CSS stylesheet for value-safe Antigravity settings UI. */
523
+ const SETTINGS_CSS = `
524
+ .agy-settings {
525
+ display: flex;
526
+ flex-direction: column;
527
+ gap: 16px;
528
+ width: 100%;
529
+ max-width: 820px;
530
+ margin: 0 auto;
531
+ padding: 4px 0 32px;
532
+ color: var(--dsw-alias-label-primary, #e6edf3);
533
+ font-family: var(--dsw-font-sans, -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif);
534
+ font-size: 13px;
535
+ line-height: 1.5;
536
+ box-sizing: border-box;
537
+ -webkit-font-smoothing: antialiased;
538
+ }
539
+
540
+ .agy-settings *,
541
+ .agy-settings *::before,
542
+ .agy-settings *::after {
543
+ box-sizing: border-box;
544
+ }
545
+
546
+ .agy-bundle-header {
547
+ display: flex;
548
+ align-items: flex-start;
549
+ justify-content: space-between;
550
+ gap: 20px;
551
+ }
552
+
553
+ .agy-title-line {
554
+ display: flex;
555
+ align-items: center;
556
+ gap: 8px;
557
+ }
558
+
559
+ .agy-bundle-title {
560
+ margin: 0;
561
+ font-size: 20px;
562
+ line-height: 28px;
563
+ font-weight: 600;
564
+ letter-spacing: -0.015em;
565
+ color: var(--dsw-alias-label-primary, #e6edf3);
566
+ }
567
+
568
+ .agy-status-dot {
569
+ display: inline-block;
570
+ width: 8px;
571
+ height: 8px;
572
+ border-radius: 50%;
573
+ background: #10b981;
574
+ box-shadow: 0 0 8px rgba(16, 185, 129, 0.4);
575
+ flex: none;
576
+ }
577
+
578
+ .agy-bundle-intro,
579
+ .agy-card-intro {
580
+ margin: 4px 0 0;
581
+ color: var(--dsw-alias-label-tertiary, #8b949e);
582
+ font-size: 13px;
583
+ line-height: 20px;
584
+ }
585
+
586
+ .agy-cards {
587
+ display: flex;
588
+ flex-direction: column;
589
+ gap: 14px;
590
+ }
591
+
592
+ .agy-card {
593
+ display: flex;
594
+ flex-direction: column;
595
+ gap: 14px;
596
+ padding: 18px 20px;
597
+ border-radius: 16px;
598
+ background: var(--dsw-alias-bg-layer-2, rgba(255, 255, 255, 0.03));
599
+ border: 1px solid var(--dsw-alias-border-l2, rgba(255, 255, 255, 0.08));
600
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
601
+ }
602
+
603
+ .agy-card-header {
604
+ display: flex;
605
+ align-items: flex-start;
606
+ justify-content: space-between;
607
+ gap: 16px;
608
+ }
609
+
610
+ .agy-card-identity {
611
+ min-width: 0;
612
+ flex: 1;
613
+ }
614
+
615
+ .agy-card-title-line {
616
+ display: flex;
617
+ align-items: center;
618
+ gap: 8px;
619
+ }
620
+
621
+ .agy-card-title {
622
+ margin: 0;
623
+ font-size: 15px;
624
+ line-height: 22px;
625
+ font-weight: 600;
626
+ color: var(--dsw-alias-label-primary, #e6edf3);
627
+ }
628
+
629
+ .agy-card-action {
630
+ flex: none;
631
+ display: flex;
632
+ align-items: center;
633
+ }
634
+
635
+ .agy-risk-card {
636
+ background: rgba(234, 179, 8, 0.04);
637
+ border: 1px solid rgba(234, 179, 8, 0.2);
638
+ border-radius: 14px;
639
+ padding: 16px 18px;
640
+ display: flex;
641
+ flex-direction: column;
642
+ gap: 12px;
643
+ }
644
+
645
+ .agy-risk-text {
646
+ color: var(--dsw-alias-label-secondary, #9ca3af);
647
+ font-size: 13px;
648
+ line-height: 19px;
649
+ margin: 0;
650
+ }
651
+
652
+ .agy-link-group {
653
+ display: flex;
654
+ flex-wrap: wrap;
655
+ align-items: center;
656
+ gap: 12px;
657
+ margin: 0;
658
+ }
659
+
660
+ .agy-link {
661
+ color: var(--dsw-alias-interactive-text, #38bdf8);
662
+ font-size: 12px;
663
+ text-decoration: none;
664
+ display: inline-flex;
665
+ align-items: center;
666
+ gap: 4px;
667
+ }
668
+
669
+ .agy-link:hover {
670
+ text-decoration: underline;
671
+ color: #7dd3fc;
672
+ }
673
+
674
+ .agy-ack-row {
675
+ display: flex;
676
+ align-items: center;
677
+ gap: 10px;
678
+ padding: 10px 14px;
679
+ background: var(--dsw-alias-bg-layer-1, rgba(0, 0, 0, 0.2));
680
+ border: 1px solid var(--dsw-alias-border-l1, rgba(255, 255, 255, 0.06));
681
+ border-radius: 8px;
682
+ cursor: pointer;
683
+ user-select: none;
684
+ font-size: 13px;
685
+ font-weight: 500;
686
+ color: var(--dsw-alias-label-primary, #e6edf3);
687
+ }
688
+
689
+ .agy-checkbox {
690
+ flex: none;
691
+ width: 16px;
692
+ height: 16px;
693
+ margin: 0;
694
+ accent-color: var(--dsw-alias-button-primary-fill, #3b82f6);
695
+ cursor: pointer;
696
+ }
697
+
698
+ .agy-checkbox:disabled {
699
+ cursor: not-allowed;
700
+ opacity: 0.5;
701
+ }
702
+
703
+ .agy-facts {
704
+ display: grid;
705
+ grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
706
+ gap: 12px 16px;
707
+ margin: 0;
708
+ padding: 14px 16px;
709
+ border-radius: 12px;
710
+ background: var(--dsw-alias-bg-layer-3, rgba(255, 255, 255, 0.02));
711
+ border: 1px solid var(--dsw-alias-border-l1, rgba(255, 255, 255, 0.05));
712
+ }
713
+
714
+ .agy-fact {
715
+ min-width: 0;
716
+ }
717
+
718
+ .agy-fact dt {
719
+ margin: 0;
720
+ font-size: 12px;
721
+ line-height: 18px;
722
+ color: var(--dsw-alias-label-tertiary, #8b949e);
723
+ }
724
+
725
+ .agy-fact dd {
726
+ margin: 2px 0 0;
727
+ font-size: 13px;
728
+ line-height: 18px;
729
+ font-weight: 500;
730
+ color: var(--dsw-alias-label-primary, #e6edf3);
731
+ overflow-wrap: anywhere;
732
+ font-variant-numeric: tabular-nums;
733
+ }
734
+
735
+ /* Quota Section */
736
+ .agy-quota-section {
737
+ display: flex;
738
+ flex-direction: column;
739
+ gap: 14px;
740
+ margin-top: 4px;
741
+ }
742
+
743
+ .agy-quota-group {
744
+ display: flex;
745
+ flex-direction: column;
746
+ gap: 10px;
747
+ padding: 14px 16px;
748
+ border-radius: 12px;
749
+ background: var(--dsw-alias-bg-layer-1, rgba(0, 0, 0, 0.15));
750
+ border: 1px solid var(--dsw-alias-border-l1, rgba(255, 255, 255, 0.05));
751
+ box-shadow: inset 0 0 0 1px var(--dsw-alias-border-l2, rgba(255, 255, 255, 0.03));
752
+ }
753
+
754
+ .agy-quota-group-header {
755
+ display: flex;
756
+ flex-direction: column;
757
+ gap: 2px;
758
+ }
759
+
760
+ .agy-quota-group-title {
761
+ margin: 0;
762
+ font-size: 12px;
763
+ font-weight: 700;
764
+ letter-spacing: 0.05em;
765
+ text-transform: uppercase;
766
+ color: var(--dsw-alias-label-secondary, #a1a1aa);
767
+ }
768
+
769
+ .agy-quota-group-desc {
770
+ margin: 0;
771
+ font-size: 12px;
772
+ color: var(--dsw-alias-label-tertiary, #71717a);
773
+ }
774
+
775
+ .agy-quota-buckets {
776
+ display: flex;
777
+ flex-direction: column;
778
+ gap: 12px;
779
+ margin-top: 4px;
780
+ }
781
+
782
+ .agy-quota-bucket {
783
+ display: flex;
784
+ flex-direction: column;
785
+ gap: 6px;
786
+ }
787
+
788
+ .agy-quota-bucket-head {
789
+ display: flex;
790
+ align-items: center;
791
+ justify-content: space-between;
792
+ font-size: 12px;
793
+ line-height: 18px;
794
+ }
795
+
796
+ .agy-quota-bucket-name {
797
+ font-weight: 500;
798
+ color: var(--dsw-alias-label-secondary, #d4d4d8);
799
+ }
800
+
801
+ .agy-quota-bucket-val {
802
+ font-weight: 600;
803
+ font-variant-numeric: tabular-nums;
804
+ font-family: var(--dsw-font-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace);
805
+ color: var(--dsw-alias-label-primary, #ffffff);
806
+ animation: agy-fade-in 250ms ease;
807
+ }
808
+
809
+ .agy-quota-bucket-val[data-tone='normal'] {
810
+ color: var(--dsw-alias-state-success-label, #10b981);
811
+ }
812
+
813
+ .agy-quota-bucket-val[data-tone='warning'] {
814
+ color: var(--dsw-alias-state-warn-label, #f59e0b);
815
+ }
816
+
817
+ .agy-quota-bucket-val[data-tone='error'] {
818
+ color: var(--dsw-alias-state-error-label, #ef4444);
819
+ }
820
+
821
+ .agy-quota-querying {
822
+ display: inline-flex;
823
+ align-items: center;
824
+ gap: 6px;
825
+ color: var(--dsw-alias-label-tertiary, #71717a);
826
+ font-size: 12px;
827
+ line-height: 18px;
828
+ animation: agy-fade-in 200ms ease;
829
+ }
830
+
831
+ .agy-querying-spinner {
832
+ width: 10px;
833
+ height: 10px;
834
+ border-radius: 50%;
835
+ border: 1.5px solid var(--dsw-alias-border-l2, rgba(255, 255, 255, 0.15));
836
+ border-top-color: var(--dsw-alias-brand-primary, #4f6ef7);
837
+ animation: agy-spin 0.8s linear infinite;
838
+ display: inline-block;
839
+ flex: none;
840
+ }
841
+
842
+ .agy-progress-track {
843
+ position: relative;
844
+ height: 6px;
845
+ width: 100%;
846
+ border-radius: 999px;
847
+ background: var(--dsw-alias-bg-layer-1, rgba(0, 0, 0, 0.2));
848
+ box-shadow: inset 0 0 0 1px var(--dsw-alias-border-l2, rgba(255, 255, 255, 0.08));
849
+ overflow: hidden;
850
+ }
851
+
852
+ @keyframes agy-fill-progress {
853
+ from {
854
+ transform: scaleX(0);
855
+ opacity: 0.7;
856
+ }
857
+ to {
858
+ transform: scaleX(1);
859
+ opacity: 1;
860
+ }
861
+ }
862
+
863
+ .agy-progress-bar {
864
+ height: 100%;
865
+ border-radius: 999px;
866
+ transform-origin: left center;
867
+ animation: agy-fill-progress 650ms cubic-bezier(0.16, 1, 0.3, 1) forwards;
868
+ transition: width 400ms cubic-bezier(0.16, 1, 0.3, 1), background 400ms ease;
869
+ }
870
+
871
+ .agy-progress-bar[data-tone='normal'] {
872
+ background: linear-gradient(90deg, #f59e0b 0%, #10b981 35%, #059669 100%);
873
+ }
874
+
875
+ .agy-progress-bar[data-tone='warning'] {
876
+ background: linear-gradient(90deg, #ef4444 0%, #f97316 40%, #f59e0b 100%);
877
+ }
878
+
879
+ .agy-progress-bar[data-tone='error'] {
880
+ background: linear-gradient(90deg, #f87171 0%, #ef4444 50%, #dc2626 100%);
881
+ }
882
+
883
+ .agy-shimmer-track {
884
+ position: absolute;
885
+ inset: 0;
886
+ width: 100%;
887
+ height: 100%;
888
+ border-radius: 999px;
889
+ background: linear-gradient(
890
+ 90deg,
891
+ transparent 0%,
892
+ var(--dsw-alias-fill-tertiary, rgba(255, 255, 255, 0.06)) 25%,
893
+ var(--dsw-alias-fill-secondary, rgba(255, 255, 255, 0.14)) 50%,
894
+ var(--dsw-alias-fill-tertiary, rgba(255, 255, 255, 0.06)) 75%,
895
+ transparent 100%
896
+ );
897
+ background-size: 200% 100%;
898
+ animation: agy-shimmer-stream 1.6s ease-in-out infinite;
899
+ }
900
+
901
+ @keyframes agy-shimmer-stream {
902
+ 0% {
903
+ background-position: -200% 0;
904
+ }
905
+ 100% {
906
+ background-position: 200% 0;
907
+ }
908
+ }
909
+
910
+ @keyframes agy-fade-in {
911
+ from {
912
+ opacity: 0;
913
+ }
914
+ to {
915
+ opacity: 1;
916
+ }
917
+ }
918
+
919
+ .agy-quota-subtext {
920
+ font-size: 11px;
921
+ line-height: 16px;
922
+ color: var(--dsw-alias-label-tertiary, #71717a);
923
+ font-variant-numeric: tabular-nums;
924
+ }
925
+
926
+ /* Actions */
927
+ .agy-action-row {
928
+ display: flex;
929
+ flex-wrap: wrap;
930
+ align-items: center;
931
+ gap: 10px;
932
+ margin-top: 4px;
933
+ }
934
+
935
+ .agy-btn {
936
+ display: inline-flex;
937
+ align-items: center;
938
+ justify-content: center;
939
+ gap: 6px;
940
+ height: 32px;
941
+ padding: 0 14px;
942
+ border-radius: 999px;
943
+ font-size: 13px;
944
+ font-weight: 500;
945
+ line-height: 20px;
946
+ cursor: pointer;
947
+ border: 1px solid transparent;
948
+ background: var(--dsw-alias-bg-layer-3, rgba(255, 255, 255, 0.08));
949
+ color: var(--dsw-alias-label-primary, #e6edf3);
950
+ text-decoration: none;
951
+ transition: background-color 0.15s ease, border-color 0.15s ease, opacity 0.15s ease;
952
+ }
953
+
954
+ .agy-btn:hover:not(:disabled) {
955
+ background: var(--dsw-alias-interactive-bg-hover, rgba(255, 255, 255, 0.12));
956
+ }
957
+
958
+ .agy-btn:disabled {
959
+ cursor: not-allowed;
960
+ opacity: 0.4;
961
+ }
962
+
963
+ .agy-btn-primary {
964
+ background: var(--dsw-alias-button-primary-fill, #18181b);
965
+ color: var(--dsw-alias-label-primary-foreground, #ffffff);
966
+ border: 1px solid var(--dsw-alias-border-l2, rgba(255, 255, 255, 0.15));
967
+ }
968
+
969
+ .agy-btn-primary:hover:not(:disabled) {
970
+ background: var(--dsw-alias-button-primary-hover, #27272a);
971
+ }
972
+
973
+ .agy-btn-outline {
974
+ background: transparent;
975
+ border: 1px solid var(--dsw-alias-border-l2, rgba(255, 255, 255, 0.15));
976
+ color: var(--dsw-alias-label-primary, #e6edf3);
977
+ }
978
+
979
+ .agy-btn-outline:hover:not(:disabled) {
980
+ background: var(--dsw-alias-interactive-bg-hover, rgba(255, 255, 255, 0.08));
981
+ }
982
+
983
+ .agy-btn-ghost {
984
+ background: transparent !important;
985
+ border-color: transparent !important;
986
+ color: var(--dsw-alias-label-secondary, #a1a1aa) !important;
987
+ padding: 0 10px !important;
988
+ border-radius: 6px !important;
989
+ }
990
+
991
+ .agy-btn-ghost:hover:not(:disabled) {
992
+ background: var(--dsw-alias-fill-tertiary, rgba(255, 255, 255, 0.06)) !important;
993
+ color: var(--dsw-alias-label-primary, #ffffff) !important;
994
+ }
995
+
996
+ .agy-refresh-btn {
997
+ margin-left: auto;
998
+ }
999
+
1000
+ .agy-footer-notice {
1001
+ margin: 6px 0 0;
1002
+ color: var(--dsw-alias-label-tertiary, #71717a);
1003
+ font-size: 11px;
1004
+ line-height: 17px;
1005
+ }
1006
+
1007
+ .agy-privacy {
1008
+ font-size: 11px;
1009
+ line-height: 16px;
1010
+ color: var(--dsw-alias-label-tertiary, #6e7681);
1011
+ margin: 4px 0 0;
1012
+ }
1013
+
1014
+ .agy-alert {
1015
+ padding: 10px 12px;
1016
+ border-radius: 8px;
1017
+ font-size: 12px;
1018
+ line-height: 18px;
1019
+ margin: 0;
1020
+ display: flex;
1021
+ align-items: center;
1022
+ justify-content: space-between;
1023
+ gap: 8px;
1024
+ background: rgba(239, 68, 68, 0.1);
1025
+ border: 1px solid rgba(239, 68, 68, 0.25);
1026
+ color: #fca5a5;
1027
+ }
1028
+
1029
+ .agy-item-list {
1030
+ list-style: none;
1031
+ margin: 0;
1032
+ padding: 0;
1033
+ display: flex;
1034
+ flex-direction: column;
1035
+ gap: 8px;
1036
+ }
1037
+
1038
+ .agy-item-row {
1039
+ display: flex;
1040
+ align-items: center;
1041
+ justify-content: space-between;
1042
+ gap: 12px;
1043
+ padding: 10px 14px;
1044
+ background: var(--dsw-alias-bg-layer-1, rgba(255, 255, 255, 0.02));
1045
+ border: 1px solid var(--dsw-alias-border-l1, rgba(255, 255, 255, 0.05));
1046
+ border-radius: 8px;
1047
+ }
1048
+
1049
+ .agy-item-title {
1050
+ font-size: 13px;
1051
+ font-weight: 500;
1052
+ color: var(--dsw-alias-label-primary, #e6edf3);
1053
+ }
1054
+
1055
+ .agy-badge {
1056
+ display: inline-flex;
1057
+ align-items: center;
1058
+ gap: 5px;
1059
+ padding: 2px 8px;
1060
+ border-radius: 12px;
1061
+ font-size: 11px;
1062
+ font-weight: 500;
1063
+ line-height: 16px;
1064
+ white-space: nowrap;
1065
+ flex: none;
1066
+ }
1067
+
1068
+ .agy-badge-dot {
1069
+ width: 6px;
1070
+ height: 6px;
1071
+ border-radius: 50%;
1072
+ flex: none;
1073
+ }
1074
+
1075
+ .agy-badge-available {
1076
+ background: rgba(34, 197, 94, 0.12);
1077
+ color: #4ade80;
1078
+ border: 1px solid rgba(34, 197, 94, 0.25);
1079
+ }
1080
+
1081
+ .agy-badge-available .agy-badge-dot {
1082
+ background: #22c55e;
1083
+ }
1084
+
1085
+ .agy-badge-disabled {
1086
+ background: rgba(148, 163, 184, 0.1);
1087
+ color: #94a3b8;
1088
+ border: 1px solid rgba(148, 163, 184, 0.2);
1089
+ }
1090
+
1091
+ .agy-badge-disabled .agy-badge-dot {
1092
+ background: #64748b;
1093
+ }
1094
+
1095
+ .agy-badge-warning {
1096
+ background: rgba(234, 179, 8, 0.12);
1097
+ color: #facc15;
1098
+ border: 1px solid rgba(234, 179, 8, 0.25);
1099
+ }
1100
+
1101
+ .agy-badge-warning .agy-badge-dot {
1102
+ background: #eab308;
1103
+ }
1104
+
1105
+ .agy-badge-error {
1106
+ background: rgba(239, 68, 68, 0.12);
1107
+ color: #f87171;
1108
+ border: 1px solid rgba(239, 68, 68, 0.25);
1109
+ }
1110
+
1111
+ .agy-badge-error .agy-badge-dot {
1112
+ background: #ef4444;
1113
+ }
1114
+
1115
+ /* Switch Component */
1116
+ .agy-switch {
1117
+ position: relative;
1118
+ display: inline-flex;
1119
+ align-items: center;
1120
+ width: 36px;
1121
+ height: 20px;
1122
+ cursor: pointer;
1123
+ user-select: none;
1124
+ }
1125
+
1126
+ .agy-switch input {
1127
+ opacity: 0;
1128
+ width: 0;
1129
+ height: 0;
1130
+ position: absolute;
1131
+ }
1132
+
1133
+ .agy-switch-slider {
1134
+ position: absolute;
1135
+ top: 0;
1136
+ left: 0;
1137
+ right: 0;
1138
+ bottom: 0;
1139
+ background-color: var(--dsw-alias-bg-layer-4, rgba(255, 255, 255, 0.15));
1140
+ border-radius: 20px;
1141
+ transition: 0.2s ease;
1142
+ }
1143
+
1144
+ .agy-switch-slider::before {
1145
+ position: absolute;
1146
+ content: "";
1147
+ height: 14px;
1148
+ width: 14px;
1149
+ left: 3px;
1150
+ bottom: 3px;
1151
+ background-color: #ffffff;
1152
+ border-radius: 50%;
1153
+ transition: 0.2s ease;
1154
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
1155
+ }
1156
+
1157
+ .agy-switch input:checked + .agy-switch-slider {
1158
+ background-color: var(--dsw-alias-button-primary-fill, #2563eb);
1159
+ }
1160
+
1161
+ .agy-switch input:checked + .agy-switch-slider::before {
1162
+ transform: translateX(16px);
1163
+ }
1164
+
1165
+ .agy-switch input:disabled + .agy-switch-slider {
1166
+ opacity: 0.4;
1167
+ cursor: not-allowed;
1168
+ }
1169
+
1170
+ .agy-spin-icon {
1171
+ display: inline-flex;
1172
+ animation: agy-spin 1s linear infinite;
1173
+ }
1174
+
1175
+ @keyframes agy-spin {
1176
+ from { transform: rotate(0deg); }
1177
+ to { transform: rotate(360deg); }
1178
+ }
1179
+ `;
1180
+ const STYLE_ELEMENT_ID = "dsh-antigravity-auth-styles";
1181
+ function ensureSettingsStyles() {
1182
+ if (typeof document === "undefined") return;
1183
+ if (document.getElementById(STYLE_ELEMENT_ID)) return;
1184
+ const style = document.createElement("style");
1185
+ style.id = STYLE_ELEMENT_ID;
1186
+ style.textContent = SETTINGS_CSS;
1187
+ document.head.appendChild(style);
1188
+ }
1189
+ //#endregion
1190
+ //#region src/client/AntigravityAuthSettings.tsx
1191
+ /** Settings shell for value-safe Antigravity login status. */
1192
+ const EMPTY_SETTINGS_SNAPSHOT = {
1193
+ status: "unavailable",
1194
+ value: void 0,
1195
+ base: void 0,
1196
+ user: void 0,
1197
+ revision: void 0,
1198
+ writable: false,
1199
+ mode: "memory"
1200
+ };
1201
+ function useCapabilitySettings(scope) {
1202
+ const subscribe = (0, react.useCallback)((listener) => scope?.subscribe(listener) ?? (() => {}), [scope]);
1203
+ const getSnapshot = (0, react.useCallback)(() => scope?.getSnapshot() ?? EMPTY_SETTINGS_SNAPSHOT, [scope]);
1204
+ return (0, react.useSyncExternalStore)(subscribe, getSnapshot, () => EMPTY_SETTINGS_SNAPSHOT);
1205
+ }
1206
+ function useUnmountSignal() {
1207
+ const controller = (0, react.useRef)(new AbortController());
1208
+ (0, react.useEffect)(() => {
1209
+ const active = new AbortController();
1210
+ controller.current = active;
1211
+ return () => active.abort();
1212
+ }, []);
1213
+ return (0, react.useCallback)(() => controller.current.signal, []);
1214
+ }
1215
+ /** One navigable settings section; credentials remain Host-only and actions use typed RPC. */
1216
+ function AntigravityAuthSettings({ rpc, t, subscribe, searchScope, imageScope, videoScope }) {
1217
+ const [status, setStatus] = (0, react.useState)(null);
1218
+ const searchSettings = useCapabilitySettings(searchScope);
1219
+ const imageSettings = useCapabilitySettings(imageScope);
1220
+ const videoSettings = useCapabilitySettings(videoScope);
1221
+ const [quota, setQuota] = (0, react.useState)(null);
1222
+ const [quotaBusy, setQuotaBusy] = (0, react.useState)(false);
1223
+ const [quotaError, setQuotaError] = (0, react.useState)(null);
1224
+ const [loadState, setLoadState] = (0, react.useState)("loading");
1225
+ const [_error, setError] = (0, react.useState)(null);
1226
+ const [loginBusy, setLoginBusy] = (0, react.useState)(false);
1227
+ const [actionBusy, setActionBusy] = (0, react.useState)(false);
1228
+ const [resetTick, setResetTick] = (0, react.useState)(0);
1229
+ const statusGeneration = (0, react.useRef)(0);
1230
+ const quotaGeneration = (0, react.useRef)(0);
1231
+ const unmountSignal = useUnmountSignal();
1232
+ (0, react.useEffect)(() => {
1233
+ ensureSettingsStyles();
1234
+ }, []);
1235
+ (0, react.useEffect)(() => subscribe(() => {
1236
+ setResetTick((value) => value + 1);
1237
+ }), [subscribe]);
1238
+ const load = (0, react.useCallback)(async (signal, silent = false) => {
1239
+ const generation = ++statusGeneration.current;
1240
+ if (!silent) {
1241
+ setLoadState((prev) => prev === "ready" ? "ready" : "loading");
1242
+ setError(null);
1243
+ }
1244
+ try {
1245
+ const result = await rpc.status(signal);
1246
+ if (signal?.aborted === true || generation !== statusGeneration.current) return;
1247
+ if (!result.ok) {
1248
+ setLoadState("error");
1249
+ setError(result.error.message || t("statusFailed"));
1250
+ return;
1251
+ }
1252
+ setStatus(result.value.status);
1253
+ setLoadState("ready");
1254
+ } catch (cause) {
1255
+ if (signal?.aborted === true || generation !== statusGeneration.current) return;
1256
+ setLoadState("error");
1257
+ setError(messageOf(cause, t("statusFailed")));
1258
+ }
1259
+ }, [rpc, t]);
1260
+ const loadQuota = (0, react.useCallback)(async (force = false, signal) => {
1261
+ if (rpc.usage === void 0) return;
1262
+ const generation = ++quotaGeneration.current;
1263
+ setQuotaBusy(true);
1264
+ setQuotaError(null);
1265
+ try {
1266
+ const result = await rpc.usage(signal, force);
1267
+ if (signal?.aborted === true || generation !== quotaGeneration.current) return;
1268
+ if (!result.ok) {
1269
+ setQuotaError(result.error.message || t("quotaFailed"));
1270
+ return;
1271
+ }
1272
+ setQuota(result.value);
1273
+ } catch (cause) {
1274
+ if (signal?.aborted === true || generation !== quotaGeneration.current) return;
1275
+ setQuotaError(messageOf(cause, t("quotaFailed")));
1276
+ } finally {
1277
+ if (signal?.aborted !== true && generation === quotaGeneration.current) setQuotaBusy(false);
1278
+ }
1279
+ }, [rpc, t]);
1280
+ (0, react.useEffect)(() => {
1281
+ if (status?.login.projectAvailable !== true || rpc.usage === void 0) {
1282
+ setQuota(null);
1283
+ return;
1284
+ }
1285
+ const controller = new AbortController();
1286
+ loadQuota(false, controller.signal);
1287
+ return () => controller.abort();
1288
+ }, [
1289
+ loadQuota,
1290
+ rpc.usage,
1291
+ status?.login.projectAvailable,
1292
+ resetTick
1293
+ ]);
1294
+ (0, react.useEffect)(() => {
1295
+ const controller = new AbortController();
1296
+ load(controller.signal);
1297
+ return () => controller.abort();
1298
+ }, [load, resetTick]);
1299
+ (0, react.useEffect)(() => {
1300
+ if (status?.login.phase !== "pending") return;
1301
+ const controller = new AbortController();
1302
+ const timer = globalThis.setInterval(() => {
1303
+ load(controller.signal, true);
1304
+ }, 1e3);
1305
+ return () => {
1306
+ globalThis.clearInterval(timer);
1307
+ controller.abort();
1308
+ };
1309
+ }, [load, status?.login.phase]);
1310
+ const startLogin = (0, react.useCallback)(async () => {
1311
+ const signal = unmountSignal();
1312
+ setLoginBusy(true);
1313
+ setError(null);
1314
+ try {
1315
+ if (status?.riskAcknowledged !== true) await rpc.acknowledgeRisk(signal);
1316
+ const result = await rpc.login(signal);
1317
+ if (signal.aborted) return;
1318
+ if (!result.ok) {
1319
+ setError(result.error.message || t("loginFailed"));
1320
+ await load(signal);
1321
+ return;
1322
+ }
1323
+ setStatus((previous) => {
1324
+ if (previous === null) return previous;
1325
+ const { errorCode: _ignoredErrorCode, ...login } = previous.login;
1326
+ return {
1327
+ ...previous,
1328
+ riskAcknowledged: true,
1329
+ login: {
1330
+ ...login,
1331
+ phase: "pending",
1332
+ authorizationUrl: result.value.authorizationUrl,
1333
+ expiresAt: result.value.expiresAt
1334
+ }
1335
+ };
1336
+ });
1337
+ } catch (cause) {
1338
+ if (!signal.aborted) setError(messageOf(cause, t("loginFailed")));
1339
+ } finally {
1340
+ if (!signal.aborted) setLoginBusy(false);
1341
+ }
1342
+ }, [
1343
+ load,
1344
+ rpc,
1345
+ status?.riskAcknowledged,
1346
+ t,
1347
+ unmountSignal
1348
+ ]);
1349
+ const cancelLogin = (0, react.useCallback)(async () => {
1350
+ const signal = unmountSignal();
1351
+ setLoginBusy(true);
1352
+ setError(null);
1353
+ try {
1354
+ const result = await rpc.cancelLogin(signal);
1355
+ if (signal.aborted) return;
1356
+ if (!result.ok) {
1357
+ setError(result.error.message || t("cancelLoginFailed"));
1358
+ return;
1359
+ }
1360
+ setStatus((previous) => {
1361
+ if (previous === null) return previous;
1362
+ const { errorCode: _ignoredErrorCode, ...login } = previous.login;
1363
+ return {
1364
+ ...previous,
1365
+ login: result.value.errorCode === void 0 ? {
1366
+ ...login,
1367
+ phase: result.value.phase
1368
+ } : {
1369
+ ...login,
1370
+ phase: result.value.phase,
1371
+ errorCode: result.value.errorCode
1372
+ }
1373
+ };
1374
+ });
1375
+ } catch (cause) {
1376
+ if (!signal.aborted) setError(messageOf(cause, t("cancelLoginFailed")));
1377
+ } finally {
1378
+ if (!signal.aborted) setLoginBusy(false);
1379
+ }
1380
+ }, [
1381
+ rpc,
1382
+ t,
1383
+ unmountSignal
1384
+ ]);
1385
+ const logout = (0, react.useCallback)(async () => {
1386
+ const signal = unmountSignal();
1387
+ setActionBusy(true);
1388
+ setError(null);
1389
+ try {
1390
+ const result = await rpc.logout(signal);
1391
+ if (signal.aborted) return;
1392
+ if (!result.ok) {
1393
+ setError(result.error.message || t("logoutFailed"));
1394
+ return;
1395
+ }
1396
+ await load(signal);
1397
+ } catch (cause) {
1398
+ if (!signal.aborted) setError(messageOf(cause, t("logoutFailed")));
1399
+ } finally {
1400
+ if (!signal.aborted) setActionBusy(false);
1401
+ }
1402
+ }, [
1403
+ load,
1404
+ rpc,
1405
+ t,
1406
+ unmountSignal
1407
+ ]);
1408
+ const projectError = projectErrorText(status?.login.errorCode, t);
1409
+ const isConfigured = status?.login.configured === true;
1410
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1411
+ className: "agy-settings",
1412
+ "data-plugin": "dsh-antigravity-auth",
1413
+ "aria-labelledby": "antigravity-auth-title",
1414
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("header", {
1415
+ className: "agy-bundle-header",
1416
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1417
+ className: "agy-title-line",
1418
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h1", {
1419
+ id: "antigravity-auth-title",
1420
+ className: "agy-bundle-title",
1421
+ children: t("title")
1422
+ }), isConfigured ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1423
+ className: "agy-status-dot",
1424
+ role: "status",
1425
+ "aria-label": "Ready"
1426
+ }) : null]
1427
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1428
+ className: "agy-bundle-intro",
1429
+ children: t("intro")
1430
+ })] })
1431
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1432
+ className: "agy-cards",
1433
+ children: [
1434
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("article", {
1435
+ className: "agy-card",
1436
+ "aria-labelledby": "antigravity-auth-card-title",
1437
+ children: [
1438
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1439
+ className: "agy-card-header",
1440
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1441
+ className: "agy-card-identity",
1442
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
1443
+ id: "antigravity-auth-card-title",
1444
+ className: "agy-card-title",
1445
+ children: t("authCardTitle")
1446
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1447
+ className: "agy-card-intro",
1448
+ children: t("authCardIntro")
1449
+ })]
1450
+ })
1451
+ }),
1452
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(QuotaVisualDashboard, {
1453
+ quota,
1454
+ busy: quotaBusy,
1455
+ error: quotaError,
1456
+ onRefresh: () => {
1457
+ loadQuota(true, unmountSignal());
1458
+ },
1459
+ t
1460
+ }),
1461
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1462
+ className: "agy-action-row",
1463
+ children: [
1464
+ status?.login.phase === "pending" && typeof status.login.authorizationUrl === "string" ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
1465
+ className: "agy-btn agy-btn-primary",
1466
+ href: status.login.authorizationUrl,
1467
+ target: "_blank",
1468
+ rel: "noreferrer",
1469
+ children: t("openAuthorization")
1470
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1471
+ className: "agy-btn agy-btn-outline",
1472
+ type: "button",
1473
+ disabled: loginBusy,
1474
+ onClick: () => {
1475
+ cancelLogin();
1476
+ },
1477
+ children: t("cancelLogin")
1478
+ })] }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1479
+ className: "agy-btn agy-btn-primary",
1480
+ type: "button",
1481
+ disabled: status === null || loginBusy,
1482
+ onClick: () => {
1483
+ startLogin();
1484
+ },
1485
+ children: loginBusy ? t("startingLogin") : isConfigured ? t("relogin") : t("login")
1486
+ }),
1487
+ status?.credential?.configured ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1488
+ className: "agy-btn agy-btn-outline",
1489
+ type: "button",
1490
+ disabled: actionBusy,
1491
+ onClick: () => {
1492
+ logout();
1493
+ },
1494
+ children: t("logout")
1495
+ }) : null,
1496
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1497
+ className: "agy-btn agy-btn-ghost agy-refresh-btn",
1498
+ type: "button",
1499
+ disabled: loadState === "loading" || quotaBusy,
1500
+ onClick: () => {
1501
+ const minDelay = new Promise((resolve) => setTimeout(resolve, 500));
1502
+ Promise.all([
1503
+ load(unmountSignal()),
1504
+ loadQuota(true, unmountSignal()),
1505
+ minDelay
1506
+ ]);
1507
+ },
1508
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1509
+ className: quotaBusy || loadState === "loading" ? "agy-spin-icon" : "",
1510
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
1511
+ width: "15",
1512
+ height: "15",
1513
+ viewBox: "0 0 24 24",
1514
+ fill: "none",
1515
+ stroke: "currentColor",
1516
+ strokeWidth: "2",
1517
+ children: [
1518
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" }),
1519
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M3 3v5h5" }),
1520
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16" }),
1521
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M16 21h5v-5" })
1522
+ ]
1523
+ })
1524
+ }), quotaBusy || loadState === "loading" ? t("queryingQuota") : t("refreshStatus")]
1525
+ })
1526
+ ]
1527
+ }),
1528
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1529
+ className: "agy-footer-notice",
1530
+ children: t("quotaFooterNotice")
1531
+ }),
1532
+ status?.login.phase === "expired" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1533
+ className: "agy-alert",
1534
+ role: "alert",
1535
+ children: t("loginExpired")
1536
+ }) : null,
1537
+ status?.login.phase === "port-conflict" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1538
+ className: "agy-alert",
1539
+ role: "alert",
1540
+ children: t("loginPortConflict")
1541
+ }) : null,
1542
+ status?.login.phase === "failed" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1543
+ className: "agy-alert",
1544
+ role: "alert",
1545
+ children: t("loginFailed")
1546
+ }) : null,
1547
+ projectError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1548
+ className: "agy-alert",
1549
+ role: "alert",
1550
+ children: projectError
1551
+ }),
1552
+ status?.login.phase === "pending" && status.login.expiresAt !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
1553
+ className: "agy-card-subtext",
1554
+ children: [
1555
+ t("expiresAt"),
1556
+ ": ",
1557
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("time", {
1558
+ dateTime: status.login.expiresAt,
1559
+ children: status.login.expiresAt
1560
+ })
1561
+ ]
1562
+ }) : null,
1563
+ status?.revoke === void 0 || status.revoke.state === "idle" ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1564
+ className: "agy-card-subtext",
1565
+ role: "status",
1566
+ children: revokeStatusText(status.revoke.state, t)
1567
+ })
1568
+ ]
1569
+ }),
1570
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("article", {
1571
+ className: "agy-card",
1572
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1573
+ className: "agy-card-header",
1574
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1575
+ className: "agy-card-identity",
1576
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
1577
+ className: "agy-card-title",
1578
+ children: t("search")
1579
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1580
+ className: "agy-card-intro",
1581
+ children: t("searchCardIntro")
1582
+ })]
1583
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1584
+ className: "agy-card-action",
1585
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Switch, {
1586
+ checked: searchSettings.value?.enabled ?? false,
1587
+ disabled: !capabilityAvailable(status, "search") || searchSettings.status !== "ready" || !searchSettings.writable,
1588
+ onChange: (next) => {
1589
+ searchScope?.set("enabled", next);
1590
+ }
1591
+ })
1592
+ })]
1593
+ })
1594
+ }),
1595
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("article", {
1596
+ className: "agy-card",
1597
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1598
+ className: "agy-card-header",
1599
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1600
+ className: "agy-card-identity",
1601
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
1602
+ className: "agy-card-title",
1603
+ children: t("image")
1604
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1605
+ className: "agy-card-intro",
1606
+ children: t("imageCardIntro")
1607
+ })]
1608
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1609
+ className: "agy-card-action",
1610
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Switch, {
1611
+ checked: imageSettings.value?.enabled ?? false,
1612
+ disabled: !capabilityAvailable(status, "image") || imageSettings.status !== "ready" || !imageSettings.writable,
1613
+ onChange: (next) => {
1614
+ imageScope?.set("enabled", next);
1615
+ }
1616
+ })
1617
+ })]
1618
+ })
1619
+ }),
1620
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("article", {
1621
+ className: "agy-card",
1622
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1623
+ className: "agy-card-header",
1624
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1625
+ className: "agy-card-identity",
1626
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
1627
+ className: "agy-card-title",
1628
+ children: t("video")
1629
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1630
+ className: "agy-card-intro",
1631
+ children: t("videoCardIntro")
1632
+ })]
1633
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1634
+ className: "agy-card-action",
1635
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Switch, {
1636
+ checked: videoSettings.value?.enabled ?? false,
1637
+ disabled: !capabilityAvailable(status, "video") || videoSettings.status !== "ready" || !videoSettings.writable,
1638
+ onChange: (next) => {
1639
+ videoScope?.set("enabled", next);
1640
+ }
1641
+ })
1642
+ })]
1643
+ })
1644
+ })
1645
+ ]
1646
+ })]
1647
+ });
1648
+ }
1649
+ function capabilityAvailable(status, id) {
1650
+ return status?.login.projectAvailable === true && status.capabilities.some((capability) => capability.id === id && capability.state === "available");
1651
+ }
1652
+ function Switch({ checked, disabled, onChange }) {
1653
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
1654
+ className: "agy-switch",
1655
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1656
+ type: "checkbox",
1657
+ checked,
1658
+ disabled,
1659
+ onChange: (e) => {
1660
+ onChange(e.target.checked);
1661
+ }
1662
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "agy-switch-slider" })]
1663
+ });
1664
+ }
1665
+ function formatRefreshTime(resetTime, now = Date.now()) {
1666
+ const target = new Date(resetTime).getTime();
1667
+ if (Number.isNaN(target)) return resetTime;
1668
+ const diffMs = target - now;
1669
+ if (diffMs <= 0) return "0m";
1670
+ const diffMinutes = Math.floor(diffMs / 6e4);
1671
+ const hours = Math.floor(diffMinutes / 60);
1672
+ const remMinutes = diffMinutes % 60;
1673
+ if (hours >= 24) return `${hours}h ${remMinutes}m`;
1674
+ if (hours > 0) return `${hours}h ${remMinutes}m`;
1675
+ return `${remMinutes}m`;
1676
+ }
1677
+ function quotaTone(fraction) {
1678
+ if (fraction < .3) return "error";
1679
+ if (fraction <= .6) return "warning";
1680
+ return "normal";
1681
+ }
1682
+ function QuotaVisualDashboard({ quota, busy, error, t }) {
1683
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1684
+ className: "agy-quota-section",
1685
+ children: [quota?.state === "available" && quota.groups !== void 0 && quota.groups.length > 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1686
+ className: "agy-quota-groups",
1687
+ children: quota.groups.map((group) => {
1688
+ const groupTitle = group.group === "gemini" ? t("geminiGroupTitle") : t("claudeGptGroupTitle");
1689
+ const groupDesc = group.group === "gemini" ? t("geminiGroupDesc") : t("claudeGptGroupDesc");
1690
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1691
+ className: "agy-quota-group",
1692
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1693
+ className: "agy-quota-group-header",
1694
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1695
+ className: "agy-quota-group-title",
1696
+ children: groupTitle
1697
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1698
+ className: "agy-quota-group-desc",
1699
+ children: groupDesc
1700
+ })]
1701
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1702
+ className: "agy-quota-buckets",
1703
+ children: group.windows.map((window) => {
1704
+ const windowName = window.window === "5h" ? t("window5hTitle") : t("windowWeeklyTitle");
1705
+ const pctFormatted = (window.remainingFraction * 100).toFixed(2) + "%";
1706
+ const pctRounded = Math.round(window.remainingFraction * 100);
1707
+ const refreshStr = formatRefreshTime(window.resetTime);
1708
+ const subtext = `${pctRounded}% ${t("remaining")} · ${t("refreshesIn")} ${refreshStr}`;
1709
+ const widthPct = Math.max(0, Math.min(100, window.remainingFraction * 100));
1710
+ const tone = quotaTone(window.remainingFraction);
1711
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1712
+ className: "agy-quota-bucket",
1713
+ children: [
1714
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1715
+ className: "agy-quota-bucket-head",
1716
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1717
+ className: "agy-quota-bucket-name",
1718
+ children: windowName
1719
+ }), busy ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1720
+ className: "agy-quota-querying",
1721
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1722
+ className: "agy-querying-spinner",
1723
+ "aria-hidden": "true"
1724
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("queryingQuota") })]
1725
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1726
+ className: "agy-quota-bucket-val",
1727
+ "data-tone": tone,
1728
+ children: pctFormatted
1729
+ })]
1730
+ }),
1731
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1732
+ className: "agy-progress-track",
1733
+ children: busy ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1734
+ className: "agy-shimmer-track",
1735
+ "aria-hidden": "true"
1736
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1737
+ className: "agy-progress-bar",
1738
+ "data-tone": tone,
1739
+ style: { width: `${widthPct}%` }
1740
+ })
1741
+ }),
1742
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1743
+ className: "agy-quota-subtext",
1744
+ children: subtext
1745
+ })
1746
+ ]
1747
+ }, window.window);
1748
+ })
1749
+ })]
1750
+ }, group.group);
1751
+ })
1752
+ }) : null, error === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1753
+ className: "agy-alert",
1754
+ role: "alert",
1755
+ children: error
1756
+ })]
1757
+ });
1758
+ }
1759
+ const PROJECT_ERROR_KEYS = {
1760
+ "project-unavailable": "projectUnavailable",
1761
+ "project-authentication-failed": "projectAuthenticationFailed",
1762
+ "project-forbidden": "projectForbidden",
1763
+ "project-rate-limited": "projectRateLimited",
1764
+ "project-offline": "projectOffline",
1765
+ "project-malformed": "projectMalformed",
1766
+ "project-protocol-drift": "projectProtocolDrift"
1767
+ };
1768
+ const REVOKE_STATE_KEYS = {
1769
+ idle: "credentialLoggedOut",
1770
+ "logged-out": "credentialLoggedOut",
1771
+ pending: "revokePending",
1772
+ revoked: "revokeSuccess",
1773
+ failed: "revokeFailed",
1774
+ superseded: "revokeSuperseded",
1775
+ "confirmation-required": "revokeConfirmationRequired"
1776
+ };
1777
+ function projectErrorText(errorCode, t) {
1778
+ if (errorCode === void 0) return void 0;
1779
+ const key = PROJECT_ERROR_KEYS[errorCode];
1780
+ return key === void 0 ? void 0 : t(key);
1781
+ }
1782
+ function revokeStatusText(state, t) {
1783
+ return t(REVOKE_STATE_KEYS[state]);
1784
+ }
1785
+ function messageOf(_error, fallback) {
1786
+ return fallback;
1787
+ }
1788
+ //#endregion
1789
+ //#region src/client/locales.ts
1790
+ /** English and Chinese copy for the private bootstrap settings section. */
1791
+ const en = {
1792
+ nav: "Antigravity",
1793
+ title: "Antigravity Auth",
1794
+ intro: "使用 Antigravity Auth 登录接入 Antigravity 的 LLM、文生图、Websearch、视频理解能力。",
1795
+ riskTitle: "Before you continue",
1796
+ risk: "Google does not support third-party Antigravity login tools. Your account may be suspended or terminated.",
1797
+ terms: "Review the Google FAQ before continuing.",
1798
+ additionalTerms: "Review the Additional Terms.",
1799
+ singleAccount: "Single-account only. This bundle does not provide an account pool.",
1800
+ riskAcknowledgement: "I understand the unofficial third-party login risk and want to continue.",
1801
+ acknowledgeBusy: "Saving acknowledgement…",
1802
+ acknowledgeFailed: "The risk acknowledgement could not be recorded.",
1803
+ login: "Start Antigravity login",
1804
+ startingLogin: "Starting login…",
1805
+ cancelLogin: "Cancel login",
1806
+ cancelLoginFailed: "The login cancellation failed.",
1807
+ openAuthorization: "Open the Google authorization page",
1808
+ loginReady: "Login is ready to start.",
1809
+ loginRequiresAck: "Acknowledge the risk before login can begin.",
1810
+ loginPending: "Login is waiting for the browser authorization callback.",
1811
+ loginSuccess: "Login completed successfully.",
1812
+ loginCancelled: "Login was cancelled.",
1813
+ loginExpired: "The login expired. Start again to receive a new authorization link.",
1814
+ loginPortConflict: "The fixed callback port is already in use. Close the conflicting process and try again.",
1815
+ loginFailed: "The login could not be completed safely.",
1816
+ credentialLoggedIn: "The Antigravity account is ready.",
1817
+ credentialRefreshing: "Refreshing the Antigravity access token…",
1818
+ credentialRefreshFailed: "The access token could not be refreshed. No fallback account or endpoint was used.",
1819
+ credentialReloginRequired: "The Antigravity grant requires login again.",
1820
+ credentialLoggedOut: "The Antigravity account is logged out on this Host.",
1821
+ logout: "Log out locally",
1822
+ logoutFailed: "The local logout could not be completed.",
1823
+ revoke: "Revoke Google grant",
1824
+ revokeConfirm: "Revoke this Google grant? This is separate from local logout.",
1825
+ revokePending: "Revoking the Google grant…",
1826
+ revokeSuccess: "The Google grant was revoked and local state was cleared.",
1827
+ revokeFailed: "The Google grant could not be revoked. Local state was retained.",
1828
+ revokeSuperseded: "The account changed before revocation completed. No newer account was cleared.",
1829
+ revokeConfirmationRequired: "Confirm revocation before contacting Google.",
1830
+ account: "Account",
1831
+ projectAvailable: "Read-only project validation is available.",
1832
+ projectUnavailable: "No usable project is available.",
1833
+ projectAuthenticationFailed: "Project discovery requires a fresh authenticated grant.",
1834
+ projectForbidden: "This account is not allowed to use the discovered project.",
1835
+ projectRateLimited: "Project discovery was rate-limited. No fallback project was used.",
1836
+ projectOffline: "Project discovery is temporarily offline. No fallback project was used.",
1837
+ projectMalformed: "Project discovery returned a malformed response.",
1838
+ projectProtocolDrift: "Project discovery no longer matches the audited protocol.",
1839
+ expiresAt: "Authorization link expires",
1840
+ statusLoading: "Loading capability status…",
1841
+ statusFailed: "Capability status is unavailable.",
1842
+ retry: "Retry",
1843
+ capabilityTitle: "Capability gates",
1844
+ authLlm: "Auth / LLM",
1845
+ search: "Web Search",
1846
+ image: "Image",
1847
+ video: "Video",
1848
+ available: "Available",
1849
+ disabled: "Disabled",
1850
+ pocPending: "POC pending",
1851
+ protocolDrift: "Protocol drift",
1852
+ gateNotRun: "This capability gate has not been run.",
1853
+ gateUnauthenticated: "This gate requires a fresh authenticated grant.",
1854
+ gateRateLimited: "This gate was rate-limited; no fallback was attempted.",
1855
+ gateCancelled: "This gate run was cancelled.",
1856
+ gate0Failed: "The fixed Wire Identity was rejected by Gate 0.",
1857
+ gateFailed: "This capability gate failed safely.",
1858
+ unsupportedVideo: "The private endpoint rejected native video input.",
1859
+ acknowledged: "Risk acknowledged for this Host session.",
1860
+ settingsTitle: "Capability settings",
1861
+ toggleSearch: "Enable grounded Web Search",
1862
+ toggleImage: "Enable image generation and editing",
1863
+ toggleVideo: "Enable video understanding POC",
1864
+ settingsUnavailable: "Unavailable until login, project validation, the live gate, and a writable Host settings scope are ready.",
1865
+ modelsTitle: "Models",
1866
+ modelsSnapshot: "Showing the pinned audited snapshot; live availability has not been refreshed.",
1867
+ modelsLiveAvailable: "Live account availability was intersected with the pinned snapshot.",
1868
+ modelsRefreshFailed: "Live availability refresh failed safely; the pinned snapshot remains advisory.",
1869
+ modelsProtocolDrift: "Live model discovery no longer matches the audited protocol.",
1870
+ modelsSnapshotEntry: "Snapshot",
1871
+ modelsAvailableEntry: "Live available",
1872
+ modelsUnavailableEntry: "Unavailable to this account",
1873
+ modelsFailed: "Model availability could not be loaded safely.",
1874
+ modelsLoading: "Refreshing models…",
1875
+ modelsRefresh: "Refresh model availability",
1876
+ quotaTitle: "Usage and quota",
1877
+ quotaAvailable: "Quota is available from the current account.",
1878
+ quotaUnknown: "Quota has not been queried.",
1879
+ quotaUnauthenticated: "Log in to query quota.",
1880
+ quotaForbidden: "This account is not allowed to query quota.",
1881
+ quotaRateLimited: "Quota is rate-limited; try again later.",
1882
+ quotaOffline: "Quota is temporarily unavailable.",
1883
+ quotaProtocolDrift: "Quota protocol drift was detected; raw provider data was discarded.",
1884
+ quotaFailed: "Quota could not be loaded safely.",
1885
+ quotaLoading: "Refreshing quota…",
1886
+ quotaRefresh: "Refresh quota",
1887
+ quotaFiveHour: "Five-hour window",
1888
+ quotaWeekly: "Weekly window",
1889
+ searchCardIntro: "Google Antigravity Search Provider used by the stock web_search tool.",
1890
+ imageCardIntro: "Durable image generation tools for image-capable models (gemini-3.1-flash-image).",
1891
+ videoCardIntro: "Workspace MP4 video understanding tools for multimodal models (gemini-3.7-flash).",
1892
+ privacyNotice: "共享Antigravity 登录态与Antigravity模型路由。",
1893
+ relogin: "Log in again with Google",
1894
+ authCardTitle: "Login",
1895
+ authCardIntro: "Share Antigravity session and Antigravity model routing.",
1896
+ queryingQuota: "Querying quota…",
1897
+ geminiGroupTitle: "GEMINI MODELS",
1898
+ geminiGroupDesc: "Models within this group: Gemini Flash, Gemini Pro",
1899
+ claudeGptGroupTitle: "CLAUDE AND GPT MODELS",
1900
+ claudeGptGroupDesc: "Models within this group: Claude Opus, Claude Sonnet, GPT-OSS",
1901
+ window5hTitle: "Five Hour Limit Remaining",
1902
+ windowWeeklyTitle: "Weekly Limit Remaining",
1903
+ quotaFooterNotice: "No token value is ever sent to the Web client, settings, logs, events, or tool metadata.",
1904
+ refreshStatus: "Refresh status",
1905
+ remaining: "remaining",
1906
+ refreshesIn: "Refreshes in"
1907
+ };
1908
+ const zh = {
1909
+ nav: "Antigravity",
1910
+ title: "Antigravity Auth",
1911
+ intro: "使用 Antigravity Auth 登录接入 Antigravity 的 LLM、文生图、Websearch、视频理解能力。",
1912
+ riskTitle: "继续前请确认",
1913
+ risk: "Google 不支持第三方 Antigravity 登录工具,账号可能被暂停或终止。",
1914
+ terms: "继续前请阅读 Google FAQ。",
1915
+ additionalTerms: "请阅读 Additional Terms。",
1916
+ singleAccount: "仅支持单账号。本能力包不提供账号池。",
1917
+ riskAcknowledgement: "我已了解非官方第三方登录风险,并确认继续。",
1918
+ acknowledgeBusy: "正在保存确认…",
1919
+ acknowledgeFailed: "无法记录风险确认。",
1920
+ login: "开始 Antigravity 登录",
1921
+ startingLogin: "正在启动登录…",
1922
+ cancelLogin: "取消登录",
1923
+ cancelLoginFailed: "取消登录失败。",
1924
+ openAuthorization: "打开 Google 授权页面",
1925
+ loginReady: "可以开始登录。",
1926
+ loginRequiresAck: "请先确认风险,才能开始登录。",
1927
+ loginPending: "等待浏览器授权回调。",
1928
+ loginSuccess: "登录已成功完成。",
1929
+ loginCancelled: "登录已取消。",
1930
+ loginExpired: "登录已过期,请重新开始以获取新的授权链接。",
1931
+ loginPortConflict: "固定回调端口已被占用,请关闭冲突进程后重试。",
1932
+ loginFailed: "登录无法安全完成。",
1933
+ credentialLoggedIn: "Antigravity 账号已就绪。",
1934
+ credentialRefreshing: "正在刷新 Antigravity access token…",
1935
+ credentialRefreshFailed: "无法刷新 access token,未使用备用账号或 endpoint。",
1936
+ credentialReloginRequired: "该 Antigravity grant 需要重新登录。",
1937
+ credentialLoggedOut: "该 Host 上的 Antigravity 账号已登出。",
1938
+ logout: "在本地登出",
1939
+ logoutFailed: "本地登出未完成。",
1940
+ revoke: "撤销 Google grant",
1941
+ revokeConfirm: "确定撤销此 Google grant 吗?这与本地登出是分开的操作。",
1942
+ revokePending: "正在撤销 Google grant…",
1943
+ revokeSuccess: "Google grant 已撤销,本地状态已清除。",
1944
+ revokeFailed: "无法撤销 Google grant,本地状态已保留。",
1945
+ revokeSuperseded: "账号在撤销完成前发生变化,未清除新账号。",
1946
+ revokeConfirmationRequired: "请先确认撤销,再联系 Google。",
1947
+ account: "账号",
1948
+ projectAvailable: "只读 project 验证可用。",
1949
+ projectUnavailable: "没有可用的 project。",
1950
+ projectAuthenticationFailed: "project discovery 需要重新认证的 grant。",
1951
+ projectForbidden: "该账号没有权限使用已发现的 project。",
1952
+ projectRateLimited: "project discovery 被限流,未使用备用 project。",
1953
+ projectOffline: "project discovery 暂时离线,未使用备用 project。",
1954
+ projectMalformed: "project discovery 返回了格式错误的响应。",
1955
+ projectProtocolDrift: "project discovery 已偏离经过审计的协议。",
1956
+ expiresAt: "授权链接过期时间",
1957
+ statusLoading: "正在加载能力状态…",
1958
+ statusFailed: "能力状态不可用。",
1959
+ retry: "重试",
1960
+ capabilityTitle: "能力门禁",
1961
+ authLlm: "认证 / LLM",
1962
+ search: "网页搜索",
1963
+ image: "图片",
1964
+ video: "视频",
1965
+ available: "可用",
1966
+ disabled: "已禁用",
1967
+ pocPending: "POC 待验证",
1968
+ protocolDrift: "协议漂移",
1969
+ gateNotRun: "该能力门禁尚未运行。",
1970
+ gateUnauthenticated: "该门禁需要新的有效认证 grant。",
1971
+ gateRateLimited: "该门禁运行被限流,未尝试任何 fallback。",
1972
+ gateCancelled: "该门禁运行已取消。",
1973
+ gate0Failed: "固定 Wire Identity 未通过 Gate 0。",
1974
+ gateFailed: "该能力门禁已安全失败。",
1975
+ unsupportedVideo: "私有 endpoint 拒绝了原生视频输入。",
1976
+ acknowledged: "本 Host 会话已确认风险。",
1977
+ settingsTitle: "能力设置",
1978
+ toggleSearch: "启用有依据的网页搜索",
1979
+ toggleImage: "启用图片生成与编辑",
1980
+ toggleVideo: "启用视频理解 POC",
1981
+ settingsUnavailable: "登录、project 验证、对应 live gate 和可写 Host 设置范围就绪后才能使用。",
1982
+ modelsTitle: "模型",
1983
+ modelsSnapshot: "当前显示已审计的固定快照;尚未刷新真实账号可用性。",
1984
+ modelsLiveAvailable: "已将真实账号可用性与固定快照取交集。",
1985
+ modelsRefreshFailed: "真实可用性刷新已安全失败;固定快照仍仅供参考。",
1986
+ modelsProtocolDrift: "真实模型发现已不再符合已审计协议。",
1987
+ modelsSnapshotEntry: "固定快照",
1988
+ modelsAvailableEntry: "真实可用",
1989
+ modelsUnavailableEntry: "当前账号不可用",
1990
+ modelsFailed: "无法安全加载模型可用性。",
1991
+ modelsLoading: "正在刷新模型…",
1992
+ modelsRefresh: "刷新模型可用性",
1993
+ quotaTitle: "用量与配额",
1994
+ quotaAvailable: "当前账号的配额可用。",
1995
+ quotaUnknown: "尚未查询配额。",
1996
+ quotaUnauthenticated: "登录后才能查询配额。",
1997
+ quotaForbidden: "该账号无权查询配额。",
1998
+ quotaRateLimited: "配额查询被限流,请稍后重试。",
1999
+ quotaOffline: "配额暂时不可用。",
2000
+ quotaProtocolDrift: "检测到配额协议漂移,已丢弃原始 provider 数据。",
2001
+ quotaFailed: "无法安全加载配额。",
2002
+ quotaLoading: "正在刷新配额…",
2003
+ quotaRefresh: "刷新配额",
2004
+ quotaFiveHour: "五小时窗口",
2005
+ quotaWeekly: "每周窗口",
2006
+ searchCardIntro: "供内置 web_search 工具使用的 Google Antigravity 全局搜索能力。",
2007
+ imageCardIntro: "面向支持图片模型的持久化 generate_image 工具(gemini-3.1-flash-image)。",
2008
+ videoCardIntro: "面向本地 MP4 视频文件的多模态 analyze_video 理解工具。",
2009
+ privacyNotice: "共享Antigravity 登录态与Antigravity模型路由。",
2010
+ relogin: "重新登录 Google",
2011
+ authCardTitle: "登录",
2012
+ authCardIntro: "共享 Antigravity 登录态与 Antigravity 模型路由。",
2013
+ queryingQuota: "正在查询额度…",
2014
+ geminiGroupTitle: "GEMINI 模型",
2015
+ geminiGroupDesc: "包含模型:Gemini Flash, Gemini Pro",
2016
+ claudeGptGroupTitle: "CLAUDE 与 GPT 模型",
2017
+ claudeGptGroupDesc: "包含模型:Claude Opus, Claude Sonnet, GPT-OSS",
2018
+ window5hTitle: "5 小时限额剩余",
2019
+ windowWeeklyTitle: "周限额剩余",
2020
+ quotaFooterNotice: "任何 Token 值都不会发送到 Web 客户端、设置、日志、事件或工具元数据。",
2021
+ refreshStatus: "刷新状态",
2022
+ remaining: "剩余",
2023
+ refreshesIn: "后刷新"
2024
+ };
2025
+ //#endregion
2026
+ //#region src/client/index.ts
2027
+ const NS = "settings.antigravityAuth";
2028
+ /** Client services required by the settings section and its loopback RPC. */
2029
+ const inject = [
2030
+ "slots",
2031
+ "locale",
2032
+ "connection",
2033
+ "settingsScope"
2034
+ ];
2035
+ /** Register one disposable settings section and no capability controls. */
2036
+ function apply(ctx) {
2037
+ ctx.effect(() => ctx.locale.register(NS, {
2038
+ zh,
2039
+ en
2040
+ }), "antigravity-auth: copy dictionaries");
2041
+ const rpc = createAntigravityAuthRpcClient(ctx.get("connection").rpc);
2042
+ const t = ctx.locale.bind(NS);
2043
+ const settingsScope = ctx.settingsScope;
2044
+ const searchScope = settingsScope?.bind({
2045
+ namespace: "antigravity-search",
2046
+ decode: decodeSearchSettings
2047
+ });
2048
+ const imageScope = settingsScope?.bind({
2049
+ namespace: "antigravity-image",
2050
+ decode: decodeImageSettings
2051
+ });
2052
+ const videoScope = settingsScope?.bind({
2053
+ namespace: "antigravity-video",
2054
+ decode: decodeVideoSettings
2055
+ });
2056
+ const listeners = /* @__PURE__ */ new Set();
2057
+ const subscribe = (listener) => {
2058
+ listeners.add(listener);
2059
+ return () => {
2060
+ listeners.delete(listener);
2061
+ };
2062
+ };
2063
+ const reset = () => {
2064
+ for (const listener of listeners) listener();
2065
+ };
2066
+ ctx.effect(() => ctx.on("connection/reset", reset), "antigravity-auth: connection invalidation");
2067
+ ctx.slots.inject("settings.section", () => ctx.slots.register({
2068
+ name: "settings.section",
2069
+ id: "antigravity-auth",
2070
+ order: 20,
2071
+ label: () => t("nav"),
2072
+ inject: () => ({
2073
+ rpc,
2074
+ t,
2075
+ subscribe,
2076
+ searchScope,
2077
+ imageScope,
2078
+ videoScope
2079
+ })
2080
+ }, AntigravityAuthSettings));
2081
+ }
2082
+ function decodeSearchSettings(value) {
2083
+ if (!isRecord(value) || typeof value.enabled !== "boolean" || typeof value.model !== "string" || value.model.length === 0 || !positiveInteger(value.maxResults) || value.maxResults > 50) return void 0;
2084
+ return {
2085
+ enabled: value.enabled,
2086
+ model: value.model,
2087
+ maxResults: value.maxResults
2088
+ };
2089
+ }
2090
+ function decodeImageSettings(value) {
2091
+ if (!isRecord(value) || typeof value.enabled !== "boolean" || typeof value.model !== "string" || value.model.length === 0 || !positiveInteger(value.n) || value.n > 4) return void 0;
2092
+ return {
2093
+ enabled: value.enabled,
2094
+ model: value.model,
2095
+ n: value.n
2096
+ };
2097
+ }
2098
+ function decodeVideoSettings(value) {
2099
+ if (!isRecord(value) || typeof value.enabled !== "boolean" || typeof value.model !== "string" || value.model.length === 0) return void 0;
2100
+ if (value.maxBytes !== void 0 && (!positiveInteger(value.maxBytes) || value.maxBytes > 134217728)) return void 0;
2101
+ return {
2102
+ enabled: value.enabled,
2103
+ model: value.model,
2104
+ ...typeof value.maxBytes === "number" ? { maxBytes: value.maxBytes } : {}
2105
+ };
2106
+ }
2107
+ function positiveInteger(value) {
2108
+ return Number.isSafeInteger(value) && value > 0;
2109
+ }
2110
+ function isRecord(value) {
2111
+ return typeof value === "object" && value !== null && !Array.isArray(value);
2112
+ }
2113
+ //#endregion
2114
+ exports.AntigravityAuthSettings = AntigravityAuthSettings;
2115
+ exports.apply = apply;
2116
+ exports.en = en;
2117
+ exports.inject = inject;
2118
+ exports.zh = zh;
2119
+ return module.exports;
2120
+ }
2121
+ });
2122
+
2123
+ //# sourceMappingURL=client.cjs.map