@abstractframework/code 0.4.2 → 0.5.0
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/README.md +8 -2
- package/bin/server.js +51 -3
- package/dist/assets/index-C5-M1QJI.js +58 -0
- package/dist/assets/index-mDhO5UHl.css +1 -0
- package/dist/index.html +2 -2
- package/package.json +3 -3
- package/dist/assets/index-BbWQ0HEw.js +0 -56
- package/dist/assets/index-D3pBYdPy.css +0 -1
package/README.md
CHANGED
|
@@ -40,8 +40,14 @@ Gateway browser session and stores only app-scoped session cookies. The raw
|
|
|
40
40
|
token is not persisted in browser settings. Vite development and the packaged
|
|
41
41
|
server use the same session proxy; the browser keeps API requests same-origin.
|
|
42
42
|
|
|
43
|
-
Choose
|
|
44
|
-
|
|
43
|
+
Choose a workflow in the toolbar: **Gateway default** (the coding agent your
|
|
44
|
+
gateway's operator set, resolved by the gateway when the turn starts), a
|
|
45
|
+
published coding agent, or, with **Show all workflows**, any registered
|
|
46
|
+
workflow. The **Files** tab shows the conversation's workspace on the gateway
|
|
47
|
+
with previews, **Settings → Stream replies** shows replies as the model writes
|
|
48
|
+
them (on gateways that support it), and **About** lists the app's and the
|
|
49
|
+
gateway's versions. Agent tasks use the composer; structured workflows use
|
|
50
|
+
**Inputs**. Questions, tool approvals, and
|
|
45
51
|
event waits appear in the conversation. Runs and history remain on the gateway
|
|
46
52
|
when you close the browser. Unsent drafts and queued turns do not survive reload.
|
|
47
53
|
|
package/bin/server.js
CHANGED
|
@@ -96,6 +96,34 @@ function isLoopbackPeer(req) {
|
|
|
96
96
|
);
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
/**
|
|
100
|
+
* The browser connection's real transport peer (req.socket.remoteAddress),
|
|
101
|
+
* IPv4-mapped IPv6 unwrapped, or "" when unknown. Headers are never read.
|
|
102
|
+
* Every request this server sends to the gateway on a browser's behalf
|
|
103
|
+
* carries it as `X-Forwarded-For`, OVERWRITING any client value (contract
|
|
104
|
+
* A-2; same rule as abstractuic's app-server). An unknown peer is refused.
|
|
105
|
+
*/
|
|
106
|
+
export function socketPeerAddress(req) {
|
|
107
|
+
let address = String(req?.socket?.remoteAddress || "").trim().toLowerCase();
|
|
108
|
+
if (address.startsWith("::ffff:") && address.includes(".")) address = address.slice(7);
|
|
109
|
+
return address;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const UNKNOWN_PEER = { detail: "Cannot determine the client address of this connection" };
|
|
113
|
+
/** Marks every request this server sends to the gateway as coming through
|
|
114
|
+
* the Code app proxy (the gateway's same-machine fail-safe keys on it). A
|
|
115
|
+
* browser-supplied value is always dropped and replaced. */
|
|
116
|
+
const APP_PROXY_HEADER = "X-AbstractFramework-App-Proxy";
|
|
117
|
+
const APP_PROXY_NAME = "code";
|
|
118
|
+
const FORWARDING_HEADERS = new Set([
|
|
119
|
+
"x-abstractframework-app-proxy",
|
|
120
|
+
"x-forwarded-for",
|
|
121
|
+
"x-forwarded-host",
|
|
122
|
+
"x-forwarded-proto",
|
|
123
|
+
"x-real-ip",
|
|
124
|
+
"forwarded",
|
|
125
|
+
]);
|
|
126
|
+
|
|
99
127
|
function connectionConfigAllowed(req, config) {
|
|
100
128
|
if (envBool(config.env, "ABSTRACTCODE_ALLOW_REMOTE_BROWSER_GATEWAY_CONFIG"))
|
|
101
129
|
return true;
|
|
@@ -387,6 +415,11 @@ function cookieValueFromSetCookie(rawHeaders, name) {
|
|
|
387
415
|
}
|
|
388
416
|
|
|
389
417
|
async function handleConnectionApi(req, res, config) {
|
|
418
|
+
const peer = socketPeerAddress(req);
|
|
419
|
+
if (!peer) {
|
|
420
|
+
sendJson(res, 400, UNKNOWN_PEER);
|
|
421
|
+
return;
|
|
422
|
+
}
|
|
390
423
|
if (req.method === "GET") {
|
|
391
424
|
const session = browserSession(req, config);
|
|
392
425
|
if (!session.sessionId) {
|
|
@@ -406,6 +439,8 @@ async function handleConnectionApi(req, res, config) {
|
|
|
406
439
|
headers: {
|
|
407
440
|
Accept: "application/json",
|
|
408
441
|
"X-AbstractGateway-Session": session.sessionId,
|
|
442
|
+
"X-Forwarded-For": peer,
|
|
443
|
+
[APP_PROXY_HEADER]: APP_PROXY_NAME,
|
|
409
444
|
},
|
|
410
445
|
},
|
|
411
446
|
undefined,
|
|
@@ -454,6 +489,8 @@ async function handleConnectionApi(req, res, config) {
|
|
|
454
489
|
Accept: "application/json",
|
|
455
490
|
"Content-Type": "application/json",
|
|
456
491
|
"Content-Length": String(body.length),
|
|
492
|
+
"X-Forwarded-For": peer,
|
|
493
|
+
[APP_PROXY_HEADER]: APP_PROXY_NAME,
|
|
457
494
|
},
|
|
458
495
|
},
|
|
459
496
|
body,
|
|
@@ -511,6 +548,8 @@ async function handleConnectionApi(req, res, config) {
|
|
|
511
548
|
"Content-Length": String(body.length),
|
|
512
549
|
"X-AbstractGateway-Session": session.sessionId,
|
|
513
550
|
"X-AbstractGateway-CSRF": session.csrfToken,
|
|
551
|
+
"X-Forwarded-For": peer,
|
|
552
|
+
[APP_PROXY_HEADER]: APP_PROXY_NAME,
|
|
514
553
|
},
|
|
515
554
|
},
|
|
516
555
|
body,
|
|
@@ -541,6 +580,11 @@ function proxyGatewayRequest(req, res, config) {
|
|
|
541
580
|
sendJson(res, 401, { detail: "Gateway sign-in required" });
|
|
542
581
|
return;
|
|
543
582
|
}
|
|
583
|
+
const peer = socketPeerAddress(req);
|
|
584
|
+
if (!peer) {
|
|
585
|
+
sendJson(res, 400, UNKNOWN_PEER);
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
544
588
|
if (mutatingMethod(req.method)) {
|
|
545
589
|
const presented = String(req.headers["x-abstractcode-csrf"] || "").trim();
|
|
546
590
|
if (!session.csrfToken || presented !== session.csrfToken) {
|
|
@@ -563,9 +607,13 @@ function proxyGatewayRequest(req, res, config) {
|
|
|
563
607
|
const headers = { ...req.headers, host: backend.url.host };
|
|
564
608
|
delete headers.cookie;
|
|
565
609
|
delete headers.authorization;
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
610
|
+
// Forwarding headers: drop every client-supplied spelling (any case, plus
|
|
611
|
+
// RFC 7239 `Forwarded` and `X-Real-IP`), then X-Forwarded-For = the socket
|
|
612
|
+
// peer, so the gateway can tell whether the browser is on its machine.
|
|
613
|
+
for (const key of Object.keys(headers))
|
|
614
|
+
if (FORWARDING_HEADERS.has(key.toLowerCase())) delete headers[key];
|
|
615
|
+
headers["x-forwarded-for"] = peer;
|
|
616
|
+
headers["x-abstractframework-app-proxy"] = APP_PROXY_NAME;
|
|
569
617
|
delete headers["x-abstractcode-csrf"];
|
|
570
618
|
headers["x-abstractgateway-session"] = session.sessionId;
|
|
571
619
|
if (mutatingMethod(req.method))
|