dsh-codex-community 0.0.1

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 (53) hide show
  1. package/CHANGELOG.md +90 -0
  2. package/CONTRIBUTING.en.md +54 -0
  3. package/CONTRIBUTING.md +54 -0
  4. package/LICENSE +201 -0
  5. package/NOTICE +12 -0
  6. package/README.en.md +98 -0
  7. package/README.md +98 -0
  8. package/SECURITY.md +41 -0
  9. package/SUPPORT.md +17 -0
  10. package/THIRD_PARTY_NOTICES.md +25 -0
  11. package/codex-community.patch.yml +13 -0
  12. package/dist/client/index.js +1037 -0
  13. package/dist/host/index.mjs +98 -0
  14. package/dist/internal/authorization-bridge.mjs +662 -0
  15. package/dist/internal/authorization-commit-tracker.mjs +49 -0
  16. package/dist/internal/codex-authorization.mjs +202 -0
  17. package/dist/internal/codex-credential-store.mjs +164 -0
  18. package/dist/internal/codex-identifiers.mjs +4 -0
  19. package/dist/internal/codex-pi-provider.mjs +137 -0
  20. package/dist/internal/codex-provider-runtime.mjs +256 -0
  21. package/dist/internal/codex-route-adapter.mjs +133 -0
  22. package/dist/internal/codex-session-resources.mjs +64 -0
  23. package/dist/internal/failure-normalizer.mjs +456 -0
  24. package/dist/internal/image-policy.mjs +45 -0
  25. package/dist/internal/quota-observer.mjs +142 -0
  26. package/dist/internal/reliability.mjs +12 -0
  27. package/dist/internal/remote-image-input.mjs +801 -0
  28. package/dist/internal/session-preference-command.mjs +52 -0
  29. package/dist/internal/session-preferences.mjs +93 -0
  30. package/dist/internal/stream-resilience.mjs +273 -0
  31. package/docs/README.en.md +15 -0
  32. package/docs/README.md +15 -0
  33. package/docs/architecture.en.md +106 -0
  34. package/docs/architecture.md +106 -0
  35. package/docs/compatibility.en.md +53 -0
  36. package/docs/compatibility.md +53 -0
  37. package/docs/configuration.en.md +61 -0
  38. package/docs/configuration.md +61 -0
  39. package/docs/contribution-sources.en.md +35 -0
  40. package/docs/contribution-sources.md +35 -0
  41. package/docs/github-about.md +15 -0
  42. package/docs/releases/v0.0.1.acceptance.json +160 -0
  43. package/docs/releases/v0.0.1.md +174 -0
  44. package/docs/releasing.en.md +290 -0
  45. package/docs/releasing.md +290 -0
  46. package/docs/testing.en.md +85 -0
  47. package/docs/testing.md +85 -0
  48. package/docs/troubleshooting.en.md +47 -0
  49. package/docs/troubleshooting.md +47 -0
  50. package/package.json +144 -0
  51. package/types/client.d.ts +160 -0
  52. package/types/index.d.ts +22 -0
  53. package/types/reliability.d.ts +78 -0
@@ -0,0 +1,1037 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "dsh-codex-community",
3
+ factory: (require) => {
4
+ const module = { exports: {} }
5
+ const exports = module.exports
6
+ const React = require("react")
7
+ const h = React.createElement
8
+
9
+ const CHANNEL = "/dsh-codex"
10
+ const NS = "settings.codex"
11
+ const CODEX_PROVIDER = "dsh-codex"
12
+ const CODEX_SETTINGS_NS = "dsh-codex"
13
+ const CODEX_SETTINGS_PATH = Object.freeze([])
14
+ const STYLE_ID = "dsh-codex-community/authorization-settings.css"
15
+ const STYLE_REF_KEY = Symbol.for(`${STYLE_ID}/references`)
16
+
17
+ const zh = {
18
+ nav: "OpenAI Codex",
19
+ title: "OpenAI Codex",
20
+ description: "使用 ChatGPT 订阅在 dsh 中调用 Codex 模型,无需 API Key。",
21
+ loading: "正在读取登录状态…",
22
+ signedIn: "已登录",
23
+ signedOut: "尚未登录",
24
+ credentialInvalid: "凭据无效",
25
+ credentialInvalidHelp: "已保存的 Codex 凭据无法使用;请重新登录,或退出后再登录。",
26
+ inProgress: "登录正在进行",
27
+ unavailable: "当前环境没有可用的 Codex 登录流程。",
28
+ writableNo: "当前凭据存储不可写。",
29
+ refresh: "刷新",
30
+ signIn: "登录",
31
+ signInWithChatGPT: "使用 ChatGPT 登录",
32
+ signInAgain: "重新登录",
33
+ cancelling: "正在取消登录",
34
+ cancel: "取消登录",
35
+ signOut: "退出登录",
36
+ signingOut: "正在退出登录",
37
+ confirmSignOut: "确认退出 Codex 登录并删除已保存的授权凭据吗?",
38
+ openPage: "打开授权页面",
39
+ deviceCode: "验证码",
40
+ submit: "继续",
41
+ decline: "取消",
42
+ authorized: "登录成功。",
43
+ cancelled: "登录已取消。",
44
+ commitInProgress: "凭据已开始提交,当前无法取消;登录完成后状态会自动更新。",
45
+ failed: "登录失败,请重试。",
46
+ cancelFailed: "取消登录失败,请重试。",
47
+ logoutFailed: "退出登录失败,请重试。",
48
+ responseFailed: "提交登录信息失败,请重试。",
49
+ requestFailed: "暂时无法连接本机授权服务。",
50
+ quotaTitle: "使用额度",
51
+ quotaProduct: "Codex",
52
+ quotaUnknown: "尚无可验证的额度观测。",
53
+ quotaUnknownHelp: "完成一次 Codex 请求后,此处会更新可验证状态;插件不会调用未公开的账户额度接口。",
54
+ quotaRecentSuccess: "最近一次 Codex 请求成功",
55
+ quotaSuccessCaution: "这只表示最近一次请求成功,不代表账户剩余额度。",
56
+ quotaExhausted: "最近观测到 Codex 账户额度已用尽。",
57
+ quotaObservedAt: "观测时间",
58
+ quotaResetAt: "观测到的重置时间",
59
+ quotaResetIn: "预计还有约",
60
+ quotaMinutes: "分钟",
61
+ quotaNoReset: "未获得通过校验的重置时间;状态会在有限时间后自动回到未知。",
62
+ modelsTitle: "模型选择器中显示的模型",
63
+ modelsDescription: "选择要在模型选择器中显示的 Codex 模型。隐藏后,已有会话中保存的精确模型仍可继续使用。",
64
+ modelsLoading: "正在读取 Codex 模型目录…",
65
+ modelsUnavailable: "当前环境没有可用的 Codex 模型管理接口。",
66
+ modelsReadOnly: "当前设置存储为只读,无法修改启用模型。",
67
+ modelsEmpty: "当前安装没有可管理的 Codex 模型。",
68
+ modelsSave: "保存启用模型",
69
+ modelsSaving: "正在保存…",
70
+ modelsSaved: "模型启用设置已保存。",
71
+ modelsAtLeastOne: "至少需要启用一个 Codex 模型。",
72
+ modelsAllEnabledFollow: "已启用全部当前目录模型;当前覆盖不含额外模型字段,保存后会清除覆盖并自动启用未来新增的目录模型。",
73
+ modelsAllEnabledPreserve: "已启用全部当前目录模型;为保留现有模型参数,保存后会固定当前模型列表,未来新增的目录模型不会自动启用。",
74
+ modelsRetry: "重新读取",
75
+ modelsLoadFailed: "读取 Codex 模型目录失败。",
76
+ modelsSaveFailed: "保存模型启用设置失败。",
77
+ }
78
+ const en = {
79
+ nav: "OpenAI Codex",
80
+ title: "OpenAI Codex",
81
+ description: "Use your ChatGPT subscription to access Codex models in dsh—no API key required.",
82
+ loading: "Reading sign-in status…",
83
+ signedIn: "Signed in",
84
+ signedOut: "Not signed in",
85
+ credentialInvalid: "Invalid credential",
86
+ credentialInvalidHelp: "The saved Codex credential cannot be used. Sign in again, or sign out and then sign in.",
87
+ inProgress: "Sign-in in progress",
88
+ unavailable: "No Codex sign-in flow is available in this environment.",
89
+ writableNo: "The credential store is currently read-only.",
90
+ refresh: "Refresh",
91
+ signIn: "Sign in",
92
+ signInWithChatGPT: "Sign in with ChatGPT",
93
+ signInAgain: "Sign in again",
94
+ cancelling: "Cancelling sign-in",
95
+ cancel: "Cancel sign-in",
96
+ signOut: "Sign out",
97
+ signingOut: "Signing out",
98
+ confirmSignOut: "Sign out of Codex and delete the saved authorization credential?",
99
+ openPage: "Open authorization page",
100
+ deviceCode: "Verification code",
101
+ submit: "Continue",
102
+ decline: "Cancel",
103
+ authorized: "Sign-in complete.",
104
+ cancelled: "Sign-in cancelled.",
105
+ commitInProgress: "Credential commit has started and can no longer be cancelled. Status will update when sign-in completes.",
106
+ failed: "Sign-in failed. Please try again.",
107
+ cancelFailed: "Could not cancel sign-in. Please try again.",
108
+ logoutFailed: "Could not sign out. Please try again.",
109
+ responseFailed: "Could not submit the sign-in response. Please try again.",
110
+ requestFailed: "The local authorization service is temporarily unavailable.",
111
+ quotaTitle: "Usage",
112
+ quotaProduct: "Codex",
113
+ quotaUnknown: "No verifiable quota observation yet.",
114
+ quotaUnknownHelp: "This area updates after a Codex request; the plugin does not call undocumented account-quota endpoints.",
115
+ quotaRecentSuccess: "The latest Codex request succeeded",
116
+ quotaSuccessCaution: "This only describes the latest request; it does not represent remaining account quota.",
117
+ quotaExhausted: "Codex account quota was exhausted in the latest observation.",
118
+ quotaObservedAt: "Observed at",
119
+ quotaResetAt: "Observed reset time",
120
+ quotaResetIn: "Approximately",
121
+ quotaMinutes: "minutes remaining",
122
+ quotaNoReset: "No reset time passed validation; this state automatically returns to unknown after a bounded interval.",
123
+ modelsTitle: "Models shown in the model selector",
124
+ modelsDescription: "Choose which Codex models appear in the model selector. Hidden models remain available to existing conversations that saved an exact model.",
125
+ modelsLoading: "Reading the Codex model catalog…",
126
+ modelsUnavailable: "Codex model management is unavailable in this environment.",
127
+ modelsReadOnly: "The settings store is read-only, so enabled models cannot be changed.",
128
+ modelsEmpty: "This installation has no Codex models to manage.",
129
+ modelsSave: "Save enabled models",
130
+ modelsSaving: "Saving…",
131
+ modelsSaved: "Enabled models saved.",
132
+ modelsAtLeastOne: "At least one Codex model must remain enabled.",
133
+ modelsAllEnabledFollow: "All current catalog models are enabled. Because the override has no additional model fields, saving will clear it and automatically enable future catalog additions.",
134
+ modelsAllEnabledPreserve: "All current catalog models are enabled. To preserve existing model parameters, saving will keep the current model list; future catalog additions will not be enabled automatically.",
135
+ modelsRetry: "Reload",
136
+ modelsLoadFailed: "Could not read the Codex model catalog.",
137
+ modelsSaveFailed: "Could not save the enabled models.",
138
+ }
139
+
140
+ function createAuthorizationClient(connection) {
141
+ const call = async (endpoint, payload, signal) => {
142
+ const result = await connection.rpc.call(CHANNEL, endpoint, payload, signal)
143
+ if (!result.ok) {
144
+ const error = new Error(result.error.message)
145
+ error.code = result.error.code
146
+ throw error
147
+ }
148
+ return result.value
149
+ }
150
+ return Object.freeze({
151
+ describe: (signal) => call("status", {}, signal),
152
+ watch: (attemptId, after, signal) => call("status", { attemptId, after }, signal),
153
+ start: (method) => call("start", method === undefined ? {} : { method }),
154
+ answer: (attemptId, promptId, value) => call("respond", {
155
+ attemptId,
156
+ promptId,
157
+ action: "answer",
158
+ value,
159
+ }),
160
+ decline: (attemptId, promptId) => call("respond", {
161
+ attemptId,
162
+ promptId,
163
+ action: "decline",
164
+ }),
165
+ cancel: (attemptId) => call("cancel", attemptId === undefined ? {} : { attemptId }),
166
+ logout: () => call("logout", {}),
167
+ })
168
+ }
169
+
170
+ function resultValue(response) {
171
+ if (response?.result?.ok === true) return response.result.value
172
+ const message = response?.result?.error?.message
173
+ throw new Error(typeof message === "string" && message.length > 0 ? message : "DSH API request failed")
174
+ }
175
+
176
+ function safeQuotaSnapshot(value, now = Date.now()) {
177
+ if (typeof value !== "object" || value === null || !Number.isSafeInteger(now) || now < 0) {
178
+ return { status: "unknown" }
179
+ }
180
+ if (value.status === "recent-success" && Number.isSafeInteger(value.observedAt) && value.observedAt >= 0) {
181
+ return { status: value.status, observedAt: value.observedAt }
182
+ }
183
+ if (value.status !== "exhausted" || !Number.isSafeInteger(value.observedAt) || value.observedAt < 0) {
184
+ return { status: "unknown" }
185
+ }
186
+ if (value.resetAt === undefined) return { status: value.status, observedAt: value.observedAt }
187
+ if (!Number.isSafeInteger(value.resetAt) || value.resetAt <= now) return { status: "unknown" }
188
+ return {
189
+ status: value.status,
190
+ observedAt: value.observedAt,
191
+ resetAt: value.resetAt,
192
+ remainingMinutes: Math.max(1, Math.ceil((value.resetAt - now) / 60_000)),
193
+ }
194
+ }
195
+
196
+ function objectAtPath(root, path) {
197
+ let current = root
198
+ for (const key of path) {
199
+ if (typeof current !== "object" || current === null || Array.isArray(current)) return undefined
200
+ current = current[key]
201
+ }
202
+ return current
203
+ }
204
+
205
+ function modelRows(value) {
206
+ if (!Array.isArray(value)) return []
207
+ return value.filter((row) => typeof row === "object" && row !== null && !Array.isArray(row) && typeof row.id === "string" && row.id.length > 0)
208
+ }
209
+
210
+ function discoveredModels(value) {
211
+ if (!Array.isArray(value)) return []
212
+ const seen = new Set()
213
+ const rows = []
214
+ for (const candidate of value) {
215
+ if (typeof candidate !== "object" || candidate === null || typeof candidate.id !== "string" || candidate.id.length === 0 || seen.has(candidate.id)) continue
216
+ seen.add(candidate.id)
217
+ rows.push({
218
+ id: candidate.id,
219
+ ...(typeof candidate.name === "string" && candidate.name.length > 0 ? { name: candidate.name } : {}),
220
+ })
221
+ }
222
+ return rows
223
+ }
224
+
225
+ function modelSavePlan(snapshot, selectedIds) {
226
+ const selected = new Set(selectedIds)
227
+ const modelIds = snapshot.models.map((model) => model.id)
228
+ const catalogIds = Array.isArray(snapshot.catalogIds)
229
+ ? snapshot.catalogIds.filter((id) => typeof id === "string")
230
+ : modelIds
231
+ const catalogSet = new Set(catalogIds)
232
+ const orderedSelected = modelIds.filter((id) => selected.has(id))
233
+ const existing = new Map(snapshot.configuredModels.map((model) => [model.id, model]))
234
+ const models = orderedSelected.map((id) => existing.has(id) ? { ...existing.get(id) } : { id })
235
+ const allCatalogSelected = catalogIds.length > 0 && catalogIds.every((id) => selected.has(id))
236
+ const hasSelectedOutsideCatalog = orderedSelected.some((id) => !catalogSet.has(id))
237
+ const hasAdditionalFields = models.some((model) => Object.keys(model).some((key) => key !== "id"))
238
+
239
+ return {
240
+ orderedSelected,
241
+ models,
242
+ allCatalogSelected,
243
+ unsetOverride: allCatalogSelected && !hasSelectedOutsideCatalog && !hasAdditionalFields,
244
+ }
245
+ }
246
+
247
+ function createModelEnablementClient(connection, settingsFace) {
248
+ const api = connection?.api
249
+ const available = typeof api?.llm?.discoverModels === "function"
250
+ && typeof api?.settings?.mutate === "function"
251
+ && typeof settingsFace?.ensure === "function"
252
+ && typeof settingsFace?.getSnapshot === "function"
253
+ && typeof settingsFace?.subscribe === "function"
254
+ && typeof settingsFace?.acceptView === "function"
255
+
256
+ const load = async (signal) => {
257
+ if (!available) return { kind: "unavailable" }
258
+
259
+ const [catalogResponse] = await Promise.all([
260
+ api.llm.discoverModels({ settingsNs: CODEX_SETTINGS_NS, provider: CODEX_PROVIDER }, signal),
261
+ settingsFace.ensure(),
262
+ ])
263
+ const discovered = discoveredModels(resultValue(catalogResponse).models)
264
+ const mirror = settingsFace.getSnapshot()
265
+ if (mirror?.status !== "ready" || mirror.view === undefined) return { kind: "unavailable" }
266
+ const settings = mirror.view
267
+ const namespace = Array.isArray(settings.namespaces)
268
+ ? settings.namespaces.find((entry) => entry?.ns === CODEX_SETTINGS_NS)
269
+ : undefined
270
+ if (namespace === undefined || typeof namespace.revision !== "number") return { kind: "unavailable" }
271
+
272
+ const modelsPath = [...CODEX_SETTINGS_PATH, "models"]
273
+ const userModelsValue = objectAtPath(namespace.user, modelsPath)
274
+ const resolvedModelsValue = objectAtPath(namespace.value, modelsPath)
275
+ const hasUserModelOverride = Array.isArray(userModelsValue)
276
+ const hasResolvedModelOverride = Array.isArray(resolvedModelsValue)
277
+ const configuredModels = hasUserModelOverride
278
+ ? modelRows(userModelsValue)
279
+ : hasResolvedModelOverride
280
+ ? modelRows(resolvedModelsValue)
281
+ : []
282
+ const hasModelOverride = hasUserModelOverride || hasResolvedModelOverride
283
+ const discoveredIds = new Set(discovered.map((model) => model.id))
284
+ const customModels = configuredModels
285
+ .filter((model) => !discoveredIds.has(model.id))
286
+ .map((model) => ({
287
+ id: model.id,
288
+ ...(typeof model.name === "string" && model.name.length > 0 ? { name: model.name } : {}),
289
+ }))
290
+ const catalog = [...discovered, ...customModels]
291
+ const configuredIds = new Set(configuredModels.map((model) => model.id))
292
+ const selectedIds = hasModelOverride
293
+ ? catalog.filter((model) => configuredIds.has(model.id)).map((model) => model.id)
294
+ : catalog.map((model) => model.id)
295
+
296
+ return {
297
+ kind: "ready",
298
+ writable: settings.writable === true,
299
+ revision: namespace.revision,
300
+ settingsNs: CODEX_SETTINGS_NS,
301
+ settingsPath: [...CODEX_SETTINGS_PATH],
302
+ models: catalog,
303
+ catalogIds: discovered.map((model) => model.id),
304
+ selectedIds,
305
+ configuredModels: configuredModels.map((model) => ({ ...model })),
306
+ }
307
+ }
308
+
309
+ const save = async (snapshot, selectedIds, signal) => {
310
+ if (!available || snapshot?.kind !== "ready") throw new Error("Codex model management is unavailable")
311
+ const plan = modelSavePlan(snapshot, selectedIds)
312
+ const { orderedSelected, models } = plan
313
+ if (orderedSelected.length === 0) {
314
+ const error = new Error("At least one Codex model must remain enabled")
315
+ error.code = "AT_LEAST_ONE_MODEL"
316
+ throw error
317
+ }
318
+
319
+ const modelsPath = [...snapshot.settingsPath, "models"]
320
+ const response = await api.settings.mutate({
321
+ ns: snapshot.settingsNs,
322
+ ops: [plan.unsetOverride
323
+ ? { op: "unset", path: modelsPath }
324
+ : { op: "set", path: modelsPath, value: models }],
325
+ expectedRevision: snapshot.revision,
326
+ }, signal)
327
+ const namespace = resultValue(response)
328
+ settingsFace.acceptView(namespace)
329
+ return {
330
+ ...snapshot,
331
+ revision: typeof namespace.revision === "number" ? namespace.revision : snapshot.revision,
332
+ selectedIds: orderedSelected,
333
+ configuredModels: plan.unsetOverride ? [] : models,
334
+ }
335
+ }
336
+
337
+ const subscribe = (listener) => available ? settingsFace.subscribe(listener) : () => undefined
338
+ return Object.freeze({ available, load, save, subscribe })
339
+ }
340
+
341
+ function AuthorizationSettings({ client, t }) {
342
+ const [snapshot, setSnapshot] = React.useState({ status: "loading" })
343
+ const [attempt, setAttempt] = React.useState(null)
344
+ const [notices, setNotices] = React.useState([])
345
+ const [prompt, setPrompt] = React.useState(null)
346
+ const [answer, setAnswer] = React.useState("")
347
+ const [outcome, setOutcome] = React.useState(null)
348
+ const [failure, setFailure] = React.useState(null)
349
+ const [responding, setResponding] = React.useState(false)
350
+ const [cancelling, setCancelling] = React.useState(false)
351
+ const [loggingOut, setLoggingOut] = React.useState(false)
352
+ const attemptRef = React.useRef(null)
353
+ const pendingStartRef = React.useRef(null)
354
+ const mountedRef = React.useRef(false)
355
+ const attemptGenerationRef = React.useRef(0)
356
+ const responseGenerationRef = React.useRef(0)
357
+ const refreshGenerationRef = React.useRef(0)
358
+ const refreshControllerRef = React.useRef(null)
359
+
360
+ const refresh = React.useCallback(async () => {
361
+ if (!mountedRef.current) return
362
+ const generation = ++refreshGenerationRef.current
363
+ refreshControllerRef.current?.abort()
364
+ const controller = new AbortController()
365
+ refreshControllerRef.current = controller
366
+ setSnapshot((current) => current.status === "ready" ? current : { status: "loading" })
367
+ try {
368
+ const value = await client.describe(controller.signal)
369
+ if (!mountedRef.current || generation !== refreshGenerationRef.current || controller.signal.aborted) return
370
+ setSnapshot({ status: "ready", value })
371
+ } catch {
372
+ if (!mountedRef.current || generation !== refreshGenerationRef.current || controller.signal.aborted) return
373
+ setSnapshot({ status: "error" })
374
+ } finally {
375
+ if (generation === refreshGenerationRef.current && refreshControllerRef.current === controller) {
376
+ refreshControllerRef.current = null
377
+ }
378
+ }
379
+ }, [client])
380
+
381
+ React.useEffect(() => {
382
+ mountedRef.current = true
383
+ void refresh()
384
+ const timer = typeof window.setInterval === "function"
385
+ ? window.setInterval(() => void refresh(), 60_000)
386
+ : undefined
387
+ return () => {
388
+ mountedRef.current = false
389
+ refreshGenerationRef.current += 1
390
+ refreshControllerRef.current?.abort()
391
+ refreshControllerRef.current = null
392
+ attemptGenerationRef.current += 1
393
+ responseGenerationRef.current += 1
394
+ pendingStartRef.current = null
395
+ const current = attemptRef.current
396
+ attemptRef.current = null
397
+ current?.controller.abort()
398
+ if (current !== null && !current.done) void client.cancel(current.id).catch(() => undefined)
399
+ if (timer !== undefined) window.clearInterval(timer)
400
+ }
401
+ }, [client])
402
+
403
+ const consume = async (current) => {
404
+ const isCurrent = () => mountedRef.current
405
+ && attemptGenerationRef.current === current.generation
406
+ && attemptRef.current === current
407
+ && !current.controller.signal.aborted
408
+ let after = 0
409
+ try {
410
+ while (isCurrent()) {
411
+ const page = await client.watch(current.id, after, current.controller.signal)
412
+ if (!isCurrent()) return
413
+ after = page.nextSeq
414
+ for (const event of page.events) {
415
+ if (!isCurrent()) return
416
+ if (event.type === "notice") {
417
+ setNotices((items) => [...items, event.notice].slice(-32))
418
+ } else if (event.type === "prompt") {
419
+ setAnswer("")
420
+ setPrompt({ id: event.promptId, value: event.prompt })
421
+ } else if (event.type === "prompt-closed") {
422
+ setPrompt((currentPrompt) => currentPrompt?.id === event.promptId ? null : currentPrompt)
423
+ setAnswer("")
424
+ } else if (event.type === "settled") {
425
+ current.done = true
426
+ responseGenerationRef.current += 1
427
+ setResponding(false)
428
+ setPrompt(null)
429
+ setFailure(null)
430
+ setOutcome(event.status)
431
+ } else if (event.type === "failed") {
432
+ current.done = true
433
+ responseGenerationRef.current += 1
434
+ setResponding(false)
435
+ setPrompt(null)
436
+ setOutcome(null)
437
+ setFailure(event.error.code)
438
+ }
439
+ }
440
+ if (page.done) {
441
+ current.done = true
442
+ break
443
+ }
444
+ }
445
+ } catch {
446
+ if (isCurrent()) setFailure("TRANSPORT")
447
+ } finally {
448
+ if (mountedRef.current
449
+ && attemptGenerationRef.current === current.generation
450
+ && attemptRef.current === current) {
451
+ attemptRef.current = null
452
+ setAttempt(null)
453
+ void refresh()
454
+ }
455
+ }
456
+ }
457
+
458
+ const begin = async (method) => {
459
+ if (!mountedRef.current
460
+ || pendingStartRef.current !== null
461
+ || attemptRef.current !== null
462
+ || cancelling
463
+ || loggingOut
464
+ || responding) return
465
+ const generation = ++attemptGenerationRef.current
466
+ const pending = { generation }
467
+ pendingStartRef.current = pending
468
+ setAttempt("starting")
469
+ setNotices([])
470
+ setPrompt(null)
471
+ setOutcome(null)
472
+ setFailure(null)
473
+ try {
474
+ const started = await client.start(method)
475
+ if (!mountedRef.current
476
+ || attemptGenerationRef.current !== generation
477
+ || pendingStartRef.current !== pending) {
478
+ void client.cancel(started.attemptId).catch(() => undefined)
479
+ return
480
+ }
481
+ pendingStartRef.current = null
482
+ const current = {
483
+ id: started.attemptId,
484
+ controller: new AbortController(),
485
+ generation,
486
+ done: false,
487
+ }
488
+ attemptRef.current = current
489
+ setAttempt(current.id)
490
+ void consume(current)
491
+ } catch {
492
+ if (!mountedRef.current
493
+ || attemptGenerationRef.current !== generation
494
+ || pendingStartRef.current !== pending) return
495
+ pendingStartRef.current = null
496
+ setAttempt(null)
497
+ setFailure("START_FAILED")
498
+ void refresh()
499
+ }
500
+ }
501
+
502
+ const cancel = async () => {
503
+ if (!mountedRef.current || cancelling || loggingOut || responding) return
504
+ const current = attemptRef.current
505
+ const generation = current?.generation ?? attemptGenerationRef.current
506
+ const isCurrentCancellation = () => mountedRef.current
507
+ && attemptGenerationRef.current === generation
508
+ && (current === null
509
+ ? attemptRef.current === null
510
+ : attemptRef.current === current && !current.done)
511
+ if (pendingStartRef.current !== null) {
512
+ attemptGenerationRef.current += 1
513
+ pendingStartRef.current = null
514
+ setAttempt(null)
515
+ }
516
+ setCancelling(true)
517
+ setFailure(null)
518
+ setOutcome(null)
519
+ try {
520
+ const result = await client.cancel(current?.id)
521
+ if (!mountedRef.current) return
522
+ if (current !== null
523
+ && isCurrentCancellation()
524
+ && result?.accepted === false
525
+ && result.reason === "commit-in-progress") {
526
+ setOutcome("commit-in-progress")
527
+ }
528
+ await refresh()
529
+ } catch {
530
+ if (isCurrentCancellation()) setFailure("CANCEL_FAILED")
531
+ } finally {
532
+ if (mountedRef.current) setCancelling(false)
533
+ }
534
+ }
535
+
536
+ const logout = async () => {
537
+ if (!mountedRef.current || loggingOut || cancelling || responding || attemptRef.current !== null || pendingStartRef.current !== null) return
538
+ if (typeof window.confirm === "function" && !window.confirm(t("confirmSignOut"))) return
539
+ setLoggingOut(true)
540
+ setFailure(null)
541
+ setOutcome(null)
542
+ try {
543
+ await client.logout()
544
+ if (!mountedRef.current) return
545
+ setNotices([])
546
+ setPrompt(null)
547
+ setAnswer("")
548
+ await refresh()
549
+ } catch {
550
+ if (mountedRef.current) setFailure("LOGOUT_FAILED")
551
+ } finally {
552
+ if (mountedRef.current) setLoggingOut(false)
553
+ }
554
+ }
555
+
556
+ const reply = async (action) => {
557
+ const current = attemptRef.current
558
+ if (!mountedRef.current || current === null || prompt === null || responding || cancelling || loggingOut) return
559
+ const generation = current.generation
560
+ const responseGeneration = ++responseGenerationRef.current
561
+ const promptId = prompt.id
562
+ const isCurrentResponse = () => mountedRef.current
563
+ && responseGenerationRef.current === responseGeneration
564
+ setResponding(true)
565
+ setFailure(null)
566
+ try {
567
+ if (action === "decline") await client.decline(current.id, prompt.id)
568
+ else await client.answer(current.id, prompt.id, answer)
569
+ if (!isCurrentResponse()
570
+ || attemptGenerationRef.current !== generation
571
+ || attemptRef.current !== current) return
572
+ setPrompt((currentPrompt) => currentPrompt?.id === promptId ? null : currentPrompt)
573
+ setAnswer("")
574
+ } catch {
575
+ if (isCurrentResponse()
576
+ && attemptGenerationRef.current === generation
577
+ && attemptRef.current === current) setFailure("RESPONSE_FAILED")
578
+ } finally {
579
+ if (isCurrentResponse()) setResponding(false)
580
+ }
581
+ }
582
+
583
+ if (snapshot.status === "loading") {
584
+ return h("section", { className: "dshCodexPanel dshCodexCard dshCodexAuthorizationCard", "aria-busy": "true" },
585
+ h("p", { className: "dshCodexMuted" }, t("loading")))
586
+ }
587
+ if (snapshot.status === "error") {
588
+ return h("section", { className: "dshCodexPanel dshCodexCard dshCodexAuthorizationCard" },
589
+ h("p", { role: "alert", className: "dshCodexError" }, t("requestFailed")),
590
+ h("button", { type: "button", className: "dshCodexButton", onClick: refresh }, t("refresh")))
591
+ }
592
+
593
+ const view = snapshot.value
594
+ const flow = view.flow
595
+ const authorizationBusy = attempt !== null || flow?.inFlight === true
596
+ const actionBusy = cancelling || loggingOut || responding
597
+ const configured = view.credential.configured === true
598
+ const credentialState = new Set(["signed-in", "signed-out", "invalid"])
599
+ .has(view.credential.state)
600
+ ? view.credential.state
601
+ : configured ? "signed-in" : "signed-out"
602
+ const signedIn = credentialState === "signed-in"
603
+ const statusText = loggingOut
604
+ ? t("signingOut")
605
+ : cancelling
606
+ ? t("cancelling")
607
+ : authorizationBusy
608
+ ? t("inProgress")
609
+ : credentialState === "invalid"
610
+ ? t("credentialInvalid")
611
+ : signedIn
612
+ ? t("signedIn")
613
+ : t("signedOut")
614
+ const quota = safeQuotaSnapshot(view.quota)
615
+ const statusTone = authorizationBusy || cancelling || loggingOut
616
+ ? "busy"
617
+ : credentialState === "invalid"
618
+ ? "invalid"
619
+ : signedIn
620
+ ? "active"
621
+ : "inactive"
622
+ const authorizationActions = flow === undefined
623
+ ? null
624
+ : h("div", { className: "dshCodexActions dshCodexAuthActions" },
625
+ ...flow.methods.map((method) => h("button", {
626
+ key: method.id,
627
+ type: "button",
628
+ className: configured ? "dshCodexButton" : "dshCodexButton dshCodexPrimary",
629
+ disabled: authorizationBusy || actionBusy || view.credential.writable === false,
630
+ onClick: () => void begin(method.id),
631
+ }, configured
632
+ ? t("signInAgain")
633
+ : flow.methods.length === 1
634
+ ? t("signInWithChatGPT")
635
+ : `${t("signIn")} · ${method.label}`)),
636
+ authorizationBusy ? h("button", {
637
+ type: "button",
638
+ className: "dshCodexButton",
639
+ disabled: actionBusy,
640
+ onClick: () => void cancel(),
641
+ }, t("cancel")) : null,
642
+ configured && !authorizationBusy ? h("button", {
643
+ type: "button",
644
+ className: "dshCodexButton",
645
+ disabled: actionBusy,
646
+ onClick: () => void logout(),
647
+ }, loggingOut ? t("signingOut") : t("signOut")) : null)
648
+
649
+ let quotaBody
650
+ if (quota.status === "recent-success") {
651
+ quotaBody = h(React.Fragment, null,
652
+ h("p", { className: "dshCodexQuotaState" }, `${t("quotaRecentSuccess")} · ${new Date(quota.observedAt).toLocaleString()}`),
653
+ h("p", { className: "dshCodexMuted" }, t("quotaSuccessCaution")))
654
+ } else if (quota.status === "exhausted") {
655
+ quotaBody = h(React.Fragment, null,
656
+ h("p", { className: "dshCodexQuotaState dshCodexQuotaExhausted" }, t("quotaExhausted")),
657
+ h("p", { className: "dshCodexMuted" }, `${t("quotaObservedAt")} · ${new Date(quota.observedAt).toLocaleString()}`),
658
+ quota.resetAt === undefined
659
+ ? h("p", { className: "dshCodexMuted" }, t("quotaNoReset"))
660
+ : h("p", { className: "dshCodexMuted" }, `${t("quotaResetAt")} · ${new Date(quota.resetAt).toLocaleString()} · ${t("quotaResetIn")} ${quota.remainingMinutes} ${t("quotaMinutes")}`))
661
+ } else {
662
+ quotaBody = h(React.Fragment, null,
663
+ h("p", { className: "dshCodexQuotaState dshCodexQuotaUnknown" }, t("quotaUnknown")),
664
+ h("p", { className: "dshCodexMuted" }, t("quotaUnknownHelp")))
665
+ }
666
+
667
+ return h("section", {
668
+ className: "dshCodexPanel dshCodexCard dshCodexAuthorizationCard",
669
+ "aria-busy": authorizationBusy || actionBusy ? "true" : undefined,
670
+ },
671
+ h("div", { className: "dshCodexAuthToolbar" },
672
+ h("div", {
673
+ className: "dshCodexStatus",
674
+ "data-state": statusTone,
675
+ role: "status",
676
+ "aria-live": "polite",
677
+ },
678
+ h("span", { className: "dshCodexStatusDot", "aria-hidden": "true" }),
679
+ h("strong", null, statusText)),
680
+ authorizationActions),
681
+ credentialState === "invalid"
682
+ ? h("p", { role: "alert", className: "dshCodexError" }, t("credentialInvalidHelp"))
683
+ : null,
684
+ flow === undefined
685
+ ? h("p", { role: "status", className: "dshCodexMuted" }, t("unavailable"))
686
+ : null,
687
+ view.credential.writable === false
688
+ ? h("p", { className: "dshCodexMuted" }, t("writableNo"))
689
+ : null,
690
+ notices.length > 0
691
+ ? h("ol", { className: "dshCodexNotices", "aria-live": "polite" },
692
+ ...notices.map((notice, index) => h("li", { key: `${index}-${notice.message}` },
693
+ h("p", null, notice.message),
694
+ notice.url === undefined ? null : h("a", {
695
+ href: notice.url,
696
+ target: "_blank",
697
+ rel: "noopener noreferrer",
698
+ }, t("openPage")),
699
+ notice.code === undefined ? null : h("div", { className: "dshCodexCode" },
700
+ h("span", null, t("deviceCode")), h("code", null, notice.code)))))
701
+ : null,
702
+ prompt === null ? null : h("form", {
703
+ className: "dshCodexPrompt",
704
+ onSubmit: (event) => {
705
+ event.preventDefault()
706
+ void reply("answer")
707
+ },
708
+ },
709
+ h("label", null,
710
+ h("span", null, prompt.value.message),
711
+ prompt.value.kind === "select"
712
+ ? h("select", {
713
+ value: answer,
714
+ required: true,
715
+ onChange: (event) => setAnswer(event.currentTarget.value),
716
+ }, h("option", { value: "", disabled: true }, "—"),
717
+ ...prompt.value.options.map((option) => h("option", { key: option.id, value: option.id }, option.label)))
718
+ : h("input", {
719
+ type: prompt.value.kind === "secret" ? "password" : "text",
720
+ value: answer,
721
+ placeholder: prompt.value.placeholder,
722
+ autoComplete: "off",
723
+ required: true,
724
+ onChange: (event) => setAnswer(event.currentTarget.value),
725
+ })),
726
+ h("div", { className: "dshCodexActions" },
727
+ h("button", { type: "submit", className: "dshCodexButton dshCodexPrimary", disabled: actionBusy }, t("submit")),
728
+ h("button", { type: "button", className: "dshCodexButton", disabled: actionBusy, onClick: () => void reply("decline") }, t("decline")))),
729
+ outcome === "authorized" ? h("p", { role: "status", className: "dshCodexSuccess" }, t("authorized")) : null,
730
+ outcome === "cancelled" ? h("p", { role: "status", className: "dshCodexMuted" }, t("cancelled")) : null,
731
+ outcome === "commit-in-progress" ? h("p", { role: "status", className: "dshCodexMuted" }, t("commitInProgress")) : null,
732
+ failure === null ? null : h("p", { role: "alert", className: "dshCodexError", "data-error-code": failure }, t(failureCopyKey(failure))),
733
+ h("section", {
734
+ className: "dshCodexQuota",
735
+ "data-quota-status": quota.status,
736
+ "aria-live": "polite",
737
+ "aria-labelledby": "dsh-codex-quota-title",
738
+ },
739
+ h("div", { className: "dshCodexQuotaHeader" },
740
+ h("h3", { id: "dsh-codex-quota-title" }, t("quotaTitle")),
741
+ h("button", {
742
+ type: "button",
743
+ className: "dshCodexButton dshCodexRefreshButton",
744
+ disabled: actionBusy,
745
+ onClick: refresh,
746
+ }, t("refresh"))),
747
+ h("p", { className: "dshCodexQuotaProduct" }, t("quotaProduct")),
748
+ quotaBody))
749
+ }
750
+
751
+ function failureCopyKey(failure) {
752
+ if (failure === "CANCEL_FAILED") return "cancelFailed"
753
+ if (failure === "LOGOUT_FAILED") return "logoutFailed"
754
+ if (failure === "RESPONSE_FAILED") return "responseFailed"
755
+ return "failed"
756
+ }
757
+
758
+ function ModelEnablementSettings({ client, t }) {
759
+ const [snapshot, setSnapshot] = React.useState(client.available ? { kind: "loading" } : { kind: "unavailable" })
760
+ const [selectedIds, setSelectedIds] = React.useState([])
761
+ const [saving, setSaving] = React.useState(false)
762
+ const [saved, setSaved] = React.useState(false)
763
+ const [failure, setFailure] = React.useState(null)
764
+ const generationRef = React.useRef(0)
765
+ const mountedRef = React.useRef(false)
766
+
767
+ const refresh = React.useCallback(async () => {
768
+ const generation = ++generationRef.current
769
+ setSaved(false)
770
+ setFailure(null)
771
+ setSnapshot({ kind: "loading" })
772
+ try {
773
+ const next = await client.load()
774
+ if (generation !== generationRef.current) return
775
+ setSnapshot(next)
776
+ setSelectedIds(next.kind === "ready" ? next.selectedIds : [])
777
+ } catch (error) {
778
+ if (generation !== generationRef.current) return
779
+ const detail = error instanceof Error ? error.message : String(error)
780
+ setSnapshot({ kind: "error", detail })
781
+ }
782
+ }, [client])
783
+
784
+ React.useEffect(() => {
785
+ mountedRef.current = true
786
+ const unsubscribe = client.subscribe(() => void refresh())
787
+ if (client.available) void refresh()
788
+ return () => {
789
+ mountedRef.current = false
790
+ generationRef.current += 1
791
+ unsubscribe()
792
+ }
793
+ }, [client, refresh])
794
+
795
+ const toggle = (id) => {
796
+ const selected = selectedIds.includes(id)
797
+ if (selected && selectedIds.length === 1) {
798
+ setFailure(t("modelsAtLeastOne"))
799
+ return
800
+ }
801
+ setSaved(false)
802
+ setFailure(null)
803
+ setSelectedIds(selected
804
+ ? selectedIds.filter((candidate) => candidate !== id)
805
+ : [...selectedIds, id])
806
+ }
807
+
808
+ const save = async () => {
809
+ if (!mountedRef.current || snapshot.kind !== "ready") return
810
+ if (selectedIds.length === 0) {
811
+ setFailure(t("modelsAtLeastOne"))
812
+ return
813
+ }
814
+ setSaving(true)
815
+ setSaved(false)
816
+ setFailure(null)
817
+ try {
818
+ const next = await client.save(snapshot, selectedIds)
819
+ if (!mountedRef.current) return
820
+ setSnapshot(next)
821
+ setSelectedIds(next.selectedIds)
822
+ setSaved(true)
823
+ } catch (error) {
824
+ if (!mountedRef.current) return
825
+ const message = error?.code === "AT_LEAST_ONE_MODEL"
826
+ ? t("modelsAtLeastOne")
827
+ : `${t("modelsSaveFailed")} ${error instanceof Error ? error.message : String(error)}`
828
+ setFailure(message)
829
+ } finally {
830
+ if (mountedRef.current) setSaving(false)
831
+ }
832
+ }
833
+
834
+ let content
835
+ if (snapshot.kind === "loading") {
836
+ content = h("p", { className: "dshCodexMuted", "aria-busy": "true" }, t("modelsLoading"))
837
+ } else if (snapshot.kind === "unavailable") {
838
+ content = h(React.Fragment, null,
839
+ h("p", { role: "status", className: "dshCodexMuted" }, t("modelsUnavailable")),
840
+ client.available ? h("button", { type: "button", className: "dshCodexButton", onClick: refresh }, t("modelsRetry")) : null)
841
+ } else if (snapshot.kind === "error") {
842
+ content = h(React.Fragment, null,
843
+ h("p", { role: "alert", className: "dshCodexError" }, `${t("modelsLoadFailed")} ${snapshot.detail}`),
844
+ h("button", { type: "button", className: "dshCodexButton", onClick: refresh }, t("modelsRetry")))
845
+ } else if (snapshot.models.length === 0) {
846
+ content = h(React.Fragment, null,
847
+ h("p", { role: "status", className: "dshCodexMuted" }, t("modelsEmpty")),
848
+ h("button", { type: "button", className: "dshCodexButton", onClick: refresh }, t("modelsRetry")))
849
+ } else {
850
+ const selected = new Set(selectedIds)
851
+ const original = new Set(snapshot.selectedIds)
852
+ const dirty = selected.size !== original.size || [...selected].some((id) => !original.has(id))
853
+ const plan = modelSavePlan(snapshot, selectedIds)
854
+ const modelItems = snapshot.models.map((model) => h("li", { key: model.id },
855
+ h("label", {
856
+ className: "dshCodexModelOption",
857
+ "data-selected": selected.has(model.id) ? "true" : undefined,
858
+ },
859
+ h("input", {
860
+ type: "checkbox",
861
+ checked: selected.has(model.id),
862
+ disabled: saving || !snapshot.writable,
863
+ onChange: () => toggle(model.id),
864
+ }),
865
+ h("span", null,
866
+ h("strong", null, model.name ?? model.id),
867
+ model.name === undefined || model.name === model.id ? null : h("code", null, model.id)))))
868
+ content = h(React.Fragment, null,
869
+ snapshot.writable ? null : h("p", { className: "dshCodexMuted" }, t("modelsReadOnly")),
870
+ h("ul", {
871
+ className: "dshCodexModelList",
872
+ "aria-labelledby": "dsh-codex-models-title",
873
+ }, ...modelItems),
874
+ plan.allCatalogSelected ? h("p", { className: "dshCodexMuted" }, t(plan.unsetOverride
875
+ ? "modelsAllEnabledFollow"
876
+ : "modelsAllEnabledPreserve")) : null,
877
+ h("div", { className: "dshCodexActions dshCodexModelActions" },
878
+ h("button", {
879
+ type: "button",
880
+ className: "dshCodexButton dshCodexPrimary",
881
+ disabled: saving || !snapshot.writable || selected.size === 0 || !dirty,
882
+ onClick: () => void save(),
883
+ }, saving ? t("modelsSaving") : t("modelsSave")),
884
+ h("button", {
885
+ type: "button",
886
+ className: "dshCodexButton",
887
+ disabled: saving,
888
+ onClick: () => void refresh(),
889
+ }, t("modelsRetry"))),
890
+ saved ? h("p", { role: "status", className: "dshCodexSuccess" }, t("modelsSaved")) : null,
891
+ failure === null ? null : h("p", { role: "alert", className: "dshCodexError" }, failure))
892
+ }
893
+
894
+ return h("section", { className: "dshCodexPanel dshCodexCard dshCodexModelPanel" },
895
+ h("header", { className: "dshCodexHeader" },
896
+ h("div", null,
897
+ h("h3", { id: "dsh-codex-models-title" }, t("modelsTitle")),
898
+ h("p", { className: "dshCodexWarning" }, t("modelsDescription")))),
899
+ content)
900
+ }
901
+
902
+ function CodexSettings({ client, modelClient, t }) {
903
+ return h("div", { className: "dshCodexPage" },
904
+ h("header", { className: "dshCodexHero" },
905
+ h("h2", null, t("title")),
906
+ h("p", null, t("description"))),
907
+ h(AuthorizationSettings, { client, t }),
908
+ h(ModelEnablementSettings, { client: modelClient, t }))
909
+ }
910
+
911
+ const inject = ["slots", "locale", "connection", "settingsScope"]
912
+
913
+ function apply(ctx) {
914
+ ctx.effect(installStyle, "dsh-codex: client styles")
915
+ ctx.effect(() => ctx.locale.register(NS, { zh, en }), "dsh-codex: client dictionaries")
916
+ const t = ctx.locale.bind(NS)
917
+ const client = createAuthorizationClient(ctx.connection)
918
+ const modelClient = createModelEnablementClient(ctx.connection, ctx.settingsScope?.describe?.())
919
+ ctx.slots.inject("settings.section", () => ctx.slots.register({
920
+ name: "settings.section",
921
+ id: "codex",
922
+ order: 15,
923
+ label: () => t("nav"),
924
+ locale: NS,
925
+ inject: () => ({ client, modelClient, t }),
926
+ }, CodexSettings))
927
+ }
928
+
929
+ function installStyle() {
930
+ if (typeof document === "undefined") return undefined
931
+ let style = document.querySelector(`style[data-plugin-css="${STYLE_ID}"]`)
932
+ if (style === null) {
933
+ style = document.createElement("style")
934
+ style.dataset.plugin = "dsh-codex-community"
935
+ style.dataset.pluginCss = STYLE_ID
936
+ document.head.appendChild(style)
937
+ }
938
+ style.textContent = [
939
+ "[role=dialog]:has(.dshCodexPage){width:min(1180px,calc(100vw - 48px))}",
940
+ ".dshCodexPage{box-sizing:border-box;display:flex;width:100%;max-width:1120px;min-width:0;flex-direction:column;gap:28px;padding:6px 0 40px;color:var(--dsw-alias-label-primary)}",
941
+ ".dshCodexPage *{box-sizing:border-box}",
942
+ ".dshCodexHero{display:flex;min-width:0;flex-direction:column;gap:8px}",
943
+ ".dshCodexHero h2,.dshCodexHero p{margin:0}",
944
+ ".dshCodexHero h2{font-size:16px;font-weight:500;line-height:24px}",
945
+ ".dshCodexHero p{max-width:760px;color:var(--dsw-alias-label-tertiary);font-size:14px;line-height:22px}",
946
+ ".dshCodexPanel{display:flex;width:100%;min-width:0;max-width:none;color:var(--dsw-alias-label-primary);flex-direction:column;gap:18px}",
947
+ ".dshCodexPanel p,.dshCodexPanel label{overflow-wrap:anywhere;word-break:break-word}",
948
+ ".dshCodexCard{border:1px solid var(--dsw-alias-border-l2);border-radius:18px;background:var(--dsw-alias-bg-module-platform,var(--dsw-alias-bg-layer-3));padding:clamp(24px,3vw,34px)}",
949
+ ".dshCodexHeader{display:flex;align-items:flex-start;justify-content:space-between;gap:16px}",
950
+ ".dshCodexHeader>div{min-width:0}",
951
+ ".dshCodexHeader h3,.dshCodexHeader p,.dshCodexNotices p,.dshCodexQuota p{margin:0}",
952
+ ".dshCodexHeader h3{font-size:16px;font-weight:500;line-height:24px}",
953
+ ".dshCodexAuthToolbar{display:flex;min-width:0;align-items:center;justify-content:space-between;gap:18px}",
954
+ ".dshCodexStatus{display:flex;min-width:0;align-items:center;gap:10px;font-size:14px;line-height:22px}",
955
+ ".dshCodexStatus strong{font-weight:500;white-space:nowrap}",
956
+ ".dshCodexStatusDot{width:11px;height:11px;flex:none;border-radius:50%;background:var(--dsw-alias-label-tertiary)}",
957
+ ".dshCodexStatus[data-state=active] .dshCodexStatusDot{background:var(--dsw-alias-state-success-primary)}",
958
+ ".dshCodexStatus[data-state=invalid] .dshCodexStatusDot{background:var(--dsw-alias-state-error-primary)}",
959
+ ".dshCodexStatus[data-state=busy] .dshCodexStatusDot{background:var(--dsw-alias-state-business-primary)}",
960
+ ".dshCodexQuota{display:flex;min-width:0;flex-direction:column;gap:10px;border-top:1px solid var(--dsw-alias-border-l2);padding-top:24px}",
961
+ ".dshCodexQuotaHeader{display:flex;min-width:0;align-items:center;justify-content:space-between;gap:12px}",
962
+ ".dshCodexQuota h3{margin:0;font-size:16px;font-weight:500;line-height:24px}",
963
+ ".dshCodexQuotaProduct{font-size:14px;font-weight:500;line-height:22px}",
964
+ ".dshCodexQuotaState{display:flex;align-items:center;gap:8px;font-size:14px;line-height:22px}",
965
+ ".dshCodexQuotaState::before{width:8px;height:8px;flex:none;border-radius:50%;background:var(--dsw-alias-state-success-primary);content:\"\"}",
966
+ ".dshCodexQuotaUnknown::before{background:var(--dsw-alias-label-tertiary)}",
967
+ ".dshCodexQuotaExhausted::before{background:var(--dsw-alias-state-error-primary)}",
968
+ ".dshCodexQuotaExhausted{color:var(--dsw-alias-state-error-primary)}",
969
+ ".dshCodexMuted{color:var(--dsw-alias-label-tertiary);font-size:14px;line-height:22px}",
970
+ ".dshCodexWarning{max-width:820px;color:var(--dsw-alias-label-secondary);font-size:14px;line-height:22px}",
971
+ ".dshCodexActions{display:flex;min-width:0;flex-wrap:wrap;gap:8px}",
972
+ ".dshCodexAuthActions{margin-left:auto;justify-content:flex-end}",
973
+ ".dshCodexAuthActions .dshCodexButton{height:36px;min-height:36px;border-radius:18px;padding:0 14px;font-size:14px;line-height:22px}",
974
+ ".dshCodexQuotaHeader .dshCodexRefreshButton{min-height:28px;border:none;border-radius:14px;background:transparent;color:var(--dsw-alias-label-tertiary);padding:0 10px;font-size:12px;line-height:18px}",
975
+ ".dshCodexButton{box-sizing:border-box;max-width:100%;white-space:normal;overflow-wrap:anywhere;word-break:break-word;border:1px solid var(--dsw-alias-border-l2);border-radius:10px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;padding:8px 14px;transition:background-color .15s ease,border-color .15s ease}",
976
+ ".dshCodexButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-2))}",
977
+ ".dshCodexButton:focus-visible,.dshCodexModelOption:has(input:focus-visible){outline:2px solid var(--dsw-alias-brand-primary,var(--dsw-alias-border-l3));outline-offset:2px}",
978
+ ".dshCodexButton:disabled{cursor:not-allowed;opacity:.5}",
979
+ ".dshCodexPrimary{border-color:var(--dsw-alias-button-primary-fill,var(--dsw-alias-state-business-primary));background:var(--dsw-alias-button-primary-fill,var(--dsw-alias-state-business-primary));color:var(--dsw-alias-label-primary-foreground,#fff)}",
980
+ ".dshCodexPrimary:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover,var(--dsw-alias-state-business-primary))}",
981
+ ".dshCodexModelList{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));column-gap:36px;row-gap:8px;margin:0;padding:0;list-style:none}",
982
+ ".dshCodexModelOption{display:flex;min-width:0;align-items:flex-start;gap:12px;border-radius:10px;padding:10px 8px;cursor:pointer;transition:background-color .15s ease}",
983
+ ".dshCodexModelOption:hover{background:var(--dsw-alias-interactive-bg-hover,var(--dsw-alias-bg-layer-2))}",
984
+ ".dshCodexModelOption:has(input:disabled){cursor:not-allowed;opacity:.65}",
985
+ ".dshCodexModelOption:has(input:disabled):hover{background:transparent}",
986
+ ".dshCodexModelOption input{width:18px;height:18px;flex:none;margin:2px 0 0;accent-color:var(--dsw-alias-state-business-primary)}",
987
+ ".dshCodexModelOption span{display:flex;min-width:0;flex-wrap:wrap;align-items:baseline;column-gap:8px;row-gap:2px}",
988
+ ".dshCodexModelOption strong{overflow-wrap:anywhere;font-size:14px;font-weight:500;line-height:22px}",
989
+ ".dshCodexModelOption code{overflow-wrap:anywhere;color:var(--dsw-alias-label-tertiary);font-family:inherit;font-size:12px;line-height:18px}",
990
+ ".dshCodexModelActions{justify-content:flex-end;border-top:1px solid var(--dsw-alias-border-l2);padding-top:20px}",
991
+ ".dshCodexNotices{display:flex;min-width:0;flex-direction:column;gap:10px;margin:0;padding:0;list-style:none}",
992
+ ".dshCodexNotices li,.dshCodexPrompt{box-sizing:border-box;min-width:0;border:1px solid var(--dsw-alias-border-l2);border-radius:12px;background:var(--dsw-alias-bg-layer-1);padding:14px 16px}",
993
+ ".dshCodexNotices a{display:inline-block;max-width:100%;margin-top:8px;color:var(--dsw-alias-state-business-primary)}",
994
+ ".dshCodexCode{display:flex;min-width:0;flex-wrap:wrap;align-items:center;gap:8px;margin-top:8px;color:var(--dsw-alias-label-secondary);font-size:12px}",
995
+ ".dshCodexCode code{min-width:0;max-width:100%;overflow-wrap:anywhere;word-break:break-all;color:var(--dsw-alias-label-primary);font-family:inherit;font-size:14px;line-height:22px;user-select:all}",
996
+ ".dshCodexPrompt{display:flex;flex-direction:column;gap:12px}",
997
+ ".dshCodexPrompt label{display:flex;min-width:0;flex-direction:column;gap:8px}",
998
+ ".dshCodexPrompt input,.dshCodexPrompt select{box-sizing:border-box;width:100%;min-width:0;height:38px;border:1px solid var(--dsw-alias-border-l2);border-radius:8px;background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;padding:0 10px}",
999
+ ".dshCodexError{margin:0;color:var(--dsw-alias-state-error-primary);font-size:12px;line-height:18px}",
1000
+ ".dshCodexSuccess{margin:0;color:var(--dsw-alias-state-success-primary);font-size:12px;line-height:18px}",
1001
+ "@media(max-width:760px){.dshCodexCard{padding:24px}.dshCodexModelList{grid-template-columns:1fr}.dshCodexModelActions{justify-content:flex-start}}",
1002
+ "@media(max-width:640px){.dshCodexAuthToolbar,.dshCodexHeader{align-items:stretch;flex-direction:column}.dshCodexAuthActions{margin-left:0;justify-content:flex-start}}",
1003
+ "@media(max-width:480px){[role=dialog]:has(.dshCodexPage){flex-direction:column}[role=dialog]:has(.dshCodexPage)>nav{box-sizing:border-box;width:100%;height:auto;flex:none;gap:0;overflow:hidden;padding:10px 12px 8px}[role=dialog]:has(.dshCodexPage)>nav>div:first-child{display:none}[role=dialog]:has(.dshCodexPage)>nav>div:last-child{display:flex;width:100%;flex-direction:row;gap:4px;overflow-x:auto;padding-bottom:2px}[role=dialog]:has(.dshCodexPage)>nav>div:last-child>*{flex:none}[role=dialog]:has(.dshCodexPage)>:not(nav){width:100%;min-width:0;min-height:0;overflow:hidden;flex:1 1 0%}.dshCodexPage{gap:20px;padding:0 0 28px}.dshCodexCard{border-radius:14px;padding:18px}.dshCodexActions{align-items:stretch;flex-direction:column}.dshCodexButton{width:100%}}",
1004
+ ].join("")
1005
+ const references = style[STYLE_REF_KEY]
1006
+ style[STYLE_REF_KEY] = Number.isSafeInteger(references) && references > 0
1007
+ ? references + 1
1008
+ : 1
1009
+ let active = true
1010
+ return () => {
1011
+ if (!active) return
1012
+ active = false
1013
+ const remaining = style[STYLE_REF_KEY]
1014
+ if (Number.isSafeInteger(remaining) && remaining > 1) {
1015
+ style[STYLE_REF_KEY] = remaining - 1
1016
+ return
1017
+ }
1018
+ delete style[STYLE_REF_KEY]
1019
+ if (typeof style.remove === "function") style.remove()
1020
+ else style.parentNode?.removeChild(style)
1021
+ }
1022
+ }
1023
+
1024
+ exports.AuthorizationSettings = AuthorizationSettings
1025
+ exports.CHANNEL = CHANNEL
1026
+ exports.CODEX_PROVIDER = CODEX_PROVIDER
1027
+ exports.CodexSettings = CodexSettings
1028
+ exports.ModelEnablementSettings = ModelEnablementSettings
1029
+ exports.NS = NS
1030
+ exports.apply = apply
1031
+ exports.createAuthorizationClient = createAuthorizationClient
1032
+ exports.createModelEnablementClient = createModelEnablementClient
1033
+ exports.inject = inject
1034
+ exports.safeQuotaSnapshot = safeQuotaSnapshot
1035
+ return module.exports
1036
+ },
1037
+ })