Skip to content

Commit c7a8184

Browse files
authored
Add devproxy installation script and update .gitignore (#1647)
* feat: add devproxy installation script and update .gitignore * feat: restore custom action workflow configuration * feat: add devproxy installation and start scripts; create devproxy configuration file * fix: ensure proxy debug logging occurs when a proxy is detected * feat: add devproxy test script to validate proxy setup * fix: ensure options are initialized and enhance logging in createFetch function * feat: add https-proxy-agent dependency and update proxy resolution methods * feat: update .gitignore and package.json for devproxy configuration
1 parent bce01a7 commit c7a8184

9 files changed

Lines changed: 80 additions & 21 deletions

File tree

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,10 @@ genaiscript*.tgz
6464

6565
*.bak
6666

67+
devproxy/
68+
devproxy-beta/
69+
.demo/
70+
dev-proxy-ca.crt
6771

6872
# START Ruler Generated Files
6973
.github/copilot-instructions.md

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626
"disk:check": "du -h --max-depth=2 | sort -hr | head -n 10",
2727
"docs": "cd docs && pnpm run dev",
2828
"install:ffmpeg": "sudo apt-get update && sudo apt-get install ffmpeg -y",
29+
"devproxy:install": "sudo bash -c \"$(curl -sL https://aka.ms/devproxy/setup.sh)\"",
30+
"devproxy:start": "cd packages/sample && devproxy",
31+
"devproxy:test": "curl -ikx http://127.0.0.1:8000 https://raw.githubusercontent.com/microsoft/genaiscript/refs/heads/main/SUPPORT.md",
2932
"format:check": "turbo format:check",
3033
"format:fix": "turbo format:fix",
3134
"gcm": "node packages/cli/dist/src/index.js run gcm --model gcm --no-run-trace --no-output-trace",
@@ -73,6 +76,7 @@
7376
"retrieval:index": "node packages/cli/dist/src/index.js retrieval index \"packages/sample/src/rag/*\"",
7477
"retrieval:search": "node packages/cli/dist/src/index.js retrieval search lorem \"packages/sample/src/rag/*\"",
7578
"run:script": "cd packages/sample/ && pnpm run:script",
79+
"run:script:devproxy": "cd packages/sample/ && HTTP_PROXY=http://127.0.0.1:8000 DEBUG=script,genaiscript:fetch* NODE_TLS_REJECT_UNAUTHORIZED=0 pnpm run:script",
7680
"serve": "pnpm build:cli && run-p serve:*",
7781
"serve:cli": "node --watch --watch-path=packages/cli/dist packages/cli/dist/src/index.js serve --dispatch-progress",
7882
"serve:web": "pnpm --filter=@genaiscript/web watch",

packages/core/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
"groq-js": "^1.17.1",
8888
"html-escaper": "3.0.3",
8989
"html-to-text": "^9.0.5",
90+
"https-proxy-agent": "^7.0.6",
9091
"ignore": "^7.0.5",
9192
"inflection": "catalog:",
9293
"ini": "^5.0.0",

packages/core/src/anthropic.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import {
3131
} from "./chattypes.js";
3232

3333
import { logError } from "./util.js";
34-
import { resolveHttpProxyAgent } from "./proxy.js";
34+
import { resolveUndiciProxyAgent } from "./proxy.js";
3535
import { ProxyAgent } from "undici";
3636
import { MarkdownTrace } from "./trace.js";
3737
import { createFetch, FetchType } from "./fetch.js";
@@ -303,7 +303,7 @@ const completerFactory = (
303303
// https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching#how-to-implement-prompt-caching
304304
const caching =
305305
/sonnet|haiku|opus/i.test(model) && req.messages.some((m) => m.cacheControl === "ephemeral");
306-
const httpAgent = await resolveHttpProxyAgent();
306+
const httpAgent = await resolveUndiciProxyAgent();
307307
const messagesApi = await resolver(trace, cfg, httpAgent, fetch);
308308
dbg("caching", caching);
309309
trace?.itemValue(`caching`, caching);

packages/core/src/fetch.ts

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@ import {
1313
import { errorMessage } from "./error.js";
1414
import { logVerbose } from "./util.js";
1515
import { CancellationOptions } from "./cancellation.js";
16-
import { resolveHttpProxyAgent } from "./proxy.js";
16+
import { resolveHttpsProxyAgent } from "./proxy.js";
1717
import { host } from "./host.js";
1818
import { renderWithPrecision } from "./precision.js";
1919
import crossFetch from "cross-fetch";
20-
import debug from "debug";
2120
import { prettyStrings } from "./pretty.js";
2221
import type { FetchOptions, RetryOptions } from "./types.js";
22+
import { genaiscriptDebug } from "./debug.js";
2323

24-
const dbg = debug("genaiscript:fetch");
24+
const dbg = genaiscriptDebug("fetch");
2525

2626
/**
2727
* Parses the retry-after header value.
@@ -51,7 +51,7 @@ export function parseRetryAfter(retryAfterHeader: string): number | null {
5151
const delaySeconds = Math.max(0, Math.ceil(delayMs / 1000));
5252
return delaySeconds;
5353
}
54-
} catch(e) {
54+
} catch (e) {
5555
dbg(`failed to parse retry-after header as date: %s`, errorMessage(e));
5656
}
5757
}
@@ -84,31 +84,38 @@ export type FetchType = (
8484
export async function createFetch(
8585
options?: TraceOptions & CancellationOptions & RetryOptions,
8686
): Promise<FetchType> {
87+
options = options || {};
8788
const {
8889
retries = FETCH_RETRY_DEFAULT,
8990
retryOn = FETCH_RETRY_ON_DEFAULT,
9091
trace,
9192
retryDelay = FETCH_RETRY_DEFAULT_DEFAULT,
9293
maxDelay = FETCH_RETRY_MAX_DELAY_DEFAULT,
9394
cancellationToken,
94-
} = options || {};
95+
} = options;
9596

97+
dbg(`create fetch`);
9698
// We create a proxy based on Node.js environment variables.
97-
const agent = await resolveHttpProxyAgent();
99+
const agent = await resolveHttpsProxyAgent();
98100

99101
// We enrich crossFetch with the proxy.
100102
const crossFetchWithProxy: typeof fetch = agent
101-
? (url, options) => crossFetch(url, { ...(options || {}), dispatcher: agent } as any)
103+
? (url, options) => crossFetch(url, { ...options, agent } as RequestInit)
102104
: crossFetch;
103105

106+
const loggingFetch: typeof fetch = (url, options) => {
107+
dbg(`fetch: %s %s`, options?.method || "GET", url);
108+
return crossFetchWithProxy(url, options);
109+
};
110+
104111
// Return the default fetch if no retry status codes are specified
105112
if (!retryOn?.length) {
106113
dbg("no retry logic applied, using crossFetchWithProxy directly");
107-
return crossFetchWithProxy;
114+
return loggingFetch;
108115
}
109116

110117
// Create a fetch function with retry logic
111-
const fetchRetry = wrapFetch(crossFetchWithProxy, {
118+
const fetchRetry = wrapFetch(loggingFetch, {
112119
retryOn,
113120
retries,
114121
retryDelay: (attempt, error, response) => {

packages/core/src/proxy.ts

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,18 @@
22
// Licensed under the MIT License.
33

44
import { genaiscriptDebug } from "./debug.js";
5-
const dbg = genaiscriptDebug("proxy");
5+
const dbg = genaiscriptDebug("fetch:proxy");
6+
7+
function resolveProxyUrl() {
8+
const proxy =
9+
process.env.GENAISCRIPT_HTTPS_PROXY ||
10+
process.env.GENAISCRIPT_HTTP_PROXY ||
11+
process.env.HTTPS_PROXY ||
12+
process.env.HTTP_PROXY ||
13+
process.env.https_proxy ||
14+
process.env.http_proxy;
15+
return proxy;
16+
}
617

718
/**
819
* Resolves an HTTP proxy agent based on environment variables.
@@ -23,19 +34,28 @@ const dbg = genaiscriptDebug("proxy");
2334
* @returns An instance of `HttpsProxyAgent` if a proxy is configured,
2435
* or null if no proxy is detected.
2536
*/
26-
export async function resolveHttpProxyAgent() {
37+
export async function resolveUndiciProxyAgent() {
2738
// We create a proxy based on Node.js environment variables.
28-
const proxy =
29-
process.env.GENAISCRIPT_HTTPS_PROXY ||
30-
process.env.GENAISCRIPT_HTTP_PROXY ||
31-
process.env.HTTPS_PROXY ||
32-
process.env.HTTP_PROXY ||
33-
process.env.https_proxy ||
34-
process.env.http_proxy;
35-
if (proxy) dbg(`proxy: %s`, proxy);
39+
const proxy = resolveProxyUrl();
3640
if (!proxy) return null;
3741

42+
dbg(`proxy (undici): %s`, proxy);
3843
const { ProxyAgent } = await import("undici");
3944
const agent = new ProxyAgent(proxy);
45+
agent.on(`connect`, (info) => dbg(`connect: %s`, info.href));
46+
agent.on(`connectionError`, (err) => dbg(`connection error: %s`, err.toString()));
47+
agent.on(`disconnect`, () => dbg(`disconnect`));
48+
return agent;
49+
}
50+
51+
export async function resolveHttpsProxyAgent() {
52+
const proxyUrl = resolveProxyUrl();
53+
if (!proxyUrl) return null;
54+
55+
dbg(`proxy (hpa): %s`, proxyUrl);
56+
const { HttpsProxyAgent } = await import("https-proxy-agent");
57+
const agent = new HttpsProxyAgent(proxyUrl);
58+
agent.on(`connect`, (info) => dbg(`connect: %s`, info.href));
59+
agent.on(`error`, (err) => dbg(`error: %s`, err.toString()));
4060
return agent;
4161
}

packages/sample/devproxyrc.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v0.29.0/rc.schema.json",
3+
"plugins": [
4+
{
5+
"name": "DevToolsPlugin",
6+
"enabled": true,
7+
"pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll",
8+
"configSection": "devTools"
9+
}
10+
],
11+
"devTools": {
12+
"$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v0.29.0/devtoolsplugin.schema.json",
13+
"preferredBrowser": "EdgeDev"
14+
},
15+
"urlsToWatch": ["*"],
16+
"logLevel": "debug",
17+
"newVersionNotification": "stable",
18+
"showSkipMessages": true,
19+
"asSystemProxy": false
20+
}

pnpm-lock.yaml

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)