abelworkflow 1.1.0 → 1.1.2

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,8 +1,5 @@
1
1
  import * as p from "@clack/prompts";
2
- import {
3
- readJsonFileSafe,
4
- writeJsonFileWithBackup
5
- } from "../config/store.mjs";
2
+ import { readJsonFileSafe, writeJson } from "../config/store.mjs";
6
3
  import { defaultPaths, maskSecret, pathToLabel } from "../paths.mjs";
7
4
 
8
5
  const claudeModelEnvKeys = [
@@ -62,23 +59,12 @@ function mergeClaudeSettingsWithDefaults(settings) {
62
59
  };
63
60
  }
64
61
 
65
- function applyClaudeInsecureTlsSetting(env = {}, enabled = false) {
66
- const nextEnv = { ...env };
67
- if (enabled) {
68
- nextEnv.NODE_TLS_REJECT_UNAUTHORIZED = "0";
69
- } else if (nextEnv.NODE_TLS_REJECT_UNAUTHORIZED === "0") {
70
- delete nextEnv.NODE_TLS_REJECT_UNAUTHORIZED;
71
- }
72
- return nextEnv;
73
- }
74
-
75
62
  function getExistingClaudeApiConfig(settings) {
76
63
  const env = mergeClaudeSettingsWithDefaults(settings).env;
77
64
  return {
78
65
  baseUrl: env.ANTHROPIC_BASE_URL || "https://api.anthropic.com",
79
66
  key: env.ANTHROPIC_API_KEY || "",
80
- model: claudeModelEnvKeys.map((field) => env[field]).find(Boolean) || "",
81
- insecureTls: env.NODE_TLS_REJECT_UNAUTHORIZED === "0"
67
+ model: claudeModelEnvKeys.map((field) => env[field]).find(Boolean) || ""
82
68
  };
83
69
  }
84
70
 
@@ -109,14 +95,9 @@ function ensureApprovedClaudeApiKey(config, apiKey) {
109
95
  function buildClaudeApiSettings(settings, {
110
96
  baseUrl,
111
97
  key,
112
- model,
113
- insecureTls = false
98
+ model
114
99
  }) {
115
100
  const nextSettings = mergeClaudeSettingsWithDefaults(settings);
116
- nextSettings.env = applyClaudeInsecureTlsSetting(
117
- nextSettings.env && typeof nextSettings.env === "object" ? nextSettings.env : {},
118
- insecureTls
119
- );
120
101
  nextSettings.env.ANTHROPIC_BASE_URL = baseUrl;
121
102
  nextSettings.env.ANTHROPIC_API_KEY = key;
122
103
  delete nextSettings.env.ANTHROPIC_AUTH_TOKEN;
@@ -127,19 +108,12 @@ function buildClaudeApiSettings(settings, {
127
108
  }
128
109
 
129
110
  async function persistClaudeConfiguration(paths, { settings, metaConfig }) {
130
- await writeJsonFileWithBackup(paths.claudeSettingsPath, settings, { sensitive: true });
131
- await writeJsonFileWithBackup(paths.claudeMetaConfigPath, metaConfig, { sensitive: true });
111
+ await writeJson(paths.claudeSettingsPath, settings, { sensitive: true });
112
+ await writeJson(paths.claudeMetaConfigPath, metaConfig, { sensitive: true });
132
113
  }
133
114
 
134
115
  async function configureClaudeApi(paths = defaultPaths, promptApi) {
135
- const {
136
- assertNotCancelled,
137
- confirmOrCancel,
138
- passwordPromptOptions,
139
- required,
140
- requiredUnlessExisting,
141
- resolvePasswordValue
142
- } = promptApi;
116
+ const { assertNotCancelled, passwordOrExisting, required } = promptApi;
143
117
  const settings = await readJsonFileSafe(paths.claudeSettingsPath, {}, { sensitive: true });
144
118
  const existing = getExistingClaudeApiConfig(settings);
145
119
 
@@ -150,17 +124,9 @@ async function configureClaudeApi(paths = defaultPaths, promptApi) {
150
124
  });
151
125
  assertNotCancelled(baseUrl);
152
126
 
153
- const key = await p.password(passwordPromptOptions(
154
- "Claude Code API Key",
155
- existing.key,
156
- requiredUnlessExisting(existing.key, "API Key 不能为空")
157
- ));
158
- assertNotCancelled(key);
159
- const finalKey = resolvePasswordValue(key, existing.key);
160
-
161
- const insecureTls = await confirmOrCancel({
162
- message: "是否跳过 Claude Code TLS 证书校验?仅证书无法修复时启用(会放宽该进程全部 HTTPS 请求)",
163
- initialValue: existing.insecureTls
127
+ const finalKey = await passwordOrExisting({
128
+ message: "Claude Code API Key",
129
+ existingValue: existing.key
164
130
  });
165
131
 
166
132
  const model = await p.text({
@@ -173,8 +139,7 @@ async function configureClaudeApi(paths = defaultPaths, promptApi) {
173
139
  const apiSettings = buildClaudeApiSettings(settings, {
174
140
  baseUrl,
175
141
  key: finalKey,
176
- model,
177
- insecureTls
142
+ model
178
143
  });
179
144
  const metaConfig = await readJsonFileSafe(paths.claudeMetaConfigPath, {}, { sensitive: true });
180
145
  metaConfig.hasCompletedOnboarding = true;
@@ -188,7 +153,6 @@ async function configureClaudeApi(paths = defaultPaths, promptApi) {
188
153
  }
189
154
 
190
155
  export {
191
- applyClaudeInsecureTlsSetting,
192
156
  buildClaudeApiSettings,
193
157
  buildDefaultClaudeSettings,
194
158
  configureClaudeApi,
@@ -1,11 +1,10 @@
1
- import { createHash } from "node:crypto";
2
1
  import { lstat, mkdir, readFile, readdir, unlink } from "node:fs/promises";
3
2
  import { join } from "node:path";
4
3
  import * as p from "@clack/prompts";
5
4
  import {
6
5
  pathExists,
7
6
  readJsonFileSafe,
8
- writeJsonFileWithBackup,
7
+ writeJson,
9
8
  writeText
10
9
  } from "../config/store.mjs";
11
10
  import {
@@ -18,11 +17,13 @@ import {
18
17
  parseTomlSection,
19
18
  readTopLevelTomlString,
20
19
  removeTomlSection,
20
+ removeTomlSectionField,
21
21
  removeTopLevelTomlField,
22
22
  updateTomlSectionFields,
23
23
  updateTopLevelTomlField
24
24
  } from "../config/toml.mjs";
25
25
  import { defaultPaths, maskSecret, pathToLabel } from "../paths.mjs";
26
+ import { hashBytes, isManagedCodexAgentFileEntry } from "../utils.mjs";
26
27
  import { normalizeOpenAiBaseUrl } from "./pi.mjs";
27
28
 
28
29
  const CODEX_ENV_KEY = "OPENAI_API_KEY";
@@ -98,7 +99,7 @@ function mergeCodexTemplateDefaults(content, templateContent) {
98
99
  const currentDeveloperInstructions = readTopLevelTomlString(nextContent, "developer_instructions");
99
100
  const templateDeveloperInstructions = readTopLevelTomlString(templateContent, "developer_instructions");
100
101
  if (templateDeveloperInstructions && publishedCodexDeveloperInstructionHashes.has(
101
- sha256(currentDeveloperInstructions)
102
+ hashBytes(currentDeveloperInstructions)
102
103
  )) {
103
104
  nextContent = updateTopLevelTomlField(
104
105
  nextContent,
@@ -125,10 +126,6 @@ async function loadBundledCodexConfigTemplate(paths = defaultPaths) {
125
126
  return readFile(paths.codexTemplateConfigPath, "utf8");
126
127
  }
127
128
 
128
- function sha256(content) {
129
- return createHash("sha256").update(content).digest("hex");
130
- }
131
-
132
129
  async function readCodexAgentTarget(path) {
133
130
  try {
134
131
  const targetStat = await lstat(path);
@@ -175,14 +172,6 @@ async function ensureCodexAgentContainer(homeDir) {
175
172
  return targetDir;
176
173
  }
177
174
 
178
- function isCodexAgentOwnership(name, hash) {
179
- return name.endsWith(".toml")
180
- && !name.includes("/")
181
- && !name.includes("\\")
182
- && typeof hash === "string"
183
- && /^[a-f0-9]{64}$/u.test(hash);
184
- }
185
-
186
175
  function isPublishedCodexAgent(name, hash) {
187
176
  return publishedCodexAgentHashes[name]?.has(hash) ?? false;
188
177
  }
@@ -205,9 +194,9 @@ async function deployBundledCodexAgents(paths = defaultPaths, previousManagedFil
205
194
  const source = join(paths.codexTemplateAgentsPath, name);
206
195
  const target = join(targetDir, name);
207
196
  const sourceContent = await readFile(source);
208
- const sourceHash = sha256(sourceContent);
197
+ const sourceHash = hashBytes(sourceContent);
209
198
  const current = await readCodexAgentTarget(target);
210
- const currentHash = current.content ? sha256(current.content) : "";
199
+ const currentHash = current.content ? hashBytes(current.content) : "";
211
200
 
212
201
  if (!current.exists) {
213
202
  await writeText(target, sourceContent, { backupLimit: 0 });
@@ -222,7 +211,7 @@ async function deployBundledCodexAgents(paths = defaultPaths, previousManagedFil
222
211
  result.updated.push(target);
223
212
  } else {
224
213
  result.conflicts.push(target);
225
- if (isCodexAgentOwnership(name, previousManagedFiles?.[name])) {
214
+ if (isManagedCodexAgentFileEntry(name, previousManagedFiles?.[name])) {
226
215
  result.managedFiles[name] = previousManagedFiles[name];
227
216
  }
228
217
  continue;
@@ -237,11 +226,11 @@ async function deployBundledCodexAgents(paths = defaultPaths, previousManagedFil
237
226
  ? Object.entries(previousManagedFiles).sort(([left], [right]) => left.localeCompare(right))
238
227
  : [];
239
228
  for (const [name, previousHash] of previousEntries) {
240
- if (bundledNames.has(name) || !isCodexAgentOwnership(name, previousHash)) continue;
229
+ if (bundledNames.has(name) || !isManagedCodexAgentFileEntry(name, previousHash)) continue;
241
230
  const target = join(targetDir, name);
242
231
  const current = await readCodexAgentTarget(target);
243
232
  if (!current.exists) continue;
244
- if (current.content && sha256(current.content) === previousHash) {
233
+ if (current.content && hashBytes(current.content) === previousHash) {
245
234
  await unlink(target);
246
235
  continue;
247
236
  }
@@ -291,17 +280,15 @@ async function getExistingCodexApiConfig(paths = defaultPaths) {
291
280
 
292
281
  async function persistCodexConfiguration(paths, { content, auth }) {
293
282
  await writeText(paths.codexConfigPath, content);
294
- await writeJsonFileWithBackup(paths.codexAuthPath, auth, { sensitive: true });
283
+ await writeJson(paths.codexAuthPath, auth, { sensitive: true });
295
284
  }
296
285
 
297
286
  async function configureCodexApi(paths = defaultPaths, promptApi, ownership = {}) {
298
287
  const {
299
288
  assertNotCancelled,
300
289
  confirmOrCancel,
301
- passwordPromptOptions,
302
- required,
303
- requiredUnlessExisting,
304
- resolvePasswordValue
290
+ passwordOrExisting,
291
+ required
305
292
  } = promptApi;
306
293
  const existing = await getExistingCodexApiConfig(paths);
307
294
  const providerId = existing.providerId || "abelworkflow";
@@ -314,13 +301,10 @@ async function configureCodexApi(paths = defaultPaths, promptApi, ownership = {}
314
301
  assertNotCancelled(baseUrlInput);
315
302
  const baseUrl = normalizeOpenAiBaseUrl(baseUrlInput);
316
303
 
317
- const apiKey = await p.password(passwordPromptOptions(
318
- "Codex 第三方 API Key",
319
- existing.apiKey,
320
- requiredUnlessExisting(existing.apiKey, "API Key 不能为空")
321
- ));
322
- assertNotCancelled(apiKey);
323
- const finalApiKey = resolvePasswordValue(apiKey, existing.apiKey);
304
+ const finalApiKey = await passwordOrExisting({
305
+ message: "Codex 第三方 API Key",
306
+ existingValue: existing.apiKey
307
+ });
324
308
 
325
309
  const shouldDeploySubagents = await confirmOrCancel({ message: "是否部署 Codex subagents 配置?", initialValue: true });
326
310
  let managedCodexAgentFiles = { ...(ownership.managedCodexAgentFiles ?? {}) };
@@ -389,13 +373,16 @@ function buildCodexConfigContent(currentContent, {
389
373
  if (includeSubagentDefaults && readTopLevelTomlString(content, "approvals_reviewer") === "reviewer") {
390
374
  content = updateTopLevelTomlField(content, "approvals_reviewer", "guardian_subagent");
391
375
  }
376
+ const providerSectionName = getCodexProviderSectionName(providerId);
392
377
  content = updateTopLevelTomlField(content, "model_provider", providerId);
393
- content = updateTopLevelTomlField(content, "preferred_auth_method", "apikey");
394
- content = updateTomlSectionFields(content, getCodexProviderSectionName(providerId), {
378
+ content = removeTopLevelTomlField(content, "preferred_auth_method");
379
+ content = removeTopLevelTomlField(content, "temp_env_key");
380
+ content = removeTomlSectionField(content, providerSectionName, "temp_env_key");
381
+ content = removeTomlSectionField(content, providerSectionName, "env_key");
382
+ content = updateTomlSectionFields(content, providerSectionName, {
395
383
  name: providerName,
396
384
  base_url: baseUrl,
397
385
  wire_api: "responses",
398
- temp_env_key: CODEX_ENV_KEY,
399
386
  requires_openai_auth: true,
400
387
  supports_websockets: true
401
388
  });
@@ -408,8 +395,13 @@ function mergeCodexAuthData(auth, envKey, apiKey, managedAuthKeys = []) {
408
395
  for (const managedAuthKey of managedAuthKeys) {
409
396
  delete nextAuth[managedAuthKey];
410
397
  }
411
- if (apiKey) nextAuth[envKey] = apiKey;
412
- else delete nextAuth[envKey];
398
+ if (apiKey) {
399
+ nextAuth.auth_mode = "apikey";
400
+ nextAuth[envKey] = apiKey;
401
+ } else {
402
+ delete nextAuth[envKey];
403
+ if (nextAuth.auth_mode === "apikey") delete nextAuth.auth_mode;
404
+ }
413
405
  return nextAuth;
414
406
  }
415
407
 
@@ -1,12 +1,11 @@
1
1
  import { spawn, spawnSync } from "node:child_process";
2
2
  import { join } from "node:path";
3
3
  import * as p from "@clack/prompts";
4
- import { piInsecureTlsHeader } from "../../extensions/pi-gpt-responses-compat/tls-fetch.mjs";
5
4
  import {
6
5
  readJsonFileSafe,
7
6
  readJsoncFileSafe,
8
7
  updateLockedJson,
9
- writeJsonFileWithBackup,
8
+ writeJson,
10
9
  writeText
11
10
  } from "../config/store.mjs";
12
11
  import { defaultPaths, maskSecret, pathToLabel } from "../paths.mjs";
@@ -310,36 +309,17 @@ function buildPiModelConfig(modelId, existingModel = {}) {
310
309
  };
311
310
  }
312
311
 
313
- function hasPiInsecureTlsSetting(modelsConfig = {}, providerId) {
314
- const headers = modelsConfig.providers?.[requirePiProviderId(providerId)]?.headers;
315
- return headers && typeof headers === "object"
316
- ? Object.keys(headers).some((key) => key.toLowerCase() === piInsecureTlsHeader)
317
- : false;
318
- }
319
-
320
312
  function buildPiModelsConfig(modelsConfig = {}, {
321
313
  providerId,
322
314
  baseUrl,
323
315
  api,
324
- modelIds,
325
- insecureTls = false
316
+ modelIds
326
317
  }) {
327
318
  const targetProviderId = requirePiProviderId(providerId);
328
319
  const providers = modelsConfig.providers && typeof modelsConfig.providers === "object" ? modelsConfig.providers : {};
329
320
  const currentProvider = providers[targetProviderId] && typeof providers[targetProviderId] === "object"
330
321
  ? providers[targetProviderId]
331
322
  : {};
332
- const headers = currentProvider.headers && typeof currentProvider.headers === "object"
333
- ? { ...currentProvider.headers }
334
- : {};
335
- for (const key of Object.keys(headers)) {
336
- if (key.toLowerCase() === piInsecureTlsHeader) {
337
- delete headers[key];
338
- }
339
- }
340
- if (insecureTls) {
341
- headers[piInsecureTlsHeader] = new URL(baseUrl).origin;
342
- }
343
323
  const existingModels = new Map(
344
324
  (Array.isArray(currentProvider.models) ? currentProvider.models : [])
345
325
  .filter((model) => model?.id)
@@ -357,11 +337,6 @@ function buildPiModelsConfig(modelsConfig = {}, {
357
337
  models: modelIds.map((modelId) => buildPiModelConfig(modelId, existingModels.get(modelId)))
358
338
  };
359
339
  delete provider.apiKey;
360
- if (Object.keys(headers).length) {
361
- provider.headers = headers;
362
- } else {
363
- delete provider.headers;
364
- }
365
340
 
366
341
  return {
367
342
  ...modelsConfig,
@@ -413,7 +388,6 @@ function buildPiConfiguration({ auth, models, settings }, {
413
388
  baseUrl,
414
389
  api,
415
390
  modelIds,
416
- insecureTls,
417
391
  defaultModel
418
392
  }) {
419
393
  const targetProviderId = requirePiProviderId(providerId);
@@ -425,8 +399,7 @@ function buildPiConfiguration({ auth, models, settings }, {
425
399
  providerId: targetProviderId,
426
400
  baseUrl,
427
401
  api,
428
- modelIds,
429
- insecureTls
402
+ modelIds
430
403
  }),
431
404
  settings: buildPiSettingsConfig(settings, targetProviderId, defaultModel)
432
405
  };
@@ -439,7 +412,7 @@ async function persistPiConfiguration(paths, configuration, operations = {}) {
439
412
  `${JSON.stringify(value, null, 2)}\n`,
440
413
  { sensitive: true }
441
414
  ));
442
- const writeSettings = operations.writeSettings ?? ((path, value) => writeJsonFileWithBackup(path, value));
415
+ const writeSettings = operations.writeSettings ?? ((path, value) => writeJson(path, value));
443
416
  await updateAuth(paths.piAuthPath, configuration.providerId, configuration.apiKey);
444
417
  await writeModels(paths.piModelsPath, configuration.models);
445
418
  await writeSettings(paths.piSettingsPath, configuration.settings);
@@ -455,11 +428,8 @@ async function configurePiApi(paths = defaultPaths, ensurePiResourcesLinked = as
455
428
  }
456
429
  const {
457
430
  assertNotCancelled,
458
- confirmOrCancel,
459
- passwordPromptOptions,
431
+ passwordOrExisting,
460
432
  required,
461
- requiredUnlessExisting,
462
- resolvePasswordValue,
463
433
  selectOrCancel
464
434
  } = promptApi;
465
435
  const {
@@ -495,11 +465,6 @@ async function configurePiApi(paths = defaultPaths, ensurePiResourcesLinked = as
495
465
  const inferredApi = inferPiApiFromBaseUrl(baseUrlInput);
496
466
  const baseUrl = normalizeOpenAiBaseUrl(baseUrlInput);
497
467
 
498
- const insecureTls = await confirmOrCancel({
499
- message: `是否仅为 ${providerLabel} 中转请求跳过 TLS 证书校验?仅证书无法修复时启用`,
500
- initialValue: hasPiInsecureTlsSetting(modelsConfig, providerId)
501
- });
502
-
503
468
  const piApiOptions = getPiApiPromptOptions();
504
469
  const initialApi = piApiOptions.some((option) => option.value === existing.api)
505
470
  ? existing.api
@@ -510,13 +475,10 @@ async function configurePiApi(paths = defaultPaths, ensurePiResourcesLinked = as
510
475
  ...(initialApi ? { initialValue: initialApi } : {})
511
476
  });
512
477
 
513
- const apiKey = await p.password(passwordPromptOptions(
514
- `${providerLabel} API Key`,
515
- existing.apiKey,
516
- requiredUnlessExisting(existing.apiKey, "API Key 不能为空")
517
- ));
518
- assertNotCancelled(apiKey);
519
- const finalApiKey = resolvePasswordValue(apiKey, existing.apiKey);
478
+ const finalApiKey = await passwordOrExisting({
479
+ message: `${providerLabel} API Key`,
480
+ existingValue: existing.apiKey
481
+ });
520
482
 
521
483
  const modelIdsText = await p.text({
522
484
  message: `${providerLabel} 模型 ID(多个用逗号分隔)`,
@@ -542,7 +504,6 @@ async function configurePiApi(paths = defaultPaths, ensurePiResourcesLinked = as
542
504
  baseUrl,
543
505
  api,
544
506
  modelIds,
545
- insecureTls,
546
507
  defaultModel: finalDefaultModel
547
508
  });
548
509
  await ensurePiResourcesLinked(paths);
@@ -564,7 +525,6 @@ export {
564
525
  configurePiApi,
565
526
  detectPiEffectiveModel,
566
527
  getPiApiPromptOptions,
567
- hasPiInsecureTlsSetting,
568
528
  inferPiApiFromBaseUrl,
569
529
  normalizeOpenAiBaseUrl,
570
530
  parsePiRpcEffectiveModel,
@@ -21,10 +21,8 @@ async function configureGrokSearchEnv(paths, ensureSkillPresent = async () => {}
21
21
  const {
22
22
  assertNotCancelled,
23
23
  confirmOrCancel,
24
- passwordPromptOptions,
25
- required,
26
- requiredUnlessExisting,
27
- resolvePasswordValue
24
+ passwordOrExisting,
25
+ required
28
26
  } = promptApi;
29
27
  await ensureSkillPresent(paths);
30
28
  const envPath = join(paths.agentsDir, "skills", "grok-search", ".env");
@@ -36,13 +34,11 @@ async function configureGrokSearchEnv(paths, ensureSkillPresent = async () => {}
36
34
  });
37
35
  assertNotCancelled(baseUrl);
38
36
 
39
- const apiKey = await p.password(passwordPromptOptions(
40
- "Grok API Key",
41
- existing.GROK_API_KEY,
42
- requiredUnlessExisting(existing.GROK_API_KEY, "Grok API Key 不能为空")
43
- ));
44
- assertNotCancelled(apiKey);
45
- const finalApiKey = resolvePasswordValue(apiKey, existing.GROK_API_KEY);
37
+ const finalApiKey = await passwordOrExisting({
38
+ message: "Grok API Key",
39
+ existingValue: existing.GROK_API_KEY,
40
+ requiredMessage: "Grok API Key 不能为空"
41
+ });
46
42
 
47
43
  const model = await p.text({
48
44
  message: "Grok 默认模型",
@@ -65,15 +61,13 @@ async function configureGrokSearchEnv(paths, ensureSkillPresent = async () => {}
65
61
  : null;
66
62
  if (useTavily) assertNotCancelled(tavilyUrl);
67
63
 
68
- const tavilyKey = useTavily
69
- ? await p.password(passwordPromptOptions(
70
- "Tavily API Key",
71
- existing.TAVILY_API_KEY,
72
- requiredUnlessExisting(existing.TAVILY_API_KEY, "Tavily API Key 不能为空")
73
- ))
74
- : "";
75
- if (useTavily) assertNotCancelled(tavilyKey);
76
- const finalTavilyKey = useTavily ? resolvePasswordValue(tavilyKey, existing.TAVILY_API_KEY) : null;
64
+ const finalTavilyKey = useTavily
65
+ ? await passwordOrExisting({
66
+ message: "Tavily API Key",
67
+ existingValue: existing.TAVILY_API_KEY,
68
+ requiredMessage: "Tavily API Key 不能为空"
69
+ })
70
+ : null;
77
71
 
78
72
  await updateSkillEnvFile(envPath, {
79
73
  GROK_API_URL: baseUrl,
@@ -88,16 +82,15 @@ async function configureGrokSearchEnv(paths, ensureSkillPresent = async () => {}
88
82
  }
89
83
 
90
84
  async function configureContext7Env(paths, ensureSkillPresent = async () => {}, promptApi) {
91
- const { assertNotCancelled, passwordPromptOptions, resolvePasswordValue } = promptApi;
85
+ const { passwordOrExisting } = promptApi;
92
86
  await ensureSkillPresent(paths);
93
87
  const envPath = join(paths.agentsDir, "skills", "context7-auto-research", ".env");
94
88
  const existing = await readSkillEnvFile(envPath);
95
- const apiKey = await p.password(passwordPromptOptions(
96
- "Context7 API Key(可选)",
97
- existing.CONTEXT7_API_KEY
98
- ));
99
- assertNotCancelled(apiKey);
100
- const finalApiKey = resolvePasswordValue(apiKey, existing.CONTEXT7_API_KEY);
89
+ const finalApiKey = await passwordOrExisting({
90
+ message: "Context7 API Key(可选)",
91
+ existingValue: existing.CONTEXT7_API_KEY,
92
+ requiredMessage: null
93
+ });
101
94
 
102
95
  await updateSkillEnvFile(envPath, {
103
96
  CONTEXT7_API_KEY: finalApiKey
@@ -1,10 +1,9 @@
1
1
  personality = "pragmatic"
2
2
  model_provider = "abelworkflow"
3
3
  disable_response_storage = true
4
- preferred_auth_method = "apikey"
5
4
  approvals_reviewer = "guardian_subagent"
6
5
  approval_policy = "on-request"
7
- sandbox_mode = "workspace-write"
6
+ sandbox_mode = "danger-full-access"
8
7
  model = "gpt-5.6-sol"
9
8
  model_reasoning_effort = "high"
10
9
  network_access = true
package/lib/utils.mjs ADDED
@@ -0,0 +1,15 @@
1
+ import { createHash } from "node:crypto";
2
+
3
+ function hashBytes(content) {
4
+ return createHash("sha256").update(content).digest("hex");
5
+ }
6
+
7
+ function isManagedCodexAgentFileEntry(name, hash) {
8
+ return name.endsWith(".toml")
9
+ && !name.includes("/")
10
+ && !name.includes("\\")
11
+ && typeof hash === "string"
12
+ && /^[a-f0-9]{64}$/u.test(hash);
13
+ }
14
+
15
+ export { hashBytes, isManagedCodexAgentFileEntry };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "abelworkflow",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "Install AbelWorkflow into ~/.agents and create Claude/Codex symlinks.",
5
5
  "type": "module",
6
6
  "scripts": {