@rashidee/co2 1.3.10 → 1.3.12
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 +0 -0
- package/dist/.co2-dat/app.db-shm +0 -0
- package/dist/.co2-dat/app.db-wal +0 -0
- package/dist/index.js +408 -73
- package/package.json +41 -41
- package/plugin/skills/mockgen-shadcn/SKILL.md +11 -7
- package/plugin/skills/mockgen-shadcn/references/admin-layout-template.md +14 -3
- package/plugin/skills/mockgen-shadcn/references/mockup-hub-template.md +98 -40
- package/plugin/skills/mockgen-tailwind/references/mockup-hub-template.md +98 -40
- package/plugin/skills/util-gencicdscript/SKILL.md +34 -8
- package/plugin/skills/util-gencicdscript/references/cicd-app-template.md +798 -689
- package/plugin/skills/util-plancicd/SKILL.md +63 -2
- package/static/assets/{abnfDiagram-VRR7QNED-ChoWkBm-.js → abnfDiagram-VRR7QNED-Qm0Wk_H_.js} +1 -1
- package/static/assets/{arc-BOiutWhq.js → arc-Cfk6Kt0D.js} +1 -1
- package/static/assets/{architectureDiagram-ZJ3FMSHR-Dn4YKYfM.js → architectureDiagram-ZJ3FMSHR-DQTlZaUK.js} +1 -1
- package/static/assets/{blockDiagram-677ZJIJ3-BrXEfs29.js → blockDiagram-677ZJIJ3-D-4ibdoX.js} +1 -1
- package/static/assets/{c4Diagram-LMCZKHZV-ChaIzrlx.js → c4Diagram-LMCZKHZV-CHuqcdJm.js} +1 -1
- package/static/assets/channel-H9DRrduO.js +1 -0
- package/static/assets/{chunk-2Q5K7J3B-BOlnmyI2.js → chunk-2Q5K7J3B-MB9pWQe1.js} +1 -1
- package/static/assets/{chunk-32BRIVSS-BYtO1gsq.js → chunk-32BRIVSS-BttYlwU1.js} +1 -1
- package/static/assets/{chunk-5VM5RSS4-Cmx4Morj.js → chunk-5VM5RSS4-oyt0Tagd.js} +1 -1
- package/static/assets/{chunk-EX3LRPZG-BcgUofmA.js → chunk-EX3LRPZG-DaTxdTWl.js} +1 -1
- package/static/assets/{chunk-JWPE2WC7-CnNRXdvN.js → chunk-JWPE2WC7-CNgDxPAD.js} +1 -1
- package/static/assets/{chunk-MOJQB5TN-DGG6Q3Wn.js → chunk-MOJQB5TN-CoAudO3B.js} +1 -1
- package/static/assets/{chunk-RYQCIY6F-C8erKHls.js → chunk-RYQCIY6F-BrkDEEha.js} +1 -1
- package/static/assets/{chunk-V7JOEXUC-BNkIJqzu.js → chunk-V7JOEXUC-D14rGvBO.js} +1 -1
- package/static/assets/{chunk-VR4S4FIN-CGq-RseS.js → chunk-VR4S4FIN-CAtas2vF.js} +1 -1
- package/static/assets/{chunk-XXDRQBXY-DOVfEC7j.js → chunk-XXDRQBXY-DcVAnNUQ.js} +1 -1
- package/static/assets/classDiagram-OUVF2IWQ-DRNPM383.js +1 -0
- package/static/assets/classDiagram-v2-EOCWNBFH-DRNPM383.js +1 -0
- package/static/assets/{cose-bilkent-JH36ORCC-BrK3gCzK.js → cose-bilkent-JH36ORCC-B7y4MI6Z.js} +1 -1
- package/static/assets/{cynefin-VYW2F7L2-BTMYzKLg.js → cynefin-VYW2F7L2-D2mnk-iJ.js} +1 -1
- package/static/assets/{cynefinDiagram-TSTJHNR4-Df4k0_Hb.js → cynefinDiagram-TSTJHNR4-DNzQJhG7.js} +1 -1
- package/static/assets/{dagre-VKFMJZFB-0LnYnEqq.js → dagre-VKFMJZFB-NMSyTo8N.js} +1 -1
- package/static/assets/{diagram-FQU43EPY-B5nIVD5g.js → diagram-FQU43EPY-CdqS5Ike.js} +1 -1
- package/static/assets/{diagram-G47NLZAW-B2YLBzdb.js → diagram-G47NLZAW-Bsb4T5JI.js} +1 -1
- package/static/assets/{diagram-NH7WQ7WH-DPVumXIF.js → diagram-NH7WQ7WH-Z-PyzBSC.js} +1 -1
- package/static/assets/{diagram-OA4YK3LP-Da20vcLJ.js → diagram-OA4YK3LP-BZBTYbxg.js} +1 -1
- package/static/assets/{diagram-WEI45ONY-DQbTpuRZ.js → diagram-WEI45ONY-BL2LA5H_.js} +1 -1
- package/static/assets/{ebnfDiagram-CCIWWBDH-BEn1vEyb.js → ebnfDiagram-CCIWWBDH-BGtukMeo.js} +1 -1
- package/static/assets/{erDiagram-Q63AITRT-Ccg0DVBG.js → erDiagram-Q63AITRT-B6OJ4YCl.js} +1 -1
- package/static/assets/{flowDiagram-23GEKE2U-B2dMjEpa.js → flowDiagram-23GEKE2U-BBkMgJ1i.js} +1 -1
- package/static/assets/{ganttDiagram-NO4QXBWP-Kp1UsG59.js → ganttDiagram-NO4QXBWP-Cv8KHFBD.js} +1 -1
- package/static/assets/{gitGraphDiagram-IHSO6WYX-D6RB8sPf.js → gitGraphDiagram-IHSO6WYX-hYxDNjWx.js} +1 -1
- package/static/assets/{index-CM4GfOLV.css → index-DKI3BgkF.css} +1 -1
- package/static/assets/{index-GEUFVo96.js → index-DnYGyYET.js} +118 -120
- package/static/assets/{infoDiagram-FWYZ7A6U-C4ILACOF.js → infoDiagram-FWYZ7A6U-WuQIiedv.js} +1 -1
- package/static/assets/{ishikawaDiagram-FXEZZL3T-Q2Ef9fW7.js → ishikawaDiagram-FXEZZL3T-BJGOD-IY.js} +1 -1
- package/static/assets/{journeyDiagram-5HDEW3XC-B7ZV1i0L.js → journeyDiagram-5HDEW3XC-Ba_NXseS.js} +1 -1
- package/static/assets/{kanban-definition-HUTT4EX6-CjaPV_vw.js → kanban-definition-HUTT4EX6-C5RMm9uF.js} +1 -1
- package/static/assets/{linear-BELpVz92.js → linear-CLKppNoj.js} +1 -1
- package/static/assets/{mindmap-definition-LN4V7U3C-COhl-GrE.js → mindmap-definition-LN4V7U3C-ClSG_qmQ.js} +1 -1
- package/static/assets/{pegDiagram-2B236MQR-3FlTtWva.js → pegDiagram-2B236MQR-C_JEZqk3.js} +1 -1
- package/static/assets/{pieDiagram-ENE6RG2P-CLeOh_sh.js → pieDiagram-ENE6RG2P-D0lx7wDi.js} +1 -1
- package/static/assets/{quadrantDiagram-ABIIQ3AL-D2MnobFS.js → quadrantDiagram-ABIIQ3AL-BBKy0PVs.js} +1 -1
- package/static/assets/{railroadDiagram-RFXS5EU6-FjtL4fEj.js → railroadDiagram-RFXS5EU6-C1Badu5q.js} +1 -1
- package/static/assets/{requirementDiagram-TGXJPOKE-C5Y2uWRV.js → requirementDiagram-TGXJPOKE-1j59jBQH.js} +1 -1
- package/static/assets/{sankeyDiagram-HTMAVEWB-ITj9bISn.js → sankeyDiagram-HTMAVEWB-BLR5S8aZ.js} +1 -1
- package/static/assets/{sequenceDiagram-DBY2YBRQ-Ct2xTSbI.js → sequenceDiagram-DBY2YBRQ-BlqdK4qm.js} +1 -1
- package/static/assets/{sizeCapture-X5ZJPWSS-w9hGRpov.js → sizeCapture-X5ZJPWSS-C27ndGIT.js} +1 -1
- package/static/assets/{stateDiagram-2N3HPSRC-BlxDF1qr.js → stateDiagram-2N3HPSRC-DOu4536_.js} +1 -1
- package/static/assets/stateDiagram-v2-6OUMAXLB-DvP1ZCp8.js +1 -0
- package/static/assets/{swimlanes-5IMT3BWC-DFYm_SCW.js → swimlanes-5IMT3BWC-BpGitR9A.js} +2 -2
- package/static/assets/swimlanesDiagram-G3AALYLV-B5cf18Ey.js +8 -0
- package/static/assets/{timeline-definition-FHXFAJF6-johIMUxt.js → timeline-definition-FHXFAJF6-JIvdjH7z.js} +1 -1
- package/static/assets/{vennDiagram-L72KCM5P-BFk2-H5y.js → vennDiagram-L72KCM5P-DulnqLWm.js} +1 -1
- package/static/assets/{wardleyDiagram-EHGQE667-T7509MWq.js → wardleyDiagram-EHGQE667--MhZNL8M.js} +1 -1
- package/static/assets/{xychartDiagram-FW5EYKEG-DskgQ7uq.js → xychartDiagram-FW5EYKEG-CjnNgnMQ.js} +1 -1
- package/static/index.html +2 -2
- package/static/assets/channel-CKovdojM.js +0 -1
- package/static/assets/classDiagram-OUVF2IWQ-BRxqfb0_.js +0 -1
- package/static/assets/classDiagram-v2-EOCWNBFH-BRxqfb0_.js +0 -1
- package/static/assets/stateDiagram-v2-6OUMAXLB-CQVgnOlz.js +0 -1
- package/static/assets/swimlanesDiagram-G3AALYLV-DubMPSZe.js +0 -8
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.12",
|
|
6813
6813
|
description: "Compound Context Studio \u2014 self-hosted team context authoring for CO2 projects",
|
|
6814
6814
|
type: "module",
|
|
6815
6815
|
license: "MIT",
|
|
@@ -14785,6 +14785,15 @@ function resolveSession(db) {
|
|
|
14785
14785
|
await next();
|
|
14786
14786
|
};
|
|
14787
14787
|
}
|
|
14788
|
+
function findSessionUser(db, rawToken) {
|
|
14789
|
+
if (!rawToken) return null;
|
|
14790
|
+
const session = db.select().from(sessions).where(
|
|
14791
|
+
and(eq(sessions.tokenHash, hashToken(rawToken)), gt(sessions.expiresAt, (/* @__PURE__ */ new Date()).toISOString()))
|
|
14792
|
+
).limit(1).get();
|
|
14793
|
+
if (!session) return null;
|
|
14794
|
+
const user = db.select().from(users).where(eq(users.id, session.userId)).limit(1).get();
|
|
14795
|
+
return user && user.isDeleted === 0 ? user : null;
|
|
14796
|
+
}
|
|
14788
14797
|
|
|
14789
14798
|
// ../../node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
|
|
14790
14799
|
var external_exports = {};
|
|
@@ -19151,6 +19160,14 @@ var gitResolveRequestSchema = external_exports.object({
|
|
|
19151
19160
|
message: "Manual resolution requires the merged content",
|
|
19152
19161
|
path: ["content"]
|
|
19153
19162
|
});
|
|
19163
|
+
var gitPullResponseSchema = external_exports.object({
|
|
19164
|
+
status: external_exports.enum(["up_to_date", "merged", "conflicts"]),
|
|
19165
|
+
branch: external_exports.string(),
|
|
19166
|
+
/** New short HEAD hash — present only for `merged`. */
|
|
19167
|
+
commitHash: external_exports.string().optional(),
|
|
19168
|
+
/** Git's own summary (e.g. the conflict list) — display verbatim. */
|
|
19169
|
+
message: external_exports.string().optional()
|
|
19170
|
+
});
|
|
19154
19171
|
|
|
19155
19172
|
// ../core/src/schemas/import.ts
|
|
19156
19173
|
var importCandidateSchema = external_exports.object({
|
|
@@ -19905,6 +19922,64 @@ async function startMockupHub(db, workspacePath, projectId, requestHost, port) {
|
|
|
19905
19922
|
}
|
|
19906
19923
|
return toStatus({ location, running: false, activePort: targetPort }, requestHost, projectId);
|
|
19907
19924
|
}
|
|
19925
|
+
var STRIP_HEADERS = /* @__PURE__ */ new Set([
|
|
19926
|
+
"connection",
|
|
19927
|
+
"keep-alive",
|
|
19928
|
+
"proxy-authenticate",
|
|
19929
|
+
"proxy-authorization",
|
|
19930
|
+
"te",
|
|
19931
|
+
"trailer",
|
|
19932
|
+
"transfer-encoding",
|
|
19933
|
+
"upgrade",
|
|
19934
|
+
"host",
|
|
19935
|
+
"content-length",
|
|
19936
|
+
"content-encoding",
|
|
19937
|
+
"accept-encoding"
|
|
19938
|
+
]);
|
|
19939
|
+
async function proxyHubRequest(db, workspacePath, projectId, prefix, subPath, req) {
|
|
19940
|
+
const location = resolveHub(db, workspacePath, projectId);
|
|
19941
|
+
if (!location.present) throw svcError("MOCKUP_HUB_MISSING", 404);
|
|
19942
|
+
if (!location.configPort) throw svcError("MOCKUP_HUB_DOWN", 502);
|
|
19943
|
+
const url = new URL(req.url);
|
|
19944
|
+
const target = `http://127.0.0.1:${location.configPort}${subPath}${url.search}`;
|
|
19945
|
+
const headers = new Headers();
|
|
19946
|
+
req.headers.forEach((value, name) => {
|
|
19947
|
+
if (!STRIP_HEADERS.has(name.toLowerCase())) headers.set(name, value);
|
|
19948
|
+
});
|
|
19949
|
+
headers.set("x-forwarded-prefix", prefix);
|
|
19950
|
+
headers.set("x-forwarded-host", url.host);
|
|
19951
|
+
headers.set("x-forwarded-proto", url.protocol.replace(/:$/, ""));
|
|
19952
|
+
const hasBody = req.method !== "GET" && req.method !== "HEAD";
|
|
19953
|
+
let upstream;
|
|
19954
|
+
try {
|
|
19955
|
+
upstream = await fetch(target, {
|
|
19956
|
+
method: req.method,
|
|
19957
|
+
headers,
|
|
19958
|
+
body: hasBody ? await req.arrayBuffer() : void 0,
|
|
19959
|
+
redirect: "manual"
|
|
19960
|
+
});
|
|
19961
|
+
} catch {
|
|
19962
|
+
throw svcError("MOCKUP_HUB_DOWN", 502);
|
|
19963
|
+
}
|
|
19964
|
+
const responseHeaders = new Headers();
|
|
19965
|
+
upstream.headers.forEach((value, name) => {
|
|
19966
|
+
if (!STRIP_HEADERS.has(name.toLowerCase())) responseHeaders.set(name, value);
|
|
19967
|
+
});
|
|
19968
|
+
const redirectTo = responseHeaders.get("location");
|
|
19969
|
+
if (redirectTo?.startsWith("/") && redirectTo !== prefix && !redirectTo.startsWith(`${prefix}/`)) {
|
|
19970
|
+
responseHeaders.set("location", `${prefix}${redirectTo}`);
|
|
19971
|
+
}
|
|
19972
|
+
const prefixAware = upstream.headers.get("x-hub-prefix") !== null;
|
|
19973
|
+
responseHeaders.delete("x-hub-prefix");
|
|
19974
|
+
if (!prefixAware && responseHeaders.get("content-type")?.includes("text/html")) {
|
|
19975
|
+
const html = (await upstream.text()).replace(
|
|
19976
|
+
/(href|src|action|hx-get|hx-post|hx-put|hx-patch|hx-delete)="(\/[^"]*)"/g,
|
|
19977
|
+
(match2, attr, url2) => url2 === prefix || url2.startsWith(`${prefix}/`) ? match2 : `${attr}="${prefix}${url2}"`
|
|
19978
|
+
);
|
|
19979
|
+
return new Response(html, { status: upstream.status, headers: responseHeaders });
|
|
19980
|
+
}
|
|
19981
|
+
return new Response(upstream.body, { status: upstream.status, headers: responseHeaders });
|
|
19982
|
+
}
|
|
19908
19983
|
function stopMockupHub(projectId) {
|
|
19909
19984
|
const child = spawnedHubs.get(projectId);
|
|
19910
19985
|
if (!child) return false;
|
|
@@ -20201,6 +20276,24 @@ function artifactRoutes(db, workspacePath) {
|
|
|
20201
20276
|
return errorResponse(c, e);
|
|
20202
20277
|
}
|
|
20203
20278
|
});
|
|
20279
|
+
app.all(
|
|
20280
|
+
"/mockup-proxy/:projectId",
|
|
20281
|
+
(c) => (
|
|
20282
|
+
// Normalize to the trailing-slash form so the prefix always maps to the hub root.
|
|
20283
|
+
c.redirect(`${c.req.path}/`, 301)
|
|
20284
|
+
)
|
|
20285
|
+
);
|
|
20286
|
+
app.all("/mockup-proxy/:projectId/*", async (c) => {
|
|
20287
|
+
const projectId = Number(c.req.param("projectId"));
|
|
20288
|
+
if (!Number.isInteger(projectId) || projectId <= 0) return c.json({ code: "INVALID_ID" }, 400);
|
|
20289
|
+
const prefix = `/api/artifacts/mockup-proxy/${projectId}`;
|
|
20290
|
+
const subPath = c.req.path.slice(prefix.length) || "/";
|
|
20291
|
+
try {
|
|
20292
|
+
return await proxyHubRequest(db, workspacePath, projectId, prefix, subPath, c.req.raw);
|
|
20293
|
+
} catch (e) {
|
|
20294
|
+
return errorResponse(c, e);
|
|
20295
|
+
}
|
|
20296
|
+
});
|
|
20204
20297
|
app.get("/:id/content", (c) => {
|
|
20205
20298
|
const id = Number(c.req.param("id"));
|
|
20206
20299
|
if (Number.isNaN(id)) return c.json({ code: "INVALID_ID" }, 400);
|
|
@@ -21770,16 +21863,19 @@ function brainstormRoutes(db, workspacePath) {
|
|
|
21770
21863
|
import { existsSync as existsSync13, readFileSync as readFileSync8 } from "fs";
|
|
21771
21864
|
import { join as join14 } from "path";
|
|
21772
21865
|
|
|
21866
|
+
// ../server/src/features/cicd/app-proxy.ts
|
|
21867
|
+
import { connect as connect2 } from "net";
|
|
21868
|
+
|
|
21773
21869
|
// ../server/src/features/cicd/app-runner.ts
|
|
21774
21870
|
import { spawn as spawn2 } from "child_process";
|
|
21775
21871
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
21776
|
-
import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
|
|
21872
|
+
import { existsSync as existsSync8, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
21777
21873
|
import { request as httpRequest2 } from "http";
|
|
21778
21874
|
import { join as join9 } from "path";
|
|
21779
21875
|
function svcError6(code, status) {
|
|
21780
21876
|
return Object.assign(new Error(code), { code, status });
|
|
21781
21877
|
}
|
|
21782
|
-
var DEFAULT_CICD_APP_PORT =
|
|
21878
|
+
var DEFAULT_CICD_APP_PORT = 5e3;
|
|
21783
21879
|
var spawnedApps = /* @__PURE__ */ new Map();
|
|
21784
21880
|
var guardTokens = /* @__PURE__ */ new Map();
|
|
21785
21881
|
function killSpawnedApps() {
|
|
@@ -21830,55 +21926,81 @@ function probeApp(port, timeoutMs = 1e3) {
|
|
|
21830
21926
|
req.end();
|
|
21831
21927
|
});
|
|
21832
21928
|
}
|
|
21833
|
-
function toStatus2(location, running,
|
|
21834
|
-
const hostname = requestHost.replace(/:\d+$/, "") || "localhost";
|
|
21929
|
+
function toStatus2(location, running, projectId) {
|
|
21835
21930
|
return {
|
|
21836
21931
|
present: location.present,
|
|
21837
21932
|
running,
|
|
21838
21933
|
port: location.present ? location.port : null,
|
|
21839
|
-
|
|
21934
|
+
// Same-origin proxy path (app-proxy.ts routes) — works locally AND behind HTTPS/reverse
|
|
21935
|
+
// proxies; the iframe and "Open in new tab" both load through it.
|
|
21936
|
+
url: location.present ? `/api/cicd/${projectId}/app-proxy/` : null
|
|
21840
21937
|
};
|
|
21841
21938
|
}
|
|
21842
|
-
async function getCicdAppStatus(db, workspacePath, projectId
|
|
21939
|
+
async function getCicdAppStatus(db, workspacePath, projectId) {
|
|
21843
21940
|
const location = resolveCicdApp(db, workspacePath, projectId);
|
|
21844
21941
|
const running = location.present ? await probeApp(location.port) : false;
|
|
21845
|
-
return toStatus2(location, running,
|
|
21942
|
+
return toStatus2(location, running, projectId);
|
|
21846
21943
|
}
|
|
21847
|
-
async function startCicdApp(db, workspacePath, projectId,
|
|
21944
|
+
async function startCicdApp(db, workspacePath, projectId, studioPort, port) {
|
|
21848
21945
|
const location = resolveCicdApp(db, workspacePath, projectId);
|
|
21849
21946
|
if (!location.present) throw svcError6("CICD_APP_MISSING", 404);
|
|
21850
|
-
|
|
21947
|
+
const targetPort = port ?? location.port;
|
|
21948
|
+
if (targetPort !== location.port) {
|
|
21949
|
+
const configPath = join9(location.appDir, "cicd.config.json");
|
|
21950
|
+
let config = {};
|
|
21951
|
+
try {
|
|
21952
|
+
config = JSON.parse(readFileSync6(configPath, "utf8"));
|
|
21953
|
+
} catch {
|
|
21954
|
+
}
|
|
21955
|
+
config.port = targetPort;
|
|
21956
|
+
try {
|
|
21957
|
+
writeFileSync5(configPath, `${JSON.stringify(config, null, 2)}
|
|
21958
|
+
`);
|
|
21959
|
+
} catch {
|
|
21960
|
+
}
|
|
21961
|
+
location.port = targetPort;
|
|
21962
|
+
}
|
|
21963
|
+
if (await probeApp(targetPort)) {
|
|
21964
|
+
location.port = targetPort;
|
|
21965
|
+
return toStatus2(location, true, projectId);
|
|
21966
|
+
}
|
|
21851
21967
|
const existing = spawnedApps.get(projectId);
|
|
21852
|
-
if (
|
|
21853
|
-
|
|
21854
|
-
|
|
21855
|
-
|
|
21856
|
-
|
|
21857
|
-
|
|
21858
|
-
|
|
21859
|
-
...process.env,
|
|
21860
|
-
PORT: String(location.port),
|
|
21861
|
-
CO2_STUDIO_URL: `http://127.0.0.1:${studioPort}`,
|
|
21862
|
-
CO2_PROJECT_ID: String(projectId),
|
|
21863
|
-
CO2_GUARD_TOKEN: token
|
|
21864
|
-
}
|
|
21865
|
-
});
|
|
21866
|
-
child.on("error", () => {
|
|
21867
|
-
spawnedApps.delete(projectId);
|
|
21868
|
-
guardTokens.delete(projectId);
|
|
21869
|
-
});
|
|
21870
|
-
child.on("exit", () => {
|
|
21871
|
-
spawnedApps.delete(projectId);
|
|
21872
|
-
guardTokens.delete(projectId);
|
|
21873
|
-
});
|
|
21874
|
-
spawnedApps.set(projectId, child);
|
|
21875
|
-
db.update(cicdSetups).set({ appPort: location.port, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq(cicdSetups.projectId, projectId)).run();
|
|
21968
|
+
if (existing && existing.exitCode === null) {
|
|
21969
|
+
try {
|
|
21970
|
+
existing.kill();
|
|
21971
|
+
} catch {
|
|
21972
|
+
}
|
|
21973
|
+
spawnedApps.delete(projectId);
|
|
21974
|
+
guardTokens.delete(projectId);
|
|
21876
21975
|
}
|
|
21976
|
+
const token = randomBytes2(24).toString("hex");
|
|
21977
|
+
guardTokens.set(projectId, token);
|
|
21978
|
+
const child = spawn2(process.execPath, ["server.js"], {
|
|
21979
|
+
cwd: location.appDir,
|
|
21980
|
+
stdio: "ignore",
|
|
21981
|
+
env: {
|
|
21982
|
+
...process.env,
|
|
21983
|
+
PORT: String(location.port),
|
|
21984
|
+
CO2_STUDIO_URL: `http://127.0.0.1:${studioPort}`,
|
|
21985
|
+
CO2_PROJECT_ID: String(projectId),
|
|
21986
|
+
CO2_GUARD_TOKEN: token
|
|
21987
|
+
}
|
|
21988
|
+
});
|
|
21989
|
+
child.on("error", () => {
|
|
21990
|
+
spawnedApps.delete(projectId);
|
|
21991
|
+
guardTokens.delete(projectId);
|
|
21992
|
+
});
|
|
21993
|
+
child.on("exit", () => {
|
|
21994
|
+
spawnedApps.delete(projectId);
|
|
21995
|
+
guardTokens.delete(projectId);
|
|
21996
|
+
});
|
|
21997
|
+
spawnedApps.set(projectId, child);
|
|
21998
|
+
db.update(cicdSetups).set({ appPort: location.port, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq(cicdSetups.projectId, projectId)).run();
|
|
21877
21999
|
for (let attempt = 0; attempt < 20; attempt++) {
|
|
21878
|
-
if (await probeApp(location.port, 500)) return toStatus2(location, true,
|
|
22000
|
+
if (await probeApp(location.port, 500)) return toStatus2(location, true, projectId);
|
|
21879
22001
|
await new Promise((r) => setTimeout(r, 250));
|
|
21880
22002
|
}
|
|
21881
|
-
return toStatus2(location, false,
|
|
22003
|
+
return toStatus2(location, false, projectId);
|
|
21882
22004
|
}
|
|
21883
22005
|
function stopCicdApp(projectId) {
|
|
21884
22006
|
const child = spawnedApps.get(projectId);
|
|
@@ -21892,13 +22014,103 @@ function stopCicdApp(projectId) {
|
|
|
21892
22014
|
return true;
|
|
21893
22015
|
}
|
|
21894
22016
|
|
|
22017
|
+
// ../server/src/features/cicd/app-proxy.ts
|
|
22018
|
+
async function proxyCicdAppRequest(db, workspacePath, projectId, prefix, subPath, req) {
|
|
22019
|
+
const location = resolveCicdApp(db, workspacePath, projectId);
|
|
22020
|
+
if (!location.present) throw svcError6("CICD_APP_MISSING", 404);
|
|
22021
|
+
const url = new URL(req.url);
|
|
22022
|
+
const target = `http://127.0.0.1:${location.port}${subPath}${url.search}`;
|
|
22023
|
+
const headers = new Headers();
|
|
22024
|
+
req.headers.forEach((value, name) => {
|
|
22025
|
+
if (!STRIP_HEADERS.has(name.toLowerCase())) headers.set(name, value);
|
|
22026
|
+
});
|
|
22027
|
+
headers.set("x-forwarded-prefix", prefix);
|
|
22028
|
+
headers.set("x-forwarded-host", url.host);
|
|
22029
|
+
headers.set("x-forwarded-proto", url.protocol.replace(/:$/, ""));
|
|
22030
|
+
const hasBody = req.method !== "GET" && req.method !== "HEAD";
|
|
22031
|
+
let upstream;
|
|
22032
|
+
try {
|
|
22033
|
+
upstream = await fetch(target, {
|
|
22034
|
+
method: req.method,
|
|
22035
|
+
headers,
|
|
22036
|
+
body: hasBody ? await req.arrayBuffer() : void 0,
|
|
22037
|
+
redirect: "manual"
|
|
22038
|
+
});
|
|
22039
|
+
} catch {
|
|
22040
|
+
throw svcError6("CICD_APP_DOWN", 502);
|
|
22041
|
+
}
|
|
22042
|
+
const responseHeaders = new Headers();
|
|
22043
|
+
upstream.headers.forEach((value, name) => {
|
|
22044
|
+
if (!STRIP_HEADERS.has(name.toLowerCase())) responseHeaders.set(name, value);
|
|
22045
|
+
});
|
|
22046
|
+
const redirectTo = responseHeaders.get("location");
|
|
22047
|
+
if (redirectTo?.startsWith("/") && redirectTo !== prefix && !redirectTo.startsWith(`${prefix}/`)) {
|
|
22048
|
+
responseHeaders.set("location", `${prefix}${redirectTo}`);
|
|
22049
|
+
}
|
|
22050
|
+
return new Response(upstream.body, { status: upstream.status, headers: responseHeaders });
|
|
22051
|
+
}
|
|
22052
|
+
var CICD_PROXY_UPGRADE_RE = /^\/api\/cicd\/(\d+)\/app-proxy(\/.*)$/;
|
|
22053
|
+
function sessionTokenFromCookieHeader(cookieHeader) {
|
|
22054
|
+
if (!cookieHeader) return void 0;
|
|
22055
|
+
for (const part of cookieHeader.split(";")) {
|
|
22056
|
+
const [name, ...rest] = part.trim().split("=");
|
|
22057
|
+
if (name === SESSION_COOKIE) return rest.join("=");
|
|
22058
|
+
}
|
|
22059
|
+
return void 0;
|
|
22060
|
+
}
|
|
22061
|
+
function handleCicdProxyUpgrade(db, workspacePath, req, socket, head) {
|
|
22062
|
+
const url = new URL(req.url ?? "", "http://localhost");
|
|
22063
|
+
const match2 = url.pathname.match(CICD_PROXY_UPGRADE_RE);
|
|
22064
|
+
if (!match2) {
|
|
22065
|
+
socket.destroy();
|
|
22066
|
+
return;
|
|
22067
|
+
}
|
|
22068
|
+
const user = findSessionUser(db, sessionTokenFromCookieHeader(req.headers.cookie));
|
|
22069
|
+
if (!user || user.mustChangePassword) {
|
|
22070
|
+
socket.write("HTTP/1.1 401 Unauthorized\r\nConnection: close\r\n\r\n");
|
|
22071
|
+
socket.destroy();
|
|
22072
|
+
return;
|
|
22073
|
+
}
|
|
22074
|
+
let port;
|
|
22075
|
+
try {
|
|
22076
|
+
const location = resolveCicdApp(db, workspacePath, Number(match2[1]));
|
|
22077
|
+
if (!location.present) {
|
|
22078
|
+
socket.destroy();
|
|
22079
|
+
return;
|
|
22080
|
+
}
|
|
22081
|
+
port = location.port;
|
|
22082
|
+
} catch {
|
|
22083
|
+
socket.destroy();
|
|
22084
|
+
return;
|
|
22085
|
+
}
|
|
22086
|
+
const upstream = connect2(port, "127.0.0.1", () => {
|
|
22087
|
+
let raw2 = `${req.method} ${match2[2]}${url.search} HTTP/1.1\r
|
|
22088
|
+
`;
|
|
22089
|
+
for (let i = 0; i < req.rawHeaders.length; i += 2) {
|
|
22090
|
+
const name = req.rawHeaders[i];
|
|
22091
|
+
const value = name.toLowerCase() === "host" ? `127.0.0.1:${port}` : req.rawHeaders[i + 1];
|
|
22092
|
+
raw2 += `${name}: ${value}\r
|
|
22093
|
+
`;
|
|
22094
|
+
}
|
|
22095
|
+
upstream.write(`${raw2}\r
|
|
22096
|
+
`);
|
|
22097
|
+
if (head.length > 0) upstream.write(head);
|
|
22098
|
+
socket.pipe(upstream);
|
|
22099
|
+
upstream.pipe(socket);
|
|
22100
|
+
});
|
|
22101
|
+
upstream.on("error", () => socket.destroy());
|
|
22102
|
+
socket.on("error", () => upstream.destroy());
|
|
22103
|
+
upstream.on("close", () => socket.destroy());
|
|
22104
|
+
socket.on("close", () => upstream.destroy());
|
|
22105
|
+
}
|
|
22106
|
+
|
|
21895
22107
|
// ../server/src/features/cicd/guard.ts
|
|
21896
22108
|
import { execFileSync as execFileSync2 } from "child_process";
|
|
21897
22109
|
import { existsSync as existsSync10 } from "fs";
|
|
21898
22110
|
import { join as join11 } from "path";
|
|
21899
22111
|
|
|
21900
22112
|
// ../server/src/features/prd-wizard/service.ts
|
|
21901
|
-
import { existsSync as existsSync9, mkdirSync as mkdirSync3, statSync as statSync2, writeFileSync as
|
|
22113
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync3, statSync as statSync2, writeFileSync as writeFileSync6 } from "fs";
|
|
21902
22114
|
import { join as join10 } from "path";
|
|
21903
22115
|
function svcError7(code, status) {
|
|
21904
22116
|
return Object.assign(new Error(code), { code, status });
|
|
@@ -21976,7 +22188,7 @@ function generatePrdWizardMarkdown(db, workspacePath, requestId, targetModule) {
|
|
|
21976
22188
|
`execution/${filename}`,
|
|
21977
22189
|
targetModule
|
|
21978
22190
|
);
|
|
21979
|
-
|
|
22191
|
+
writeFileSync6(filePath, markdown, "utf8");
|
|
21980
22192
|
db.update(prdWizardRequests).set({
|
|
21981
22193
|
generatedMarkdownPath: filePath,
|
|
21982
22194
|
status: "generated",
|
|
@@ -22213,7 +22425,7 @@ function evaluateBuildGuard(db, workspacePath, projectId, versionId) {
|
|
|
22213
22425
|
}
|
|
22214
22426
|
|
|
22215
22427
|
// ../server/src/features/cicd/service.ts
|
|
22216
|
-
import { appendFileSync, existsSync as existsSync12, mkdirSync as mkdirSync4, statSync as statSync4, writeFileSync as
|
|
22428
|
+
import { appendFileSync, existsSync as existsSync12, mkdirSync as mkdirSync4, statSync as statSync4, writeFileSync as writeFileSync7 } from "fs";
|
|
22217
22429
|
import { join as join13 } from "path";
|
|
22218
22430
|
|
|
22219
22431
|
// ../server/src/features/batch-job/constants.ts
|
|
@@ -22356,7 +22568,7 @@ function requestPlan(db, workspacePath, projectId, userId) {
|
|
|
22356
22568
|
const filename = `cicd-plan-${setup.id}-${Date.now()}.md`;
|
|
22357
22569
|
const filePath = join13(executionDir, filename);
|
|
22358
22570
|
const relative3 = `execution/${filename}`;
|
|
22359
|
-
|
|
22571
|
+
writeFileSync7(
|
|
22360
22572
|
filePath,
|
|
22361
22573
|
buildPlanRequestMarkdown(
|
|
22362
22574
|
project.name,
|
|
@@ -22516,9 +22728,12 @@ function parseProjectId(c) {
|
|
|
22516
22728
|
const id = Number(c.req.param("projectId"));
|
|
22517
22729
|
return Number.isNaN(id) ? null : id;
|
|
22518
22730
|
}
|
|
22731
|
+
var startBodySchema = external_exports.object({
|
|
22732
|
+
port: external_exports.coerce.number().int().min(1024).max(65535).optional()
|
|
22733
|
+
});
|
|
22519
22734
|
function cicdRoutes(db, workspacePath, studioPort) {
|
|
22520
22735
|
const app = new Hono2();
|
|
22521
|
-
const toDto8 = async (
|
|
22736
|
+
const toDto8 = async (setup) => {
|
|
22522
22737
|
const planFile = setup.planRequestPath?.split(/[\\/]/).pop();
|
|
22523
22738
|
return {
|
|
22524
22739
|
id: setup.id,
|
|
@@ -22530,7 +22745,7 @@ function cicdRoutes(db, workspacePath, studioPort) {
|
|
|
22530
22745
|
approvedAt: setup.approvedAt,
|
|
22531
22746
|
generatedAt: setup.generatedAt,
|
|
22532
22747
|
stale: isStale(db, setup),
|
|
22533
|
-
app: await getCicdAppStatus(db, workspacePath, setup.projectId
|
|
22748
|
+
app: await getCicdAppStatus(db, workspacePath, setup.projectId),
|
|
22534
22749
|
createdAt: setup.createdAt,
|
|
22535
22750
|
updatedAt: setup.updatedAt
|
|
22536
22751
|
};
|
|
@@ -22548,7 +22763,7 @@ function cicdRoutes(db, workspacePath, studioPort) {
|
|
|
22548
22763
|
startCicdWatcher(db, workspacePath, setup.id, "generation", user.id);
|
|
22549
22764
|
}
|
|
22550
22765
|
const fresh = getOrCreateSetup(db, projectId, user.id);
|
|
22551
|
-
return c.json(await toDto8(
|
|
22766
|
+
return c.json(await toDto8(fresh));
|
|
22552
22767
|
} catch (e) {
|
|
22553
22768
|
return errorResponse3(c, e);
|
|
22554
22769
|
}
|
|
@@ -22571,7 +22786,7 @@ function cicdRoutes(db, workspacePath, studioPort) {
|
|
|
22571
22786
|
if (fresh.status === "generating" && !hasCicdWatcher(fresh.id, "generation")) {
|
|
22572
22787
|
startCicdWatcher(db, workspacePath, fresh.id, "generation", user.id);
|
|
22573
22788
|
}
|
|
22574
|
-
return c.json(await toDto8(
|
|
22789
|
+
return c.json(await toDto8(fresh));
|
|
22575
22790
|
} catch (e) {
|
|
22576
22791
|
return errorResponse3(c, e);
|
|
22577
22792
|
}
|
|
@@ -22625,10 +22840,11 @@ function cicdRoutes(db, workspacePath, studioPort) {
|
|
|
22625
22840
|
app.post("/:projectId/app/start", async (c) => {
|
|
22626
22841
|
const projectId = parseProjectId(c);
|
|
22627
22842
|
if (projectId === null) return c.json({ code: "INVALID_ID" }, 400);
|
|
22843
|
+
const parsed = startBodySchema.safeParse(await c.req.json().catch(() => ({})));
|
|
22844
|
+
if (!parsed.success) return c.json({ code: "INVALID_PORT" }, 400);
|
|
22845
|
+
if (parsed.data.port === studioPort) return c.json({ code: "CICD_PORT_CONFLICT" }, 400);
|
|
22628
22846
|
try {
|
|
22629
|
-
return c.json(
|
|
22630
|
-
await startCicdApp(db, workspacePath, projectId, c.req.header("host") ?? "", studioPort)
|
|
22631
|
-
);
|
|
22847
|
+
return c.json(await startCicdApp(db, workspacePath, projectId, studioPort, parsed.data.port));
|
|
22632
22848
|
} catch (e) {
|
|
22633
22849
|
return errorResponse3(c, e);
|
|
22634
22850
|
}
|
|
@@ -22638,9 +22854,25 @@ function cicdRoutes(db, workspacePath, studioPort) {
|
|
|
22638
22854
|
if (projectId === null) return c.json({ code: "INVALID_ID" }, 400);
|
|
22639
22855
|
try {
|
|
22640
22856
|
stopCicdApp(projectId);
|
|
22641
|
-
return c.json(
|
|
22642
|
-
|
|
22643
|
-
);
|
|
22857
|
+
return c.json(await getCicdAppStatus(db, workspacePath, projectId));
|
|
22858
|
+
} catch (e) {
|
|
22859
|
+
return errorResponse3(c, e);
|
|
22860
|
+
}
|
|
22861
|
+
});
|
|
22862
|
+
app.all(
|
|
22863
|
+
"/:projectId/app-proxy",
|
|
22864
|
+
(c) => (
|
|
22865
|
+
// Normalize to the trailing-slash form so relative URLs in the page resolve under the prefix.
|
|
22866
|
+
c.redirect(`${c.req.path}/`, 301)
|
|
22867
|
+
)
|
|
22868
|
+
);
|
|
22869
|
+
app.all("/:projectId/app-proxy/*", async (c) => {
|
|
22870
|
+
const projectId = parseProjectId(c);
|
|
22871
|
+
if (projectId === null) return c.json({ code: "INVALID_ID" }, 400);
|
|
22872
|
+
const prefix = `/api/cicd/${projectId}/app-proxy`;
|
|
22873
|
+
const subPath = c.req.path.slice(prefix.length) || "/";
|
|
22874
|
+
try {
|
|
22875
|
+
return await proxyCicdAppRequest(db, workspacePath, projectId, prefix, subPath, c.req.raw);
|
|
22644
22876
|
} catch (e) {
|
|
22645
22877
|
return errorResponse3(c, e);
|
|
22646
22878
|
}
|
|
@@ -22666,11 +22898,11 @@ function cicdGuardRoutes(db, workspacePath) {
|
|
|
22666
22898
|
}
|
|
22667
22899
|
|
|
22668
22900
|
// ../server/src/features/project/service.ts
|
|
22669
|
-
import { existsSync as existsSync15, mkdirSync as mkdirSync6, readFileSync as readFileSync10, writeFileSync as
|
|
22901
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync6, readFileSync as readFileSync10, writeFileSync as writeFileSync9 } from "fs";
|
|
22670
22902
|
import { join as join16 } from "path";
|
|
22671
22903
|
|
|
22672
22904
|
// ../server/src/features/project-config/service.ts
|
|
22673
|
-
import { existsSync as existsSync14, mkdirSync as mkdirSync5, readFileSync as readFileSync9, writeFileSync as
|
|
22905
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync5, readFileSync as readFileSync9, writeFileSync as writeFileSync8 } from "fs";
|
|
22674
22906
|
import { join as join15 } from "path";
|
|
22675
22907
|
function svcError9(code, status) {
|
|
22676
22908
|
return Object.assign(new Error(code), { code, status });
|
|
@@ -22694,7 +22926,7 @@ function resolveImportedByName(db, importedBy) {
|
|
|
22694
22926
|
function writeProjectFile(workspacePath, folder, fileName, content) {
|
|
22695
22927
|
const dir = join15(workspacePath, folder);
|
|
22696
22928
|
mkdirSync5(dir, { recursive: true });
|
|
22697
|
-
|
|
22929
|
+
writeFileSync8(join15(dir, fileName), content, "utf8");
|
|
22698
22930
|
}
|
|
22699
22931
|
function toDto5(db, row, field) {
|
|
22700
22932
|
return {
|
|
@@ -22861,8 +23093,8 @@ function scaffoldProjectFolder(workspacePath, folderName, name, baseIdea) {
|
|
|
22861
23093
|
"# Feedback",
|
|
22862
23094
|
"<!-- Human answers appear here -->"
|
|
22863
23095
|
].join("\n");
|
|
22864
|
-
|
|
22865
|
-
|
|
23096
|
+
writeFileSync9(join16(projectDir2, `${folderName}.brainstorm.md`), template, "utf8");
|
|
23097
|
+
writeFileSync9(
|
|
22866
23098
|
join16(projectDir2, "context", "references", "brainstorm-protocol.md"),
|
|
22867
23099
|
"# Brainstorm Protocol\n\n[Installed by the co2 plugin]\n",
|
|
22868
23100
|
"utf8"
|
|
@@ -22900,7 +23132,7 @@ function updateBrainstormMd(db, workspacePath, projectId, content) {
|
|
|
22900
23132
|
project.workspaceFolderName,
|
|
22901
23133
|
`${project.workspaceFolderName}.brainstorm.md`
|
|
22902
23134
|
);
|
|
22903
|
-
|
|
23135
|
+
writeFileSync9(filePath, content, "utf8");
|
|
22904
23136
|
}
|
|
22905
23137
|
function listProjects(db, archived) {
|
|
22906
23138
|
return db.select({
|
|
@@ -23023,7 +23255,7 @@ function regenerateCO2Files(db, workspacePath, projectId) {
|
|
|
23023
23255
|
const projectDir2 = join16(workspacePath, project.workspaceFolderName);
|
|
23024
23256
|
mkdirSync6(projectDir2, { recursive: true });
|
|
23025
23257
|
const apps = db.select().from(applications).where(and(eq(applications.projectId, projectId), eq(applications.isDeleted, 0))).all();
|
|
23026
|
-
|
|
23258
|
+
writeFileSync9(
|
|
23027
23259
|
join16(projectDir2, "CLAUDE.md"),
|
|
23028
23260
|
project.rawClaudeMd ?? buildClaudeMd(project.name, apps),
|
|
23029
23261
|
"utf8"
|
|
@@ -23040,7 +23272,7 @@ function regenerateCO2Files(db, workspacePath, projectId) {
|
|
|
23040
23272
|
const appRel = resolveAppFolderName(projectDir2, app.name);
|
|
23041
23273
|
const appDir = join16(projectDir2, appRel, "context");
|
|
23042
23274
|
mkdirSync6(appDir, { recursive: true });
|
|
23043
|
-
|
|
23275
|
+
writeFileSync9(
|
|
23044
23276
|
join16(appDir, "PRD.md"),
|
|
23045
23277
|
prdSnapshot[String(app.id)] ?? buildPrdMd(app.name, mods, loadPrdSections(db, app.id)),
|
|
23046
23278
|
"utf8"
|
|
@@ -23433,7 +23665,7 @@ function dashboardRoutes(db) {
|
|
|
23433
23665
|
}
|
|
23434
23666
|
|
|
23435
23667
|
// ../server/src/features/defect/service.ts
|
|
23436
|
-
import { existsSync as existsSync16, mkdirSync as mkdirSync7, readFileSync as readFileSync11, writeFileSync as
|
|
23668
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync7, readFileSync as readFileSync11, writeFileSync as writeFileSync10 } from "fs";
|
|
23437
23669
|
import { join as join17 } from "path";
|
|
23438
23670
|
|
|
23439
23671
|
// ../server/src/features/project-import/bug-md.ts
|
|
@@ -23865,7 +24097,7 @@ function writeBugMdFile(db, workspacePath, app, projectFolderName) {
|
|
|
23865
24097
|
const appRel = resolveAppFolderName(projectDir2, app.name);
|
|
23866
24098
|
const appContextDir = join17(projectDir2, appRel, "context");
|
|
23867
24099
|
mkdirSync7(appContextDir, { recursive: true });
|
|
23868
|
-
|
|
24100
|
+
writeFileSync10(join17(appContextDir, "BUG.md"), content, "utf8");
|
|
23869
24101
|
return { content, bugCount: bugs.length };
|
|
23870
24102
|
}
|
|
23871
24103
|
function syncBugStatusesFromMaster(db, trackingFilePath, applicationId) {
|
|
@@ -23909,7 +24141,7 @@ function syncBugMdToDisk(db, workspacePath, applicationId, bugId, mode = "replac
|
|
|
23909
24141
|
).all().find((m) => m.name.toLowerCase() === bug.module?.toLowerCase())?.category : void 0) ?? null;
|
|
23910
24142
|
const merged = mergeBugIntoBugMd(readFileSync11(file, "utf8"), bug, semver, category, mode);
|
|
23911
24143
|
if (merged !== null) {
|
|
23912
|
-
|
|
24144
|
+
writeFileSync10(file, merged, "utf8");
|
|
23913
24145
|
return;
|
|
23914
24146
|
}
|
|
23915
24147
|
}
|
|
@@ -24043,7 +24275,7 @@ function defectRoutes(db, workspacePath) {
|
|
|
24043
24275
|
}
|
|
24044
24276
|
|
|
24045
24277
|
// ../server/src/features/document/service.ts
|
|
24046
|
-
import { existsSync as existsSync17, mkdirSync as mkdirSync8, readFileSync as readFileSync12, writeFileSync as
|
|
24278
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync8, readFileSync as readFileSync12, writeFileSync as writeFileSync11 } from "fs";
|
|
24047
24279
|
import { join as join18 } from "path";
|
|
24048
24280
|
function svcError12(code, status) {
|
|
24049
24281
|
return Object.assign(new Error(code), { code, status });
|
|
@@ -24056,7 +24288,7 @@ function uploadDocument(db, dataDir, projectId, file) {
|
|
|
24056
24288
|
mkdirSync8(dir, { recursive: true });
|
|
24057
24289
|
const safeName = file.name.replace(/[^a-zA-Z0-9._-]/g, "_");
|
|
24058
24290
|
const stamped = `${Date.now()}_${safeName}`;
|
|
24059
|
-
|
|
24291
|
+
writeFileSync11(join18(dir, stamped), file.bytes);
|
|
24060
24292
|
const relativePath = `${projectId}/${stamped}`;
|
|
24061
24293
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
24062
24294
|
const inserted = db.insert(documents).values({
|
|
@@ -24143,7 +24375,7 @@ function documentRoutes(db, dataDir) {
|
|
|
24143
24375
|
|
|
24144
24376
|
// ../server/src/features/git/service.ts
|
|
24145
24377
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
24146
|
-
import { existsSync as existsSync18, readFileSync as readFileSync13, rmSync, writeFileSync as
|
|
24378
|
+
import { existsSync as existsSync18, readFileSync as readFileSync13, rmSync, writeFileSync as writeFileSync12 } from "fs";
|
|
24147
24379
|
import { join as join19 } from "path";
|
|
24148
24380
|
var GitServiceError = class extends Error {
|
|
24149
24381
|
code;
|
|
@@ -24394,7 +24626,7 @@ function resolveConflict(db, workspacePath, projectId, input) {
|
|
|
24394
24626
|
if (input.content === void 0) {
|
|
24395
24627
|
throw new GitServiceError("CONTENT_REQUIRED", 400, "Manual resolution requires content");
|
|
24396
24628
|
}
|
|
24397
|
-
|
|
24629
|
+
writeFileSync12(join19(dir, input.path), input.content, "utf8");
|
|
24398
24630
|
} else {
|
|
24399
24631
|
git3(dir, ["checkout", `--${input.resolution}`, "--", input.path]);
|
|
24400
24632
|
}
|
|
@@ -24404,6 +24636,67 @@ function resolveConflict(db, workspacePath, projectId, input) {
|
|
|
24404
24636
|
throw new GitServiceError("GIT_RESOLVE_FAILED", 409, detail(e));
|
|
24405
24637
|
}
|
|
24406
24638
|
}
|
|
24639
|
+
function pullChanges(db, workspacePath, projectId, userId) {
|
|
24640
|
+
const dir = projectDir(db, workspacePath, projectId);
|
|
24641
|
+
if (!existsSync18(dir) || !isGitRepo(dir)) {
|
|
24642
|
+
throw new GitServiceError("NO_GIT_REPO", 404, "No git repository for this project");
|
|
24643
|
+
}
|
|
24644
|
+
const status = getGitStatus(db, workspacePath, projectId);
|
|
24645
|
+
if (!status.clean) {
|
|
24646
|
+
throw new GitServiceError(
|
|
24647
|
+
"UNCOMMITTED_CHANGES",
|
|
24648
|
+
409,
|
|
24649
|
+
"Commit or discard your local changes before pulling"
|
|
24650
|
+
);
|
|
24651
|
+
}
|
|
24652
|
+
const branch = safe(() => git3(dir, ["branch", "--show-current"])) ?? "";
|
|
24653
|
+
if (branch === "") {
|
|
24654
|
+
throw new GitServiceError(
|
|
24655
|
+
"DETACHED_HEAD",
|
|
24656
|
+
409,
|
|
24657
|
+
"The repository is in detached HEAD state \u2014 check out a branch before pulling"
|
|
24658
|
+
);
|
|
24659
|
+
}
|
|
24660
|
+
const ctx = resolveGitPushContext(db, projectId, userId);
|
|
24661
|
+
if (ctx.remoteUrl) ensureOrigin(dir, ctx.remoteUrl);
|
|
24662
|
+
const remotes = safe(() => git3(dir, ["remote"])) ?? "";
|
|
24663
|
+
if (!remotes.split("\n").includes("origin")) {
|
|
24664
|
+
throw new GitServiceError("NO_REMOTE", 400, "No git remote configured for this project");
|
|
24665
|
+
}
|
|
24666
|
+
const headBefore = safe(() => git3(dir, ["rev-parse", "--short", "HEAD"])) ?? "";
|
|
24667
|
+
const authB64 = ctx.auth ? Buffer.from(`${ctx.auth.username}:${ctx.auth.token}`).toString("base64") : null;
|
|
24668
|
+
try {
|
|
24669
|
+
execFileSync3(
|
|
24670
|
+
"git",
|
|
24671
|
+
[
|
|
24672
|
+
...buildPushAuthArgs(ctx.auth),
|
|
24673
|
+
...identityArgs(db, userId),
|
|
24674
|
+
"pull",
|
|
24675
|
+
"--no-rebase",
|
|
24676
|
+
"--no-edit",
|
|
24677
|
+
"origin",
|
|
24678
|
+
branch
|
|
24679
|
+
],
|
|
24680
|
+
{
|
|
24681
|
+
cwd: dir,
|
|
24682
|
+
stdio: "pipe",
|
|
24683
|
+
timeout: 12e4,
|
|
24684
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
24685
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
|
|
24686
|
+
}
|
|
24687
|
+
);
|
|
24688
|
+
} catch (e) {
|
|
24689
|
+
if (authB64) redactAuthHeaderFromError(e, authB64);
|
|
24690
|
+
const after = getGitStatus(db, workspacePath, projectId);
|
|
24691
|
+
if (after.hasConflicts) {
|
|
24692
|
+
return { status: "conflicts", branch, message: gitErrorDetail(e) };
|
|
24693
|
+
}
|
|
24694
|
+
throw new GitServiceError("GIT_PULL_FAILED", 409, withIdentityHint(gitErrorDetail(e)));
|
|
24695
|
+
}
|
|
24696
|
+
const headAfter = safe(() => git3(dir, ["rev-parse", "--short", "HEAD"])) ?? "";
|
|
24697
|
+
if (headAfter === headBefore) return { status: "up_to_date", branch };
|
|
24698
|
+
return { status: "merged", branch, commitHash: headAfter };
|
|
24699
|
+
}
|
|
24407
24700
|
function detail(e) {
|
|
24408
24701
|
const stderr = e.stderr?.toString().trim();
|
|
24409
24702
|
return stderr || e.message || String(e);
|
|
@@ -24475,6 +24768,15 @@ function gitRoutes(db, workspacePath) {
|
|
|
24475
24768
|
return errorResponse7(c, e);
|
|
24476
24769
|
}
|
|
24477
24770
|
});
|
|
24771
|
+
app.post("/pull/:projectId", (c) => {
|
|
24772
|
+
const projectId = Number(c.req.param("projectId"));
|
|
24773
|
+
if (Number.isNaN(projectId)) return c.json({ code: "INVALID_ID" }, 400);
|
|
24774
|
+
try {
|
|
24775
|
+
return c.json(pullChanges(db, workspacePath, projectId, c.get("user")?.id));
|
|
24776
|
+
} catch (e) {
|
|
24777
|
+
return errorResponse7(c, e);
|
|
24778
|
+
}
|
|
24779
|
+
});
|
|
24478
24780
|
return app;
|
|
24479
24781
|
}
|
|
24480
24782
|
|
|
@@ -25096,7 +25398,7 @@ function scanProject(db, workspacePath, folderName) {
|
|
|
25096
25398
|
import { join as join22, relative as relative2 } from "path";
|
|
25097
25399
|
|
|
25098
25400
|
// ../server/src/features/workflow/service.ts
|
|
25099
|
-
import { existsSync as existsSync20, mkdirSync as mkdirSync9, readFileSync as readFileSync15, writeFileSync as
|
|
25401
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync9, readFileSync as readFileSync15, writeFileSync as writeFileSync13 } from "fs";
|
|
25100
25402
|
import { join as join21, relative, resolve as resolve2, sep } from "path";
|
|
25101
25403
|
var ACTIVE_RUN_STATUSES = ["pending", "in_progress"];
|
|
25102
25404
|
function svcError14(code, status) {
|
|
@@ -25323,7 +25625,7 @@ function generateWorkflowMarkdown(db, workspacePath, input, creatingUser, watche
|
|
|
25323
25625
|
relPath
|
|
25324
25626
|
);
|
|
25325
25627
|
mkdirSync9(join21(projectDir2, "workflow", creatingUser.username), { recursive: true });
|
|
25326
|
-
|
|
25628
|
+
writeFileSync13(join21(projectDir2, relPath), content, "utf8");
|
|
25327
25629
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
25328
25630
|
const wfm = db.insert(workflowMarkdowns).values({
|
|
25329
25631
|
projectId: input.projectId,
|
|
@@ -26167,7 +26469,7 @@ function projectRoutes(db, workspacePath) {
|
|
|
26167
26469
|
}
|
|
26168
26470
|
|
|
26169
26471
|
// ../server/src/features/shared-context/service.ts
|
|
26170
|
-
import { existsSync as existsSync21, mkdirSync as mkdirSync10, readFileSync as readFileSync16, readdirSync as readdirSync4, statSync as statSync6, writeFileSync as
|
|
26472
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync10, readFileSync as readFileSync16, readdirSync as readdirSync4, statSync as statSync6, writeFileSync as writeFileSync14 } from "fs";
|
|
26171
26473
|
import { join as join23, resolve as resolve3, sep as sep2 } from "path";
|
|
26172
26474
|
function svcError16(code, status) {
|
|
26173
26475
|
return Object.assign(new Error(code), { code, status });
|
|
@@ -26258,7 +26560,7 @@ function uploadFile(db, workspacePath, projectId, folder, file) {
|
|
|
26258
26560
|
const dir = folder ? safeResolve(root, folder) : root;
|
|
26259
26561
|
if (folder && !existsSync21(dir)) throw svcError16("FOLDER_NOT_FOUND", 404);
|
|
26260
26562
|
const safeName = sanitizeSegment(file.name);
|
|
26261
|
-
|
|
26563
|
+
writeFileSync14(join23(dir, safeName), file.bytes);
|
|
26262
26564
|
return { path: folder ? `${folder}/${safeName}` : safeName };
|
|
26263
26565
|
}
|
|
26264
26566
|
function getFileContent(db, workspacePath, projectId, relPath) {
|
|
@@ -26273,7 +26575,7 @@ function saveMarkdown(db, workspacePath, projectId, relPath, content) {
|
|
|
26273
26575
|
const root = resolveSharedRoot(db, workspacePath, projectId);
|
|
26274
26576
|
const full = safeResolve(root, relPath);
|
|
26275
26577
|
if (!existsSync21(full) || !statSync6(full).isFile()) throw svcError16("NOT_FOUND", 404);
|
|
26276
|
-
|
|
26578
|
+
writeFileSync14(full, content, "utf8");
|
|
26277
26579
|
}
|
|
26278
26580
|
|
|
26279
26581
|
// ../server/src/features/shared-context/routes.ts
|
|
@@ -26987,6 +27289,26 @@ function appendScrollback(session, data) {
|
|
|
26987
27289
|
session.bufferBytes -= session.buffer.shift().length;
|
|
26988
27290
|
}
|
|
26989
27291
|
}
|
|
27292
|
+
var HEARTBEAT_INTERVAL_MS = 3e4;
|
|
27293
|
+
function attachHeartbeat(ws, intervalMs = HEARTBEAT_INTERVAL_MS) {
|
|
27294
|
+
let alive = true;
|
|
27295
|
+
ws.on("pong", () => {
|
|
27296
|
+
alive = true;
|
|
27297
|
+
});
|
|
27298
|
+
const timer = setInterval(() => {
|
|
27299
|
+
if (!alive) {
|
|
27300
|
+
clearInterval(timer);
|
|
27301
|
+
ws.terminate();
|
|
27302
|
+
return;
|
|
27303
|
+
}
|
|
27304
|
+
alive = false;
|
|
27305
|
+
try {
|
|
27306
|
+
ws.ping();
|
|
27307
|
+
} catch {
|
|
27308
|
+
}
|
|
27309
|
+
}, intervalMs);
|
|
27310
|
+
ws.on("close", () => clearInterval(timer));
|
|
27311
|
+
}
|
|
26990
27312
|
var sessions2 = /* @__PURE__ */ new Map();
|
|
26991
27313
|
function closeTerminalSessions(projectId, reason) {
|
|
26992
27314
|
const session = sessions2.get(projectId);
|
|
@@ -27018,8 +27340,21 @@ function resolveProjectFolder(db, projectId) {
|
|
|
27018
27340
|
return row?.folder ?? null;
|
|
27019
27341
|
}
|
|
27020
27342
|
function createWsServer(httpServer, db, workspacePath) {
|
|
27021
|
-
const wss = new import_websocket_server.default({
|
|
27343
|
+
const wss = new import_websocket_server.default({ noServer: true });
|
|
27344
|
+
httpServer.on("upgrade", (req, socket, head) => {
|
|
27345
|
+
const pathname = new URL(req.url ?? "", "http://localhost").pathname;
|
|
27346
|
+
if (pathname === "/ws/terminal") {
|
|
27347
|
+
wss.handleUpgrade(req, socket, head, (ws) => wss.emit("connection", ws, req));
|
|
27348
|
+
return;
|
|
27349
|
+
}
|
|
27350
|
+
if (pathname.startsWith("/api/cicd/")) {
|
|
27351
|
+
handleCicdProxyUpgrade(db, workspacePath, req, socket, head);
|
|
27352
|
+
return;
|
|
27353
|
+
}
|
|
27354
|
+
socket.destroy();
|
|
27355
|
+
});
|
|
27022
27356
|
wss.on("connection", async (ws, req) => {
|
|
27357
|
+
attachHeartbeat(ws);
|
|
27023
27358
|
const url = new URL(req.url ?? "", "http://localhost");
|
|
27024
27359
|
const projectId = Number(url.searchParams.get("projectId"));
|
|
27025
27360
|
const userId = Number(url.searchParams.get("userId"));
|
|
@@ -27263,7 +27598,7 @@ function workspaceRoutes(_db, config, port, host, workspacePath) {
|
|
|
27263
27598
|
}
|
|
27264
27599
|
|
|
27265
27600
|
// ../server/src/lib/config.ts
|
|
27266
|
-
import { existsSync as existsSync23, readFileSync as readFileSync18, writeFileSync as
|
|
27601
|
+
import { existsSync as existsSync23, readFileSync as readFileSync18, writeFileSync as writeFileSync15 } from "fs";
|
|
27267
27602
|
import { join as join25 } from "path";
|
|
27268
27603
|
var configSchema = external_exports.object({
|
|
27269
27604
|
port: external_exports.number().int().min(1).max(65535).optional(),
|
|
@@ -27281,7 +27616,7 @@ function loadConfig(dataDir) {
|
|
|
27281
27616
|
}
|
|
27282
27617
|
}
|
|
27283
27618
|
function saveConfig(dataDir, config) {
|
|
27284
|
-
|
|
27619
|
+
writeFileSync15(join25(dataDir, "config.json"), JSON.stringify(config, null, 2));
|
|
27285
27620
|
}
|
|
27286
27621
|
|
|
27287
27622
|
// ../server/src/lib/logger.ts
|