abelworkflow 1.0.0-rc.1 → 1.0.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.
@@ -1,30 +1,10 @@
1
1
  import * as p from "@clack/prompts";
2
2
  import {
3
- pathExists,
4
3
  readJsonFileSafe,
5
4
  writeJsonFileWithBackup
6
5
  } from "../config/store.mjs";
7
6
  import { defaultPaths, maskSecret, pathToLabel } from "../paths.mjs";
8
7
 
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
8
  const claudeModelEnvKeys = [
29
9
  "ANTHROPIC_MODEL",
30
10
  "ANTHROPIC_DEFAULT_OPUS_MODEL",
@@ -37,47 +17,32 @@ const defaultClaudeSettings = {
37
17
  env: {
38
18
  DISABLE_TELEMETRY: "1",
39
19
  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: ""
20
+ CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: "1"
48
21
  },
49
22
  includeCoAuthoredBy: false,
50
23
  permissions: {
51
24
  allow: [],
52
- deny: []
25
+ deny: [],
26
+ defaultMode: "bypassPermissions"
53
27
  },
54
28
  hooks: {},
55
29
  alwaysThinkingEnabled: true,
56
30
  language: "Chinese"
57
31
  };
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 = {
32
+ function buildDefaultClaudeSettings() {
33
+ return {
63
34
  ...defaultClaudeSettings,
64
35
  env: { ...defaultClaudeSettings.env },
65
36
  permissions: {
66
37
  ...defaultClaudeSettings.permissions,
67
- allow: profile === "trusted" ? [...trustedClaudePermissions] : [],
38
+ allow: [...defaultClaudeSettings.permissions.allow],
68
39
  deny: [...defaultClaudeSettings.permissions.deny]
69
40
  },
70
41
  hooks: { ...defaultClaudeSettings.hooks }
71
42
  };
72
-
73
- if (augmentContextEngine && !settings.permissions.allow.includes(augmentContextEnginePermission)) {
74
- settings.permissions.allow.push(augmentContextEnginePermission);
75
- }
76
-
77
- return settings;
78
43
  }
79
- function mergeClaudeSettingsWithDefaults(settings, { augmentContextEngine = false } = {}) {
80
- const defaults = buildDefaultClaudeSettings({ augmentContextEngine });
44
+ function mergeClaudeSettingsWithDefaults(settings) {
45
+ const defaults = buildDefaultClaudeSettings();
81
46
  const env = settings?.env && typeof settings.env === "object" ? settings.env : {};
82
47
  const permissions = settings?.permissions && typeof settings.permissions === "object" ? settings.permissions : {};
83
48
  return {
@@ -107,138 +72,11 @@ function applyClaudeInsecureTlsSetting(env = {}, enabled = false) {
107
72
  return nextEnv;
108
73
  }
109
74
 
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
75
  function getExistingClaudeApiConfig(settings) {
237
76
  const env = mergeClaudeSettingsWithDefaults(settings).env;
238
77
  return {
239
78
  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 || "",
79
+ key: env.ANTHROPIC_API_KEY || "",
242
80
  model: claudeModelEnvKeys.map((field) => env[field]).find(Boolean) || "",
243
81
  insecureTls: env.NODE_TLS_REJECT_UNAUTHORIZED === "0"
244
82
  };
@@ -269,7 +107,6 @@ function ensureApprovedClaudeApiKey(config, apiKey) {
269
107
  }
270
108
 
271
109
  function buildClaudeApiSettings(settings, {
272
- authType,
273
110
  baseUrl,
274
111
  key,
275
112
  model,
@@ -281,13 +118,8 @@ function buildClaudeApiSettings(settings, {
281
118
  insecureTls
282
119
  );
283
120
  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
- }
121
+ nextSettings.env.ANTHROPIC_API_KEY = key;
122
+ delete nextSettings.env.ANTHROPIC_AUTH_TOKEN;
291
123
  for (const field of claudeModelEnvKeys) {
292
124
  nextSettings.env[field] = model;
293
125
  }
@@ -299,39 +131,30 @@ async function persistClaudeConfiguration(paths, { settings, metaConfig }) {
299
131
  await writeJsonFileWithBackup(paths.claudeMetaConfigPath, metaConfig, { sensitive: true });
300
132
  }
301
133
 
302
- async function configureClaudeApi(paths = defaultPaths, promptApi, options = {}) {
134
+ async function configureClaudeApi(paths = defaultPaths, promptApi) {
303
135
  const {
304
136
  assertNotCancelled,
305
137
  confirmOrCancel,
138
+ passwordPromptOptions,
306
139
  required,
307
140
  requiredUnlessExisting,
308
- resolvePasswordValue,
309
- selectOrCancel
141
+ resolvePasswordValue
310
142
  } = promptApi;
311
143
  const settings = await readJsonFileSafe(paths.claudeSettingsPath, {}, { sensitive: true });
312
144
  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
145
 
322
146
  const baseUrl = await p.text({
323
147
  message: "Claude Code Base URL",
324
- defaultValue: existing.baseUrl,
148
+ initialValue: existing.baseUrl,
325
149
  validate: required()
326
150
  });
327
151
  assertNotCancelled(baseUrl);
328
152
 
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
- });
153
+ const key = await p.password(passwordPromptOptions(
154
+ "Claude Code API Key",
155
+ existing.key,
156
+ requiredUnlessExisting(existing.key, "API Key 不能为空")
157
+ ));
335
158
  assertNotCancelled(key);
336
159
  const finalKey = resolvePasswordValue(key, existing.key);
337
160
 
@@ -342,47 +165,33 @@ async function configureClaudeApi(paths = defaultPaths, promptApi, options = {})
342
165
 
343
166
  const model = await p.text({
344
167
  message: "Claude Code 模型",
345
- defaultValue: existing.model || undefined,
168
+ initialValue: existing.model || undefined,
346
169
  validate: required()
347
170
  });
348
171
  assertNotCancelled(model);
349
172
 
350
173
  const apiSettings = buildClaudeApiSettings(settings, {
351
- authType,
352
174
  baseUrl,
353
175
  key: finalKey,
354
176
  model,
355
177
  insecureTls
356
178
  });
357
- const profileResult = applyClaudePermissionProfile(apiSettings, {
358
- profile: options.permissionProfile ?? "standard",
359
- previousManagedPermissions: options.previousManagedPermissions ?? []
360
- });
361
179
  const metaConfig = await readJsonFileSafe(paths.claudeMetaConfigPath, {}, { sensitive: true });
362
180
  metaConfig.hasCompletedOnboarding = true;
363
181
  ensureApprovedClaudeApiKey(metaConfig, finalKey);
364
182
  await persistClaudeConfiguration(paths, {
365
- settings: profileResult.settings,
183
+ settings: apiSettings,
366
184
  metaConfig
367
185
  });
368
186
 
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
- };
187
+ p.log.step(`已更新 ${pathToLabel(paths.claudeSettingsPath, paths.homeDir)} (${baseUrl}, ${maskSecret(finalKey)})`);
374
188
  }
375
189
 
376
-
377
190
  export {
378
191
  applyClaudeInsecureTlsSetting,
379
- applyClaudePermissionFeature,
380
- applyClaudePermissionProfile,
381
192
  buildClaudeApiSettings,
382
193
  buildDefaultClaudeSettings,
383
194
  configureClaudeApi,
384
- ensureClaudeSettingsForFeature,
385
- getPreviousManagedClaudePermissions,
386
195
  mergeClaudeSettingsWithDefaults,
387
196
  persistClaudeConfiguration
388
197
  };
@@ -304,6 +304,7 @@ async function configureCodexApi(paths = defaultPaths, promptApi, ownership = {}
304
304
  const {
305
305
  assertNotCancelled,
306
306
  confirmOrCancel,
307
+ passwordPromptOptions,
307
308
  required,
308
309
  requiredUnlessExisting,
309
310
  resolvePasswordValue
@@ -313,18 +314,17 @@ async function configureCodexApi(paths = defaultPaths, promptApi, ownership = {}
313
314
  const providerName = existing.providerName || providerId;
314
315
  const baseUrlInput = await p.text({
315
316
  message: "Codex Base URL",
316
- defaultValue: existing.baseUrl,
317
+ initialValue: existing.baseUrl,
317
318
  validate: required()
318
319
  });
319
320
  assertNotCancelled(baseUrlInput);
320
321
  const baseUrl = normalizeOpenAiBaseUrl(baseUrlInput);
321
322
 
322
- const apiKey = await p.password({
323
- message: "Codex 第三方 API Key(输入 - 清除)",
324
- mask: "*",
325
- defaultValue: existing.apiKey || undefined,
326
- validate: requiredUnlessExisting(existing.apiKey, "API Key 不能为空")
327
- });
323
+ const apiKey = await p.password(passwordPromptOptions(
324
+ "Codex 第三方 API Key",
325
+ existing.apiKey,
326
+ requiredUnlessExisting(existing.apiKey, "API Key 不能为空")
327
+ ));
328
328
  assertNotCancelled(apiKey);
329
329
  const finalApiKey = resolvePasswordValue(apiKey, existing.apiKey);
330
330