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.
- package/extensions/pi-gpt-responses-compat/index.ts +0 -17
- package/lib/cli/main.mjs +39 -48
- package/lib/cli/prompts.mjs +22 -19
- package/lib/config/dotenv.mjs +1 -9
- package/lib/config/store.mjs +0 -29
- package/lib/config/toml.mjs +10 -9
- package/lib/installer/assets.mjs +2 -6
- package/lib/installer/install.mjs +18 -43
- package/lib/installer/links.mjs +7 -18
- package/lib/installer/state.mjs +3 -22
- package/lib/providers/claude.mjs +10 -46
- package/lib/providers/codex.mjs +29 -37
- package/lib/providers/pi.mjs +9 -49
- package/lib/providers/skills.mjs +20 -27
- package/lib/templates/codex/config-base.toml +1 -2
- package/lib/utils.mjs +15 -0
- package/package.json +1 -1
- package/extensions/pi-gpt-responses-compat/tls-fetch.mjs +0 -188
package/lib/providers/claude.mjs
CHANGED
|
@@ -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
|
|
131
|
-
await
|
|
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
|
|
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,
|
package/lib/providers/codex.mjs
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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 =
|
|
197
|
+
const sourceHash = hashBytes(sourceContent);
|
|
209
198
|
const current = await readCodexAgentTarget(target);
|
|
210
|
-
const currentHash = 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 (
|
|
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) || !
|
|
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 &&
|
|
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
|
|
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
|
-
|
|
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
|
|
318
|
-
"Codex 第三方 API Key",
|
|
319
|
-
existing.apiKey
|
|
320
|
-
|
|
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 =
|
|
394
|
-
content =
|
|
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)
|
|
412
|
-
|
|
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
|
|
package/lib/providers/pi.mjs
CHANGED
|
@@ -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
|
-
|
|
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) =>
|
|
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
|
-
|
|
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
|
|
514
|
-
`${providerLabel} API Key`,
|
|
515
|
-
existing.apiKey
|
|
516
|
-
|
|
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,
|
package/lib/providers/skills.mjs
CHANGED
|
@@ -21,10 +21,8 @@ async function configureGrokSearchEnv(paths, ensureSkillPresent = async () => {}
|
|
|
21
21
|
const {
|
|
22
22
|
assertNotCancelled,
|
|
23
23
|
confirmOrCancel,
|
|
24
|
-
|
|
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
|
|
40
|
-
"Grok API Key",
|
|
41
|
-
existing.GROK_API_KEY,
|
|
42
|
-
|
|
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
|
|
69
|
-
? await
|
|
70
|
-
"Tavily API Key",
|
|
71
|
-
existing.TAVILY_API_KEY,
|
|
72
|
-
|
|
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 {
|
|
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
|
|
96
|
-
"Context7 API Key(可选)",
|
|
97
|
-
existing.CONTEXT7_API_KEY
|
|
98
|
-
|
|
99
|
-
|
|
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 = "
|
|
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 };
|