@eduwork/dsh-oidc 0.1.0 → 0.2.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 (58) hide show
  1. package/CHANGELOG.en.md +40 -1
  2. package/CHANGELOG.md +40 -1
  3. package/README.en.md +23 -21
  4. package/README.md +17 -15
  5. package/THIRD_PARTY_NOTICES.en.md +7 -5
  6. package/THIRD_PARTY_NOTICES.md +7 -5
  7. package/cordis.patch.yml +2 -1
  8. package/docs/EDUWORK-MIGRATION.en.md +14 -2
  9. package/docs/EDUWORK-MIGRATION.md +14 -2
  10. package/docs/account-extensions.en.md +40 -0
  11. package/docs/account-extensions.md +42 -0
  12. package/docs/architecture.en.md +9 -9
  13. package/docs/architecture.md +11 -10
  14. package/docs/compatibility.en.md +11 -2
  15. package/docs/compatibility.md +11 -2
  16. package/docs/desktop-host.en.md +69 -0
  17. package/docs/desktop-host.md +79 -0
  18. package/docs/development.en.md +42 -0
  19. package/docs/development.md +42 -0
  20. package/docs/dsh-integration.en.md +23 -7
  21. package/docs/dsh-integration.md +23 -7
  22. package/docs/ecnu-reference.en.md +3 -3
  23. package/docs/ecnu-reference.md +3 -3
  24. package/docs/enterprise-profile.en.md +19 -11
  25. package/docs/enterprise-profile.md +18 -10
  26. package/docs/getting-started.en.md +12 -12
  27. package/docs/getting-started.md +16 -14
  28. package/docs/key-binding-protocol.en.md +2 -2
  29. package/docs/key-binding-protocol.md +2 -2
  30. package/docs/oidc-interoperability.en.md +3 -3
  31. package/docs/oidc-interoperability.md +2 -2
  32. package/docs/public-resource-protocol.en.md +51 -0
  33. package/docs/public-resource-protocol.md +50 -0
  34. package/docs/release-checklist.en.md +4 -2
  35. package/docs/release-checklist.md +4 -2
  36. package/docs/server-integration-contract.en.md +124 -8
  37. package/docs/server-integration-contract.md +136 -8
  38. package/examples/ecnu.enterprise-profile.example.json +2 -2
  39. package/examples/enterprise-profile.example.json +78 -77
  40. package/examples/identity-only.example.json +10 -0
  41. package/examples/resources-discovery.example.json +20 -0
  42. package/lib/callback-page.js +42 -0
  43. package/lib/client.js +934 -294
  44. package/lib/desktop-oidc.js +205 -0
  45. package/lib/index.js +148 -18
  46. package/lib/oidc.js +314 -58
  47. package/lib/profile.js +53 -24
  48. package/lib/provider/core.js +76 -15
  49. package/lib/provider/index.js +33 -20
  50. package/lib/provider/transform-adapter.js +31 -10
  51. package/lib/resources.js +23 -0
  52. package/lib/typert-schemas.js +34 -3
  53. package/lib/typert.host.js +42 -36
  54. package/lib/typert.remote-client.js +41 -35
  55. package/package.json +100 -29
  56. package/protocol/openapi.yaml +2 -2
  57. package/protocol/resources.openapi.yaml +51 -0
  58. package/schema/enterprise-profile.v1alpha1.schema.json +193 -188
package/lib/client.js CHANGED
@@ -28,6 +28,7 @@ window.__ModuleLoader__.load({
28
28
  //#endregion
29
29
  let react = require("react");
30
30
  react = __toESM(react, 1);
31
+ let react_dom = require("react-dom");
31
32
  //#region node_modules/zod/v4/core/core.js
32
33
  var _a$1;
33
34
  function $constructor(name, initializer, params) {
@@ -4242,18 +4243,33 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4242
4243
  return /* @__PURE__ */ _superRefine(fn, params);
4243
4244
  }
4244
4245
  //#endregion
4245
- //#region src/client/remote.ts
4246
- const pkg = "@eduwork/dsh-oidc";
4247
- const source = {
4248
- file: "lib/index.js",
4249
- line: 1,
4250
- column: 1
4251
- };
4252
- const profileId = string().min(1).max(64);
4253
- const options = object({ allowProvision: boolean().optional() }).strict();
4254
- const baseURL = string().min(1).max(2048);
4255
- const modelMode = _enum(["discovery", "manual"]);
4256
- const brand = object({
4246
+ //#region src/host/typert-schemas.js
4247
+ const pkg$1 = "@eduwork/dsh-oidc";
4248
+ const profileIdSchema = string().min(1).max(64);
4249
+ const loginIdSchema = string().regex(/^[A-Za-z0-9_-]{32}$/);
4250
+ const reconcileOptionsSchema = object({ allowProvision: boolean().optional() }).strict();
4251
+ const baseURLSchema = string().min(1).max(2048);
4252
+ const modelCatalogModeSchema = _enum(["discovery", "manual"]);
4253
+ const configurationTargetSchema = _enum(["config", "examples"]);
4254
+ const openConfigurationResult = Object.freeze({
4255
+ mode: "strict",
4256
+ typeSymbol: `${pkg$1}#OpenConfigurationResult`,
4257
+ schema: object({ opened: literal(true) }).strict()
4258
+ });
4259
+ const enterpriseSelectionResult = Object.freeze({
4260
+ mode: "strict",
4261
+ typeSymbol: `${pkg$1}#EnterpriseSelectionResult`,
4262
+ schema: object({
4263
+ changed: boolean(),
4264
+ selection: object({
4265
+ provider: string(),
4266
+ model: string(),
4267
+ reasoningEffort: string().optional()
4268
+ }).strict().optional()
4269
+ }).strict()
4270
+ });
4271
+ const enterpriseSelectionOptionsSchema = object({ onlyIfMissing: boolean().optional() }).strict();
4272
+ const brandSchema = object({
4257
4273
  productName: string().optional(),
4258
4274
  organizationName: string().optional(),
4259
4275
  mark: string().optional(),
@@ -4263,11 +4279,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4263
4279
  loginDescription: string().optional(),
4264
4280
  supportURL: string().optional()
4265
4281
  }).strict();
4266
- const profile = object({
4282
+ const publicProfileSchema = object({
4267
4283
  id: string(),
4268
4284
  displayName: string(),
4269
4285
  organization: string(),
4270
- brand,
4286
+ brand: brandSchema,
4271
4287
  credentialRef: string(),
4272
4288
  provider: object({
4273
4289
  id: string(),
@@ -4277,9 +4293,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4277
4293
  name: string(),
4278
4294
  input: array(_enum(["text", "image"]))
4279
4295
  }).strict())
4280
- }).strict()
4296
+ }).strict().optional()
4281
4297
  }).strict();
4282
- const account = object({
4298
+ const accountSchema = object({
4283
4299
  profileID: string(),
4284
4300
  displayName: string(),
4285
4301
  organization: string(),
@@ -4292,7 +4308,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4292
4308
  credentialState: string().optional(),
4293
4309
  capabilities: array(string()).optional()
4294
4310
  }).strict();
4295
- const runtimeModels = array(object({
4311
+ const runtimeModelsSchema = array(object({
4296
4312
  id: string().min(1).max(256),
4297
4313
  name: string().max(256).optional(),
4298
4314
  upstreamModelID: string().max(256).optional(),
@@ -4305,95 +4321,190 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4305
4321
  "video"
4306
4322
  ])).optional(),
4307
4323
  reasoning: boolean().optional(),
4308
- reasoningEfforts: record(string(), string().nullable()).optional(),
4324
+ reasoningEfforts: union([literal(false), record(string(), string().nullable())]).optional(),
4325
+ defaultReasoningEffort: _enum([
4326
+ "off",
4327
+ "minimal",
4328
+ "low",
4329
+ "medium",
4330
+ "high",
4331
+ "xhigh",
4332
+ "max"
4333
+ ]).optional(),
4309
4334
  compat: record(string(), unknown()).optional()
4310
4335
  }).strict()).max(128);
4311
- const management = object({
4312
- schemaVersion: literal("dsh-oidc/management/v1alpha1"),
4313
- mode: _enum(["profile", "native"]),
4314
- activeProfileID: string(),
4315
- restartRequired: boolean(),
4316
- capabilities: object({
4317
- manageProfiles: boolean(),
4318
- manageModels: boolean(),
4319
- restart: boolean()
4320
- }).strict(),
4321
- profiles: array(object({
4322
- id: string(),
4336
+ const resourcesResult = Object.freeze({
4337
+ mode: "strict",
4338
+ typeSymbol: `${pkg$1}#Resources`,
4339
+ schema: object({
4340
+ profileID: string(),
4341
+ modelSource: string(),
4342
+ models: runtimeModelsSchema,
4343
+ issues: array(string())
4344
+ }).strict()
4345
+ });
4346
+ const managementProfileSchema = object({
4347
+ id: string(),
4348
+ displayName: string(),
4349
+ organization: string(),
4350
+ baseURL: string(),
4351
+ builtIn: boolean(),
4352
+ configured: boolean(),
4353
+ enabled: boolean(),
4354
+ providerID: string(),
4355
+ runtime: object({
4323
4356
  displayName: string(),
4324
- organization: string(),
4325
- baseURL: string(),
4326
- builtIn: boolean(),
4327
- configured: boolean(),
4328
- enabled: boolean(),
4329
- providerID: string(),
4330
- runtime: object({
4331
- displayName: string(),
4332
- modelSource: string().optional(),
4333
- reasoning: string().optional(),
4334
- defaultContextWindow: number().int().positive().optional(),
4335
- defaultMaxTokens: number().int().positive().optional(),
4336
- compat: record(string(), unknown()).optional(),
4337
- models: runtimeModels
4338
- }).strict()
4339
- }).strict())
4357
+ modelSource: string().optional(),
4358
+ reasoning: string().optional(),
4359
+ defaultContextWindow: number().int().positive().optional(),
4360
+ defaultMaxTokens: number().int().positive().optional(),
4361
+ compat: record(string(), unknown()).optional(),
4362
+ models: runtimeModelsSchema
4363
+ }).strict()
4340
4364
  }).strict();
4341
- const result = (typeSymbol, schema) => ({
4365
+ const configurationResult = Object.freeze({
4366
+ mode: "strict",
4367
+ typeSymbol: `${pkg$1}#Configuration`,
4368
+ schema: object({
4369
+ schemaVersion: literal("dsh-oidc/v1alpha1"),
4370
+ uiMode: _enum([
4371
+ "standard",
4372
+ "models-only",
4373
+ "external"
4374
+ ]),
4375
+ profiles: array(publicProfileSchema),
4376
+ manageProductBrand: boolean().optional(),
4377
+ configFile: object({
4378
+ path: string(),
4379
+ examplesPath: string(),
4380
+ canOpen: boolean()
4381
+ }).strict().optional()
4382
+ }).strict()
4383
+ });
4384
+ const managementResult = Object.freeze({
4385
+ mode: "strict",
4386
+ typeSymbol: `${pkg$1}#ManagementConfiguration`,
4387
+ schema: object({
4388
+ schemaVersion: literal("dsh-oidc/management/v1alpha1"),
4389
+ mode: _enum(["profile", "native"]),
4390
+ activeProfileID: string(),
4391
+ restartRequired: boolean(),
4392
+ capabilities: object({
4393
+ manageProfiles: boolean(),
4394
+ manageModels: boolean(),
4395
+ restart: boolean()
4396
+ }).strict(),
4397
+ profiles: array(managementProfileSchema)
4398
+ }).strict()
4399
+ });
4400
+ const restartResult = Object.freeze({
4401
+ mode: "strict",
4402
+ typeSymbol: `${pkg$1}#RestartResult`,
4403
+ schema: object({ restarting: literal(true) }).strict()
4404
+ });
4405
+ const accountResult = Object.freeze({
4342
4406
  mode: "strict",
4343
- typeSymbol,
4344
- schema
4407
+ typeSymbol: `${pkg$1}#AccountStatus`,
4408
+ schema: accountSchema
4409
+ });
4410
+ const beginResult = Object.freeze({
4411
+ mode: "strict",
4412
+ typeSymbol: `${pkg$1}#BeginResult`,
4413
+ schema: union([
4414
+ object({
4415
+ mode: literal("redirect"),
4416
+ authorizationURL: string().url()
4417
+ }).strict(),
4418
+ object({
4419
+ mode: literal("completed"),
4420
+ status: accountSchema
4421
+ }).strict(),
4422
+ object({
4423
+ mode: literal("external"),
4424
+ loginID: loginIdSchema,
4425
+ expiresAt: string().datetime()
4426
+ }).strict()
4427
+ ])
4345
4428
  });
4346
- const parameter = (name, schema, typeSymbol) => ({
4347
- name,
4348
- wire: name,
4349
- source: "json",
4350
- codec: result(typeSymbol, schema)
4429
+ const loginResult = Object.freeze({
4430
+ mode: "strict",
4431
+ typeSymbol: `${pkg$1}#LoginResult`,
4432
+ schema: object({
4433
+ loginID: loginIdSchema,
4434
+ profileID: profileIdSchema,
4435
+ expiresAt: string().datetime(),
4436
+ state: _enum([
4437
+ "pending",
4438
+ "completed",
4439
+ "cancelled",
4440
+ "expired",
4441
+ "failed"
4442
+ ]),
4443
+ status: accountSchema.optional(),
4444
+ errorCode: string().optional()
4445
+ }).strict()
4351
4446
  });
4352
- const descriptor = (method, parameters, output) => ({
4447
+ function jsonParameter(name, schema, typeSymbol) {
4448
+ return Object.freeze({
4449
+ name,
4450
+ wire: name,
4451
+ source: "json",
4452
+ codec: Object.freeze({
4453
+ mode: "strict",
4454
+ typeSymbol,
4455
+ schema
4456
+ })
4457
+ });
4458
+ }
4459
+ //#endregion
4460
+ //#region src/host/typert.remote-client.js
4461
+ const pkg = "@eduwork/dsh-oidc";
4462
+ const source = {
4463
+ file: "lib/index.js",
4464
+ line: 1,
4465
+ column: 1
4466
+ };
4467
+ const profile = () => jsonParameter("profileID", profileIdSchema, `${pkg}#ProfileID`);
4468
+ const login = () => jsonParameter("loginID", loginIdSchema, `${pkg}#LoginID`);
4469
+ const options = () => jsonParameter("options", reconcileOptionsSchema, `${pkg}#ReconcileOptions`);
4470
+ const baseURL = () => jsonParameter("baseURL", baseURLSchema, `${pkg}#BaseURL`);
4471
+ const modelMode = () => jsonParameter("modelMode", modelCatalogModeSchema, `${pkg}#ModelCatalogMode`);
4472
+ const models = () => jsonParameter("models", runtimeModelsSchema, `${pkg}#RuntimeModels`);
4473
+ const descriptor = (method, parameters, result) => ({
4353
4474
  id: `${pkg}#oidcAccounts/${method}`,
4354
4475
  service: "oidcAccounts",
4355
4476
  namespace: "oidcAccounts",
4356
4477
  method,
4357
4478
  invocation: { kind: "direct" },
4358
4479
  parameters,
4359
- result: output,
4480
+ result,
4360
4481
  sourceLocation: source
4361
4482
  });
4362
- const profileParameter = () => parameter("profileID", profileId, `${pkg}#ProfileID`);
4363
- var remote_default = {
4483
+ const TYPERT_REMOTE = {
4364
4484
  package: pkg,
4365
4485
  descriptors: [
4366
- descriptor("configuration", [], result(`${pkg}#Configuration`, object({
4367
- schemaVersion: literal("dsh-oidc/v1alpha1"),
4368
- uiMode: _enum([
4369
- "standard",
4370
- "models-only",
4371
- "external"
4372
- ]),
4373
- profiles: array(profile)
4374
- }).strict())),
4375
- descriptor("status", [profileParameter()], result(`${pkg}#AccountStatus`, account)),
4376
- descriptor("begin", [profileParameter()], result(`${pkg}#BeginResult`, union([object({
4377
- mode: literal("redirect"),
4378
- authorizationURL: string().url()
4379
- }).strict(), object({
4380
- mode: literal("completed"),
4381
- status: account
4382
- }).strict()]))),
4383
- descriptor("reconcile", [profileParameter(), parameter("options", options, `${pkg}#ReconcileOptions`)], result(`${pkg}#AccountStatus`, account)),
4384
- descriptor("logout", [profileParameter()], result(`${pkg}#AccountStatus`, account)),
4385
- descriptor("management", [], result(`${pkg}#ManagementConfiguration`, management)),
4386
- descriptor("activate", [profileParameter()], result(`${pkg}#ManagementConfiguration`, management)),
4387
- descriptor("configure", [profileParameter()], result(`${pkg}#ManagementConfiguration`, management)),
4388
- descriptor("addCustom", [parameter("baseURL", baseURL, `${pkg}#BaseURL`)], result(`${pkg}#ManagementConfiguration`, management)),
4389
- descriptor("updateCustom", [profileParameter(), parameter("baseURL", baseURL, `${pkg}#BaseURL`)], result(`${pkg}#ManagementConfiguration`, management)),
4390
- descriptor("removeProfile", [profileParameter()], result(`${pkg}#ManagementConfiguration`, management)),
4486
+ descriptor("configuration", [], configurationResult),
4487
+ descriptor("openConfiguration", [jsonParameter("target", configurationTargetSchema, `${pkg}#ConfigurationTarget`)], openConfigurationResult),
4488
+ descriptor("status", [profile()], accountResult),
4489
+ descriptor("resources", [profile()], resourcesResult),
4490
+ descriptor("begin", [profile()], beginResult),
4491
+ descriptor("loginStatus", [login()], loginResult),
4492
+ descriptor("cancelLogin", [login()], loginResult),
4493
+ descriptor("reconcile", [profile(), options()], accountResult),
4494
+ descriptor("selectEnterpriseModel", [profile(), jsonParameter("options", enterpriseSelectionOptionsSchema, `${pkg}#EnterpriseSelectionOptions`)], enterpriseSelectionResult),
4495
+ descriptor("logout", [profile()], accountResult),
4496
+ descriptor("management", [], managementResult),
4497
+ descriptor("activate", [profile()], managementResult),
4498
+ descriptor("configure", [profile()], managementResult),
4499
+ descriptor("addCustom", [baseURL()], managementResult),
4500
+ descriptor("updateCustom", [profile(), baseURL()], managementResult),
4501
+ descriptor("removeProfile", [profile()], managementResult),
4391
4502
  descriptor("configureModels", [
4392
- profileParameter(),
4393
- parameter("modelMode", modelMode, `${pkg}#ModelCatalogMode`),
4394
- parameter("models", runtimeModels, `${pkg}#RuntimeModels`)
4395
- ], result(`${pkg}#ManagementConfiguration`, management)),
4396
- descriptor("restart", [], result(`${pkg}#RestartResult`, object({ restarting: literal(true) }).strict()))
4503
+ profile(),
4504
+ modelMode(),
4505
+ models()
4506
+ ], managementResult),
4507
+ descriptor("restart", [], restartResult)
4397
4508
  ]
4398
4509
  };
4399
4510
  //#endregion
@@ -4412,13 +4523,15 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4412
4523
  const label = cleanText(statusLabel);
4413
4524
  return userName && organization ? `${organization} · ${label}` : label;
4414
4525
  }
4415
- const reasoningEffortOrder = [
4416
- "xhigh",
4417
- "max",
4418
- "high",
4526
+ const reasoningEffortOrder = Object.freeze([
4527
+ "off",
4528
+ "minimal",
4529
+ "low",
4419
4530
  "medium",
4420
- "low"
4421
- ];
4531
+ "high",
4532
+ "xhigh",
4533
+ "max"
4534
+ ]);
4422
4535
  function formatModelCapacity(value) {
4423
4536
  if (!Number.isFinite(value) || value <= 0) return "";
4424
4537
  if (value >= 1e6 && value % 1e6 === 0) return `${value / 1e6}M`;
@@ -4439,8 +4552,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4439
4552
  contextWindow: formatModelCapacity(model.contextWindow || provider.defaultContextWindow),
4440
4553
  maxTokens: formatModelCapacity(model.maxTokens || provider.defaultMaxTokens),
4441
4554
  input,
4442
- reasoningSupported: model.reasoning !== false,
4443
- reasoningEfforts: model.reasoning === false ? [] : efforts,
4555
+ reasoningSupported: model.reasoning !== false && model.reasoningEfforts !== false,
4556
+ reasoningEfforts: model.reasoning === false || model.reasoningEfforts === false ? [] : efforts,
4557
+ defaultReasoningEffort: model.reasoning === false || model.reasoningEfforts === false ? "" : cleanText(model.defaultReasoningEffort),
4444
4558
  multimodal: input.some((value) => value !== "text")
4445
4559
  };
4446
4560
  }
@@ -4454,8 +4568,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4454
4568
  maxTokens: model.maxTokens ? String(model.maxTokens) : "",
4455
4569
  input: Array.isArray(model.input) && model.input.length > 0 ? [...model.input] : ["text"],
4456
4570
  compat: model.compat && typeof model.compat === "object" && !Array.isArray(model.compat) ? { ...model.compat } : {},
4457
- reasoningSupported: existing ? model.reasoning !== false : false,
4458
- reasoning: model.reasoningEfforts && typeof model.reasoningEfforts === "object" ? Object.keys(model.reasoningEfforts) : []
4571
+ reasoningSupported: existing ? model.reasoning !== false && model.reasoningEfforts !== false : false,
4572
+ reasoning: model.reasoningEfforts && typeof model.reasoningEfforts === "object" ? Object.keys(model.reasoningEfforts) : [],
4573
+ defaultReasoningEffort: cleanText(model.defaultReasoningEffort)
4459
4574
  };
4460
4575
  }
4461
4576
  function serializeRuntimeModelDraft(draft) {
@@ -4474,8 +4589,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4474
4589
  "video"
4475
4590
  ].includes(value)))];
4476
4591
  if (input.length === 0) input.push("text");
4477
- const efforts = [...new Set(Array.isArray(draft.reasoning) ? draft.reasoning : [])];
4592
+ const efforts = [...new Set(Array.isArray(draft.reasoning) ? draft.reasoning : [])].filter((level) => reasoningEffortOrder.includes(level)).sort((left, right) => reasoningEffortOrder.indexOf(left) - reasoningEffortOrder.indexOf(right));
4478
4593
  const reasoningSupported = draft.reasoningSupported !== false;
4594
+ const defaultReasoningEffort = cleanText(draft.defaultReasoningEffort);
4595
+ if (defaultReasoningEffort && !efforts.includes(defaultReasoningEffort)) throw new Error("Default reasoning effort must be one of the selected reasoning efforts");
4479
4596
  const compat = draft.compat && typeof draft.compat === "object" && !Array.isArray(draft.compat) ? { ...draft.compat } : {};
4480
4597
  if (reasoningSupported) compat.supportsReasoningEffort = efforts.length > 0;
4481
4598
  else delete compat.supportsReasoningEffort;
@@ -4490,19 +4607,96 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4490
4607
  input,
4491
4608
  ...!reasoningSupported ? { reasoning: false } : {},
4492
4609
  ...reasoningSupported && efforts.length > 0 ? { reasoningEfforts: Object.fromEntries(efforts.map((level) => [level, level])) } : {},
4610
+ ...reasoningSupported && defaultReasoningEffort ? { defaultReasoningEffort } : {},
4493
4611
  ...Object.keys(compat).length === 0 ? {} : { compat }
4494
4612
  };
4495
4613
  }
4496
4614
  //#endregion
4615
+ //#region src/client/login-flow.js
4616
+ const wait = (ms, signal) => new Promise((resolve) => {
4617
+ const stop = () => {
4618
+ clearTimeout(timer);
4619
+ signal?.removeEventListener("abort", stop);
4620
+ resolve();
4621
+ };
4622
+ const timer = setTimeout(stop, ms);
4623
+ signal?.addEventListener("abort", stop, { once: true });
4624
+ if (signal?.aborted) stop();
4625
+ });
4626
+ /**
4627
+ * Browser navigation is kept for Web; desktop never navigates its renderer to the IdP.
4628
+ * @param {any} service
4629
+ * @param {string} profileID
4630
+ * @param {{signal?: AbortSignal, onPending?: (pending: boolean) => void, redirect?: (url: string) => void, interval?: number}} options
4631
+ */
4632
+ async function signIn(service, profileID, { signal, onPending = (pending = false) => {}, redirect = (url) => window.location.assign(url), interval = 1e3 } = {}) {
4633
+ const result = await service.begin(profileID);
4634
+ if (result.mode === "redirect") {
4635
+ if (!signal?.aborted) redirect(result.authorizationURL);
4636
+ return service.status(profileID);
4637
+ }
4638
+ if (result.mode === "completed") return result.status;
4639
+ if (result.mode !== "external") throw new Error("Unsupported organization sign-in response");
4640
+ const expires = Math.min(Date.parse(result.expiresAt), Date.now() + 6e5);
4641
+ let completed = false;
4642
+ onPending(true);
4643
+ try {
4644
+ while (!signal?.aborted && Number.isFinite(expires) && Date.now() < expires) {
4645
+ const next = await service.loginStatus(result.loginID);
4646
+ if (next.state === "completed") {
4647
+ completed = true;
4648
+ return next.status;
4649
+ }
4650
+ if (next.state === "cancelled") return service.status(profileID);
4651
+ if (next.state === "failed") throw Object.assign(/* @__PURE__ */ new Error("Organization sign-in did not complete. Please try again."), { code: next.errorCode });
4652
+ if (next.state === "expired") break;
4653
+ await wait(interval, signal);
4654
+ }
4655
+ if (signal?.aborted) {
4656
+ await service.cancelLogin(result.loginID);
4657
+ completed = true;
4658
+ return service.status(profileID);
4659
+ }
4660
+ throw Object.assign(/* @__PURE__ */ new Error("Organization sign-in expired. Please try again."), { code: "oidc_login_expired" });
4661
+ } finally {
4662
+ if (!completed) await service.cancelLogin(result.loginID).catch(() => {});
4663
+ onPending(false);
4664
+ }
4665
+ }
4666
+ //#endregion
4667
+ //#region src/client/use-sign-in.ts
4668
+ function useSignIn(service) {
4669
+ const controller = (0, react.useRef)(null);
4670
+ const [pending, setPending] = (0, react.useState)(false);
4671
+ (0, react.useEffect)(() => () => {
4672
+ controller.current?.abort();
4673
+ }, []);
4674
+ return {
4675
+ pending,
4676
+ cancel: () => controller.current?.abort(),
4677
+ begin: async (profileID) => {
4678
+ controller.current?.abort();
4679
+ const current = new AbortController();
4680
+ controller.current = current;
4681
+ return signIn(service, profileID, {
4682
+ signal: current.signal,
4683
+ onPending: (value) => setPending(Boolean(value))
4684
+ });
4685
+ }
4686
+ };
4687
+ }
4688
+ //#endregion
4497
4689
  //#region src/client/managed-provider.ts
4498
- const h$1 = react.default.createElement;
4690
+ const h$2 = react.default.createElement;
4499
4691
  const border$1 = "var(--dsw-alias-border-l2, #e8d9d2)";
4500
4692
  const textPrimary = "var(--dsw-alias-label-primary, #231a17)";
4501
4693
  const textSecondary = "var(--dsw-alias-label-secondary, #75635c)";
4502
4694
  const textTertiary = "var(--dsw-alias-label-tertiary, #8a766f)";
4503
4695
  const background = "var(--dsw-alias-bg-layer-1, #fffdfb)";
4504
- const isChinese = typeof navigator !== "undefined" && navigator.language.toLowerCase().startsWith("zh");
4505
- const copy = isChinese ? {
4696
+ const isChinese$1 = typeof navigator !== "undefined" && navigator.language.toLowerCase().startsWith("zh");
4697
+ const copy = isChinese$1 ? {
4698
+ waiting: "请在浏览器中完成登录…",
4699
+ cancelLogin: "取消登录",
4506
4700
  title: "学校 / 企业服务",
4507
4701
  description: "已验证机构和兼容协议服务使用统一入口管理;普通 API Key 提供方继续由 DSH 原生设置管理。",
4508
4702
  loading: "正在读取学校 / 企业服务…",
@@ -4553,6 +4747,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4553
4747
  maxOutput: "最大输出",
4554
4748
  modalities: "输入模态",
4555
4749
  reasoningLevels: "思考强度",
4750
+ defaultReasoning: "默认思考强度",
4556
4751
  supportsReasoning: "支持思考",
4557
4752
  addModel: "+ 添加模型",
4558
4753
  cancel: "取消",
@@ -4571,8 +4766,17 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4571
4766
  restart: "立即重启应用",
4572
4767
  emptyTitle: "尚未配置学校 / 企业服务",
4573
4768
  emptyHint: "添加只保存配置;需要使用时再显式启用。全局同时只会启用一个企业服务。",
4574
- profileNotice: "Web 版本从受信 Enterprise Profile 读取机构与模型目录;如需修改,请更新 Profile 后重启 DSH。"
4769
+ profileNotice: "应用从受信 Enterprise Profile 读取机构与模型目录;如需修改,请更新 Profile 后重启 DSH。",
4770
+ fileHint: "通过配置文件连接学校或企业服务。修改后退出应用,再重新启动。",
4771
+ openConfig: "打开配置文件",
4772
+ openExamples: "查看示例",
4773
+ configOpened: "已请求系统打开配置文件,修改后请退出并重新启动应用。",
4774
+ examplesOpened: "已请求系统打开示例目录。",
4775
+ fileFallback: "请在运行应用的电脑上打开:",
4776
+ examplesHint: "完整示例见同目录下的 examples 文件夹。"
4575
4777
  } : {
4778
+ waiting: "Complete sign-in in your browser…",
4779
+ cancelLogin: "Cancel sign-in",
4576
4780
  title: "Organization services",
4577
4781
  description: "Manage verified organizations and compatible enterprise services here; ordinary API-key providers remain in DSH model settings.",
4578
4782
  loading: "Loading organization services…",
@@ -4623,6 +4827,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4623
4827
  maxOutput: "Max output",
4624
4828
  modalities: "Input modalities",
4625
4829
  reasoningLevels: "Reasoning effort",
4830
+ defaultReasoning: "Default reasoning effort",
4626
4831
  supportsReasoning: "Supports reasoning",
4627
4832
  addModel: "+ Add model",
4628
4833
  cancel: "Cancel",
@@ -4641,7 +4846,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4641
4846
  restart: "Restart now",
4642
4847
  emptyTitle: "No organization service configured",
4643
4848
  emptyHint: "Adding a service only saves its configuration. Enable one explicitly when needed; only one service is active at a time.",
4644
- profileNotice: "The Web edition reads organizations and models from a trusted Enterprise Profile. Update the Profile and restart DSH to change them."
4849
+ profileNotice: "The app reads organizations and models from a trusted Enterprise Profile. Update the Profile and restart DSH to change them.",
4850
+ fileHint: "Connect an organization through the configuration file. Quit and relaunch after editing.",
4851
+ openConfig: "Open configuration file",
4852
+ openExamples: "View examples",
4853
+ configOpened: "The file was sent to your system editor. Quit and relaunch after editing.",
4854
+ examplesOpened: "The examples folder was sent to your system file manager.",
4855
+ fileFallback: "Open this file on the computer running the app: ",
4856
+ examplesHint: "Complete examples are in the examples folder beside it."
4645
4857
  };
4646
4858
  const buttonStyle = Object.freeze({
4647
4859
  border: `1px solid ${border$1}`,
@@ -4677,24 +4889,26 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4677
4889
  boxSizing: "border-box"
4678
4890
  });
4679
4891
  const sourceLabel = (value) => ({
4680
- preset: isChinese ? "内置审核目录" : "Reviewed catalog",
4681
- discovered: isChinese ? "服务端自动发现" : "Server discovery",
4682
- discovery: isChinese ? "等待服务端同步" : "Awaiting discovery",
4683
- manual: isChinese ? "本机手动配置" : "Local manual catalog",
4892
+ preset: isChinese$1 ? "内置审核目录" : "Reviewed catalog",
4893
+ discovered: isChinese$1 ? "服务端自动发现" : "Server discovery",
4894
+ discovery: isChinese$1 ? "等待服务端同步" : "Awaiting discovery",
4895
+ manual: isChinese$1 ? "本机手动配置" : "Local manual catalog",
4684
4896
  profile: copy.profileManaged
4685
- })[value] || (isChinese ? "提供方声明" : "Provider declaration");
4897
+ })[value] || (isChinese$1 ? "提供方声明" : "Provider declaration");
4686
4898
  const capabilityLabel = (value) => ({
4687
- xhigh: isChinese ? "极高" : "Extra high",
4688
- max: "Max",
4689
- high: isChinese ? "高" : "High",
4690
- medium: isChinese ? "中" : "Medium",
4691
- low: isChinese ? "低" : "Low"
4899
+ off: isChinese$1 ? "关闭" : "Off",
4900
+ minimal: isChinese$1 ? "极低" : "Minimal",
4901
+ low: isChinese$1 ? "低" : "Low",
4902
+ medium: isChinese$1 ? "中" : "Medium",
4903
+ high: isChinese$1 ? "高" : "High",
4904
+ xhigh: isChinese$1 ? "极高" : "Extra high",
4905
+ max: "Max"
4692
4906
  })[value] || value;
4693
4907
  const modalityLabel = (value) => ({
4694
- text: isChinese ? "文本" : "Text",
4695
- image: isChinese ? "图片" : "Image",
4696
- audio: isChinese ? "音频" : "Audio",
4697
- video: isChinese ? "视频" : "Video"
4908
+ text: isChinese$1 ? "文本" : "Text",
4909
+ image: isChinese$1 ? "图片" : "Image",
4910
+ audio: isChinese$1 ? "音频" : "Audio",
4911
+ video: isChinese$1 ? "视频" : "Video"
4698
4912
  })[value] || value;
4699
4913
  function ManagedProviderCard({ service, configuration }) {
4700
4914
  const [management, setManagement] = (0, react.useState)(null);
@@ -4736,18 +4950,32 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4736
4950
  return [profile.id, null];
4737
4951
  }
4738
4952
  }));
4739
- setStatuses(Object.fromEntries(rows));
4953
+ setStatuses(Object.fromEntries(rows.map(([id]) => [id, service.accountSnapshot(id)])));
4954
+ };
4955
+ const refreshResources = async (id) => {
4956
+ if (service.accountSnapshot(id)?.credentialReady) await service.resources(id);
4740
4957
  };
4741
4958
  const refresh = async () => {
4742
4959
  const next = await service.management();
4743
4960
  setManagement(next);
4744
4961
  setRestartRequired(next.restartRequired === true);
4745
4962
  await loadStatuses(next);
4963
+ for (const profile of next.profiles.filter((row) => row.enabled)) refreshResources(profile.id).then(() => {
4964
+ service.management().then(setManagement).catch(() => {});
4965
+ }).catch(() => {});
4746
4966
  return next;
4747
4967
  };
4748
4968
  (0, react.useEffect)(() => {
4749
4969
  refresh().catch((cause) => setError(cause?.message || String(cause)));
4750
4970
  }, []);
4971
+ (0, react.useEffect)(() => service.subscribeAccounts((id) => {
4972
+ const status = service.accountSnapshot(id);
4973
+ setStatuses((current) => ({
4974
+ ...current,
4975
+ [id]: status
4976
+ }));
4977
+ refreshResources(id).catch(() => {});
4978
+ }), [service]);
4751
4979
  const run = async (operation, message = "") => {
4752
4980
  setBusy(true);
4753
4981
  setError("");
@@ -4758,10 +4986,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4758
4986
  setManagement(result);
4759
4987
  setRestartRequired(result.restartRequired === true);
4760
4988
  await loadStatuses(result);
4761
- } else if (result?.profileID) setStatuses((current) => ({
4762
- ...current,
4763
- [result.profileID]: result
4764
- }));
4989
+ } else if (result?.profileID) {
4990
+ setStatuses((current) => ({
4991
+ ...current,
4992
+ [result.profileID]: service.accountSnapshot(result.profileID)
4993
+ }));
4994
+ await refreshResources(result.profileID);
4995
+ setManagement(await service.management());
4996
+ }
4765
4997
  if (message) setNotice(message);
4766
4998
  return result;
4767
4999
  } catch (cause) {
@@ -4771,22 +5003,17 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4771
5003
  setBusy(false);
4772
5004
  }
4773
5005
  };
4774
- const signIn = (profileID) => run(async () => {
4775
- const result = await service.begin(profileID);
4776
- if (result.mode === "redirect") {
4777
- window.location.assign(result.authorizationURL);
4778
- return null;
4779
- }
4780
- return result.status;
4781
- });
5006
+ const login = useSignIn(service);
5007
+ const signIn = (profileID) => run(() => login.begin(profileID));
4782
5008
  const connect = (profileID, status) => status?.state === "authenticated" || status?.state === "provision_required" ? run(() => service.reconcile(profileID, { allowProvision: true })) : signIn(profileID);
4783
- if (!management) return h$1("p", { style: {
5009
+ if (!management) return h$2("p", { style: {
4784
5010
  margin: "18px 0",
4785
5011
  color: textSecondary,
4786
5012
  fontSize: 12
4787
5013
  } }, copy.loading);
4788
- const canManageProfiles = management.capabilities.manageProfiles === true;
4789
- const canManageModels = management.capabilities.manageModels === true;
5014
+ const canManageProfiles = !configuration.configFile && management.capabilities.manageProfiles === true;
5015
+ const canManageModels = !configuration.configFile && management.capabilities.manageModels === true;
5016
+ const noModelsNotice = configuration.configFile ? isChinese$1 ? "尚未同步模型目录。登录后将读取服务端模型;也可在配置文件中声明模型目录。" : "No model catalog has been synced. Sign in to discover models, or declare them in the configuration file." : copy.noModels;
4790
5017
  const canRestart = management.capabilities.restart === true;
4791
5018
  const detail = management.profiles.find((profile) => profile.id === detailID) ?? null;
4792
5019
  const configured = management.profiles.filter((profile) => profile.configured);
@@ -4848,13 +5075,19 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4848
5075
  setModelDrafts([]);
4849
5076
  }
4850
5077
  };
4851
- const providerCards = configured.length > 0 ? h$1("div", { style: {
5078
+ const providerCards = configured.length > 0 ? h$2("div", { style: {
4852
5079
  display: "grid",
4853
5080
  gap: 10
4854
5081
  } }, ...configured.map((profile) => {
4855
5082
  const status = statuses[profile.id];
4856
- const connected = profile.enabled && status?.credentialReady === true;
4857
- const modelBadges = profile.runtime.models.length > 0 ? profile.runtime.models.map((model) => h$1("code", {
5083
+ const connected = profile.enabled && status?.state === "connected";
5084
+ const modelBadges = !profile.providerID ? [h$2("span", {
5085
+ key: "identity",
5086
+ style: {
5087
+ color: textSecondary,
5088
+ fontSize: 11
5089
+ }
5090
+ }, isChinese$1 ? "身份登录 · 模型可自行配置" : "Identity sign-in · configure your own models")] : profile.runtime.models.length > 0 ? profile.runtime.models.map((model) => h$2("code", {
4858
5091
  key: model.id,
4859
5092
  style: {
4860
5093
  borderRadius: 6,
@@ -4863,14 +5096,14 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4863
5096
  color: textSecondary,
4864
5097
  fontSize: 11
4865
5098
  }
4866
- }, model.id)) : [h$1("span", {
5099
+ }, model.id)) : [h$2("span", {
4867
5100
  key: "empty",
4868
5101
  style: {
4869
5102
  color: textTertiary,
4870
5103
  fontSize: 11
4871
5104
  }
4872
- }, copy.noModels)];
4873
- return h$1("article", {
5105
+ }, noModelsNotice)];
5106
+ return h$2("article", {
4874
5107
  key: profile.id,
4875
5108
  style: {
4876
5109
  border: `1px solid ${profile.enabled ? accent : border$1}`,
@@ -4878,85 +5111,85 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4878
5111
  background,
4879
5112
  padding: 15
4880
5113
  }
4881
- }, h$1("div", { style: {
5114
+ }, h$2("div", { style: {
4882
5115
  display: "flex",
4883
5116
  justifyContent: "space-between",
4884
5117
  alignItems: "flex-start",
4885
5118
  gap: 16
4886
- } }, h$1("div", { style: { minWidth: 0 } }, h$1("div", { style: {
5119
+ } }, h$2("div", { style: { minWidth: 0 } }, h$2("div", { style: {
4887
5120
  display: "flex",
4888
5121
  alignItems: "center",
4889
5122
  gap: 7,
4890
5123
  flexWrap: "wrap"
4891
- } }, h$1("strong", { style: { fontSize: 15 } }, profile.displayName), h$1("span", { style: {
5124
+ } }, h$2("strong", { style: { fontSize: 15 } }, profile.displayName), h$2("span", { style: {
4892
5125
  borderRadius: 999,
4893
5126
  padding: "2px 7px",
4894
5127
  background: profile.builtIn ? "#f5e9e8" : "#eef1f6",
4895
5128
  color: profile.builtIn ? accent : "#536174",
4896
5129
  fontSize: 11
4897
- } }, profile.builtIn ? copy.verified : copy.custom), h$1("span", { style: {
5130
+ } }, configuration.configFile ? isChinese$1 ? "配置文件" : "Configuration file" : profile.builtIn ? copy.verified : copy.custom), h$2("span", { style: {
4898
5131
  borderRadius: 999,
4899
5132
  padding: "2px 7px",
4900
5133
  border: `1px solid ${connected ? "#b9d9c8" : border$1}`,
4901
5134
  color: connected ? "#357a55" : profile.enabled ? accent : textTertiary,
4902
5135
  fontSize: 11
4903
- } }, connected ? copy.connected : profile.enabled ? copy.enabled : copy.disabled)), h$1("div", { style: {
5136
+ } }, connected ? copy.connected : profile.enabled ? copy.enabled : copy.disabled)), h$2("div", { style: {
4904
5137
  marginTop: 6,
4905
5138
  color: textSecondary,
4906
5139
  fontSize: 11,
4907
5140
  overflowWrap: "anywhere"
4908
- } }, profile.baseURL), h$1("div", { style: {
5141
+ } }, profile.baseURL), h$2("div", { style: {
4909
5142
  display: "flex",
4910
5143
  gap: 6,
4911
5144
  marginTop: 8,
4912
5145
  flexWrap: "wrap"
4913
- } }, ...modelBadges)), h$1("div", { style: {
5146
+ } }, ...modelBadges)), h$2("div", { style: {
4914
5147
  display: "flex",
4915
5148
  gap: 7,
4916
5149
  flex: "none",
4917
5150
  flexWrap: "wrap",
4918
5151
  justifyContent: "flex-end"
4919
- } }, h$1("button", {
5152
+ } }, h$2("button", {
4920
5153
  type: "button",
4921
5154
  disabled: busy,
4922
5155
  style: buttonStyle,
4923
5156
  onClick: () => openDetail(profile)
4924
- }, copy.configure), canManageProfiles && !profile.enabled && h$1("button", {
5157
+ }, canManageProfiles || canManageModels ? copy.configure : isChinese$1 ? "查看详情" : "View details"), canManageProfiles && !profile.enabled && h$2("button", {
4925
5158
  type: "button",
4926
5159
  disabled: busy,
4927
5160
  style: primaryStyle,
4928
5161
  onClick: () => enable(profile.id)
4929
- }, copy.enable), profile.enabled && !restartRequired && !connected && h$1("button", {
5162
+ }, copy.enable), profile.enabled && !restartRequired && !connected && h$2("button", {
4930
5163
  type: "button",
4931
5164
  disabled: busy,
4932
5165
  style: primaryStyle,
4933
5166
  onClick: () => connect(profile.id, status)
4934
- }, busy ? copy.processing : status?.state === "authenticated" || status?.state === "provision_required" ? copy.provision : copy.login), profile.enabled && !restartRequired && connected && h$1("button", {
5167
+ }, busy ? copy.processing : status?.state === "authenticated" || status?.state === "provision_required" ? copy.provision : copy.login), profile.enabled && !restartRequired && connected && h$2("button", {
4935
5168
  type: "button",
4936
5169
  disabled: busy,
4937
5170
  style: buttonStyle,
4938
5171
  onClick: () => run(() => service.reconcile(profile.id, { allowProvision: false }))
4939
5172
  }, copy.refresh))));
4940
- })) : h$1("div", { style: {
5173
+ })) : h$2("div", { style: {
4941
5174
  padding: 18,
4942
5175
  border: `1px dashed ${border$1}`,
4943
5176
  borderRadius: 13,
4944
5177
  background
4945
- } }, h$1("strong", { style: {
5178
+ } }, h$2("strong", { style: {
4946
5179
  display: "block",
4947
5180
  fontSize: 14
4948
- } }, copy.emptyTitle), h$1("p", { style: {
5181
+ } }, copy.emptyTitle), h$2("p", { style: {
4949
5182
  margin: "6px 0 0",
4950
5183
  color: textSecondary,
4951
5184
  fontSize: 12,
4952
5185
  lineHeight: 1.55
4953
- } }, copy.emptyHint));
4954
- const addDialog = addOpen && h$1("div", {
5186
+ } }, configuration.configFile ? isChinese$1 ? "参考上方示例,在配置文件的 organizations 中填写企业信息,保存并退出后重新启动。也可以直接在模型设置中使用自己的 API Key。" : "Use the examples above to fill in organizations, save, quit and relaunch. You can also use your own API key in model settings." : copy.emptyHint));
5187
+ const addDialog = addOpen && h$2("div", {
4955
5188
  style: panelStyle,
4956
5189
  onMouseDown: (event) => {
4957
5190
  if (event.target === event.currentTarget && !busy) setAddOpen(false);
4958
5191
  }
4959
- }, h$1("div", {
5192
+ }, h$2("div", {
4960
5193
  role: "dialog",
4961
5194
  "aria-modal": "true",
4962
5195
  "aria-label": copy.addTitle,
@@ -4964,28 +5197,28 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4964
5197
  ...cardStyle,
4965
5198
  width: "min(660px, calc(100vw - 40px))"
4966
5199
  }
4967
- }, h$1("div", { style: {
5200
+ }, h$2("div", { style: {
4968
5201
  display: "flex",
4969
5202
  justifyContent: "space-between",
4970
5203
  gap: 14
4971
- } }, h$1("div", null, h$1("h2", { style: {
5204
+ } }, h$2("div", null, h$2("h2", { style: {
4972
5205
  margin: "0 0 4px",
4973
5206
  fontSize: 22
4974
- } }, copy.addTitle), h$1("p", { style: {
5207
+ } }, copy.addTitle), h$2("p", { style: {
4975
5208
  margin: 0,
4976
5209
  color: textSecondary,
4977
5210
  fontSize: 12
4978
- } }, copy.addDescription)), h$1("button", {
5211
+ } }, copy.addDescription)), h$2("button", {
4979
5212
  type: "button",
4980
5213
  disabled: busy,
4981
5214
  "aria-label": copy.close,
4982
5215
  onClick: () => setAddOpen(false),
4983
5216
  style: buttonStyle
4984
- }, "×")), available.length > 0 && h$1("div", { style: {
5217
+ }, "×")), available.length > 0 && h$2("div", { style: {
4985
5218
  display: "grid",
4986
5219
  gap: 8,
4987
5220
  marginTop: 18
4988
- } }, ...available.map((profile) => h$1("article", {
5221
+ } }, ...available.map((profile) => h$2("article", {
4989
5222
  key: profile.id,
4990
5223
  style: {
4991
5224
  display: "flex",
@@ -4995,60 +5228,60 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
4995
5228
  borderRadius: 11,
4996
5229
  padding: 13
4997
5230
  }
4998
- }, h$1("div", null, h$1("strong", null, profile.displayName), h$1("div", { style: {
5231
+ }, h$2("div", null, h$2("strong", null, profile.displayName), h$2("div", { style: {
4999
5232
  marginTop: 4,
5000
5233
  color: textTertiary,
5001
5234
  fontSize: 11
5002
- } }, profile.baseURL)), h$1("button", {
5235
+ } }, profile.baseURL)), h$2("button", {
5003
5236
  type: "button",
5004
5237
  disabled: busy,
5005
5238
  style: primaryStyle,
5006
5239
  onClick: () => run(() => service.configure(profile.id)).then(() => setAddOpen(false))
5007
- }, copy.addVerified)))), h$1("form", {
5240
+ }, copy.addVerified)))), h$2("form", {
5008
5241
  onSubmit: addCustom,
5009
5242
  style: {
5010
5243
  marginTop: 20,
5011
5244
  paddingTop: 17,
5012
5245
  borderTop: `1px solid ${border$1}`
5013
5246
  }
5014
- }, h$1("strong", { style: {
5247
+ }, h$2("strong", { style: {
5015
5248
  display: "block",
5016
5249
  fontSize: 13
5017
- } }, copy.compatible), h$1("p", { style: {
5250
+ } }, copy.compatible), h$2("p", { style: {
5018
5251
  margin: "5px 0 10px",
5019
5252
  color: textSecondary,
5020
5253
  fontSize: 12
5021
- } }, copy.compatibleHint), h$1("div", { style: {
5254
+ } }, copy.compatibleHint), h$2("div", { style: {
5022
5255
  display: "flex",
5023
5256
  gap: 8
5024
- } }, h$1("input", {
5257
+ } }, h$2("input", {
5025
5258
  type: "url",
5026
5259
  required: true,
5027
5260
  value: baseURL,
5028
5261
  placeholder: "https://ai.example.edu",
5029
5262
  onChange: (event) => setBaseURL(event.currentTarget.value),
5030
5263
  style: inputStyle
5031
- }), h$1("button", {
5264
+ }), h$2("button", {
5032
5265
  type: "submit",
5033
5266
  disabled: busy,
5034
5267
  style: primaryStyle
5035
5268
  }, busy ? copy.saving : copy.saveService)))));
5036
- const modelEditors = modelDrafts.map((draft, index) => h$1("article", {
5269
+ const modelEditors = modelDrafts.map((draft, index) => h$2("article", {
5037
5270
  key: index,
5038
5271
  style: {
5039
5272
  border: `1px solid ${border$1}`,
5040
5273
  borderRadius: 11,
5041
5274
  padding: 13
5042
5275
  }
5043
- }, h$1("div", { style: {
5276
+ }, h$2("div", { style: {
5044
5277
  display: "flex",
5045
5278
  justifyContent: "space-between",
5046
5279
  marginBottom: 10
5047
- } }, h$1("strong", null, `${copy.model} ${index + 1}`), modelDrafts.length > 1 && h$1("button", {
5280
+ } }, h$2("strong", null, `${copy.model} ${index + 1}`), modelDrafts.length > 1 && h$2("button", {
5048
5281
  type: "button",
5049
5282
  style: buttonStyle,
5050
5283
  onClick: () => setModelDrafts((current) => current.filter((_, candidate) => candidate !== index))
5051
- }, copy.remove)), h$1("div", { style: {
5284
+ }, copy.remove)), h$2("div", { style: {
5052
5285
  display: "grid",
5053
5286
  gridTemplateColumns: "repeat(2, minmax(0, 1fr))",
5054
5287
  gap: 9
@@ -5058,10 +5291,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5058
5291
  [copy.baseModelID, "upstreamModelID"],
5059
5292
  [copy.contextWindow, "contextWindow"],
5060
5293
  [copy.maxOutput, "maxTokens"]
5061
- ].map(([label, field]) => h$1("label", {
5294
+ ].map(([label, field]) => h$2("label", {
5062
5295
  key: field,
5063
5296
  style: { fontSize: 11 }
5064
- }, label, h$1("input", {
5297
+ }, label, h$2("input", {
5065
5298
  required: field === "id",
5066
5299
  value: draft[field],
5067
5300
  onChange: (event) => updateDraft(index, { [field]: event.currentTarget.value }),
@@ -5069,10 +5302,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5069
5302
  ...inputStyle,
5070
5303
  marginTop: 5
5071
5304
  }
5072
- })))), h$1("div", { style: {
5305
+ })))), h$2("div", { style: {
5073
5306
  marginTop: 10,
5074
5307
  fontSize: 11
5075
- } }, h$1("span", { style: {
5308
+ } }, h$2("span", { style: {
5076
5309
  marginRight: 10,
5077
5310
  color: textSecondary
5078
5311
  } }, copy.modalities), ...[
@@ -5080,69 +5313,77 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5080
5313
  "image",
5081
5314
  "audio",
5082
5315
  "video"
5083
- ].map((value) => h$1("label", {
5316
+ ].map((value) => h$2("label", {
5084
5317
  key: value,
5085
5318
  style: { marginRight: 11 }
5086
- }, h$1("input", {
5319
+ }, h$2("input", {
5087
5320
  type: "checkbox",
5088
5321
  checked: draft.input.includes(value),
5089
5322
  onChange: () => toggleDraft(index, "input", value)
5090
- }), ` ${modalityLabel(value)}`))), h$1("div", { style: {
5323
+ }), ` ${modalityLabel(value)}`))), h$2("div", { style: {
5091
5324
  marginTop: 8,
5092
5325
  fontSize: 11
5093
- } }, h$1("span", { style: {
5326
+ } }, h$2("span", { style: {
5094
5327
  marginRight: 10,
5095
5328
  color: textSecondary
5096
- } }, copy.reasoningLevels), h$1("label", { style: { marginRight: 12 } }, h$1("input", {
5329
+ } }, copy.reasoningLevels), h$2("label", { style: { marginRight: 12 } }, h$2("input", {
5097
5330
  type: "checkbox",
5098
5331
  checked: draft.reasoningSupported,
5099
5332
  onChange: (event) => updateDraft(index, {
5100
5333
  reasoningSupported: event.currentTarget.checked,
5101
5334
  reasoning: event.currentTarget.checked ? draft.reasoning : []
5102
5335
  })
5103
- }), ` ${copy.supportsReasoning}`), ...draft.reasoningSupported ? [
5104
- "xhigh",
5105
- "max",
5106
- "high",
5107
- "medium",
5108
- "low"
5109
- ].map((value) => h$1("label", {
5336
+ }), ` ${copy.supportsReasoning}`), ...draft.reasoningSupported ? reasoningEffortOrder.map((value) => h$2("label", {
5110
5337
  key: value,
5111
5338
  style: { marginRight: 11 }
5112
- }, h$1("input", {
5339
+ }, h$2("input", {
5113
5340
  type: "checkbox",
5114
5341
  checked: draft.reasoning.includes(value),
5115
5342
  onChange: () => toggleDraft(index, "reasoning", value)
5116
- }), ` ${capabilityLabel(value)}`)) : [])));
5117
- const detailDialog = detail && h$1("div", {
5343
+ }), ` ${capabilityLabel(value)}`)) : []), draft.reasoningSupported && draft.reasoning.length > 0 && h$2("label", { style: {
5344
+ display: "block",
5345
+ marginTop: 9,
5346
+ fontSize: 11
5347
+ } }, copy.defaultReasoning, h$2("select", {
5348
+ value: draft.reasoning.includes(draft.defaultReasoningEffort) ? draft.defaultReasoningEffort : "",
5349
+ onChange: (event) => updateDraft(index, { defaultReasoningEffort: event.currentTarget.value }),
5350
+ style: {
5351
+ ...inputStyle,
5352
+ marginTop: 5
5353
+ }
5354
+ }, h$2("option", { value: "" }, copy.decidedByService), ...draft.reasoning.slice().sort((left, right) => reasoningEffortOrder.indexOf(left) - reasoningEffortOrder.indexOf(right)).map((value) => h$2("option", {
5355
+ key: value,
5356
+ value
5357
+ }, capabilityLabel(value)))))));
5358
+ const detailDialog = detail && h$2("div", {
5118
5359
  style: panelStyle,
5119
5360
  onMouseDown: (event) => {
5120
5361
  if (event.target === event.currentTarget && !busy) setDetailID("");
5121
5362
  }
5122
- }, h$1("div", {
5363
+ }, h$2("div", {
5123
5364
  role: "dialog",
5124
5365
  "aria-modal": "true",
5125
5366
  "aria-label": `${detail.displayName} ${copy.configure}`,
5126
5367
  style: cardStyle
5127
- }, h$1("div", { style: {
5368
+ }, h$2("div", { style: {
5128
5369
  display: "flex",
5129
5370
  justifyContent: "space-between",
5130
5371
  gap: 14
5131
- } }, h$1("div", { style: { minWidth: 0 } }, h$1("h2", { style: {
5372
+ } }, h$2("div", { style: { minWidth: 0 } }, h$2("h2", { style: {
5132
5373
  margin: "0 0 4px",
5133
5374
  fontSize: 22
5134
- } }, detail.displayName), h$1("p", { style: {
5375
+ } }, detail.displayName), h$2("p", { style: {
5135
5376
  margin: 0,
5136
5377
  color: textSecondary,
5137
5378
  fontSize: 12,
5138
5379
  overflowWrap: "anywhere"
5139
- } }, `${detail.organization} · ${detail.baseURL}`)), h$1("button", {
5380
+ } }, `${detail.organization} · ${detail.baseURL}`)), h$2("button", {
5140
5381
  type: "button",
5141
5382
  disabled: busy,
5142
5383
  "aria-label": copy.close,
5143
5384
  onClick: () => setDetailID(""),
5144
5385
  style: buttonStyle
5145
- }, "×")), h$1("div", { style: {
5386
+ }, "×")), h$2("div", { style: {
5146
5387
  display: "grid",
5147
5388
  gridTemplateColumns: "repeat(auto-fit, minmax(145px, 1fr))",
5148
5389
  gap: 8,
@@ -5152,22 +5393,22 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5152
5393
  [copy.modelCatalog, sourceLabel(detail.runtime.modelSource)],
5153
5394
  [copy.defaultContext, modelCapabilitySummary({}, detail.runtime).contextWindow],
5154
5395
  [copy.defaultOutput, modelCapabilitySummary({}, detail.runtime).maxTokens]
5155
- ].map(([label, value]) => h$1("div", {
5396
+ ].map(([label, value]) => h$2("div", {
5156
5397
  key: label,
5157
5398
  style: {
5158
5399
  border: `1px solid ${border$1}`,
5159
5400
  borderRadius: 9,
5160
5401
  padding: 10
5161
5402
  }
5162
- }, h$1("span", { style: {
5403
+ }, h$2("span", { style: {
5163
5404
  display: "block",
5164
5405
  color: textTertiary,
5165
5406
  fontSize: 10
5166
- } }, label), h$1("strong", { style: {
5407
+ } }, label), h$2("strong", { style: {
5167
5408
  display: "block",
5168
5409
  marginTop: 4,
5169
5410
  fontSize: 12
5170
- } }, value || copy.unspecified)))), canManageProfiles && !detail.builtIn && h$1("form", {
5411
+ } }, value || copy.unspecified)))), canManageProfiles && !detail.builtIn && h$2("form", {
5171
5412
  onSubmit: updateCustom,
5172
5413
  style: {
5173
5414
  marginTop: 16,
@@ -5175,41 +5416,41 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5175
5416
  border: `1px solid ${border$1}`,
5176
5417
  borderRadius: 10
5177
5418
  }
5178
- }, h$1("strong", { style: { fontSize: 12 } }, copy.editAddress), h$1("p", { style: {
5419
+ }, h$2("strong", { style: { fontSize: 12 } }, copy.editAddress), h$2("p", { style: {
5179
5420
  margin: "4px 0 9px",
5180
5421
  color: textTertiary,
5181
5422
  fontSize: 11
5182
- } }, copy.editAddressHint), h$1("div", { style: {
5423
+ } }, copy.editAddressHint), h$2("div", { style: {
5183
5424
  display: "flex",
5184
5425
  gap: 8
5185
- } }, h$1("input", {
5426
+ } }, h$2("input", {
5186
5427
  type: "url",
5187
5428
  required: true,
5188
5429
  value: editBaseURL,
5189
5430
  onChange: (event) => setEditBaseURL(event.currentTarget.value),
5190
5431
  style: inputStyle
5191
- }), h$1("button", {
5432
+ }), h$2("button", {
5192
5433
  type: "submit",
5193
5434
  disabled: busy || editBaseURL === detail.baseURL,
5194
5435
  style: buttonStyle
5195
- }, copy.save))), h$1("div", { style: {
5436
+ }, copy.save))), h$2("div", { style: {
5196
5437
  marginTop: 20,
5197
5438
  display: "flex",
5198
5439
  justifyContent: "space-between",
5199
5440
  gap: 12
5200
- } }, h$1("div", null, h$1("strong", { style: { fontSize: 14 } }, copy.modelCapability), h$1("p", { style: {
5441
+ } }, h$2("div", null, h$2("strong", { style: { fontSize: 14 } }, copy.modelCapability), h$2("p", { style: {
5201
5442
  margin: "4px 0 0",
5202
5443
  color: textTertiary,
5203
5444
  fontSize: 11
5204
- } }, canManageModels && !detail.builtIn ? copy.customModels : copy.readonlyModels)), canManageModels && !detail.builtIn && h$1("div", { style: {
5445
+ } }, canManageModels && !detail.builtIn ? copy.customModels : copy.readonlyModels)), canManageModels && !detail.builtIn && h$2("div", { style: {
5205
5446
  display: "flex",
5206
5447
  gap: 7
5207
- } }, h$1("button", {
5448
+ } }, h$2("button", {
5208
5449
  type: "button",
5209
5450
  disabled: busy,
5210
5451
  style: buttonStyle,
5211
5452
  onClick: useDiscovery
5212
- }, copy.automatic), h$1("button", {
5453
+ }, copy.automatic), h$2("button", {
5213
5454
  type: "button",
5214
5455
  disabled: busy,
5215
5456
  style: editingModels ? primaryStyle : buttonStyle,
@@ -5217,38 +5458,38 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5217
5458
  setModelDrafts(detail.runtime.models.map(runtimeModelDraft).concat(detail.runtime.models.length ? [] : [runtimeModelDraft()]));
5218
5459
  setEditingModels(true);
5219
5460
  }
5220
- }, copy.manual))), !editingModels && h$1("div", { style: {
5461
+ }, copy.manual))), !editingModels && h$2("div", { style: {
5221
5462
  display: "grid",
5222
5463
  gap: 9,
5223
5464
  marginTop: 11
5224
5465
  } }, ...detail.runtime.models.length > 0 ? detail.runtime.models.map((model) => {
5225
5466
  const summary = modelCapabilitySummary(model, detail.runtime);
5226
- return h$1("article", {
5467
+ return h$2("article", {
5227
5468
  key: model.id,
5228
5469
  style: {
5229
5470
  border: `1px solid ${border$1}`,
5230
5471
  borderRadius: 11,
5231
5472
  padding: 13
5232
5473
  }
5233
- }, h$1("div", { style: {
5474
+ }, h$2("div", { style: {
5234
5475
  display: "flex",
5235
5476
  gap: 8,
5236
5477
  flexWrap: "wrap"
5237
- } }, h$1("strong", null, summary.name), summary.name !== summary.id && h$1("code", { style: {
5478
+ } }, h$2("strong", null, summary.name), summary.name !== summary.id && h$2("code", { style: {
5238
5479
  color: textTertiary,
5239
5480
  fontSize: 11
5240
- } }, summary.id), summary.multimodal && h$1("span", { style: {
5481
+ } }, summary.id), summary.multimodal && h$2("span", { style: {
5241
5482
  color: "#355c91",
5242
5483
  fontSize: 10
5243
- } }, isChinese ? "多模态" : "Multimodal")), h$1("div", { style: {
5484
+ } }, isChinese$1 ? "多模态" : "Multimodal")), h$2("div", { style: {
5244
5485
  display: "grid",
5245
5486
  gridTemplateColumns: "repeat(auto-fit, minmax(125px, 1fr))",
5246
5487
  gap: 8,
5247
5488
  marginTop: 10,
5248
5489
  color: textSecondary,
5249
5490
  fontSize: 11
5250
- } }, summary.upstreamModelID && h$1("span", null, `${copy.baseModel}:${summary.upstreamModelID}`), h$1("span", null, `${copy.context}:${summary.contextWindow || copy.unspecified}`), h$1("span", null, `${copy.output}:${summary.maxTokens || copy.unspecified}`), h$1("span", null, `${copy.input}:${summary.input.map(modalityLabel).join("、")}`), h$1("span", null, `${copy.reasoning}:${!summary.reasoningSupported ? copy.unsupported : summary.reasoningEfforts.length ? summary.reasoningEfforts.map(capabilityLabel).join("、") : copy.decidedByService}`)));
5251
- }) : [h$1("div", {
5491
+ } }, summary.upstreamModelID && h$2("span", null, `${copy.baseModel}:${summary.upstreamModelID}`), h$2("span", null, `${copy.context}:${summary.contextWindow || copy.unspecified}`), h$2("span", null, `${copy.output}:${summary.maxTokens || copy.unspecified}`), h$2("span", null, `${copy.input}:${summary.input.map(modalityLabel).join("、")}`), h$2("span", null, `${copy.reasoning}:${!summary.reasoningSupported ? copy.unsupported : summary.reasoningEfforts.length ? summary.reasoningEfforts.map(capabilityLabel).join("、") : copy.decidedByService}`)));
5492
+ }) : [h$2("div", {
5252
5493
  key: "empty",
5253
5494
  style: {
5254
5495
  border: `1px dashed ${border$1}`,
@@ -5257,42 +5498,42 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5257
5498
  color: textTertiary,
5258
5499
  fontSize: 12
5259
5500
  }
5260
- }, copy.noModels)]), editingModels && h$1("form", {
5501
+ }, noModelsNotice)]), editingModels && h$2("form", {
5261
5502
  onSubmit: saveModels,
5262
5503
  style: { marginTop: 11 }
5263
- }, h$1("div", { style: {
5504
+ }, h$2("div", { style: {
5264
5505
  display: "grid",
5265
5506
  gap: 10
5266
- } }, ...modelEditors), h$1("div", { style: {
5507
+ } }, ...modelEditors), h$2("div", { style: {
5267
5508
  display: "flex",
5268
5509
  justifyContent: "space-between",
5269
5510
  marginTop: 11
5270
- } }, h$1("button", {
5511
+ } }, h$2("button", {
5271
5512
  type: "button",
5272
5513
  style: buttonStyle,
5273
5514
  onClick: () => setModelDrafts((current) => [...current, runtimeModelDraft()])
5274
- }, copy.addModel), h$1("div", { style: {
5515
+ }, copy.addModel), h$2("div", { style: {
5275
5516
  display: "flex",
5276
5517
  gap: 8
5277
- } }, h$1("button", {
5518
+ } }, h$2("button", {
5278
5519
  type: "button",
5279
5520
  style: buttonStyle,
5280
5521
  onClick: () => setEditingModels(false)
5281
- }, copy.cancel), h$1("button", {
5522
+ }, copy.cancel), h$2("button", {
5282
5523
  type: "submit",
5283
5524
  disabled: busy,
5284
5525
  style: primaryStyle
5285
- }, copy.saveModels)))), canManageProfiles && h$1("div", { style: {
5526
+ }, copy.saveModels)))), canManageProfiles && h$2("div", { style: {
5286
5527
  display: "flex",
5287
5528
  justifyContent: "space-between",
5288
5529
  gap: 10,
5289
5530
  marginTop: 18,
5290
5531
  paddingTop: 14,
5291
5532
  borderTop: `1px solid ${border$1}`
5292
- } }, h$1("div", null, removeConfirmID === detail.id ? h$1("span", { style: {
5533
+ } }, h$2("div", null, removeConfirmID === detail.id ? h$2("span", { style: {
5293
5534
  color: "#a82332",
5294
5535
  fontSize: 11
5295
- } }, detail.builtIn ? copy.confirmRemove : copy.confirmDelete) : h$1("button", {
5536
+ } }, detail.builtIn ? copy.confirmRemove : copy.confirmDelete) : h$2("button", {
5296
5537
  type: "button",
5297
5538
  disabled: busy,
5298
5539
  style: {
@@ -5300,42 +5541,42 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5300
5541
  color: "#a82332"
5301
5542
  },
5302
5543
  onClick: () => setRemoveConfirmID(detail.id)
5303
- }, detail.builtIn ? copy.removeService : copy.deleteService), removeConfirmID === detail.id && h$1("span", { style: { marginLeft: 8 } }, h$1("button", {
5544
+ }, detail.builtIn ? copy.removeService : copy.deleteService), removeConfirmID === detail.id && h$2("span", { style: { marginLeft: 8 } }, h$2("button", {
5304
5545
  type: "button",
5305
5546
  style: buttonStyle,
5306
5547
  onClick: () => setRemoveConfirmID("")
5307
- }, copy.cancel), h$1("button", {
5548
+ }, copy.cancel), h$2("button", {
5308
5549
  type: "button",
5309
5550
  style: {
5310
5551
  ...primaryStyle,
5311
5552
  marginLeft: 6
5312
5553
  },
5313
5554
  onClick: () => removeProfile(detail)
5314
- }, copy.confirm))), detail.enabled ? h$1("button", {
5555
+ }, copy.confirm))), detail.enabled ? h$2("button", {
5315
5556
  type: "button",
5316
5557
  disabled: busy,
5317
5558
  style: buttonStyle,
5318
5559
  onClick: disable
5319
- }, copy.disable) : h$1("button", {
5560
+ }, copy.disable) : h$2("button", {
5320
5561
  type: "button",
5321
5562
  disabled: busy,
5322
5563
  style: primaryStyle,
5323
5564
  onClick: () => enable(detail.id)
5324
5565
  }, copy.enable))));
5325
- return h$1("section", {
5566
+ return h$2("section", {
5326
5567
  "data-dsh-oidc-managed-provider": "true",
5327
5568
  style: { margin: "18px 0" }
5328
- }, h$1("div", { style: {
5569
+ }, h$2("div", { style: {
5329
5570
  display: "flex",
5330
5571
  justifyContent: "space-between",
5331
5572
  gap: 16,
5332
5573
  marginBottom: 11
5333
- } }, h$1("div", null, h$1("strong", { style: { fontSize: 14 } }, copy.title), h$1("p", { style: {
5574
+ } }, h$2("div", null, h$2("strong", { style: { fontSize: 14 } }, copy.title), h$2("p", { style: {
5334
5575
  margin: "4px 0 0",
5335
5576
  color: textSecondary,
5336
5577
  fontSize: 12,
5337
5578
  lineHeight: 1.55
5338
- } }, copy.description)), canManageProfiles && h$1("button", {
5579
+ } }, copy.description)), canManageProfiles && h$2("button", {
5339
5580
  type: "button",
5340
5581
  disabled: busy,
5341
5582
  style: primaryStyle,
@@ -5344,23 +5585,51 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5344
5585
  setNotice("");
5345
5586
  setAddOpen(true);
5346
5587
  }
5347
- }, copy.add)), management.mode === "profile" && h$1("p", { style: {
5588
+ }, copy.add)), configuration.configFile ? h$2("div", {
5589
+ "data-eduwork-config-file": true,
5590
+ style: {
5591
+ margin: "10px 0",
5592
+ color: textSecondary,
5593
+ fontSize: 12,
5594
+ lineHeight: 1.7,
5595
+ overflowWrap: "anywhere"
5596
+ }
5597
+ }, h$2("p", { style: { margin: 0 } }, copy.fileHint), configuration.configFile.canOpen ? h$2("div", { style: {
5598
+ display: "flex",
5599
+ flexWrap: "wrap",
5600
+ gap: 8,
5601
+ marginTop: 10
5602
+ } }, h$2("button", {
5603
+ type: "button",
5604
+ style: primaryStyle,
5605
+ disabled: busy,
5606
+ onClick: () => run(() => service.openConfiguration("config"), copy.configOpened)
5607
+ }, copy.openConfig), h$2("button", {
5608
+ type: "button",
5609
+ style: buttonStyle,
5610
+ disabled: busy,
5611
+ onClick: () => run(() => service.openConfiguration("examples"), copy.examplesOpened)
5612
+ }, copy.openExamples)) : h$2("div", { style: { marginTop: 6 } }, h$2("div", null, copy.fileFallback, h$2("code", null, configuration.configFile.path)), h$2("div", null, copy.examplesHint))) : management.mode === "profile" && h$2("p", { style: {
5348
5613
  margin: "8px 0",
5349
5614
  color: textTertiary,
5350
5615
  fontSize: 11
5351
- } }, copy.profileNotice), error && h$1("p", {
5616
+ } }, copy.profileNotice), login.pending && h$2("p", { role: "status" }, copy.waiting, " ", h$2("button", {
5617
+ type: "button",
5618
+ style: buttonStyle,
5619
+ onClick: login.cancel
5620
+ }, copy.cancelLogin)), error && h$2("p", {
5352
5621
  role: "alert",
5353
5622
  style: {
5354
5623
  color: "#a82332",
5355
5624
  fontSize: 12
5356
5625
  }
5357
- }, error), notice && h$1("p", {
5626
+ }, error), notice && h$2("p", {
5358
5627
  role: "status",
5359
5628
  style: {
5360
5629
  color: "#357a55",
5361
5630
  fontSize: 12
5362
5631
  }
5363
- }, notice), restartRequired && canRestart && h$1("div", { style: {
5632
+ }, notice), restartRequired && canRestart && h$2("div", { style: {
5364
5633
  display: "flex",
5365
5634
  justifyContent: "space-between",
5366
5635
  gap: 12,
@@ -5368,10 +5637,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5368
5637
  padding: 11,
5369
5638
  border: `1px solid ${border$1}`,
5370
5639
  borderRadius: 10
5371
- } }, h$1("span", { style: {
5640
+ } }, h$2("span", { style: {
5372
5641
  color: textSecondary,
5373
5642
  fontSize: 12
5374
- } }, copy.restartNotice), h$1("button", {
5643
+ } }, copy.restartNotice), h$2("button", {
5375
5644
  type: "button",
5376
5645
  disabled: busy,
5377
5646
  style: primaryStyle,
@@ -5379,6 +5648,338 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5379
5648
  }, copy.restart)), providerCards, addDialog, detailDialog);
5380
5649
  }
5381
5650
  //#endregion
5651
+ //#region src/client/use-account-status.ts
5652
+ function useAccountStatus(service, profileID) {
5653
+ const subscribe = (0, react.useCallback)((changed) => service.subscribeAccounts((id) => {
5654
+ if (id === profileID) changed();
5655
+ }), [service, profileID]);
5656
+ const snapshot = (0, react.useCallback)(() => service.accountSnapshot(profileID), [service, profileID]);
5657
+ const status = (0, react.useSyncExternalStore)(subscribe, snapshot, snapshot);
5658
+ (0, react.useEffect)(() => {
5659
+ service.status(profileID).catch(() => {});
5660
+ }, [service, profileID]);
5661
+ return status;
5662
+ }
5663
+ //#endregion
5664
+ //#region src/client/account-state.js
5665
+ /** One account snapshot shared by onboarding, settings and sidebar consumers. */
5666
+ function createAccountState(base, { connected = async (_profileID) => {} } = {}) {
5667
+ const values = /* @__PURE__ */ new Map(), versions = /* @__PURE__ */ new Map(), epochs = /* @__PURE__ */ new Map(), reads = /* @__PURE__ */ new Map(), listeners = /* @__PURE__ */ new Set(), logins = /* @__PURE__ */ new Map();
5668
+ let disposed = false;
5669
+ const revision = (id) => versions.get(id) ?? 0;
5670
+ const invalidate = (id) => {
5671
+ versions.set(id, revision(id) + 1);
5672
+ };
5673
+ const epoch = (id) => epochs.get(id) ?? 0;
5674
+ const mutate = (id) => {
5675
+ invalidate(id);
5676
+ epochs.set(id, epoch(id) + 1);
5677
+ return epoch(id);
5678
+ };
5679
+ const publish = (status) => {
5680
+ if (disposed || !status?.profileID) return status;
5681
+ invalidate(status.profileID);
5682
+ const previous = values.get(status.profileID);
5683
+ if (JSON.stringify(previous) === JSON.stringify(status)) return previous;
5684
+ values.set(status.profileID, status);
5685
+ for (const listener of [...listeners]) listener(status.profileID);
5686
+ return status;
5687
+ };
5688
+ const status = (id) => {
5689
+ const version = revision(id), pending = reads.get(id);
5690
+ if (pending?.version === version) return pending.promise;
5691
+ const promise = base.status(id).then((value) => {
5692
+ if (revision(id) === version) publish(value);
5693
+ return values.get(id) ?? value;
5694
+ }).finally(() => {
5695
+ if (reads.get(id)?.promise === promise) reads.delete(id);
5696
+ });
5697
+ reads.set(id, {
5698
+ version,
5699
+ promise
5700
+ });
5701
+ return promise;
5702
+ };
5703
+ const activate = async (value) => {
5704
+ if (value?.state === "connected" && value.credentialReady) await connected(value.profileID);
5705
+ };
5706
+ return {
5707
+ service: {
5708
+ ...base,
5709
+ status,
5710
+ accountSnapshot: (id) => values.get(id) ?? null,
5711
+ subscribeAccounts(listener) {
5712
+ listeners.add(listener);
5713
+ return () => listeners.delete(listener);
5714
+ },
5715
+ async begin(id) {
5716
+ const generation = mutate(id), result = await base.begin(id);
5717
+ if (epoch(id) !== generation || disposed) return result;
5718
+ if (result.mode === "external") logins.set(result.loginID, {
5719
+ id,
5720
+ generation
5721
+ });
5722
+ if (result.mode === "completed") {
5723
+ publish(result.status);
5724
+ await activate(result.status);
5725
+ }
5726
+ return result;
5727
+ },
5728
+ async loginStatus(loginID) {
5729
+ const next = await base.loginStatus(loginID), flow = logins.get(loginID);
5730
+ if (!flow || disposed || epoch(flow.id) !== flow.generation) return next;
5731
+ if (next.state === "completed" && next.status) {
5732
+ if (!flow.completion) {
5733
+ publish(next.status);
5734
+ flow.completion = activate(next.status);
5735
+ }
5736
+ await flow.completion;
5737
+ }
5738
+ return next;
5739
+ },
5740
+ async reconcile(id, options) {
5741
+ const generation = mutate(id), result = await base.reconcile(id, options);
5742
+ if (epoch(id) === generation && !disposed) {
5743
+ publish(result);
5744
+ if (options?.allowProvision === true) await activate(result);
5745
+ }
5746
+ return result;
5747
+ },
5748
+ async logout(id) {
5749
+ const generation = mutate(id), result = await base.logout(id);
5750
+ if (epoch(id) === generation) publish(result);
5751
+ return result;
5752
+ },
5753
+ async refreshAccounts(ids) {
5754
+ for (const id of ids) invalidate(id);
5755
+ await Promise.all(ids.map((id) => status(id).catch(() => {})));
5756
+ }
5757
+ },
5758
+ dispose() {
5759
+ disposed = true;
5760
+ listeners.clear();
5761
+ logins.clear();
5762
+ reads.clear();
5763
+ }
5764
+ };
5765
+ }
5766
+ /** DSH 0.1.5 uses the current Session's ModelDirectory, not a global UI field. */
5767
+ async function selectConnectedModel(ctx, service, profileID, onlyIfMissing = false) {
5768
+ const { selection, changed } = await service.selectEnterpriseModel(profileID, { onlyIfMissing });
5769
+ if (!changed || !selection) return;
5770
+ const sessions = ctx.get?.("sessions");
5771
+ const current = sessions?.list?.getSnapshot().current;
5772
+ if (!current || sessions.subagentAddress(current) !== void 0) return;
5773
+ const directories = ctx.get?.("modelDirectories");
5774
+ if (!directories) throw new Error("已连接企业模型,但当前对话的模型选择器尚未准备好。请在模型菜单中选择。");
5775
+ const directory = directories.directoryFor(current);
5776
+ if (onlyIfMissing) {
5777
+ const state = await directory.load();
5778
+ if (state.current && state.routable !== false) return;
5779
+ }
5780
+ await directory.select(selection);
5781
+ }
5782
+ //#endregion
5783
+ //#region src/client/account-menu.ts
5784
+ const h$1 = react.default.createElement;
5785
+ const words = typeof navigator !== "undefined" && navigator.language.toLowerCase().startsWith("zh") ? {
5786
+ menu: "账户菜单",
5787
+ check: "刷新账户",
5788
+ logout: "退出登录",
5789
+ login: "登录账户",
5790
+ provision: "确认创建模型凭据",
5791
+ waiting: "请在浏览器中完成登录…",
5792
+ cancel: "取消登录",
5793
+ processing: "正在处理…"
5794
+ } : {
5795
+ menu: "Account menu",
5796
+ check: "Refresh account",
5797
+ logout: "Sign out",
5798
+ login: "Sign in",
5799
+ provision: "Provision model credential",
5800
+ waiting: "Complete sign-in in your browser…",
5801
+ cancel: "Cancel sign-in",
5802
+ processing: "Working…"
5803
+ };
5804
+ const action = {
5805
+ width: "100%",
5806
+ textAlign: "left",
5807
+ border: 0,
5808
+ borderRadius: 7,
5809
+ padding: "9px 10px",
5810
+ font: "inherit",
5811
+ cursor: "pointer",
5812
+ background: "var(--dsw-alias-bg-layer-2, #f5f6f8)",
5813
+ color: "inherit"
5814
+ };
5815
+ const secondary = "var(--dsw-alias-label-secondary, #69717f)";
5816
+ function AccountMenu({ service, profile, children, renderSlot, wide = true }) {
5817
+ const status = useAccountStatus(service, profile.id);
5818
+ const [open, setOpen] = (0, react.useState)(false);
5819
+ const [position, setPosition] = (0, react.useState)({
5820
+ left: 12,
5821
+ bottom: 60
5822
+ });
5823
+ const [busy, setBusy] = (0, react.useState)(false);
5824
+ const [error, setError] = (0, react.useState)("");
5825
+ const trigger = (0, react.useRef)(null), panel = (0, react.useRef)(null);
5826
+ const initialFocus = (0, react.useRef)("first");
5827
+ const id = (0, react.useId)(), login = useSignIn(service);
5828
+ const signedIn = status?.state === "connected" || status?.state === "authenticated";
5829
+ const close = (restore = false) => {
5830
+ setOpen(false);
5831
+ if (restore) trigger.current?.focus();
5832
+ };
5833
+ (0, react.useEffect)(() => {
5834
+ if (!open) return;
5835
+ const place = () => {
5836
+ const rect = trigger.current?.getBoundingClientRect();
5837
+ if (rect) setPosition({
5838
+ left: Math.max(8, Math.min(rect.left, window.innerWidth - 304)),
5839
+ bottom: Math.max(8, window.innerHeight - rect.top + 8)
5840
+ });
5841
+ };
5842
+ const inside = (target) => target instanceof Node && (trigger.current?.contains(target) || panel.current?.contains(target));
5843
+ const outside = (event) => {
5844
+ if (!inside(event.target)) close();
5845
+ };
5846
+ place();
5847
+ const items = panel.current?.querySelectorAll("[role=\"menuitem\"]:not(:disabled)");
5848
+ const first = initialFocus.current === "last" ? items?.[items.length - 1] : items?.[0];
5849
+ initialFocus.current = "first";
5850
+ if (first) first.focus();
5851
+ else panel.current?.focus();
5852
+ document.addEventListener("pointerdown", outside, true);
5853
+ document.addEventListener("focusin", outside, true);
5854
+ window.addEventListener("resize", place);
5855
+ window.addEventListener("scroll", place, true);
5856
+ return () => {
5857
+ document.removeEventListener("pointerdown", outside, true);
5858
+ document.removeEventListener("focusin", outside, true);
5859
+ window.removeEventListener("resize", place);
5860
+ window.removeEventListener("scroll", place, true);
5861
+ };
5862
+ }, [open]);
5863
+ const run = async (operation, exit = false) => {
5864
+ setBusy(true);
5865
+ setError("");
5866
+ try {
5867
+ await operation();
5868
+ if (exit) close(true);
5869
+ } catch (cause) {
5870
+ setError(cause?.message || String(cause));
5871
+ } finally {
5872
+ setBusy(false);
5873
+ }
5874
+ };
5875
+ const keydown = (event) => {
5876
+ if (event.key === "Escape") {
5877
+ event.preventDefault();
5878
+ event.stopPropagation();
5879
+ close(true);
5880
+ return;
5881
+ }
5882
+ if (![
5883
+ "ArrowDown",
5884
+ "ArrowUp",
5885
+ "Home",
5886
+ "End"
5887
+ ].includes(event.key)) return;
5888
+ event.preventDefault();
5889
+ const items = Array.from(panel.current?.querySelectorAll("[role=\"menuitem\"]:not(:disabled)") ?? []);
5890
+ const index = items.indexOf(document.activeElement);
5891
+ items[event.key === "Home" ? 0 : event.key === "End" ? items.length - 1 : (index + (event.key === "ArrowDown" ? 1 : -1) + items.length) % items.length]?.focus();
5892
+ };
5893
+ const button = (label, onClick) => h$1("button", {
5894
+ type: "button",
5895
+ role: "menuitem",
5896
+ disabled: busy,
5897
+ style: action,
5898
+ onClick
5899
+ }, label);
5900
+ const refreshAccount = () => service.reconcile(profile.id, { allowProvision: false });
5901
+ const defaultContent = signedIn && button(busy ? words.processing : words.check, () => void run(refreshAccount));
5902
+ const content = open && h$1("div", {
5903
+ ref: panel,
5904
+ id,
5905
+ role: "menu",
5906
+ tabIndex: -1,
5907
+ "aria-label": words.menu,
5908
+ onKeyDown: keydown,
5909
+ style: {
5910
+ position: "fixed",
5911
+ zIndex: 11e3,
5912
+ ...position,
5913
+ width: "min(296px, calc(100vw - 16px))",
5914
+ maxHeight: "min(540px, calc(100vh - 80px))",
5915
+ overflowY: "auto",
5916
+ boxSizing: "border-box",
5917
+ padding: 14,
5918
+ border: "1px solid var(--dsw-alias-border-l2, #e1e4eb)",
5919
+ borderRadius: 13,
5920
+ background: "var(--dsw-alias-bg-layer-1, #fff)",
5921
+ color: "var(--dsw-alias-label-primary, #20232c)",
5922
+ boxShadow: "0 12px 38px #17264d20",
5923
+ fontSize: 12
5924
+ }
5925
+ }, h$1("div", {
5926
+ role: "presentation",
5927
+ style: { padding: "2px 3px 12px" }
5928
+ }, h$1("strong", null, accountUserName(status) || accountOrganization(profile)), h$1("div", { style: {
5929
+ marginTop: 4,
5930
+ color: secondary
5931
+ } }, accountOrganization(profile))), typeof renderSlot === "function" ? renderSlot("oidc.account.menu.details", {
5932
+ profile,
5933
+ status,
5934
+ busy,
5935
+ run,
5936
+ refreshAccount,
5937
+ defaultContent
5938
+ }, { fallback: defaultContent }) : defaultContent, h$1("div", { style: {
5939
+ display: "grid",
5940
+ gap: 6
5941
+ } }, status?.state === "authenticated" && profile.provider && button(words.provision, () => void run(() => service.reconcile(profile.id, { allowProvision: true }))), !signedIn && button(busy ? words.processing : words.login, () => void run(() => login.begin(profile.id))), signedIn && button(words.logout, () => void run(() => service.logout(profile.id), true))), login.pending && h$1("p", { role: "status" }, words.waiting, " ", h$1("button", {
5942
+ type: "button",
5943
+ role: "menuitem",
5944
+ style: action,
5945
+ onClick: login.cancel
5946
+ }, words.cancel)), error && h$1("p", {
5947
+ role: "alert",
5948
+ style: {
5949
+ color: "var(--dsw-alias-state-error-primary, #a14d3b)",
5950
+ marginBottom: 0
5951
+ }
5952
+ }, error));
5953
+ return h$1(react.default.Fragment, null, h$1("button", {
5954
+ ref: trigger,
5955
+ type: "button",
5956
+ "aria-label": `${accountUserName(status) || accountOrganization(profile)} · ${words.menu}`,
5957
+ "aria-haspopup": "menu",
5958
+ "aria-expanded": open,
5959
+ "aria-controls": open ? id : void 0,
5960
+ title: words.menu,
5961
+ onClick: () => setOpen((value) => !value),
5962
+ onKeyDown: (event) => {
5963
+ if (event.key === "ArrowDown" || event.key === "ArrowUp") {
5964
+ event.preventDefault();
5965
+ initialFocus.current = event.key === "ArrowUp" ? "last" : "first";
5966
+ setOpen(true);
5967
+ } else if (event.key === "Escape") close(true);
5968
+ },
5969
+ style: {
5970
+ display: "block",
5971
+ width: wide ? "100%" : 36,
5972
+ border: 0,
5973
+ borderRadius: 9,
5974
+ padding: wide ? "7px 4px" : 4,
5975
+ cursor: "pointer",
5976
+ background: "transparent",
5977
+ color: "inherit",
5978
+ textAlign: "left"
5979
+ }
5980
+ }, children), content && typeof document !== "undefined" ? (0, react_dom.createPortal)(content, document.body) : null);
5981
+ }
5982
+ //#endregion
5382
5983
  //#region src/client/index.ts
5383
5984
  const inject = [
5384
5985
  "slots",
@@ -5396,7 +5997,12 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5396
5997
  background: "var(--dsw-alias-bg-layer-1, #fff)",
5397
5998
  color: "var(--dsw-alias-label-primary, #241a18)"
5398
5999
  });
5399
- const messages = typeof navigator !== "undefined" && navigator.language.toLowerCase().startsWith("zh") ? {
6000
+ const isChinese = typeof navigator !== "undefined" && navigator.language.toLowerCase().startsWith("zh");
6001
+ const messages = isChinese ? {
6002
+ waiting: "请在浏览器中完成登录…",
6003
+ cancelLogin: "取消登录",
6004
+ identityDescription: "使用组织账号登录。模型服务可在设置中单独配置。",
6005
+ identityConnected: "组织身份已登录",
5400
6006
  unavailable: "企业模型服务暂时不可用",
5401
6007
  connected: "已连接",
5402
6008
  authenticated: "身份认证完成,等待创建模型凭据",
@@ -5418,6 +6024,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5418
6024
  footerAuthenticated: "身份认证完成,等待创建模型凭据",
5419
6025
  footerSetup: "点击设置完成登录"
5420
6026
  } : {
6027
+ waiting: "Complete sign-in in your browser…",
6028
+ cancelLogin: "Cancel sign-in",
6029
+ identityDescription: "Sign in with your organization account. Configure model services separately in settings.",
6030
+ identityConnected: "Organization identity connected",
5421
6031
  unavailable: "Enterprise model service is temporarily unavailable",
5422
6032
  connected: "Connected",
5423
6033
  authenticated: "Identity verified; model credential is not provisioned",
@@ -5482,6 +6092,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5482
6092
  }
5483
6093
  function installBrand(ctx, profile) {
5484
6094
  const brand = profile.brand ?? {};
6095
+ if (Object.keys(brand).length === 0) return () => {};
5485
6096
  const productName = brand.productName || profile.displayName;
5486
6097
  const previousTitle = document.title;
5487
6098
  document.title = productName;
@@ -5523,18 +6134,23 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5523
6134
  };
5524
6135
  }
5525
6136
  function useAccount(service, profileID) {
5526
- const [status, setStatus] = (0, react.useState)(null);
6137
+ const status = useAccountStatus(service, profileID);
5527
6138
  const [busy, setBusy] = (0, react.useState)("");
5528
6139
  const [error, setError] = (0, react.useState)("");
5529
- const refresh = async () => setStatus(await service.status(profileID));
5530
6140
  (0, react.useEffect)(() => {
5531
- refresh().catch((cause) => setError(cause?.message || String(cause)));
5532
- }, [profileID]);
6141
+ let active = true;
6142
+ service.status(profileID).catch((cause) => {
6143
+ if (active) setError(cause?.message || String(cause));
6144
+ });
6145
+ return () => {
6146
+ active = false;
6147
+ };
6148
+ }, [service, profileID]);
5533
6149
  const run = async (name, operation) => {
5534
6150
  setBusy(name);
5535
6151
  setError("");
5536
6152
  try {
5537
- setStatus(await operation());
6153
+ await operation();
5538
6154
  } catch (cause) {
5539
6155
  setError(cause?.message || String(cause));
5540
6156
  } finally {
@@ -5558,15 +6174,9 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5558
6174
  color: "white",
5559
6175
  borderColor: "transparent"
5560
6176
  };
5561
- const begin = () => account.run("login", async () => {
5562
- const result = await service.begin(profile.id);
5563
- if (result.mode === "redirect") {
5564
- window.location.assign(result.authorizationURL);
5565
- return account.status;
5566
- }
5567
- return result.status;
5568
- });
5569
- const stateLabel = account.status?.credentialReady ? messages.connected : account.status?.state === "authenticated" ? messages.authenticated : messages.disconnected;
6177
+ const login = useSignIn(service);
6178
+ const begin = () => account.run("login", () => login.begin(profile.id));
6179
+ const stateLabel = account.status?.state === "connected" ? messages.connected : account.status?.state === "authenticated" ? messages.authenticated : messages.disconnected;
5570
6180
  const userName = accountUserName(account.status);
5571
6181
  return h("section", { style: {
5572
6182
  padding: "16px 0",
@@ -5601,22 +6211,22 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5601
6211
  color: "var(--dsw-alias-label-secondary)",
5602
6212
  fontSize: 12,
5603
6213
  lineHeight: 1.6
5604
- } }, profile.brand?.loginDescription || messages.description), h("div", { style: {
6214
+ } }, profile.brand?.loginDescription || (profile.provider ? messages.description : messages.identityDescription)), h("div", { style: {
5605
6215
  marginTop: 12,
5606
6216
  display: "flex",
5607
6217
  gap: 8,
5608
6218
  flexWrap: "wrap"
5609
- } }, !account.status?.credentialReady && account.status?.state !== "authenticated" && h("button", {
6219
+ } }, account.status?.state !== "connected" && account.status?.state !== "authenticated" && h("button", {
5610
6220
  type: "button",
5611
6221
  disabled: Boolean(account.busy),
5612
6222
  style: primary,
5613
6223
  onClick: begin
5614
- }, account.busy ? messages.connecting : messages.login), !account.status?.credentialReady && account.status?.state === "authenticated" && h("button", {
6224
+ }, account.busy ? messages.connecting : messages.login), profile.provider && !account.status?.credentialReady && account.status?.state === "authenticated" && h("button", {
5615
6225
  type: "button",
5616
6226
  disabled: Boolean(account.busy),
5617
6227
  style: primary,
5618
6228
  onClick: () => account.run("provision", () => service.reconcile(profile.id, { allowProvision: true }))
5619
- }, account.busy ? messages.provisioning : messages.provision), account.status?.credentialReady && h("button", {
6229
+ }, account.busy ? messages.provisioning : messages.provision), account.status?.state === "connected" && h("button", {
5620
6230
  type: "button",
5621
6231
  disabled: Boolean(account.busy),
5622
6232
  style: button,
@@ -5634,7 +6244,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5634
6244
  ...button,
5635
6245
  textDecoration: "none"
5636
6246
  }
5637
- }, messages.help)), account.error && h("p", {
6247
+ }, messages.help)), login.pending && h("p", { role: "status" }, messages.waiting, " ", h("button", {
6248
+ type: "button",
6249
+ style: button,
6250
+ onClick: login.cancel
6251
+ }, messages.cancelLogin)), account.error && h("p", {
5638
6252
  role: "alert",
5639
6253
  style: {
5640
6254
  margin: "10px 0 0",
@@ -5645,29 +6259,28 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5645
6259
  margin: "10px 0 0",
5646
6260
  color: "var(--dsw-alias-label-tertiary)",
5647
6261
  fontSize: 11
5648
- } }, `${profile.provider.displayName} · ${profile.provider.models.map((model) => model.name).join("、")}`));
6262
+ } }, profile.provider ? `${profile.provider.displayName} · ${profile.provider.models.map((model) => model.name).join("、")}` : messages.other));
5649
6263
  }
5650
6264
  function EnterpriseOnboarding({ service, configuration, complete }) {
5651
6265
  const profile = configuration.profiles[0];
5652
6266
  const account = useAccount(service, profile.id);
5653
6267
  (0, react.useEffect)(() => {
5654
- if (account.status?.credentialReady) complete();
5655
- }, [account.status?.credentialReady, complete]);
5656
- if (account.status === null || account.status?.credentialReady) return null;
6268
+ if (account.status?.state === "connected" && !account.busy && !account.error) complete();
6269
+ }, [
6270
+ account.status?.state,
6271
+ account.busy,
6272
+ account.error,
6273
+ complete
6274
+ ]);
6275
+ const login = useSignIn(service);
6276
+ if (account.status === null || account.status?.state === "connected" && !account.busy && !account.error) return null;
5657
6277
  const primary = {
5658
6278
  ...button,
5659
6279
  background: profile.brand?.primaryColor || "var(--dsw-alias-brand-primary, #5157af)",
5660
6280
  color: "white",
5661
6281
  borderColor: "transparent"
5662
6282
  };
5663
- const begin = () => account.run("login", async () => {
5664
- const result = await service.begin(profile.id);
5665
- if (result.mode === "redirect") {
5666
- window.location.assign(result.authorizationURL);
5667
- return account.status;
5668
- }
5669
- return result.status;
5670
- });
6283
+ const begin = () => account.run("login", () => login.begin(profile.id));
5671
6284
  return h("div", { style: {
5672
6285
  position: "fixed",
5673
6286
  inset: 0,
@@ -5714,7 +6327,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5714
6327
  color: "var(--dsw-alias-label-secondary)",
5715
6328
  fontSize: 13,
5716
6329
  lineHeight: 1.7
5717
- } }, profile.brand?.loginDescription || messages.shortDescription), h("div", { style: {
6330
+ } }, profile.brand?.loginDescription || (profile.provider ? messages.shortDescription : messages.identityDescription)), h("div", { style: {
5718
6331
  marginTop: 20,
5719
6332
  display: "flex",
5720
6333
  gap: 9,
@@ -5723,8 +6336,8 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5723
6336
  type: "button",
5724
6337
  disabled: Boolean(account.busy),
5725
6338
  style: primary,
5726
- onClick: begin
5727
- }, account.busy ? messages.connecting : messages.login), account.status?.state === "authenticated" && h("button", {
6339
+ onClick: account.status?.state === "connected" ? () => account.run("provision", () => service.reconcile(profile.id, { allowProvision: true })) : begin
6340
+ }, account.busy ? messages.connecting : account.status?.state === "connected" ? isChinese ? "使用企业模型" : "Use organization model" : messages.login), account.status?.state === "authenticated" && h("button", {
5728
6341
  type: "button",
5729
6342
  disabled: Boolean(account.busy),
5730
6343
  style: primary,
@@ -5734,7 +6347,11 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5734
6347
  disabled: Boolean(account.busy),
5735
6348
  style: button,
5736
6349
  onClick: complete
5737
- }, messages.other)), account.error && h("p", {
6350
+ }, messages.other)), login.pending && h("p", { role: "status" }, messages.waiting, " ", h("button", {
6351
+ type: "button",
6352
+ style: button,
6353
+ onClick: login.cancel
6354
+ }, messages.cancelLogin)), account.error && h("p", {
5738
6355
  role: "alert",
5739
6356
  style: {
5740
6357
  margin: "12px 0 0",
@@ -5746,17 +6363,19 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5746
6363
  color: "var(--dsw-alias-label-tertiary)",
5747
6364
  fontSize: 11,
5748
6365
  lineHeight: 1.55
5749
- } }, `${messages.enabled} ${profile.provider.displayName}: ${profile.provider.models.map((model) => model.name).join("、")}`)));
6366
+ } }, profile.provider ? `${messages.enabled} ${profile.provider.displayName}: ${profile.provider.models.map((model) => model.name).join("、")}` : messages.other)));
5750
6367
  }
5751
- function FooterAccount({ service, configuration }) {
6368
+ function FooterAccount({ service, configuration, renderSlot, wide = true }) {
5752
6369
  const profile = configuration.profiles[0];
5753
- const [status, setStatus] = (0, react.useState)(null);
5754
- (0, react.useEffect)(() => {
5755
- service.status(profile.id).then(setStatus).catch(() => {});
5756
- }, [profile.id]);
6370
+ const status = useAccountStatus(service, profile.id);
5757
6371
  const userName = accountUserName(status);
5758
- const statusLabel = status?.credentialReady ? messages.footerConnected : status?.state === "authenticated" ? messages.footerAuthenticated : messages.footerSetup;
5759
- return h("div", { style: {
6372
+ const statusLabel = status?.state === "connected" ? profile.provider ? messages.footerConnected : messages.identityConnected : status?.state === "authenticated" ? messages.footerAuthenticated : messages.footerSetup;
6373
+ return h(AccountMenu, {
6374
+ service,
6375
+ profile,
6376
+ renderSlot,
6377
+ wide
6378
+ }, h("div", { style: {
5760
6379
  display: "flex",
5761
6380
  alignItems: "center",
5762
6381
  gap: 9,
@@ -5764,7 +6383,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5764
6383
  } }, h(ProductMark, {
5765
6384
  profile,
5766
6385
  size: 28
5767
- }), h("div", { style: { minWidth: 0 } }, h("div", { style: {
6386
+ }), wide && h("div", { style: { minWidth: 0 } }, h("div", { style: {
5768
6387
  overflow: "hidden",
5769
6388
  textOverflow: "ellipsis",
5770
6389
  whiteSpace: "nowrap",
@@ -5773,19 +6392,24 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5773
6392
  } }, userName || accountOrganization(profile)), h("div", { style: {
5774
6393
  fontSize: 10,
5775
6394
  color: "var(--dsw-alias-label-tertiary)"
5776
- } }, accountStatusLine(profile, statusLabel, userName))));
6395
+ } }, accountStatusLine(profile, statusLabel, userName)))));
5777
6396
  }
5778
6397
  async function apply(ctx) {
5779
- const disposeRemote = await ctx.remote.$mount(remote_default);
6398
+ const disposeRemote = await ctx.remote.$mount(TYPERT_REMOTE);
5780
6399
  ctx.inject(["remote.oidcAccounts"], (surfaceCtx) => {
5781
6400
  let cancelled = false;
5782
6401
  let disposeBrand = () => {};
5783
6402
  const disposers = [];
5784
- const service = {
6403
+ const base = {
5785
6404
  configuration: () => unwrap(surfaceCtx.remote.oidcAccounts.configuration()),
6405
+ openConfiguration: (target) => unwrap(surfaceCtx.remote.oidcAccounts.openConfiguration(target)),
5786
6406
  status: (profileID) => unwrap(surfaceCtx.remote.oidcAccounts.status(profileID)),
6407
+ resources: (profileID) => unwrap(surfaceCtx.remote.oidcAccounts.resources(profileID)),
5787
6408
  begin: (profileID) => unwrap(surfaceCtx.remote.oidcAccounts.begin(profileID)),
6409
+ loginStatus: (loginID) => unwrap(surfaceCtx.remote.oidcAccounts.loginStatus(loginID)),
6410
+ cancelLogin: (loginID) => unwrap(surfaceCtx.remote.oidcAccounts.cancelLogin(loginID)),
5788
6411
  reconcile: (profileID, options) => unwrap(surfaceCtx.remote.oidcAccounts.reconcile(profileID, options)),
6412
+ selectEnterpriseModel: (profileID, options) => unwrap(surfaceCtx.remote.oidcAccounts.selectEnterpriseModel(profileID, options)),
5789
6413
  logout: (profileID) => unwrap(surfaceCtx.remote.oidcAccounts.logout(profileID)),
5790
6414
  management: () => unwrap(surfaceCtx.remote.oidcAccounts.management()),
5791
6415
  activate: (profileID) => unwrap(surfaceCtx.remote.oidcAccounts.activate(profileID)),
@@ -5796,8 +6420,19 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5796
6420
  configureModels: (profileID, modelMode, models) => unwrap(surfaceCtx.remote.oidcAccounts.configureModels(profileID, modelMode, models)),
5797
6421
  restart: () => unwrap(surfaceCtx.remote.oidcAccounts.restart())
5798
6422
  };
6423
+ const accounts = createAccountState(base, { connected: (profileID) => selectConnectedModel(surfaceCtx, base, profileID) });
6424
+ const service = accounts.service;
5799
6425
  service.configuration().then((configuration) => {
5800
6426
  if (cancelled || configuration.uiMode === "external") return;
6427
+ const ids = configuration.profiles.map((profile) => profile.id);
6428
+ const refreshAccounts = () => service.refreshAccounts(ids);
6429
+ if (typeof surfaceCtx.remote.$on === "function") disposers.push(surfaceCtx.remote.$on("credentials/reference-updated", refreshAccounts));
6430
+ if (typeof surfaceCtx.on === "function") disposers.push(surfaceCtx.on("connection/reset", refreshAccounts));
6431
+ refreshAccounts().then(async () => {
6432
+ if (cancelled) return;
6433
+ const connected = configuration.profiles.find((profile) => profile.provider && service.accountSnapshot(profile.id)?.credentialReady);
6434
+ if (connected) await selectConnectedModel(surfaceCtx, base, connected.id, true);
6435
+ }).catch(() => {});
5801
6436
  disposers.push(surfaceCtx.slots.inject("settings.models.footer", () => surfaceCtx.slots.register({
5802
6437
  name: "settings.models.footer",
5803
6438
  id: "dsh-oidc-enterprise",
@@ -5808,7 +6443,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5808
6443
  })
5809
6444
  }, ManagedProviderCard)));
5810
6445
  if (configuration.uiMode === "models-only" || configuration.profiles.length === 0) return;
5811
- disposeBrand = installBrand(surfaceCtx, configuration.profiles[0]);
6446
+ if (configuration.manageProductBrand !== false) disposeBrand = installBrand(surfaceCtx, configuration.profiles[0]);
5812
6447
  disposers.push(surfaceCtx.slots.inject("settings.onboarding", () => surfaceCtx.slots.register({
5813
6448
  name: "settings.onboarding",
5814
6449
  id: "dsh-oidc-enterprise",
@@ -5822,6 +6457,10 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5822
6457
  name: "sidebar.footer.action",
5823
6458
  id: "dsh-oidc-account",
5824
6459
  order: -90,
6460
+ children: { "oidc.account.menu.details": {
6461
+ kind: "single",
6462
+ scope: "root"
6463
+ } },
5825
6464
  inject: () => ({
5826
6465
  service,
5827
6466
  configuration
@@ -5841,6 +6480,7 @@ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.
5841
6480
  });
5842
6481
  surfaceCtx.effect(() => () => {
5843
6482
  cancelled = true;
6483
+ accounts.dispose();
5844
6484
  for (const dispose of disposers.reverse()) dispose?.();
5845
6485
  disposeBrand();
5846
6486
  }, "dsh-oidc: client surfaces");