@trim21/personal-pi-extensions 0.1.635 → 0.1.636
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/package.json +1 -1
- package/src/gh/base.ts +3 -2
- package/src/lib/egress.ts +29 -0
- package/src/lib/gh-process.ts +5 -11
- package/src/web/fetch.ts +5 -7
- package/src/web/search.ts +2 -1
package/package.json
CHANGED
package/src/gh/base.ts
CHANGED
|
@@ -12,7 +12,8 @@ import { delimiter, join } from "node:path";
|
|
|
12
12
|
import { Type } from "typebox";
|
|
13
13
|
import { Value } from "typebox/value";
|
|
14
14
|
|
|
15
|
-
import {
|
|
15
|
+
import { egress } from "../lib/egress.js";
|
|
16
|
+
import { type GhResult, runGh } from "../lib/gh-process.js";
|
|
16
17
|
import {
|
|
17
18
|
type ActionJob,
|
|
18
19
|
type CheckRun,
|
|
@@ -344,7 +345,7 @@ export class GhClient {
|
|
|
344
345
|
readonly checks: GithubChecksClient;
|
|
345
346
|
|
|
346
347
|
constructor(
|
|
347
|
-
fetchImpl: typeof globalThis.fetch =
|
|
348
|
+
fetchImpl: typeof globalThis.fetch = egress.fetch,
|
|
348
349
|
options: Pick<GithubClientOptions, "token"> = {},
|
|
349
350
|
) {
|
|
350
351
|
this.fetch = fetchImpl;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 出网入口:代理配置的解析、dispatcher 与 NO_PROXY 分流仍在 `src/lib/proxy.ts`(代理
|
|
3
|
+
* 适配器),本模块只把它收敛成一处,让所有出网调用方从同一处取 `fetch` 与子进程代理
|
|
4
|
+
* 环境变量,并保持「配置写错即加载失败」的行为——不静默直连。
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { createHttpProxy, type HttpProxySettings } from "./proxy.js";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 出网的单一入口:需要的调用方从这里取请求用的 fetch 与子进程代理环境变量,
|
|
11
|
+
* 不必各自判断「该用代理 fetch 还是全局 fetch」。
|
|
12
|
+
*/
|
|
13
|
+
export interface Egress {
|
|
14
|
+
/** 生效的代理设置,供诊断与展示。 */
|
|
15
|
+
readonly settings: HttpProxySettings;
|
|
16
|
+
/** 请求用的 fetch:undici 的 fetch,配置了代理时自动走代理(NO_PROXY 由 dispatcher 处理)。 */
|
|
17
|
+
readonly fetch: typeof globalThis.fetch;
|
|
18
|
+
/** 要注入子进程的代理环境变量;未配置代理时为空对象。 */
|
|
19
|
+
readonly env: NodeJS.ProcessEnv;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** 组装出网层(读一次代理配置;配置写错直接抛)。 */
|
|
23
|
+
export function createEgress(): Egress {
|
|
24
|
+
const proxy = createHttpProxy();
|
|
25
|
+
return { settings: proxy.settings, fetch: proxy.fetch, env: proxy.env };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** 共享出网实例:进程内所有出网调用方共用(代理配置在扩展加载时读一次)。 */
|
|
29
|
+
export const egress: Egress = createEgress();
|
package/src/lib/gh-process.ts
CHANGED
|
@@ -1,19 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `gh` subprocess adapter: start a `gh` process, collect its output, apply the
|
|
3
|
-
* shared timeout / kill semantics, and
|
|
4
|
-
*
|
|
5
|
-
* octokit clients (`lib/github.ts`) can depend on it without a cycle.
|
|
3
|
+
* shared timeout / kill semantics, and hand it the proxy env from the shared
|
|
4
|
+
* egress layer. It lives in `lib/` so both the gh tool layer (`src/gh/`) and
|
|
5
|
+
* the octokit clients (`lib/github.ts`) can depend on it without a cycle.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { spawn } from "node:child_process";
|
|
9
9
|
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* 代理配置(~/.pi/agent/proxy.json,回退到 HTTP(S)_PROXY 环境变量)在本模块内共享:
|
|
14
|
-
* `gh` 子进程与 octokit 请求都从这里取,配置只在首次使用时读一次。
|
|
15
|
-
*/
|
|
16
|
-
export const httpProxy = createHttpProxy();
|
|
10
|
+
import { egress } from "./egress.js";
|
|
17
11
|
|
|
18
12
|
export interface GhResult {
|
|
19
13
|
stdout: string;
|
|
@@ -42,7 +36,7 @@ export function runGh(args: string[], ctx: GhRunContext): Promise<GhResult> {
|
|
|
42
36
|
shell: false,
|
|
43
37
|
stdio: ["ignore", "pipe", "pipe"],
|
|
44
38
|
// gh 是 Go 程序,只认环境变量形式的代理配置;ctx.env 最后合并,调用方可覆盖。
|
|
45
|
-
env: { ...process.env, ...
|
|
39
|
+
env: { ...process.env, ...egress.env, ...ctx.env, GH_PAGER: "cat" },
|
|
46
40
|
});
|
|
47
41
|
|
|
48
42
|
let stdout = "";
|
package/src/web/fetch.ts
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
* SSRF 防护:DNS 预解析 + 拒绝私有/保留地址 + 每跳重定向重新校验,
|
|
5
5
|
* 防止把 agent 变成内网探测口。正文提取用 readability 主内容算法。
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* 出网走共享出网层 `src/lib/egress.ts`(代理配置 ~/.pi/agent/proxy.json,回退 HTTPS_PROXY
|
|
8
|
+
* 等环境变量):Node 的全局 fetch 不认代理环境变量,GitHub 的用户附件、release 资产这类
|
|
9
|
+
* 只在代理可达的 host 上,必须从这里挂出去,否则沙箱内一律 fetch failed。
|
|
10
10
|
*
|
|
11
11
|
* 本文件是独立扩展入口(见 package.json 的 pi.extensions),可在配置里单独禁用。
|
|
12
12
|
*/
|
|
@@ -21,13 +21,11 @@ import { parseHTML } from "linkedom";
|
|
|
21
21
|
import TurndownService from "turndown";
|
|
22
22
|
import { Type } from "typebox";
|
|
23
23
|
|
|
24
|
+
import { egress } from "../lib/egress.js";
|
|
24
25
|
import { resolvePathArg } from "../lib/path.js";
|
|
25
|
-
import { createHttpProxy } from "../lib/proxy.js";
|
|
26
26
|
import { createRequestPolicy } from "../lib/request-policy.js";
|
|
27
27
|
import { guardWriteAccess } from "../lib/write-guard.js";
|
|
28
28
|
|
|
29
|
-
const httpProxy = createHttpProxy();
|
|
30
|
-
|
|
31
29
|
const MAX_REDIRECTS = 5;
|
|
32
30
|
const TIMEOUT_MS = 30_000;
|
|
33
31
|
const MAX_BYTES = 5 * 1024 * 1024;
|
|
@@ -143,7 +141,7 @@ interface SavedFile {
|
|
|
143
141
|
async function fetchWithRedirects(
|
|
144
142
|
url: URL,
|
|
145
143
|
signal: AbortSignal | undefined,
|
|
146
|
-
fetchFn: typeof fetch =
|
|
144
|
+
fetchFn: typeof fetch = egress.fetch,
|
|
147
145
|
): Promise<{ response: Response; url: URL }> {
|
|
148
146
|
let current = url;
|
|
149
147
|
for (let redirects = 0; ; redirects++) {
|
package/src/web/search.ts
CHANGED
|
@@ -11,6 +11,7 @@ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
|
11
11
|
import { Type } from "typebox";
|
|
12
12
|
import { Value } from "typebox/value";
|
|
13
13
|
|
|
14
|
+
import { egress } from "../lib/egress.js";
|
|
14
15
|
import { loadSearch1ApiKey } from "./config.js";
|
|
15
16
|
|
|
16
17
|
const SEARCH_URL = "https://api.search1api.com/search";
|
|
@@ -150,7 +151,7 @@ export async function searchWeb(
|
|
|
150
151
|
apiKey: string,
|
|
151
152
|
options: SearchOptions = {},
|
|
152
153
|
): Promise<SearchResult> {
|
|
153
|
-
const response = await fetch(SEARCH_URL, {
|
|
154
|
+
const response = await egress.fetch(SEARCH_URL, {
|
|
154
155
|
method: "POST",
|
|
155
156
|
headers: {
|
|
156
157
|
Authorization: `Bearer ${apiKey}`,
|