@x-otto/setting 0.0.1-alpha.3 → 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 +6 -18
- package/dist/index.d.ts +154 -42
- 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,
|
|
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
|
|
|
@@ -25,13 +25,6 @@ const manager = new SettingsManager({
|
|
|
25
25
|
await manager.load()
|
|
26
26
|
console.log(manager.get('model')) // current model setting
|
|
27
27
|
console.log(manager.config) // merged full config
|
|
28
|
-
|
|
29
|
-
// Watch for file changes
|
|
30
|
-
const { createSettingWatcher } = await import('@x-otto/setting')
|
|
31
|
-
const watcher = createSettingWatcher({
|
|
32
|
-
paths: ['/path/to/project/.otto/config.json'],
|
|
33
|
-
reload: (path) => manager.reload(),
|
|
34
|
-
})
|
|
35
28
|
```
|
|
36
29
|
|
|
37
30
|
## API
|
|
@@ -41,23 +34,18 @@ const watcher = createSettingWatcher({
|
|
|
41
34
|
- `load()` / `reload()` — load/reload from store + env
|
|
42
35
|
- `update(overrides)` — apply runtime overrides
|
|
43
36
|
- `get(key)` — typed access to specific settings
|
|
44
|
-
- `config` / `
|
|
45
|
-
- `
|
|
46
|
-
- Events: `'change'` — emitted on config change
|
|
37
|
+
- `config` / `model` — full merged config and model accessor
|
|
38
|
+
- Events: `'change'` — emitted on config change; `'remote-sync-failed'` — remote layer read failure
|
|
47
39
|
|
|
48
40
|
### SettingLoader
|
|
49
41
|
- `loadSetting(options)` — multi-source load + merge + validate
|
|
50
|
-
- `SettingLoader
|
|
42
|
+
- `SettingLoader` — loader class (no write-back API; persistence is handled by stores)
|
|
51
43
|
|
|
52
44
|
### Stores
|
|
53
45
|
- `RawFileSettingStore` — literal file path read/write (with `~/` expansion)
|
|
54
46
|
- `RemoteSettingStore` — HTTP remote config fetching
|
|
55
47
|
- `SettingStore` interface — pluggable storage
|
|
56
48
|
|
|
57
|
-
### Watcher
|
|
58
|
-
- `createSettingWatcher(options)` — `fs.watch` with per-path debounce (300ms default)
|
|
59
|
-
- Implements `Disposable` (`Symbol.dispose`)
|
|
60
|
-
|
|
61
49
|
### Migration
|
|
62
50
|
- `migrate(config, migrations)` — pure function, semver-ordered migration chain
|
|
63
51
|
- Appends `_migrations` log on each step
|
|
@@ -80,7 +68,7 @@ const watcher = createSettingWatcher({
|
|
|
80
68
|
### Built-in Presets
|
|
81
69
|
- `BUILTIN_REVIEWER_AGENTS` — 2 reviewer agent presets
|
|
82
70
|
- `DEFAULT_CONFIG` — complete default configuration
|
|
83
|
-
- `BUILTIN_AGENT_PROFILES` — agent profile presets
|
|
71
|
+
- `BUILTIN_AGENT_PROFILES` — agent profile presets (removed; empty array — custom agents go through declarative `.md` files)
|
|
84
72
|
- `HIGH_RISK_PROJECT_KEYS` / `sanitizeUntrustedProjectLayer` — project trust model
|
|
85
73
|
|
|
86
74
|
### Constants
|
|
@@ -90,7 +78,7 @@ const watcher = createSettingWatcher({
|
|
|
90
78
|
|
|
91
79
|
## Dependencies
|
|
92
80
|
|
|
93
|
-
- Internal: `@x-otto/env`, `@x-otto/shared`, `@x-otto/hook-contracts`, `@x-otto/orchestration-contracts`, `@x-otto/plugin`, `@x-otto/prompt`
|
|
81
|
+
- Internal: `@x-otto/env`, `@x-otto/shared`, `@x-otto/hook-contracts`, `@x-otto/orchestration-contracts`, `@x-otto/persistence`, `@x-otto/plugin`, `@x-otto/prompt`
|
|
94
82
|
- External: `semver`, `zod`
|
|
95
83
|
|
|
96
84
|
## Related
|
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/…)
|
|
@@ -160,8 +180,8 @@ declare const recentMentionEntrySchema: z.ZodObject<{
|
|
|
160
180
|
label: z.ZodString;
|
|
161
181
|
value: z.ZodString;
|
|
162
182
|
kind: z.ZodEnum<{
|
|
163
|
-
file: "file";
|
|
164
183
|
model: "model";
|
|
184
|
+
file: "file";
|
|
165
185
|
hashtag: "hashtag";
|
|
166
186
|
person: "person";
|
|
167
187
|
agent: "agent";
|
|
@@ -951,8 +971,8 @@ declare const SettingSchema: z.ZodObject<{
|
|
|
951
971
|
label: z.ZodString;
|
|
952
972
|
value: z.ZodString;
|
|
953
973
|
kind: z.ZodEnum<{
|
|
954
|
-
file: "file";
|
|
955
974
|
model: "model";
|
|
975
|
+
file: "file";
|
|
956
976
|
hashtag: "hashtag";
|
|
957
977
|
person: "person";
|
|
958
978
|
agent: "agent";
|
|
@@ -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>;
|
|
@@ -1396,12 +1423,19 @@ declare const SettingSchema: z.ZodObject<{
|
|
|
1396
1423
|
every_n_turns: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
1397
1424
|
max_lessons: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
1398
1425
|
similarity_threshold: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
1426
|
+
min_relevant_hits: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
1399
1427
|
}, z.core.$strip>>>;
|
|
1400
1428
|
skill_loop: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
1401
1429
|
enabled: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
1402
1430
|
min_messages: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
1403
1431
|
dismiss_cooldown_days: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
1404
1432
|
}, z.core.$strip>>>;
|
|
1433
|
+
skill_internalization: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
1434
|
+
enabled: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
1435
|
+
min_executions: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
1436
|
+
min_success_rate: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
1437
|
+
max_internalized: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
1438
|
+
}, z.core.$strip>>>;
|
|
1405
1439
|
notification: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
1406
1440
|
enabled: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
1407
1441
|
sound: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
@@ -1449,6 +1483,8 @@ declare const SettingSchema: z.ZodObject<{
|
|
|
1449
1483
|
retry: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
1450
1484
|
enabled: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
1451
1485
|
maxAttempts: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
1486
|
+
backoffMs: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
1487
|
+
backoffMultiplier: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
1452
1488
|
}, z.core.$strip>>>;
|
|
1453
1489
|
circuitBreaker: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
1454
1490
|
enabled: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
@@ -1458,6 +1494,12 @@ declare const SettingSchema: z.ZodObject<{
|
|
|
1458
1494
|
routing: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
1459
1495
|
enabled: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
1460
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>>>;
|
|
1461
1503
|
}, z.core.$strip>>>;
|
|
1462
1504
|
model_slots: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
1463
1505
|
slots: z.ZodCatch<z.ZodOptional<z.ZodRecord<z.ZodEnum<{
|
|
@@ -1527,6 +1569,7 @@ declare const SettingSchema: z.ZodObject<{
|
|
|
1527
1569
|
writablePaths: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
1528
1570
|
protectCredentials: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
1529
1571
|
autoAllowBashIfSandboxed: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
1572
|
+
semanticReview: z.ZodCatch<z.ZodOptional<z.ZodBoolean>>;
|
|
1530
1573
|
}, z.core.$strip>>>;
|
|
1531
1574
|
residency: z.ZodCatch<z.ZodOptional<z.ZodObject<{
|
|
1532
1575
|
total_bytes: z.ZodCatch<z.ZodOptional<z.ZodNumber>>;
|
|
@@ -1599,6 +1642,7 @@ declare const SettingSchema: z.ZodObject<{
|
|
|
1599
1642
|
}>;
|
|
1600
1643
|
tools: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
1601
1644
|
paths: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
1645
|
+
commands: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
1602
1646
|
deny_reason: z.ZodCatch<z.ZodOptional<z.ZodString>>;
|
|
1603
1647
|
ask_prompt: z.ZodCatch<z.ZodOptional<z.ZodString>>;
|
|
1604
1648
|
}, z.core.$strip>>;
|
|
@@ -1618,7 +1662,6 @@ declare const SettingSchema: z.ZodObject<{
|
|
|
1618
1662
|
_migrations: z.ZodCatch<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
1619
1663
|
}, z.core.$strip>;
|
|
1620
1664
|
type Setting = z.infer<typeof SettingSchema>;
|
|
1621
|
-
type SettingFromSchema = Setting;
|
|
1622
1665
|
type ThemeColorRole = z.infer<typeof themeColorRoleSchema>;
|
|
1623
1666
|
type MarkdownThemeOverridesSetting = z.infer<typeof markdownThemeOverridesSchema>;
|
|
1624
1667
|
type ThemeOverridesSetting = z.infer<typeof themeOverridesSchema>;
|
|
@@ -1636,7 +1679,7 @@ type SandboxConfig = z.infer<typeof sandboxConfigSchema>;
|
|
|
1636
1679
|
type ProxyConfig = z.infer<typeof proxyConfigSchema>;
|
|
1637
1680
|
type McpServerSetting = z.infer<typeof mcpServerSettingSchema>;
|
|
1638
1681
|
type McpServersConfig = z.infer<typeof SettingSchema>['mcp_servers'];
|
|
1639
|
-
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" | "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")[];
|
|
1640
1683
|
type SettingKey = keyof Setting;
|
|
1641
1684
|
//#endregion
|
|
1642
1685
|
//#region src/types.d.ts
|
|
@@ -1644,8 +1687,6 @@ declare const BUILTIN_REVIEWER_AGENTS: Record<string, AgentOptions>;
|
|
|
1644
1687
|
interface SettingWarning {
|
|
1645
1688
|
key: string;
|
|
1646
1689
|
message: string;
|
|
1647
|
-
line?: number;
|
|
1648
|
-
column?: number;
|
|
1649
1690
|
}
|
|
1650
1691
|
interface LoadSettingOptions {
|
|
1651
1692
|
store?: SettingStore;
|
|
@@ -1760,19 +1801,49 @@ declare class RemoteSettingStore implements SettingStore {
|
|
|
1760
1801
|
exists(_path: string): Promise<boolean>;
|
|
1761
1802
|
}
|
|
1762
1803
|
//#endregion
|
|
1763
|
-
//#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
|
+
}
|
|
1764
1822
|
/**
|
|
1765
|
-
* RFC-
|
|
1766
|
-
*
|
|
1767
|
-
* 与 SettingSchema 同包维护,字段增删时编译期提醒同步检查(R2)。
|
|
1823
|
+
* RFC-405 D1:字段元数据单表。每个 SettingKey 必须显式声明——satisfies 编译期穷举门
|
|
1824
|
+
* 钉死覆盖(新增 schema 字段后 tsc 报错逼停,不允许"先加字段后补元数据")。
|
|
1768
1825
|
*/
|
|
1769
|
-
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
|
+
}
|
|
1770
1840
|
/**
|
|
1771
|
-
*
|
|
1772
|
-
*
|
|
1773
|
-
* 此列表保持与 SYNCABLE_KEYS 互斥;不在任一集合的字段为"当前不支持同步,未来可能加入"。
|
|
1841
|
+
* 断言字段元数据表覆盖全部 `SETTING_KEYS`。satisfies 已在编译期保证全覆盖,
|
|
1842
|
+
* 此函数保留供运行时测试调用(向后兼容现有测试)。
|
|
1774
1843
|
*/
|
|
1775
|
-
declare
|
|
1844
|
+
declare function assertFieldClassificationCoversAllKeys(): void;
|
|
1845
|
+
//#endregion
|
|
1846
|
+
//#region src/remote-sync-keys.d.ts
|
|
1776
1847
|
/**
|
|
1777
1848
|
* 从 Partial<Setting> 中过滤出仅 syncable 字段(R2)。
|
|
1778
1849
|
* NEVER_SYNCED 字段即使传入也被移除。
|
|
@@ -1937,6 +2008,62 @@ interface SanitizeResult {
|
|
|
1937
2008
|
*/
|
|
1938
2009
|
declare function sanitizeUntrustedProjectLayer(layer: Partial<Setting>): SanitizeResult;
|
|
1939
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
|
|
1940
2067
|
//#region src/presets.d.ts
|
|
1941
2068
|
/**
|
|
1942
2069
|
* 预制 agent persona 已移除(2026-06-13,agent-profiles-removal 方案)。
|
|
@@ -1948,34 +2075,11 @@ declare function sanitizeUntrustedProjectLayer(layer: Partial<Setting>): Sanitiz
|
|
|
1948
2075
|
declare const BUILTIN_AGENT_PROFILES: AgentProfile[];
|
|
1949
2076
|
//#endregion
|
|
1950
2077
|
//#region src/config-merge-strategy.d.ts
|
|
1951
|
-
/** 合并策略类别(RFC-219 milestone M2 §设计闸门要点订正后的接口)。 */
|
|
1952
|
-
type MergeCategory = 'scalar-override' | 'array-merge' | 'record-merge';
|
|
1953
|
-
/** 单个字段的静态分类(与具体 diff 值无关)。 */
|
|
1954
|
-
interface FieldClassification {
|
|
1955
|
-
category: MergeCategory;
|
|
1956
|
-
/**
|
|
1957
|
-
* 恒定路由 user 级配置存储,不受目标 scope 参数影响(RFC-219 重要事项规则 5)。
|
|
1958
|
-
* 仅供 M3/M4 的导入落盘目标判定使用,本模块自身不做任何写入。
|
|
1959
|
-
*/
|
|
1960
|
-
deviceBound: boolean;
|
|
1961
|
-
}
|
|
1962
|
-
/**
|
|
1963
|
-
* 排除字段——不导出、不导入,`diffConfig` 返回结果中永不出现(RFC-219 字段策略表)。
|
|
1964
|
-
* `config_version`/`version` 是元数据字段(导入侧沿用当前 otto 版本自身值,不采用导入
|
|
1965
|
-
* 文件里的值);`last_update_check`/`_migrations` 是内部记账字段。
|
|
1966
|
-
*/
|
|
1967
|
-
declare const EXCLUDED_KEYS: ReadonlySet<SettingKey>;
|
|
1968
2078
|
/**
|
|
1969
|
-
*
|
|
1970
|
-
*
|
|
1971
|
-
* `assertFieldClassificationCoversAllKeys`)。
|
|
2079
|
+
* RFC-405 D1:MergeCategory 从 field-meta 的 FieldMergeCategory 派生(单源)。
|
|
2080
|
+
* 保留原类型名向后兼容(消费方 import MergeCategory)。
|
|
1972
2081
|
*/
|
|
1973
|
-
|
|
1974
|
-
/**
|
|
1975
|
-
* 断言字段分类表覆盖全部 `SETTING_KEYS`(含 excluded 字段)。新增 schema 字段忘记归类
|
|
1976
|
-
* 时抛错——供单测调用,防止静默漏判(RFC-219 §风险与验证策略)。
|
|
1977
|
-
*/
|
|
1978
|
-
declare function assertFieldClassificationCoversAllKeys(): void;
|
|
2082
|
+
type MergeCategory = FieldMergeCategory;
|
|
1979
2083
|
/** 单字段差异。`risk` 是按本次具体 from/to 值计算的实例级结果,非字段静态属性。 */
|
|
1980
2084
|
interface FieldDiff {
|
|
1981
2085
|
key: SettingKey;
|
|
@@ -2055,6 +2159,14 @@ interface ConfigExportFile {
|
|
|
2055
2159
|
/** 导出侧的配置 scope('global' = 用户级 `~/.otto/config.json`,'project' = 项目级)。 */
|
|
2056
2160
|
scope: 'global' | 'project';
|
|
2057
2161
|
fields: Partial<Setting>;
|
|
2162
|
+
/**
|
|
2163
|
+
* RFC review D13:脱敏 sidecar(可选,向后兼容)——`mcp_servers.*.env/headers`
|
|
2164
|
+
* 在导出时被剥离,此处记录位置与「是否曾持有值」。缺省(旧导出产物)无此字段。
|
|
2165
|
+
*/
|
|
2166
|
+
redacted_secrets?: Array<{
|
|
2167
|
+
path: string[];
|
|
2168
|
+
set: boolean;
|
|
2169
|
+
}>;
|
|
2058
2170
|
}
|
|
2059
2171
|
/**
|
|
2060
2172
|
* 从当前配置构建导出产物(RFC-219 重要事项规则 9)。
|
|
@@ -2081,5 +2193,5 @@ declare class ExportFileParseError extends Error {
|
|
|
2081
2193
|
*/
|
|
2082
2194
|
declare function parseExportFile(raw: string): ConfigExportFile;
|
|
2083
2195
|
//#endregion
|
|
2084
|
-
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, type FieldClassification, type FieldDiff, 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 ProxyConfig, RawFileSettingStore, type RecentMentionEntry, RemoteSettingStore, SETTING_KEYS, SYNCABLE_KEYS, type SandboxConfig, type SanitizeResult, type Setting, type
|
|
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 };
|
|
2085
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/setting.ts","../src/raw-file-store.ts","../src/remote-setting-store.ts","../src/remote-sync-keys.ts","../src/settings-manager.ts","../src/claude-settings-adapter.ts","../src/project-trust.ts","../src/presets.ts","../src/config-merge-strategy.ts","../src/config-export-format.ts"],"mappings":"
|
|
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":";;;;;;;KAEY,WAAA;AAAA,UAEK,YAAA;EAAA,SACN,IAAA,EAAM,WAAA;EACf,IAAA,CAAK,IAAA,WAAe,OAAA;EACpB,KAAA,CAAM,IAAA,UAAc,MAAA,EAAQ,OAAA,CAAQ,OAAA,IAAW,OAAA;EAC/C,MAAA,CAAO,IAAA,WAAe,OAAA;AAAA;;;cCRX,YAAA;AAAA,KACD,UAAA,WAAqB,YAAA;AAAA,cAEpB,UAAA;AAAA,KACD,QAAA,WAAmB,UAAA;;;cCMzB,kBAAA,EAAkB,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;cA4BlB,oBAAA,EAAoB,CAAA,CAAA,SAAA;;;;;cAuDpB,wBAAA,EAAwB,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;cAsCxB,2BAAA,EAA2B,CAAA,CAAA,SAAA;;;;;;;;;cAM3B,mBAAA,EAAmB,CAAA,CAAA,SAAA;;;;;;;;;;;cAInB,mBAAA,EAAmB,CAAA,CAAA,SAAA;;;;;;;;;;;cA4InB,iBAAA,EAAiB,CAAA,CAAA,SAAA;;;;cAOjB,sBAAA,EAAsB,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;;;;;iBAmEZ,0BAAA,CAA2B,KAAA,YAAiB,wBAAA;;;;;;iBAuB5C,0BAAA,CACd,GAAA,sBACA,IAAA,IAAO,OAAA,oBACN,wBAAA;;;;;;;;;;;;cAyBG,0BAAA,EAA0B,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;cA4B1B,wBAAA,EAAwB,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;cA+DxB,oBAAA,EAAoB,CAAA,CAAA,OAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAQpB,4BAAA,EAA4B,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAyB5B,oBAAA,EAAoB,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAKpB,0BAAA,EAA0B,CAAA,CAAA,SAAA;;;;cAKnB,aAAA,EAAa,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAqLd,OAAA,GAAU,CAAA,CAAE,KAAA,QAAa,aAAA;AAAA,KAEzB,cAAA,GAAiB,CAAA,CAAE,KAAA,QAAa,oBAAA;AAAA,KAChC,6BAAA,GAAgC,CAAA,CAAE,KAAA,QAAa,4BAAA;AAAA,KAC/C,qBAAA,GAAwB,CAAA,CAAE,KAAA,QAAa,oBAAA;AAAA,KACvC,2BAAA,GAA8B,CAAA,CAAE,KAAA,QAAa,0BAAA;;cAE5C,iBAAA;AAAA,KAED,kBAAA,GAAqB,CAAA,CAAE,KAAA,QAAa,wBAAA;AAAA,KACpC,oBAAA,GAAuB,CAAA,CAAE,KAAA,QAAa,0BAAA;AAAA,KAEtC,YAAA,GAAe,CAAA,CAAE,KAAA,QAAa,kBAAA;AAAA,KAE9B,cAAA,GAAiB,CAAA,CAAE,KAAA,QAAa,oBAAA;AAAA,KAMhC,kBAAA,GAAqB,CAAA,CAAE,KAAA,QAAa,wBAAA;AAAA,KAEpC,qBAAA,GAAwB,CAAA,CAAE,KAAA,QAAa,2BAAA;AAAA,KACvC,aAAA,GAAgB,CAAA,CAAE,KAAA,QAAa,mBAAA;AAAA,KAC/B,aAAA,GAAgB,CAAA,CAAE,KAAA,QAAa,mBAAA;AAAA,KAO/B,WAAA,GAAc,CAAA,CAAE,KAAA,QAAa,iBAAA;AAAA,KAC7B,gBAAA,GAAmB,CAAA,CAAE,KAAA,QAAa,sBAAA;AAAA,KAClC,gBAAA,GAAmB,CAAA,CAAE,KAAA,QAAa,aAAA;AAAA,cAEjC,YAAA;AAAA,KACD,UAAA,SAAmB,OAAA;;;cC7sBlB,uBAAA,EAAyB,MAAA,SAAe,YAAA;AAAA,UAapC,cAAA;EACf,GAAA;EACA,OAAA;AAAA;AAAA,UAGe,kBAAA;EACf,KAAA,GAAQ,YAAA;EACR,KAAA;EHvDe;EGyDf,WAAA,GAAc,OAAA,CAAQ,OAAA;EHvDc;;;;EG4DpC,oBAAA;AAAA;AAAA,cAGW,cAAA,EAAgB,OAAA;;;UCjEZ,SAAA;EACf,OAAA;EACA,WAAA;EACA,OAAA,CAAQ,MAAA,EAAQ,MAAA,oBAA0B,MAAA;AAAA;AAAA,iBAG5B,OAAA,CACd,MAAA,EAAQ,MAAA,mBACR,UAAA,GAAY,SAAA;EAEZ,MAAA,EAAQ,MAAA;EACR,OAAA;AAAA;;;cCoOW,aAAA;EAAA,QACH,KAAA;cAEI,KAAA,GAAQ,YAAA;EAId,IAAA,CAAK,OAAA,GAAS,kBAAA,GAA0B,OAAA,CAAQ,OAAA;AAAA;AAAA,iBAsClC,WAAA,CAAY,OAAA,GAAS,kBAAA,GAA0B,OAAA,CAAQ,OAAA;;;;;;;;AL/R7E;;;;;AAEA;;;;;;cMkBa,mBAAA,YAA+B,YAAA;EAAA,SACjC,IAAA;EAEH,IAAA,CAAK,IAAA,WAAe,OAAA;EASpB,KAAA,CAAM,IAAA,UAAc,MAAA,EAAQ,OAAA,CAAQ,OAAA,IAAW,OAAA;EAS/C,MAAA,CAAO,IAAA,WAAe,OAAA;AAAA;;;;;;;;ANzC9B;;;;cOWa,kBAAA,YAA8B,YAAA;EAAA,SAChC,IAAA;EAAA,iBAEQ,OAAA;EAAA,iBACA,MAAA;EAAA,iBACA,OAAA;EAAA,iBACA,SAAA;EAAA,iBACA,SAAA;cAEL,OAAA;IPfmC,4BOiB7C,OAAA,UPhB2B;IOkB3B,OAAA,QAAe,OAAA;MAAU,KAAA;IAAA,IPpB3B;IOsBE,KAAA,SAAc,UAAA,CAAW,KAAA,EPtBP;IOwBlB,SAAA,UPvBI;IOyBJ,MAAA;EAAA;EPzBkB;;;;;;;;;;ACPtB;EMoDQ,IAAA,CAAK,KAAA,WAAgB,OAAA;;;;ANnD7B;;;;;AAEA;;;;;AACA;;EMiGQ,KAAA,CAAM,KAAA,UAAe,MAAA,EAAQ,OAAA,CAAQ,OAAA,IAAW,OAAA;EAgChD,MAAA,CAAO,KAAA,WAAgB,OAAA;AAAA;;;;KCtHnB,kBAAA;;KAGA,iBAAA;;KAGA,eAAA;ARnBZ;AAAA,UQsBiB,SAAA;;EAEf,KAAA,EAAO,kBAAA;ERxBc;EQ0BrB,WAAA;ERxB2B;EQ0B3B,MAAA,EAAQ,iBAAA;ERzBO;EQ2Bf,IAAA,EAAM,eAAA;AAAA;;;;;cAiBK,UAAA,EAAY,QAAA,CAAS,MAAA,CAAO,UAAA,EAAY,SAAA;;cAuFxC,aAAA,EAAe,WAAA,CAAY,UAAA;;cAO3B,aAAA,EAAe,WAAA,CAAY,UAAA;;cAO3B,iBAAA,EAAmB,WAAA;;cAOnB,oBAAA,EAAsB,WAAA,CAAY,UAAA,EAAY,mBAAA;;UAO1C,mBAAA;EACf,QAAA,EAAU,kBAAA;EACV,WAAA;AAAA;;;;;iBAOc,sCAAA,CAAA;;;;AR3KhB;;;iBSWgB,cAAA,CAAe,OAAA,EAAS,OAAA,CAAQ,OAAA,IAAW,OAAA,CAAQ,OAAA;;;UCFlD,eAAA;EACf,YAAA;EACA,iBAAA;EACA,SAAA,GAAY,OAAA,CAAQ,OAAA;EVZV;EUcV,KAAA,GAAQ,YAAA;;;;AVZV;;EUkBE,WAAA,GAAc,YAAA;EVjBC;;;;;;;EUyBf,oBAAA;AAAA;AAAA,UAGQ,cAAA;EACR,MAAA,GAAS,MAAA,EAAQ,OAAA;EV5BZ;;;;;;;;;;EUuCL,oBAAA,GAAuB,OAAA;IAAW,EAAA;IAAsB,KAAA;EAAA;AAAA;AAAA,cAG7C,eAAA,SAAwB,iBAAA,CAAkB,cAAA;EAAA,iBACpC,KAAA;EAAA,iBACA,YAAA;;;;ATjDnB;mBSsDmB,oBAAA;EAAA,QACT,SAAA;EAAA,QAEA,KAAA;EAAA,QACA,WAAA;EAAA,QACA,OAAA;ETzD6E;;;;AACvF;EADuF,QS+D7E,UAAA;cAEI,OAAA,GAAS,eAAA;EAUrB,GAAA,iBAAoB,OAAA,CAAA,CAAS,GAAA,EAAK,CAAA,GAAI,OAAA,CAAQ,CAAA;ET1EP;;;;ACDkB;EDClB,ISmFnC,MAAA,CAAA,GAAU,QAAA,CAAS,OAAA;EAAA,IAInB,KAAA,CAAA,GAAS,OAAA;EAIP,IAAA,CAAA,GAAQ,OAAA,CAAQ,OAAA;EAIhB,MAAA,CAAO,SAAA,GAAY,OAAA,CAAQ,OAAA,IAAW,OAAA,CAAQ,OAAA;EAqC9C,MAAA,CAAO,SAAA,EAAW,OAAA,CAAQ,OAAA,IAAW,OAAA,CAAQ,OAAA;;;;;;;;;;;;;EAqB7C,OAAA,CAAQ,OAAA,EAAS,OAAA,CAAQ,OAAA,GAAU,MAAA,0BAAgC,OAAA,CAAQ,OAAA;;;;;UA0CzE,YAAA;;;;;;UAcA,2BAAA;EASR,OAAA,CAAA;AAAA;;;;iBCtMc,kBAAA,CAAmB,IAAA;EAAiB,IAAA;EAAc,SAAA;AAAA;;iBAOlD,iBAAA,CAAkB,IAAA;;iBAsElB,uBAAA,CAAwB,GAAA;EACtC,OAAA,EAAS,OAAA,CAAQ,OAAA;EACjB,QAAA;AAAA;;;;;;iBAoCc,uBAAA,CAAwB,YAAA,YAAwB,OAAA,CAAQ,OAAA;;;;;;;;;AXzIxE;;;;;AAEA;;;;;;;;;;;;;;;;;cY2Ba,sBAAA;AAAA,UASI,cAAA;EZjCqB;EYmCpC,KAAA,EAAO,OAAA,CAAQ,OAAA;EZnCgC;EYqC/C,QAAA;AAAA;;;;;iBAOc,6BAAA,CAA8B,KAAA,EAAO,OAAA,CAAQ,OAAA,IAAW,cAAA;;;;;;;iBCjBxD,oBAAA,CAAA;;iBAKA,gBAAA,CACd,YAAA,UACA,SAAA;AbvCF;AAAA,iBa6CgB,YAAA,CAAa,YAAA,UAAsB,SAAA;;iBAOnC,cAAA,CAAe,YAAA,UAAsB,SAAA;;AblDrD;;;;;iBagEgB,4BAAA,CAAA;AAAA,UAIC,yBAAA;EbjEgC;EamE/C,WAAA;AAAA;;;;;iBAOc,2BAAA,CACd,YAAA,UACA,GAAA,GAAK,yBAAA;AAAA,UAOU,mBAAA;EbnFf;EaqFA,YAAA;EbrF4B;EauF5B,KAAA;AAAA;;;;;iBAOc,yBAAA,CACd,YAAA,UACA,GAAA,GAAK,yBAAA,GACJ,mBAAA;;KAiBS,oBAAA;AAAA,UAEK,4BAAA;;EAEf,YAAA;EZ7HkE;EY+HlE,IAAA;EZ/HkE;EYiIlE,MAAA;AAAA;;;;;AZ9HF;iBYqJgB,4BAAA,CACd,YAAA,UACA,GAAA,GAAK,yBAAA,GACJ,4BAAA;;;;;;;;;AbzJH;ccOa,sBAAA,EAAwB,YAAA;;;;;;;KCGzB,aAAA,GAAgB,kBAAA;;UAWX,SAAA;EACf,GAAA,EAAK,UAAA;EACL,IAAA;EACA,EAAA;EACA,QAAA,EAAU,aAAA;EACV,WAAA;EACA,IAAA;AAAA;;;;;;;;iBAwGc,UAAA,CAAW,OAAA,EAAS,OAAA,EAAS,QAAA,EAAU,OAAA,CAAQ,OAAA,IAAW,SAAA;;UA2BzD,UAAA;EfzJf;Ee2JA,SAAA,EAAW,oBAAA;Ef3JiB;Ee6J5B,SAAA,EAAW,oBAAA;Ef7JS;Ee+JpB,eAAA,EAAiB,KAAA;IAAQ,EAAA;IAAY,KAAA;IAAe,QAAA;EAAA;AAAA;;;;AdtKtD;;iBc8KgB,qBAAA,CACd,OAAA,WAAkB,oBAAA,IAClB,QAAA,WAAmB,oBAAA,KAClB,UAAA;;;AdhLH;;;;;AAEA;;;;;AACA;;iBciNgB,2BAAA,CAA4B,KAAA,WAAgB,SAAA,IAAa,UAAA,EAAY,UAAA;;;;;;AblN1B;;;;;;;;;;iBawO3C,kBAAA,CACd,KAAA,WAAgB,SAAA,IAChB,MAAA,wBACC,OAAA,CAAQ,OAAA;;;;cChOE,sBAAA;AAAA,UAEI,gBAAA;EACf,mBAAA;EACA,WAAA;;EAEA,aAAA;EhBlBqB;EgBoBrB,KAAA;EACA,MAAA,EAAQ,OAAA,CAAQ,OAAA;EhBrBK;AAEvB;;;EgBwBE,gBAAA,GAAmB,KAAA;IAAQ,IAAA;IAAgB,GAAA;EAAA;AAAA;;;;;;;;;;;iBAa7B,kBAAA,CACd,MAAA,EAAQ,OAAA,EACR,KAAA,wBACA,QAAA,UACA,GAAA,kBACC,gBAAA;AAAA,cAkCU,sBAAA,SAA+B,KAAA;EAAA,SAExB,WAAA;EAAA,SACA,gBAAA;cADA,WAAA,UACA,gBAAA;AAAA;AAAA,cAUP,oBAAA,SAA6B,KAAA;cAC5B,MAAA;AAAA;;;;Af9Fd;iBe4GgB,eAAA,CAAgB,GAAA,WAAc,gBAAA"}
|
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 ee,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 ae}from"node:os";import{existsSync as oe,readFileSync as se}from"node:fs";import{HIGH_RISK_CAPABILITIES as ce}from"@x-otto/plugin";const m=[`minimal`,`standard`,`full`],h=[`trace`,`debug`,`info`,`warn`,`error`,`fatal`],g=e=>e.optional().catch(void 0),_=t.enum(n),le=t.object({model:g(t.string()),description:g(t.string()),system_prompt:g(t.string()),tools:g(t.array(t.string())),capabilities:g(t.array(t.string())),categories:g(t.array(t.string())),default_workflow_slot:g(_),max_tool_turns:g(t.number()),max_tool_turn_extensions:g(t.number()),prompt_output_token_budget:g(t.number()),prompt_wall_clock_budget_ms:g(t.number()),session_cost_budget_usd:g(t.number()),stall_detection:g(t.union([t.literal(!1),t.object({window_turns:g(t.number()),repeat_threshold:g(t.number())})])),temperature:g(t.number()),max_tokens:g(t.number()),disabled:g(t.boolean())}),ue=t.object({preferred_models:g(t.array(t.string())),default_model:g(t.string())}),de=t.object({enabled:g(t.boolean()),every_n_turns:g(t.number()),max_lessons:g(t.number()),similarity_threshold:g(t.number())}),fe=t.object({enabled:g(t.boolean()),min_messages:g(t.number()),dismiss_cooldown_days:g(t.number())}),pe=t.object({slots:g(t.partialRecord(_,t.union([t.string(),t.array(t.string())]))),default:g(t.string()),subagent_default_slot:g(_)}),me=t.object({enabled:g(t.boolean()),sound:g(t.boolean()),on_completion:g(t.boolean()),on_error:g(t.boolean()),on_idle:g(t.boolean()),channel:g(t.enum([`auto`,`terminal_bell`,`iterm2`,`iterm2_with_bell`,`kitty`,`ghostty`,`disabled`])),condition:g(t.enum([`unfocused`,`always`])),idle_threshold_ms:g(t.number()),command:g(t.array(t.string()))}),he=t.object({enabled:g(t.boolean()),probability:g(t.number()),min_turn_gap:g(t.number()),sink:g(t.string())}),ge=t.object({id:g(t.string()),path:g(t.string()),enabled:g(t.boolean())}),_e=t.object({enabled:g(t.boolean()),maxEntries:g(t.number()),sources:g(t.array(ge))}),ve=t.object({external:g(_e)}),ye=t.object({enabled:t.boolean(),network:g(t.enum([`allow`,`deny`])),writablePaths:g(t.array(t.string())),protectCredentials:g(t.boolean()),autoAllowBashIfSandboxed:g(t.boolean())}),be=t.object({total_bytes:g(t.number()),min_per_session_bytes:g(t.number()),max_per_session_bytes:g(t.number()),max_history_messages:g(t.number()),resume_window_messages:g(t.number()),max_content_bytes:g(t.number()),warn_threshold_pct:g(t.number()),hard_threshold_pct:g(t.number()),compaction_timeout_ms:g(t.number()),coverage_warn_threshold:g(t.number()),coverage_error_threshold:g(t.number()),coverage_floor_threshold:g(t.number())});t.object({tool_preset:g(t.enum(m)),disabled_tools:g(t.array(t.string()))});const xe=t.object({enabled:g(t.boolean())}),Se=t.object({enabled:g(t.boolean()),maxAttempts:g(t.number())}),Ce=t.object({enabled:g(t.boolean()),failureThreshold:g(t.number()),timeoutMs:g(t.number())}),we=t.object({enabled:g(t.boolean())}),Te=t.object({enabled:g(t.boolean()),review:g(xe),retry:g(Se),circuitBreaker:g(Ce),routing:g(we)}),Ee=t.object({name:t.string(),effect:t.enum([`allow`,`deny`,`ask`]),tools:g(t.array(t.string())),paths:g(t.array(t.string())),deny_reason:g(t.string()),ask_prompt:g(t.string())}),De=t.object({name:t.string(),priority:g(t.number()),enabled:g(t.boolean()),scope:g(t.object({agents:g(t.array(t.string())),sessions:g(t.array(t.string()))})),rules:t.array(Ee)}),Oe=t.object({url:g(t.string()),enabled:g(t.boolean())}),ke=t.object({command:g(t.string()),args:g(t.array(t.string())),env:g(t.record(t.string(),t.string())),url:g(t.string()),type:g(t.enum([`stdio`,`sse`,`http`])),headers:g(t.record(t.string(),t.string())),requestTimeoutMs:g(t.number()),autoReconnect:g(t.boolean()),disabled:g(t.boolean())}),Ae=t.object({maxRetries:g(t.number().int().positive()),initialDelayMs:g(t.number().positive()),backoffFactor:g(t.number().positive()),maxDelayMs:g(t.number().positive())}),je=t.object({maxRetries:g(t.number().int().positive()),intervalMs:g(t.number().positive())}),Me=t.object({maxReconnectAttempts:g(t.number().int().nonnegative()),reconnectDelayMs:g(t.number().positive()),requestTimeoutMs:g(t.number().positive())}),Ne=t.object({retryBackoffMs:g(t.number().nonnegative())}),Pe=t.object({maxAttempts:g(t.number().int().positive()),intervalMs:g(t.number().positive())}),Fe=t.object({refreshLockTimeoutMs:g(t.number().min(1e3))}),Ie=t.object({stream:g(Ae),networkDisconnect:g(je),mcp:g(Me),schedule:g(Ne),errorRetry:g(Pe),oauth:g(Fe)}),Le=t.object({id:t.string(),source:t.enum([`npm`,`dir`,`url`,`git`]),sourceSpec:g(t.string()),installedAt:t.number(),version:g(t.string()),scope:t.enum([`global`,`project`,`repository`]),capabilities:g(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:g(t.string())}),ze=t.object({name:t.string().min(1).max(64),text:t.string().min(1).max(4e3)}),v=`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(`.`),y=t.enum(v),Be=t.strictObject({heading1:g(y),heading2:g(y),headingWeak:g(y),listMarker:g(y),listMarkerMuted:g(y),quoteBar:g(y),quoteText:g(y),link:g(y),inlineCode:g(y),codeFence:g(y),tableHeader:g(y),tableDivider:g(y),listIndent:g(t.union([t.literal(2),t.literal(3)])),listMarkers:g(t.tuple([t.string().min(1),t.string().min(1),t.string().min(1),t.string().min(1)])),codeBlockDivider:g(t.enum([`hr`,`none`]))}),Ve=t.strictObject({markdown:g(Be)}),He=t.strictObject({dark:g(t.string().min(1).max(128)),light:g(t.string().min(1).max(128))}),b=t.object({config_version:g(t.string()),version:g(t.string()),log_level:g(t.enum(h)),model:g(t.string()),model_fallback:g(t.array(t.string())),recent_models:g(t.array(t.string())),recent_mentions:g(t.array(Re)),shortcuts:g(t.array(ze)),feedback_repo:g(t.string()),keybinding_overrides:g(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:g(t.string()),theme:g(t.enum([`dark`,`light`,`system`])),theme_overrides:g(Ve),theme_preset:g(He),nickname:g(t.string().max(64)),has_completed_onboarding:g(t.boolean()),has_completed_wizard:g(t.boolean()),wizard_dont_show_again:g(t.boolean()),first_prompt_submitted_at:g(t.number()),thinking_level:g(t.string()),agents:g(t.record(t.string(),le)),categories:g(t.record(t.string(),ue)),tool_preset:g(t.enum(m)),memory_auto_extract:g(de),skill_loop:g(fe),notification:g(me),pulse_survey:g(he),history:g(ve),workflow:g(Te),model_slots:g(pe),mcp_servers:g(t.record(t.string(),ke)),resilience:g(Ie),sandbox:g(ye),residency:g(be),disabled_agents:g(t.array(t.string())),disabled_hooks:g(t.array(t.string())),disabled_tools:g(t.array(t.string())),disabled_skills:g(t.array(t.string())),disabled_plugins:g(t.array(t.string())),installed_plugins:g(t.array(Le)),auto_update_check:g(t.boolean()),last_update_check:g(t.number()),prompt_output_token_budget:g(t.number()),prompt_wall_clock_budget_ms:g(t.number()),session_cost_budget_usd:g(t.number()),stall_detection:g(t.union([t.literal(!1),t.object({window_turns:g(t.number()),repeat_threshold:g(t.number())})])),todo_continue_max:g(t.number()),permission_mode:g(t.enum(e)),permissions:g(t.array(De)),permission_always_allow:g(t.array(t.string())),plugin_lifecycle:g(t.object({setupDone:g(t.array(t.object({id:t.string(),version:g(t.string())})))})),proxy:g(Oe),web_fetch_allowed_hosts:g(t.array(t.string())),_migrations:g(t.array(t.string()))}),Ue=v,x=b.keyof().options,S={"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:{...S},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 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}`),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 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`),Ge=new Set(h),Ke=[`mcp`,`skills`,`disabled_mcps`,`disabled_skills`],qe=[`theme_file`,`theme_spacing`,`theme_border`,`theme_figures`],Je=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 k(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)&&k(a,e,[...n,i],r)}}function A(e,t){let n={...e};for(let[e,r]of Object.entries(t))r!==void 0&&(O(r)&&O(n[e])?n[e]=A(n[e],r):n[e]=r);return n}function j(...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 O(t)&&O(i[e])?i[e]=A(i[e],t):i[e]=t}return r.length>0?j(i,...r):i}function Ye(){let e={},t=process.env.OTTO_MODEL;t&&(e.model=t);let n=process.env.OTTO_LOG_LEVEL;return n&&Ge.has(n)&&(e.log_level=n),e}function Xe(e){let t=[],n={...e};for(let e of Ke)n[e]!==void 0&&(t.push({key:e,message:`Removed legacy config key is ignored`}),delete n[e]);for(let e of qe)n[e]!==void 0&&(t.push({key:e,message:`RFC-205 legacy theme override key is ignored`}),delete n[e]);if(O(n.theme_overrides)){let e=Object.keys(n.theme_overrides).filter(e=>Je.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=b.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 x){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}O(r)&&O(i)&&k(r,i,[e],t)}return{value:a,warnings:t}}async function Ze(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 M=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 Ze(t,n,i):{layers:[],stripped:[]};for(let e of o)D.warn({key:e,path:i},`未受信任项目配置剔除高危键(permission_mode/mcp_servers 等需先信任该目录)`);let s=Ye(),{config:c,applied:l}=w(j(C,...r,...a,s));l.length>0&&D.info({applied:l},`Config migrations applied`);let{value:u,warnings:d}=Xe(c);for(let e of d)D.warn({key:e.key,message:e.message},`Config validation issue`);return{...C,...u}}};async function N(e={}){return new M(e.store).load(e)}var P=class{type=`file`;async read(e){let{readFile:t}=await import(`node:fs/promises`);try{return await t(await F(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 F(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 F(e)),!0}catch{return!1}}};async function F(e){if(!e.startsWith(`~/`))return e;let{homedir:t}=await import(`node:os`);return`${t()}${e.slice(1)}`}const I=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`]),L=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`,`workflow`,`categories`,`agents`,`disabled_plugins`,`installed_plugins`]);function R(e){let t={};for(let n of Object.keys(e))I.has(n)&&!L.has(n)&&(t[n]=e[n]);return t}var Qe=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=R(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 z=a(`@x-otto/setting:claude-adapter`),$e=new Set([`Read`,`Write`,`Edit`,`MultiEdit`,`Glob`,`Grep`]),B={default:`confirm`,acceptEdits:`auto`,bypassPermissions:`bypass`,plan:`readonly`};function V(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 H(e){let t=e.startsWith(`~/`)?f(ae(),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 et(e,t,n,r){let{tool:i,specifier:a}=V(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($e.has(i)){n.rules.push({name:`claude-${r}`,effect:t,tools:[s],paths:[H(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 U(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`&&et(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&&B[a]&&(i.permission_mode=B[a],a===`plan`&&t.warnings.push(`defaultMode=plan otto 无对应 → 降 readonly`)),{setting:i,warnings:t.warnings}}function W(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(!oe(e))continue;let t;try{t=s(se(e,`utf-8`))}catch(t){z.warn({file:e,err:t},`Failed to parse .claude settings, skipped`);continue}let{setting:o,warnings:c}=U(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){z.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)z.warn({rule:e},`.claude settings 翻译降级`)}return o}var tt=class extends i{paths;workspaceDir;projectConfigTrusted;overrides;store;remoteStore;setting={};writeChain=Promise.resolve();constructor(e={}){super(),this.paths=nt(e),this.workspaceDir=e.workspaceDir,this.projectConfigTrusted=e.projectConfigTrusted??!0,this.overrides={...e.overrides},this.store=e.store??new P,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 N({store:this.store,paths:this.paths,untrustedProjectPath:this.resolveUntrustedProjectPath(),extraLayers:[W(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 nt(e){let t;if(e.projectConfigPath?t=e.projectConfigPath:e.workspaceDir&&(t=p(e.workspaceDir,te)),!t)return[];let n=p(ee,d);return p(t)===n?[t]:[n,t]}const rt=[],G=new Set([`config_version`,`version`,`last_update_check`,`_migrations`]),K=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}],[`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 it(){let e=x.filter(e=>!G.has(e)&&!K.has(e));if(e.length>0)throw Error(`ConfigMergeStrategy 字段分类表遗漏以下 SettingKey(需在 FIELD_CLASSIFICATION 或 EXCLUDED_KEYS 中归类): ${e.join(`, `)}`)}const at=new Set(T);function q(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function J(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 Y={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`?Y[e]:void 0,r=Y[t];return n===void 0||r===void 0?!0:r<n}function st(e,t){let n=q(e)?e.enabled:void 0,r=q(t)?t.enabled:void 0;return n===!0&&r===!1}function X(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 ct(e,t,n){if(!at.has(e))return`safe`;if(e===`permission_mode`)return ot(t,n)?`sensitive`:`safe`;if(e===`sandbox`)return st(t,n)?`sensitive`:`safe`;if(e===`mcp_servers`){let e=q(t)?t:{},r=q(n)?n:{};for(let[t,n]of Object.entries(r))if(!(t in e)||!J(e[t],n))return`sensitive`;return`safe`}if(e===`disabled_hooks`)return J(t,n)?`safe`:`sensitive`;if(e===`permissions`||e===`permission_always_allow`)return X(t,n).length>0?`sensitive`:`safe`;throw Error(`classifyRisk: 字段 "${e}" 在 HIGH_RISK_PROJECT_KEYS 中但未被显式处理——新增高危字段必须同步补充判定分支,不允许 fail-open`)}function lt(e,t){let n=[];for(let r of x){if(G.has(r)||r===`installed_plugins`||!(r in t))continue;let i=e[r],a=t[r];if(J(i,a))continue;let o=K.get(r);o&&n.push({key:r,from:i,to:a,category:o.category,deviceBound:o.deviceBound,risk:ct(r,i,a)})}return n}function ut(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 dt(e,t){return e.some(e=>e.risk===`sensitive`)?!0:t.toInstall.some(e=>(e.capabilities??[]).some(e=>ce.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===`record-merge`){let e=q(r.from)?r.from:{},t=q(r.to)?r.to:{};n[r.key]={...e,...t}}else n[r.key]=r.to;return n}const pt=1;function mt(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&&ie(e.sourceSpec)?{...e,sourceSpec:``}:e);continue}i[t]=n}return{otto_config_version:1,exported_at:r(),exported_from:n,scope:t,fields:i}}var Z=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 Z(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}}export{rt as BUILTIN_AGENT_PROFILES,S as BUILTIN_REVIEWER_AGENTS,pt as CURRENT_EXPORT_VERSION,C as DEFAULT_CONFIG,G as EXCLUDED_KEYS,Q as ExportFileParseError,Z as ExportFileVersionError,K as FIELD_CLASSIFICATION,T as HIGH_RISK_PROJECT_KEYS,h as LOG_LEVELS,L as NEVER_SYNCED_KEYS,e as PERMISSION_MODES,P as RawFileSettingStore,Qe as RemoteSettingStore,x as SETTING_KEYS,I as SYNCABLE_KEYS,M as SettingLoader,tt as SettingsManager,Ue as THEME_COLOR_ROLES,m as TOOL_PRESETS,ft as applyMergeStrategy,it as assertFieldClassificationCoversAllKeys,mt as buildExportPayload,ut as classifyPluginChanges,lt as diffConfig,R as filterSyncable,H as globToRegexSource,dt as hasSecuritySensitiveChanges,W as loadClaudeSettingsLayer,N as loadSetting,w as migrate,ht as parseExportFile,V as parseToolSpecifier,E as sanitizeUntrustedProjectLayer,U 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
|