@x-otto/setting 0.0.1-alpha.4 → 0.0.1-alpha.5
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 +137 -64
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
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
|
|
@@ -107,6 +108,7 @@ declare const sandboxConfigSchema: z.ZodObject<{
|
|
|
107
108
|
writablePaths: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
108
109
|
protectCredentials: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
109
110
|
autoAllowBashIfSandboxed: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
111
|
+
semanticReview: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
110
112
|
}, z.core.$strip>;
|
|
111
113
|
declare const proxyConfigSchema: z.ZodObject<{
|
|
112
114
|
url: z.ZodCatch<z.ZodOptional<z.ZodString>>;
|
|
@@ -127,6 +129,24 @@ declare const mcpServerSettingSchema: z.ZodObject<{
|
|
|
127
129
|
autoReconnect: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
128
130
|
disabled: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
129
131
|
}, z.core.$strip>;
|
|
132
|
+
/**
|
|
133
|
+
* RFC-412 M3:resilience override 的**唯一校验真源**(此前 env 手写递归校验器与本 zod schema
|
|
134
|
+
* 双实现,已收敛至此)。把任意 `unknown`(通常来自 `JSON.parse` 或 settings/programmatic 注入)
|
|
135
|
+
* 经 `resilienceConfigSchema` 校验,剔除非法字段/子对象,返回可安全传给
|
|
136
|
+
* `@x-otto/env` `mergeResilienceConfig` 的干净覆盖对象。
|
|
137
|
+
*
|
|
138
|
+
* 剪除 undefined 的必要性:`lenient = optional().catch(undefined)` 对非法字段产出 `key: undefined`,
|
|
139
|
+
* 而 `mergeResilienceConfig` 用 `Object.assign` 深合并——若保留 `key: undefined` 会把默认值覆盖成
|
|
140
|
+
* undefined。故 parse 后递归剪除 undefined 字段与空子对象,复刻 env 原"非法字段整体剔除、
|
|
141
|
+
* 兄弟字段不受影响"语义。非对象输入返回 undefined。
|
|
142
|
+
*/
|
|
143
|
+
declare function validateResilienceOverride(input: unknown): ResilienceConfigOverride | undefined;
|
|
144
|
+
/**
|
|
145
|
+
* RFC-412 M3:解析 `OTTO_RESILIENCE_CONFIG` 环境变量(JSON 字符串)为已校验的覆盖对象。
|
|
146
|
+
* 整体 JSON 解析失败时经 `warn` 上报并返回 undefined(重要诊断,不静默吞);解析成功后走
|
|
147
|
+
* `validateResilienceOverride` 做字段级校验。
|
|
148
|
+
*/
|
|
149
|
+
declare function parseResilienceEnvOverride(raw: string | undefined, warn?: (message: string) => void): ResilienceConfigOverride | undefined;
|
|
130
150
|
/**
|
|
131
151
|
* RFC-036 @ 面板「最近使用」条目快照(MRU,最多 5 条)——`/model` `recent_models` 同款
|
|
132
152
|
* 持久化模式的推广:不止模型,@ 面板选中的任意 sigil 条目(文件/agent/skill/mcp/…)
|
|
@@ -970,6 +990,13 @@ declare const SettingSchema: z.ZodObject<{
|
|
|
970
990
|
text: z.ZodString;
|
|
971
991
|
}, z.core.$strip>>>>;
|
|
972
992
|
feedback_repo: z.ZodCatch<z.ZodOptional<z.ZodString>>;
|
|
993
|
+
feedback_platform: z.ZodCatch<z.ZodOptional<z.ZodEnum<{
|
|
994
|
+
auto: "auto";
|
|
995
|
+
github: "github";
|
|
996
|
+
coding: "coding";
|
|
997
|
+
}>>>;
|
|
998
|
+
feedback_coding_base_url: z.ZodCatch<z.ZodOptional<z.ZodString>>;
|
|
999
|
+
feedback_coding_repo: z.ZodCatch<z.ZodOptional<z.ZodString>>;
|
|
973
1000
|
keybinding_overrides: z.ZodCatch<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
974
1001
|
ctrl: z.ZodOptional<z.ZodBoolean>;
|
|
975
1002
|
shift: z.ZodOptional<z.ZodBoolean>;
|
|
@@ -1456,6 +1483,8 @@ declare const SettingSchema: z.ZodObject<{
|
|
|
1456
1483
|
retry: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
1457
1484
|
enabled: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
1458
1485
|
maxAttempts: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
1486
|
+
backoffMs: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
1487
|
+
backoffMultiplier: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
1459
1488
|
}, z.core.$strip>>>;
|
|
1460
1489
|
circuitBreaker: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
1461
1490
|
enabled: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
@@ -1465,6 +1494,12 @@ declare const SettingSchema: z.ZodObject<{
|
|
|
1465
1494
|
routing: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
1466
1495
|
enabled: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
1467
1496
|
}, z.core.$strip>>>;
|
|
1497
|
+
orchestration: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
1498
|
+
maxActiveTasks: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
1499
|
+
maxStoredTasks: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
1500
|
+
maxDelegationDepth: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
1501
|
+
maxFanOutPerParent: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
1502
|
+
}, z.core.$strip>>>;
|
|
1468
1503
|
}, z.core.$strip>>>;
|
|
1469
1504
|
model_slots: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
1470
1505
|
slots: z.ZodCatch<z.ZodOptional<z.ZodRecord<z.ZodEnum<{
|
|
@@ -1534,6 +1569,7 @@ declare const SettingSchema: z.ZodObject<{
|
|
|
1534
1569
|
writablePaths: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
1535
1570
|
protectCredentials: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
1536
1571
|
autoAllowBashIfSandboxed: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
1572
|
+
semanticReview: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
1537
1573
|
}, z.core.$strip>>>;
|
|
1538
1574
|
residency: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
1539
1575
|
total_bytes: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
@@ -1606,6 +1642,7 @@ declare const SettingSchema: z.ZodObject<{
|
|
|
1606
1642
|
}>;
|
|
1607
1643
|
tools: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
1608
1644
|
paths: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
1645
|
+
commands: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
1609
1646
|
deny_reason: z.ZodCatch<z.ZodOptional<z.ZodString>>;
|
|
1610
1647
|
ask_prompt: z.ZodCatch<z.ZodOptional<z.ZodString>>;
|
|
1611
1648
|
}, z.core.$strip>>;
|
|
@@ -1625,7 +1662,6 @@ declare const SettingSchema: z.ZodObject<{
|
|
|
1625
1662
|
_migrations: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
1626
1663
|
}, z.core.$strip>;
|
|
1627
1664
|
type Setting = z.infer<typeof SettingSchema>;
|
|
1628
|
-
type SettingFromSchema = Setting;
|
|
1629
1665
|
type ThemeColorRole = z.infer<typeof themeColorRoleSchema>;
|
|
1630
1666
|
type MarkdownThemeOverridesSetting = z.infer<typeof markdownThemeOverridesSchema>;
|
|
1631
1667
|
type ThemeOverridesSetting = z.infer<typeof themeOverridesSchema>;
|
|
@@ -1643,7 +1679,7 @@ type SandboxConfig = z.infer<typeof sandboxConfigSchema>;
|
|
|
1643
1679
|
type ProxyConfig = z.infer<typeof proxyConfigSchema>;
|
|
1644
1680
|
type McpServerSetting = z.infer<typeof mcpServerSettingSchema>;
|
|
1645
1681
|
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")[];
|
|
1682
|
+
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" | "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
1683
|
type SettingKey = keyof Setting;
|
|
1648
1684
|
//#endregion
|
|
1649
1685
|
//#region src/types.d.ts
|
|
@@ -1651,8 +1687,6 @@ declare const BUILTIN_REVIEWER_AGENTS: Record<string, AgentOptions>;
|
|
|
1651
1687
|
interface SettingWarning {
|
|
1652
1688
|
key: string;
|
|
1653
1689
|
message: string;
|
|
1654
|
-
line?: number;
|
|
1655
|
-
column?: number;
|
|
1656
1690
|
}
|
|
1657
1691
|
interface LoadSettingOptions {
|
|
1658
1692
|
store?: SettingStore;
|
|
@@ -1678,34 +1712,10 @@ declare function migrate(config: Record<string, unknown>, migrations?: Migration
|
|
|
1678
1712
|
applied: string[];
|
|
1679
1713
|
};
|
|
1680
1714
|
//#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
1715
|
//#region src/setting.d.ts
|
|
1702
1716
|
declare class SettingLoader {
|
|
1703
1717
|
private store?;
|
|
1704
|
-
/** RFC review D11:上次 load 的来源树(惰性快照,供诊断面查询「值来自哪层」)。 */
|
|
1705
|
-
private lastOrigins?;
|
|
1706
1718
|
constructor(store?: SettingStore);
|
|
1707
|
-
/** RFC review D11:上次 load 的配置来源树(未 load 过返回 undefined)。 */
|
|
1708
|
-
getOrigins(): ConfigOrigins | undefined;
|
|
1709
1719
|
load(options?: LoadSettingOptions): Promise<Setting>;
|
|
1710
1720
|
}
|
|
1711
1721
|
declare function loadSetting(options?: LoadSettingOptions): Promise<Setting>;
|
|
@@ -1791,19 +1801,49 @@ declare class RemoteSettingStore implements SettingStore {
|
|
|
1791
1801
|
exists(_path: string): Promise<boolean>;
|
|
1792
1802
|
}
|
|
1793
1803
|
//#endregion
|
|
1794
|
-
//#region src/
|
|
1804
|
+
//#region src/field-meta.d.ts
|
|
1805
|
+
/** 字段合并策略类别(与 config-merge-strategy 的 MergeCategory 对齐)。 */
|
|
1806
|
+
type FieldMergeCategory = 'scalar-override' | 'array-merge' | 'array-merge-by-name' | 'record-merge';
|
|
1807
|
+
/** 是否参与导出/导入。 */
|
|
1808
|
+
type FieldExportPolicy = 'include' | 'exclude';
|
|
1809
|
+
/** 是否可跨设备远端同步。 */
|
|
1810
|
+
type FieldSyncPolicy = 'syncable' | 'never-synced' | 'local-only';
|
|
1811
|
+
/** 单字段元数据。 */
|
|
1812
|
+
interface FieldMeta {
|
|
1813
|
+
/** 跨层合并 / 导入合并策略。 */
|
|
1814
|
+
merge: FieldMergeCategory;
|
|
1815
|
+
/** 是否恒定路由 user 级配置存储(RFC-219 重要事项规则 5)。 */
|
|
1816
|
+
deviceBound: boolean;
|
|
1817
|
+
/** 导出/导入策略:exclude = 不导出不导入(元数据/内部记账字段)。 */
|
|
1818
|
+
export: FieldExportPolicy;
|
|
1819
|
+
/** 远端同步策略:syncable = 可跨设备同步;never-synced = 绝不同步(安全面/凭据/本地路径);local-only = 当前不支持同步但未来可能加入。 */
|
|
1820
|
+
sync: FieldSyncPolicy;
|
|
1821
|
+
}
|
|
1795
1822
|
/**
|
|
1796
|
-
* RFC-
|
|
1797
|
-
*
|
|
1798
|
-
* 与 SettingSchema 同包维护,字段增删时编译期提醒同步检查(R2)。
|
|
1823
|
+
* RFC-405 D1:字段元数据单表。每个 SettingKey 必须显式声明——satisfies 编译期穷举门
|
|
1824
|
+
* 钉死覆盖(新增 schema 字段后 tsc 报错逼停,不允许"先加字段后补元数据")。
|
|
1799
1825
|
*/
|
|
1800
|
-
declare const
|
|
1826
|
+
declare const FIELD_META: Readonly<Record<SettingKey, FieldMeta>>;
|
|
1827
|
+
/** 不导出/不导入的字段集合(取代 EXCLUDED_KEYS)。 */
|
|
1828
|
+
declare const EXCLUDED_KEYS: ReadonlySet<SettingKey>;
|
|
1829
|
+
/** 可跨设备同步的字段集合(取代 SYNCABLE_KEYS)。 */
|
|
1830
|
+
declare const SYNCABLE_KEYS: ReadonlySet<SettingKey>;
|
|
1831
|
+
/** 绝不可远端同步的字段集合(取代 NEVER_SYNCED_KEYS)。 */
|
|
1832
|
+
declare const NEVER_SYNCED_KEYS: ReadonlySet<string>;
|
|
1833
|
+
/** 字段静态分类表(取代 FIELD_CLASSIFICATION)。 */
|
|
1834
|
+
declare const FIELD_CLASSIFICATION: ReadonlyMap<SettingKey, FieldClassification>;
|
|
1835
|
+
/** 单字段的静态分类(与具体 diff 值无关)。 */
|
|
1836
|
+
interface FieldClassification {
|
|
1837
|
+
category: FieldMergeCategory;
|
|
1838
|
+
deviceBound: boolean;
|
|
1839
|
+
}
|
|
1801
1840
|
/**
|
|
1802
|
-
*
|
|
1803
|
-
*
|
|
1804
|
-
* 此列表保持与 SYNCABLE_KEYS 互斥;不在任一集合的字段为"当前不支持同步,未来可能加入"。
|
|
1841
|
+
* 断言字段元数据表覆盖全部 `SETTING_KEYS`。satisfies 已在编译期保证全覆盖,
|
|
1842
|
+
* 此函数保留供运行时测试调用(向后兼容现有测试)。
|
|
1805
1843
|
*/
|
|
1806
|
-
declare
|
|
1844
|
+
declare function assertFieldClassificationCoversAllKeys(): void;
|
|
1845
|
+
//#endregion
|
|
1846
|
+
//#region src/remote-sync-keys.d.ts
|
|
1807
1847
|
/**
|
|
1808
1848
|
* 从 Partial<Setting> 中过滤出仅 syncable 字段(R2)。
|
|
1809
1849
|
* NEVER_SYNCED 字段即使传入也被移除。
|
|
@@ -1968,6 +2008,62 @@ interface SanitizeResult {
|
|
|
1968
2008
|
*/
|
|
1969
2009
|
declare function sanitizeUntrustedProjectLayer(layer: Partial<Setting>): SanitizeResult;
|
|
1970
2010
|
//#endregion
|
|
2011
|
+
//#region src/project-trust-gate.d.ts
|
|
2012
|
+
/**
|
|
2013
|
+
* 信任白名单落盘路径(用户级)。`OTTO_CLAUDE_TRUST_PATH` 可覆盖(测试/隔离用,
|
|
2014
|
+
* 避免污染真实 `~/.otto`——参照 auth-store 真路径教训)。调用时求值。
|
|
2015
|
+
*/
|
|
2016
|
+
declare function claudeTrustStorePath(): string;
|
|
2017
|
+
/** 该项目根是否已被用户显式信任。 */
|
|
2018
|
+
declare function isProjectTrusted(workspaceDir: string, storePath?: string): boolean;
|
|
2019
|
+
/** 显式信任某项目根(幂等,持久化)。 */
|
|
2020
|
+
declare function trustProject(workspaceDir: string, storePath?: string): void;
|
|
2021
|
+
/** 撤销对某项目根的信任(持久化)。 */
|
|
2022
|
+
declare function untrustProject(workspaceDir: string, storePath?: string): void;
|
|
2023
|
+
/**
|
|
2024
|
+
* 严格项目配置门控是否开启(env-only,项目 config 不可影响,杜绝恶意仓库自关门)。
|
|
2025
|
+
* **默认开**(安全默认):克隆仓库的 `.otto/config.json` 高危键默认不生效,须 `/trust trust`。
|
|
2026
|
+
* `OTTO_STRICT_PROJECT_CONFIG=0` 是逃生口(关门控、恢复旧的全信任行为)。
|
|
2027
|
+
* 注:仅交互态实际剔除(headless fail-open,见 resolveProjectConfigTrusted),故 CI/脚本/测试不受影响。
|
|
2028
|
+
*/
|
|
2029
|
+
declare function isStrictProjectConfigEnabled(): boolean;
|
|
2030
|
+
interface ProjectConfigTrustContext {
|
|
2031
|
+
/** 是否交互(TUI)态。缺省 false=headless → fail-open 放行。 */
|
|
2032
|
+
interactive?: boolean;
|
|
2033
|
+
}
|
|
2034
|
+
/**
|
|
2035
|
+
* 该 workspace 的项目配置是否受信任。
|
|
2036
|
+
* 严格模式关 → 恒 `true`;headless → 恒 `true`(fail-open);交互+严格 → 取信任白名单判定。
|
|
2037
|
+
*/
|
|
2038
|
+
declare function resolveProjectConfigTrusted(workspaceDir: string, ctx?: ProjectConfigTrustContext): boolean;
|
|
2039
|
+
interface ProjectConfigGating {
|
|
2040
|
+
/** 项目 `.otto/config.json` 实际含有的高危键(空 = 无可门控内容)。 */
|
|
2041
|
+
highRiskKeys: string[];
|
|
2042
|
+
/** 这些高危键当前是否被剔除(严格开 + 交互 + 未信任)。 */
|
|
2043
|
+
gated: boolean;
|
|
2044
|
+
}
|
|
2045
|
+
/**
|
|
2046
|
+
* 探测项目 `.otto/config.json` 的高危键门控状态(供 `/compat status` 呈现)。
|
|
2047
|
+
* 严格模式关 → 返空(特性未启用,无需呈现噪声)。读文件失败/无文件 → 返空。
|
|
2048
|
+
*/
|
|
2049
|
+
declare function detectProjectConfigGating(workspaceDir: string, ctx?: ProjectConfigTrustContext): ProjectConfigGating;
|
|
2050
|
+
/** 用户对工作区信任弹窗的选择。 */
|
|
2051
|
+
type WorkspaceTrustAction = 'trust' | 'readonly' | 'exit';
|
|
2052
|
+
interface WorkspaceTrustPromptDecision {
|
|
2053
|
+
/** 是否应弹"信任此文件夹吗"。 */
|
|
2054
|
+
shouldPrompt: boolean;
|
|
2055
|
+
/** 解析到的工作区根(信任按它记录)。 */
|
|
2056
|
+
root: string;
|
|
2057
|
+
/** 不弹的原因(诊断/测试)。 */
|
|
2058
|
+
reason: 'trusted' | 'not-interactive' | 'strict-off' | 'not-a-project' | 'untrusted';
|
|
2059
|
+
}
|
|
2060
|
+
/**
|
|
2061
|
+
* 工作区信任弹窗的纯决策:启动时是否要弹"信任此文件夹吗"。
|
|
2062
|
+
* 弹 ⟺ 交互 且 严格开 且 根未信任 且 是真项目。其余一律不弹。
|
|
2063
|
+
* 纯函数:只读 FS + env,不做 IO 副作用、不渲染。
|
|
2064
|
+
*/
|
|
2065
|
+
declare function evaluateWorkspaceTrustPrompt(workspaceDir: string, ctx?: ProjectConfigTrustContext): WorkspaceTrustPromptDecision;
|
|
2066
|
+
//#endregion
|
|
1971
2067
|
//#region src/presets.d.ts
|
|
1972
2068
|
/**
|
|
1973
2069
|
* 预制 agent persona 已移除(2026-06-13,agent-profiles-removal 方案)。
|
|
@@ -1979,34 +2075,11 @@ declare function sanitizeUntrustedProjectLayer(layer: Partial<Setting>): Sanitiz
|
|
|
1979
2075
|
declare const BUILTIN_AGENT_PROFILES: AgentProfile[];
|
|
1980
2076
|
//#endregion
|
|
1981
2077
|
//#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
2078
|
/**
|
|
1994
|
-
*
|
|
1995
|
-
*
|
|
1996
|
-
* 文件里的值);`last_update_check`/`_migrations` 是内部记账字段。
|
|
2079
|
+
* RFC-405 D1:MergeCategory 从 field-meta 的 FieldMergeCategory 派生(单源)。
|
|
2080
|
+
* 保留原类型名向后兼容(消费方 import MergeCategory)。
|
|
1997
2081
|
*/
|
|
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;
|
|
2082
|
+
type MergeCategory = FieldMergeCategory;
|
|
2010
2083
|
/** 单字段差异。`risk` 是按本次具体 from/to 值计算的实例级结果,非字段静态属性。 */
|
|
2011
2084
|
interface FieldDiff {
|
|
2012
2085
|
key: SettingKey;
|
|
@@ -2120,5 +2193,5 @@ declare class ExportFileParseError extends Error {
|
|
|
2120
2193
|
*/
|
|
2121
2194
|
declare function parseExportFile(raw: string): ConfigExportFile;
|
|
2122
2195
|
//#endregion
|
|
2123
|
-
export { type AgentOptions, BUILTIN_AGENT_PROFILES, BUILTIN_REVIEWER_AGENTS, CURRENT_EXPORT_VERSION, type CategoryConfig, type ConfigExportFile,
|
|
2196
|
+
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 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
2197
|
//# 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 ee,OTTO_HOME as d,OTTO_PROJECT_CONFIG_RELPATH as te,isClaudeSettingsCompatEnabled as ne,isShadowModeEnabled as re}from"@x-otto/env";import{isAbsolute as ie,join as f,resolve as p}from"node:path";import{homedir as m}from"node:os";import{existsSync as ae,readFileSync as oe}from"node:fs";import{HIGH_RISK_CAPABILITIES as se,isPathTrusted as ce,trustPath as le,untrustPath as ue}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),de=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())}),fe=t.object({preferred_models:_(t.array(t.string())),default_model:_(t.string())}),pe=t.object({enabled:_(t.boolean()),every_n_turns:_(t.number()),max_lessons:_(t.number()),similarity_threshold:_(t.number()),min_relevant_hits:_(t.number())}),me=t.object({enabled:_(t.boolean()),min_messages:_(t.number()),dismiss_cooldown_days:_(t.number())}),he=t.object({enabled:_(t.boolean()),min_executions:_(t.number()),min_success_rate:_(t.number()),max_internalized:_(t.number())}),ge=t.object({slots:_(t.partialRecord(v,t.union([t.string(),t.array(t.string())]))),default:_(t.string()),subagent_default_slot:_(v)}),_e=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()))}),ve=t.object({enabled:_(t.boolean()),probability:_(t.number()),min_turn_gap:_(t.number()),sink:_(t.string())}),ye=t.object({id:_(t.string()),path:_(t.string()),enabled:_(t.boolean())}),be=t.object({enabled:_(t.boolean()),maxEntries:_(t.number()),sources:_(t.array(ye))}),xe=t.object({external:_(be)}),Se=t.object({enabled:t.boolean(),network:_(t.enum([`allow`,`deny`])),writablePaths:_(t.array(t.string())),protectCredentials:_(t.boolean()),autoAllowBashIfSandboxed:_(t.boolean()),semanticReview:_(t.boolean())}),Ce=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 we=t.object({enabled:_(t.boolean())}),Te=t.object({enabled:_(t.boolean()),maxAttempts:_(t.number().int().min(1)),backoffMs:_(t.number().min(0)),backoffMultiplier:_(t.number().min(0))}),Ee=t.object({enabled:_(t.boolean()),failureThreshold:_(t.number().int().min(1)),timeoutMs:_(t.number().min(0))}),De=t.object({enabled:_(t.boolean())}),Oe=t.object({maxActiveTasks:_(t.number().int().min(1)),maxStoredTasks:_(t.number().int().min(1)),maxDelegationDepth:_(t.number().int().min(0)),maxFanOutPerParent:_(t.number().int().min(1))}),ke=t.object({enabled:_(t.boolean()),review:_(we),retry:_(Te),circuitBreaker:_(Ee),routing:_(De),orchestration:_(Oe)}),Ae=t.object({name:t.string(),effect:t.enum([`allow`,`deny`,`ask`]),tools:_(t.array(t.string())),paths:_(t.array(t.string())),commands:_(t.array(t.string())),deny_reason:_(t.string()),ask_prompt:_(t.string())}),je=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(Ae)}),Me=t.object({url:_(t.string()),enabled:_(t.boolean())}),Ne=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())}),Pe=t.object({maxRetries:_(t.number().int().positive()),initialDelayMs:_(t.number().positive()),backoffFactor:_(t.number().positive()),maxDelayMs:_(t.number().positive())}),Fe=t.object({maxRetries:_(t.number().int().positive()),intervalMs:_(t.number().positive())}),Ie=t.object({maxReconnectAttempts:_(t.number().int().nonnegative()),reconnectDelayMs:_(t.number().positive()),requestTimeoutMs:_(t.number().positive())}),Le=t.object({retryBackoffMs:_(t.number().nonnegative())}),Re=t.object({maxAttempts:_(t.number().int().positive()),intervalMs:_(t.number().positive())}),ze=t.object({refreshLockTimeoutMs:_(t.number().min(1e3))}),Be=t.object({stream:_(Pe),networkDisconnect:_(Fe),mcp:_(Ie),schedule:_(Le),errorRetry:_(Re),oauth:_(ze)});function y(e){if(typeof e!=`object`||!e||Array.isArray(e))return;let t=Be.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 Ve(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 y(n)}const He=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()))}),Ue=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())}),We=t.object({name:t.string().min(1).max(64),text:t.string().min(1).max(4e3)}),Ge=`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(Ge),Ke=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`]))}),qe=t.strictObject({markdown:_(Ke)}),Je=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(Ue)),shortcuts:_(t.array(We)),feedback_repo:_(t.string()),feedback_platform:_(t.enum([`auto`,`github`,`coding`])),feedback_coding_base_url:_(t.string()),feedback_coding_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:_(qe),theme_preset:_(Je),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(),de)),categories:_(t.record(t.string(),fe)),tool_preset:_(t.enum(h)),memory_auto_extract:_(pe),skill_loop:_(me),skill_internalization:_(he),notification:_(_e),pulse_survey:_(ve),history:_(xe),workflow:_(ke),model_slots:_(ge),mcp_servers:_(t.record(t.string(),Ne)),resilience:_(Be),sandbox:_(Se),residency:_(Ce),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(He)),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(je)),permission_always_allow:_(t.array(t.string())),plugin_lifecycle:_(t.object({setupDone:_(t.array(t.object({id:t.string(),version:_(t.string())})))})),proxy:_(Me),web_fetch_allowed_hosts:_(t.array(t.string())),_migrations:_(t.array(t.string()))}),Ye=Ge,S=x.keyof().options,Xe={"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`}},C={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:{...Xe},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},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:[]},Ze=a(`@x-otto/setting:migrator`);function w(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}`),Ze.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 T=[`permission_mode`,`permissions`,`permission_always_allow`,`mcp_servers`,`disabled_hooks`,`sandbox`];function E(e){let t={...e},n=[];for(let e of T)e in t&&(delete t[e],n.push(e));return{value:t,stripped:n}}const D=a(`@x-otto/setting`),Qe=new Set(g),$e=[{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`}],et=new Set([`accent`,`accentDim`,`accentBright`,`colors`,`spacing`,`border`,`figures`,`text`,`inactive`,`secondary`,`success`,`error`,`warning`,`special`]);function O(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function tt(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}O(a)&&O(e)&&tt(a,e,[...n,i],r)}}function k(e,t){let n={...e};for(let[e,r]of Object.entries(t))r!==void 0&&(O(r)&&O(n[e])?n[e]=k(n[e],r):n[e]=r);return n}function nt(e,t,n){let r=new Map;for(let i of[...e,...t])O(i)&&typeof i[n]==`string`&&r.set(i[n],i);return[...r.values()]}function A(...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]=nt(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 O(t)&&O(i[e])?i[e]=k(i[e],t):i[e]=t}return r.length>0?A(i,...r):i}function rt(){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 it(e){let t={...e},n=[];for(let{key:e,message:r}of $e)t[e]!==void 0&&(n.push({key:e,message:r}),delete t[e]);if(O(t.theme_overrides)){let e=Object.keys(t.theme_overrides).filter(e=>et.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 at(e){let t=x.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 ot(e,t){let n=[];for(let r of S){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}O(i)&&O(a)&&tt(i,a,[r],n)}return n}function st(e){let{cleaned:t,warnings:n}=it(e),r=at(t),i=ot(t,r);return{value:r,warnings:[...n,...i]}}async function ct(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=E(e);e=t.value,i.push(...t.stripped)}r.push(e),D.debug({path:a},`Config loaded from store`)}}catch(e){D.warn({path:a,err:e},`Failed to parse config file, skipping`)}return{layers:r,stripped:i}}var j=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 ct(t,n,i):{layers:[],stripped:[]};for(let e of o)D.warn({key:e,path:i},`未受信任项目配置剔除高危键(permission_mode/mcp_servers 等需先信任该目录)`);let s=rt(),{config:c,applied:l}=w(A(C,...r,...a,s));l.length>0&&D.info({applied:l},`Config migrations applied`);let{value:u,warnings:ee}=st(c);for(let e of ee)D.warn({key:e.key,message:e.message},`Config validation issue`);return{...C,...u}}};async function M(e={}){return new j(e.store).load(e)}var N=class{type=`file`;async read(e){let{readFile:t}=await import(`node:fs/promises`);try{return await t(await P(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 P(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 P(e)),!0}catch{return!1}}};async function P(e){if(!e.startsWith(`~/`))return e;let{homedir:t}=await import(`node:os`);return`${t()}${e.slice(1)}`}const F=(e={})=>({merge:`scalar-override`,deviceBound:!1,export:`include`,sync:`local-only`,...e}),I={config_version:F({export:`exclude`,sync:`never-synced`}),version:F({export:`exclude`,sync:`never-synced`}),last_update_check:F({export:`exclude`,sync:`never-synced`}),_migrations:F({export:`exclude`,sync:`never-synced`}),model:F({sync:`syncable`}),model_fallback:F({merge:`array-merge`,sync:`syncable`}),recent_models:F({merge:`array-merge`,deviceBound:!0,sync:`never-synced`}),recent_mentions:F({merge:`array-merge`,sync:`never-synced`}),shortcuts:F({merge:`array-merge`,sync:`never-synced`}),feedback_repo:F({sync:`local-only`}),feedback_platform:F({sync:`local-only`}),feedback_coding_base_url:F({sync:`local-only`}),feedback_coding_repo:F({sync:`local-only`}),keybinding_overrides:F({merge:`record-merge`,deviceBound:!0,sync:`never-synced`}),language:F({sync:`syncable`}),theme:F({deviceBound:!0,sync:`never-synced`}),theme_overrides:F({deviceBound:!0,sync:`never-synced`}),theme_preset:F({deviceBound:!0,sync:`never-synced`}),nickname:F({sync:`syncable`}),has_completed_onboarding:F({sync:`syncable`}),has_completed_wizard:F({sync:`syncable`}),wizard_dont_show_again:F({sync:`syncable`}),first_prompt_submitted_at:F({sync:`syncable`}),thinking_level:F({sync:`local-only`}),log_level:F({sync:`syncable`}),tool_preset:F({sync:`local-only`}),agents:F({merge:`record-merge`,sync:`never-synced`}),categories:F({merge:`record-merge`,sync:`never-synced`}),model_slots:F({merge:`record-merge`,sync:`syncable`}),memory_auto_extract:F({sync:`never-synced`}),skill_loop:F({sync:`never-synced`}),skill_internalization:F({sync:`never-synced`}),notification:F({deviceBound:!0,sync:`syncable`}),pulse_survey:F({deviceBound:!0,sync:`never-synced`}),history:F({sync:`never-synced`}),workflow:F({sync:`never-synced`}),residency:F({sync:`never-synced`}),resilience:F({sync:`never-synced`}),sandbox:F({sync:`never-synced`}),permission_mode:F({sync:`local-only`}),permissions:F({merge:`array-merge-by-name`,sync:`never-synced`}),permission_always_allow:F({merge:`array-merge`,sync:`never-synced`}),mcp_servers:F({merge:`record-merge`,sync:`never-synced`}),proxy:F({deviceBound:!0,sync:`never-synced`}),web_fetch_allowed_hosts:F({merge:`array-merge`,sync:`never-synced`}),prompt_output_token_budget:F({sync:`local-only`}),prompt_wall_clock_budget_ms:F({sync:`local-only`}),session_cost_budget_usd:F({sync:`local-only`}),stall_detection:F({sync:`never-synced`}),todo_continue_max:F({sync:`local-only`}),disabled_agents:F({merge:`array-merge`,sync:`syncable`}),disabled_hooks:F({merge:`array-merge`,sync:`syncable`}),disabled_tools:F({merge:`array-merge`,sync:`syncable`}),disabled_skills:F({merge:`array-merge`,sync:`syncable`}),disabled_plugins:F({merge:`array-merge`,sync:`never-synced`}),installed_plugins:F({merge:`array-merge`,deviceBound:!0,sync:`never-synced`}),plugin_lifecycle:F({deviceBound:!0,sync:`never-synced`}),auto_update_check:F({deviceBound:!0,sync:`syncable`})},L=new Set(Object.entries(I).filter(([,e])=>e.export===`exclude`).map(([e])=>e)),R=new Set(Object.entries(I).filter(([,e])=>e.sync===`syncable`).map(([e])=>e)),z=new Set(Object.entries(I).filter(([,e])=>e.sync===`never-synced`).map(([e])=>e)),B=new Map(Object.entries(I).filter(([,e])=>e.export!==`exclude`).map(([e,t])=>[e,{category:t.merge,deviceBound:t.deviceBound}]));function lt(){let e=S.filter(e=>!I[e]);if(e.length>0)throw Error(`FIELD_META 遗漏以下 SettingKey(satisfies 门禁应已拦截): ${e.join(`, `)}`)}function V(e){let t={};for(let n of Object.keys(e))R.has(n)&&!z.has(n)&&(t[n]=e[n]);return t}var ut=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`),dt=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(`~/`)?f(m(),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 ft(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(dt.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`&&ft(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 pt(e){if(!ne())return{};let t=[f(u,`settings.json`),...e?[f(p(e),`.claude`,`settings.json`),f(p(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 mt=class extends i{paths;workspaceDir;projectConfigTrusted;overrides;store;remoteStore;setting={};writeChain=Promise.resolve();constructor(e={}){super(),this.paths=ht(e),this.workspaceDir=e.workspaceDir,this.projectConfigTrusted=e.projectConfigTrusted??!0,this.overrides={...e.overrides},this.store=e.store??new N,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 M({store:this.store,paths:this.paths,untrustedProjectPath:this.resolveUntrustedProjectPath(),extraLayers:[pt(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(re())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 ht(e){let t;if(e.projectConfigPath?t=e.projectConfigPath:e.workspaceDir&&(t=p(e.workspaceDir,te)),!t)return[];let n=p(d,ee);return p(t)===n?[t]:[n,t]}const gt=a(`@x-otto/setting:project-trust-gate`);function q(){return process.env.OTTO_CLAUDE_TRUST_PATH||f(d,`claude-trust.json`)}function J(e,t=q()){return ce(t,e)}function _t(e,t=q()){le(t,e,`project-trust`)&>.info({root:p(e)},`Project trusted`)}function vt(e,t=q()){ue(t,e,`project-trust`)&>.info({root:p(e)},`Project trust revoked`)}function Y(){return process.env.OTTO_STRICT_PROJECT_CONFIG!==`0`}function X(e,t={}){return!Y()||!t.interactive?!0:J(e)}function yt(e,t={}){if(!Y())return{highRiskKeys:[],gated:!1};let n;try{n=s(oe(f(e,te),`utf-8`))}catch{return{highRiskKeys:[],gated:!1}}let r=E(n).stripped;return{highRiskKeys:r,gated:r.length>0&&!X(e,t)}}function bt(e){return e===p(m())||e===p(`/`)?!1:[`.git`,`.claude`,`.mcp.json`].some(t=>ae(f(e,t)))}function xt(e,t={}){let n=p(e);return t.interactive?Y()?J(e)?{shouldPrompt:!1,root:n,reason:`trusted`}:bt(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 St=[],Ct=new Set(T);function Z(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function Q(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 wt={bypass:0,auto:1,confirm:2,strict:3,readonly:4};function Tt(e,t){if(typeof t!=`string`||e===t)return!1;let n=typeof e==`string`?wt[e]:void 0,r=wt[t];return n===void 0||r===void 0?!0:r<n}function Et(e,t){let n=Z(e)?e.enabled:void 0,r=Z(t)?t.enabled:void 0;return n===!0&&r===!1}function Dt(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 Ot(e,t,n){if(!Ct.has(e))return`safe`;if(e===`permission_mode`)return Tt(t,n)?`sensitive`:`safe`;if(e===`sandbox`)return Et(t,n)?`sensitive`:`safe`;if(e===`mcp_servers`){let e=Z(t)?t:{},r=Z(n)?n:{};for(let[t,n]of Object.entries(r))if(!(t in e)||!Q(e[t],n))return`sensitive`;return`safe`}if(e===`disabled_hooks`)return Q(t,n)?`safe`:`sensitive`;if(e===`permissions`||e===`permission_always_allow`)return Dt(t,n).length>0?`sensitive`:`safe`;throw Error(`classifyRisk: 字段 "${e}" 在 HIGH_RISK_PROJECT_KEYS 中但未被显式处理——新增高危字段必须同步补充判定分支,不允许 fail-open`)}function kt(e,t){let n=[];for(let r of S){if(L.has(r)||r===`installed_plugins`||!(r in t))continue;let i=e[r],a=t[r];if(Q(i,a))continue;let o=B.get(r);o&&n.push({key:r,from:i,to:a,category:o.category,deviceBound:o.deviceBound,risk:Ot(r,i,a)})}return n}function At(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 jt(e,t){return e.some(e=>e.risk===`sensitive`)?!0:t.toInstall.some(e=>(e.capabilities??[]).some(e=>se.includes(e)))}function Mt(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])Z(n)&&typeof n.name==`string`&&i.set(n.name,n);n[r.key]=[...i.values()]}else if(r.category===`record-merge`){let e=Z(r.from)?r.from:{},t=Z(r.to)?r.to:{};n[r.key]={...e,...t}}else n[r.key]=r.to;return n}const Nt=new Set([`env`,`headers`]);function Pt(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function Ft(e){let t=[],n=(e,r)=>{if(!Pt(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`&&Nt.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 It=1;function Lt(e,t,n,r=()=>new Date().toISOString()){let i={};for(let[t,n]of Object.entries(e))if(!L.has(t)&&n!==void 0){if(t===`installed_plugins`){i[t]=n.map(e=>e.sourceSpec&&ie(e.sourceSpec)?{...e,sourceSpec:``}:e);continue}i[t]=n}let a=Ft(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 Rt=class extends Error{constructor(e,t){super(`导入文件 otto_config_version=${e},当前 otto 支持=${t}。请升级 otto 到最新版本后再试。`),this.fileVersion=e,this.supportedVersion=t,this.name=`ExportFileVersionError`}},$=class extends Error{constructor(e){super(`导入文件格式非法:${e}`),this.name=`ExportFileParseError`}};function zt(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function Bt(e){let t;try{t=JSON.parse(e)}catch(e){throw new $(e instanceof Error?e.message:String(e))}if(!zt(t))throw new $(`顶层结构必须是 JSON 对象`);let{otto_config_version:n,exported_at:r,exported_from:i,scope:a,fields:o}=t;if(typeof n!=`number`)throw new $(`缺少或非法的 otto_config_version 字段`);if(n!==1)throw new Rt(n,1);if(typeof r!=`string`||typeof i!=`string`)throw new $(`缺少或非法的 exported_at/exported_from 字段`);if(a!==`global`&&a!==`project`)throw new $(`scope 字段必须是 "global" 或 "project"`);if(!zt(o))throw new $(`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{St as BUILTIN_AGENT_PROFILES,Xe as BUILTIN_REVIEWER_AGENTS,It as CURRENT_EXPORT_VERSION,C as DEFAULT_CONFIG,L as EXCLUDED_KEYS,$ as ExportFileParseError,Rt as ExportFileVersionError,B as FIELD_CLASSIFICATION,I as FIELD_META,T as HIGH_RISK_PROJECT_KEYS,g as LOG_LEVELS,z as NEVER_SYNCED_KEYS,e as PERMISSION_MODES,N as RawFileSettingStore,ut as RemoteSettingStore,S as SETTING_KEYS,R as SYNCABLE_KEYS,j as SettingLoader,mt as SettingsManager,Ye as THEME_COLOR_ROLES,h as TOOL_PRESETS,Mt as applyMergeStrategy,lt as assertFieldClassificationCoversAllKeys,Lt as buildExportPayload,At as classifyPluginChanges,q as claudeTrustStorePath,yt as detectProjectConfigGating,kt as diffConfig,xt as evaluateWorkspaceTrustPrompt,V as filterSyncable,G as globToRegexSource,jt as hasSecuritySensitiveChanges,J as isProjectTrusted,Y as isStrictProjectConfigEnabled,pt as loadClaudeSettingsLayer,M as loadSetting,w as migrate,Bt as parseExportFile,Ve as parseResilienceEnvOverride,W as parseToolSpecifier,X as resolveProjectConfigTrusted,E as sanitizeUntrustedProjectLayer,K as translateClaudeSettings,_t as trustProject,vt as untrustProject,y as validateResilienceOverride};
|
|
2
2
|
//# sourceMappingURL=index.js.map
|