@aion0/forge 0.10.87 → 0.10.88

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/RELEASE_NOTES.md CHANGED
@@ -1,14 +1,8 @@
1
- # Forge v0.10.87
1
+ # Forge v0.10.88
2
2
 
3
3
  Released: 2026-06-17
4
4
 
5
- ## Changes since v0.10.86
5
+ ## Changes since v0.10.87
6
6
 
7
- ### Other
8
- - feat(pipeline): log attached skills in task init line
9
- - feat(skills): per-skill source badge in marketplace (#354)
10
- - feat(skills): multi-source sync + install (enterprise + public) — #354
11
- - feat(pipeline): workflow-level skills: field + template _skills auto-install
12
7
 
13
-
14
- **Full Changelog**: https://github.com/aiwatching/forge/compare/v0.10.86...v0.10.87
8
+ **Full Changelog**: https://github.com/aiwatching/forge/compare/v0.10.87...v0.10.88
@@ -28,6 +28,17 @@ import { homedir } from 'node:os';
28
28
  const __dirname = dirname(fileURLToPath(import.meta.url));
29
29
  const ROOT = join(__dirname, '..');
30
30
 
31
+ // Pre-install EnvHttpProxyAgent before any dynamic import('undici') can
32
+ // clobber Node's NODE_USE_ENV_PROXY agent (see lib/proxy-setup.ts).
33
+ // No-op off-corp (no proxy env), so non-docker deployments are unchanged.
34
+ if (process.env.HTTPS_PROXY || process.env.HTTP_PROXY ||
35
+ process.env.https_proxy || process.env.http_proxy) {
36
+ try {
37
+ const { setGlobalDispatcher, EnvHttpProxyAgent } = await import('undici');
38
+ setGlobalDispatcher(new EnvHttpProxyAgent());
39
+ } catch { /* undici unavailable — leave Node's default dispatcher */ }
40
+ }
41
+
31
42
  /** Build Next.js — install devDependencies first if missing */
32
43
  function buildNext() {
33
44
  // Check if devDependencies are installed (e.g. @tailwindcss/postcss)
@@ -2,6 +2,8 @@
2
2
  * Next.js instrumentation — runs once when the server starts.
3
3
  * Loads .env.local and prints login password.
4
4
  */
5
+ import './lib/proxy-setup'; // MUST be first — see proxy-setup.ts header.
6
+
5
7
  export async function register() {
6
8
  // Only run on server, not Edge
7
9
  if (process.env.NEXT_RUNTIME === 'nodejs') {
@@ -39,6 +39,7 @@
39
39
  * Usage: npx tsx lib/browser-bridge-standalone.ts [--forge-port=8403]
40
40
  */
41
41
 
42
+ import './proxy-setup'; // MUST be first — see proxy-setup.ts header.
42
43
  import { createServer, type IncomingMessage, type ServerResponse } from 'node:http';
43
44
  import { WebSocket, WebSocketServer } from 'ws';
44
45
  import { randomUUID, createHash } from 'node:crypto';
@@ -241,8 +241,14 @@ async function exchangeBearerToken(
241
241
  const exchangeFetchInit = { method: auth.exchange_method || 'POST', headers, body };
242
242
  let res: Response;
243
243
  if (verifyTls === false) {
244
- const { fetch: undiciFetch, Agent } = await import('undici');
245
- const dispatcher = new Agent({ connect: { rejectUnauthorized: false } });
244
+ // Per-call dispatcher bypasses the global proxy agent — route via
245
+ // ProxyAgent when a proxy is set (corp), else plain Agent (direct).
246
+ const { fetch: undiciFetch, Agent, ProxyAgent } = await import('undici');
247
+ const proxyUrl = process.env.HTTPS_PROXY || process.env.https_proxy ||
248
+ process.env.HTTP_PROXY || process.env.http_proxy;
249
+ const dispatcher = proxyUrl
250
+ ? new ProxyAgent({ uri: proxyUrl, requestTls: { rejectUnauthorized: false } })
251
+ : new Agent({ connect: { rejectUnauthorized: false } });
246
252
  res = await undiciFetch(exchangeUrl, { ...exchangeFetchInit, dispatcher }) as unknown as Response;
247
253
  } else {
248
254
  res = await fetch(exchangeUrl, exchangeFetchInit);
@@ -488,8 +494,14 @@ export async function runHttp({ tool, settings, args, connectorAuth, noTruncatio
488
494
  // fetch are version-matched; passing an external undici Agent
489
495
  // into Node's bundled global fetch fails with UND_ERR_INVALID_ARG
490
496
  // because Node 22 ships an older undici than the installed one.
491
- const { fetch: undiciFetch, Agent } = await import('undici');
492
- const dispatcher = new Agent({ connect: { rejectUnauthorized: false } });
497
+ // Per-call dispatcher bypasses the global proxy agent — route via
498
+ // ProxyAgent when a proxy is set (corp), else plain Agent (direct).
499
+ const { fetch: undiciFetch, Agent, ProxyAgent } = await import('undici');
500
+ const proxyUrl = process.env.HTTPS_PROXY || process.env.https_proxy ||
501
+ process.env.HTTP_PROXY || process.env.http_proxy;
502
+ const dispatcher = proxyUrl
503
+ ? new ProxyAgent({ uri: proxyUrl, requestTls: { rejectUnauthorized: false } })
504
+ : new Agent({ connect: { rejectUnauthorized: false } });
493
505
  res = await undiciFetch(url, { method, headers, body, signal: controller.signal, dispatcher }) as unknown as Response;
494
506
  } else {
495
507
  res = await fetch(url, { method, headers, body, signal: controller.signal });
@@ -30,6 +30,7 @@
30
30
  * keep their existing WS path; HTTP-only surfaces use SSE.
31
31
  */
32
32
 
33
+ import './proxy-setup'; // MUST be first — see proxy-setup.ts header.
33
34
  import { createServer, type IncomingMessage, type ServerResponse } from 'node:http';
34
35
  import {
35
36
  createSession, getSession, listSessions, updateSession, deleteSession, listMessages,
@@ -134,8 +134,14 @@ async function runHttpProbe(
134
134
  // NAC, ESXi …) need undici with rejectUnauthorized:false, same as http.ts.
135
135
  const fetchInit = { method, headers, body, signal: ctrl.signal };
136
136
  if (def.http?.verify_tls === false) {
137
- const { fetch: undiciFetch, Agent } = await import('undici');
138
- const dispatcher = new Agent({ connect: { rejectUnauthorized: false } });
137
+ // A per-call dispatcher bypasses the global proxy agent, so when a proxy
138
+ // is set (corp) route through ProxyAgent; else plain Agent (direct).
139
+ const { fetch: undiciFetch, Agent, ProxyAgent } = await import('undici');
140
+ const proxyUrl = process.env.HTTPS_PROXY || process.env.https_proxy ||
141
+ process.env.HTTP_PROXY || process.env.http_proxy;
142
+ const dispatcher = proxyUrl
143
+ ? new ProxyAgent({ uri: proxyUrl, requestTls: { rejectUnauthorized: false } })
144
+ : new Agent({ connect: { rejectUnauthorized: false } });
139
145
  res = await undiciFetch(url, { ...fetchInit, dispatcher } as any) as unknown as Response;
140
146
  } else {
141
147
  res = await fetch(url, fetchInit);
@@ -76,6 +76,19 @@ The WebSocket dropped (system suspend, network blip). Forge auto-reconnects afte
76
76
  gh auth login
77
77
  ```
78
78
 
79
+ ### Connector Test fails with `ENETUNREACH` behind a corp proxy
80
+ Symptom: a brand-new deployment behind a corporate proxy/ZTNA, configure a
81
+ connector (e.g. GitLab) → **Test** → `request failed: fetch failed: connect
82
+ ENETUNREACH <backend-ip>:443`.
83
+
84
+ Forge honours `HTTPS_PROXY` / `HTTP_PROXY` / `NO_PROXY` automatically — every
85
+ entry point pre-installs an `EnvHttpProxyAgent` before any code makes a request,
86
+ so a proxy set in the environment is used for all connector calls (including
87
+ `verify_tls: false` self-signed appliances like FortiNAC). Just make sure the
88
+ proxy env vars are exported for the Forge process (in Docker, via the
89
+ container's environment). Off-corp deployments with no proxy env are unaffected
90
+ — requests go direct as before.
91
+
79
92
  ### Skills not syncing
80
93
  Click "Sync" in Skills tab. Check `skillsRepoUrl` in Settings points to valid registry.
81
94
 
@@ -18,6 +18,8 @@
18
18
  * next sub-task still runs in the same cycle.
19
19
  */
20
20
 
21
+ import './proxy-setup'; // MUST be first — see proxy-setup.ts header.
22
+
21
23
  const MEMORY_TICK_SEC = 120;
22
24
 
23
25
  export interface MemorySubTask {
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Pre-install an EnvHttpProxyAgent on undici's global dispatcher BEFORE
3
+ * anything dynamic-imports the npm undici (8.x).
4
+ *
5
+ * Why: Node honours NODE_USE_ENV_PROXY by putting EnvHttpProxyAgent on
6
+ * undici v1's dispatcher symbol. The npm undici uses v2; on load its
7
+ * `if (getGlobalDispatcher() === undefined) setGlobalDispatcher(new Agent())`
8
+ * reads v2 (undefined) and writes a plain Agent into BOTH v1 and v2,
9
+ * silently clobbering Node's proxy agent — after which every fetch ignores
10
+ * HTTPS_PROXY and direct-connects (ENETUNREACH behind corp ZTNA).
11
+ * Setting v2 here makes that check non-undefined, so npm undici skips it.
12
+ *
13
+ * MUST be the FIRST import in every entry point. Off-corp (no proxy env)
14
+ * this is a no-op, so non-docker / standalone deployments are unchanged.
15
+ */
16
+ import { setGlobalDispatcher, EnvHttpProxyAgent } from 'undici';
17
+
18
+ if (process.env.HTTPS_PROXY || process.env.HTTP_PROXY ||
19
+ process.env.https_proxy || process.env.http_proxy) {
20
+ setGlobalDispatcher(new EnvHttpProxyAgent());
21
+ }
@@ -4,6 +4,7 @@
4
4
  * Runs as a single process — no duplication from Next.js workers.
5
5
  */
6
6
 
7
+ import './proxy-setup'; // MUST be first — see proxy-setup.ts header.
7
8
  import { loadSettings } from './settings';
8
9
 
9
10
  const settings = loadSettings();
@@ -24,6 +24,7 @@
24
24
  * Usage: npx tsx lib/terminal-standalone.ts
25
25
  */
26
26
 
27
+ import './proxy-setup'; // MUST be first — see proxy-setup.ts header.
27
28
  import { WebSocketServer, WebSocket } from 'ws';
28
29
  import * as pty from 'node-pty';
29
30
  import { execSync } from 'node:child_process';
@@ -12,6 +12,7 @@
12
12
  * FORGE_DATA_DIR — data directory
13
13
  */
14
14
 
15
+ import './proxy-setup'; // MUST be first — see proxy-setup.ts header.
15
16
  import { createServer, type IncomingMessage, type ServerResponse } from 'node:http';
16
17
  import { readdirSync, statSync } from 'node:fs';
17
18
  import { join, resolve } from 'node:path';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aion0/forge",
3
- "version": "0.10.87",
3
+ "version": "0.10.88",
4
4
  "description": "Unified AI workflow platform — multi-model task orchestration, persistent sessions, web terminal, remote access",
5
5
  "type": "module",
6
6
  "scripts": {