@rashidee/co2 1.3.15 → 1.3.16
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/dist/.co2-dat/app.db-shm
CHANGED
|
Binary file
|
package/dist/.co2-dat/app.db-wal
CHANGED
|
Binary file
|
package/dist/index.js
CHANGED
|
@@ -6809,7 +6809,7 @@ var {
|
|
|
6809
6809
|
// package.json
|
|
6810
6810
|
var package_default = {
|
|
6811
6811
|
name: "@rashidee/co2",
|
|
6812
|
-
version: "1.3.
|
|
6812
|
+
version: "1.3.16",
|
|
6813
6813
|
description: "Compound Context Studio \u2014 self-hosted team context authoring for CO2 projects",
|
|
6814
6814
|
type: "module",
|
|
6815
6815
|
license: "MIT",
|
|
@@ -22015,6 +22015,46 @@ function stopCicdApp(projectId) {
|
|
|
22015
22015
|
}
|
|
22016
22016
|
|
|
22017
22017
|
// ../server/src/features/cicd/app-proxy.ts
|
|
22018
|
+
var PROXY_BASE_SHIM = `<script data-co2-proxy-base-shim>(function () {
|
|
22019
|
+
var m = location.pathname.match(/^(.*?\\/app-proxy)(?:\\/|$)/)
|
|
22020
|
+
if (!m) return
|
|
22021
|
+
var prefix = m[1]
|
|
22022
|
+
function reroot(p) {
|
|
22023
|
+
return p.charAt(0) === '/' && p.charAt(1) !== '/' && p.indexOf(prefix + '/') !== 0 && p !== prefix ? prefix + p : p
|
|
22024
|
+
}
|
|
22025
|
+
var origFetch = window.fetch.bind(window)
|
|
22026
|
+
window.fetch = function (input, init) {
|
|
22027
|
+
if (typeof input === 'string') input = reroot(input)
|
|
22028
|
+
else if (input instanceof URL && input.host === location.host)
|
|
22029
|
+
input = new URL(reroot(input.pathname) + input.search + input.hash, location.origin)
|
|
22030
|
+
return origFetch(input, init)
|
|
22031
|
+
}
|
|
22032
|
+
var OrigWS = window.WebSocket
|
|
22033
|
+
function PatchedWS(url, protocols) {
|
|
22034
|
+
try {
|
|
22035
|
+
var u = new URL(url, location.href)
|
|
22036
|
+
if (u.host === location.host) {
|
|
22037
|
+
u.pathname = reroot(u.pathname)
|
|
22038
|
+
u.protocol = location.protocol === 'https:' ? 'wss:' : 'ws:'
|
|
22039
|
+
url = u.toString()
|
|
22040
|
+
}
|
|
22041
|
+
} catch (e) { /* malformed URL \u2014 let the real constructor throw */ }
|
|
22042
|
+
return protocols === undefined ? new OrigWS(url) : new OrigWS(url, protocols)
|
|
22043
|
+
}
|
|
22044
|
+
PatchedWS.prototype = OrigWS.prototype
|
|
22045
|
+
PatchedWS.CONNECTING = OrigWS.CONNECTING
|
|
22046
|
+
PatchedWS.OPEN = OrigWS.OPEN
|
|
22047
|
+
PatchedWS.CLOSING = OrigWS.CLOSING
|
|
22048
|
+
PatchedWS.CLOSED = OrigWS.CLOSED
|
|
22049
|
+
window.WebSocket = PatchedWS
|
|
22050
|
+
document.addEventListener('click', function (e) {
|
|
22051
|
+
var t = e.target
|
|
22052
|
+
var a = t && t.closest ? t.closest('a[href]') : null
|
|
22053
|
+
if (!a) return
|
|
22054
|
+
var href = a.getAttribute('href')
|
|
22055
|
+
if (href) a.setAttribute('href', reroot(href))
|
|
22056
|
+
}, true)
|
|
22057
|
+
})()</script>`;
|
|
22018
22058
|
async function proxyCicdAppRequest(db, workspacePath, projectId, prefix, subPath, req) {
|
|
22019
22059
|
const location = resolveCicdApp(db, workspacePath, projectId);
|
|
22020
22060
|
if (!location.present) throw svcError6("CICD_APP_MISSING", 404);
|
|
@@ -22047,6 +22087,13 @@ async function proxyCicdAppRequest(db, workspacePath, projectId, prefix, subPath
|
|
|
22047
22087
|
if (redirectTo?.startsWith("/") && redirectTo !== prefix && !redirectTo.startsWith(`${prefix}/`)) {
|
|
22048
22088
|
responseHeaders.set("location", `${prefix}${redirectTo}`);
|
|
22049
22089
|
}
|
|
22090
|
+
const contentType = upstream.headers.get("content-type") ?? "";
|
|
22091
|
+
if (contentType.includes("text/html")) {
|
|
22092
|
+
const html = await upstream.text();
|
|
22093
|
+
const headTag = html.match(/<head[^>]*>/i);
|
|
22094
|
+
const patched = headTag ? html.replace(headTag[0], `${headTag[0]}${PROXY_BASE_SHIM}`) : `${PROXY_BASE_SHIM}${html}`;
|
|
22095
|
+
return new Response(patched, { status: upstream.status, headers: responseHeaders });
|
|
22096
|
+
}
|
|
22050
22097
|
return new Response(upstream.body, { status: upstream.status, headers: responseHeaders });
|
|
22051
22098
|
}
|
|
22052
22099
|
var CICD_PROXY_UPGRADE_RE = /^\/api\/cicd\/(\d+)\/app-proxy(\/.*)$/;
|
package/package.json
CHANGED
|
@@ -111,18 +111,49 @@ port, application count) and STOP — the studio starts/embeds the app.
|
|
|
111
111
|
once at boot). Never cache the parsed config in a top-level `const`.
|
|
112
112
|
- The page is bare: no header, sidebar or footer (it is iframed by the studio). Light theme,
|
|
113
113
|
one nav tab per application; the tab body is that application's live log.
|
|
114
|
-
- **Path-prefix proxy awareness (MANDATORY
|
|
115
|
-
same-origin reverse proxy at `/api/cicd/<projectId>/app-proxy/` —
|
|
116
|
-
(`fetch('/api/state')`, `href="/infra"`, `ws://host/ws`) escape the
|
|
117
|
-
instead of this app and render an
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
114
|
+
- **Path-prefix proxy awareness (MANDATORY — copy the template below verbatim)**: the studio
|
|
115
|
+
embeds the page through its same-origin reverse proxy at `/api/cicd/<projectId>/app-proxy/` —
|
|
116
|
+
root-absolute client URLs (`fetch('/api/state')`, `href="/infra"`, `ws://host/ws`) escape the
|
|
117
|
+
prefix, hit the studio instead of this app and render an EMPTY BLACK PAGE in the studio.
|
|
118
|
+
Every generated page's client `<script>` MUST begin with this exact bootstrap block:
|
|
119
|
+
|
|
120
|
+
```js
|
|
121
|
+
// Proxy-aware base path — the studio serves this page at /api/cicd/<id>/app-proxy/ (main)
|
|
122
|
+
// and .../app-proxy/infra; direct access serves / and /infra. All client URLs derive from it.
|
|
123
|
+
// Regex-free ON PURPOSE — see the template-literal escaping rule below.
|
|
124
|
+
var _p = location.pathname
|
|
125
|
+
if (_p.endsWith('/infra')) _p = _p.slice(0, -6)
|
|
126
|
+
if (_p.endsWith('/')) _p = _p.slice(0, -1)
|
|
127
|
+
var BASE = _p
|
|
128
|
+
function apiUrl(path) { return BASE + path } // apiUrl('/api/state')
|
|
129
|
+
function wsUrl(path) { // wsUrl('/ws?app=x')
|
|
130
|
+
return (location.protocol === 'https:' ? 'wss://' : 'ws://') + location.host + BASE + path
|
|
131
|
+
}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
and then NEVER write a root-absolute URL anywhere in client code:
|
|
135
|
+
- every fetch goes through `apiUrl(...)` — `fetch(apiUrl('/api/state'))`, never `fetch('/api/state')`;
|
|
136
|
+
- the log stream socket is `new WebSocket(wsUrl('/ws?app='+encodeURIComponent(name)))` —
|
|
137
|
+
never a hardcoded `ws://` (mixed-content-blocked when the studio is served over HTTPS);
|
|
138
|
+
- the Infrastructure link is set from script (`infralink.href = BASE + '/infra'`), never a
|
|
139
|
+
literal `href="/infra"`.
|
|
124
140
|
Server-side routes stay unprefixed (`/api/state`, `/ws`, `/infra`) — the studio proxy strips
|
|
125
141
|
the prefix before forwarding.
|
|
142
|
+
- **Template-literal escaping in client code (MANDATORY)**: the page HTML/JS is emitted from a
|
|
143
|
+
template literal inside `server.js`, where every backslash is consumed once when the template
|
|
144
|
+
cooks — a client-side regex like `.replace(/\/$/, '')` reaches the browser as
|
|
145
|
+
`.replace(//$/, '')`, the `//` starts a comment, and the WHOLE client script dies with a
|
|
146
|
+
SyntaxError (blank page both direct AND embedded; this exact regression has shipped before).
|
|
147
|
+
Client code inside the template must therefore contain NO backslashes at all: use string
|
|
148
|
+
methods (`endsWith`, `slice`, `split`, `indexOf`) instead of regex literals, and avoid `\n`
|
|
149
|
+
in client strings (concatenate with real newlines or use `String.fromCharCode(10)`).
|
|
150
|
+
- **Syntax self-check gate (MANDATORY before flipping the generation COMPLETED)**:
|
|
151
|
+
1. `node --check server.js` (validates the server code);
|
|
152
|
+
2. start the server briefly, fetch `/` and `/infra`, extract every `<script>…</script>` block
|
|
153
|
+
and run `new Function(script)` on each — any SyntaxError means the page renders blank;
|
|
154
|
+
3. grep the client sections — any remaining `fetch('/`, `href="/`, `'ws://'` or backslash
|
|
155
|
+
inside the page template is a defect; fix via `apiUrl`/`wsUrl`/`BASE` and the escaping
|
|
156
|
+
rule first. Only then flip `CICD_GENERATION.md` to COMPLETED.
|
|
126
157
|
- Infrastructure page: when `infra` is non-empty the main page shows an "Infrastructure" link
|
|
127
158
|
that opens the separate `/infra` page in a new tab — a status-only list of supporting
|
|
128
159
|
3rd-party services, each with a live TCP-reachability dot (polled) and a console link when a
|