@armadra/agent 0.2.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +99 -0
- package/README.md +142 -49
- package/dist/agent/retry.d.ts +1 -1
- package/dist/agent/retry.js +2 -1
- package/dist/agent/session-cache.js +6 -3
- package/dist/agent/session-classifier.d.ts +18 -0
- package/dist/agent/session-classifier.js +103 -0
- package/dist/agent/session-core.d.ts +13 -0
- package/dist/agent/session-state.js +2 -1
- package/dist/agent/session-sync.js +9 -2
- package/dist/agent/session-tools.js +24 -3
- package/dist/agent/session.d.ts +3 -0
- package/dist/agent/session.js +20 -10
- package/dist/agent/tool-runner.js +15 -2
- package/dist/agent/types.d.ts +9 -1
- package/dist/ai/apis/anthropic-messages.js +3 -2
- package/dist/ai/apis/google-generative-ai.js +3 -2
- package/dist/ai/apis/openai-completions.js +3 -2
- package/dist/ai/apis/openai-responses.js +5 -3
- package/dist/ai/cache/fingerprint.d.ts +1 -1
- package/dist/ai/cache/fingerprint.js +1 -1
- package/dist/ai/cache/reporting.js +2 -1
- package/dist/ai/http.d.ts +28 -6
- package/dist/ai/http.js +41 -8
- package/dist/ai/providers/channels.d.ts +51 -0
- package/dist/ai/providers/channels.js +96 -0
- package/dist/ai/providers/enrich.d.ts +34 -0
- package/dist/ai/providers/enrich.js +86 -0
- package/dist/ai/providers/models-dev-cache.d.ts +53 -0
- package/dist/ai/providers/models-dev-cache.js +147 -0
- package/dist/ai/providers/models-dev.d.ts +99 -0
- package/dist/ai/providers/models-dev.js +315 -0
- package/dist/ai/providers/registry.d.ts +42 -4
- package/dist/ai/providers/registry.js +195 -34
- package/dist/ai/providers/suggest.d.ts +18 -0
- package/dist/ai/providers/suggest.js +72 -0
- package/dist/ai/sse.d.ts +6 -2
- package/dist/ai/sse.js +22 -2
- package/dist/ai/types.d.ts +30 -3
- package/dist/bundle/ama.cjs +17893 -10698
- package/dist/cli/args.d.ts +23 -5
- package/dist/cli/args.js +113 -13
- package/dist/cli/bootstrap.js +28 -3
- package/dist/cli/codemode-notice.d.ts +20 -0
- package/dist/cli/codemode-notice.js +55 -0
- package/dist/cli/compose-providers.js +3 -0
- package/dist/cli/compose-session.d.ts +8 -0
- package/dist/cli/compose-session.js +32 -1
- package/dist/cli/compose-store.d.ts +1 -1
- package/dist/cli/compose-store.js +3 -1
- package/dist/cli/compose.d.ts +17 -5
- package/dist/cli/compose.js +53 -19
- package/dist/cli/default-model.d.ts +38 -1
- package/dist/cli/default-model.js +96 -9
- package/dist/cli/deps.d.ts +38 -0
- package/dist/cli/exit-codes.d.ts +2 -0
- package/dist/cli/exit-codes.js +3 -0
- package/dist/cli/fake-visibility.d.ts +13 -0
- package/dist/cli/fake-visibility.js +26 -0
- package/dist/cli/from-prompt.d.ts +18 -0
- package/dist/cli/from-prompt.js +49 -0
- package/dist/cli/main.d.ts +10 -2
- package/dist/cli/main.js +88 -2
- package/dist/cli/proxy.d.ts +51 -0
- package/dist/cli/proxy.js +135 -0
- package/dist/cli/startup-screen.d.ts +29 -0
- package/dist/cli/startup-screen.js +47 -0
- package/dist/cli/startup-steps.d.ts +1 -1
- package/dist/cli/startup-steps.js +20 -13
- package/dist/cli/subcommands/config.d.ts +29 -4
- package/dist/cli/subcommands/config.js +188 -25
- package/dist/cli/subcommands/context.js +5 -1
- package/dist/cli/subcommands/doctor.js +12 -1
- package/dist/cli/subcommands/init.d.ts +6 -0
- package/dist/cli/subcommands/init.js +23 -0
- package/dist/cli/subcommands/model-meta.d.ts +16 -0
- package/dist/cli/subcommands/model-meta.js +54 -0
- package/dist/cli/subcommands/models-cache-probe.js +1 -1
- package/dist/cli/subcommands/models-discover.d.ts +18 -6
- package/dist/cli/subcommands/models-discover.js +87 -52
- package/dist/cli/subcommands/models.js +26 -18
- package/dist/cli/subcommands/probe-runner.d.ts +96 -0
- package/dist/cli/subcommands/probe-runner.js +264 -0
- package/dist/cli/subcommands/providers-list.d.ts +8 -0
- package/dist/cli/subcommands/providers-list.js +97 -0
- package/dist/cli/subcommands/providers-plan.d.ts +79 -0
- package/dist/cli/subcommands/providers-plan.js +215 -0
- package/dist/cli/subcommands/providers-probe.d.ts +34 -0
- package/dist/cli/subcommands/providers-probe.js +87 -0
- package/dist/cli/subcommands/providers.d.ts +28 -0
- package/dist/cli/subcommands/providers.js +436 -0
- package/dist/cli/subcommands/sessions-export.d.ts +10 -0
- package/dist/cli/subcommands/sessions-export.js +59 -0
- package/dist/cli/subcommands/sessions-search.d.ts +13 -0
- package/dist/cli/subcommands/sessions-search.js +103 -0
- package/dist/cli/subcommands/sessions.d.ts +3 -2
- package/dist/cli/subcommands/sessions.js +22 -1
- package/dist/cli/subcommands/stats.d.ts +17 -0
- package/dist/cli/subcommands/stats.js +198 -0
- package/dist/cli/system-prompt-arg.d.ts +11 -0
- package/dist/cli/system-prompt-arg.js +34 -0
- package/dist/codemode/modes.d.ts +4 -11
- package/dist/codemode/modes.js +5 -27
- package/dist/codemode/tool.d.ts +18 -14
- package/dist/codemode/tool.js +71 -28
- package/dist/config/init.d.ts +34 -0
- package/dist/config/init.js +99 -0
- package/dist/config/json-schema.d.ts +15 -0
- package/dist/config/json-schema.js +215 -0
- package/dist/config/key-docs.d.ts +22 -0
- package/dist/config/key-docs.js +105 -0
- package/dist/config/merge.d.ts +9 -8
- package/dist/config/merge.js +22 -7
- package/dist/config/schema.d.ts +1 -1
- package/dist/config/schema.js +97 -15
- package/dist/config/types.d.ts +57 -11
- package/dist/config/types.js +19 -1
- package/dist/modes/commands-core.js +6 -5
- package/dist/modes/image-input.d.ts +27 -0
- package/dist/modes/image-input.js +78 -0
- package/dist/modes/interactive/approval-dialog.d.ts +21 -5
- package/dist/modes/interactive/approval-dialog.js +106 -27
- package/dist/modes/interactive/commands.d.ts +11 -2
- package/dist/modes/interactive/commands.js +58 -19
- package/dist/modes/interactive/interactive-mode.d.ts +2 -1
- package/dist/modes/interactive/interactive-mode.js +70 -82
- package/dist/modes/interactive/key-dispatch.d.ts +3 -1
- package/dist/modes/interactive/key-dispatch.js +5 -6
- package/dist/modes/interactive/line/line-mode.d.ts +1 -0
- package/dist/modes/interactive/line/line-mode.js +9 -5
- package/dist/modes/interactive/line/line-render.d.ts +4 -0
- package/dist/modes/interactive/line/line-render.js +28 -6
- package/dist/modes/interactive/message-view.d.ts +48 -9
- package/dist/modes/interactive/message-view.js +238 -44
- package/dist/modes/interactive/panels.d.ts +18 -0
- package/dist/modes/interactive/panels.js +143 -0
- package/dist/modes/interactive/pickers.d.ts +23 -2
- package/dist/modes/interactive/pickers.js +48 -15
- package/dist/modes/interactive/run-indicator.d.ts +51 -0
- package/dist/modes/interactive/run-indicator.js +189 -0
- package/dist/modes/interactive/startup-header.d.ts +40 -0
- package/dist/modes/interactive/startup-header.js +169 -0
- package/dist/modes/interactive/startup-ui.d.ts +7 -2
- package/dist/modes/interactive/startup-ui.js +41 -11
- package/dist/modes/interactive/status-bar.d.ts +18 -15
- package/dist/modes/interactive/status-bar.js +98 -56
- package/dist/modes/interactive/tool-summary.d.ts +46 -0
- package/dist/modes/interactive/tool-summary.js +218 -0
- package/dist/modes/interactive/tool-view.d.ts +48 -15
- package/dist/modes/interactive/tool-view.js +203 -145
- package/dist/modes/print/print-mode.d.ts +33 -4
- package/dist/modes/print/print-mode.js +128 -8
- package/dist/modes/rpc/commands.js +12 -2
- package/dist/permissions/auto-safe.d.ts +60 -0
- package/dist/permissions/auto-safe.js +529 -0
- package/dist/permissions/classifier.d.ts +64 -0
- package/dist/permissions/classifier.js +184 -0
- package/dist/permissions/dangerous.d.ts +5 -0
- package/dist/permissions/dangerous.js +1 -1
- package/dist/permissions/modes.d.ts +30 -0
- package/dist/permissions/modes.js +78 -0
- package/dist/permissions/pipeline.d.ts +31 -4
- package/dist/permissions/pipeline.js +196 -6
- package/dist/permissions/protected.d.ts +19 -0
- package/dist/permissions/protected.js +74 -0
- package/dist/permissions/rules.js +3 -0
- package/dist/permissions/types.d.ts +50 -3
- package/dist/permissions/types.js +3 -0
- package/dist/rpc.d.ts +2 -0
- package/dist/sdk.d.ts +9 -3
- package/dist/sdk.js +10 -2
- package/dist/session/export.d.ts +32 -0
- package/dist/session/export.js +187 -0
- package/dist/session/projection.js +5 -1
- package/dist/session/redact.d.ts +15 -0
- package/dist/session/redact.js +55 -0
- package/dist/session/reuse.d.ts +33 -0
- package/dist/session/reuse.js +86 -0
- package/dist/session/scan.d.ts +34 -0
- package/dist/session/scan.js +140 -0
- package/dist/session/search.d.ts +52 -0
- package/dist/session/search.js +211 -0
- package/dist/session/stats-aggregate.d.ts +63 -0
- package/dist/session/stats-aggregate.js +163 -0
- package/dist/session/stats-index.d.ts +26 -0
- package/dist/session/stats-index.js +91 -0
- package/dist/session/stats-scan.d.ts +54 -0
- package/dist/session/stats-scan.js +236 -0
- package/dist/session/types.d.ts +2 -0
- package/dist/tools/image-file.d.ts +31 -0
- package/dist/tools/image-file.js +114 -0
- package/dist/tools/presets.d.ts +35 -8
- package/dist/tools/presets.js +56 -17
- package/dist/tools/read.d.ts +4 -8
- package/dist/tools/read.js +15 -64
- package/dist/tui/component.d.ts +8 -2
- package/dist/tui/component.js +3 -1
- package/dist/tui/components/box.d.ts +6 -1
- package/dist/tui/components/box.js +16 -6
- package/dist/tui/components/card.d.ts +23 -0
- package/dist/tui/components/card.js +37 -0
- package/dist/tui/components/editor-history.d.ts +6 -0
- package/dist/tui/components/editor-history.js +45 -0
- package/dist/tui/components/editor-paste.d.ts +1 -1
- package/dist/tui/components/editor-paste.js +4 -4
- package/dist/tui/components/editor.d.ts +11 -5
- package/dist/tui/components/editor.js +52 -58
- package/dist/tui/components/key-value.d.ts +3 -0
- package/dist/tui/components/key-value.js +16 -6
- package/dist/tui/components/loader.d.ts +37 -7
- package/dist/tui/components/loader.js +84 -21
- package/dist/tui/components/markdown.d.ts +5 -1
- package/dist/tui/components/markdown.js +45 -21
- package/dist/tui/components/meter.d.ts +3 -3
- package/dist/tui/components/meter.js +13 -11
- package/dist/tui/components/select-list.d.ts +23 -1
- package/dist/tui/components/select-list.js +76 -13
- package/dist/tui/glyphs.d.ts +68 -0
- package/dist/tui/glyphs.js +114 -0
- package/dist/tui/theme.d.ts +30 -6
- package/dist/tui/theme.js +103 -17
- package/dist/tui.d.ts +4 -2
- package/dist/tui.js +3 -1
- package/docs/codemode.md +23 -9
- package/docs/hooks.md +10 -10
- package/docs/permissions.md +148 -0
- package/docs/providers.md +241 -3
- package/docs/rpc.md +38 -38
- package/docs/session-format.md +3 -2
- package/docs/sessions.md +134 -0
- package/docs/tui.md +140 -61
- package/package.json +3 -1
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 配置目录初始化(docs/providers.md「配置目录」):`ama init` 与 CLI 首次运行的自动初始化。
|
|
3
|
+
*
|
|
4
|
+
* - 目录 0700;`config.json` 只在不存在时写最小内容(`--force` 时先备份再重写):只有 `$schema`、
|
|
5
|
+
* `version` 与空 `providers`,不写死任何缺省值——缺省值以后调整(例如 codemode 跟随预设)对老用户
|
|
6
|
+
* 同样生效,`config show` 的来源也显示 default 而不是 user;
|
|
7
|
+
* - `config.schema.json` 不是用户文件,每次 init 都按当前版本重写(内容相同则不动);
|
|
8
|
+
* - 不创建 auth.json(只有 `ama auth set` / `ama providers add` 才写,0600)。
|
|
9
|
+
* - 自动初始化只在 CLI 里、配置目录不存在时触发,`AMA_NO_INIT=1` 关闭;SDK 与测试不走这里。
|
|
10
|
+
*/
|
|
11
|
+
import { type AmaConfig } from "./types.js";
|
|
12
|
+
export declare const NO_INIT_ENV = "AMA_NO_INIT";
|
|
13
|
+
/** 最小 config.json:只有 `$schema`、`version` 与空 `providers`(缺省值见 `ama config show`)。 */
|
|
14
|
+
export declare function minimalConfig(): AmaConfig & {
|
|
15
|
+
$schema: string;
|
|
16
|
+
};
|
|
17
|
+
export type InitStatus = "created" | "exists" | "updated" | "unchanged" | "overwritten";
|
|
18
|
+
export interface InitResult {
|
|
19
|
+
dir: string;
|
|
20
|
+
dirCreated: boolean;
|
|
21
|
+
files: {
|
|
22
|
+
path: string;
|
|
23
|
+
status: InitStatus;
|
|
24
|
+
}[];
|
|
25
|
+
}
|
|
26
|
+
/** 建目录并补齐缺失文件;已有的 config.json 不覆盖(`force` 时备份为 .bak 再重写)。 */
|
|
27
|
+
export declare function initConfigDir(dir: string, options?: {
|
|
28
|
+
force?: boolean;
|
|
29
|
+
}): InitResult;
|
|
30
|
+
/** CLI 启动时:配置目录不存在就静默初始化;返回是否初始化了。失败不影响启动。 */
|
|
31
|
+
export declare function autoInitConfigDir(dir: string, env: Readonly<Record<string, string | undefined>>): boolean;
|
|
32
|
+
/** init 之后的下一步(配 key / 接中转站 / 自检)。 */
|
|
33
|
+
export declare const INIT_NEXT_STEPS: string[];
|
|
34
|
+
export declare function describeInit(result: InitResult): string;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 配置目录初始化(docs/providers.md「配置目录」):`ama init` 与 CLI 首次运行的自动初始化。
|
|
3
|
+
*
|
|
4
|
+
* - 目录 0700;`config.json` 只在不存在时写最小内容(`--force` 时先备份再重写):只有 `$schema`、
|
|
5
|
+
* `version` 与空 `providers`,不写死任何缺省值——缺省值以后调整(例如 codemode 跟随预设)对老用户
|
|
6
|
+
* 同样生效,`config show` 的来源也显示 default 而不是 user;
|
|
7
|
+
* - `config.schema.json` 不是用户文件,每次 init 都按当前版本重写(内容相同则不动);
|
|
8
|
+
* - 不创建 auth.json(只有 `ama auth set` / `ama providers add` 才写,0600)。
|
|
9
|
+
* - 自动初始化只在 CLI 里、配置目录不存在时触发,`AMA_NO_INIT=1` 关闭;SDK 与测试不走这里。
|
|
10
|
+
*/
|
|
11
|
+
import { chmodSync, copyFileSync, existsSync, mkdirSync, readFileSync, renameSync, writeFileSync, } from "node:fs";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
import { CONFIG_SCHEMA_FILE, configSchemaText } from "./json-schema.js";
|
|
14
|
+
import { AUTH_FILE, CONFIG_FILE } from "./paths.js";
|
|
15
|
+
import { CONFIG_FILE_VERSION } from "./types.js";
|
|
16
|
+
export const NO_INIT_ENV = "AMA_NO_INIT";
|
|
17
|
+
/** 最小 config.json:只有 `$schema`、`version` 与空 `providers`(缺省值见 `ama config show`)。 */
|
|
18
|
+
export function minimalConfig() {
|
|
19
|
+
return {
|
|
20
|
+
$schema: `./${CONFIG_SCHEMA_FILE}`,
|
|
21
|
+
version: CONFIG_FILE_VERSION,
|
|
22
|
+
providers: {},
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function writeAtomic(path, text, mode) {
|
|
26
|
+
const tmp = `${path}.${process.pid}.tmp`;
|
|
27
|
+
writeFileSync(tmp, text, { mode });
|
|
28
|
+
renameSync(tmp, path);
|
|
29
|
+
}
|
|
30
|
+
/** 建目录并补齐缺失文件;已有的 config.json 不覆盖(`force` 时备份为 .bak 再重写)。 */
|
|
31
|
+
export function initConfigDir(dir, options = {}) {
|
|
32
|
+
const dirCreated = !existsSync(dir);
|
|
33
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
34
|
+
if (dirCreated && process.platform !== "win32")
|
|
35
|
+
chmodSync(dir, 0o700);
|
|
36
|
+
const files = [];
|
|
37
|
+
const config = join(dir, CONFIG_FILE);
|
|
38
|
+
const text = `${JSON.stringify(minimalConfig(), null, 2)}\n`;
|
|
39
|
+
if (!existsSync(config)) {
|
|
40
|
+
writeAtomic(config, text, 0o644);
|
|
41
|
+
files.push({ path: config, status: "created" });
|
|
42
|
+
}
|
|
43
|
+
else if (options.force) {
|
|
44
|
+
copyFileSync(config, `${config}.bak`);
|
|
45
|
+
writeAtomic(config, text, 0o644);
|
|
46
|
+
files.push({ path: config, status: "overwritten" });
|
|
47
|
+
}
|
|
48
|
+
else
|
|
49
|
+
files.push({ path: config, status: "exists" });
|
|
50
|
+
const schema = join(dir, CONFIG_SCHEMA_FILE);
|
|
51
|
+
const schemaText = configSchemaText();
|
|
52
|
+
if (!existsSync(schema)) {
|
|
53
|
+
writeAtomic(schema, schemaText, 0o644);
|
|
54
|
+
files.push({ path: schema, status: "created" });
|
|
55
|
+
}
|
|
56
|
+
else if (readFileSync(schema, "utf8") !== schemaText) {
|
|
57
|
+
writeAtomic(schema, schemaText, 0o644);
|
|
58
|
+
files.push({ path: schema, status: "updated" });
|
|
59
|
+
}
|
|
60
|
+
else
|
|
61
|
+
files.push({ path: schema, status: "unchanged" });
|
|
62
|
+
const auth = join(dir, AUTH_FILE);
|
|
63
|
+
if (existsSync(auth))
|
|
64
|
+
files.push({ path: auth, status: "exists" });
|
|
65
|
+
return { dir, dirCreated, files };
|
|
66
|
+
}
|
|
67
|
+
/** CLI 启动时:配置目录不存在就静默初始化;返回是否初始化了。失败不影响启动。 */
|
|
68
|
+
export function autoInitConfigDir(dir, env) {
|
|
69
|
+
if (env[NO_INIT_ENV] === "1" || existsSync(dir))
|
|
70
|
+
return false;
|
|
71
|
+
try {
|
|
72
|
+
initConfigDir(dir);
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
const STATUS_TEXT = {
|
|
80
|
+
created: "已创建",
|
|
81
|
+
exists: "已存在,未改动",
|
|
82
|
+
updated: "已更新为当前版本",
|
|
83
|
+
unchanged: "已是当前版本",
|
|
84
|
+
overwritten: "已重写(原文件备份为 .bak)",
|
|
85
|
+
};
|
|
86
|
+
/** init 之后的下一步(配 key / 接中转站 / 自检)。 */
|
|
87
|
+
export const INIT_NEXT_STEPS = [
|
|
88
|
+
"下一步:",
|
|
89
|
+
" ama auth set anthropic 保存内置供应商的 key(或设置 ANTHROPIC_API_KEY 等环境变量)",
|
|
90
|
+
" ama providers add <id> --base-url <url> 接入中转站或自建服务",
|
|
91
|
+
" ama doctor 检查配置、key 与运行环境",
|
|
92
|
+
" ama config show 查看生效配置与每项来源",
|
|
93
|
+
];
|
|
94
|
+
export function describeInit(result) {
|
|
95
|
+
const lines = [`${result.dir}${result.dirCreated ? " 已创建(0700)" : " 已存在"}`];
|
|
96
|
+
for (const file of result.files)
|
|
97
|
+
lines.push(` ${file.path} ${STATUS_TEXT[file.status]}`);
|
|
98
|
+
return `${lines.join("\n")}\n`;
|
|
99
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* config.json 的 JSON Schema(draft-07 子集),写到配置目录的 `config.schema.json` 给编辑器补全与校验
|
|
3
|
+
* (docs/providers.md「配置目录」)。规则与 schema.ts 的 `validateConfig` 一一对应:未知字段在那里是
|
|
4
|
+
* warning、这里是 `additionalProperties: false`;渠道引用(模型 `channels` 指向已定义的渠道)这类跨字段
|
|
5
|
+
* 规则 JSON Schema 表达不了,只在 `validateConfig` 里查。一致性由 json-schema.test.ts 的正反例守住。
|
|
6
|
+
* 顶层与各段的键(供应商内部除外)的 description / default 来自 key-docs.ts(`annotate`)。
|
|
7
|
+
*/
|
|
8
|
+
type Schema = Record<string, unknown>;
|
|
9
|
+
export declare const CONFIG_SCHEMA_FILE = "config.schema.json";
|
|
10
|
+
export declare const CONFIG_SCHEMA_ID = "https://github.com/Owlbay/armadra-agent/config.schema.json";
|
|
11
|
+
/** 生成 config.json 的 JSON Schema。 */
|
|
12
|
+
export declare function buildConfigJsonSchema(): Schema;
|
|
13
|
+
/** 写进文件的文本(2 空格缩进 + 换行结尾)。 */
|
|
14
|
+
export declare function configSchemaText(): string;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* config.json 的 JSON Schema(draft-07 子集),写到配置目录的 `config.schema.json` 给编辑器补全与校验
|
|
3
|
+
* (docs/providers.md「配置目录」)。规则与 schema.ts 的 `validateConfig` 一一对应:未知字段在那里是
|
|
4
|
+
* warning、这里是 `additionalProperties: false`;渠道引用(模型 `channels` 指向已定义的渠道)这类跨字段
|
|
5
|
+
* 规则 JSON Schema 表达不了,只在 `validateConfig` 里查。一致性由 json-schema.test.ts 的正反例守住。
|
|
6
|
+
* 顶层与各段的键(供应商内部除外)的 description / default 来自 key-docs.ts(`annotate`)。
|
|
7
|
+
*/
|
|
8
|
+
import { WARMING_MODES } from "../ai/cache/types.js";
|
|
9
|
+
import { PERMISSION_MODES_STRICT_FIRST } from "../permissions/types.js";
|
|
10
|
+
import { CONFIG_KEY_DOCS, DYNAMIC_DEFAULTS, defaultFor } from "./key-docs.js";
|
|
11
|
+
import { HOOK_TIMEOUT_MAX_MS, THINKING_LEVELS } from "./schema.js";
|
|
12
|
+
import { CACHE_RETENTIONS, CHANNEL_NAME_PATTERN, CODEMODE_MODES, TOOLS_PRESET_INPUTS, } from "./types.js";
|
|
13
|
+
export const CONFIG_SCHEMA_FILE = "config.schema.json";
|
|
14
|
+
export const CONFIG_SCHEMA_ID = "https://github.com/Owlbay/armadra-agent/config.schema.json";
|
|
15
|
+
const KNOWN_APIS = [
|
|
16
|
+
"openai-completions",
|
|
17
|
+
"openai-responses",
|
|
18
|
+
"anthropic-messages",
|
|
19
|
+
"google-generative-ai",
|
|
20
|
+
];
|
|
21
|
+
function object(properties, extra = {}) {
|
|
22
|
+
return { type: "object", properties, additionalProperties: false, ...extra };
|
|
23
|
+
}
|
|
24
|
+
const str = (description) => ({
|
|
25
|
+
type: "string",
|
|
26
|
+
...(description ? { description } : {}),
|
|
27
|
+
});
|
|
28
|
+
const bool = (description) => ({
|
|
29
|
+
type: "boolean",
|
|
30
|
+
...(description ? { description } : {}),
|
|
31
|
+
});
|
|
32
|
+
const num = (minimum, maximum, description) => ({
|
|
33
|
+
type: "number",
|
|
34
|
+
...(minimum !== undefined ? { minimum } : {}),
|
|
35
|
+
...(maximum !== undefined ? { maximum } : {}),
|
|
36
|
+
...(description ? { description } : {}),
|
|
37
|
+
});
|
|
38
|
+
const strings = { type: "array", items: { type: "string" } };
|
|
39
|
+
const stringRecord = { type: "object", additionalProperties: { type: "string" } };
|
|
40
|
+
const oneOf = (values, description) => ({
|
|
41
|
+
type: "string",
|
|
42
|
+
enum: [...values],
|
|
43
|
+
...(description ? { description } : {}),
|
|
44
|
+
});
|
|
45
|
+
/** api 只查是字符串(与 validateConfig 一致),enum 给编辑器补全。 */
|
|
46
|
+
const api = {
|
|
47
|
+
type: "string",
|
|
48
|
+
description: "协议",
|
|
49
|
+
examples: KNOWN_APIS,
|
|
50
|
+
anyOf: [{ enum: KNOWN_APIS }, { type: "string" }],
|
|
51
|
+
};
|
|
52
|
+
const compat = {
|
|
53
|
+
type: "object",
|
|
54
|
+
description: "协议兼容开关(docs/providers.md「compat」)",
|
|
55
|
+
properties: {
|
|
56
|
+
sendPromptCacheKey: bool(),
|
|
57
|
+
sendSessionAffinityHeaders: bool(),
|
|
58
|
+
supportsLongCacheRetention: bool(),
|
|
59
|
+
supportsExplicitPromptCacheMode: bool(),
|
|
60
|
+
cacheReporting: oneOf(["auto", "silent", "reported"]),
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
function model(description) {
|
|
64
|
+
return {
|
|
65
|
+
type: "object",
|
|
66
|
+
description,
|
|
67
|
+
required: ["id"],
|
|
68
|
+
properties: {
|
|
69
|
+
id: str("模型 id(发给上游的名字)"),
|
|
70
|
+
name: str(),
|
|
71
|
+
api,
|
|
72
|
+
baseUrl: str("覆盖渠道 / 供应商的地址"),
|
|
73
|
+
contextWindow: num(1, undefined, "上下文 token;缺省从 models.dev 补,匹配不到不猜"),
|
|
74
|
+
maxTokens: num(1, undefined, "单次输出上限;缺省 min(models.dev, 64k) 或 8192"),
|
|
75
|
+
reasoning: bool(),
|
|
76
|
+
input: {
|
|
77
|
+
type: "array",
|
|
78
|
+
items: { enum: ["text", "image"] },
|
|
79
|
+
description: '["text"] 或 ["text", "image"](收图片)',
|
|
80
|
+
},
|
|
81
|
+
channels: { ...strings, description: "挂载的渠道,第一个是首选" },
|
|
82
|
+
modelsDev: {
|
|
83
|
+
description: 'models.dev 条目 "provider/model";false 关闭补全',
|
|
84
|
+
anyOf: [{ type: "string", pattern: "^[^/]+/.+$" }, { const: false }],
|
|
85
|
+
},
|
|
86
|
+
headers: stringRecord,
|
|
87
|
+
compat,
|
|
88
|
+
promptCache: object({ short: num(), long: num(), minTokens: num() }),
|
|
89
|
+
cost: { type: "object", description: "$/M token" },
|
|
90
|
+
thinkingLevelMap: { type: "object" },
|
|
91
|
+
samplingParams: { type: "object" },
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
const channel = object({
|
|
96
|
+
api,
|
|
97
|
+
baseUrl: str("接口地址"),
|
|
98
|
+
apiKey: str("$ENV / ${ENV} / !command / 字面量;缺省用供应商的 key"),
|
|
99
|
+
authHeader: { type: "object" },
|
|
100
|
+
headers: stringRecord,
|
|
101
|
+
compat,
|
|
102
|
+
}, { required: ["api", "baseUrl"] });
|
|
103
|
+
const provider = object({
|
|
104
|
+
name: str(),
|
|
105
|
+
api,
|
|
106
|
+
baseUrl: str(),
|
|
107
|
+
apiKey: str("$ENV / ${ENV} / !command / 字面量"),
|
|
108
|
+
envKeys: strings,
|
|
109
|
+
authHeader: { type: "object" },
|
|
110
|
+
headers: stringRecord,
|
|
111
|
+
compat,
|
|
112
|
+
requiresApiKey: bool(),
|
|
113
|
+
channels: {
|
|
114
|
+
type: "object",
|
|
115
|
+
description: "渠道:一个供应商下的多种接口",
|
|
116
|
+
minProperties: 1,
|
|
117
|
+
propertyNames: { pattern: CHANNEL_NAME_PATTERN.source },
|
|
118
|
+
additionalProperties: channel,
|
|
119
|
+
},
|
|
120
|
+
defaultChannel: str("首选渠道;缺省 channels 的第一个"),
|
|
121
|
+
models: { type: "array", items: model("自定义模型(同 id 整条替换)") },
|
|
122
|
+
modelOverrides: { type: "array", items: model("只改已有模型的元数据") },
|
|
123
|
+
});
|
|
124
|
+
/** 给顶层与各段的键写上 description 与 default(key-docs.ts);供应商内部不动。 */
|
|
125
|
+
function annotate(properties, prefix = "") {
|
|
126
|
+
for (const [key, shared] of Object.entries(properties)) {
|
|
127
|
+
const path = prefix === "" ? key : `${prefix}.${key}`;
|
|
128
|
+
// 片段(strings、bool() 等)在多处复用:先浅拷贝再写说明
|
|
129
|
+
const schema = { ...shared };
|
|
130
|
+
properties[key] = schema;
|
|
131
|
+
const description = CONFIG_KEY_DOCS[path];
|
|
132
|
+
if (description !== undefined)
|
|
133
|
+
schema["description"] = description;
|
|
134
|
+
const nested = schema["properties"];
|
|
135
|
+
if (nested !== undefined && path !== "providers") {
|
|
136
|
+
annotate(nested, path);
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
const value = defaultFor(path);
|
|
140
|
+
if (value !== undefined && DYNAMIC_DEFAULTS[path] === undefined)
|
|
141
|
+
schema["default"] = value;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/** 生成 config.json 的 JSON Schema。 */
|
|
145
|
+
export function buildConfigJsonSchema() {
|
|
146
|
+
const schema = buildBaseSchema();
|
|
147
|
+
annotate(schema["properties"]);
|
|
148
|
+
return schema;
|
|
149
|
+
}
|
|
150
|
+
function buildBaseSchema() {
|
|
151
|
+
return {
|
|
152
|
+
$schema: "http://json-schema.org/draft-07/schema#",
|
|
153
|
+
$id: CONFIG_SCHEMA_ID,
|
|
154
|
+
title: "ama config.json",
|
|
155
|
+
...object({
|
|
156
|
+
$schema: str(),
|
|
157
|
+
version: { const: 1 },
|
|
158
|
+
defaultModel: str("provider/model 或 provider/model@channel"),
|
|
159
|
+
thinkingLevel: oneOf(THINKING_LEVELS),
|
|
160
|
+
providers: { type: "object", additionalProperties: provider },
|
|
161
|
+
permission: object({
|
|
162
|
+
mode: oneOf(PERMISSION_MODES_STRICT_FIRST),
|
|
163
|
+
allow: strings,
|
|
164
|
+
deny: strings,
|
|
165
|
+
builtinDeny: { anyOf: [{ type: "boolean" }, strings] },
|
|
166
|
+
autoModel: str("auto 模式分类器的模型 provider/model;缺省用当前会话模型"),
|
|
167
|
+
autoSafeCommands: strings,
|
|
168
|
+
}),
|
|
169
|
+
compaction: object({ enabled: bool(), reserveTokens: num(0), keepRecentTokens: num(0) }),
|
|
170
|
+
retry: object({
|
|
171
|
+
enabled: bool(),
|
|
172
|
+
maxRetries: num(0, 100),
|
|
173
|
+
baseDelayMs: num(0),
|
|
174
|
+
maxDelayMs: num(0),
|
|
175
|
+
}),
|
|
176
|
+
tools: object({
|
|
177
|
+
preset: oneOf(TOOLS_PRESET_INPUTS),
|
|
178
|
+
default: strings,
|
|
179
|
+
maxToolResultChars: num(1),
|
|
180
|
+
bashTimeoutMs: num(1),
|
|
181
|
+
disabled: strings,
|
|
182
|
+
}),
|
|
183
|
+
codemode: object({
|
|
184
|
+
mode: oneOf(CODEMODE_MODES),
|
|
185
|
+
inlineBudget: num(0),
|
|
186
|
+
requireStrict: bool(),
|
|
187
|
+
}),
|
|
188
|
+
hooks: object({ timeoutMs: num(1, HOOK_TIMEOUT_MAX_MS) }),
|
|
189
|
+
ui: object({
|
|
190
|
+
theme: oneOf(["dark", "light", "auto"]),
|
|
191
|
+
markdown: bool(),
|
|
192
|
+
showThinking: oneOf(["full", "collapsed", "hidden"]),
|
|
193
|
+
tuiMode: oneOf(["regular"]),
|
|
194
|
+
quietStartup: oneOf(["normal", "header", "silent"]),
|
|
195
|
+
ascii: bool(),
|
|
196
|
+
compact: bool(),
|
|
197
|
+
animation: bool(),
|
|
198
|
+
}),
|
|
199
|
+
skills: object({ dirs: strings }),
|
|
200
|
+
cache: object({
|
|
201
|
+
warming: oneOf(WARMING_MODES),
|
|
202
|
+
retention: oneOf(CACHE_RETENTIONS),
|
|
203
|
+
minSavingsUsd: num(0),
|
|
204
|
+
missNotices: bool(),
|
|
205
|
+
warmSubagents: bool(),
|
|
206
|
+
}),
|
|
207
|
+
request: object({ idleTimeoutMs: num(0) }),
|
|
208
|
+
}),
|
|
209
|
+
required: ["version"],
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
/** 写进文件的文本(2 空格缩进 + 换行结尾)。 */
|
|
213
|
+
export function configSchemaText() {
|
|
214
|
+
return `${JSON.stringify(buildConfigJsonSchema(), null, 2)}\n`;
|
|
215
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* config.json 每个键的说明与缺省值:config.schema.json(编辑器悬停)与 `ama config show`(来源 default
|
|
3
|
+
* 的行)共用这一张表。
|
|
4
|
+
*
|
|
5
|
+
* - `description` 写在 `CONFIG_KEY_DOCS`;缺省值不手写,从 `DEFAULT_CONFIG`(merge.ts)、
|
|
6
|
+
* `DEFAULT_CACHE_CONFIG`(types.ts)与代码里的隐式缺省(`IMPLICIT_DEFAULTS`)合成 `DISPLAY_DEFAULTS`;
|
|
7
|
+
* - 缺省值取决于运行时的键(自动选模型、跟随预设等)列在 `DYNAMIC_DEFAULTS`,schema 里不写 `default`,
|
|
8
|
+
* 说明里写规则;
|
|
9
|
+
* - 一致性由 json-schema.test.ts 守住:schema 的每个键都在表里,表里的每个键 schema 都有,缺省值与
|
|
10
|
+
* DISPLAY_DEFAULTS 相同,DISPLAY_DEFAULTS 本身通过 `validateConfig`。
|
|
11
|
+
*/
|
|
12
|
+
import { type AmaConfig } from "./types.js";
|
|
13
|
+
/** 全部有固定缺省值的键(展示用;运行时仍以 DEFAULT_CONFIG 合并)。 */
|
|
14
|
+
export declare const DISPLAY_DEFAULTS: Readonly<AmaConfig>;
|
|
15
|
+
/** 缺省值由运行时决定的键 → 规则(schema 不写 default)。 */
|
|
16
|
+
export declare const DYNAMIC_DEFAULTS: Readonly<Record<string, string>>;
|
|
17
|
+
/** 键路径 → 说明(段落本身也有一条)。 */
|
|
18
|
+
export declare const CONFIG_KEY_DOCS: Readonly<Record<string, string>>;
|
|
19
|
+
/** 叶子路径(段落不算)。 */
|
|
20
|
+
export declare function documentedLeaves(): string[];
|
|
21
|
+
/** 键路径的缺省值(DISPLAY_DEFAULTS 里取;没有返回 undefined)。 */
|
|
22
|
+
export declare function defaultFor(path: string): unknown;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* config.json 每个键的说明与缺省值:config.schema.json(编辑器悬停)与 `ama config show`(来源 default
|
|
3
|
+
* 的行)共用这一张表。
|
|
4
|
+
*
|
|
5
|
+
* - `description` 写在 `CONFIG_KEY_DOCS`;缺省值不手写,从 `DEFAULT_CONFIG`(merge.ts)、
|
|
6
|
+
* `DEFAULT_CACHE_CONFIG`(types.ts)与代码里的隐式缺省(`IMPLICIT_DEFAULTS`)合成 `DISPLAY_DEFAULTS`;
|
|
7
|
+
* - 缺省值取决于运行时的键(自动选模型、跟随预设等)列在 `DYNAMIC_DEFAULTS`,schema 里不写 `default`,
|
|
8
|
+
* 说明里写规则;
|
|
9
|
+
* - 一致性由 json-schema.test.ts 守住:schema 的每个键都在表里,表里的每个键 schema 都有,缺省值与
|
|
10
|
+
* DISPLAY_DEFAULTS 相同,DISPLAY_DEFAULTS 本身通过 `validateConfig`。
|
|
11
|
+
*/
|
|
12
|
+
import { DEFAULT_IDLE_TIMEOUT_MS } from "../ai/http.js";
|
|
13
|
+
import { DEFAULT_INLINE_BUDGET } from "../codemode/declarations.js";
|
|
14
|
+
import { DEFAULT_CONFIG, mergeConfig } from "./merge.js";
|
|
15
|
+
import { DEFAULT_CACHE_CONFIG } from "./types.js";
|
|
16
|
+
/** 代码里生效、但 DEFAULT_CONFIG 不写的缺省(写进去会改变合并结果或 `config.codemode` 的有无)。 */
|
|
17
|
+
const IMPLICIT_DEFAULTS = {
|
|
18
|
+
providers: {},
|
|
19
|
+
permission: { builtinDeny: true, autoSafeCommands: [] },
|
|
20
|
+
tools: { default: [] },
|
|
21
|
+
codemode: { inlineBudget: DEFAULT_INLINE_BUDGET, requireStrict: false },
|
|
22
|
+
cache: { ...DEFAULT_CACHE_CONFIG },
|
|
23
|
+
request: { idleTimeoutMs: DEFAULT_IDLE_TIMEOUT_MS },
|
|
24
|
+
ui: { compact: false, animation: true },
|
|
25
|
+
};
|
|
26
|
+
/** 全部有固定缺省值的键(展示用;运行时仍以 DEFAULT_CONFIG 合并)。 */
|
|
27
|
+
export const DISPLAY_DEFAULTS = Object.freeze(mergeConfig(DEFAULT_CONFIG, IMPLICIT_DEFAULTS));
|
|
28
|
+
/** 缺省值由运行时决定的键 → 规则(schema 不写 default)。 */
|
|
29
|
+
export const DYNAMIC_DEFAULTS = Object.freeze({
|
|
30
|
+
$schema: "ama init 写 ./config.schema.json",
|
|
31
|
+
defaultModel: "零配置自动选择",
|
|
32
|
+
"permission.autoModel": "当前会话模型",
|
|
33
|
+
"codemode.mode": "跟随预设",
|
|
34
|
+
"ui.ascii": "自动检测",
|
|
35
|
+
});
|
|
36
|
+
/** 键路径 → 说明(段落本身也有一条)。 */
|
|
37
|
+
export const CONFIG_KEY_DOCS = Object.freeze({
|
|
38
|
+
$schema: "编辑器用的 JSON Schema 路径",
|
|
39
|
+
version: "配置文件格式版本,固定为 1",
|
|
40
|
+
defaultModel: "缺省模型 provider/model 或 provider/model@channel;不写时零配置自动选择(ama config show 显示选了谁、为什么)",
|
|
41
|
+
thinkingLevel: "思考强度",
|
|
42
|
+
providers: "自定义供应商与对内置供应商的覆盖(docs/providers.md)",
|
|
43
|
+
permission: "权限(docs/permissions.md)",
|
|
44
|
+
"permission.mode": "权限模式;项目级只能更严",
|
|
45
|
+
"permission.allow": "放行规则,如 bash(npm test);项目级不能加",
|
|
46
|
+
"permission.deny": "拒绝规则,如 write(**/.env);各层累加",
|
|
47
|
+
"permission.builtinDeny": "内置 deny 表(.git/** 写、.ssh/** 读写):true 全部启用,false 全部移除,数组 = 要移除的规则原文;只认用户级",
|
|
48
|
+
"permission.autoModel": "auto 模式分类器的模型 provider/model;不写时用当前会话模型;只认用户级",
|
|
49
|
+
"permission.autoSafeCommands": "auto 模式安全名单追加(词前缀或含 * 的通配);只认用户级",
|
|
50
|
+
compaction: "自动压缩",
|
|
51
|
+
"compaction.enabled": "上下文接近上限时自动压缩",
|
|
52
|
+
"compaction.reserveTokens": "为回复预留的 token;剩余不足时触发压缩",
|
|
53
|
+
"compaction.keepRecentTokens": "压缩时原样保留的最近消息 token",
|
|
54
|
+
retry: "请求失败重试",
|
|
55
|
+
"retry.enabled": "可重试的错误(限流、服务端错误、连接中断等)自动重试",
|
|
56
|
+
"retry.maxRetries": "最多重试次数",
|
|
57
|
+
"retry.baseDelayMs": "首次重试等待(毫秒,指数退避;有 Retry-After 时按它)",
|
|
58
|
+
"retry.maxDelayMs": "单次等待上限(毫秒)",
|
|
59
|
+
tools: "内置工具",
|
|
60
|
+
"tools.preset": "工具预设:default(六个工具,Node ≥ 25 时另加 codemode)、minimal、codemode-only、coordinator;codemode 是 codemode-only 的旧名;项目级只能更严",
|
|
61
|
+
"tools.default": "在预设上微调:+name 加、-name 去,不带前缀的名字整组替换预设的内置工具;只认用户级",
|
|
62
|
+
"tools.maxToolResultChars": "单条工具结果进上下文的字符上限,超出保留首尾",
|
|
63
|
+
"tools.bashTimeoutMs": "bash 缺省超时(毫秒,单次调用可覆盖,最大 600000)",
|
|
64
|
+
"tools.disabled": "禁用的工具名;各层累加",
|
|
65
|
+
codemode: "codemode:模型写脚本批量调用工具(docs/codemode.md)",
|
|
66
|
+
"codemode.mode": "off | on | only;不写时跟随预设:default → on(Node ≥ 25;Node 22 / 24 → off)、codemode-only → only、minimal / coordinator → off;项目级只接受 off",
|
|
67
|
+
"codemode.inlineBudget": "only 模式在描述里内联工具声明的预算(估算 token),超出只列名字",
|
|
68
|
+
"codemode.requireStrict": "true:运行时 Node 不隔离网络(Node 22 / 24)时直接禁用 codemode",
|
|
69
|
+
hooks: "Hook 设置(Hook 本身写在 hooks.json)",
|
|
70
|
+
"hooks.timeoutMs": "单个 Hook 命令的缺省超时(毫秒)",
|
|
71
|
+
ui: "终端界面",
|
|
72
|
+
"ui.theme": "配色主题:dark、light,或 auto(按 COLORFGBG 猜,不发终端查询,猜不出用 dark)",
|
|
73
|
+
"ui.markdown": "按 Markdown 渲染回复",
|
|
74
|
+
"ui.showThinking": "思考内容的显示方式",
|
|
75
|
+
"ui.tuiMode": "终端界面形态(目前只有 regular)",
|
|
76
|
+
"ui.quietStartup": "启动画面详略",
|
|
77
|
+
"ui.ascii": "ASCII 字形(> * L、+ - |);不写时自动检测:区域设置不含 UTF-8、TERM=linux、旧 conhost 时开启;AMA_ASCII=1/0 覆盖",
|
|
78
|
+
"ui.compact": "消息区块间不空行、启动头不画框",
|
|
79
|
+
"ui.animation": "false:运行中 spinner 静止,只在秒数变化时重绘",
|
|
80
|
+
skills: "Skill",
|
|
81
|
+
"skills.dirs": "追加的 Skill 目录;各层累加",
|
|
82
|
+
cache: "提示缓存;整段只认用户级",
|
|
83
|
+
request: "模型请求;整段只认用户级 / profile",
|
|
84
|
+
"request.idleTimeoutMs": "流空闲超时(毫秒):等响应头或两块数据之间超过即判卡住并按可重试错误重试;0 关闭;AMA_IDLE_TIMEOUT_MS 覆盖",
|
|
85
|
+
"cache.warming": "保温:off 关闭,streaming 生成期间保温,idle 空闲时也保温",
|
|
86
|
+
"cache.retention": "缓存时长档:none、short、long(供应商支持时)",
|
|
87
|
+
"cache.minSavingsUsd": "保温的最低期望节省(美元)",
|
|
88
|
+
"cache.missNotices": "在消息区提示缓存未命中与上下文余量",
|
|
89
|
+
"cache.warmSubagents": "子会话(task)也保温",
|
|
90
|
+
});
|
|
91
|
+
/** 叶子路径(段落不算)。 */
|
|
92
|
+
export function documentedLeaves() {
|
|
93
|
+
const keys = Object.keys(CONFIG_KEY_DOCS);
|
|
94
|
+
return keys.filter((key) => !keys.some((other) => other.startsWith(`${key}.`)));
|
|
95
|
+
}
|
|
96
|
+
/** 键路径的缺省值(DISPLAY_DEFAULTS 里取;没有返回 undefined)。 */
|
|
97
|
+
export function defaultFor(path) {
|
|
98
|
+
let node = DISPLAY_DEFAULTS;
|
|
99
|
+
for (const key of path.split(".")) {
|
|
100
|
+
if (typeof node !== "object" || node === null || Array.isArray(node))
|
|
101
|
+
return undefined;
|
|
102
|
+
node = node[key];
|
|
103
|
+
}
|
|
104
|
+
return node;
|
|
105
|
+
}
|
package/dist/config/merge.d.ts
CHANGED
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
* 配置层级合并(设计 §7.2、§10.2)。[B5]
|
|
3
3
|
*
|
|
4
4
|
* 顺序:内置缺省 ← 用户级 ← profile.config ← 项目级(受限字段,只能收紧)← 命令行。
|
|
5
|
-
* - 对象深合并;数组整体替换,例外是「累加型」列表:`permission.allow / deny`、
|
|
5
|
+
* - 对象深合并;数组整体替换,例外是「累加型」列表:`permission.allow / deny / autoSafeCommands`、
|
|
6
6
|
* `tools.disabled`、`skills.dirs` 跨层拼接去重。
|
|
7
|
-
* - 项目级 `.ama/config.json` 只接受:`permission.deny`(追加)、`permission.mode
|
|
7
|
+
* - 项目级 `.ama/config.json` 只接受:`permission.deny`(追加)、`permission.mode`(只能更严,且不能是
|
|
8
|
+
* auto / full-auto)、
|
|
8
9
|
* `compaction`、`tools.disabled`、`tools.preset`(只能更严)、`codemode.mode: "off"`、`ui`;
|
|
9
|
-
* 其它字段与放宽项(含 `permission.builtinDeny`)被忽略并记 warning。
|
|
10
|
+
* 其它字段与放宽项(含 `permission.builtinDeny / autoModel / autoSafeCommands`)被忽略并记 warning。
|
|
10
11
|
* - 同时产出带来源的权限规则清单(`ruleSpecs`),交给权限管线(B3 的 rules.ts 解析)。
|
|
11
12
|
*/
|
|
12
|
-
import type { AmaConfig, CodemodeMode,
|
|
13
|
+
import type { AmaConfig, CodemodeMode, ToolsPresetInput } from "./types.js";
|
|
13
14
|
import type { PermissionMode, RuleSource } from "../permissions/types.js";
|
|
14
15
|
import type { ModelThinkingLevel } from "../ai/types.js";
|
|
15
16
|
export declare const DEFAULT_CONFIG: Readonly<AmaConfig>;
|
|
@@ -28,13 +29,13 @@ export interface RestrictResult {
|
|
|
28
29
|
accepted: Partial<AmaConfig>;
|
|
29
30
|
warnings: string[];
|
|
30
31
|
}
|
|
31
|
-
/** 预设 a 是否比 b 更严或相同(coordinator 最严,codemode
|
|
32
|
-
export declare function isPresetStricterOrEqual(a:
|
|
32
|
+
/** 预设 a 是否比 b 更严或相同(coordinator 最严,codemode-only 最宽;别名按规范名比)。 */
|
|
33
|
+
export declare function isPresetStricterOrEqual(a: ToolsPresetInput, b: ToolsPresetInput): boolean;
|
|
33
34
|
/**
|
|
34
35
|
* 把项目级配置裁剪为受限字段(§7.2);`currentMode` / `currentPreset` 是合并到此为止的
|
|
35
36
|
* 权限模式与工具预设。
|
|
36
37
|
*/
|
|
37
|
-
export declare function restrictProjectConfig(project: AmaConfig, currentMode: PermissionMode, label?: string, currentPreset?:
|
|
38
|
+
export declare function restrictProjectConfig(project: AmaConfig, currentMode: PermissionMode, label?: string, currentPreset?: ToolsPresetInput): RestrictResult;
|
|
38
39
|
/** 命令行对配置的覆盖(只含与 config 同名的那部分参数)。 */
|
|
39
40
|
export interface CliConfigOverrides {
|
|
40
41
|
thinkingLevel?: ModelThinkingLevel | undefined;
|
|
@@ -45,7 +46,7 @@ export interface CliConfigOverrides {
|
|
|
45
46
|
tuiMode?: "regular" | undefined;
|
|
46
47
|
skillDirs?: readonly string[] | undefined;
|
|
47
48
|
/** `--tools-preset`。 */
|
|
48
|
-
toolsPreset?:
|
|
49
|
+
toolsPreset?: ToolsPresetInput | undefined;
|
|
49
50
|
/** `--codemode`。 */
|
|
50
51
|
codemode?: CodemodeMode | undefined;
|
|
51
52
|
}
|
package/dist/config/merge.js
CHANGED
|
@@ -2,14 +2,15 @@
|
|
|
2
2
|
* 配置层级合并(设计 §7.2、§10.2)。[B5]
|
|
3
3
|
*
|
|
4
4
|
* 顺序:内置缺省 ← 用户级 ← profile.config ← 项目级(受限字段,只能收紧)← 命令行。
|
|
5
|
-
* - 对象深合并;数组整体替换,例外是「累加型」列表:`permission.allow / deny`、
|
|
5
|
+
* - 对象深合并;数组整体替换,例外是「累加型」列表:`permission.allow / deny / autoSafeCommands`、
|
|
6
6
|
* `tools.disabled`、`skills.dirs` 跨层拼接去重。
|
|
7
|
-
* - 项目级 `.ama/config.json` 只接受:`permission.deny`(追加)、`permission.mode
|
|
7
|
+
* - 项目级 `.ama/config.json` 只接受:`permission.deny`(追加)、`permission.mode`(只能更严,且不能是
|
|
8
|
+
* auto / full-auto)、
|
|
8
9
|
* `compaction`、`tools.disabled`、`tools.preset`(只能更严)、`codemode.mode: "off"`、`ui`;
|
|
9
|
-
* 其它字段与放宽项(含 `permission.builtinDeny`)被忽略并记 warning。
|
|
10
|
+
* 其它字段与放宽项(含 `permission.builtinDeny / autoModel / autoSafeCommands`)被忽略并记 warning。
|
|
10
11
|
* - 同时产出带来源的权限规则清单(`ruleSpecs`),交给权限管线(B3 的 rules.ts 解析)。
|
|
11
12
|
*/
|
|
12
|
-
import { CONFIG_FILE_VERSION, TOOLS_PRESETS_STRICT_FIRST } from "./types.js";
|
|
13
|
+
import { CONFIG_FILE_VERSION, TOOLS_PRESETS_STRICT_FIRST, canonicalPreset } from "./types.js";
|
|
13
14
|
import { isAtLeastAsStrict } from "../permissions/rules.js";
|
|
14
15
|
export const DEFAULT_CONFIG = Object.freeze({
|
|
15
16
|
version: CONFIG_FILE_VERSION,
|
|
@@ -43,6 +44,7 @@ const RULE_SOURCE = {
|
|
|
43
44
|
const ACCUMULATING = new Set([
|
|
44
45
|
"permission.allow",
|
|
45
46
|
"permission.deny",
|
|
47
|
+
"permission.autoSafeCommands",
|
|
46
48
|
"tools.disabled",
|
|
47
49
|
"skills.dirs",
|
|
48
50
|
]);
|
|
@@ -78,11 +80,15 @@ export function mergeConfig(base, over) {
|
|
|
78
80
|
return structuredCloneSafe(base);
|
|
79
81
|
const merged = mergeValue(base, over, "");
|
|
80
82
|
merged.version = CONFIG_FILE_VERSION;
|
|
83
|
+
// 别名(`codemode`)在合并后折成规范名:下游只见规范名。
|
|
84
|
+
if (merged.tools?.preset !== undefined)
|
|
85
|
+
merged.tools.preset = canonicalPreset(merged.tools.preset);
|
|
81
86
|
return merged;
|
|
82
87
|
}
|
|
83
|
-
/** 预设 a 是否比 b 更严或相同(coordinator 最严,codemode
|
|
88
|
+
/** 预设 a 是否比 b 更严或相同(coordinator 最严,codemode-only 最宽;别名按规范名比)。 */
|
|
84
89
|
export function isPresetStricterOrEqual(a, b) {
|
|
85
|
-
return TOOLS_PRESETS_STRICT_FIRST.indexOf(a) <=
|
|
90
|
+
return (TOOLS_PRESETS_STRICT_FIRST.indexOf(canonicalPreset(a)) <=
|
|
91
|
+
TOOLS_PRESETS_STRICT_FIRST.indexOf(canonicalPreset(b)));
|
|
86
92
|
}
|
|
87
93
|
/**
|
|
88
94
|
* 把项目级配置裁剪为受限字段(§7.2);`currentMode` / `currentPreset` 是合并到此为止的
|
|
@@ -155,7 +161,16 @@ function restrictPermission(permission, currentMode, label, warnings) {
|
|
|
155
161
|
if (permission.builtinDeny !== undefined) {
|
|
156
162
|
warnings.push(`${label}: 项目级不能改内置 deny 表,忽略 permission.builtinDeny`);
|
|
157
163
|
}
|
|
158
|
-
if (permission.
|
|
164
|
+
if (permission.autoModel !== undefined) {
|
|
165
|
+
warnings.push(`${label}: 项目级不能设 permission.autoModel,已忽略`);
|
|
166
|
+
}
|
|
167
|
+
if (permission.autoSafeCommands !== undefined && permission.autoSafeCommands.length > 0) {
|
|
168
|
+
warnings.push(`${label}: 项目级不能追加 permission.autoSafeCommands(放宽),已忽略`);
|
|
169
|
+
}
|
|
170
|
+
if (permission.mode === "auto" || permission.mode === "full-auto") {
|
|
171
|
+
warnings.push(`${label}: 项目级不能把权限模式设为 ${permission.mode}(放宽,只能在用户级配置、profile 或命令行设),已忽略`);
|
|
172
|
+
}
|
|
173
|
+
else if (permission.mode !== undefined) {
|
|
159
174
|
if (isAtLeastAsStrict(permission.mode, currentMode)) {
|
|
160
175
|
result.mode = permission.mode;
|
|
161
176
|
}
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import type { AmaConfig, AuthFile, ProfileFile, TrustFile } from "./types.js";
|
|
9
9
|
import type { HookConfig } from "../hooks/types.js";
|
|
10
|
-
export type { AmaConfig, AuthFile, ProfileFile, TrustFile, TrustEntry, ProviderConfig, PermissionConfig, CompactionConfig, RetryConfig, ToolsConfig, ToolsPreset, CodemodeConfig, CodemodeMode, HooksSettings, UiConfig, SkillsConfig, CacheConfig, ModelConfig, ModelOverride, } from "./types.js";
|
|
10
|
+
export type { AmaConfig, AuthFile, ProfileFile, TrustFile, TrustEntry, ProviderConfig, PermissionConfig, CompactionConfig, RetryConfig, ToolsConfig, ToolsPreset, ToolsPresetInput, CodemodeConfig, CodemodeMode, HooksSettings, UiConfig, SkillsConfig, CacheConfig, ModelConfig, ModelOverride, ChannelConfig, } from "./types.js";
|
|
11
11
|
export { CONFIG_FILE_VERSION } from "./types.js";
|
|
12
12
|
export type { HookConfig } from "../hooks/types.js";
|
|
13
13
|
export interface Diagnostic {
|