abelworkflow 1.1.0 → 1.1.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.
|
@@ -1,23 +1,6 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import { createNodeInsecureDispatcher, installProviderTlsFetch } from "./tls-fetch.mjs";
|
|
3
|
-
|
|
4
|
-
let nodeInsecureDispatcher: unknown;
|
|
5
|
-
|
|
6
|
-
function getNodeInsecureDispatcher() {
|
|
7
|
-
if (nodeInsecureDispatcher) return nodeInsecureDispatcher;
|
|
8
|
-
nodeInsecureDispatcher = createNodeInsecureDispatcher();
|
|
9
|
-
return nodeInsecureDispatcher;
|
|
10
|
-
}
|
|
11
2
|
|
|
12
3
|
export default function (pi: ExtensionAPI) {
|
|
13
|
-
if (typeof globalThis.fetch === "function") {
|
|
14
|
-
const runtime = typeof (globalThis as any).Bun === "undefined" ? "node" : "bun";
|
|
15
|
-
installProviderTlsFetch({
|
|
16
|
-
runtime,
|
|
17
|
-
insecureDispatcher: runtime === "node" ? getNodeInsecureDispatcher : undefined
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
|
|
21
4
|
pi.on("before_provider_request", (event, ctx) => {
|
|
22
5
|
const payload = event.payload as any;
|
|
23
6
|
if (!payload || typeof payload !== "object" || Array.isArray(payload)) return;
|
package/lib/providers/claude.mjs
CHANGED
|
@@ -62,23 +62,12 @@ function mergeClaudeSettingsWithDefaults(settings) {
|
|
|
62
62
|
};
|
|
63
63
|
}
|
|
64
64
|
|
|
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
65
|
function getExistingClaudeApiConfig(settings) {
|
|
76
66
|
const env = mergeClaudeSettingsWithDefaults(settings).env;
|
|
77
67
|
return {
|
|
78
68
|
baseUrl: env.ANTHROPIC_BASE_URL || "https://api.anthropic.com",
|
|
79
69
|
key: env.ANTHROPIC_API_KEY || "",
|
|
80
|
-
model: claudeModelEnvKeys.map((field) => env[field]).find(Boolean) || ""
|
|
81
|
-
insecureTls: env.NODE_TLS_REJECT_UNAUTHORIZED === "0"
|
|
70
|
+
model: claudeModelEnvKeys.map((field) => env[field]).find(Boolean) || ""
|
|
82
71
|
};
|
|
83
72
|
}
|
|
84
73
|
|
|
@@ -109,14 +98,9 @@ function ensureApprovedClaudeApiKey(config, apiKey) {
|
|
|
109
98
|
function buildClaudeApiSettings(settings, {
|
|
110
99
|
baseUrl,
|
|
111
100
|
key,
|
|
112
|
-
model
|
|
113
|
-
insecureTls = false
|
|
101
|
+
model
|
|
114
102
|
}) {
|
|
115
103
|
const nextSettings = mergeClaudeSettingsWithDefaults(settings);
|
|
116
|
-
nextSettings.env = applyClaudeInsecureTlsSetting(
|
|
117
|
-
nextSettings.env && typeof nextSettings.env === "object" ? nextSettings.env : {},
|
|
118
|
-
insecureTls
|
|
119
|
-
);
|
|
120
104
|
nextSettings.env.ANTHROPIC_BASE_URL = baseUrl;
|
|
121
105
|
nextSettings.env.ANTHROPIC_API_KEY = key;
|
|
122
106
|
delete nextSettings.env.ANTHROPIC_AUTH_TOKEN;
|
|
@@ -134,7 +118,6 @@ async function persistClaudeConfiguration(paths, { settings, metaConfig }) {
|
|
|
134
118
|
async function configureClaudeApi(paths = defaultPaths, promptApi) {
|
|
135
119
|
const {
|
|
136
120
|
assertNotCancelled,
|
|
137
|
-
confirmOrCancel,
|
|
138
121
|
passwordPromptOptions,
|
|
139
122
|
required,
|
|
140
123
|
requiredUnlessExisting,
|
|
@@ -158,11 +141,6 @@ async function configureClaudeApi(paths = defaultPaths, promptApi) {
|
|
|
158
141
|
assertNotCancelled(key);
|
|
159
142
|
const finalKey = resolvePasswordValue(key, existing.key);
|
|
160
143
|
|
|
161
|
-
const insecureTls = await confirmOrCancel({
|
|
162
|
-
message: "是否跳过 Claude Code TLS 证书校验?仅证书无法修复时启用(会放宽该进程全部 HTTPS 请求)",
|
|
163
|
-
initialValue: existing.insecureTls
|
|
164
|
-
});
|
|
165
|
-
|
|
166
144
|
const model = await p.text({
|
|
167
145
|
message: "Claude Code 模型",
|
|
168
146
|
initialValue: existing.model || undefined,
|
|
@@ -173,8 +151,7 @@ async function configureClaudeApi(paths = defaultPaths, promptApi) {
|
|
|
173
151
|
const apiSettings = buildClaudeApiSettings(settings, {
|
|
174
152
|
baseUrl,
|
|
175
153
|
key: finalKey,
|
|
176
|
-
model
|
|
177
|
-
insecureTls
|
|
154
|
+
model
|
|
178
155
|
});
|
|
179
156
|
const metaConfig = await readJsonFileSafe(paths.claudeMetaConfigPath, {}, { sensitive: true });
|
|
180
157
|
metaConfig.hasCompletedOnboarding = true;
|
|
@@ -188,7 +165,6 @@ async function configureClaudeApi(paths = defaultPaths, promptApi) {
|
|
|
188
165
|
}
|
|
189
166
|
|
|
190
167
|
export {
|
|
191
|
-
applyClaudeInsecureTlsSetting,
|
|
192
168
|
buildClaudeApiSettings,
|
|
193
169
|
buildDefaultClaudeSettings,
|
|
194
170
|
configureClaudeApi,
|
package/lib/providers/pi.mjs
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
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,
|
|
@@ -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
|
};
|
|
@@ -455,7 +428,6 @@ async function configurePiApi(paths = defaultPaths, ensurePiResourcesLinked = as
|
|
|
455
428
|
}
|
|
456
429
|
const {
|
|
457
430
|
assertNotCancelled,
|
|
458
|
-
confirmOrCancel,
|
|
459
431
|
passwordPromptOptions,
|
|
460
432
|
required,
|
|
461
433
|
requiredUnlessExisting,
|
|
@@ -495,11 +467,6 @@ async function configurePiApi(paths = defaultPaths, ensurePiResourcesLinked = as
|
|
|
495
467
|
const inferredApi = inferPiApiFromBaseUrl(baseUrlInput);
|
|
496
468
|
const baseUrl = normalizeOpenAiBaseUrl(baseUrlInput);
|
|
497
469
|
|
|
498
|
-
const insecureTls = await confirmOrCancel({
|
|
499
|
-
message: `是否仅为 ${providerLabel} 中转请求跳过 TLS 证书校验?仅证书无法修复时启用`,
|
|
500
|
-
initialValue: hasPiInsecureTlsSetting(modelsConfig, providerId)
|
|
501
|
-
});
|
|
502
|
-
|
|
503
470
|
const piApiOptions = getPiApiPromptOptions();
|
|
504
471
|
const initialApi = piApiOptions.some((option) => option.value === existing.api)
|
|
505
472
|
? existing.api
|
|
@@ -542,7 +509,6 @@ async function configurePiApi(paths = defaultPaths, ensurePiResourcesLinked = as
|
|
|
542
509
|
baseUrl,
|
|
543
510
|
api,
|
|
544
511
|
modelIds,
|
|
545
|
-
insecureTls,
|
|
546
512
|
defaultModel: finalDefaultModel
|
|
547
513
|
});
|
|
548
514
|
await ensurePiResourcesLinked(paths);
|
|
@@ -564,7 +530,6 @@ export {
|
|
|
564
530
|
configurePiApi,
|
|
565
531
|
detectPiEffectiveModel,
|
|
566
532
|
getPiApiPromptOptions,
|
|
567
|
-
hasPiInsecureTlsSetting,
|
|
568
533
|
inferPiApiFromBaseUrl,
|
|
569
534
|
normalizeOpenAiBaseUrl,
|
|
570
535
|
parsePiRpcEffectiveModel,
|
package/package.json
CHANGED
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
export const piInsecureTlsHeader = "x-abelworkflow-insecure-tls";
|
|
2
|
-
|
|
3
|
-
const installedFetchMarker = Symbol.for("abelworkflow.pi-provider-tls-fetch");
|
|
4
|
-
const undiciGlobalDispatchers = [
|
|
5
|
-
Symbol.for("undici.globalDispatcher.2"),
|
|
6
|
-
Symbol.for("undici.globalDispatcher.1")
|
|
7
|
-
];
|
|
8
|
-
const redirectStatuses = new Set([301, 302, 303, 307, 308]);
|
|
9
|
-
const maxRedirects = 5;
|
|
10
|
-
|
|
11
|
-
function getMarkedHeaders(input, init) {
|
|
12
|
-
const source = init?.headers !== undefined
|
|
13
|
-
? init.headers
|
|
14
|
-
: typeof Request !== "undefined" && input instanceof Request
|
|
15
|
-
? input.headers
|
|
16
|
-
: undefined;
|
|
17
|
-
if (source === undefined) return null;
|
|
18
|
-
|
|
19
|
-
const headers = new Headers(source);
|
|
20
|
-
if (!headers.has(piInsecureTlsHeader)) return null;
|
|
21
|
-
const allowedOrigin = headers.get(piInsecureTlsHeader);
|
|
22
|
-
headers.delete(piInsecureTlsHeader);
|
|
23
|
-
return { allowedOrigin, headers };
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function getRequestUrl(input) {
|
|
27
|
-
return typeof Request !== "undefined" && input instanceof Request
|
|
28
|
-
? input.url
|
|
29
|
-
: input instanceof URL
|
|
30
|
-
? input.href
|
|
31
|
-
: input;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function getRequestOrigin(input) {
|
|
35
|
-
try {
|
|
36
|
-
return new URL(getRequestUrl(input)).origin;
|
|
37
|
-
} catch {
|
|
38
|
-
return null;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function redirectRequestInit(input, init, status) {
|
|
43
|
-
const method = String(init.method ?? (
|
|
44
|
-
typeof Request !== "undefined" && input instanceof Request ? input.method : "GET"
|
|
45
|
-
)).toUpperCase();
|
|
46
|
-
const switchToGet = status === 303 && method !== "HEAD"
|
|
47
|
-
|| (status === 301 || status === 302) && method === "POST";
|
|
48
|
-
if (!switchToGet) {
|
|
49
|
-
if (typeof Request !== "undefined" && input instanceof Request && method !== "GET" && method !== "HEAD") {
|
|
50
|
-
throw new Error("Pi insecure TLS cannot safely replay a redirected Request body");
|
|
51
|
-
}
|
|
52
|
-
return init;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const headers = new Headers(init.headers);
|
|
56
|
-
for (const name of ["content-encoding", "content-language", "content-length", "content-location", "content-type"]) {
|
|
57
|
-
headers.delete(name);
|
|
58
|
-
}
|
|
59
|
-
const nextInit = { ...init, method: "GET", headers };
|
|
60
|
-
delete nextInit.body;
|
|
61
|
-
return nextInit;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
async function fetchWithSameOriginRedirects({ fetchOnce, input, init, allowedOrigin }) {
|
|
65
|
-
let nextInput = input;
|
|
66
|
-
let nextInit = init;
|
|
67
|
-
|
|
68
|
-
for (let redirectCount = 0; redirectCount <= maxRedirects; redirectCount += 1) {
|
|
69
|
-
const response = await fetchOnce(nextInput, nextInit);
|
|
70
|
-
const location = redirectStatuses.has(response?.status) ? response.headers?.get?.("location") : null;
|
|
71
|
-
if (!location) return response;
|
|
72
|
-
if (redirectCount === maxRedirects) {
|
|
73
|
-
await response.body?.cancel?.();
|
|
74
|
-
throw new Error(`Pi insecure TLS exceeded ${maxRedirects} same-origin redirects`);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
const target = new URL(location, getRequestUrl(nextInput));
|
|
78
|
-
if (target.origin !== allowedOrigin) {
|
|
79
|
-
await response.body?.cancel?.();
|
|
80
|
-
throw new Error(`Pi insecure TLS blocked cross-origin redirect to ${target.origin}`);
|
|
81
|
-
}
|
|
82
|
-
await response.body?.cancel?.();
|
|
83
|
-
nextInit = redirectRequestInit(nextInput, nextInit, response.status);
|
|
84
|
-
nextInput = target.href;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function applyNodeDispatcher(fetchImpl, input, init, dispatcherSource) {
|
|
89
|
-
let dispatcher;
|
|
90
|
-
try {
|
|
91
|
-
dispatcher = typeof dispatcherSource === "function" ? dispatcherSource() : dispatcherSource;
|
|
92
|
-
} catch (error) {
|
|
93
|
-
return Promise.reject(error);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (dispatcher && typeof dispatcher.then === "function") {
|
|
97
|
-
return dispatcher.then((resolved) => applyNodeDispatcher(fetchImpl, input, init, resolved));
|
|
98
|
-
}
|
|
99
|
-
if (!dispatcher || typeof dispatcher.dispatch !== "function") {
|
|
100
|
-
return Promise.reject(new Error("Pi insecure TLS requires an injected Undici dispatcher"));
|
|
101
|
-
}
|
|
102
|
-
return fetchImpl(input, { ...init, dispatcher });
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function getUndiciDispatcherConstructor(target) {
|
|
106
|
-
return undiciGlobalDispatchers
|
|
107
|
-
.map((symbol) => target[symbol]?.constructor)
|
|
108
|
-
.find((constructor) => typeof constructor === "function");
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function initializeUndiciGlobalDispatcher(target) {
|
|
112
|
-
if (typeof target.fetch !== "function") return;
|
|
113
|
-
try {
|
|
114
|
-
void Promise.resolve(target.fetch("data:,")).catch(() => {});
|
|
115
|
-
} catch {
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
export function createNodeInsecureDispatcher(target = globalThis) {
|
|
120
|
-
let Dispatcher = getUndiciDispatcherConstructor(target);
|
|
121
|
-
if (typeof Dispatcher !== "function") {
|
|
122
|
-
initializeUndiciGlobalDispatcher(target);
|
|
123
|
-
Dispatcher = getUndiciDispatcherConstructor(target);
|
|
124
|
-
}
|
|
125
|
-
if (typeof Dispatcher !== "function") {
|
|
126
|
-
throw new Error("Pi insecure TLS requires the active Undici dispatcher");
|
|
127
|
-
}
|
|
128
|
-
return new Dispatcher({
|
|
129
|
-
allowH2: false,
|
|
130
|
-
connect: { rejectUnauthorized: false },
|
|
131
|
-
requestTls: { rejectUnauthorized: false }
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
export function createProviderTlsFetch({
|
|
136
|
-
fetchImpl = globalThis.fetch,
|
|
137
|
-
runtime = typeof globalThis.Bun === "undefined" ? "node" : "bun",
|
|
138
|
-
insecureDispatcher
|
|
139
|
-
} = {}) {
|
|
140
|
-
if (typeof fetchImpl !== "function") {
|
|
141
|
-
throw new TypeError("A fetch implementation is required");
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return function providerTlsFetch(input, init) {
|
|
145
|
-
const marked = getMarkedHeaders(input, init);
|
|
146
|
-
if (!marked) return fetchImpl(input, init);
|
|
147
|
-
|
|
148
|
-
const nextInit = { ...(init ?? {}), headers: marked.headers };
|
|
149
|
-
if (!marked.allowedOrigin || getRequestOrigin(input) !== marked.allowedOrigin) {
|
|
150
|
-
return fetchImpl(input, nextInit);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
nextInit.redirect = "manual";
|
|
154
|
-
const fetchOnce = runtime === "bun"
|
|
155
|
-
? (nextInput, redirectInit) => {
|
|
156
|
-
const tls = redirectInit.tls && typeof redirectInit.tls === "object" ? redirectInit.tls : {};
|
|
157
|
-
return fetchImpl(nextInput, {
|
|
158
|
-
...redirectInit,
|
|
159
|
-
tls: { ...tls, rejectUnauthorized: false }
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
: (nextInput, redirectInit) => applyNodeDispatcher(
|
|
163
|
-
fetchImpl,
|
|
164
|
-
nextInput,
|
|
165
|
-
redirectInit,
|
|
166
|
-
insecureDispatcher
|
|
167
|
-
);
|
|
168
|
-
|
|
169
|
-
return fetchWithSameOriginRedirects({
|
|
170
|
-
fetchOnce,
|
|
171
|
-
input,
|
|
172
|
-
init: nextInit,
|
|
173
|
-
allowedOrigin: marked.allowedOrigin
|
|
174
|
-
});
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
export function installProviderTlsFetch({ target = globalThis, ...options } = {}) {
|
|
179
|
-
if (target.fetch?.[installedFetchMarker]) return target.fetch;
|
|
180
|
-
|
|
181
|
-
const wrapped = createProviderTlsFetch({
|
|
182
|
-
...options,
|
|
183
|
-
fetchImpl: target.fetch.bind(target)
|
|
184
|
-
});
|
|
185
|
-
Object.defineProperty(wrapped, installedFetchMarker, { value: true });
|
|
186
|
-
target.fetch = wrapped;
|
|
187
|
-
return wrapped;
|
|
188
|
-
}
|