@capybara-hq/kernel 0.1.0-rc.10 → 0.1.0-rc.12
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.
- package/dist/admin/admin.schemas.d.ts +3 -0
- package/dist/agents/agents.schemas.d.ts +3 -0
- package/dist/agents/agents.service.js +11 -1
- package/dist/agents/definition.d.ts +1 -0
- package/dist/agents/definition.js +7 -3
- package/dist/agents/org-templates.js +4 -0
- package/dist/agents/pending-connectors.service.js +1 -1
- package/dist/catalog/catalog-content.d.ts +33 -1
- package/dist/catalog/catalog-content.js +65 -2
- package/dist/catalog/catalog-items.js +1 -0
- package/dist/catalog/catalog-scope.service.d.ts +29 -3
- package/dist/catalog/catalog-scope.service.js +82 -10
- package/dist/catalog/catalog.service.d.ts +7 -5
- package/dist/catalog/catalog.service.js +26 -5
- package/dist/catalog/claude/translate.js +3 -0
- package/dist/catalog/component-scope.controller.d.ts +18 -6
- package/dist/catalog/component-scope.controller.js +36 -13
- package/dist/catalog/component-scope.schemas.d.ts +4 -0
- package/dist/catalog/component-scope.service.d.ts +59 -9
- package/dist/catalog/component-scope.service.js +156 -27
- package/dist/catalog/component-state.d.ts +20 -3
- package/dist/catalog/component-state.js +40 -14
- package/dist/catalog/marketplaces.service.d.ts +1 -1
- package/dist/catalog/org-catalog.service.js +3 -0
- package/dist/catalog/owned-items.js +7 -0
- package/dist/catalog/platform-components.d.ts +23 -3
- package/dist/catalog/platform-components.js +118 -9
- package/dist/catalog/platform-migration.js +2 -1
- package/dist/catalog/plugin-import.d.ts +24 -4
- package/dist/catalog/plugin-import.js +41 -7
- package/dist/catalog/plugin-install.d.ts +20 -1
- package/dist/catalog/plugin-install.js +71 -1
- package/dist/catalog/plugin-package.d.ts +41 -4
- package/dist/catalog/plugin-package.js +127 -7
- package/dist/catalog/plugin-upgrade.d.ts +13 -2
- package/dist/catalog/plugin-upgrade.js +124 -6
- package/dist/catalog/unshare.js +1 -0
- package/dist/conversations/conversations.service.d.ts +9 -0
- package/dist/conversations/conversations.service.js +9 -2
- package/dist/conversations/events.d.ts +1 -0
- package/dist/db/kernel-schema.d.ts +54 -4
- package/dist/errors.d.ts +27 -0
- package/dist/errors.js +27 -0
- package/dist/http/api.module.js +7 -0
- package/dist/identity/accounts.d.ts +11 -4
- package/dist/identity/accounts.js +23 -10
- package/dist/identity/me.controller.d.ts +10 -10
- package/dist/identity/me.controller.js +8 -6
- package/dist/identity/me.schemas.d.ts +13 -3
- package/dist/identity/me.schemas.js +10 -2
- package/dist/identity/settings.d.ts +27 -0
- package/dist/identity/settings.js +64 -0
- package/dist/orgs/settings.service.d.ts +31 -0
- package/dist/orgs/settings.service.js +31 -1
- package/dist/platform/exclusions.js +26 -0
- package/dist/platform/operation.js +6 -2
- package/dist/platform/ops/admin.ops.d.ts +3 -1
- package/dist/platform/ops/admin.ops.js +25 -7
- package/dist/platform/ops/agents.ops.js +19 -6
- package/dist/platform/ops/components.ops.js +1 -0
- package/dist/platform/ops/org-settings.ops.d.ts +3 -1
- package/dist/platform/ops/org-settings.ops.js +33 -8
- package/dist/platform/ops/orgs.ops.js +1 -0
- package/dist/platform/ops/platform.ops.d.ts +3 -1
- package/dist/platform/ops/platform.ops.js +43 -16
- package/dist/platform/ops/themes.ops.d.ts +14 -0
- package/dist/platform/ops/themes.ops.js +47 -0
- package/dist/platform/platform-ops.module.d.ts +2 -1
- package/dist/platform/platform-ops.module.js +5 -0
- package/dist/platform/route-schemas.js +3 -0
- package/dist/runtime/subagents/definition.d.ts +3 -2
- package/dist/runtime/subagents/definition.js +2 -1
- package/dist/themes/render-css.d.ts +12 -0
- package/dist/themes/render-css.js +132 -0
- package/dist/themes/theme-delivery.service.d.ts +34 -0
- package/dist/themes/theme-delivery.service.js +111 -0
- package/dist/themes/theme-state.d.ts +95 -0
- package/dist/themes/theme-state.js +203 -0
- package/dist/themes/theme-usage.d.ts +61 -0
- package/dist/themes/theme-usage.js +98 -0
- package/dist/themes/themes-public.controller.d.ts +28 -0
- package/dist/themes/themes-public.controller.js +92 -0
- package/dist/themes/themes.controller.d.ts +13 -0
- package/dist/themes/themes.controller.js +38 -0
- package/dist/themes/themes.module.d.ts +3 -0
- package/dist/themes/themes.module.js +21 -0
- package/dist/themes/themes.schemas.d.ts +3 -0
- package/dist/themes/themes.schemas.js +2 -0
- package/dist/themes/themes.service.d.ts +50 -0
- package/dist/themes/themes.service.js +99 -0
- package/dist/tools/connector-account-policies.d.ts +51 -0
- package/dist/tools/connector-account-policies.js +165 -0
- package/dist/tools/connectors.controller.d.ts +35 -4
- package/dist/tools/connectors.controller.js +66 -7
- package/dist/tools/connectors.schemas.d.ts +11 -0
- package/dist/tools/connectors.schemas.js +11 -0
- package/dist/tools/connectors.service.d.ts +2 -0
- package/dist/tools/connectors.service.js +1 -0
- package/dist/tools/pipeline.d.ts +1 -1
- package/dist/tools/policy.d.ts +23 -1
- package/dist/tools/policy.js +99 -24
- package/dist/tools/tools.module.js +3 -0
- package/package.json +3 -3
- package/src/migrations/0102_themes.sql +64 -0
- package/src/migrations/0103_ui_theme_setting.sql +7 -0
- package/src/migrations/0104_connector_tool_account_policies.sql +16 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FileSystemHandle, SkillInvocation } from "@capybara-hq/plugin-sdk";
|
|
1
|
+
import type { FileSystemHandle, ParsedTheme, SkillInvocation } from "@capybara-hq/plugin-sdk";
|
|
2
2
|
import type { ColumnType, Generated, Kysely } from "kysely";
|
|
3
3
|
/** 带默认值的时间戳列:插入可省略,读出为 Date。 */
|
|
4
4
|
type Ts = ColumnType<Date, Date | string | undefined, Date | string>;
|
|
@@ -20,6 +20,8 @@ export interface AccountsTable {
|
|
|
20
20
|
time_zone: ColumnType<string | null, string | null | undefined, string | null>;
|
|
21
21
|
/** 0090: the account must set its own password first (a generated initial password). */
|
|
22
22
|
must_change_password: Generated<boolean>;
|
|
23
|
+
/** 0093: the person's theme id across all their orgs; null follows the org (themes spec §7). */
|
|
24
|
+
theme: ColumnType<string | null, string | null | undefined, string | null>;
|
|
23
25
|
}
|
|
24
26
|
/**
|
|
25
27
|
* 0090: a generated initial password sealed for the person who approved the account's creation,
|
|
@@ -418,6 +420,18 @@ export interface ConnectorsTable {
|
|
|
418
420
|
/** 0081: the platform connector template this connector was made from (null = none). */
|
|
419
421
|
platform_template_id: ColumnType<string | null, string | null | undefined, string | null>;
|
|
420
422
|
}
|
|
423
|
+
/**
|
|
424
|
+
* 0104: a member's own policy for one tool of a personal-credential connector, for calls made with
|
|
425
|
+
* their own connection (maintainer decision 2026-11-09).
|
|
426
|
+
*/
|
|
427
|
+
export interface ConnectorToolAccountPoliciesTable {
|
|
428
|
+
org_id: string;
|
|
429
|
+
connector_id: string;
|
|
430
|
+
account_id: string;
|
|
431
|
+
tool_name: string;
|
|
432
|
+
policy: "allow" | "ask" | "deny";
|
|
433
|
+
updated_at: Ts;
|
|
434
|
+
}
|
|
421
435
|
/** 0010_tools.sql(0033 由 `app_tools` 改名):连接器暴露的工具。 */
|
|
422
436
|
export interface ConnectorToolsTable {
|
|
423
437
|
connector_id: string;
|
|
@@ -1140,6 +1154,7 @@ export interface KernelDatabase {
|
|
|
1140
1154
|
lg_checkpoint_writes: LgCheckpointWritesTable;
|
|
1141
1155
|
connectors: ConnectorsTable;
|
|
1142
1156
|
connector_tools: ConnectorToolsTable;
|
|
1157
|
+
connector_tool_account_policies: ConnectorToolAccountPoliciesTable;
|
|
1143
1158
|
tool_grants: ToolGrantsTable;
|
|
1144
1159
|
conversation_tool_grants: ConversationToolGrantsTable;
|
|
1145
1160
|
approvals: ApprovalsTable;
|
|
@@ -1195,6 +1210,8 @@ export interface KernelDatabase {
|
|
|
1195
1210
|
platform_agent_templates: PlatformAgentTemplatesTable;
|
|
1196
1211
|
platform_agent_template_versions: PlatformAgentTemplateVersionsTable;
|
|
1197
1212
|
platform_connector_templates: PlatformConnectorTemplatesTable;
|
|
1213
|
+
platform_themes: PlatformThemesTable;
|
|
1214
|
+
platform_theme_versions: PlatformThemeVersionsTable;
|
|
1198
1215
|
component_scope_orgs: ComponentScopeOrgsTable;
|
|
1199
1216
|
component_org_switches: ComponentOrgSwitchesTable;
|
|
1200
1217
|
plugin_installs: PluginInstallsTable;
|
|
@@ -1319,7 +1336,7 @@ export interface CatalogItemOrgSwitchesTable {
|
|
|
1319
1336
|
}
|
|
1320
1337
|
type ScopeMode = "follow" | "own";
|
|
1321
1338
|
type ComponentScope = "all" | "optional" | "named";
|
|
1322
|
-
type ComponentKind = "skill" | "agent_template" | "connector_template";
|
|
1339
|
+
type ComponentKind = "skill" | "agent_template" | "connector_template" | "theme";
|
|
1323
1340
|
type Dflt<T> = ColumnType<T, T | undefined, T>;
|
|
1324
1341
|
type NullableDflt<T> = ColumnType<T | null, T | null | undefined, T | null>;
|
|
1325
1342
|
/** 0081: the scope columns every platform component carries (ruling C2). */
|
|
@@ -1409,6 +1426,39 @@ export interface PlatformConnectorTemplatesTable extends PlatformScopeColumns {
|
|
|
1409
1426
|
updated_at: Ts;
|
|
1410
1427
|
deleted_at: TsNullable;
|
|
1411
1428
|
}
|
|
1429
|
+
/** 0093: a theme (themes spec §6); every theme comes from a plugin, owned by the platform or an org. */
|
|
1430
|
+
export interface PlatformThemesTable extends PlatformScopeColumns {
|
|
1431
|
+
id: string;
|
|
1432
|
+
/** The owning org (null = the platform). */
|
|
1433
|
+
owner_org_id: NullableDflt<string>;
|
|
1434
|
+
/** The org it was created in; never changed. */
|
|
1435
|
+
origin_org_id: NullableDflt<string>;
|
|
1436
|
+
/** The owner shares it with other orgs. */
|
|
1437
|
+
shareable: Dflt<boolean>;
|
|
1438
|
+
catalog_item_id: NullableDflt<string>;
|
|
1439
|
+
plugin_name: NullableDflt<string>;
|
|
1440
|
+
name: string;
|
|
1441
|
+
description: NullableDflt<string>;
|
|
1442
|
+
description_zh: NullableDflt<string>;
|
|
1443
|
+
current_version: number;
|
|
1444
|
+
created_at: Ts;
|
|
1445
|
+
updated_at: Ts;
|
|
1446
|
+
deleted_at: TsNullable;
|
|
1447
|
+
}
|
|
1448
|
+
/**
|
|
1449
|
+
* 0093: one version of a theme; `definition` is the parsed theme file, its fonts and logo live
|
|
1450
|
+
* under `object_prefix` in `deployment/` (null without assets).
|
|
1451
|
+
*/
|
|
1452
|
+
export interface PlatformThemeVersionsTable {
|
|
1453
|
+
theme_id: string;
|
|
1454
|
+
version: number;
|
|
1455
|
+
definition: Json<ParsedTheme>;
|
|
1456
|
+
object_prefix: NullableDflt<string>;
|
|
1457
|
+
catalog_version: NullableDflt<string>;
|
|
1458
|
+
updated_on_own: Dflt<boolean>;
|
|
1459
|
+
created_by: NullableDflt<string>;
|
|
1460
|
+
created_at: Ts;
|
|
1461
|
+
}
|
|
1412
1462
|
/** 0081: the orgs an own-scope `named` component is offered to. */
|
|
1413
1463
|
export interface ComponentScopeOrgsTable {
|
|
1414
1464
|
kind: ComponentKind;
|
|
@@ -1628,8 +1678,8 @@ export interface PluginInstallsTable {
|
|
|
1628
1678
|
*/
|
|
1629
1679
|
export interface PluginInstallComponentsTable {
|
|
1630
1680
|
install_id: string;
|
|
1631
|
-
/** 0053: 'agent_template', ref = the agent_templates id. */
|
|
1632
|
-
kind: "extension" | "connector" | "skill" | "plugin" | "agent_template";
|
|
1681
|
+
/** 0053: 'agent_template', ref = the agent_templates id; 0093: 'theme', ref = the platform_themes id. */
|
|
1682
|
+
kind: "extension" | "connector" | "skill" | "plugin" | "agent_template" | "theme";
|
|
1633
1683
|
ref: string;
|
|
1634
1684
|
created: boolean;
|
|
1635
1685
|
skill_version: NullableCol<number>;
|
package/dist/errors.d.ts
CHANGED
|
@@ -108,6 +108,31 @@ export declare const ERROR_CODES: {
|
|
|
108
108
|
/** What deleting an org's own plugin does changed since its preview (P3-19). */
|
|
109
109
|
readonly "catalog.preview_stale": 409;
|
|
110
110
|
readonly "catalog.name_conflict": 409;
|
|
111
|
+
/** A theme of an imported plugin fails its file or asset checks (themes spec §4, §5). */
|
|
112
|
+
readonly "theme.invalid": 400;
|
|
113
|
+
/** A theme of an imported plugin fails the readability checks of its merged schemes (§5). */
|
|
114
|
+
readonly "theme.unreadable": 400;
|
|
115
|
+
/**
|
|
116
|
+
* A settings write names a theme that is not available where it is set (§7, §9): an org default
|
|
117
|
+
* off in the org, a platform default that is not a platform theme with scope `all`, or a
|
|
118
|
+
* person's pick on in none of their orgs; details `[{ theme }]`.
|
|
119
|
+
*/
|
|
120
|
+
readonly "theme.not_available": 400;
|
|
121
|
+
/**
|
|
122
|
+
* The change would take the platform default theme (`uiTheme`) away from an org: narrowing,
|
|
123
|
+
* disabling, removing it or its plugin (§7); details `[{ theme, setting: "uiTheme" }]`.
|
|
124
|
+
*/
|
|
125
|
+
readonly "theme.platform_default": 409;
|
|
126
|
+
/**
|
|
127
|
+
* Another theme with the same id is already on in an org the change would turn this one on in
|
|
128
|
+
* (§6, rule 10); details `[{ theme, orgId, themes, plugins }]`.
|
|
129
|
+
*/
|
|
130
|
+
readonly "theme.id_taken": 409;
|
|
131
|
+
/**
|
|
132
|
+
* A theme delivery URL names no stored theme version, or a file that version does not name (§8);
|
|
133
|
+
* a removed theme's versions are still served.
|
|
134
|
+
*/
|
|
135
|
+
readonly "theme.not_found": 404;
|
|
111
136
|
readonly "directory.not_configured": 409;
|
|
112
137
|
readonly "catalog.item_disabled": 403;
|
|
113
138
|
readonly "plugin.disabled": 409;
|
|
@@ -226,6 +251,8 @@ export declare const ERROR_CODES: {
|
|
|
226
251
|
readonly "connector.tool_not_found": 404;
|
|
227
252
|
readonly "connector.tool_not_pending": 409;
|
|
228
253
|
readonly "connector.resources_disabled": 409;
|
|
254
|
+
/** Per-member tool policies on a connector with one org credential (decision 2026-11-09). */
|
|
255
|
+
readonly "connector.not_personal": 409;
|
|
229
256
|
readonly "approval.not_found": 404;
|
|
230
257
|
readonly "approval.already_decided": 409;
|
|
231
258
|
readonly "approval.scope_not_allowed": 409;
|
package/dist/errors.js
CHANGED
|
@@ -108,6 +108,31 @@ export const ERROR_CODES = {
|
|
|
108
108
|
/** What deleting an org's own plugin does changed since its preview (P3-19). */
|
|
109
109
|
"catalog.preview_stale": 409,
|
|
110
110
|
"catalog.name_conflict": 409,
|
|
111
|
+
/** A theme of an imported plugin fails its file or asset checks (themes spec §4, §5). */
|
|
112
|
+
"theme.invalid": 400,
|
|
113
|
+
/** A theme of an imported plugin fails the readability checks of its merged schemes (§5). */
|
|
114
|
+
"theme.unreadable": 400,
|
|
115
|
+
/**
|
|
116
|
+
* A settings write names a theme that is not available where it is set (§7, §9): an org default
|
|
117
|
+
* off in the org, a platform default that is not a platform theme with scope `all`, or a
|
|
118
|
+
* person's pick on in none of their orgs; details `[{ theme }]`.
|
|
119
|
+
*/
|
|
120
|
+
"theme.not_available": 400,
|
|
121
|
+
/**
|
|
122
|
+
* The change would take the platform default theme (`uiTheme`) away from an org: narrowing,
|
|
123
|
+
* disabling, removing it or its plugin (§7); details `[{ theme, setting: "uiTheme" }]`.
|
|
124
|
+
*/
|
|
125
|
+
"theme.platform_default": 409,
|
|
126
|
+
/**
|
|
127
|
+
* Another theme with the same id is already on in an org the change would turn this one on in
|
|
128
|
+
* (§6, rule 10); details `[{ theme, orgId, themes, plugins }]`.
|
|
129
|
+
*/
|
|
130
|
+
"theme.id_taken": 409,
|
|
131
|
+
/**
|
|
132
|
+
* A theme delivery URL names no stored theme version, or a file that version does not name (§8);
|
|
133
|
+
* a removed theme's versions are still served.
|
|
134
|
+
*/
|
|
135
|
+
"theme.not_found": 404,
|
|
111
136
|
"directory.not_configured": 409,
|
|
112
137
|
"catalog.item_disabled": 403,
|
|
113
138
|
"plugin.disabled": 409,
|
|
@@ -226,6 +251,8 @@ export const ERROR_CODES = {
|
|
|
226
251
|
"connector.tool_not_found": 404,
|
|
227
252
|
"connector.tool_not_pending": 409,
|
|
228
253
|
"connector.resources_disabled": 409,
|
|
254
|
+
/** Per-member tool policies on a connector with one org credential (decision 2026-11-09). */
|
|
255
|
+
"connector.not_personal": 409,
|
|
229
256
|
"approval.not_found": 404,
|
|
230
257
|
"approval.already_decided": 409,
|
|
231
258
|
"approval.scope_not_allowed": 409,
|
package/dist/http/api.module.js
CHANGED
|
@@ -65,6 +65,9 @@ import { InvocableSkillsController } from "../skills/invocable.controller.js";
|
|
|
65
65
|
import { SkillsController } from "../skills/skills.controller.js";
|
|
66
66
|
import { SkillsModule } from "../skills/skills.module.js";
|
|
67
67
|
import { StorageModule } from "../storage/storage.module.js";
|
|
68
|
+
import { OrgThemesController } from "../themes/themes.controller.js";
|
|
69
|
+
import { ThemesModule } from "../themes/themes.module.js";
|
|
70
|
+
import { PlatformThemeController, ThemeDeliveryController, } from "../themes/themes-public.controller.js";
|
|
68
71
|
import { ApprovalsController, ToolGrantsController } from "../tools/approvals.controller.js";
|
|
69
72
|
import { ConnectorContentController } from "../tools/connector-content.controller.js";
|
|
70
73
|
import { ConnectorsController } from "../tools/connectors.controller.js";
|
|
@@ -135,6 +138,9 @@ const CONTROLLERS = [
|
|
|
135
138
|
OrgOwnedPluginsController,
|
|
136
139
|
OrgComponentsController,
|
|
137
140
|
AdminCatalogRequestsController,
|
|
141
|
+
OrgThemesController,
|
|
142
|
+
ThemeDeliveryController,
|
|
143
|
+
PlatformThemeController,
|
|
138
144
|
DeviceAuthController,
|
|
139
145
|
];
|
|
140
146
|
/**
|
|
@@ -168,6 +174,7 @@ ApiModule = __decorate([
|
|
|
168
174
|
FilesModule,
|
|
169
175
|
UiModule,
|
|
170
176
|
InboxModule,
|
|
177
|
+
ThemesModule,
|
|
171
178
|
],
|
|
172
179
|
controllers: CONTROLLERS,
|
|
173
180
|
providers: [{ provide: APP_GUARD, useClass: AuthGuard }],
|
|
@@ -100,17 +100,24 @@ export declare class AccountsService {
|
|
|
100
100
|
verifyPassword(accountId: string, password: string): Promise<boolean>;
|
|
101
101
|
get(id: string): Promise<Account | undefined>;
|
|
102
102
|
/**
|
|
103
|
-
* A person's preferences
|
|
104
|
-
*
|
|
103
|
+
* A person's preferences: `timeZone` is their time zone setting (IANA), null = follow the browser
|
|
104
|
+
* (run context spec §3.4); `theme` their theme (a `platform_themes` id), null = follow the org
|
|
105
|
+
* (themes spec §7). Missing account -> nulls.
|
|
105
106
|
*/
|
|
106
107
|
preferences(accountId: string): Promise<{
|
|
107
108
|
timeZone: string | null;
|
|
109
|
+
theme: string | null;
|
|
108
110
|
}>;
|
|
109
|
-
/**
|
|
111
|
+
/**
|
|
112
|
+
* Updates the preferences given (a field left out keeps its value; the caller validated the time
|
|
113
|
+
* zone). A theme must be on in at least one of the person's orgs, else 400 `theme.not_available`.
|
|
114
|
+
*/
|
|
110
115
|
setPreferences(accountId: string, prefs: {
|
|
111
|
-
timeZone
|
|
116
|
+
timeZone?: string | null | undefined;
|
|
117
|
+
theme?: string | null | undefined;
|
|
112
118
|
}): Promise<{
|
|
113
119
|
timeZone: string | null;
|
|
120
|
+
theme: string | null;
|
|
114
121
|
}>;
|
|
115
122
|
/** 按 id 升序分页,`after` 为上一页最后一个 id。 */
|
|
116
123
|
list(opts: {
|
|
@@ -15,6 +15,7 @@ import { ApiError } from "../errors.js";
|
|
|
15
15
|
import { KernelDb } from "../infra/database/kernel-db.js";
|
|
16
16
|
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
17
17
|
import { Transactional } from "../infra/database/transactional.decorator.js";
|
|
18
|
+
import { assertThemeOnForAccount } from "../themes/theme-state.js";
|
|
18
19
|
import { audit } from "./audit.js";
|
|
19
20
|
import { MAX_PASSWORD_LENGTH, normalizeLogin, validatePassword } from "./login.js";
|
|
20
21
|
import { DUMMY_PASSWORD_HASH, hashPassword, verifyPassword } from "./passwords.js";
|
|
@@ -237,24 +238,36 @@ let AccountsService = class AccountsService {
|
|
|
237
238
|
return row && toAccount(row);
|
|
238
239
|
}
|
|
239
240
|
/**
|
|
240
|
-
* A person's preferences
|
|
241
|
-
*
|
|
241
|
+
* A person's preferences: `timeZone` is their time zone setting (IANA), null = follow the browser
|
|
242
|
+
* (run context spec §3.4); `theme` their theme (a `platform_themes` id), null = follow the org
|
|
243
|
+
* (themes spec §7). Missing account -> nulls.
|
|
242
244
|
*/
|
|
243
245
|
async preferences(accountId) {
|
|
244
246
|
const row = await this.db
|
|
245
247
|
.selectFrom("accounts")
|
|
246
|
-
.select("time_zone")
|
|
248
|
+
.select(["time_zone", "theme"])
|
|
247
249
|
.where("id", "=", accountId)
|
|
248
250
|
.executeTakeFirst();
|
|
249
|
-
return { timeZone: row?.time_zone ?? null };
|
|
251
|
+
return { timeZone: row?.time_zone ?? null, theme: row?.theme ?? null };
|
|
250
252
|
}
|
|
251
|
-
/**
|
|
253
|
+
/**
|
|
254
|
+
* Updates the preferences given (a field left out keeps its value; the caller validated the time
|
|
255
|
+
* zone). A theme must be on in at least one of the person's orgs, else 400 `theme.not_available`.
|
|
256
|
+
*/
|
|
252
257
|
async setPreferences(accountId, prefs) {
|
|
253
|
-
|
|
254
|
-
.
|
|
255
|
-
|
|
256
|
-
.
|
|
257
|
-
.
|
|
258
|
+
if (prefs.theme != null)
|
|
259
|
+
await assertThemeOnForAccount(this.db.db, accountId, prefs.theme);
|
|
260
|
+
const set = {
|
|
261
|
+
...(prefs.timeZone !== undefined && { time_zone: prefs.timeZone }),
|
|
262
|
+
...(prefs.theme !== undefined && { theme: prefs.theme }),
|
|
263
|
+
};
|
|
264
|
+
if (Object.keys(set).length > 0) {
|
|
265
|
+
await this.db
|
|
266
|
+
.updateTable("accounts")
|
|
267
|
+
.set({ ...set, updated_at: new Date() })
|
|
268
|
+
.where("id", "=", accountId)
|
|
269
|
+
.execute();
|
|
270
|
+
}
|
|
258
271
|
return this.preferences(accountId);
|
|
259
272
|
}
|
|
260
273
|
/** 按 id 升序分页,`after` 为上一页最后一个 id。 */
|
|
@@ -6,7 +6,7 @@ import { OrgsService } from "../orgs/orgs.service.js";
|
|
|
6
6
|
import type { OrgRole } from "../orgs/roles.js";
|
|
7
7
|
import { type Account, AccountsService } from "./accounts.js";
|
|
8
8
|
import { type ApiTokenRecord, ApiTokenService } from "./api-tokens.js";
|
|
9
|
-
import { type PreferencesSchema } from "./me.schemas.js";
|
|
9
|
+
import { type Preferences, type PreferencesSchema } from "./me.schemas.js";
|
|
10
10
|
import { type Principal } from "./principal.js";
|
|
11
11
|
import { SessionService } from "./sessions.js";
|
|
12
12
|
import { SettingsService } from "./settings.js";
|
|
@@ -46,16 +46,16 @@ export declare class MeController {
|
|
|
46
46
|
canCreateOrg: boolean;
|
|
47
47
|
}>;
|
|
48
48
|
/**
|
|
49
|
-
* The person's preferences
|
|
50
|
-
*
|
|
49
|
+
* The person's preferences: `timeZone` null = follow the browser's time zone sent with each
|
|
50
|
+
* message, a set zone overrides it for the messages sent afterwards (run context spec §3.4);
|
|
51
|
+
* `theme` null = follow the org (themes spec §7).
|
|
51
52
|
*/
|
|
52
|
-
preferences(principal: Principal): Promise<
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}>;
|
|
53
|
+
preferences(principal: Principal): Promise<Preferences>;
|
|
54
|
+
/**
|
|
55
|
+
* Updates the preferences given; a field left out keeps its value. An unknown time zone → 400
|
|
56
|
+
* `validation_failed`; a theme on in none of the person's orgs → 400 `theme.not_available`.
|
|
57
|
+
*/
|
|
58
|
+
setPreferences(principal: Principal, body: z.infer<typeof PreferencesSchema>): Promise<Preferences>;
|
|
59
59
|
/** 切换当前组织(仅 Web 会话);`null` 清空。非成员或已软删除的组织 → `org.not_found`。 */
|
|
60
60
|
setCurrentOrg(principal: Principal, body: z.infer<typeof CurrentOrgSchema>): Promise<void>;
|
|
61
61
|
/**
|
|
@@ -74,17 +74,19 @@ let MeController = class MeController {
|
|
|
74
74
|
return { account: p.account, via: p.via, currentOrgId: current, memberships, canCreateOrg };
|
|
75
75
|
}
|
|
76
76
|
/**
|
|
77
|
-
* The person's preferences
|
|
78
|
-
*
|
|
77
|
+
* The person's preferences: `timeZone` null = follow the browser's time zone sent with each
|
|
78
|
+
* message, a set zone overrides it for the messages sent afterwards (run context spec §3.4);
|
|
79
|
+
* `theme` null = follow the org (themes spec §7).
|
|
79
80
|
*/
|
|
80
81
|
async preferences(principal) {
|
|
81
82
|
return this.accounts.preferences(requireUser(principal).account.id);
|
|
82
83
|
}
|
|
83
|
-
/**
|
|
84
|
+
/**
|
|
85
|
+
* Updates the preferences given; a field left out keeps its value. An unknown time zone → 400
|
|
86
|
+
* `validation_failed`; a theme on in none of the person's orgs → 400 `theme.not_available`.
|
|
87
|
+
*/
|
|
84
88
|
async setPreferences(principal, body) {
|
|
85
|
-
return this.accounts.setPreferences(requireUser(principal).account.id,
|
|
86
|
-
timeZone: body.timeZone,
|
|
87
|
-
});
|
|
89
|
+
return this.accounts.setPreferences(requireUser(principal).account.id, body);
|
|
88
90
|
}
|
|
89
91
|
/** 切换当前组织(仅 Web 会话);`null` 清空。非成员或已软删除的组织 → `org.not_found`。 */
|
|
90
92
|
async setCurrentOrg(principal, body) {
|
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import type { RouteSchemas } from "../platform/route-schemas.js";
|
|
3
|
-
/**
|
|
3
|
+
/**
|
|
4
|
+
* `PUT /me/preferences`: a partial update of the person's preferences; a field left out keeps its
|
|
5
|
+
* stored value (themes spec §9).
|
|
6
|
+
*/
|
|
4
7
|
export declare const PreferencesSchema: z.ZodObject<{
|
|
5
|
-
timeZone: z.ZodNullable<z.ZodString
|
|
8
|
+
timeZone: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
9
|
+
theme: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
6
10
|
}, z.core.$strict>;
|
|
11
|
+
/** The person's preferences as `GET` / `PUT /me/preferences` return them. */
|
|
12
|
+
export interface Preferences {
|
|
13
|
+
timeZone: string | null;
|
|
14
|
+
theme: string | null;
|
|
15
|
+
}
|
|
7
16
|
declare const PreferencesBody_base: import("../infra/validation/create-zod-dto.js").ZodDtoClass<z.ZodObject<{
|
|
8
|
-
timeZone: z.ZodNullable<z.ZodString
|
|
17
|
+
timeZone: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
18
|
+
theme: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
9
19
|
}, z.core.$strict>>;
|
|
10
20
|
export declare class PreferencesBody extends PreferencesBody_base {
|
|
11
21
|
}
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { createZodDto } from "../infra/validation/create-zod-dto.js";
|
|
3
3
|
import { IanaTimeZone } from "../time-zone.js";
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* `PUT /me/preferences`: a partial update of the person's preferences; a field left out keeps its
|
|
6
|
+
* stored value (themes spec §9).
|
|
7
|
+
*/
|
|
5
8
|
export const PreferencesSchema = z
|
|
6
9
|
.object({
|
|
7
10
|
/** The person's time zone (IANA); null = follow the browser (run context spec §3.4). */
|
|
8
|
-
timeZone: IanaTimeZone.nullable(),
|
|
11
|
+
timeZone: IanaTimeZone.nullable().optional(),
|
|
12
|
+
/**
|
|
13
|
+
* The person's theme (a `platform_themes` id on in at least one of their orgs, else 400
|
|
14
|
+
* `theme.not_available`); null = follow the org (themes spec §7).
|
|
15
|
+
*/
|
|
16
|
+
theme: z.string().min(1).max(200).nullable().optional(),
|
|
9
17
|
})
|
|
10
18
|
.strict();
|
|
11
19
|
export class PreferencesBody extends createZodDto(PreferencesSchema, "PreferencesBody") {
|
|
@@ -31,6 +31,18 @@ export declare const SUBAGENT_LIMIT_OVERRIDE: z.ZodObject<{
|
|
|
31
31
|
maxTokensPerRun: z.ZodOptional<z.ZodNumber>;
|
|
32
32
|
}, z.core.$strict>;
|
|
33
33
|
export type SubagentLimitOverride = z.infer<typeof SUBAGENT_LIMIT_OVERRIDE>;
|
|
34
|
+
/** The deployment setting key of the platform default theme (themes spec §7). */
|
|
35
|
+
export declare const UI_THEME_SETTING = "uiTheme";
|
|
36
|
+
/**
|
|
37
|
+
* The platform default theme (themes spec §7): a `platform_themes` id, or null for the built-in
|
|
38
|
+
* theme. Only a platform-owned theme whose effective scope is `all` can be set (checked by
|
|
39
|
+
* {@link SettingsService.update}); every reader goes through {@link readUiTheme} or
|
|
40
|
+
* {@link SettingsService.get}, both parsing with this schema.
|
|
41
|
+
*/
|
|
42
|
+
export declare const UiThemeSetting: z.ZodObject<{
|
|
43
|
+
id: z.ZodNullable<z.ZodString>;
|
|
44
|
+
}, z.core.$strip>;
|
|
45
|
+
export type UiTheme = z.infer<typeof UiThemeSetting>;
|
|
34
46
|
export declare const DEPLOYMENT_SETTINGS: z.ZodObject<{
|
|
35
47
|
orgCreation: z.ZodDefault<z.ZodEnum<{
|
|
36
48
|
anyone: "anyone";
|
|
@@ -57,6 +69,9 @@ export declare const DEPLOYMENT_SETTINGS: z.ZodObject<{
|
|
|
57
69
|
deny: "deny";
|
|
58
70
|
}>>;
|
|
59
71
|
orgMarketplaceBrowse: z.ZodDefault<z.ZodBoolean>;
|
|
72
|
+
uiTheme: z.ZodDefault<z.ZodObject<{
|
|
73
|
+
id: z.ZodNullable<z.ZodString>;
|
|
74
|
+
}, z.core.$strip>>;
|
|
60
75
|
}, z.core.$strip>;
|
|
61
76
|
export type DeploymentSettings = z.infer<typeof DEPLOYMENT_SETTINGS>;
|
|
62
77
|
/**
|
|
@@ -93,6 +108,9 @@ export declare const DEPLOYMENT_SETTINGS_PATCH: z.ZodObject<{
|
|
|
93
108
|
deny: "deny";
|
|
94
109
|
}>>;
|
|
95
110
|
orgMarketplaceBrowse: z.ZodOptional<z.ZodBoolean>;
|
|
111
|
+
uiTheme: z.ZodOptional<z.ZodObject<{
|
|
112
|
+
id: z.ZodNullable<z.ZodString>;
|
|
113
|
+
}, z.core.$strict>>;
|
|
96
114
|
}, z.core.$strict>;
|
|
97
115
|
export type DeploymentSettingsPatch = z.infer<typeof DEPLOYMENT_SETTINGS_PATCH>;
|
|
98
116
|
/**
|
|
@@ -118,6 +136,15 @@ export declare function assertCustomAllowed(db: KernelSchemaDb, key: "customConn
|
|
|
118
136
|
* when there is one); missing or invalid -> the default, true.
|
|
119
137
|
*/
|
|
120
138
|
export declare function readOrgMarketplaceBrowse(db: KernelSchemaDb): Promise<boolean>;
|
|
139
|
+
/**
|
|
140
|
+
* The platform default theme setting, read live from PG (in the caller's transaction when there is
|
|
141
|
+
* one) and parsed with {@link UiThemeSetting}: missing or invalid -> `{ id: null }`. `lock`: the
|
|
142
|
+
* row is read FOR SHARE, so a concurrent write of the setting (which holds it FOR UPDATE) waits for
|
|
143
|
+
* the caller's transaction, and the reverse.
|
|
144
|
+
*/
|
|
145
|
+
export declare function readUiTheme(db: KernelSchemaDb, opts?: {
|
|
146
|
+
lock?: boolean;
|
|
147
|
+
}): Promise<UiTheme>;
|
|
121
148
|
/** Deployment settings: one row per key, defaults for missing keys. */
|
|
122
149
|
export declare class SettingsService {
|
|
123
150
|
private readonly db;
|
|
@@ -14,6 +14,7 @@ import { KernelDb } from "../infra/database/kernel-db.js";
|
|
|
14
14
|
import { TransactionHost } from "../infra/database/transaction-host.js";
|
|
15
15
|
import { Transactional } from "../infra/database/transactional.decorator.js";
|
|
16
16
|
import { KERNEL_LOGGER } from "../infra/tokens.js";
|
|
17
|
+
import { assertPlatformDefaultEligible, lockThemeShared } from "../themes/theme-state.js";
|
|
17
18
|
import { audit } from "./audit.js";
|
|
18
19
|
import { SuperAdminLock } from "./super-admin.js";
|
|
19
20
|
/** 币种代码:ISO 4217 的三位大写字母(如 USD、CNY)。 */
|
|
@@ -63,6 +64,15 @@ export const SUBAGENT_LIMIT_OVERRIDE = z
|
|
|
63
64
|
});
|
|
64
65
|
/** 组织能否自建连接器 / 技能(C22):`deny` 时只能从平台目录安装(T26 执行)。 */
|
|
65
66
|
const CustomPolicy = z.enum(["allow", "deny"]);
|
|
67
|
+
/** The deployment setting key of the platform default theme (themes spec §7). */
|
|
68
|
+
export const UI_THEME_SETTING = "uiTheme";
|
|
69
|
+
/**
|
|
70
|
+
* The platform default theme (themes spec §7): a `platform_themes` id, or null for the built-in
|
|
71
|
+
* theme. Only a platform-owned theme whose effective scope is `all` can be set (checked by
|
|
72
|
+
* {@link SettingsService.update}); every reader goes through {@link readUiTheme} or
|
|
73
|
+
* {@link SettingsService.get}, both parsing with this schema.
|
|
74
|
+
*/
|
|
75
|
+
export const UiThemeSetting = z.object({ id: z.string().min(1).max(200).nullable() });
|
|
66
76
|
export const DEPLOYMENT_SETTINGS = z.object({
|
|
67
77
|
orgCreation: z.enum(["anyone", "super_admin"]).default("anyone"),
|
|
68
78
|
/** 按组织覆盖运行并发上限(orgId → 上限);未列出的组织用 `runtime.orgConcurrency`。 */
|
|
@@ -88,6 +98,8 @@ export const DEPLOYMENT_SETTINGS = z.object({
|
|
|
88
98
|
* scope spec §5.1 rule 3). false hides the browse route and refuses those requests.
|
|
89
99
|
*/
|
|
90
100
|
orgMarketplaceBrowse: z.boolean().default(true),
|
|
101
|
+
/** The platform default theme (themes spec §7); `{ id: null }`: the built-in theme. */
|
|
102
|
+
uiTheme: UiThemeSetting.default({ id: null }),
|
|
91
103
|
});
|
|
92
104
|
const DEFAULTS = DEPLOYMENT_SETTINGS.parse({});
|
|
93
105
|
/**
|
|
@@ -114,6 +126,11 @@ export const DEPLOYMENT_SETTINGS_PATCH = z
|
|
|
114
126
|
customConnectors: CustomPolicy.optional(),
|
|
115
127
|
customSkills: CustomPolicy.optional(),
|
|
116
128
|
orgMarketplaceBrowse: z.boolean().optional(),
|
|
129
|
+
/**
|
|
130
|
+
* The platform default theme: a platform-owned theme whose effective scope is `all`, not
|
|
131
|
+
* disabled (else 400 `theme.not_available`), or `{ id: null }` for the built-in theme.
|
|
132
|
+
*/
|
|
133
|
+
uiTheme: UiThemeSetting.strict().optional(),
|
|
117
134
|
})
|
|
118
135
|
.strict();
|
|
119
136
|
/**
|
|
@@ -191,6 +208,38 @@ export async function readOrgMarketplaceBrowse(db) {
|
|
|
191
208
|
const parsed = z.boolean().safeParse(row.value);
|
|
192
209
|
return parsed.success ? parsed.data : true;
|
|
193
210
|
}
|
|
211
|
+
/**
|
|
212
|
+
* The platform default theme setting, read live from PG (in the caller's transaction when there is
|
|
213
|
+
* one) and parsed with {@link UiThemeSetting}: missing or invalid -> `{ id: null }`. `lock`: the
|
|
214
|
+
* row is read FOR SHARE, so a concurrent write of the setting (which holds it FOR UPDATE) waits for
|
|
215
|
+
* the caller's transaction, and the reverse.
|
|
216
|
+
*/
|
|
217
|
+
export async function readUiTheme(db, opts = {}) {
|
|
218
|
+
let q = db.selectFrom("deployment_settings").select("value").where("key", "=", UI_THEME_SETTING);
|
|
219
|
+
if (opts.lock)
|
|
220
|
+
q = q.forShare();
|
|
221
|
+
const row = await q.executeTakeFirst();
|
|
222
|
+
const parsed = UiThemeSetting.safeParse(row?.value);
|
|
223
|
+
return parsed.success ? parsed.data : { id: null };
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Locks the `uiTheme` row FOR UPDATE in the caller's transaction, creating it as `{ id: null }`
|
|
227
|
+
* first when it is missing (migration 0094 seeds it; a test may have truncated the table), so the
|
|
228
|
+
* FOR SHARE readers always find a row to wait on.
|
|
229
|
+
*/
|
|
230
|
+
async function lockUiThemeRow(db) {
|
|
231
|
+
await db
|
|
232
|
+
.insertInto("deployment_settings")
|
|
233
|
+
.values({ key: UI_THEME_SETTING, value: JSON.stringify({ id: null }) })
|
|
234
|
+
.onConflict((oc) => oc.column("key").doNothing())
|
|
235
|
+
.execute();
|
|
236
|
+
await db
|
|
237
|
+
.selectFrom("deployment_settings")
|
|
238
|
+
.select("key")
|
|
239
|
+
.where("key", "=", UI_THEME_SETTING)
|
|
240
|
+
.forUpdate()
|
|
241
|
+
.execute();
|
|
242
|
+
}
|
|
194
243
|
/** 按组织合并覆盖:值为 null 删除该组织,其余整项替换;未列出的组织不变。 */
|
|
195
244
|
function mergeByOrg(current, patch) {
|
|
196
245
|
const merged = { ...current };
|
|
@@ -267,6 +316,21 @@ let SettingsService = class SettingsService {
|
|
|
267
316
|
if (patch.orgMarketplaceBrowse !== undefined) {
|
|
268
317
|
next.orgMarketplaceBrowse = patch.orgMarketplaceBrowse;
|
|
269
318
|
}
|
|
319
|
+
if (patch.uiTheme !== undefined) {
|
|
320
|
+
// The one theme lock order (themes spec §7), inside the super admin lock above: the chosen
|
|
321
|
+
// theme's catalog item, then its `platform_themes` row FOR SHARE, then the `uiTheme` row FOR
|
|
322
|
+
// UPDATE, and only then the eligibility check (on in every live org). The writers that take
|
|
323
|
+
// a theme away or switch it off in an org (scope, disable, removal, the org switch and
|
|
324
|
+
// uninstall) hold the item or theme row before they read `uiTheme` FOR SHARE, so they and
|
|
325
|
+
// this write wait for each other in the same order and never deadlock.
|
|
326
|
+
if (patch.uiTheme.id !== null)
|
|
327
|
+
await lockThemeShared(this.db.db, patch.uiTheme.id);
|
|
328
|
+
await lockUiThemeRow(this.db.db);
|
|
329
|
+
if (patch.uiTheme.id !== null) {
|
|
330
|
+
await assertPlatformDefaultEligible(this.db.db, patch.uiTheme.id);
|
|
331
|
+
}
|
|
332
|
+
next.uiTheme = { id: patch.uiTheme.id };
|
|
333
|
+
}
|
|
270
334
|
const { runConcurrencyOverrides: runPatch, sandboxLimitOverrides: sandboxPatch, subagentLimitOverrides: subagentPatch, } = patch;
|
|
271
335
|
if (runPatch !== undefined || sandboxPatch !== undefined || subagentPatch !== undefined) {
|
|
272
336
|
const current = await this.get();
|
|
@@ -19,6 +19,8 @@ import { OrgLock } from "./org-lock.js";
|
|
|
19
19
|
* `read-only` (writes listed as unavailable) or `off` (default `on`).
|
|
20
20
|
* - `timeZone`: the org's default time zone (IANA; absent = UTC) for senders without one of their
|
|
21
21
|
* own (run context spec §3.3); an invalid stored value reads as absent.
|
|
22
|
+
* - `theme`: the org's default theme and whether members may pick their own (themes spec §7);
|
|
23
|
+
* absent (or an invalid stored value) = follow the platform, personal picks allowed.
|
|
22
24
|
*/
|
|
23
25
|
/** 组织沙箱出站白名单的主机数上限。 */
|
|
24
26
|
export declare const ORG_SANDBOX_ALLOW_HOSTS_MAX = 200;
|
|
@@ -34,6 +36,21 @@ export declare const SandboxEgressSettingSchema: z.ZodObject<{
|
|
|
34
36
|
allowHosts: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
35
37
|
}, z.core.$strict>;
|
|
36
38
|
export type SandboxEgressSetting = z.infer<typeof SandboxEgressSettingSchema>;
|
|
39
|
+
/**
|
|
40
|
+
* The org's theme setting (themes spec §7): `id` a theme on in the org (a `platform_themes` id),
|
|
41
|
+
* null = follow the platform's default; `personal: "locked"` hides the personal picker in the org
|
|
42
|
+
* and resolution ignores personal picks there.
|
|
43
|
+
*/
|
|
44
|
+
export declare const OrgThemeSettingSchema: z.ZodObject<{
|
|
45
|
+
id: z.ZodNullable<z.ZodString>;
|
|
46
|
+
personal: z.ZodEnum<{
|
|
47
|
+
allowed: "allowed";
|
|
48
|
+
locked: "locked";
|
|
49
|
+
}>;
|
|
50
|
+
}, z.core.$strict>;
|
|
51
|
+
export type OrgThemeSetting = z.infer<typeof OrgThemeSettingSchema>;
|
|
52
|
+
/** The theme setting of an org that has none: follow the platform, personal picks allowed. */
|
|
53
|
+
export declare const DEFAULT_ORG_THEME: OrgThemeSetting;
|
|
37
54
|
export declare const OrgSettingsSchema: z.ZodObject<{
|
|
38
55
|
toolSearch: z.ZodCatch<z.ZodDefault<z.ZodEnum<{
|
|
39
56
|
off: "off";
|
|
@@ -61,6 +78,13 @@ export declare const OrgSettingsSchema: z.ZodObject<{
|
|
|
61
78
|
}, z.core.$strict>>>;
|
|
62
79
|
wakeOnBackgroundTask: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
63
80
|
timeZone: z.ZodCatch<z.ZodOptional<z.ZodString>>;
|
|
81
|
+
theme: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
82
|
+
id: z.ZodNullable<z.ZodString>;
|
|
83
|
+
personal: z.ZodEnum<{
|
|
84
|
+
allowed: "allowed";
|
|
85
|
+
locked: "locked";
|
|
86
|
+
}>;
|
|
87
|
+
}, z.core.$strict>>>;
|
|
64
88
|
}, z.core.$strip>;
|
|
65
89
|
export type OrgSettings = z.infer<typeof OrgSettingsSchema>;
|
|
66
90
|
/** PATCH 体:只改给出的字段;未知字段 → 400。 */
|
|
@@ -91,6 +115,13 @@ export declare const OrgSettingsPatchSchema: z.ZodObject<{
|
|
|
91
115
|
allowHosts: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
92
116
|
}, z.core.$strict>>>;
|
|
93
117
|
timeZone: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
118
|
+
theme: z.ZodOptional<z.ZodNullable<z.ZodObject<{
|
|
119
|
+
id: z.ZodNullable<z.ZodString>;
|
|
120
|
+
personal: z.ZodEnum<{
|
|
121
|
+
allowed: "allowed";
|
|
122
|
+
locked: "locked";
|
|
123
|
+
}>;
|
|
124
|
+
}, z.core.$strict>>>;
|
|
94
125
|
}, z.core.$strict>;
|
|
95
126
|
export type OrgSettingsPatch = z.infer<typeof OrgSettingsPatchSchema>;
|
|
96
127
|
/** 部署的沙箱出站(`sandbox.limits.egress` + `allowHosts`)。 */
|