@rashidee/co2 1.3.11 → 1.3.13

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.
Files changed (77) hide show
  1. package/dist/.co2-dat/app.db +0 -0
  2. package/dist/.co2-dat/app.db-shm +0 -0
  3. package/dist/.co2-dat/app.db-wal +0 -0
  4. package/dist/index.js +254 -73
  5. package/package.json +41 -41
  6. package/plugin/.claude-plugin/marketplace.json +1 -1
  7. package/plugin/.claude-plugin/plugin.json +1 -1
  8. package/plugin/README.md +3 -1
  9. package/plugin/SKILLS.md +5 -2
  10. package/plugin/skills/conductor-feature-develop/SKILL.md +13 -0
  11. package/plugin/skills/conductor-feature-prepare/SKILL.md +39 -11
  12. package/plugin/skills/specgen-custom/SKILL.md +442 -0
  13. package/plugin/skills/specgen-custom/references/spec-template.md +271 -0
  14. package/plugin/skills/specgen-custom/references/stack-doc-template.md +154 -0
  15. package/plugin/skills/util-gencicdscript/references/cicd-app-template.md +798 -796
  16. package/plugin/skills/util-plancicd/SKILL.md +1 -1
  17. package/static/assets/{abnfDiagram-VRR7QNED-DBzgA_2G.js → abnfDiagram-VRR7QNED-Qm0Wk_H_.js} +1 -1
  18. package/static/assets/{arc-Bc02_4l6.js → arc-Cfk6Kt0D.js} +1 -1
  19. package/static/assets/{architectureDiagram-ZJ3FMSHR-ixN9wSDW.js → architectureDiagram-ZJ3FMSHR-DQTlZaUK.js} +1 -1
  20. package/static/assets/{blockDiagram-677ZJIJ3-Ob4lZkCY.js → blockDiagram-677ZJIJ3-D-4ibdoX.js} +1 -1
  21. package/static/assets/{c4Diagram-LMCZKHZV-BeJY3Zks.js → c4Diagram-LMCZKHZV-CHuqcdJm.js} +1 -1
  22. package/static/assets/channel-H9DRrduO.js +1 -0
  23. package/static/assets/{chunk-2Q5K7J3B-DTFIWNGb.js → chunk-2Q5K7J3B-MB9pWQe1.js} +1 -1
  24. package/static/assets/{chunk-32BRIVSS-DLTvoPpT.js → chunk-32BRIVSS-BttYlwU1.js} +1 -1
  25. package/static/assets/{chunk-5VM5RSS4-BKlvf6hI.js → chunk-5VM5RSS4-oyt0Tagd.js} +1 -1
  26. package/static/assets/{chunk-EX3LRPZG-B9ej8_ma.js → chunk-EX3LRPZG-DaTxdTWl.js} +1 -1
  27. package/static/assets/{chunk-JWPE2WC7-DAYaiy17.js → chunk-JWPE2WC7-CNgDxPAD.js} +1 -1
  28. package/static/assets/{chunk-MOJQB5TN-D6rRKzWc.js → chunk-MOJQB5TN-CoAudO3B.js} +1 -1
  29. package/static/assets/{chunk-RYQCIY6F-CE4CHN6p.js → chunk-RYQCIY6F-BrkDEEha.js} +1 -1
  30. package/static/assets/{chunk-V7JOEXUC-BR-Kl6S2.js → chunk-V7JOEXUC-D14rGvBO.js} +1 -1
  31. package/static/assets/{chunk-VR4S4FIN-Bo9J3WGe.js → chunk-VR4S4FIN-CAtas2vF.js} +1 -1
  32. package/static/assets/{chunk-XXDRQBXY-DjI5dJHR.js → chunk-XXDRQBXY-DcVAnNUQ.js} +1 -1
  33. package/static/assets/classDiagram-OUVF2IWQ-DRNPM383.js +1 -0
  34. package/static/assets/classDiagram-v2-EOCWNBFH-DRNPM383.js +1 -0
  35. package/static/assets/{cose-bilkent-JH36ORCC-DLciz-3w.js → cose-bilkent-JH36ORCC-B7y4MI6Z.js} +1 -1
  36. package/static/assets/{cynefin-VYW2F7L2-DqfVJKpm.js → cynefin-VYW2F7L2-D2mnk-iJ.js} +1 -1
  37. package/static/assets/{cynefinDiagram-TSTJHNR4-D14oLdNM.js → cynefinDiagram-TSTJHNR4-DNzQJhG7.js} +1 -1
  38. package/static/assets/{dagre-VKFMJZFB-BduMbW80.js → dagre-VKFMJZFB-NMSyTo8N.js} +1 -1
  39. package/static/assets/{diagram-FQU43EPY-CLQg7RWT.js → diagram-FQU43EPY-CdqS5Ike.js} +1 -1
  40. package/static/assets/{diagram-G47NLZAW-Dq3Ymr1b.js → diagram-G47NLZAW-Bsb4T5JI.js} +1 -1
  41. package/static/assets/{diagram-NH7WQ7WH-BPjov8S5.js → diagram-NH7WQ7WH-Z-PyzBSC.js} +1 -1
  42. package/static/assets/{diagram-OA4YK3LP-BgQgpiJD.js → diagram-OA4YK3LP-BZBTYbxg.js} +1 -1
  43. package/static/assets/{diagram-WEI45ONY-BeOPSl27.js → diagram-WEI45ONY-BL2LA5H_.js} +1 -1
  44. package/static/assets/{ebnfDiagram-CCIWWBDH-DFsTw8OM.js → ebnfDiagram-CCIWWBDH-BGtukMeo.js} +1 -1
  45. package/static/assets/{erDiagram-Q63AITRT-scBEBawL.js → erDiagram-Q63AITRT-B6OJ4YCl.js} +1 -1
  46. package/static/assets/{flowDiagram-23GEKE2U-BfX-2ZO-.js → flowDiagram-23GEKE2U-BBkMgJ1i.js} +1 -1
  47. package/static/assets/{ganttDiagram-NO4QXBWP-BR5IJ1mv.js → ganttDiagram-NO4QXBWP-Cv8KHFBD.js} +1 -1
  48. package/static/assets/{gitGraphDiagram-IHSO6WYX-zPu_QDUM.js → gitGraphDiagram-IHSO6WYX-hYxDNjWx.js} +1 -1
  49. package/static/assets/{index-Dnp-sAA_.js → index-DnYGyYET.js} +81 -79
  50. package/static/assets/{infoDiagram-FWYZ7A6U-CpyKTQb9.js → infoDiagram-FWYZ7A6U-WuQIiedv.js} +1 -1
  51. package/static/assets/{ishikawaDiagram-FXEZZL3T-C2SXt-2Y.js → ishikawaDiagram-FXEZZL3T-BJGOD-IY.js} +1 -1
  52. package/static/assets/{journeyDiagram-5HDEW3XC-DJavk0C4.js → journeyDiagram-5HDEW3XC-Ba_NXseS.js} +1 -1
  53. package/static/assets/{kanban-definition-HUTT4EX6-xN4pVGL7.js → kanban-definition-HUTT4EX6-C5RMm9uF.js} +1 -1
  54. package/static/assets/{linear-BMWKojRX.js → linear-CLKppNoj.js} +1 -1
  55. package/static/assets/{mindmap-definition-LN4V7U3C-DOaDf7u0.js → mindmap-definition-LN4V7U3C-ClSG_qmQ.js} +1 -1
  56. package/static/assets/{pegDiagram-2B236MQR-B1px906A.js → pegDiagram-2B236MQR-C_JEZqk3.js} +1 -1
  57. package/static/assets/{pieDiagram-ENE6RG2P-s0vEagFS.js → pieDiagram-ENE6RG2P-D0lx7wDi.js} +1 -1
  58. package/static/assets/{quadrantDiagram-ABIIQ3AL-BEhIl8IZ.js → quadrantDiagram-ABIIQ3AL-BBKy0PVs.js} +1 -1
  59. package/static/assets/{railroadDiagram-RFXS5EU6-fFsin92S.js → railroadDiagram-RFXS5EU6-C1Badu5q.js} +1 -1
  60. package/static/assets/{requirementDiagram-TGXJPOKE-Cf9J7tii.js → requirementDiagram-TGXJPOKE-1j59jBQH.js} +1 -1
  61. package/static/assets/{sankeyDiagram-HTMAVEWB-Cy5gaZqM.js → sankeyDiagram-HTMAVEWB-BLR5S8aZ.js} +1 -1
  62. package/static/assets/{sequenceDiagram-DBY2YBRQ-DI61Vnk1.js → sequenceDiagram-DBY2YBRQ-BlqdK4qm.js} +1 -1
  63. package/static/assets/{sizeCapture-X5ZJPWSS-CmUBUW3B.js → sizeCapture-X5ZJPWSS-C27ndGIT.js} +1 -1
  64. package/static/assets/{stateDiagram-2N3HPSRC-CBMhhwuE.js → stateDiagram-2N3HPSRC-DOu4536_.js} +1 -1
  65. package/static/assets/stateDiagram-v2-6OUMAXLB-DvP1ZCp8.js +1 -0
  66. package/static/assets/{swimlanes-5IMT3BWC-CtZlr9wD.js → swimlanes-5IMT3BWC-BpGitR9A.js} +2 -2
  67. package/static/assets/swimlanesDiagram-G3AALYLV-B5cf18Ey.js +8 -0
  68. package/static/assets/{timeline-definition-FHXFAJF6-BC69PWR-.js → timeline-definition-FHXFAJF6-JIvdjH7z.js} +1 -1
  69. package/static/assets/{vennDiagram-L72KCM5P-jEaqKPSP.js → vennDiagram-L72KCM5P-DulnqLWm.js} +1 -1
  70. package/static/assets/{wardleyDiagram-EHGQE667-CKFWi3EK.js → wardleyDiagram-EHGQE667--MhZNL8M.js} +1 -1
  71. package/static/assets/{xychartDiagram-FW5EYKEG-CjyCKfOu.js → xychartDiagram-FW5EYKEG-CjnNgnMQ.js} +1 -1
  72. package/static/index.html +1 -1
  73. package/static/assets/channel-CJFxgMC1.js +0 -1
  74. package/static/assets/classDiagram-OUVF2IWQ-B5OYUyBf.js +0 -1
  75. package/static/assets/classDiagram-v2-EOCWNBFH-B5OYUyBf.js +0 -1
  76. package/static/assets/stateDiagram-v2-6OUMAXLB-BD1I889C.js +0 -1
  77. package/static/assets/swimlanesDiagram-G3AALYLV-BfEZlw_9.js +0 -8
Binary file
Binary file
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.11",
6812
+ version: "1.3.13",
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 = {};
@@ -21854,16 +21863,19 @@ function brainstormRoutes(db, workspacePath) {
21854
21863
  import { existsSync as existsSync13, readFileSync as readFileSync8 } from "fs";
21855
21864
  import { join as join14 } from "path";
21856
21865
 
21866
+ // ../server/src/features/cicd/app-proxy.ts
21867
+ import { connect as connect2 } from "net";
21868
+
21857
21869
  // ../server/src/features/cicd/app-runner.ts
21858
21870
  import { spawn as spawn2 } from "child_process";
21859
21871
  import { randomBytes as randomBytes2 } from "crypto";
21860
- import { existsSync as existsSync8, readFileSync as readFileSync6 } from "fs";
21872
+ import { existsSync as existsSync8, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
21861
21873
  import { request as httpRequest2 } from "http";
21862
21874
  import { join as join9 } from "path";
21863
21875
  function svcError6(code, status) {
21864
21876
  return Object.assign(new Error(code), { code, status });
21865
21877
  }
21866
- var DEFAULT_CICD_APP_PORT = 4100;
21878
+ var DEFAULT_CICD_APP_PORT = 5e3;
21867
21879
  var spawnedApps = /* @__PURE__ */ new Map();
21868
21880
  var guardTokens = /* @__PURE__ */ new Map();
21869
21881
  function killSpawnedApps() {
@@ -21914,55 +21926,81 @@ function probeApp(port, timeoutMs = 1e3) {
21914
21926
  req.end();
21915
21927
  });
21916
21928
  }
21917
- function toStatus2(location, running, requestHost) {
21918
- const hostname = requestHost.replace(/:\d+$/, "") || "localhost";
21929
+ function toStatus2(location, running, projectId) {
21919
21930
  return {
21920
21931
  present: location.present,
21921
21932
  running,
21922
21933
  port: location.present ? location.port : null,
21923
- url: location.present ? `http://${hostname}:${location.port}/` : null
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
21924
21937
  };
21925
21938
  }
21926
- async function getCicdAppStatus(db, workspacePath, projectId, requestHost) {
21939
+ async function getCicdAppStatus(db, workspacePath, projectId) {
21927
21940
  const location = resolveCicdApp(db, workspacePath, projectId);
21928
21941
  const running = location.present ? await probeApp(location.port) : false;
21929
- return toStatus2(location, running, requestHost);
21942
+ return toStatus2(location, running, projectId);
21930
21943
  }
21931
- async function startCicdApp(db, workspacePath, projectId, requestHost, studioPort) {
21944
+ async function startCicdApp(db, workspacePath, projectId, studioPort, port) {
21932
21945
  const location = resolveCicdApp(db, workspacePath, projectId);
21933
21946
  if (!location.present) throw svcError6("CICD_APP_MISSING", 404);
21934
- if (await probeApp(location.port)) return toStatus2(location, true, requestHost);
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
+ }
21935
21967
  const existing = spawnedApps.get(projectId);
21936
- if (!existing || existing.exitCode !== null) {
21937
- const token = randomBytes2(24).toString("hex");
21938
- guardTokens.set(projectId, token);
21939
- const child = spawn2(process.execPath, ["server.js"], {
21940
- cwd: location.appDir,
21941
- stdio: "ignore",
21942
- env: {
21943
- ...process.env,
21944
- PORT: String(location.port),
21945
- CO2_STUDIO_URL: `http://127.0.0.1:${studioPort}`,
21946
- CO2_PROJECT_ID: String(projectId),
21947
- CO2_GUARD_TOKEN: token
21948
- }
21949
- });
21950
- child.on("error", () => {
21951
- spawnedApps.delete(projectId);
21952
- guardTokens.delete(projectId);
21953
- });
21954
- child.on("exit", () => {
21955
- spawnedApps.delete(projectId);
21956
- guardTokens.delete(projectId);
21957
- });
21958
- spawnedApps.set(projectId, child);
21959
- 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);
21960
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();
21961
21999
  for (let attempt = 0; attempt < 20; attempt++) {
21962
- if (await probeApp(location.port, 500)) return toStatus2(location, true, requestHost);
22000
+ if (await probeApp(location.port, 500)) return toStatus2(location, true, projectId);
21963
22001
  await new Promise((r) => setTimeout(r, 250));
21964
22002
  }
21965
- return toStatus2(location, false, requestHost);
22003
+ return toStatus2(location, false, projectId);
21966
22004
  }
21967
22005
  function stopCicdApp(projectId) {
21968
22006
  const child = spawnedApps.get(projectId);
@@ -21976,13 +22014,103 @@ function stopCicdApp(projectId) {
21976
22014
  return true;
21977
22015
  }
21978
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
+
21979
22107
  // ../server/src/features/cicd/guard.ts
21980
22108
  import { execFileSync as execFileSync2 } from "child_process";
21981
22109
  import { existsSync as existsSync10 } from "fs";
21982
22110
  import { join as join11 } from "path";
21983
22111
 
21984
22112
  // ../server/src/features/prd-wizard/service.ts
21985
- import { existsSync as existsSync9, mkdirSync as mkdirSync3, statSync as statSync2, writeFileSync as writeFileSync5 } from "fs";
22113
+ import { existsSync as existsSync9, mkdirSync as mkdirSync3, statSync as statSync2, writeFileSync as writeFileSync6 } from "fs";
21986
22114
  import { join as join10 } from "path";
21987
22115
  function svcError7(code, status) {
21988
22116
  return Object.assign(new Error(code), { code, status });
@@ -22060,7 +22188,7 @@ function generatePrdWizardMarkdown(db, workspacePath, requestId, targetModule) {
22060
22188
  `execution/${filename}`,
22061
22189
  targetModule
22062
22190
  );
22063
- writeFileSync5(filePath, markdown, "utf8");
22191
+ writeFileSync6(filePath, markdown, "utf8");
22064
22192
  db.update(prdWizardRequests).set({
22065
22193
  generatedMarkdownPath: filePath,
22066
22194
  status: "generated",
@@ -22297,7 +22425,7 @@ function evaluateBuildGuard(db, workspacePath, projectId, versionId) {
22297
22425
  }
22298
22426
 
22299
22427
  // ../server/src/features/cicd/service.ts
22300
- import { appendFileSync, existsSync as existsSync12, mkdirSync as mkdirSync4, statSync as statSync4, writeFileSync as writeFileSync6 } from "fs";
22428
+ import { appendFileSync, existsSync as existsSync12, mkdirSync as mkdirSync4, statSync as statSync4, writeFileSync as writeFileSync7 } from "fs";
22301
22429
  import { join as join13 } from "path";
22302
22430
 
22303
22431
  // ../server/src/features/batch-job/constants.ts
@@ -22440,7 +22568,7 @@ function requestPlan(db, workspacePath, projectId, userId) {
22440
22568
  const filename = `cicd-plan-${setup.id}-${Date.now()}.md`;
22441
22569
  const filePath = join13(executionDir, filename);
22442
22570
  const relative3 = `execution/${filename}`;
22443
- writeFileSync6(
22571
+ writeFileSync7(
22444
22572
  filePath,
22445
22573
  buildPlanRequestMarkdown(
22446
22574
  project.name,
@@ -22600,9 +22728,12 @@ function parseProjectId(c) {
22600
22728
  const id = Number(c.req.param("projectId"));
22601
22729
  return Number.isNaN(id) ? null : id;
22602
22730
  }
22731
+ var startBodySchema = external_exports.object({
22732
+ port: external_exports.coerce.number().int().min(1024).max(65535).optional()
22733
+ });
22603
22734
  function cicdRoutes(db, workspacePath, studioPort) {
22604
22735
  const app = new Hono2();
22605
- const toDto8 = async (c, setup) => {
22736
+ const toDto8 = async (setup) => {
22606
22737
  const planFile = setup.planRequestPath?.split(/[\\/]/).pop();
22607
22738
  return {
22608
22739
  id: setup.id,
@@ -22614,7 +22745,7 @@ function cicdRoutes(db, workspacePath, studioPort) {
22614
22745
  approvedAt: setup.approvedAt,
22615
22746
  generatedAt: setup.generatedAt,
22616
22747
  stale: isStale(db, setup),
22617
- app: await getCicdAppStatus(db, workspacePath, setup.projectId, c.req.header("host") ?? ""),
22748
+ app: await getCicdAppStatus(db, workspacePath, setup.projectId),
22618
22749
  createdAt: setup.createdAt,
22619
22750
  updatedAt: setup.updatedAt
22620
22751
  };
@@ -22632,7 +22763,7 @@ function cicdRoutes(db, workspacePath, studioPort) {
22632
22763
  startCicdWatcher(db, workspacePath, setup.id, "generation", user.id);
22633
22764
  }
22634
22765
  const fresh = getOrCreateSetup(db, projectId, user.id);
22635
- return c.json(await toDto8(c, fresh));
22766
+ return c.json(await toDto8(fresh));
22636
22767
  } catch (e) {
22637
22768
  return errorResponse3(c, e);
22638
22769
  }
@@ -22655,7 +22786,7 @@ function cicdRoutes(db, workspacePath, studioPort) {
22655
22786
  if (fresh.status === "generating" && !hasCicdWatcher(fresh.id, "generation")) {
22656
22787
  startCicdWatcher(db, workspacePath, fresh.id, "generation", user.id);
22657
22788
  }
22658
- return c.json(await toDto8(c, fresh));
22789
+ return c.json(await toDto8(fresh));
22659
22790
  } catch (e) {
22660
22791
  return errorResponse3(c, e);
22661
22792
  }
@@ -22709,10 +22840,11 @@ function cicdRoutes(db, workspacePath, studioPort) {
22709
22840
  app.post("/:projectId/app/start", async (c) => {
22710
22841
  const projectId = parseProjectId(c);
22711
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);
22712
22846
  try {
22713
- return c.json(
22714
- await startCicdApp(db, workspacePath, projectId, c.req.header("host") ?? "", studioPort)
22715
- );
22847
+ return c.json(await startCicdApp(db, workspacePath, projectId, studioPort, parsed.data.port));
22716
22848
  } catch (e) {
22717
22849
  return errorResponse3(c, e);
22718
22850
  }
@@ -22722,9 +22854,25 @@ function cicdRoutes(db, workspacePath, studioPort) {
22722
22854
  if (projectId === null) return c.json({ code: "INVALID_ID" }, 400);
22723
22855
  try {
22724
22856
  stopCicdApp(projectId);
22725
- return c.json(
22726
- await getCicdAppStatus(db, workspacePath, projectId, c.req.header("host") ?? "")
22727
- );
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);
22728
22876
  } catch (e) {
22729
22877
  return errorResponse3(c, e);
22730
22878
  }
@@ -22750,11 +22898,11 @@ function cicdGuardRoutes(db, workspacePath) {
22750
22898
  }
22751
22899
 
22752
22900
  // ../server/src/features/project/service.ts
22753
- import { existsSync as existsSync15, mkdirSync as mkdirSync6, readFileSync as readFileSync10, writeFileSync as writeFileSync8 } from "fs";
22901
+ import { existsSync as existsSync15, mkdirSync as mkdirSync6, readFileSync as readFileSync10, writeFileSync as writeFileSync9 } from "fs";
22754
22902
  import { join as join16 } from "path";
22755
22903
 
22756
22904
  // ../server/src/features/project-config/service.ts
22757
- import { existsSync as existsSync14, mkdirSync as mkdirSync5, readFileSync as readFileSync9, writeFileSync as writeFileSync7 } from "fs";
22905
+ import { existsSync as existsSync14, mkdirSync as mkdirSync5, readFileSync as readFileSync9, writeFileSync as writeFileSync8 } from "fs";
22758
22906
  import { join as join15 } from "path";
22759
22907
  function svcError9(code, status) {
22760
22908
  return Object.assign(new Error(code), { code, status });
@@ -22778,7 +22926,7 @@ function resolveImportedByName(db, importedBy) {
22778
22926
  function writeProjectFile(workspacePath, folder, fileName, content) {
22779
22927
  const dir = join15(workspacePath, folder);
22780
22928
  mkdirSync5(dir, { recursive: true });
22781
- writeFileSync7(join15(dir, fileName), content, "utf8");
22929
+ writeFileSync8(join15(dir, fileName), content, "utf8");
22782
22930
  }
22783
22931
  function toDto5(db, row, field) {
22784
22932
  return {
@@ -22945,8 +23093,8 @@ function scaffoldProjectFolder(workspacePath, folderName, name, baseIdea) {
22945
23093
  "# Feedback",
22946
23094
  "<!-- Human answers appear here -->"
22947
23095
  ].join("\n");
22948
- writeFileSync8(join16(projectDir2, `${folderName}.brainstorm.md`), template, "utf8");
22949
- writeFileSync8(
23096
+ writeFileSync9(join16(projectDir2, `${folderName}.brainstorm.md`), template, "utf8");
23097
+ writeFileSync9(
22950
23098
  join16(projectDir2, "context", "references", "brainstorm-protocol.md"),
22951
23099
  "# Brainstorm Protocol\n\n[Installed by the co2 plugin]\n",
22952
23100
  "utf8"
@@ -22984,7 +23132,7 @@ function updateBrainstormMd(db, workspacePath, projectId, content) {
22984
23132
  project.workspaceFolderName,
22985
23133
  `${project.workspaceFolderName}.brainstorm.md`
22986
23134
  );
22987
- writeFileSync8(filePath, content, "utf8");
23135
+ writeFileSync9(filePath, content, "utf8");
22988
23136
  }
22989
23137
  function listProjects(db, archived) {
22990
23138
  return db.select({
@@ -23107,7 +23255,7 @@ function regenerateCO2Files(db, workspacePath, projectId) {
23107
23255
  const projectDir2 = join16(workspacePath, project.workspaceFolderName);
23108
23256
  mkdirSync6(projectDir2, { recursive: true });
23109
23257
  const apps = db.select().from(applications).where(and(eq(applications.projectId, projectId), eq(applications.isDeleted, 0))).all();
23110
- writeFileSync8(
23258
+ writeFileSync9(
23111
23259
  join16(projectDir2, "CLAUDE.md"),
23112
23260
  project.rawClaudeMd ?? buildClaudeMd(project.name, apps),
23113
23261
  "utf8"
@@ -23124,7 +23272,7 @@ function regenerateCO2Files(db, workspacePath, projectId) {
23124
23272
  const appRel = resolveAppFolderName(projectDir2, app.name);
23125
23273
  const appDir = join16(projectDir2, appRel, "context");
23126
23274
  mkdirSync6(appDir, { recursive: true });
23127
- writeFileSync8(
23275
+ writeFileSync9(
23128
23276
  join16(appDir, "PRD.md"),
23129
23277
  prdSnapshot[String(app.id)] ?? buildPrdMd(app.name, mods, loadPrdSections(db, app.id)),
23130
23278
  "utf8"
@@ -23517,7 +23665,7 @@ function dashboardRoutes(db) {
23517
23665
  }
23518
23666
 
23519
23667
  // ../server/src/features/defect/service.ts
23520
- import { existsSync as existsSync16, mkdirSync as mkdirSync7, readFileSync as readFileSync11, writeFileSync as writeFileSync9 } from "fs";
23668
+ import { existsSync as existsSync16, mkdirSync as mkdirSync7, readFileSync as readFileSync11, writeFileSync as writeFileSync10 } from "fs";
23521
23669
  import { join as join17 } from "path";
23522
23670
 
23523
23671
  // ../server/src/features/project-import/bug-md.ts
@@ -23949,7 +24097,7 @@ function writeBugMdFile(db, workspacePath, app, projectFolderName) {
23949
24097
  const appRel = resolveAppFolderName(projectDir2, app.name);
23950
24098
  const appContextDir = join17(projectDir2, appRel, "context");
23951
24099
  mkdirSync7(appContextDir, { recursive: true });
23952
- writeFileSync9(join17(appContextDir, "BUG.md"), content, "utf8");
24100
+ writeFileSync10(join17(appContextDir, "BUG.md"), content, "utf8");
23953
24101
  return { content, bugCount: bugs.length };
23954
24102
  }
23955
24103
  function syncBugStatusesFromMaster(db, trackingFilePath, applicationId) {
@@ -23993,7 +24141,7 @@ function syncBugMdToDisk(db, workspacePath, applicationId, bugId, mode = "replac
23993
24141
  ).all().find((m) => m.name.toLowerCase() === bug.module?.toLowerCase())?.category : void 0) ?? null;
23994
24142
  const merged = mergeBugIntoBugMd(readFileSync11(file, "utf8"), bug, semver, category, mode);
23995
24143
  if (merged !== null) {
23996
- writeFileSync9(file, merged, "utf8");
24144
+ writeFileSync10(file, merged, "utf8");
23997
24145
  return;
23998
24146
  }
23999
24147
  }
@@ -24127,7 +24275,7 @@ function defectRoutes(db, workspacePath) {
24127
24275
  }
24128
24276
 
24129
24277
  // ../server/src/features/document/service.ts
24130
- import { existsSync as existsSync17, mkdirSync as mkdirSync8, readFileSync as readFileSync12, writeFileSync as writeFileSync10 } from "fs";
24278
+ import { existsSync as existsSync17, mkdirSync as mkdirSync8, readFileSync as readFileSync12, writeFileSync as writeFileSync11 } from "fs";
24131
24279
  import { join as join18 } from "path";
24132
24280
  function svcError12(code, status) {
24133
24281
  return Object.assign(new Error(code), { code, status });
@@ -24140,7 +24288,7 @@ function uploadDocument(db, dataDir, projectId, file) {
24140
24288
  mkdirSync8(dir, { recursive: true });
24141
24289
  const safeName = file.name.replace(/[^a-zA-Z0-9._-]/g, "_");
24142
24290
  const stamped = `${Date.now()}_${safeName}`;
24143
- writeFileSync10(join18(dir, stamped), file.bytes);
24291
+ writeFileSync11(join18(dir, stamped), file.bytes);
24144
24292
  const relativePath = `${projectId}/${stamped}`;
24145
24293
  const now = (/* @__PURE__ */ new Date()).toISOString();
24146
24294
  const inserted = db.insert(documents).values({
@@ -24227,7 +24375,7 @@ function documentRoutes(db, dataDir) {
24227
24375
 
24228
24376
  // ../server/src/features/git/service.ts
24229
24377
  import { execFileSync as execFileSync3 } from "child_process";
24230
- import { existsSync as existsSync18, readFileSync as readFileSync13, rmSync, writeFileSync as writeFileSync11 } from "fs";
24378
+ import { existsSync as existsSync18, readFileSync as readFileSync13, rmSync, writeFileSync as writeFileSync12 } from "fs";
24231
24379
  import { join as join19 } from "path";
24232
24380
  var GitServiceError = class extends Error {
24233
24381
  code;
@@ -24478,7 +24626,7 @@ function resolveConflict(db, workspacePath, projectId, input) {
24478
24626
  if (input.content === void 0) {
24479
24627
  throw new GitServiceError("CONTENT_REQUIRED", 400, "Manual resolution requires content");
24480
24628
  }
24481
- writeFileSync11(join19(dir, input.path), input.content, "utf8");
24629
+ writeFileSync12(join19(dir, input.path), input.content, "utf8");
24482
24630
  } else {
24483
24631
  git3(dir, ["checkout", `--${input.resolution}`, "--", input.path]);
24484
24632
  }
@@ -25250,7 +25398,7 @@ function scanProject(db, workspacePath, folderName) {
25250
25398
  import { join as join22, relative as relative2 } from "path";
25251
25399
 
25252
25400
  // ../server/src/features/workflow/service.ts
25253
- import { existsSync as existsSync20, mkdirSync as mkdirSync9, readFileSync as readFileSync15, writeFileSync as writeFileSync12 } from "fs";
25401
+ import { existsSync as existsSync20, mkdirSync as mkdirSync9, readFileSync as readFileSync15, writeFileSync as writeFileSync13 } from "fs";
25254
25402
  import { join as join21, relative, resolve as resolve2, sep } from "path";
25255
25403
  var ACTIVE_RUN_STATUSES = ["pending", "in_progress"];
25256
25404
  function svcError14(code, status) {
@@ -25477,7 +25625,7 @@ function generateWorkflowMarkdown(db, workspacePath, input, creatingUser, watche
25477
25625
  relPath
25478
25626
  );
25479
25627
  mkdirSync9(join21(projectDir2, "workflow", creatingUser.username), { recursive: true });
25480
- writeFileSync12(join21(projectDir2, relPath), content, "utf8");
25628
+ writeFileSync13(join21(projectDir2, relPath), content, "utf8");
25481
25629
  const now = (/* @__PURE__ */ new Date()).toISOString();
25482
25630
  const wfm = db.insert(workflowMarkdowns).values({
25483
25631
  projectId: input.projectId,
@@ -26321,7 +26469,7 @@ function projectRoutes(db, workspacePath) {
26321
26469
  }
26322
26470
 
26323
26471
  // ../server/src/features/shared-context/service.ts
26324
- import { existsSync as existsSync21, mkdirSync as mkdirSync10, readFileSync as readFileSync16, readdirSync as readdirSync4, statSync as statSync6, writeFileSync as writeFileSync13 } from "fs";
26472
+ import { existsSync as existsSync21, mkdirSync as mkdirSync10, readFileSync as readFileSync16, readdirSync as readdirSync4, statSync as statSync6, writeFileSync as writeFileSync14 } from "fs";
26325
26473
  import { join as join23, resolve as resolve3, sep as sep2 } from "path";
26326
26474
  function svcError16(code, status) {
26327
26475
  return Object.assign(new Error(code), { code, status });
@@ -26412,7 +26560,7 @@ function uploadFile(db, workspacePath, projectId, folder, file) {
26412
26560
  const dir = folder ? safeResolve(root, folder) : root;
26413
26561
  if (folder && !existsSync21(dir)) throw svcError16("FOLDER_NOT_FOUND", 404);
26414
26562
  const safeName = sanitizeSegment(file.name);
26415
- writeFileSync13(join23(dir, safeName), file.bytes);
26563
+ writeFileSync14(join23(dir, safeName), file.bytes);
26416
26564
  return { path: folder ? `${folder}/${safeName}` : safeName };
26417
26565
  }
26418
26566
  function getFileContent(db, workspacePath, projectId, relPath) {
@@ -26427,7 +26575,7 @@ function saveMarkdown(db, workspacePath, projectId, relPath, content) {
26427
26575
  const root = resolveSharedRoot(db, workspacePath, projectId);
26428
26576
  const full = safeResolve(root, relPath);
26429
26577
  if (!existsSync21(full) || !statSync6(full).isFile()) throw svcError16("NOT_FOUND", 404);
26430
- writeFileSync13(full, content, "utf8");
26578
+ writeFileSync14(full, content, "utf8");
26431
26579
  }
26432
26580
 
26433
26581
  // ../server/src/features/shared-context/routes.ts
@@ -27141,6 +27289,26 @@ function appendScrollback(session, data) {
27141
27289
  session.bufferBytes -= session.buffer.shift().length;
27142
27290
  }
27143
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
+ }
27144
27312
  var sessions2 = /* @__PURE__ */ new Map();
27145
27313
  function closeTerminalSessions(projectId, reason) {
27146
27314
  const session = sessions2.get(projectId);
@@ -27172,8 +27340,21 @@ function resolveProjectFolder(db, projectId) {
27172
27340
  return row?.folder ?? null;
27173
27341
  }
27174
27342
  function createWsServer(httpServer, db, workspacePath) {
27175
- const wss = new import_websocket_server.default({ server: httpServer, path: "/ws/terminal" });
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
+ });
27176
27356
  wss.on("connection", async (ws, req) => {
27357
+ attachHeartbeat(ws);
27177
27358
  const url = new URL(req.url ?? "", "http://localhost");
27178
27359
  const projectId = Number(url.searchParams.get("projectId"));
27179
27360
  const userId = Number(url.searchParams.get("userId"));
@@ -27417,7 +27598,7 @@ function workspaceRoutes(_db, config, port, host, workspacePath) {
27417
27598
  }
27418
27599
 
27419
27600
  // ../server/src/lib/config.ts
27420
- import { existsSync as existsSync23, readFileSync as readFileSync18, writeFileSync as writeFileSync14 } from "fs";
27601
+ import { existsSync as existsSync23, readFileSync as readFileSync18, writeFileSync as writeFileSync15 } from "fs";
27421
27602
  import { join as join25 } from "path";
27422
27603
  var configSchema = external_exports.object({
27423
27604
  port: external_exports.number().int().min(1).max(65535).optional(),
@@ -27435,7 +27616,7 @@ function loadConfig(dataDir) {
27435
27616
  }
27436
27617
  }
27437
27618
  function saveConfig(dataDir, config) {
27438
- writeFileSync14(join25(dataDir, "config.json"), JSON.stringify(config, null, 2));
27619
+ writeFileSync15(join25(dataDir, "config.json"), JSON.stringify(config, null, 2));
27439
27620
  }
27440
27621
 
27441
27622
  // ../server/src/lib/logger.ts