@x-otto/setting 0.0.1-alpha.4 → 0.1.0-alpha.9
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/README.md +1 -1
- package/dist/index.d.ts +149 -69
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> Configuration loading, merging, validation, migration, and monitoring. Single source of truth for the config shape via zod schema.
|
|
4
4
|
|
|
5
|
-
`@x-otto/setting` manages the entire configuration lifecycle: loading from file/env/memory sources, deep merging with precedence layering, version migration, validation with lenient error handling, and remote sync. The config shape is defined by a canonical zod schema (`schema.ts`) from which the `Setting` type and `SETTING_KEYS` are derived.
|
|
5
|
+
`@x-otto/setting` manages the entire configuration lifecycle: loading from file/env/memory sources, deep merging with precedence layering, version migration, validation with lenient error handling, and remote sync. The config shape is defined by a canonical zod schema (`schema.ts`) from which the `Setting` type and `SETTING_KEYS` are derived. `permissions` field merges by `policy.name` key (RFC-404 D4: same-name policies across layers are merged by override, not array union). `installed_plugins` persist is always routed to the global config layer (RFC-404 D3).
|
|
6
6
|
|
|
7
7
|
## Installation
|
|
8
8
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { PERMISSION_MODES, PermissionMode, PermissionPolicySetting, PermissionRuleConfig } from "@x-otto/hook-contracts";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { TypedEventEmitter } from "@x-otto/shared";
|
|
4
|
+
import { ResilienceConfigOverride } from "@x-otto/env";
|
|
4
5
|
import { AgentProfile } from "@x-otto/prompt";
|
|
5
6
|
|
|
6
7
|
//#region src/store.d.ts
|
|
@@ -50,6 +51,9 @@ declare const categoryConfigSchema: z.ZodObject<{
|
|
|
50
51
|
preferred_models: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
51
52
|
default_model: z.ZodCatch<z.ZodOptional<z.ZodString>>;
|
|
52
53
|
}, z.core.$strip>;
|
|
54
|
+
declare const providerOverridesSchema: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
55
|
+
baseUrl: z.ZodString;
|
|
56
|
+
}, z.core.$strict>>;
|
|
53
57
|
/** 会话结束后自动抽取跨会话经验(operational-learning)。默认关,灰度后再放开。 */
|
|
54
58
|
declare const notificationConfigSchema: z.ZodObject<{
|
|
55
59
|
enabled: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
@@ -107,6 +111,7 @@ declare const sandboxConfigSchema: z.ZodObject<{
|
|
|
107
111
|
writablePaths: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
108
112
|
protectCredentials: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
109
113
|
autoAllowBashIfSandboxed: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
114
|
+
semanticReview: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
110
115
|
}, z.core.$strip>;
|
|
111
116
|
declare const proxyConfigSchema: z.ZodObject<{
|
|
112
117
|
url: z.ZodCatch<z.ZodOptional<z.ZodString>>;
|
|
@@ -127,6 +132,24 @@ declare const mcpServerSettingSchema: z.ZodObject<{
|
|
|
127
132
|
autoReconnect: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
128
133
|
disabled: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
129
134
|
}, z.core.$strip>;
|
|
135
|
+
/**
|
|
136
|
+
* RFC-412 M3:resilience override 的**唯一校验真源**(此前 env 手写递归校验器与本 zod schema
|
|
137
|
+
* 双实现,已收敛至此)。把任意 `unknown`(通常来自 `JSON.parse` 或 settings/programmatic 注入)
|
|
138
|
+
* 经 `resilienceConfigSchema` 校验,剔除非法字段/子对象,返回可安全传给
|
|
139
|
+
* `@x-otto/env` `mergeResilienceConfig` 的干净覆盖对象。
|
|
140
|
+
*
|
|
141
|
+
* 剪除 undefined 的必要性:`lenient = optional().catch(undefined)` 对非法字段产出 `key: undefined`,
|
|
142
|
+
* 而 `mergeResilienceConfig` 用 `Object.assign` 深合并——若保留 `key: undefined` 会把默认值覆盖成
|
|
143
|
+
* undefined。故 parse 后递归剪除 undefined 字段与空子对象,复刻 env 原"非法字段整体剔除、
|
|
144
|
+
* 兄弟字段不受影响"语义。非对象输入返回 undefined。
|
|
145
|
+
*/
|
|
146
|
+
declare function validateResilienceOverride(input: unknown): ResilienceConfigOverride | undefined;
|
|
147
|
+
/**
|
|
148
|
+
* RFC-412 M3:解析 `OTTO_RESILIENCE_CONFIG` 环境变量(JSON 字符串)为已校验的覆盖对象。
|
|
149
|
+
* 整体 JSON 解析失败时经 `warn` 上报并返回 undefined(重要诊断,不静默吞);解析成功后走
|
|
150
|
+
* `validateResilienceOverride` 做字段级校验。
|
|
151
|
+
*/
|
|
152
|
+
declare function parseResilienceEnvOverride(raw: string | undefined, warn?: (message: string) => void): ResilienceConfigOverride | undefined;
|
|
130
153
|
/**
|
|
131
154
|
* RFC-036 @ 面板「最近使用」条目快照(MRU,最多 5 条)——`/model` `recent_models` 同款
|
|
132
155
|
* 持久化模式的推广:不止模型,@ 面板选中的任意 sigil 条目(文件/agent/skill/mcp/…)
|
|
@@ -160,8 +183,8 @@ declare const recentMentionEntrySchema: z.ZodObject<{
|
|
|
160
183
|
label: z.ZodString;
|
|
161
184
|
value: z.ZodString;
|
|
162
185
|
kind: z.ZodEnum<{
|
|
163
|
-
model: "model";
|
|
164
186
|
file: "file";
|
|
187
|
+
model: "model";
|
|
165
188
|
hashtag: "hashtag";
|
|
166
189
|
person: "person";
|
|
167
190
|
agent: "agent";
|
|
@@ -951,8 +974,8 @@ declare const SettingSchema: z.ZodObject<{
|
|
|
951
974
|
label: z.ZodString;
|
|
952
975
|
value: z.ZodString;
|
|
953
976
|
kind: z.ZodEnum<{
|
|
954
|
-
model: "model";
|
|
955
977
|
file: "file";
|
|
978
|
+
model: "model";
|
|
956
979
|
hashtag: "hashtag";
|
|
957
980
|
person: "person";
|
|
958
981
|
agent: "agent";
|
|
@@ -970,6 +993,13 @@ declare const SettingSchema: z.ZodObject<{
|
|
|
970
993
|
text: z.ZodString;
|
|
971
994
|
}, z.core.$strip>>>>;
|
|
972
995
|
feedback_repo: z.ZodCatch<z.ZodOptional<z.ZodString>>;
|
|
996
|
+
feedback_platform: z.ZodCatch<z.ZodOptional<z.ZodEnum<{
|
|
997
|
+
auto: "auto";
|
|
998
|
+
github: "github";
|
|
999
|
+
coding: "coding";
|
|
1000
|
+
}>>>;
|
|
1001
|
+
feedback_coding_base_url: z.ZodCatch<z.ZodOptional<z.ZodString>>;
|
|
1002
|
+
feedback_coding_repo: z.ZodCatch<z.ZodOptional<z.ZodString>>;
|
|
973
1003
|
keybinding_overrides: z.ZodCatch<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
974
1004
|
ctrl: z.ZodOptional<z.ZodBoolean>;
|
|
975
1005
|
shift: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -1456,6 +1486,8 @@ declare const SettingSchema: z.ZodObject<{
|
|
|
1456
1486
|
retry: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
1457
1487
|
enabled: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
1458
1488
|
maxAttempts: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
1489
|
+
backoffMs: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
1490
|
+
backoffMultiplier: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
1459
1491
|
}, z.core.$strip>>>;
|
|
1460
1492
|
circuitBreaker: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
1461
1493
|
enabled: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
@@ -1465,6 +1497,12 @@ declare const SettingSchema: z.ZodObject<{
|
|
|
1465
1497
|
routing: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
1466
1498
|
enabled: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
1467
1499
|
}, z.core.$strip>>>;
|
|
1500
|
+
orchestration: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
1501
|
+
maxActiveTasks: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
1502
|
+
maxStoredTasks: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
1503
|
+
maxDelegationDepth: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
1504
|
+
maxFanOutPerParent: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
1505
|
+
}, z.core.$strip>>>;
|
|
1468
1506
|
}, z.core.$strip>>>;
|
|
1469
1507
|
model_slots: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
1470
1508
|
slots: z.ZodCatch<z.ZodOptional<z.ZodRecord<z.ZodEnum<{
|
|
@@ -1483,6 +1521,9 @@ declare const SettingSchema: z.ZodObject<{
|
|
|
1483
1521
|
vision: "vision";
|
|
1484
1522
|
}>>>;
|
|
1485
1523
|
}, z.core.$strip>>>;
|
|
1524
|
+
provider_overrides: z.ZodCatch<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
1525
|
+
baseUrl: z.ZodString;
|
|
1526
|
+
}, z.core.$strict>>>>;
|
|
1486
1527
|
mcp_servers: z.ZodCatch<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
1487
1528
|
command: z.ZodCatch<z.ZodOptional<z.ZodString>>;
|
|
1488
1529
|
args: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
@@ -1534,6 +1575,7 @@ declare const SettingSchema: z.ZodObject<{
|
|
|
1534
1575
|
writablePaths: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
1535
1576
|
protectCredentials: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
1536
1577
|
autoAllowBashIfSandboxed: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
1578
|
+
semanticReview: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
1537
1579
|
}, z.core.$strip>>>;
|
|
1538
1580
|
residency: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
1539
1581
|
total_bytes: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
@@ -1606,6 +1648,7 @@ declare const SettingSchema: z.ZodObject<{
|
|
|
1606
1648
|
}>;
|
|
1607
1649
|
tools: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
1608
1650
|
paths: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
1651
|
+
commands: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
1609
1652
|
deny_reason: z.ZodCatch<z.ZodOptional<z.ZodString>>;
|
|
1610
1653
|
ask_prompt: z.ZodCatch<z.ZodOptional<z.ZodString>>;
|
|
1611
1654
|
}, z.core.$strip>>;
|
|
@@ -1625,7 +1668,6 @@ declare const SettingSchema: z.ZodObject<{
|
|
|
1625
1668
|
_migrations: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
1626
1669
|
}, z.core.$strip>;
|
|
1627
1670
|
type Setting = z.infer<typeof SettingSchema>;
|
|
1628
|
-
type SettingFromSchema = Setting;
|
|
1629
1671
|
type ThemeColorRole = z.infer<typeof themeColorRoleSchema>;
|
|
1630
1672
|
type MarkdownThemeOverridesSetting = z.infer<typeof markdownThemeOverridesSchema>;
|
|
1631
1673
|
type ThemeOverridesSetting = z.infer<typeof themeOverridesSchema>;
|
|
@@ -1636,6 +1678,7 @@ type RecentMentionEntry = z.infer<typeof recentMentionEntrySchema>;
|
|
|
1636
1678
|
type InstalledPluginEntry = z.infer<typeof installedPluginEntrySchema>;
|
|
1637
1679
|
type AgentOptions = z.infer<typeof agentOptionsSchema>;
|
|
1638
1680
|
type CategoryConfig = z.infer<typeof categoryConfigSchema>;
|
|
1681
|
+
type ProviderOverrides = z.infer<typeof providerOverridesSchema>;
|
|
1639
1682
|
type NotificationConfig = z.infer<typeof notificationConfigSchema>;
|
|
1640
1683
|
type ExternalHistoryConfig = z.infer<typeof externalHistoryConfigSchema>;
|
|
1641
1684
|
type HistoryConfig = z.infer<typeof historyConfigSchema>;
|
|
@@ -1643,7 +1686,7 @@ type SandboxConfig = z.infer<typeof sandboxConfigSchema>;
|
|
|
1643
1686
|
type ProxyConfig = z.infer<typeof proxyConfigSchema>;
|
|
1644
1687
|
type McpServerSetting = z.infer<typeof mcpServerSettingSchema>;
|
|
1645
1688
|
type McpServersConfig = z.infer<typeof SettingSchema>['mcp_servers'];
|
|
1646
|
-
declare const SETTING_KEYS: ("model" | "categories" | "prompt_output_token_budget" | "prompt_wall_clock_budget_ms" | "session_cost_budget_usd" | "stall_detection" | "config_version" | "version" | "log_level" | "model_fallback" | "recent_models" | "recent_mentions" | "shortcuts" | "feedback_repo" | "keybinding_overrides" | "language" | "theme" | "theme_overrides" | "theme_preset" | "nickname" | "has_completed_onboarding" | "has_completed_wizard" | "wizard_dont_show_again" | "first_prompt_submitted_at" | "thinking_level" | "agents" | "tool_preset" | "memory_auto_extract" | "skill_loop" | "skill_internalization" | "notification" | "pulse_survey" | "history" | "workflow" | "model_slots" | "mcp_servers" | "resilience" | "sandbox" | "residency" | "disabled_agents" | "disabled_hooks" | "disabled_tools" | "disabled_skills" | "disabled_plugins" | "installed_plugins" | "auto_update_check" | "last_update_check" | "todo_continue_max" | "permission_mode" | "permissions" | "permission_always_allow" | "plugin_lifecycle" | "proxy" | "web_fetch_allowed_hosts" | "_migrations")[];
|
|
1689
|
+
declare const SETTING_KEYS: ("model" | "categories" | "prompt_output_token_budget" | "prompt_wall_clock_budget_ms" | "session_cost_budget_usd" | "stall_detection" | "config_version" | "version" | "log_level" | "model_fallback" | "recent_models" | "recent_mentions" | "shortcuts" | "feedback_repo" | "feedback_platform" | "feedback_coding_base_url" | "feedback_coding_repo" | "keybinding_overrides" | "language" | "theme" | "theme_overrides" | "theme_preset" | "nickname" | "has_completed_onboarding" | "has_completed_wizard" | "wizard_dont_show_again" | "first_prompt_submitted_at" | "thinking_level" | "agents" | "tool_preset" | "memory_auto_extract" | "skill_loop" | "skill_internalization" | "notification" | "pulse_survey" | "history" | "workflow" | "model_slots" | "provider_overrides" | "mcp_servers" | "resilience" | "sandbox" | "residency" | "disabled_agents" | "disabled_hooks" | "disabled_tools" | "disabled_skills" | "disabled_plugins" | "installed_plugins" | "auto_update_check" | "last_update_check" | "todo_continue_max" | "permission_mode" | "permissions" | "permission_always_allow" | "plugin_lifecycle" | "proxy" | "web_fetch_allowed_hosts" | "_migrations")[];
|
|
1647
1690
|
type SettingKey = keyof Setting;
|
|
1648
1691
|
//#endregion
|
|
1649
1692
|
//#region src/types.d.ts
|
|
@@ -1651,8 +1694,6 @@ declare const BUILTIN_REVIEWER_AGENTS: Record<string, AgentOptions>;
|
|
|
1651
1694
|
interface SettingWarning {
|
|
1652
1695
|
key: string;
|
|
1653
1696
|
message: string;
|
|
1654
|
-
line?: number;
|
|
1655
|
-
column?: number;
|
|
1656
1697
|
}
|
|
1657
1698
|
interface LoadSettingOptions {
|
|
1658
1699
|
store?: SettingStore;
|
|
@@ -1678,34 +1719,10 @@ declare function migrate(config: Record<string, unknown>, migrations?: Migration
|
|
|
1678
1719
|
applied: string[];
|
|
1679
1720
|
};
|
|
1680
1721
|
//#endregion
|
|
1681
|
-
//#region src/config-origins.d.ts
|
|
1682
|
-
/**
|
|
1683
|
-
* config-origins.ts —— RFC review D11:配置来源溯源(codex config/fingerprint.rs
|
|
1684
|
-
* record_origins 移植)。
|
|
1685
|
-
*
|
|
1686
|
-
* 与 `setting.ts` 的 merge 链并行构建一棵「来源树」:每个配置字段记录其最终值的来源层。
|
|
1687
|
-
* 用途:`otto doctor` / 诊断面回答「这个值来自哪层」(多层配置合并时最常见的排障问题)。
|
|
1688
|
-
*
|
|
1689
|
-
* 层序(低→高,与 setting.ts merge 调用序一致):
|
|
1690
|
-
* 'default' < 'extra:N' < 'global' < 'project' < 'env'
|
|
1691
|
-
*
|
|
1692
|
-
* 纯函数、无 IO(与 merge 同构);数组/对象深合并语义与 merge 逐字对齐。
|
|
1693
|
-
*/
|
|
1694
|
-
/** 配置层名:'default' | 'global' | 'project' | 'env' | 'extra:N'(N = extraLayers 下标)。 */
|
|
1695
|
-
type ConfigLayerName = 'default' | 'global' | 'project' | 'env' | `extra:${number}`;
|
|
1696
|
-
/** 来源树:与配置树同构;叶子 = 该字段最终值的来源层名。 */
|
|
1697
|
-
interface ConfigOrigins {
|
|
1698
|
-
[key: string]: ConfigLayerName | ConfigOrigins;
|
|
1699
|
-
}
|
|
1700
|
-
//#endregion
|
|
1701
1722
|
//#region src/setting.d.ts
|
|
1702
1723
|
declare class SettingLoader {
|
|
1703
1724
|
private store?;
|
|
1704
|
-
/** RFC review D11:上次 load 的来源树(惰性快照,供诊断面查询「值来自哪层」)。 */
|
|
1705
|
-
private lastOrigins?;
|
|
1706
1725
|
constructor(store?: SettingStore);
|
|
1707
|
-
/** RFC review D11:上次 load 的配置来源树(未 load 过返回 undefined)。 */
|
|
1708
|
-
getOrigins(): ConfigOrigins | undefined;
|
|
1709
1726
|
load(options?: LoadSettingOptions): Promise<Setting>;
|
|
1710
1727
|
}
|
|
1711
1728
|
declare function loadSetting(options?: LoadSettingOptions): Promise<Setting>;
|
|
@@ -1791,19 +1808,49 @@ declare class RemoteSettingStore implements SettingStore {
|
|
|
1791
1808
|
exists(_path: string): Promise<boolean>;
|
|
1792
1809
|
}
|
|
1793
1810
|
//#endregion
|
|
1794
|
-
//#region src/
|
|
1811
|
+
//#region src/field-meta.d.ts
|
|
1812
|
+
/** 字段合并策略类别(与 config-merge-strategy 的 MergeCategory 对齐)。 */
|
|
1813
|
+
type FieldMergeCategory = 'scalar-override' | 'array-merge' | 'array-merge-by-name' | 'record-merge';
|
|
1814
|
+
/** 是否参与导出/导入。 */
|
|
1815
|
+
type FieldExportPolicy = 'include' | 'exclude';
|
|
1816
|
+
/** 是否可跨设备远端同步。 */
|
|
1817
|
+
type FieldSyncPolicy = 'syncable' | 'never-synced' | 'local-only';
|
|
1818
|
+
/** 单字段元数据。 */
|
|
1819
|
+
interface FieldMeta {
|
|
1820
|
+
/** 跨层合并 / 导入合并策略。 */
|
|
1821
|
+
merge: FieldMergeCategory;
|
|
1822
|
+
/** 是否恒定路由 user 级配置存储(RFC-219 重要事项规则 5)。 */
|
|
1823
|
+
deviceBound: boolean;
|
|
1824
|
+
/** 导出/导入策略:exclude = 不导出不导入(元数据/内部记账字段)。 */
|
|
1825
|
+
export: FieldExportPolicy;
|
|
1826
|
+
/** 远端同步策略:syncable = 可跨设备同步;never-synced = 绝不同步(安全面/凭据/本地路径);local-only = 当前不支持同步但未来可能加入。 */
|
|
1827
|
+
sync: FieldSyncPolicy;
|
|
1828
|
+
}
|
|
1795
1829
|
/**
|
|
1796
|
-
* RFC-
|
|
1797
|
-
*
|
|
1798
|
-
* 与 SettingSchema 同包维护,字段增删时编译期提醒同步检查(R2)。
|
|
1830
|
+
* RFC-405 D1:字段元数据单表。每个 SettingKey 必须显式声明——satisfies 编译期穷举门
|
|
1831
|
+
* 钉死覆盖(新增 schema 字段后 tsc 报错逼停,不允许"先加字段后补元数据")。
|
|
1799
1832
|
*/
|
|
1800
|
-
declare const
|
|
1833
|
+
declare const FIELD_META: Readonly<Record<SettingKey, FieldMeta>>;
|
|
1834
|
+
/** 不导出/不导入的字段集合(取代 EXCLUDED_KEYS)。 */
|
|
1835
|
+
declare const EXCLUDED_KEYS: ReadonlySet<SettingKey>;
|
|
1836
|
+
/** 可跨设备同步的字段集合(取代 SYNCABLE_KEYS)。 */
|
|
1837
|
+
declare const SYNCABLE_KEYS: ReadonlySet<SettingKey>;
|
|
1838
|
+
/** 绝不可远端同步的字段集合(取代 NEVER_SYNCED_KEYS)。 */
|
|
1839
|
+
declare const NEVER_SYNCED_KEYS: ReadonlySet<string>;
|
|
1840
|
+
/** 字段静态分类表(取代 FIELD_CLASSIFICATION)。 */
|
|
1841
|
+
declare const FIELD_CLASSIFICATION: ReadonlyMap<SettingKey, FieldClassification>;
|
|
1842
|
+
/** 单字段的静态分类(与具体 diff 值无关)。 */
|
|
1843
|
+
interface FieldClassification {
|
|
1844
|
+
category: FieldMergeCategory;
|
|
1845
|
+
deviceBound: boolean;
|
|
1846
|
+
}
|
|
1801
1847
|
/**
|
|
1802
|
-
*
|
|
1803
|
-
*
|
|
1804
|
-
* 此列表保持与 SYNCABLE_KEYS 互斥;不在任一集合的字段为"当前不支持同步,未来可能加入"。
|
|
1848
|
+
* 断言字段元数据表覆盖全部 `SETTING_KEYS`。satisfies 已在编译期保证全覆盖,
|
|
1849
|
+
* 此函数保留供运行时测试调用(向后兼容现有测试)。
|
|
1805
1850
|
*/
|
|
1806
|
-
declare
|
|
1851
|
+
declare function assertFieldClassificationCoversAllKeys(): void;
|
|
1852
|
+
//#endregion
|
|
1853
|
+
//#region src/remote-sync-keys.d.ts
|
|
1807
1854
|
/**
|
|
1808
1855
|
* 从 Partial<Setting> 中过滤出仅 syncable 字段(R2)。
|
|
1809
1856
|
* NEVER_SYNCED 字段即使传入也被移除。
|
|
@@ -1950,12 +1997,12 @@ declare function loadClaudeSettingsLayer(workspaceDir?: string): Partial<Setting
|
|
|
1950
1997
|
* - `mcp_servers` —— stdio server 启动即 spawn `command`+`args` 任意二进制。
|
|
1951
1998
|
* - `disabled_hooks` —— 可关掉 permission-guard / sandbox-guard 等安全 hook。
|
|
1952
1999
|
* - `sandbox` —— 可弱化/关闭沙箱(逃逸)。
|
|
2000
|
+
* - `provider_overrides` —— 可把模型请求与认证凭据转发到任意服务端。
|
|
1953
2001
|
*
|
|
1954
|
-
* 刻意不含的键:`model`/`model_slots
|
|
1955
|
-
* 重定向风险低于业界 CLI 实现的 `model_providers`)、`disabled_tools`(减能力非提权)、`agents`
|
|
2002
|
+
* 刻意不含的键:`model`/`model_slots`(只选择已注册模型)、`disabled_tools`(减能力非提权)、`agents`
|
|
1956
2003
|
* (prompt+工具限制、加载不执行,与业界终端 Agent 实现视 agent 为非可执行面一致)。这些如需收紧另议。
|
|
1957
2004
|
*/
|
|
1958
|
-
declare const HIGH_RISK_PROJECT_KEYS: readonly ["permission_mode", "permissions", "permission_always_allow", "mcp_servers", "disabled_hooks", "sandbox"];
|
|
2005
|
+
declare const HIGH_RISK_PROJECT_KEYS: readonly ["permission_mode", "permissions", "permission_always_allow", "mcp_servers", "disabled_hooks", "sandbox", "provider_overrides"];
|
|
1959
2006
|
interface SanitizeResult {
|
|
1960
2007
|
/** 剔除高危键后的层(浅拷贝,不改入参)。 */
|
|
1961
2008
|
value: Partial<Setting>;
|
|
@@ -1968,6 +2015,62 @@ interface SanitizeResult {
|
|
|
1968
2015
|
*/
|
|
1969
2016
|
declare function sanitizeUntrustedProjectLayer(layer: Partial<Setting>): SanitizeResult;
|
|
1970
2017
|
//#endregion
|
|
2018
|
+
//#region src/project-trust-gate.d.ts
|
|
2019
|
+
/**
|
|
2020
|
+
* 信任白名单落盘路径(用户级)。`OTTO_CLAUDE_TRUST_PATH` 可覆盖(测试/隔离用,
|
|
2021
|
+
* 避免污染真实 `~/.otto`——参照 auth-store 真路径教训)。调用时求值。
|
|
2022
|
+
*/
|
|
2023
|
+
declare function claudeTrustStorePath(): string;
|
|
2024
|
+
/** 该项目根是否已被用户显式信任。 */
|
|
2025
|
+
declare function isProjectTrusted(workspaceDir: string, storePath?: string): boolean;
|
|
2026
|
+
/** 显式信任某项目根(幂等,持久化)。 */
|
|
2027
|
+
declare function trustProject(workspaceDir: string, storePath?: string): void;
|
|
2028
|
+
/** 撤销对某项目根的信任(持久化)。 */
|
|
2029
|
+
declare function untrustProject(workspaceDir: string, storePath?: string): void;
|
|
2030
|
+
/**
|
|
2031
|
+
* 严格项目配置门控是否开启(env-only,项目 config 不可影响,杜绝恶意仓库自关门)。
|
|
2032
|
+
* **默认开**(安全默认):克隆仓库的 `.otto/config.json` 高危键默认不生效,须 `/trust trust`。
|
|
2033
|
+
* `OTTO_STRICT_PROJECT_CONFIG=0` 是逃生口(关门控、恢复旧的全信任行为)。
|
|
2034
|
+
* 注:仅交互态实际剔除(headless fail-open,见 resolveProjectConfigTrusted),故 CI/脚本/测试不受影响。
|
|
2035
|
+
*/
|
|
2036
|
+
declare function isStrictProjectConfigEnabled(): boolean;
|
|
2037
|
+
interface ProjectConfigTrustContext {
|
|
2038
|
+
/** 是否交互(TUI)态。缺省 false=headless → fail-open 放行。 */
|
|
2039
|
+
interactive?: boolean;
|
|
2040
|
+
}
|
|
2041
|
+
/**
|
|
2042
|
+
* 该 workspace 的项目配置是否受信任。
|
|
2043
|
+
* 严格模式关 → 恒 `true`;headless → 恒 `true`(fail-open);交互+严格 → 取信任白名单判定。
|
|
2044
|
+
*/
|
|
2045
|
+
declare function resolveProjectConfigTrusted(workspaceDir: string, ctx?: ProjectConfigTrustContext): boolean;
|
|
2046
|
+
interface ProjectConfigGating {
|
|
2047
|
+
/** 项目 `.otto/config.json` 实际含有的高危键(空 = 无可门控内容)。 */
|
|
2048
|
+
highRiskKeys: string[];
|
|
2049
|
+
/** 这些高危键当前是否被剔除(严格开 + 交互 + 未信任)。 */
|
|
2050
|
+
gated: boolean;
|
|
2051
|
+
}
|
|
2052
|
+
/**
|
|
2053
|
+
* 探测项目 `.otto/config.json` 的高危键门控状态(供 `/compat status` 呈现)。
|
|
2054
|
+
* 严格模式关 → 返空(特性未启用,无需呈现噪声)。读文件失败/无文件 → 返空。
|
|
2055
|
+
*/
|
|
2056
|
+
declare function detectProjectConfigGating(workspaceDir: string, ctx?: ProjectConfigTrustContext): ProjectConfigGating;
|
|
2057
|
+
/** 用户对工作区信任弹窗的选择。 */
|
|
2058
|
+
type WorkspaceTrustAction = 'trust' | 'readonly' | 'exit';
|
|
2059
|
+
interface WorkspaceTrustPromptDecision {
|
|
2060
|
+
/** 是否应弹"信任此文件夹吗"。 */
|
|
2061
|
+
shouldPrompt: boolean;
|
|
2062
|
+
/** 解析到的工作区根(信任按它记录)。 */
|
|
2063
|
+
root: string;
|
|
2064
|
+
/** 不弹的原因(诊断/测试)。 */
|
|
2065
|
+
reason: 'trusted' | 'not-interactive' | 'strict-off' | 'not-a-project' | 'untrusted';
|
|
2066
|
+
}
|
|
2067
|
+
/**
|
|
2068
|
+
* 工作区信任弹窗的纯决策:启动时是否要弹"信任此文件夹吗"。
|
|
2069
|
+
* 弹 ⟺ 交互 且 严格开 且 根未信任 且 是真项目。其余一律不弹。
|
|
2070
|
+
* 纯函数:只读 FS + env,不做 IO 副作用、不渲染。
|
|
2071
|
+
*/
|
|
2072
|
+
declare function evaluateWorkspaceTrustPrompt(workspaceDir: string, ctx?: ProjectConfigTrustContext): WorkspaceTrustPromptDecision;
|
|
2073
|
+
//#endregion
|
|
1971
2074
|
//#region src/presets.d.ts
|
|
1972
2075
|
/**
|
|
1973
2076
|
* 预制 agent persona 已移除(2026-06-13,agent-profiles-removal 方案)。
|
|
@@ -1979,34 +2082,11 @@ declare function sanitizeUntrustedProjectLayer(layer: Partial<Setting>): Sanitiz
|
|
|
1979
2082
|
declare const BUILTIN_AGENT_PROFILES: AgentProfile[];
|
|
1980
2083
|
//#endregion
|
|
1981
2084
|
//#region src/config-merge-strategy.d.ts
|
|
1982
|
-
/** 合并策略类别(RFC-219 milestone M2 §设计闸门要点订正后的接口)。 */
|
|
1983
|
-
type MergeCategory = 'scalar-override' | 'array-merge' | 'record-merge';
|
|
1984
|
-
/** 单个字段的静态分类(与具体 diff 值无关)。 */
|
|
1985
|
-
interface FieldClassification {
|
|
1986
|
-
category: MergeCategory;
|
|
1987
|
-
/**
|
|
1988
|
-
* 恒定路由 user 级配置存储,不受目标 scope 参数影响(RFC-219 重要事项规则 5)。
|
|
1989
|
-
* 仅供 M3/M4 的导入落盘目标判定使用,本模块自身不做任何写入。
|
|
1990
|
-
*/
|
|
1991
|
-
deviceBound: boolean;
|
|
1992
|
-
}
|
|
1993
2085
|
/**
|
|
1994
|
-
*
|
|
1995
|
-
*
|
|
1996
|
-
* 文件里的值);`last_update_check`/`_migrations` 是内部记账字段。
|
|
2086
|
+
* RFC-405 D1:MergeCategory 从 field-meta 的 FieldMergeCategory 派生(单源)。
|
|
2087
|
+
* 保留原类型名向后兼容(消费方 import MergeCategory)。
|
|
1997
2088
|
*/
|
|
1998
|
-
|
|
1999
|
-
/**
|
|
2000
|
-
* 字段静态分类表(RFC-219 §设计·字段策略表,M2 实现期订正版:合并策略与设备绑定分离为
|
|
2001
|
-
* 两个独立字段)。`EXCLUDED_KEYS` 里的字段不在此表出现(完整性校验单独处理,见
|
|
2002
|
-
* `assertFieldClassificationCoversAllKeys`)。
|
|
2003
|
-
*/
|
|
2004
|
-
declare const FIELD_CLASSIFICATION: ReadonlyMap<SettingKey, FieldClassification>;
|
|
2005
|
-
/**
|
|
2006
|
-
* 断言字段分类表覆盖全部 `SETTING_KEYS`(含 excluded 字段)。新增 schema 字段忘记归类
|
|
2007
|
-
* 时抛错——供单测调用,防止静默漏判(RFC-219 §风险与验证策略)。
|
|
2008
|
-
*/
|
|
2009
|
-
declare function assertFieldClassificationCoversAllKeys(): void;
|
|
2089
|
+
type MergeCategory = FieldMergeCategory;
|
|
2010
2090
|
/** 单字段差异。`risk` 是按本次具体 from/to 值计算的实例级结果,非字段静态属性。 */
|
|
2011
2091
|
interface FieldDiff {
|
|
2012
2092
|
key: SettingKey;
|
|
@@ -2120,5 +2200,5 @@ declare class ExportFileParseError extends Error {
|
|
|
2120
2200
|
*/
|
|
2121
2201
|
declare function parseExportFile(raw: string): ConfigExportFile;
|
|
2122
2202
|
//#endregion
|
|
2123
|
-
export { type AgentOptions, BUILTIN_AGENT_PROFILES, BUILTIN_REVIEWER_AGENTS, CURRENT_EXPORT_VERSION, type CategoryConfig, type ConfigExportFile,
|
|
2203
|
+
export { type AgentOptions, BUILTIN_AGENT_PROFILES, BUILTIN_REVIEWER_AGENTS, CURRENT_EXPORT_VERSION, type CategoryConfig, type ConfigExportFile, DEFAULT_CONFIG, EXCLUDED_KEYS, ExportFileParseError, ExportFileVersionError, type ExternalHistoryConfig, FIELD_CLASSIFICATION, FIELD_META, type FieldClassification, type FieldDiff, type FieldExportPolicy, type FieldMergeCategory, type FieldMeta, type FieldSyncPolicy, HIGH_RISK_PROJECT_KEYS, type HistoryConfig, type InstalledPluginEntry, LOG_LEVELS, type LoadSettingOptions, type LogLevel, type MarkdownThemeOverridesSetting, type McpServerSetting, type McpServersConfig, type MergeCategory, type Migration, NEVER_SYNCED_KEYS, type NotificationConfig, PERMISSION_MODES, type PermissionMode, type PermissionPolicySetting, type PermissionRuleConfig, type PluginDiff, type ProjectConfigGating, type ProjectConfigTrustContext, type ProviderOverrides, type ProxyConfig, RawFileSettingStore, type RecentMentionEntry, RemoteSettingStore, SETTING_KEYS, SYNCABLE_KEYS, type SandboxConfig, type SanitizeResult, type Setting, type SettingKey, SettingLoader, type SettingStore, type SettingWarning, SettingsManager, type SettingsOptions, type StorageType, THEME_COLOR_ROLES, TOOL_PRESETS, type ThemeColorRole, type ThemeOverridesSetting, type ThemePresetSelectionSetting, type ToolPreset, type WorkspaceTrustAction, type WorkspaceTrustPromptDecision, applyMergeStrategy, assertFieldClassificationCoversAllKeys, buildExportPayload, classifyPluginChanges, claudeTrustStorePath, detectProjectConfigGating, diffConfig, evaluateWorkspaceTrustPrompt, filterSyncable, globToRegexSource, hasSecuritySensitiveChanges, isProjectTrusted, isStrictProjectConfigEnabled, loadClaudeSettingsLayer, loadSetting, migrate, parseExportFile, parseResilienceEnvOverride, parseToolSpecifier, resolveProjectConfigTrusted, sanitizeUntrustedProjectLayer, translateClaudeSettings, trustProject, untrustProject, validateResilienceOverride };
|
|
2124
2204
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/store.ts","../src/constants.ts","../src/schema.ts","../src/types.ts","../src/migrator.ts","../src/
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/store.ts","../src/constants.ts","../src/schema.ts","../src/types.ts","../src/migrator.ts","../src/setting.ts","../src/raw-file-store.ts","../src/remote-setting-store.ts","../src/field-meta.ts","../src/remote-sync-keys.ts","../src/settings-manager.ts","../src/claude-settings-adapter.ts","../src/project-trust.ts","../src/project-trust-gate.ts","../src/presets.ts","../src/config-merge-strategy.ts","../src/config-export-format.ts"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{PERMISSION_MODES as e}from"@x-otto/hook-contracts";import{z as t}from"zod";import{MODEL_SLOTS as n}from"@x-otto/orchestration-contracts";import{compare as r}from"semver";import{TypedEventEmitter as i,createLogger as a,mapClaudeToolName as o,parseJsonc as s,safeStringify as c}from"@x-otto/shared";import{atomicWriteFile as l}from"@x-otto/persistence";import{CLAUDE_HOME as u,OTTO_CONFIG_FILENAME as d,OTTO_HOME as f,OTTO_PROJECT_CONFIG_RELPATH as ee,isClaudeSettingsCompatEnabled as te,isShadowModeEnabled as ne}from"@x-otto/env";import{isAbsolute as re,join as p,resolve as m}from"node:path";import{homedir as ie}from"node:os";import{existsSync as ae,readFileSync as oe}from"node:fs";import{HIGH_RISK_CAPABILITIES as se}from"@x-otto/plugin";const h=[`minimal`,`standard`,`full`],g=[`trace`,`debug`,`info`,`warn`,`error`,`fatal`],_=e=>e.optional().catch(void 0),v=t.enum(n),ce=t.object({model:_(t.string()),description:_(t.string()),system_prompt:_(t.string()),tools:_(t.array(t.string())),capabilities:_(t.array(t.string())),categories:_(t.array(t.string())),default_workflow_slot:_(v),max_tool_turns:_(t.number()),max_tool_turn_extensions:_(t.number()),prompt_output_token_budget:_(t.number()),prompt_wall_clock_budget_ms:_(t.number()),session_cost_budget_usd:_(t.number()),stall_detection:_(t.union([t.literal(!1),t.object({window_turns:_(t.number()),repeat_threshold:_(t.number())})])),temperature:_(t.number()),max_tokens:_(t.number()),disabled:_(t.boolean())}),le=t.object({preferred_models:_(t.array(t.string())),default_model:_(t.string())}),ue=t.object({enabled:_(t.boolean()),every_n_turns:_(t.number()),max_lessons:_(t.number()),similarity_threshold:_(t.number()),min_relevant_hits:_(t.number())}),de=t.object({enabled:_(t.boolean()),min_messages:_(t.number()),dismiss_cooldown_days:_(t.number())}),fe=t.object({enabled:_(t.boolean()),min_executions:_(t.number()),min_success_rate:_(t.number()),max_internalized:_(t.number())}),pe=t.object({slots:_(t.partialRecord(v,t.union([t.string(),t.array(t.string())]))),default:_(t.string()),subagent_default_slot:_(v)}),me=t.object({enabled:_(t.boolean()),sound:_(t.boolean()),on_completion:_(t.boolean()),on_error:_(t.boolean()),on_idle:_(t.boolean()),channel:_(t.enum([`auto`,`terminal_bell`,`iterm2`,`iterm2_with_bell`,`kitty`,`ghostty`,`disabled`])),condition:_(t.enum([`unfocused`,`always`])),idle_threshold_ms:_(t.number()),command:_(t.array(t.string()))}),he=t.object({enabled:_(t.boolean()),probability:_(t.number()),min_turn_gap:_(t.number()),sink:_(t.string())}),ge=t.object({id:_(t.string()),path:_(t.string()),enabled:_(t.boolean())}),_e=t.object({enabled:_(t.boolean()),maxEntries:_(t.number()),sources:_(t.array(ge))}),ve=t.object({external:_(_e)}),ye=t.object({enabled:t.boolean(),network:_(t.enum([`allow`,`deny`])),writablePaths:_(t.array(t.string())),protectCredentials:_(t.boolean()),autoAllowBashIfSandboxed:_(t.boolean())}),be=t.object({total_bytes:_(t.number()),min_per_session_bytes:_(t.number()),max_per_session_bytes:_(t.number()),max_history_messages:_(t.number()),resume_window_messages:_(t.number()),max_content_bytes:_(t.number()),warn_threshold_pct:_(t.number()),hard_threshold_pct:_(t.number()),compaction_timeout_ms:_(t.number()),coverage_warn_threshold:_(t.number()),coverage_error_threshold:_(t.number()),coverage_floor_threshold:_(t.number())});t.object({tool_preset:_(t.enum(h)),disabled_tools:_(t.array(t.string()))});const xe=t.object({enabled:_(t.boolean())}),Se=t.object({enabled:_(t.boolean()),maxAttempts:_(t.number())}),Ce=t.object({enabled:_(t.boolean()),failureThreshold:_(t.number()),timeoutMs:_(t.number())}),we=t.object({enabled:_(t.boolean())}),Te=t.object({enabled:_(t.boolean()),review:_(xe),retry:_(Se),circuitBreaker:_(Ce),routing:_(we)}),Ee=t.object({name:t.string(),effect:t.enum([`allow`,`deny`,`ask`]),tools:_(t.array(t.string())),paths:_(t.array(t.string())),deny_reason:_(t.string()),ask_prompt:_(t.string())}),De=t.object({name:t.string(),priority:_(t.number()),enabled:_(t.boolean()),scope:_(t.object({agents:_(t.array(t.string())),sessions:_(t.array(t.string()))})),rules:t.array(Ee)}),Oe=t.object({url:_(t.string()),enabled:_(t.boolean())}),ke=t.object({command:_(t.string()),args:_(t.array(t.string())),env:_(t.record(t.string(),t.string())),url:_(t.string()),type:_(t.enum([`stdio`,`sse`,`http`])),headers:_(t.record(t.string(),t.string())),requestTimeoutMs:_(t.number()),autoReconnect:_(t.boolean()),disabled:_(t.boolean())}),Ae=t.object({maxRetries:_(t.number().int().positive()),initialDelayMs:_(t.number().positive()),backoffFactor:_(t.number().positive()),maxDelayMs:_(t.number().positive())}),je=t.object({maxRetries:_(t.number().int().positive()),intervalMs:_(t.number().positive())}),Me=t.object({maxReconnectAttempts:_(t.number().int().nonnegative()),reconnectDelayMs:_(t.number().positive()),requestTimeoutMs:_(t.number().positive())}),Ne=t.object({retryBackoffMs:_(t.number().nonnegative())}),Pe=t.object({maxAttempts:_(t.number().int().positive()),intervalMs:_(t.number().positive())}),Fe=t.object({refreshLockTimeoutMs:_(t.number().min(1e3))}),Ie=t.object({stream:_(Ae),networkDisconnect:_(je),mcp:_(Me),schedule:_(Ne),errorRetry:_(Pe),oauth:_(Fe)}),Le=t.object({id:t.string(),source:t.enum([`npm`,`dir`,`url`,`git`]),sourceSpec:_(t.string()),installedAt:t.number(),version:_(t.string()),scope:t.enum([`global`,`project`,`repository`]),capabilities:_(t.array(t.string()))}),Re=t.object({label:t.string(),value:t.string(),kind:t.enum([`hashtag`,`file`,`person`,`agent`,`skill`,`mcp`,`plugin`,`model`,`resource`,`shortcut`,`effort`]),description:_(t.string())}),ze=t.object({name:t.string().min(1).max(64),text:t.string().min(1).max(4e3)}),y=`brand.brandShimmer.accent.accentDim.accentBright.text.inactive.secondary.subtle.replyPrefix.success.error.warning.special.suggestion.permission.codeText.diffAdd.diffRemove.diffAddBg.diffRemoveBg.panelBorder.overlayBg.tagBg.toolText.focusBorder.selection`.split(`.`),b=t.enum(y),Be=t.strictObject({heading1:_(b),heading2:_(b),headingWeak:_(b),listMarker:_(b),listMarkerMuted:_(b),quoteBar:_(b),quoteText:_(b),link:_(b),inlineCode:_(b),codeFence:_(b),tableHeader:_(b),tableDivider:_(b),listIndent:_(t.union([t.literal(2),t.literal(3)])),listMarkers:_(t.tuple([t.string().min(1),t.string().min(1),t.string().min(1),t.string().min(1)])),codeBlockDivider:_(t.enum([`hr`,`none`]))}),Ve=t.strictObject({markdown:_(Be)}),He=t.strictObject({dark:_(t.string().min(1).max(128)),light:_(t.string().min(1).max(128))}),x=t.object({config_version:_(t.string()),version:_(t.string()),log_level:_(t.enum(g)),model:_(t.string()),model_fallback:_(t.array(t.string())),recent_models:_(t.array(t.string())),recent_mentions:_(t.array(Re)),shortcuts:_(t.array(ze)),feedback_repo:_(t.string()),keybinding_overrides:_(t.record(t.string(),t.object({ctrl:t.boolean().optional(),shift:t.boolean().optional(),meta:t.boolean().optional(),input:t.string().optional(),key:t.enum([`tab`,`escape`,`return`]).optional()}))),language:_(t.string()),theme:_(t.enum([`dark`,`light`,`system`])),theme_overrides:_(Ve),theme_preset:_(He),nickname:_(t.string().max(64)),has_completed_onboarding:_(t.boolean()),has_completed_wizard:_(t.boolean()),wizard_dont_show_again:_(t.boolean()),first_prompt_submitted_at:_(t.number()),thinking_level:_(t.string()),agents:_(t.record(t.string(),ce)),categories:_(t.record(t.string(),le)),tool_preset:_(t.enum(h)),memory_auto_extract:_(ue),skill_loop:_(de),skill_internalization:_(fe),notification:_(me),pulse_survey:_(he),history:_(ve),workflow:_(Te),model_slots:_(pe),mcp_servers:_(t.record(t.string(),ke)),resilience:_(Ie),sandbox:_(ye),residency:_(be),disabled_agents:_(t.array(t.string())),disabled_hooks:_(t.array(t.string())),disabled_tools:_(t.array(t.string())),disabled_skills:_(t.array(t.string())),disabled_plugins:_(t.array(t.string())),installed_plugins:_(t.array(Le)),auto_update_check:_(t.boolean()),last_update_check:_(t.number()),prompt_output_token_budget:_(t.number()),prompt_wall_clock_budget_ms:_(t.number()),session_cost_budget_usd:_(t.number()),stall_detection:_(t.union([t.literal(!1),t.object({window_turns:_(t.number()),repeat_threshold:_(t.number())})])),todo_continue_max:_(t.number()),permission_mode:_(t.enum(e)),permissions:_(t.array(De)),permission_always_allow:_(t.array(t.string())),plugin_lifecycle:_(t.object({setupDone:_(t.array(t.object({id:t.string(),version:_(t.string())})))})),proxy:_(Oe),web_fetch_allowed_hosts:_(t.array(t.string())),_migrations:_(t.array(t.string()))}),Ue=y,S=x.keyof().options,C={"spec-reviewer":{description:`Reviews implementation against specification requirements`,categories:[`review`],default_workflow_slot:`critique`},"quality-reviewer":{description:`Reviews code quality, patterns, and best practices`,categories:[`review`],default_workflow_slot:`critique`}},w={config_version:`1.0.0`,log_level:`info`,model:void 0,recent_models:[],recent_mentions:[],shortcuts:[],language:`auto`,theme:void 0,nickname:void 0,has_completed_onboarding:void 0,has_completed_wizard:void 0,wizard_dont_show_again:void 0,first_prompt_submitted_at:void 0,thinking_level:void 0,tool_preset:`full`,agents:{...C},categories:{},workflow:{},disabled_agents:[],disabled_hooks:[],disabled_tools:[],disabled_skills:[],disabled_plugins:[],installed_plugins:[],auto_update_check:!0,last_update_check:void 0,permission_mode:`auto`,permissions:[],permission_always_allow:[],web_fetch_allowed_hosts:[],notification:{enabled:!0,sound:!1,on_completion:!0,on_error:!0,channel:`auto`,condition:`unfocused`,idle_threshold_ms:6e4}},We=a(`@x-otto/setting:migrator`);function T(e,t=[]){let n=typeof e.config_version==`string`?e.config_version:`0.0.0`,i=[],a={...e};for(let e of t)r(e.version,n)>0&&(a=e.migrate(a),a.config_version=e.version,i.push(`${n} → ${e.version}`),We.info({from:n,to:e.version},`Applying migration: ${e.description}`));if(i.length>0){let e=Array.isArray(a._migrations)?a._migrations:[];a._migrations=[...e,...i]}return{config:a,applied:i}}const E=[`permission_mode`,`permissions`,`permission_always_allow`,`mcp_servers`,`disabled_hooks`,`sandbox`];function D(e){let t={...e},n=[];for(let e of E)e in t&&(delete t[e],n.push(e));return{value:t,stripped:n}}function O(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function k(e,t,n,r){let i={...e},a={...n};for(let[e,o]of Object.entries(t))if(o!==void 0)if(O(o)&&O(i[e])){let t=k(i[e],o,n[e]??{},r);i[e]=t.value,a[e]=t.origins}else i[e]=o,a[e]=r;return{value:i,origins:a}}function Ge(e){let t={},n={};for(let{layer:r,name:i}of e)for(let[e,a]of Object.entries(r))if(a!==void 0){if(Array.isArray(t[e])&&Array.isArray(a)){let r=t[e],o=a;t[e]=[...new Set([...r,...o])],n[e]=i;continue}if(O(a)&&O(t[e])){let r=k(t[e],a,n[e]??{},i);t[e]=r.value,n[e]=r.origins}else if(O(a)){let r=k({},a,{},i);t[e]=r.value,n[e]=r.origins}else t[e]=a,n[e]=i}return{value:t,origins:n}}const A=a(`@x-otto/setting`),Ke=new Set(g),qe=[`mcp`,`skills`,`disabled_mcps`,`disabled_skills`],Je=[`theme_file`,`theme_spacing`,`theme_border`,`theme_figures`],Ye=new Set([`accent`,`accentDim`,`accentBright`,`colors`,`spacing`,`border`,`figures`,`text`,`inactive`,`secondary`,`success`,`error`,`warning`,`special`]);function j(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function M(e,t,n,r){for(let[i,a]of Object.entries(e)){if(a===void 0)continue;let e=t[i],o=[...n,i].join(`.`);if(e===void 0){r.push({key:o,message:`Invalid value for "${o}" ignored`});continue}j(a)&&j(e)&&M(a,e,[...n,i],r)}}function N(e,t){let n={...e};for(let[e,r]of Object.entries(t))r!==void 0&&(j(r)&&j(n[e])?n[e]=N(n[e],r):n[e]=r);return n}function P(...e){let[t,n,...r]=e,i={...t};if(n){for(let[e,t]of Object.entries(n))if(t!==void 0)if(Array.isArray(i[e])&&Array.isArray(t)){let n=i[e],r=t;i[e]=[...new Set([...n,...r])]}else j(t)&&j(i[e])?i[e]=N(i[e],t):i[e]=t}return r.length>0?P(i,...r):i}function Xe(){let e={},t=process.env.OTTO_MODEL;t&&(e.model=t);let n=process.env.OTTO_LOG_LEVEL;return n&&Ke.has(n)&&(e.log_level=n),e}function Ze(e){let t=[],n={...e};for(let e of qe)n[e]!==void 0&&(t.push({key:e,message:`Removed legacy config key is ignored`}),delete n[e]);for(let e of Je)n[e]!==void 0&&(t.push({key:e,message:`RFC-205 legacy theme override key is ignored`}),delete n[e]);if(j(n.theme_overrides)){let e=Object.keys(n.theme_overrides).filter(e=>Ye.has(e));e.length>0&&t.push({key:`theme_overrides`,message:`RFC-205 legacy theme_overrides shape (keys: ${e.join(`, `)}) is ignored — use theme_overrides.markdown instead`})}let r=x.loose().safeParse(n),i=r.success?r.data:n,a={};for(let[e,t]of Object.entries(i))t!==void 0&&(a[e]=t);for(let e of S){let r=n[e],i=a[e];if(r!==void 0&&i===void 0){t.push({key:e,message:`Invalid value for "${e}" ignored`});continue}j(r)&&j(i)&&M(r,i,[e],t)}return{value:a,warnings:t}}async function Qe(e,t,n){let r=[],i=[];for(let a of t)try{let t=await e.read(a);if(t!==void 0){let e=s(t);if(n!==void 0&&a===n){let t=D(e);e=t.value,i.push(...t.stripped)}r.push(e),A.debug({path:a},`Config loaded from store`)}}catch(e){A.warn({path:a,err:e},`Failed to parse config file, skipping`)}return{layers:r,stripped:i}}var F=class{store;lastOrigins;constructor(e){this.store=e}getOrigins(){return this.lastOrigins}async load(e={}){let{store:t=this.store,paths:n=[],extraLayers:r=[],untrustedProjectPath:i}=e,{layers:a,stripped:o}=t&&n.length>0?await Qe(t,n,i):{layers:[],stripped:[]};for(let e of o)A.warn({key:e,path:i},`未受信任项目配置剔除高危键(permission_mode/mcp_servers 等需先信任该目录)`);let s=Xe(),c=P(w,...r,...a,s);this.lastOrigins=Ge([{layer:w,name:`default`},...r.map((e,t)=>({layer:e,name:`extra:${t}`})),...a.map((e,t)=>({layer:e,name:t===a.length-1?`project`:`global`})),{layer:s,name:`env`}]).origins;let{config:l,applied:u}=T(c);u.length>0&&A.info({applied:u},`Config migrations applied`);let{value:d,warnings:f}=Ze(l);for(let e of f)A.warn({key:e.key,message:e.message},`Config validation issue`);return{...w,...d}}};async function I(e={}){return new F(e.store).load(e)}var L=class{type=`file`;async read(e){let{readFile:t}=await import(`node:fs/promises`);try{return await t(await R(e),`utf-8`)}catch{return}}async write(e,t){let{mkdir:n}=await import(`node:fs/promises`),{dirname:r}=await import(`node:path`),i=await R(e);await n(r(i),{recursive:!0}),await l(i,c(t,2))}async exists(e){let{access:t}=await import(`node:fs/promises`);try{return await t(await R(e)),!0}catch{return!1}}};async function R(e){if(!e.startsWith(`~/`))return e;let{homedir:t}=await import(`node:os`);return`${t()}${e.slice(1)}`}const z=new Set([`model`,`model_fallback`,`recent_models`,`language`,`nickname`,`has_completed_onboarding`,`has_completed_wizard`,`wizard_dont_show_again`,`first_prompt_submitted_at`,`log_level`,`notification`,`auto_update_check`,`model_slots`,`disabled_agents`,`disabled_hooks`,`disabled_tools`,`disabled_skills`]),B=new Set([`recent_mentions`,`shortcuts`,`mcp_servers`,`sandbox`,`permission_mode`,`permissions`,`permission_always_allow`,`web_fetch_allowed_hosts`,`session`,`tool_preset`,`config_version`,`version`,`_migrations`,`last_update_check`,`memory_auto_extract`,`skill_loop`,`skill_internalization`,`workflow`,`categories`,`agents`,`disabled_plugins`,`installed_plugins`]);function V(e){let t={};for(let n of Object.keys(e))z.has(n)&&!B.has(n)&&(t[n]=e[n]);return t}var $e=class{type=`file`;baseUrl;userId;getAuth;fetchImpl;timeoutMs;constructor(e){this.baseUrl=e.baseUrl.replace(/\/+$/,``),this.userId=e.userId,this.getAuth=e.getAuth,this.fetchImpl=e.fetch,this.timeoutMs=e.timeoutMs}async read(e){let t={Authorization:`Bearer ${(await this.getAuth()).token}`,Accept:`application/json`,"X-Otto-User-Id":this.userId},n=new AbortController,r=setTimeout(()=>n.abort(),this.timeoutMs),i;try{i=await this.fetchImpl(`${this.baseUrl}/config/user`,{method:`GET`,headers:t,signal:n.signal})}finally{clearTimeout(r)}if(i.status===404)return;if(!i.ok)throw Error(`remote settings read failed: HTTP ${i.status}`);let a=await i.json();return JSON.stringify(a)}async write(e,t){let n=V(t);if(Object.keys(n).length===0)return;let r={Authorization:`Bearer ${(await this.getAuth()).token}`,"Content-Type":`application/json`,"X-Otto-User-Id":this.userId},i=new AbortController,a=setTimeout(()=>i.abort(),this.timeoutMs),o;try{o=await this.fetchImpl(`${this.baseUrl}/config/user`,{method:`PUT`,headers:r,body:JSON.stringify(n),signal:i.signal})}finally{clearTimeout(a)}if(!o.ok)throw Error(`remote settings write failed: HTTP ${o.status}`)}async exists(e){return!1}};const H=a(`@x-otto/setting:claude-adapter`),et=new Set([`Read`,`Write`,`Edit`,`MultiEdit`,`Glob`,`Grep`]),U={default:`confirm`,acceptEdits:`auto`,bypassPermissions:`bypass`,plan:`readonly`};function W(e){let t=/^([A-Za-z][A-Za-z0-9_]*)(?:\((.*)\))?$/.exec(e.trim());return t?{tool:t[1],specifier:t[2]}:{tool:e.trim()}}function G(e){let t=e.startsWith(`~/`)?p(ie(),e.slice(2)):e,n=``;for(let e=0;e<t.length;e++){let r=t[e];r===`*`?t[e+1]===`*`?(n+=`.*`,e++):n+=`[^/]*`:r===`?`?n+=`.`:n+=r.replace(/[.+^${}()|[\]\\]/g,`\\$&`)}return`^${n}$`}function tt(e,t,n,r){let{tool:i,specifier:a}=W(e),s=o(i);if(!s){n.warnings.push(`未知工具 "${i}"(${e})→ fail-closed 丢弃`);return}if(i===`WebFetch`&&a?.startsWith(`domain:`)){let e=a.slice(7).trim();if(t===`allow`&&e){n.webFetchHosts.push(e);return}let i=t===`deny`?`deny`:`ask`;n.rules.push({name:`claude-${r}`,effect:i,tools:[s]}),n.warnings.push(`WebFetch(${a}) 的 ${t} 不可按域表达 → ${i===`deny`?`降工具级 deny(fail-closed)`:`降 ask`}`);return}if(a===void 0||a===``){n.rules.push({name:`claude-${r}`,effect:t,tools:[s]});return}if(et.has(i)){n.rules.push({name:`claude-${r}`,effect:t,tools:[s],paths:[G(a)]});return}n.rules.push({name:`claude-${r}`,effect:`ask`,tools:[s],ask_prompt:`Claude 规则 "${e}" 含命令参数限定,otto 无法精确表达,已降为询问。`}),n.warnings.push(`"${e}" 含参数 specifier 不可表达 → fail-closed 降 ask`)}function K(e){let t={rules:[],webFetchHosts:[],warnings:[]},n=e?.permissions,r=0;for(let e of[`allow`,`deny`,`ask`]){let i=n?.[e];if(Array.isArray(i))for(let n of i)typeof n==`string`&&tt(n,e,t,r++)}let i={};t.rules.length>0&&(i.permissions=[{name:`claude-compat`,rules:t.rules}]),t.webFetchHosts.length>0&&(i.web_fetch_allowed_hosts=[...new Set(t.webFetchHosts)]);let a=typeof n?.defaultMode==`string`?n.defaultMode:void 0;return a&&U[a]&&(i.permission_mode=U[a],a===`plan`&&t.warnings.push(`defaultMode=plan otto 无对应 → 降 readonly`)),{setting:i,warnings:t.warnings}}function q(e){if(!te())return{};let t=[p(u,`settings.json`),...e?[p(m(e),`.claude`,`settings.json`),p(m(e),`.claude`,`settings.local.json`)]:[]],n=[],r=[],i,a=[];for(let e of t){if(!ae(e))continue;let t;try{t=s(oe(e,`utf-8`))}catch(t){H.warn({file:e,err:t},`Failed to parse .claude settings, skipped`);continue}let{setting:o,warnings:c}=K(t);a.push(...c);let l=o.permissions?.[0]?.rules;l&&n.push(...l),o.web_fetch_allowed_hosts&&r.push(...o.web_fetch_allowed_hosts),o.permission_mode&&(i=o.permission_mode)}let o={};n.length>0&&(o.permissions=[{name:`claude-compat`,rules:n}]),r.length>0&&(o.web_fetch_allowed_hosts=[...new Set(r)]),i&&(o.permission_mode=i);let c=n.length;if(c>0||a.length>0){H.info({ruleCount:c,hosts:o.web_fetch_allowed_hosts?.length??0,mode:o.permission_mode},`Imported .claude/settings.json (RFC-032 D4)`);for(let e of a)H.warn({rule:e},`.claude settings 翻译降级`)}return o}var nt=class extends i{paths;workspaceDir;projectConfigTrusted;overrides;store;remoteStore;setting={};writeChain=Promise.resolve();constructor(e={}){super(),this.paths=rt(e),this.workspaceDir=e.workspaceDir,this.projectConfigTrusted=e.projectConfigTrusted??!0,this.overrides={...e.overrides},this.store=e.store??new L,this.remoteStore=e.remoteStore}get(e){return this.setting[e]}get config(){return this.setting}get model(){return this.setting.model}async load(){return this.reload(this.overrides)}async reload(e){let t;if(this.remoteStore)try{let e=await this.remoteStore.read(`/config/user`);e!==void 0&&(t=s(e))}catch(e){this.emit(`remote-sync-failed`,{op:`read`,error:e})}return this.setting={...await I({store:this.store,paths:this.paths,untrustedProjectPath:this.resolveUntrustedProjectPath(),extraLayers:[q(this.workspaceDir),...t?[t]:[]]}),...e},this.emit(`change`,this.setting),this.setting}async update(e){return this.overrides={...this.overrides,...e},this.reload(this.overrides)}async persist(e,t){if(ne())return this.update(e);let n=t===`global`?this.paths[0]:this.paths[this.paths.length-1],r=this.store;return r&&n?this.enqueueWrite(async()=>{let t=await r.read(n),i={};if(t)try{i=s(t)}catch{i={}}return await r.write(n,{...i,...e}),this.remoteStore&&this.remoteStore.write(`/config/user`,{...i,...e}).catch(e=>{this.emit(`remote-sync-failed`,{op:`write`,error:e})}),this.reload(this.overrides)}):this.update(e)}enqueueWrite(e){let t=this.writeChain.then(e,e);return this.writeChain=t.then(()=>void 0,()=>void 0),t}resolveUntrustedProjectPath(){if(this.paths.length!==0)return(typeof this.projectConfigTrusted==`function`?this.projectConfigTrusted():this.projectConfigTrusted)?void 0:this.paths[this.paths.length-1]}dispose(){this.removeAllListeners()}};function rt(e){let t;if(e.projectConfigPath?t=e.projectConfigPath:e.workspaceDir&&(t=m(e.workspaceDir,ee)),!t)return[];let n=m(f,d);return m(t)===n?[t]:[n,t]}const it=[],J=new Set([`config_version`,`version`,`last_update_check`,`_migrations`]),Y=new Map([[`model`,{category:`scalar-override`,deviceBound:!1}],[`language`,{category:`scalar-override`,deviceBound:!1}],[`nickname`,{category:`scalar-override`,deviceBound:!1}],[`has_completed_onboarding`,{category:`scalar-override`,deviceBound:!1}],[`has_completed_wizard`,{category:`scalar-override`,deviceBound:!1}],[`wizard_dont_show_again`,{category:`scalar-override`,deviceBound:!1}],[`first_prompt_submitted_at`,{category:`scalar-override`,deviceBound:!1}],[`thinking_level`,{category:`scalar-override`,deviceBound:!1}],[`tool_preset`,{category:`scalar-override`,deviceBound:!1}],[`log_level`,{category:`scalar-override`,deviceBound:!1}],[`feedback_repo`,{category:`scalar-override`,deviceBound:!1}],[`permission_mode`,{category:`scalar-override`,deviceBound:!1}],[`prompt_output_token_budget`,{category:`scalar-override`,deviceBound:!1}],[`prompt_wall_clock_budget_ms`,{category:`scalar-override`,deviceBound:!1}],[`session_cost_budget_usd`,{category:`scalar-override`,deviceBound:!1}],[`memory_auto_extract`,{category:`scalar-override`,deviceBound:!1}],[`skill_internalization`,{category:`scalar-override`,deviceBound:!1}],[`residency`,{category:`scalar-override`,deviceBound:!1}],[`skill_loop`,{category:`scalar-override`,deviceBound:!1}],[`history`,{category:`scalar-override`,deviceBound:!1}],[`workflow`,{category:`scalar-override`,deviceBound:!1}],[`todo_continue_max`,{category:`scalar-override`,deviceBound:!1}],[`stall_detection`,{category:`scalar-override`,deviceBound:!1}],[`sandbox`,{category:`scalar-override`,deviceBound:!1}],[`resilience`,{category:`scalar-override`,deviceBound:!1}],[`disabled_plugins`,{category:`array-merge`,deviceBound:!1}],[`model_fallback`,{category:`array-merge`,deviceBound:!1}],[`web_fetch_allowed_hosts`,{category:`array-merge`,deviceBound:!1}],[`recent_mentions`,{category:`array-merge`,deviceBound:!1}],[`shortcuts`,{category:`array-merge`,deviceBound:!1}],[`disabled_agents`,{category:`array-merge`,deviceBound:!1}],[`disabled_tools`,{category:`array-merge`,deviceBound:!1}],[`disabled_skills`,{category:`array-merge`,deviceBound:!1}],[`disabled_hooks`,{category:`array-merge`,deviceBound:!1}],[`permissions`,{category:`array-merge`,deviceBound:!1}],[`permission_always_allow`,{category:`array-merge`,deviceBound:!1}],[`agents`,{category:`record-merge`,deviceBound:!1}],[`categories`,{category:`record-merge`,deviceBound:!1}],[`model_slots`,{category:`record-merge`,deviceBound:!1}],[`mcp_servers`,{category:`record-merge`,deviceBound:!1}],[`recent_models`,{category:`array-merge`,deviceBound:!0}],[`keybinding_overrides`,{category:`record-merge`,deviceBound:!0}],[`installed_plugins`,{category:`array-merge`,deviceBound:!0}],[`plugin_lifecycle`,{category:`scalar-override`,deviceBound:!0}],[`auto_update_check`,{category:`scalar-override`,deviceBound:!0}],[`theme`,{category:`scalar-override`,deviceBound:!0}],[`theme_overrides`,{category:`scalar-override`,deviceBound:!0}],[`theme_preset`,{category:`scalar-override`,deviceBound:!0}],[`proxy`,{category:`scalar-override`,deviceBound:!0}],[`notification`,{category:`scalar-override`,deviceBound:!0}],[`pulse_survey`,{category:`scalar-override`,deviceBound:!0}]]);function at(){let e=S.filter(e=>!J.has(e)&&!Y.has(e));if(e.length>0)throw Error(`ConfigMergeStrategy 字段分类表遗漏以下 SettingKey(需在 FIELD_CLASSIFICATION 或 EXCLUDED_KEYS 中归类): ${e.join(`, `)}`)}const ot=new Set(E);function X(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function Z(e,t){return e===t?!0:Array.isArray(e)&&Array.isArray(t)?e.length===t.length&&e.every((e,n)=>JSON.stringify(e)===JSON.stringify(t[n])):JSON.stringify(e)===JSON.stringify(t)}const st={bypass:0,auto:1,confirm:2,strict:3,readonly:4};function ct(e,t){if(typeof t!=`string`||e===t)return!1;let n=typeof e==`string`?st[e]:void 0,r=st[t];return n===void 0||r===void 0?!0:r<n}function lt(e,t){let n=X(e)?e.enabled:void 0,r=X(t)?t.enabled:void 0;return n===!0&&r===!1}function ut(e,t){let n=Array.isArray(e)?e:[],r=Array.isArray(t)?t:[],i=new Set(n.map(e=>JSON.stringify(e)));return r.filter(e=>!i.has(JSON.stringify(e)))}function dt(e,t,n){if(!ot.has(e))return`safe`;if(e===`permission_mode`)return ct(t,n)?`sensitive`:`safe`;if(e===`sandbox`)return lt(t,n)?`sensitive`:`safe`;if(e===`mcp_servers`){let e=X(t)?t:{},r=X(n)?n:{};for(let[t,n]of Object.entries(r))if(!(t in e)||!Z(e[t],n))return`sensitive`;return`safe`}if(e===`disabled_hooks`)return Z(t,n)?`safe`:`sensitive`;if(e===`permissions`||e===`permission_always_allow`)return ut(t,n).length>0?`sensitive`:`safe`;throw Error(`classifyRisk: 字段 "${e}" 在 HIGH_RISK_PROJECT_KEYS 中但未被显式处理——新增高危字段必须同步补充判定分支,不允许 fail-open`)}function ft(e,t){let n=[];for(let r of S){if(J.has(r)||r===`installed_plugins`||!(r in t))continue;let i=e[r],a=t[r];if(Z(i,a))continue;let o=Y.get(r);o&&n.push({key:r,from:i,to:a,category:o.category,deviceBound:o.deviceBound,risk:dt(r,i,a)})}return n}function pt(e,t){let n=new Map(e.map(e=>[e.id,e])),r=new Map(t.map(e=>[e.id,e])),i=[],a=[];for(let e of t){let t=n.get(e.id);if(!t){i.push(e);continue}e.version!==void 0&&t.version!==void 0&&e.version!==t.version&&a.push({id:e.id,local:t.version,incoming:e.version})}return{toInstall:i,localOnly:e.filter(e=>!r.has(e.id)),versionMismatch:a}}function mt(e,t){return e.some(e=>e.risk===`sensitive`)?!0:t.toInstall.some(e=>(e.capabilities??[]).some(e=>se.includes(e)))}function ht(e,t){let n={};for(let r of e)if(!(t===`safe-only`&&r.risk===`sensitive`))if(r.category===`array-merge`){let e=Array.isArray(r.from)?r.from:[],t=Array.isArray(r.to)?r.to:[],i=new Set(e.map(e=>JSON.stringify(e))),a=[...e];for(let e of t){let t=JSON.stringify(e);i.has(t)||(i.add(t),a.push(e))}n[r.key]=a}else if(r.category===`record-merge`){let e=X(r.from)?r.from:{},t=X(r.to)?r.to:{};n[r.key]={...e,...t}}else n[r.key]=r.to;return n}const gt=new Set([`env`,`headers`]);function _t(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function vt(e){let t=[],n=(e,r)=>{if(!_t(e))return e;let i={};for(let[a,o]of Object.entries(e)){let e=[...r,a];if(r.length===2&&r[0]===`mcp_servers`&>.has(a)){t.push({path:e,set:o!==void 0});continue}i[a]=n(o,e)}return i};return{value:n(e,[]),secrets:t}}const yt=1;function bt(e,t,n,r=()=>new Date().toISOString()){let i={};for(let[t,n]of Object.entries(e))if(!J.has(t)&&n!==void 0){if(t===`installed_plugins`){i[t]=n.map(e=>e.sourceSpec&&re(e.sourceSpec)?{...e,sourceSpec:``}:e);continue}i[t]=n}let a=vt(i),o=a.value;return{otto_config_version:1,exported_at:r(),exported_from:n,scope:t,fields:o,...a.secrets.length>0?{redacted_secrets:a.secrets}:{redacted_secrets:[]}}}var xt=class extends Error{constructor(e,t){super(`导入文件 otto_config_version=${e},当前 otto 支持=${t}。请升级 otto 到最新版本后再试。`),this.fileVersion=e,this.supportedVersion=t,this.name=`ExportFileVersionError`}},Q=class extends Error{constructor(e){super(`导入文件格式非法:${e}`),this.name=`ExportFileParseError`}};function $(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function St(e){let t;try{t=JSON.parse(e)}catch(e){throw new Q(e instanceof Error?e.message:String(e))}if(!$(t))throw new Q(`顶层结构必须是 JSON 对象`);let{otto_config_version:n,exported_at:r,exported_from:i,scope:a,fields:o}=t;if(typeof n!=`number`)throw new Q(`缺少或非法的 otto_config_version 字段`);if(n!==1)throw new xt(n,1);if(typeof r!=`string`||typeof i!=`string`)throw new Q(`缺少或非法的 exported_at/exported_from 字段`);if(a!==`global`&&a!==`project`)throw new Q(`scope 字段必须是 "global" 或 "project"`);if(!$(o))throw new Q(`fields 字段必须是 JSON 对象`);return{otto_config_version:n,exported_at:r,exported_from:i,scope:a,fields:o,...Array.isArray(t.redacted_secrets)?{redacted_secrets:t.redacted_secrets}:{}}}export{it as BUILTIN_AGENT_PROFILES,C as BUILTIN_REVIEWER_AGENTS,yt as CURRENT_EXPORT_VERSION,w as DEFAULT_CONFIG,J as EXCLUDED_KEYS,Q as ExportFileParseError,xt as ExportFileVersionError,Y as FIELD_CLASSIFICATION,E as HIGH_RISK_PROJECT_KEYS,g as LOG_LEVELS,B as NEVER_SYNCED_KEYS,e as PERMISSION_MODES,L as RawFileSettingStore,$e as RemoteSettingStore,S as SETTING_KEYS,z as SYNCABLE_KEYS,F as SettingLoader,nt as SettingsManager,Ue as THEME_COLOR_ROLES,h as TOOL_PRESETS,ht as applyMergeStrategy,at as assertFieldClassificationCoversAllKeys,bt as buildExportPayload,pt as classifyPluginChanges,ft as diffConfig,V as filterSyncable,G as globToRegexSource,mt as hasSecuritySensitiveChanges,q as loadClaudeSettingsLayer,I as loadSetting,T as migrate,St as parseExportFile,W as parseToolSpecifier,D as sanitizeUntrustedProjectLayer,K as translateClaudeSettings};
|
|
1
|
+
import{PERMISSION_MODES as e}from"@x-otto/hook-contracts";import{z as t}from"zod";import{MODEL_SLOTS as n}from"@x-otto/orchestration-contracts";import{compare as r}from"semver";import{TypedEventEmitter as i,createLogger as a,mapClaudeToolName as o,parseJsonc as s,safeStringify as c}from"@x-otto/shared";import{atomicWriteFile as l}from"@x-otto/persistence";import{CLAUDE_HOME as u,OTTO_CONFIG_FILENAME as d,OTTO_HOME as f,OTTO_PROJECT_CONFIG_RELPATH as p,isClaudeSettingsCompatEnabled as ee,isShadowModeEnabled as te}from"@x-otto/env";import{isAbsolute as ne,join as m,resolve as h}from"node:path";import{homedir as g}from"node:os";import{existsSync as _,readFileSync as v}from"node:fs";import{HIGH_RISK_CAPABILITIES as re,isPathTrusted as ie,trustPath as ae,untrustPath as oe}from"@x-otto/plugin";const y=[`minimal`,`standard`,`full`],b=[`trace`,`debug`,`info`,`warn`,`error`,`fatal`],x=e=>e.optional().catch(void 0),S=t.enum(n),se=t.object({model:x(t.string()),description:x(t.string()),system_prompt:x(t.string()),tools:x(t.array(t.string())),capabilities:x(t.array(t.string())),categories:x(t.array(t.string())),default_workflow_slot:x(S),max_tool_turns:x(t.number()),max_tool_turn_extensions:x(t.number()),prompt_output_token_budget:x(t.number()),prompt_wall_clock_budget_ms:x(t.number()),session_cost_budget_usd:x(t.number()),stall_detection:x(t.union([t.literal(!1),t.object({window_turns:x(t.number()),repeat_threshold:x(t.number())})])),temperature:x(t.number()),max_tokens:x(t.number()),disabled:x(t.boolean())}),ce=t.object({preferred_models:x(t.array(t.string())),default_model:x(t.string())}),le=t.strictObject({baseUrl:t.string().min(1)}),ue=t.record(t.string().min(1),le),de=t.object({enabled:x(t.boolean()),every_n_turns:x(t.number()),max_lessons:x(t.number()),similarity_threshold:x(t.number()),min_relevant_hits:x(t.number())}),fe=t.object({enabled:x(t.boolean()),min_messages:x(t.number()),dismiss_cooldown_days:x(t.number())}),pe=t.object({enabled:x(t.boolean()),min_executions:x(t.number()),min_success_rate:x(t.number()),max_internalized:x(t.number())}),me=t.object({slots:x(t.partialRecord(S,t.union([t.string(),t.array(t.string())]))),default:x(t.string()),subagent_default_slot:x(S)}),he=t.object({enabled:x(t.boolean()),sound:x(t.boolean()),on_completion:x(t.boolean()),on_error:x(t.boolean()),on_idle:x(t.boolean()),channel:x(t.enum([`auto`,`terminal_bell`,`iterm2`,`iterm2_with_bell`,`kitty`,`ghostty`,`disabled`])),condition:x(t.enum([`unfocused`,`always`])),idle_threshold_ms:x(t.number()),command:x(t.array(t.string()))}),ge=t.object({enabled:x(t.boolean()),probability:x(t.number()),min_turn_gap:x(t.number()),sink:x(t.string())}),_e=t.object({id:x(t.string()),path:x(t.string()),enabled:x(t.boolean())}),ve=t.object({enabled:x(t.boolean()),maxEntries:x(t.number()),sources:x(t.array(_e))}),ye=t.object({external:x(ve)}),be=t.object({enabled:t.boolean(),network:x(t.enum([`allow`,`deny`])),writablePaths:x(t.array(t.string())),protectCredentials:x(t.boolean()),autoAllowBashIfSandboxed:x(t.boolean()),semanticReview:x(t.boolean())}),xe=t.object({total_bytes:x(t.number()),min_per_session_bytes:x(t.number()),max_per_session_bytes:x(t.number()),max_history_messages:x(t.number()),resume_window_messages:x(t.number()),max_content_bytes:x(t.number()),warn_threshold_pct:x(t.number()),hard_threshold_pct:x(t.number()),compaction_timeout_ms:x(t.number()),coverage_warn_threshold:x(t.number()),coverage_error_threshold:x(t.number()),coverage_floor_threshold:x(t.number())});t.object({tool_preset:x(t.enum(y)),disabled_tools:x(t.array(t.string()))});const Se=t.object({enabled:x(t.boolean())}),Ce=t.object({enabled:x(t.boolean()),maxAttempts:x(t.number().int().min(1)),backoffMs:x(t.number().min(0)),backoffMultiplier:x(t.number().min(0))}),we=t.object({enabled:x(t.boolean()),failureThreshold:x(t.number().int().min(1)),timeoutMs:x(t.number().min(0))}),Te=t.object({enabled:x(t.boolean())}),Ee=t.object({maxActiveTasks:x(t.number().int().min(1)),maxStoredTasks:x(t.number().int().min(1)),maxDelegationDepth:x(t.number().int().min(0)),maxFanOutPerParent:x(t.number().int().min(1))}),De=t.object({enabled:x(t.boolean()),review:x(Se),retry:x(Ce),circuitBreaker:x(we),routing:x(Te),orchestration:x(Ee)}),Oe=t.object({name:t.string(),effect:t.enum([`allow`,`deny`,`ask`]),tools:x(t.array(t.string())),paths:x(t.array(t.string())),commands:x(t.array(t.string())),deny_reason:x(t.string()),ask_prompt:x(t.string())}),ke=t.object({name:t.string(),priority:x(t.number()),enabled:x(t.boolean()),scope:x(t.object({agents:x(t.array(t.string())),sessions:x(t.array(t.string()))})),rules:t.array(Oe)}),Ae=t.object({url:x(t.string()),enabled:x(t.boolean())}),je=t.object({command:x(t.string()),args:x(t.array(t.string())),env:x(t.record(t.string(),t.string())),url:x(t.string()),type:x(t.enum([`stdio`,`sse`,`http`])),headers:x(t.record(t.string(),t.string())),requestTimeoutMs:x(t.number()),autoReconnect:x(t.boolean()),disabled:x(t.boolean())}),Me=t.object({maxRetries:x(t.number().int().positive()),initialDelayMs:x(t.number().positive()),backoffFactor:x(t.number().positive()),maxDelayMs:x(t.number().positive())}),Ne=t.object({maxRetries:x(t.number().int().positive()),intervalMs:x(t.number().positive())}),Pe=t.object({maxReconnectAttempts:x(t.number().int().nonnegative()),reconnectDelayMs:x(t.number().positive()),requestTimeoutMs:x(t.number().positive())}),Fe=t.object({retryBackoffMs:x(t.number().nonnegative())}),Ie=t.object({maxAttempts:x(t.number().int().positive()),intervalMs:x(t.number().positive())}),Le=t.object({refreshLockTimeoutMs:x(t.number().min(1e3))}),C=t.object({stream:x(Me),networkDisconnect:x(Ne),mcp:x(Pe),schedule:x(Fe),errorRetry:x(Ie),oauth:x(Le)});function w(e){if(typeof e!=`object`||!e||Array.isArray(e))return;let t=C.parse(e),n={};for(let[e,r]of Object.entries(t)){if(!r||typeof r!=`object`)continue;let t={};for(let[e,n]of Object.entries(r))typeof n==`number`&&(t[e]=n);Object.keys(t).length>0&&(n[e]=t)}return n}function Re(e,t=e=>console.warn(`[resilience-config] ${e}`)){if(!e)return;let n;try{n=JSON.parse(e)}catch(e){t(`OTTO_RESILIENCE_CONFIG is not valid JSON, ignoring entirely: ${e instanceof Error?e.message:String(e)}`);return}return w(n)}const ze=t.object({id:t.string(),source:t.enum([`npm`,`dir`,`url`,`git`]),sourceSpec:x(t.string()),installedAt:t.number(),version:x(t.string()),scope:t.enum([`global`,`project`,`repository`]),capabilities:x(t.array(t.string()))}),Be=t.object({label:t.string(),value:t.string(),kind:t.enum([`hashtag`,`file`,`person`,`agent`,`skill`,`mcp`,`plugin`,`model`,`resource`,`shortcut`,`effort`]),description:x(t.string())}),Ve=t.object({name:t.string().min(1).max(64),text:t.string().min(1).max(4e3)}),T=`brand.brandShimmer.accent.accentDim.accentBright.text.inactive.secondary.subtle.replyPrefix.success.error.warning.special.suggestion.permission.codeText.diffAdd.diffRemove.diffAddBg.diffRemoveBg.panelBorder.overlayBg.tagBg.toolText.focusBorder.selection`.split(`.`),E=t.enum(T),He=t.strictObject({heading1:x(E),heading2:x(E),headingWeak:x(E),listMarker:x(E),listMarkerMuted:x(E),quoteBar:x(E),quoteText:x(E),link:x(E),inlineCode:x(E),codeFence:x(E),tableHeader:x(E),tableDivider:x(E),listIndent:x(t.union([t.literal(2),t.literal(3)])),listMarkers:x(t.tuple([t.string().min(1),t.string().min(1),t.string().min(1),t.string().min(1)])),codeBlockDivider:x(t.enum([`hr`,`none`]))}),Ue=t.strictObject({markdown:x(He)}),We=t.strictObject({dark:x(t.string().min(1).max(128)),light:x(t.string().min(1).max(128))}),D=t.object({config_version:x(t.string()),version:x(t.string()),log_level:x(t.enum(b)),model:x(t.string()),model_fallback:x(t.array(t.string())),recent_models:x(t.array(t.string())),recent_mentions:x(t.array(Be)),shortcuts:x(t.array(Ve)),feedback_repo:x(t.string()),feedback_platform:x(t.enum([`auto`,`github`,`coding`])),feedback_coding_base_url:x(t.string()),feedback_coding_repo:x(t.string()),keybinding_overrides:x(t.record(t.string(),t.object({ctrl:t.boolean().optional(),shift:t.boolean().optional(),meta:t.boolean().optional(),input:t.string().optional(),key:t.enum([`tab`,`escape`,`return`]).optional()}))),language:x(t.string()),theme:x(t.enum([`dark`,`light`,`system`])),theme_overrides:x(Ue),theme_preset:x(We),nickname:x(t.string().max(64)),has_completed_onboarding:x(t.boolean()),has_completed_wizard:x(t.boolean()),wizard_dont_show_again:x(t.boolean()),first_prompt_submitted_at:x(t.number()),thinking_level:x(t.string()),agents:x(t.record(t.string(),se)),categories:x(t.record(t.string(),ce)),tool_preset:x(t.enum(y)),memory_auto_extract:x(de),skill_loop:x(fe),skill_internalization:x(pe),notification:x(he),pulse_survey:x(ge),history:x(ye),workflow:x(De),model_slots:x(me),provider_overrides:x(ue),mcp_servers:x(t.record(t.string(),je)),resilience:x(C),sandbox:x(be),residency:x(xe),disabled_agents:x(t.array(t.string())),disabled_hooks:x(t.array(t.string())),disabled_tools:x(t.array(t.string())),disabled_skills:x(t.array(t.string())),disabled_plugins:x(t.array(t.string())),installed_plugins:x(t.array(ze)),auto_update_check:x(t.boolean()),last_update_check:x(t.number()),prompt_output_token_budget:x(t.number()),prompt_wall_clock_budget_ms:x(t.number()),session_cost_budget_usd:x(t.number()),stall_detection:x(t.union([t.literal(!1),t.object({window_turns:x(t.number()),repeat_threshold:x(t.number())})])),todo_continue_max:x(t.number()),permission_mode:x(t.enum(e)),permissions:x(t.array(ke)),permission_always_allow:x(t.array(t.string())),plugin_lifecycle:x(t.object({setupDone:x(t.array(t.object({id:t.string(),version:x(t.string())})))})),proxy:x(Ae),web_fetch_allowed_hosts:x(t.array(t.string())),_migrations:x(t.array(t.string()))}),Ge=T,O=D.keyof().options,k={"spec-reviewer":{description:`Reviews implementation against specification requirements`,categories:[`review`],default_workflow_slot:`critique`},"quality-reviewer":{description:`Reviews code quality, patterns, and best practices`,categories:[`review`],default_workflow_slot:`critique`}},A={config_version:`1.0.0`,log_level:`info`,model:void 0,recent_models:[],recent_mentions:[],shortcuts:[],language:`auto`,theme:void 0,nickname:void 0,has_completed_onboarding:void 0,has_completed_wizard:void 0,wizard_dont_show_again:void 0,first_prompt_submitted_at:void 0,thinking_level:void 0,tool_preset:`full`,agents:{...k},categories:{},workflow:{},disabled_agents:[],disabled_hooks:[],disabled_tools:[],disabled_skills:[],disabled_plugins:[],installed_plugins:[],auto_update_check:!0,last_update_check:void 0,permission_mode:`auto`,permissions:[],permission_always_allow:[],web_fetch_allowed_hosts:[],notification:{enabled:!0,sound:!1,on_completion:!0,on_error:!0,channel:`auto`,condition:`unfocused`,idle_threshold_ms:6e4},version:void 0,model_fallback:[],feedback_repo:void 0,feedback_platform:`auto`,feedback_coding_base_url:void 0,feedback_coding_repo:void 0,keybinding_overrides:{},theme_overrides:void 0,theme_preset:{dark:void 0,light:void 0},memory_auto_extract:{enabled:!1},skill_loop:{enabled:!1},skill_internalization:{enabled:!1},pulse_survey:{enabled:!0,probability:.05,min_turn_gap:20,sink:`local`},history:{external:{enabled:!1,maxEntries:1e3,sources:[]}},model_slots:{slots:void 0,default:void 0,subagent_default_slot:void 0},provider_overrides:{},mcp_servers:{},resilience:{},sandbox:{enabled:!0},residency:{},prompt_output_token_budget:void 0,prompt_wall_clock_budget_ms:void 0,session_cost_budget_usd:void 0,stall_detection:void 0,todo_continue_max:void 0,plugin_lifecycle:{setupDone:[]},proxy:{url:void 0,enabled:void 0},_migrations:[]},Ke=a(`@x-otto/setting:migrator`);function j(e,t=[]){let n=typeof e.config_version==`string`?e.config_version:`0.0.0`,i=[],a={...e};for(let e of t)r(e.version,n)>0&&(a=e.migrate(a),a.config_version=e.version,i.push(`${n} → ${e.version}`),Ke.info({from:n,to:e.version},`Applying migration: ${e.description}`));if(i.length>0){let e=Array.isArray(a._migrations)?a._migrations:[];a._migrations=[...e,...i]}return{config:a,applied:i}}const M=[`permission_mode`,`permissions`,`permission_always_allow`,`mcp_servers`,`disabled_hooks`,`sandbox`,`provider_overrides`];function N(e){let t={...e},n=[];for(let e of M)e in t&&(delete t[e],n.push(e));return{value:t,stripped:n}}const P=a(`@x-otto/setting`),qe=new Set(b),Je=[{key:`mcp`,message:`Removed legacy config key is ignored`},{key:`skills`,message:`Removed legacy config key is ignored`},{key:`disabled_mcps`,message:`Removed legacy config key is ignored`},{key:`disabled_skills`,message:`Removed legacy config key is ignored`},{key:`theme_file`,message:`RFC-205 legacy theme override key is ignored`},{key:`theme_spacing`,message:`RFC-205 legacy theme override key is ignored`},{key:`theme_border`,message:`RFC-205 legacy theme override key is ignored`},{key:`theme_figures`,message:`RFC-205 legacy theme override key is ignored`}],Ye=new Set([`accent`,`accentDim`,`accentBright`,`colors`,`spacing`,`border`,`figures`,`text`,`inactive`,`secondary`,`success`,`error`,`warning`,`special`]);function F(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function I(e,t,n,r){for(let[i,a]of Object.entries(e)){if(a===void 0)continue;let e=t[i],o=[...n,i].join(`.`);if(e===void 0){r.push({key:o,message:`Invalid value for "${o}" ignored`});continue}F(a)&&F(e)&&I(a,e,[...n,i],r)}}function L(e,t){let n={...e};for(let[e,r]of Object.entries(t))r!==void 0&&(F(r)&&F(n[e])?n[e]=L(n[e],r):n[e]=r);return n}function Xe(e,t,n){let r=new Map;for(let i of[...e,...t])F(i)&&typeof i[n]==`string`&&r.set(i[n],i);return[...r.values()]}function R(...e){let[t,n,...r]=e,i={...t};if(n){for(let[e,t]of Object.entries(n))if(t!==void 0)if(e===`permissions`&&Array.isArray(i[e])&&Array.isArray(t))i[e]=Xe(i[e],t,`name`);else if(Array.isArray(i[e])&&Array.isArray(t)){let n=i[e],r=t;i[e]=[...new Set([...n,...r])]}else F(t)&&F(i[e])?i[e]=L(i[e],t):i[e]=t}return r.length>0?R(i,...r):i}function Ze(){let e={},t=process.env.OTTO_MODEL;t&&(e.model=t);let n=process.env.OTTO_LOG_LEVEL;return n&&qe.has(n)&&(e.log_level=n),e}function Qe(e){let t={...e},n=[];for(let{key:e,message:r}of Je)t[e]!==void 0&&(n.push({key:e,message:r}),delete t[e]);if(F(t.theme_overrides)){let e=Object.keys(t.theme_overrides).filter(e=>Ye.has(e));e.length>0&&n.push({key:`theme_overrides`,message:`RFC-205 legacy theme_overrides shape (keys: ${e.join(`, `)}) is ignored — use theme_overrides.markdown instead`})}return{cleaned:t,warnings:n}}function $e(e){let t=D.loose().safeParse(e),n=t.success?t.data:e,r={};for(let[e,t]of Object.entries(n))t!==void 0&&(r[e]=t);return r}function et(e,t){let n=[];for(let r of O){let i=e[r],a=t[r];if(i!==void 0&&a===void 0){n.push({key:r,message:`Invalid value for "${r}" ignored`});continue}F(i)&&F(a)&&I(i,a,[r],n)}return n}function tt(e){let{cleaned:t,warnings:n}=Qe(e),r=$e(t),i=et(t,r);return{value:r,warnings:[...n,...i]}}async function nt(e,t,n){let r=[],i=[];for(let a of t)try{let t=await e.read(a);if(t!==void 0){let e=s(t);if(n!==void 0&&a===n){let t=N(e);e=t.value,i.push(...t.stripped)}r.push(e),P.debug({path:a},`Config loaded from store`)}}catch(e){P.warn({path:a,err:e},`Failed to parse config file, skipping`)}return{layers:r,stripped:i}}var z=class{store;constructor(e){this.store=e}async load(e={}){let{store:t=this.store,paths:n=[],extraLayers:r=[],untrustedProjectPath:i}=e,{layers:a,stripped:o}=t&&n.length>0?await nt(t,n,i):{layers:[],stripped:[]};for(let e of o)P.warn({key:e,path:i},`未受信任项目配置剔除高危键(permission_mode/mcp_servers 等需先信任该目录)`);let s=Ze(),{config:c,applied:l}=j(R(A,...r,...a,s));l.length>0&&P.info({applied:l},`Config migrations applied`);let{value:u,warnings:d}=tt(c);for(let e of d)P.warn({key:e.key,message:e.message},`Config validation issue`);return{...A,...u}}};async function B(e={}){return new z(e.store).load(e)}var V=class{type=`file`;async read(e){let{readFile:t}=await import(`node:fs/promises`);try{return await t(await H(e),`utf-8`)}catch{return}}async write(e,t){let{mkdir:n}=await import(`node:fs/promises`),{dirname:r}=await import(`node:path`),i=await H(e);await n(r(i),{recursive:!0}),await l(i,c(t,2))}async exists(e){let{access:t}=await import(`node:fs/promises`);try{return await t(await H(e)),!0}catch{return!1}}};async function H(e){if(!e.startsWith(`~/`))return e;let{homedir:t}=await import(`node:os`);return`${t()}${e.slice(1)}`}const U=(e={})=>({merge:`scalar-override`,deviceBound:!1,export:`include`,sync:`local-only`,...e}),W={config_version:U({export:`exclude`,sync:`never-synced`}),version:U({export:`exclude`,sync:`never-synced`}),last_update_check:U({export:`exclude`,sync:`never-synced`}),_migrations:U({export:`exclude`,sync:`never-synced`}),model:U({sync:`syncable`}),model_fallback:U({merge:`array-merge`,sync:`syncable`}),recent_models:U({merge:`array-merge`,deviceBound:!0,sync:`never-synced`}),recent_mentions:U({merge:`array-merge`,sync:`never-synced`}),shortcuts:U({merge:`array-merge`,sync:`never-synced`}),feedback_repo:U({sync:`local-only`}),feedback_platform:U({sync:`local-only`}),feedback_coding_base_url:U({sync:`local-only`}),feedback_coding_repo:U({sync:`local-only`}),keybinding_overrides:U({merge:`record-merge`,deviceBound:!0,sync:`never-synced`}),language:U({sync:`syncable`}),theme:U({deviceBound:!0,sync:`never-synced`}),theme_overrides:U({deviceBound:!0,sync:`never-synced`}),theme_preset:U({deviceBound:!0,sync:`never-synced`}),nickname:U({sync:`syncable`}),has_completed_onboarding:U({sync:`syncable`}),has_completed_wizard:U({sync:`syncable`}),wizard_dont_show_again:U({sync:`syncable`}),first_prompt_submitted_at:U({sync:`syncable`}),thinking_level:U({sync:`local-only`}),log_level:U({sync:`syncable`}),tool_preset:U({sync:`local-only`}),agents:U({merge:`record-merge`,sync:`never-synced`}),categories:U({merge:`record-merge`,sync:`never-synced`}),model_slots:U({merge:`record-merge`,sync:`syncable`}),provider_overrides:U({merge:`record-merge`,sync:`never-synced`}),memory_auto_extract:U({sync:`never-synced`}),skill_loop:U({sync:`never-synced`}),skill_internalization:U({sync:`never-synced`}),notification:U({deviceBound:!0,sync:`syncable`}),pulse_survey:U({deviceBound:!0,sync:`never-synced`}),history:U({sync:`never-synced`}),workflow:U({sync:`never-synced`}),residency:U({sync:`never-synced`}),resilience:U({sync:`never-synced`}),sandbox:U({sync:`never-synced`}),permission_mode:U({sync:`local-only`}),permissions:U({merge:`array-merge-by-name`,sync:`never-synced`}),permission_always_allow:U({merge:`array-merge`,sync:`never-synced`}),mcp_servers:U({merge:`record-merge`,sync:`never-synced`}),proxy:U({deviceBound:!0,sync:`never-synced`}),web_fetch_allowed_hosts:U({merge:`array-merge`,sync:`never-synced`}),prompt_output_token_budget:U({sync:`local-only`}),prompt_wall_clock_budget_ms:U({sync:`local-only`}),session_cost_budget_usd:U({sync:`local-only`}),stall_detection:U({sync:`never-synced`}),todo_continue_max:U({sync:`local-only`}),disabled_agents:U({merge:`array-merge`,sync:`syncable`}),disabled_hooks:U({merge:`array-merge`,sync:`syncable`}),disabled_tools:U({merge:`array-merge`,sync:`syncable`}),disabled_skills:U({merge:`array-merge`,sync:`syncable`}),disabled_plugins:U({merge:`array-merge`,sync:`never-synced`}),installed_plugins:U({merge:`array-merge`,deviceBound:!0,sync:`never-synced`}),plugin_lifecycle:U({deviceBound:!0,sync:`never-synced`}),auto_update_check:U({deviceBound:!0,sync:`syncable`})},G=new Set(Object.entries(W).filter(([,e])=>e.export===`exclude`).map(([e])=>e)),rt=new Set(Object.entries(W).filter(([,e])=>e.sync===`syncable`).map(([e])=>e)),it=new Set(Object.entries(W).filter(([,e])=>e.sync===`never-synced`).map(([e])=>e)),at=new Map(Object.entries(W).filter(([,e])=>e.export!==`exclude`).map(([e,t])=>[e,{category:t.merge,deviceBound:t.deviceBound}]));function ot(){let e=O.filter(e=>!W[e]);if(e.length>0)throw Error(`FIELD_META 遗漏以下 SettingKey(satisfies 门禁应已拦截): ${e.join(`, `)}`)}function st(e){let t={};for(let n of Object.keys(e))rt.has(n)&&!it.has(n)&&(t[n]=e[n]);return t}var ct=class{type=`file`;baseUrl;userId;getAuth;fetchImpl;timeoutMs;constructor(e){this.baseUrl=e.baseUrl.replace(/\/+$/,``),this.userId=e.userId,this.getAuth=e.getAuth,this.fetchImpl=e.fetch,this.timeoutMs=e.timeoutMs}async read(e){let t={Authorization:`Bearer ${(await this.getAuth()).token}`,Accept:`application/json`,"X-Otto-User-Id":this.userId},n=new AbortController,r=setTimeout(()=>n.abort(),this.timeoutMs),i;try{i=await this.fetchImpl(`${this.baseUrl}/config/user`,{method:`GET`,headers:t,signal:n.signal})}finally{clearTimeout(r)}if(i.status===404)return;if(!i.ok)throw Error(`remote settings read failed: HTTP ${i.status}`);let a=await i.json();return JSON.stringify(a)}async write(e,t){let n=st(t);if(Object.keys(n).length===0)return;let r={Authorization:`Bearer ${(await this.getAuth()).token}`,"Content-Type":`application/json`,"X-Otto-User-Id":this.userId},i=new AbortController,a=setTimeout(()=>i.abort(),this.timeoutMs),o;try{o=await this.fetchImpl(`${this.baseUrl}/config/user`,{method:`PUT`,headers:r,body:JSON.stringify(n),signal:i.signal})}finally{clearTimeout(a)}if(!o.ok)throw Error(`remote settings write failed: HTTP ${o.status}`)}async exists(e){return!1}};const K=a(`@x-otto/setting:claude-adapter`),lt=new Set([`Read`,`Write`,`Edit`,`MultiEdit`,`Glob`,`Grep`]),ut={default:`confirm`,acceptEdits:`auto`,bypassPermissions:`bypass`,plan:`readonly`};function dt(e){let t=/^([A-Za-z][A-Za-z0-9_]*)(?:\((.*)\))?$/.exec(e.trim());return t?{tool:t[1],specifier:t[2]}:{tool:e.trim()}}function ft(e){let t=e.startsWith(`~/`)?m(g(),e.slice(2)):e,n=``;for(let e=0;e<t.length;e++){let r=t[e];r===`*`?t[e+1]===`*`?(n+=`.*`,e++):n+=`[^/]*`:r===`?`?n+=`.`:n+=r.replace(/[.+^${}()|[\]\\]/g,`\\$&`)}return`^${n}$`}function pt(e,t,n,r){let{tool:i,specifier:a}=dt(e),s=o(i);if(!s){n.warnings.push(`未知工具 "${i}"(${e})→ fail-closed 丢弃`);return}if(i===`WebFetch`&&a?.startsWith(`domain:`)){let e=a.slice(7).trim();if(t===`allow`&&e){n.webFetchHosts.push(e);return}let i=t===`deny`?`deny`:`ask`;n.rules.push({name:`claude-${r}`,effect:i,tools:[s]}),n.warnings.push(`WebFetch(${a}) 的 ${t} 不可按域表达 → ${i===`deny`?`降工具级 deny(fail-closed)`:`降 ask`}`);return}if(a===void 0||a===``){n.rules.push({name:`claude-${r}`,effect:t,tools:[s]});return}if(lt.has(i)){n.rules.push({name:`claude-${r}`,effect:t,tools:[s],paths:[ft(a)]});return}n.rules.push({name:`claude-${r}`,effect:`ask`,tools:[s],ask_prompt:`Claude 规则 "${e}" 含命令参数限定,otto 无法精确表达,已降为询问。`}),n.warnings.push(`"${e}" 含参数 specifier 不可表达 → fail-closed 降 ask`)}function mt(e){let t={rules:[],webFetchHosts:[],warnings:[]},n=e?.permissions,r=0;for(let e of[`allow`,`deny`,`ask`]){let i=n?.[e];if(Array.isArray(i))for(let n of i)typeof n==`string`&&pt(n,e,t,r++)}let i={};t.rules.length>0&&(i.permissions=[{name:`claude-compat`,rules:t.rules}]),t.webFetchHosts.length>0&&(i.web_fetch_allowed_hosts=[...new Set(t.webFetchHosts)]);let a=typeof n?.defaultMode==`string`?n.defaultMode:void 0;return a&&ut[a]&&(i.permission_mode=ut[a],a===`plan`&&t.warnings.push(`defaultMode=plan otto 无对应 → 降 readonly`)),{setting:i,warnings:t.warnings}}function ht(e){if(!ee())return{};let t=[m(u,`settings.json`),...e?[m(h(e),`.claude`,`settings.json`),m(h(e),`.claude`,`settings.local.json`)]:[]],n=[],r=[],i,a=[];for(let e of t){if(!_(e))continue;let t;try{t=s(v(e,`utf-8`))}catch(t){K.warn({file:e,err:t},`Failed to parse .claude settings, skipped`);continue}let{setting:o,warnings:c}=mt(t);a.push(...c);let l=o.permissions?.[0]?.rules;l&&n.push(...l),o.web_fetch_allowed_hosts&&r.push(...o.web_fetch_allowed_hosts),o.permission_mode&&(i=o.permission_mode)}let o={};n.length>0&&(o.permissions=[{name:`claude-compat`,rules:n}]),r.length>0&&(o.web_fetch_allowed_hosts=[...new Set(r)]),i&&(o.permission_mode=i);let c=n.length;if(c>0||a.length>0){K.info({ruleCount:c,hosts:o.web_fetch_allowed_hosts?.length??0,mode:o.permission_mode},`Imported .claude/settings.json (RFC-032 D4)`);for(let e of a)K.warn({rule:e},`.claude settings 翻译降级`)}return o}var gt=class extends i{paths;workspaceDir;projectConfigTrusted;overrides;store;remoteStore;setting={};writeChain=Promise.resolve();constructor(e={}){super(),this.paths=_t(e),this.workspaceDir=e.workspaceDir,this.projectConfigTrusted=e.projectConfigTrusted??!0,this.overrides={...e.overrides},this.store=e.store??new V,this.remoteStore=e.remoteStore}get(e){return this.setting[e]}get config(){return this.setting}get model(){return this.setting.model}async load(){return this.reload(this.overrides)}async reload(e){let t;if(this.remoteStore)try{let e=await this.remoteStore.read(`/config/user`);e!==void 0&&(t=s(e))}catch(e){this.emit(`remote-sync-failed`,{op:`read`,error:e})}return this.setting={...await B({store:this.store,paths:this.paths,untrustedProjectPath:this.resolveUntrustedProjectPath(),extraLayers:[ht(this.workspaceDir),...t?[t]:[]]}),...e},this.emit(`change`,this.setting),this.setting}async update(e){return this.overrides={...this.overrides,...e},this.reload(this.overrides)}async persist(e,t){if(te())return this.update(e);let n=t===`global`?this.paths[0]:this.paths[this.paths.length-1],r=this.store;return r&&n?this.enqueueWrite(async()=>{let t=await r.read(n),i={};if(t)try{i=s(t)}catch{i={}}return await r.write(n,{...i,...e}),this.remoteStore&&this.remoteStore.write(`/config/user`,{...i,...e}).catch(e=>{this.emit(`remote-sync-failed`,{op:`write`,error:e})}),this.reload(this.overrides)}):this.update(e)}enqueueWrite(e){let t=this.writeChain.then(e,e);return this.writeChain=t.then(()=>void 0,()=>void 0),t}resolveUntrustedProjectPath(){if(this.paths.length!==0)return(typeof this.projectConfigTrusted==`function`?this.projectConfigTrusted():this.projectConfigTrusted)?void 0:this.paths[this.paths.length-1]}dispose(){this.removeAllListeners()}};function _t(e){let t;if(e.projectConfigPath?t=e.projectConfigPath:e.workspaceDir&&(t=h(e.workspaceDir,p)),!t)return[];let n=h(f,d);return h(t)===n?[t]:[n,t]}const vt=a(`@x-otto/setting:project-trust-gate`);function q(){return process.env.OTTO_CLAUDE_TRUST_PATH||m(f,`claude-trust.json`)}function J(e,t=q()){return ie(t,e)}function yt(e,t=q()){ae(t,e,`project-trust`)&&vt.info({root:h(e)},`Project trusted`)}function bt(e,t=q()){oe(t,e,`project-trust`)&&vt.info({root:h(e)},`Project trust revoked`)}function Y(){return process.env.OTTO_STRICT_PROJECT_CONFIG!==`0`}function xt(e,t={}){return!Y()||!t.interactive?!0:J(e)}function St(e,t={}){if(!Y())return{highRiskKeys:[],gated:!1};let n;try{n=s(v(m(e,p),`utf-8`))}catch{return{highRiskKeys:[],gated:!1}}let r=N(n).stripped;return{highRiskKeys:r,gated:r.length>0&&!xt(e,t)}}function Ct(e){return e===h(g())||e===h(`/`)?!1:[`.git`,`.claude`,`.mcp.json`].some(t=>_(m(e,t)))}function wt(e,t={}){let n=h(e);return t.interactive?Y()?J(e)?{shouldPrompt:!1,root:n,reason:`trusted`}:Ct(n)?{shouldPrompt:!0,root:n,reason:`untrusted`}:{shouldPrompt:!1,root:n,reason:`not-a-project`}:{shouldPrompt:!1,root:n,reason:`strict-off`}:{shouldPrompt:!1,root:n,reason:`not-interactive`}}const Tt=[],Et=new Set(M);function X(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function Z(e,t){return e===t?!0:Array.isArray(e)&&Array.isArray(t)?e.length===t.length&&e.every((e,n)=>JSON.stringify(e)===JSON.stringify(t[n])):JSON.stringify(e)===JSON.stringify(t)}const Dt={bypass:0,auto:1,confirm:2,strict:3,readonly:4};function Ot(e,t){if(typeof t!=`string`||e===t)return!1;let n=typeof e==`string`?Dt[e]:void 0,r=Dt[t];return n===void 0||r===void 0?!0:r<n}function kt(e,t){let n=X(e)?e.enabled:void 0,r=X(t)?t.enabled:void 0;return n===!0&&r===!1}function At(e,t){let n=Array.isArray(e)?e:[],r=Array.isArray(t)?t:[],i=new Set(n.map(e=>JSON.stringify(e)));return r.filter(e=>!i.has(JSON.stringify(e)))}function jt(e,t,n){if(!Et.has(e))return`safe`;if(e===`permission_mode`)return Ot(t,n)?`sensitive`:`safe`;if(e===`sandbox`)return kt(t,n)?`sensitive`:`safe`;if(e===`mcp_servers`||e===`provider_overrides`){let e=X(t)?t:{},r=X(n)?n:{};for(let[t,n]of Object.entries(r))if(!(t in e)||!Z(e[t],n))return`sensitive`;return`safe`}if(e===`disabled_hooks`)return Z(t,n)?`safe`:`sensitive`;if(e===`permissions`||e===`permission_always_allow`)return At(t,n).length>0?`sensitive`:`safe`;throw Error(`classifyRisk: 字段 "${e}" 在 HIGH_RISK_PROJECT_KEYS 中但未被显式处理——新增高危字段必须同步补充判定分支,不允许 fail-open`)}function Mt(e,t){let n=[];for(let r of O){if(G.has(r)||r===`installed_plugins`||!(r in t))continue;let i=e[r],a=t[r];if(Z(i,a))continue;let o=at.get(r);o&&n.push({key:r,from:i,to:a,category:o.category,deviceBound:o.deviceBound,risk:jt(r,i,a)})}return n}function Nt(e,t){let n=new Map(e.map(e=>[e.id,e])),r=new Map(t.map(e=>[e.id,e])),i=[],a=[];for(let e of t){let t=n.get(e.id);if(!t){i.push(e);continue}e.version!==void 0&&t.version!==void 0&&e.version!==t.version&&a.push({id:e.id,local:t.version,incoming:e.version})}return{toInstall:i,localOnly:e.filter(e=>!r.has(e.id)),versionMismatch:a}}function Pt(e,t){return e.some(e=>e.risk===`sensitive`)?!0:t.toInstall.some(e=>(e.capabilities??[]).some(e=>re.includes(e)))}function Ft(e,t){let n={};for(let r of e)if(!(t===`safe-only`&&r.risk===`sensitive`))if(r.category===`array-merge`){let e=Array.isArray(r.from)?r.from:[],t=Array.isArray(r.to)?r.to:[],i=new Set(e.map(e=>JSON.stringify(e))),a=[...e];for(let e of t){let t=JSON.stringify(e);i.has(t)||(i.add(t),a.push(e))}n[r.key]=a}else if(r.category===`array-merge-by-name`){let e=Array.isArray(r.from)?r.from:[],t=Array.isArray(r.to)?r.to:[],i=new Map;for(let n of[...e,...t])X(n)&&typeof n.name==`string`&&i.set(n.name,n);n[r.key]=[...i.values()]}else if(r.category===`record-merge`){let e=X(r.from)?r.from:{},t=X(r.to)?r.to:{};n[r.key]={...e,...t}}else n[r.key]=r.to;return n}const It=new Set([`env`,`headers`]);function Lt(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function Rt(e){let t=[],n=(e,r)=>{if(!Lt(e))return e;let i={};for(let[a,o]of Object.entries(e)){let e=[...r,a];if(r.length===2&&r[0]===`mcp_servers`&&It.has(a)){t.push({path:e,set:o!==void 0});continue}i[a]=n(o,e)}return i};return{value:n(e,[]),secrets:t}}const zt=1;function Bt(e,t,n,r=()=>new Date().toISOString()){let i={};for(let[t,n]of Object.entries(e))if(!G.has(t)&&n!==void 0){if(t===`installed_plugins`){i[t]=n.map(e=>e.sourceSpec&&ne(e.sourceSpec)?{...e,sourceSpec:``}:e);continue}i[t]=n}let a=Rt(i),o=a.value;return{otto_config_version:1,exported_at:r(),exported_from:n,scope:t,fields:o,...a.secrets.length>0?{redacted_secrets:a.secrets}:{redacted_secrets:[]}}}var Vt=class extends Error{constructor(e,t){super(`导入文件 otto_config_version=${e},当前 otto 支持=${t}。请升级 otto 到最新版本后再试。`),this.fileVersion=e,this.supportedVersion=t,this.name=`ExportFileVersionError`}},Q=class extends Error{constructor(e){super(`导入文件格式非法:${e}`),this.name=`ExportFileParseError`}};function $(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function Ht(e){let t;try{t=JSON.parse(e)}catch(e){throw new Q(e instanceof Error?e.message:String(e))}if(!$(t))throw new Q(`顶层结构必须是 JSON 对象`);let{otto_config_version:n,exported_at:r,exported_from:i,scope:a,fields:o}=t;if(typeof n!=`number`)throw new Q(`缺少或非法的 otto_config_version 字段`);if(n!==1)throw new Vt(n,1);if(typeof r!=`string`||typeof i!=`string`)throw new Q(`缺少或非法的 exported_at/exported_from 字段`);if(a!==`global`&&a!==`project`)throw new Q(`scope 字段必须是 "global" 或 "project"`);if(!$(o))throw new Q(`fields 字段必须是 JSON 对象`);return{otto_config_version:n,exported_at:r,exported_from:i,scope:a,fields:o,...Array.isArray(t.redacted_secrets)?{redacted_secrets:t.redacted_secrets}:{}}}export{Tt as BUILTIN_AGENT_PROFILES,k as BUILTIN_REVIEWER_AGENTS,zt as CURRENT_EXPORT_VERSION,A as DEFAULT_CONFIG,G as EXCLUDED_KEYS,Q as ExportFileParseError,Vt as ExportFileVersionError,at as FIELD_CLASSIFICATION,W as FIELD_META,M as HIGH_RISK_PROJECT_KEYS,b as LOG_LEVELS,it as NEVER_SYNCED_KEYS,e as PERMISSION_MODES,V as RawFileSettingStore,ct as RemoteSettingStore,O as SETTING_KEYS,rt as SYNCABLE_KEYS,z as SettingLoader,gt as SettingsManager,Ge as THEME_COLOR_ROLES,y as TOOL_PRESETS,Ft as applyMergeStrategy,ot as assertFieldClassificationCoversAllKeys,Bt as buildExportPayload,Nt as classifyPluginChanges,q as claudeTrustStorePath,St as detectProjectConfigGating,Mt as diffConfig,wt as evaluateWorkspaceTrustPrompt,st as filterSyncable,ft as globToRegexSource,Pt as hasSecuritySensitiveChanges,J as isProjectTrusted,Y as isStrictProjectConfigEnabled,ht as loadClaudeSettingsLayer,B as loadSetting,j as migrate,Ht as parseExportFile,Re as parseResilienceEnvOverride,dt as parseToolSpecifier,xt as resolveProjectConfigTrusted,N as sanitizeUntrustedProjectLayer,mt as translateClaudeSettings,yt as trustProject,bt as untrustProject,w as validateResilienceOverride};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|