abelworkflow 0.9.2 → 1.0.0-rc.1
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 +55 -182
- package/bin/abelworkflow.mjs +5 -3
- package/lib/cli/args.mjs +76 -0
- package/lib/cli/main.mjs +387 -0
- package/lib/cli/prompts.mjs +119 -0
- package/lib/config/dotenv.mjs +102 -0
- package/lib/config/jsonc.mjs +80 -0
- package/lib/config/store.mjs +310 -0
- package/lib/config/toml.mjs +638 -0
- package/lib/installer/assets.mjs +326 -0
- package/lib/installer/install.mjs +167 -0
- package/lib/installer/links.mjs +427 -0
- package/lib/installer/render.mjs +46 -0
- package/lib/installer/state.mjs +182 -0
- package/lib/paths.mjs +99 -0
- package/lib/providers/claude.mjs +388 -0
- package/lib/providers/codex.mjs +433 -0
- package/lib/providers/pi.mjs +362 -0
- package/lib/providers/skills.mjs +164 -0
- package/lib/templates/codex/agents/default.toml +7 -0
- package/lib/templates/codex/agents/explorer.toml +7 -0
- package/lib/templates/codex/agents/planner.toml +7 -0
- package/lib/templates/codex/agents/reviewer.toml +7 -0
- package/lib/templates/codex/agents/worker.toml +7 -0
- package/lib/templates/codex/config-base.toml +11 -3
- package/{AGENTS.md → lib/templates/workflow/AGENTS.md} +8 -3
- package/{commands → lib/templates/workflow/commands}/abel-diagnose.md +5 -5
- package/{commands → lib/templates/workflow/commands}/abel-implement.md +3 -8
- package/{commands → lib/templates/workflow/commands}/abel-init.md +1 -1
- package/{commands → lib/templates/workflow/commands}/abel-plan.md +1 -7
- package/{commands → lib/templates/workflow/commands}/abel-research.md +1 -1
- package/{.gitignore → lib/templates/workflow/gitignore.template} +0 -2
- package/lib/tools/cli-installer.mjs +277 -0
- package/package.json +43 -10
- package/skills/context7-auto-research/SKILL.md +5 -5
- package/skills/context7-auto-research/{context7-api.js → context7-api.cjs} +5 -8
- package/skills/dev-browser/SKILL.md +53 -33
- package/skills/dev-browser/dist/scripts/start.d.ts +2 -0
- package/skills/dev-browser/dist/scripts/start.d.ts.map +1 -0
- package/skills/dev-browser/dist/scripts/start.js +118 -0
- package/skills/dev-browser/dist/scripts/start.js.map +1 -0
- package/skills/dev-browser/dist/src/client.d.ts +93 -0
- package/skills/dev-browser/dist/src/client.d.ts.map +1 -0
- package/skills/dev-browser/dist/src/client.js +310 -0
- package/skills/dev-browser/dist/src/client.js.map +1 -0
- package/skills/dev-browser/dist/src/entrypoint.d.ts +29 -0
- package/skills/dev-browser/dist/src/entrypoint.d.ts.map +1 -0
- package/skills/dev-browser/dist/src/entrypoint.js +106 -0
- package/skills/dev-browser/dist/src/entrypoint.js.map +1 -0
- package/skills/dev-browser/dist/src/index.d.ts +4 -0
- package/skills/dev-browser/dist/src/index.d.ts.map +1 -0
- package/skills/dev-browser/dist/src/index.js +2 -0
- package/skills/dev-browser/dist/src/index.js.map +1 -0
- package/skills/dev-browser/dist/src/page-api.d.ts +25 -0
- package/skills/dev-browser/dist/src/page-api.d.ts.map +1 -0
- package/skills/dev-browser/dist/src/page-api.js +104 -0
- package/skills/dev-browser/dist/src/page-api.js.map +1 -0
- package/skills/dev-browser/dist/src/relay.d.ts +27 -0
- package/skills/dev-browser/dist/src/relay.d.ts.map +1 -0
- package/skills/dev-browser/dist/src/relay.js +521 -0
- package/skills/dev-browser/dist/src/relay.js.map +1 -0
- package/skills/dev-browser/dist/src/runtime.d.ts +45 -0
- package/skills/dev-browser/dist/src/runtime.d.ts.map +1 -0
- package/skills/dev-browser/dist/src/runtime.js +54 -0
- package/skills/dev-browser/dist/src/runtime.js.map +1 -0
- package/skills/dev-browser/dist/src/snapshot/browser-script.d.ts +23 -0
- package/skills/dev-browser/dist/src/snapshot/browser-script.d.ts.map +1 -0
- package/skills/dev-browser/{src/snapshot/browser-script.ts → dist/src/snapshot/browser-script.js} +21 -30
- package/skills/dev-browser/dist/src/snapshot/browser-script.js.map +1 -0
- package/skills/dev-browser/dist/src/snapshot/index.d.ts +14 -0
- package/skills/dev-browser/dist/src/snapshot/index.d.ts.map +1 -0
- package/skills/dev-browser/{src/snapshot/index.ts → dist/src/snapshot/index.js} +2 -2
- package/skills/dev-browser/dist/src/snapshot/index.js.map +1 -0
- package/skills/dev-browser/dist/src/snapshot/inject.d.ts +13 -0
- package/skills/dev-browser/dist/src/snapshot/inject.d.ts.map +1 -0
- package/skills/dev-browser/{src/snapshot/inject.ts → dist/src/snapshot/inject.js} +2 -2
- package/skills/dev-browser/dist/src/snapshot/inject.js.map +1 -0
- package/skills/dev-browser/dist/src/standalone.d.ts +32 -0
- package/skills/dev-browser/dist/src/standalone.d.ts.map +1 -0
- package/skills/dev-browser/dist/src/standalone.js +174 -0
- package/skills/dev-browser/dist/src/standalone.js.map +1 -0
- package/skills/dev-browser/dist/src/startup.d.ts +55 -0
- package/skills/dev-browser/dist/src/startup.d.ts.map +1 -0
- package/skills/dev-browser/dist/src/startup.js +81 -0
- package/skills/dev-browser/dist/src/startup.js.map +1 -0
- package/skills/dev-browser/dist/src/target-registry.d.ts +29 -0
- package/skills/dev-browser/dist/src/target-registry.d.ts.map +1 -0
- package/skills/dev-browser/dist/src/target-registry.js +135 -0
- package/skills/dev-browser/dist/src/target-registry.js.map +1 -0
- package/skills/dev-browser/dist/src/types.d.ts +27 -0
- package/skills/dev-browser/dist/src/types.d.ts.map +1 -0
- package/skills/dev-browser/dist/src/types.js +2 -0
- package/skills/dev-browser/dist/src/types.js.map +1 -0
- package/skills/dev-browser/package-lock.json +67 -1510
- package/skills/dev-browser/package.json +18 -14
- package/skills/dev-browser/references/scraping.md +94 -105
- package/skills/grok-search/defaults.json +3 -0
- package/skills/grok-search/gitignore.template +4 -0
- package/skills/grok-search/scripts/_dotenv.py +5 -1
- package/skills/grok-search/scripts/groksearch_cli.py +15 -33
- package/skills/prompt-enhancer/TEMPLATE.md +0 -4
- package/skills/prompt-enhancer/requirements.txt +0 -1
- package/skills/prompt-enhancer/scripts/_dotenv.py +5 -1
- package/skills/prompt-enhancer/scripts/enhance.py +4 -51
- package/skills/prompt-enhancer/scripts/prompt_enhancer_entry.py +1 -16
- package/.skill-lock.json +0 -29
- package/lib/cli/logic.mjs +0 -213
- package/lib/cli.mjs +0 -3016
- package/skills/confidence-check/SKILL.md +0 -34
- package/skills/confidence-check/confidence.ts +0 -276
- package/skills/dev-browser/bun.lock +0 -460
- package/skills/dev-browser/scripts/start.ts +0 -280
- package/skills/dev-browser/src/client.ts +0 -474
- package/skills/dev-browser/src/entrypoint.ts +0 -157
- package/skills/dev-browser/src/index.ts +0 -289
- package/skills/dev-browser/src/relay.ts +0 -731
- package/skills/dev-browser/src/runtime.test.ts +0 -60
- package/skills/dev-browser/src/runtime.ts +0 -171
- package/skills/dev-browser/src/startup.test.ts +0 -95
- package/skills/dev-browser/src/startup.ts +0 -153
- package/skills/dev-browser/src/types.ts +0 -35
- package/skills/dev-browser/tsconfig.json +0 -36
- package/skills/dev-browser/vitest.config.ts +0 -12
- package/skills/sequential-think/SKILL.md +0 -198
- package/skills/sequential-think/scripts/.env.example +0 -5
- package/skills/sequential-think/scripts/sequential_think_cli.py +0 -253
package/lib/paths.mjs
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { realpathSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
|
|
6
|
+
const defaultPackageRoot = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
7
|
+
|
|
8
|
+
function createPaths({
|
|
9
|
+
homeDir = homedir(),
|
|
10
|
+
packageRoot = defaultPackageRoot,
|
|
11
|
+
agentsDir = join(homeDir, ".agents")
|
|
12
|
+
} = {}) {
|
|
13
|
+
const absoluteHomeDir = resolve(homeDir);
|
|
14
|
+
const absolutePackageRoot = resolve(packageRoot);
|
|
15
|
+
const absoluteAgentsDir = resolve(agentsDir);
|
|
16
|
+
assertSeparateSourceAndDeployment(absolutePackageRoot, absoluteAgentsDir);
|
|
17
|
+
|
|
18
|
+
const codexTemplateRoot = join(absolutePackageRoot, "lib", "templates", "codex");
|
|
19
|
+
const workflowTemplateRoot = join(absolutePackageRoot, "lib", "templates", "workflow");
|
|
20
|
+
const piAgentDir = join(absoluteHomeDir, ".pi", "agent");
|
|
21
|
+
return {
|
|
22
|
+
homeDir: absoluteHomeDir,
|
|
23
|
+
packageRoot: absolutePackageRoot,
|
|
24
|
+
agentsDir: absoluteAgentsDir,
|
|
25
|
+
workflowTemplateRoot,
|
|
26
|
+
installMetadataName: ".abelworkflow-install.json",
|
|
27
|
+
claudeSettingsPath: join(absoluteHomeDir, ".claude", "settings.json"),
|
|
28
|
+
claudeMetaConfigPath: join(absoluteHomeDir, ".claude.json"),
|
|
29
|
+
codexConfigPath: join(absoluteHomeDir, ".codex", "config.toml"),
|
|
30
|
+
codexAuthPath: join(absoluteHomeDir, ".codex", "auth.json"),
|
|
31
|
+
codexTemplateConfigPath: join(codexTemplateRoot, "config-base.toml"),
|
|
32
|
+
codexTemplateAgentsPath: join(codexTemplateRoot, "agents"),
|
|
33
|
+
piAgentDir,
|
|
34
|
+
piAuthPath: join(piAgentDir, "auth.json"),
|
|
35
|
+
piModelsPath: join(piAgentDir, "models.json"),
|
|
36
|
+
piSettingsPath: join(piAgentDir, "settings.json")
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function assertSeparateSourceAndDeployment(packageRoot, agentsDir) {
|
|
41
|
+
const canonicalPackageRoot = canonicalizeExistingPath(packageRoot);
|
|
42
|
+
const canonicalAgentsDir = canonicalizeExistingPath(agentsDir);
|
|
43
|
+
if (!containsPath(canonicalPackageRoot, canonicalAgentsDir)
|
|
44
|
+
&& !containsPath(canonicalAgentsDir, canonicalPackageRoot)) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
throw new Error(
|
|
49
|
+
`源码目录 packageRoot (${packageRoot}) 与部署目录 agentsDir (${agentsDir}) 不能相同或互相嵌套。`
|
|
50
|
+
+ "请将源码克隆到独立目录(例如 ~/src/AbelWorkflow),再用 --agents-dir 指向 ~/.agents;"
|
|
51
|
+
+ "请手动迁移已有源码克隆,安装器不会自动移动或删除数据,--force 也不能绕过此限制。"
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function canonicalizeExistingPath(path) {
|
|
56
|
+
const missingParts = [];
|
|
57
|
+
let candidate = path;
|
|
58
|
+
|
|
59
|
+
while (true) {
|
|
60
|
+
try {
|
|
61
|
+
return resolve(realpathSync.native(candidate), ...missingParts);
|
|
62
|
+
} catch (error) {
|
|
63
|
+
const parent = dirname(candidate);
|
|
64
|
+
if (error?.code !== "ENOENT" || parent === candidate) return path;
|
|
65
|
+
missingParts.unshift(basename(candidate));
|
|
66
|
+
candidate = parent;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function containsPath(parent, child, pathOps = { isAbsolute, relative, sep }) {
|
|
72
|
+
const childRelativePath = pathOps.relative(parent, child);
|
|
73
|
+
return childRelativePath === ""
|
|
74
|
+
|| (childRelativePath !== ".."
|
|
75
|
+
&& !childRelativePath.startsWith(`..${pathOps.sep}`)
|
|
76
|
+
&& !pathOps.isAbsolute(childRelativePath));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const defaultPaths = createPaths();
|
|
80
|
+
|
|
81
|
+
function getPlatform() {
|
|
82
|
+
return process.env.ABELWORKFLOW_TEST_PLATFORM || process.platform;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function isWindows() {
|
|
86
|
+
return getPlatform() === "win32";
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function pathToLabel(path, homeDir = defaultPaths.homeDir) {
|
|
90
|
+
return path.replace(homeDir, "~");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function maskSecret(value) {
|
|
94
|
+
if (!value) return "未配置";
|
|
95
|
+
if (value.length <= 4) return "*".repeat(value.length);
|
|
96
|
+
return `${"*".repeat(Math.max(4, value.length - 4))}${value.slice(-4)}`;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export { containsPath, createPaths, defaultPaths, getPlatform, isWindows, maskSecret, pathToLabel };
|
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import {
|
|
3
|
+
pathExists,
|
|
4
|
+
readJsonFileSafe,
|
|
5
|
+
writeJsonFileWithBackup
|
|
6
|
+
} from "../config/store.mjs";
|
|
7
|
+
import { defaultPaths, maskSecret, pathToLabel } from "../paths.mjs";
|
|
8
|
+
|
|
9
|
+
const augmentContextEnginePermission = "mcp__augment-context-engine";
|
|
10
|
+
const claudePermissionProfiles = ["standard", "trusted"];
|
|
11
|
+
const trustedClaudePermissions = [
|
|
12
|
+
"Bash",
|
|
13
|
+
"Skill",
|
|
14
|
+
"LS",
|
|
15
|
+
"Read",
|
|
16
|
+
"Agent",
|
|
17
|
+
"Write",
|
|
18
|
+
"Edit",
|
|
19
|
+
"MultiEdit",
|
|
20
|
+
"Glob",
|
|
21
|
+
"Grep",
|
|
22
|
+
"WebFetch",
|
|
23
|
+
"WebSearch",
|
|
24
|
+
"TodoWrite",
|
|
25
|
+
"NotebookRead",
|
|
26
|
+
"NotebookEdit"
|
|
27
|
+
];
|
|
28
|
+
const claudeModelEnvKeys = [
|
|
29
|
+
"ANTHROPIC_MODEL",
|
|
30
|
+
"ANTHROPIC_DEFAULT_OPUS_MODEL",
|
|
31
|
+
"ANTHROPIC_DEFAULT_SONNET_MODEL",
|
|
32
|
+
"ANTHROPIC_DEFAULT_HAIKU_MODEL",
|
|
33
|
+
"CLAUDE_CODE_SUBAGENT_MODEL"
|
|
34
|
+
];
|
|
35
|
+
const defaultClaudeSettings = {
|
|
36
|
+
$schema: "https://json.schemastore.org/claude-code-settings.json",
|
|
37
|
+
env: {
|
|
38
|
+
DISABLE_TELEMETRY: "1",
|
|
39
|
+
DISABLE_ERROR_REPORTING: "1",
|
|
40
|
+
CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1",
|
|
41
|
+
ANTHROPIC_BASE_URL: "",
|
|
42
|
+
ANTHROPIC_API_KEY: "",
|
|
43
|
+
ANTHROPIC_MODEL: "",
|
|
44
|
+
ANTHROPIC_DEFAULT_OPUS_MODEL: "",
|
|
45
|
+
ANTHROPIC_DEFAULT_SONNET_MODEL: "",
|
|
46
|
+
ANTHROPIC_DEFAULT_HAIKU_MODEL: "",
|
|
47
|
+
CLAUDE_CODE_SUBAGENT_MODEL: ""
|
|
48
|
+
},
|
|
49
|
+
includeCoAuthoredBy: false,
|
|
50
|
+
permissions: {
|
|
51
|
+
allow: [],
|
|
52
|
+
deny: []
|
|
53
|
+
},
|
|
54
|
+
hooks: {},
|
|
55
|
+
alwaysThinkingEnabled: true,
|
|
56
|
+
language: "Chinese"
|
|
57
|
+
};
|
|
58
|
+
function buildDefaultClaudeSettings({ augmentContextEngine = false, profile = "standard" } = {}) {
|
|
59
|
+
if (!claudePermissionProfiles.includes(profile)) {
|
|
60
|
+
throw new Error(`Unknown Claude permission profile: ${profile}`);
|
|
61
|
+
}
|
|
62
|
+
const settings = {
|
|
63
|
+
...defaultClaudeSettings,
|
|
64
|
+
env: { ...defaultClaudeSettings.env },
|
|
65
|
+
permissions: {
|
|
66
|
+
...defaultClaudeSettings.permissions,
|
|
67
|
+
allow: profile === "trusted" ? [...trustedClaudePermissions] : [],
|
|
68
|
+
deny: [...defaultClaudeSettings.permissions.deny]
|
|
69
|
+
},
|
|
70
|
+
hooks: { ...defaultClaudeSettings.hooks }
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
if (augmentContextEngine && !settings.permissions.allow.includes(augmentContextEnginePermission)) {
|
|
74
|
+
settings.permissions.allow.push(augmentContextEnginePermission);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return settings;
|
|
78
|
+
}
|
|
79
|
+
function mergeClaudeSettingsWithDefaults(settings, { augmentContextEngine = false } = {}) {
|
|
80
|
+
const defaults = buildDefaultClaudeSettings({ augmentContextEngine });
|
|
81
|
+
const env = settings?.env && typeof settings.env === "object" ? settings.env : {};
|
|
82
|
+
const permissions = settings?.permissions && typeof settings.permissions === "object" ? settings.permissions : {};
|
|
83
|
+
return {
|
|
84
|
+
...defaults,
|
|
85
|
+
...settings,
|
|
86
|
+
env: {
|
|
87
|
+
...defaults.env,
|
|
88
|
+
...env
|
|
89
|
+
},
|
|
90
|
+
permissions: {
|
|
91
|
+
...defaults.permissions,
|
|
92
|
+
...permissions,
|
|
93
|
+
allow: Array.isArray(permissions.allow) ? permissions.allow : defaults.permissions.allow,
|
|
94
|
+
deny: Array.isArray(permissions.deny) ? permissions.deny : defaults.permissions.deny
|
|
95
|
+
},
|
|
96
|
+
hooks: settings?.hooks && typeof settings.hooks === "object" ? settings.hooks : defaults.hooks
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function applyClaudeInsecureTlsSetting(env = {}, enabled = false) {
|
|
101
|
+
const nextEnv = { ...env };
|
|
102
|
+
if (enabled) {
|
|
103
|
+
nextEnv.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
|
104
|
+
} else if (nextEnv.NODE_TLS_REJECT_UNAUTHORIZED === "0") {
|
|
105
|
+
delete nextEnv.NODE_TLS_REJECT_UNAUTHORIZED;
|
|
106
|
+
}
|
|
107
|
+
return nextEnv;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function getPreviousManagedClaudePermissions(previousMetadata = {}) {
|
|
111
|
+
return Array.isArray(previousMetadata.managedClaudePermissions)
|
|
112
|
+
? previousMetadata.managedClaudePermissions.filter((value) => typeof value === "string")
|
|
113
|
+
: [];
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function applyClaudePermissionProfile(settings, {
|
|
117
|
+
profile = "standard",
|
|
118
|
+
previousManagedPermissions = []
|
|
119
|
+
} = {}) {
|
|
120
|
+
if (!claudePermissionProfiles.includes(profile)) {
|
|
121
|
+
throw new Error(`Unknown Claude permission profile: ${profile}`);
|
|
122
|
+
}
|
|
123
|
+
const hasSettings = settings && typeof settings === "object" && !Array.isArray(settings);
|
|
124
|
+
const nextSettings = hasSettings
|
|
125
|
+
? {
|
|
126
|
+
...settings,
|
|
127
|
+
permissions: settings.permissions && typeof settings.permissions === "object"
|
|
128
|
+
? { ...settings.permissions }
|
|
129
|
+
: {}
|
|
130
|
+
}
|
|
131
|
+
: buildDefaultClaudeSettings();
|
|
132
|
+
const allow = Array.isArray(nextSettings.permissions.allow)
|
|
133
|
+
? [...nextSettings.permissions.allow]
|
|
134
|
+
: [];
|
|
135
|
+
const previousManaged = new Set(previousManagedPermissions.filter((value) => typeof value === "string"));
|
|
136
|
+
const retainedManaged = [];
|
|
137
|
+
|
|
138
|
+
for (const permission of previousManaged) {
|
|
139
|
+
if (permission === augmentContextEnginePermission) {
|
|
140
|
+
if (allow.includes(permission)) retainedManaged.push(permission);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
const index = allow.indexOf(permission);
|
|
144
|
+
if (index !== -1) allow.splice(index, 1);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (profile === "trusted") {
|
|
148
|
+
for (const permission of trustedClaudePermissions) {
|
|
149
|
+
if (allow.includes(permission)) continue;
|
|
150
|
+
allow.push(permission);
|
|
151
|
+
retainedManaged.push(permission);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const previousAllow = Array.isArray(nextSettings.permissions.allow)
|
|
156
|
+
? nextSettings.permissions.allow
|
|
157
|
+
: [];
|
|
158
|
+
nextSettings.permissions.allow = allow;
|
|
159
|
+
return {
|
|
160
|
+
settings: nextSettings,
|
|
161
|
+
changed: !hasSettings || previousAllow.length !== allow.length
|
|
162
|
+
|| previousAllow.some((permission, index) => permission !== allow[index]),
|
|
163
|
+
managedPermissions: [...new Set(retainedManaged)]
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function applyClaudePermissionFeature(settings, {
|
|
168
|
+
augmentContextEngine = false,
|
|
169
|
+
previousManagedPermissions = []
|
|
170
|
+
} = {}) {
|
|
171
|
+
const hasSettings = settings && typeof settings === "object";
|
|
172
|
+
const wasManaged = previousManagedPermissions.includes(augmentContextEnginePermission);
|
|
173
|
+
if (!hasSettings && !augmentContextEngine) {
|
|
174
|
+
return { settings, changed: false, managedPermissions: [] };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const nextSettings = hasSettings
|
|
178
|
+
? {
|
|
179
|
+
...settings,
|
|
180
|
+
permissions: settings.permissions && typeof settings.permissions === "object"
|
|
181
|
+
? { ...settings.permissions }
|
|
182
|
+
: {}
|
|
183
|
+
}
|
|
184
|
+
: { permissions: {} };
|
|
185
|
+
const permissions = nextSettings.permissions;
|
|
186
|
+
const allow = Array.isArray(permissions.allow)
|
|
187
|
+
? [...permissions.allow]
|
|
188
|
+
: [];
|
|
189
|
+
let changed = !hasSettings;
|
|
190
|
+
let isManaged = wasManaged;
|
|
191
|
+
|
|
192
|
+
if (augmentContextEngine) {
|
|
193
|
+
if (!allow.includes(augmentContextEnginePermission)) {
|
|
194
|
+
allow.push(augmentContextEnginePermission);
|
|
195
|
+
changed = true;
|
|
196
|
+
isManaged = true;
|
|
197
|
+
}
|
|
198
|
+
} else if (wasManaged && allow.includes(augmentContextEnginePermission)) {
|
|
199
|
+
allow.splice(allow.indexOf(augmentContextEnginePermission), 1);
|
|
200
|
+
changed = true;
|
|
201
|
+
isManaged = false;
|
|
202
|
+
} else {
|
|
203
|
+
isManaged = false;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
permissions.allow = allow;
|
|
207
|
+
nextSettings.permissions = permissions;
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
settings: nextSettings,
|
|
211
|
+
changed,
|
|
212
|
+
managedPermissions: [
|
|
213
|
+
...previousManagedPermissions.filter((permission) => permission !== augmentContextEnginePermission && allow.includes(permission)),
|
|
214
|
+
...(isManaged ? [augmentContextEnginePermission] : [])
|
|
215
|
+
]
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async function ensureClaudeSettingsForFeature(paths, augmentContextEngine, previousMetadata) {
|
|
220
|
+
const settingsExists = await pathExists(paths.claudeSettingsPath);
|
|
221
|
+
const settings = settingsExists
|
|
222
|
+
? await readJsonFileSafe(paths.claudeSettingsPath, {}, { sensitive: true })
|
|
223
|
+
: undefined;
|
|
224
|
+
const result = applyClaudePermissionFeature(settings, {
|
|
225
|
+
augmentContextEngine,
|
|
226
|
+
previousManagedPermissions: getPreviousManagedClaudePermissions(previousMetadata)
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
if (result.changed && result.settings) {
|
|
230
|
+
await writeJsonFileWithBackup(paths.claudeSettingsPath, result.settings, { sensitive: true });
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return result.managedPermissions;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function getExistingClaudeApiConfig(settings) {
|
|
237
|
+
const env = mergeClaudeSettingsWithDefaults(settings).env;
|
|
238
|
+
return {
|
|
239
|
+
baseUrl: env.ANTHROPIC_BASE_URL || "https://api.anthropic.com",
|
|
240
|
+
authType: env.ANTHROPIC_AUTH_TOKEN ? "auth_token" : "api_key",
|
|
241
|
+
key: env.ANTHROPIC_AUTH_TOKEN || env.ANTHROPIC_API_KEY || "",
|
|
242
|
+
model: claudeModelEnvKeys.map((field) => env[field]).find(Boolean) || "",
|
|
243
|
+
insecureTls: env.NODE_TLS_REJECT_UNAUTHORIZED === "0"
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function ensureApprovedClaudeApiKey(config, apiKey) {
|
|
248
|
+
if (!apiKey) {
|
|
249
|
+
return config;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const truncated = apiKey.slice(0, 20);
|
|
253
|
+
if (!config.customApiKeyResponses || typeof config.customApiKeyResponses !== "object") {
|
|
254
|
+
config.customApiKeyResponses = { approved: [], rejected: [] };
|
|
255
|
+
}
|
|
256
|
+
if (!Array.isArray(config.customApiKeyResponses.approved)) {
|
|
257
|
+
config.customApiKeyResponses.approved = [];
|
|
258
|
+
}
|
|
259
|
+
if (!Array.isArray(config.customApiKeyResponses.rejected)) {
|
|
260
|
+
config.customApiKeyResponses.rejected = [];
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
config.customApiKeyResponses.rejected = config.customApiKeyResponses.rejected.filter((item) => item !== truncated);
|
|
264
|
+
if (!config.customApiKeyResponses.approved.includes(truncated)) {
|
|
265
|
+
config.customApiKeyResponses.approved.push(truncated);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return config;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function buildClaudeApiSettings(settings, {
|
|
272
|
+
authType,
|
|
273
|
+
baseUrl,
|
|
274
|
+
key,
|
|
275
|
+
model,
|
|
276
|
+
insecureTls = false
|
|
277
|
+
}) {
|
|
278
|
+
const nextSettings = mergeClaudeSettingsWithDefaults(settings);
|
|
279
|
+
nextSettings.env = applyClaudeInsecureTlsSetting(
|
|
280
|
+
nextSettings.env && typeof nextSettings.env === "object" ? nextSettings.env : {},
|
|
281
|
+
insecureTls
|
|
282
|
+
);
|
|
283
|
+
nextSettings.env.ANTHROPIC_BASE_URL = baseUrl;
|
|
284
|
+
if (authType === "auth_token") {
|
|
285
|
+
nextSettings.env.ANTHROPIC_AUTH_TOKEN = key;
|
|
286
|
+
delete nextSettings.env.ANTHROPIC_API_KEY;
|
|
287
|
+
} else {
|
|
288
|
+
nextSettings.env.ANTHROPIC_API_KEY = key;
|
|
289
|
+
delete nextSettings.env.ANTHROPIC_AUTH_TOKEN;
|
|
290
|
+
}
|
|
291
|
+
for (const field of claudeModelEnvKeys) {
|
|
292
|
+
nextSettings.env[field] = model;
|
|
293
|
+
}
|
|
294
|
+
return nextSettings;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
async function persistClaudeConfiguration(paths, { settings, metaConfig }) {
|
|
298
|
+
await writeJsonFileWithBackup(paths.claudeSettingsPath, settings, { sensitive: true });
|
|
299
|
+
await writeJsonFileWithBackup(paths.claudeMetaConfigPath, metaConfig, { sensitive: true });
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
async function configureClaudeApi(paths = defaultPaths, promptApi, options = {}) {
|
|
303
|
+
const {
|
|
304
|
+
assertNotCancelled,
|
|
305
|
+
confirmOrCancel,
|
|
306
|
+
required,
|
|
307
|
+
requiredUnlessExisting,
|
|
308
|
+
resolvePasswordValue,
|
|
309
|
+
selectOrCancel
|
|
310
|
+
} = promptApi;
|
|
311
|
+
const settings = await readJsonFileSafe(paths.claudeSettingsPath, {}, { sensitive: true });
|
|
312
|
+
const existing = getExistingClaudeApiConfig(settings);
|
|
313
|
+
const authType = await selectOrCancel({
|
|
314
|
+
message: "Claude Code 第三方 API 认证方式",
|
|
315
|
+
options: [
|
|
316
|
+
{ value: "api_key", label: "API Key" },
|
|
317
|
+
{ value: "auth_token", label: "Auth Token" }
|
|
318
|
+
],
|
|
319
|
+
initialValue: existing.authType
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
const baseUrl = await p.text({
|
|
323
|
+
message: "Claude Code Base URL",
|
|
324
|
+
defaultValue: existing.baseUrl,
|
|
325
|
+
validate: required()
|
|
326
|
+
});
|
|
327
|
+
assertNotCancelled(baseUrl);
|
|
328
|
+
|
|
329
|
+
const key = await p.password({
|
|
330
|
+
message: authType === "auth_token" ? "Claude Code Auth Token(输入 - 清除)" : "Claude Code API Key(输入 - 清除)",
|
|
331
|
+
mask: "*",
|
|
332
|
+
defaultValue: existing.key || undefined,
|
|
333
|
+
validate: requiredUnlessExisting(existing.key, "API Key / Auth Token 不能为空")
|
|
334
|
+
});
|
|
335
|
+
assertNotCancelled(key);
|
|
336
|
+
const finalKey = resolvePasswordValue(key, existing.key);
|
|
337
|
+
|
|
338
|
+
const insecureTls = await confirmOrCancel({
|
|
339
|
+
message: "是否跳过 Claude Code TLS 证书校验?仅证书无法修复时启用(会放宽该进程全部 HTTPS 请求)",
|
|
340
|
+
initialValue: existing.insecureTls
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
const model = await p.text({
|
|
344
|
+
message: "Claude Code 模型",
|
|
345
|
+
defaultValue: existing.model || undefined,
|
|
346
|
+
validate: required()
|
|
347
|
+
});
|
|
348
|
+
assertNotCancelled(model);
|
|
349
|
+
|
|
350
|
+
const apiSettings = buildClaudeApiSettings(settings, {
|
|
351
|
+
authType,
|
|
352
|
+
baseUrl,
|
|
353
|
+
key: finalKey,
|
|
354
|
+
model,
|
|
355
|
+
insecureTls
|
|
356
|
+
});
|
|
357
|
+
const profileResult = applyClaudePermissionProfile(apiSettings, {
|
|
358
|
+
profile: options.permissionProfile ?? "standard",
|
|
359
|
+
previousManagedPermissions: options.previousManagedPermissions ?? []
|
|
360
|
+
});
|
|
361
|
+
const metaConfig = await readJsonFileSafe(paths.claudeMetaConfigPath, {}, { sensitive: true });
|
|
362
|
+
metaConfig.hasCompletedOnboarding = true;
|
|
363
|
+
ensureApprovedClaudeApiKey(metaConfig, finalKey);
|
|
364
|
+
await persistClaudeConfiguration(paths, {
|
|
365
|
+
settings: profileResult.settings,
|
|
366
|
+
metaConfig
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
p.log.step(`已更新 ${pathToLabel(paths.claudeSettingsPath, paths.homeDir)} (${authType}, ${baseUrl}, ${maskSecret(finalKey)}, ${options.permissionProfile ?? "standard"})`);
|
|
370
|
+
return {
|
|
371
|
+
managedPermissions: profileResult.managedPermissions,
|
|
372
|
+
permissionProfile: options.permissionProfile ?? "standard"
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
export {
|
|
378
|
+
applyClaudeInsecureTlsSetting,
|
|
379
|
+
applyClaudePermissionFeature,
|
|
380
|
+
applyClaudePermissionProfile,
|
|
381
|
+
buildClaudeApiSettings,
|
|
382
|
+
buildDefaultClaudeSettings,
|
|
383
|
+
configureClaudeApi,
|
|
384
|
+
ensureClaudeSettingsForFeature,
|
|
385
|
+
getPreviousManagedClaudePermissions,
|
|
386
|
+
mergeClaudeSettingsWithDefaults,
|
|
387
|
+
persistClaudeConfiguration
|
|
388
|
+
};
|