@rashidee/co2 1.3.17 → 1.3.19
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/index.js +75 -26
- package/package.json +1 -1
- package/plugin/.claude-plugin/marketplace.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/skills/util-gencicdscript/SKILL.md +18 -23
- package/plugin/skills/util-gencicdscript/references/cicd-app-template.md +176 -141
- package/plugin/skills/util-plancicd/SKILL.md +319 -312
- package/static/assets/{abnfDiagram-VRR7QNED-kjS3baPt.js → abnfDiagram-VRR7QNED-cQAiKjYG.js} +1 -1
- package/static/assets/{arc-CXd0xnDj.js → arc-C4ZlEQ3S.js} +1 -1
- package/static/assets/{architectureDiagram-ZJ3FMSHR-5QfnAz_Q.js → architectureDiagram-ZJ3FMSHR-C_31NGnA.js} +1 -1
- package/static/assets/{blockDiagram-677ZJIJ3-ByICFNDb.js → blockDiagram-677ZJIJ3-DDQQ2PAJ.js} +1 -1
- package/static/assets/{c4Diagram-LMCZKHZV-C5a0Ubby.js → c4Diagram-LMCZKHZV-BPbCZxQJ.js} +1 -1
- package/static/assets/channel-CufY_jLp.js +1 -0
- package/static/assets/{chunk-2Q5K7J3B-UucADWxr.js → chunk-2Q5K7J3B-B3LoONkc.js} +1 -1
- package/static/assets/{chunk-32BRIVSS-WAe17d3N.js → chunk-32BRIVSS-Buim75a9.js} +1 -1
- package/static/assets/{chunk-5VM5RSS4-BQBMOb0f.js → chunk-5VM5RSS4-BjHqxxbw.js} +1 -1
- package/static/assets/{chunk-EX3LRPZG-BzaET0n4.js → chunk-EX3LRPZG-Dn0_qUTW.js} +1 -1
- package/static/assets/{chunk-JWPE2WC7-D_tBvbZg.js → chunk-JWPE2WC7-DpYEF0BV.js} +1 -1
- package/static/assets/{chunk-MOJQB5TN-CsYb9JBc.js → chunk-MOJQB5TN-CbNDWijQ.js} +1 -1
- package/static/assets/{chunk-RYQCIY6F-D5xRlnc7.js → chunk-RYQCIY6F-DOhXYkGG.js} +1 -1
- package/static/assets/{chunk-V7JOEXUC-y0cEgq6c.js → chunk-V7JOEXUC-CfASUuSJ.js} +1 -1
- package/static/assets/{chunk-VR4S4FIN-CSXJHPXT.js → chunk-VR4S4FIN-vgVNnFtp.js} +1 -1
- package/static/assets/{chunk-XXDRQBXY-QFNFGLYZ.js → chunk-XXDRQBXY-CVjoYUTB.js} +1 -1
- package/static/assets/classDiagram-OUVF2IWQ-CVQD_QZa.js +1 -0
- package/static/assets/classDiagram-v2-EOCWNBFH-CVQD_QZa.js +1 -0
- package/static/assets/{cose-bilkent-JH36ORCC-ValIntpZ.js → cose-bilkent-JH36ORCC-EqjuuBpY.js} +1 -1
- package/static/assets/{cynefin-VYW2F7L2-BgHVKyAR.js → cynefin-VYW2F7L2-DioyO_Cm.js} +1 -1
- package/static/assets/{cynefinDiagram-TSTJHNR4-LIZzpkLM.js → cynefinDiagram-TSTJHNR4-CuPa5A8w.js} +1 -1
- package/static/assets/{dagre-VKFMJZFB-DY9b36A5.js → dagre-VKFMJZFB-MIf1Dtbk.js} +1 -1
- package/static/assets/{diagram-FQU43EPY-aeuYUUxe.js → diagram-FQU43EPY-5OBJqg6R.js} +1 -1
- package/static/assets/{diagram-G47NLZAW-BWwcYYCF.js → diagram-G47NLZAW-B2ZGPKp3.js} +1 -1
- package/static/assets/{diagram-NH7WQ7WH-BObFlRPR.js → diagram-NH7WQ7WH-B5YEN-B-.js} +1 -1
- package/static/assets/{diagram-OA4YK3LP-BUnc8m1-.js → diagram-OA4YK3LP-BXg_CQ2m.js} +1 -1
- package/static/assets/{diagram-WEI45ONY-DsNxBIuJ.js → diagram-WEI45ONY-DVbqehF6.js} +1 -1
- package/static/assets/{ebnfDiagram-CCIWWBDH-DDL2Id1r.js → ebnfDiagram-CCIWWBDH-hmnXWJuZ.js} +1 -1
- package/static/assets/{erDiagram-Q63AITRT-DZZcG2QU.js → erDiagram-Q63AITRT-CLYATrvi.js} +1 -1
- package/static/assets/{flowDiagram-23GEKE2U-C6GvkBFb.js → flowDiagram-23GEKE2U-cs1YvuVH.js} +1 -1
- package/static/assets/{ganttDiagram-NO4QXBWP-C7ydCTnF.js → ganttDiagram-NO4QXBWP-CH5rfioY.js} +1 -1
- package/static/assets/{gitGraphDiagram-IHSO6WYX-C60P6bwf.js → gitGraphDiagram-IHSO6WYX-DjAOB5L7.js} +1 -1
- package/static/assets/{index-CfGMfAGO.js → index-B4Y8oBUt.js} +4 -4
- package/static/assets/{infoDiagram-FWYZ7A6U-CnH7Q2nX.js → infoDiagram-FWYZ7A6U-De2-PVxo.js} +1 -1
- package/static/assets/{ishikawaDiagram-FXEZZL3T-BNEvOQYX.js → ishikawaDiagram-FXEZZL3T-CDikxFR6.js} +1 -1
- package/static/assets/{journeyDiagram-5HDEW3XC-BOwGZTfX.js → journeyDiagram-5HDEW3XC-Bo6isp1w.js} +1 -1
- package/static/assets/{kanban-definition-HUTT4EX6-BEfHVl-l.js → kanban-definition-HUTT4EX6-jM1azdC7.js} +1 -1
- package/static/assets/{linear-BOHPNxBi.js → linear-CUk0_yzf.js} +1 -1
- package/static/assets/{mindmap-definition-LN4V7U3C-B84wfT_w.js → mindmap-definition-LN4V7U3C-l-cgg6PS.js} +1 -1
- package/static/assets/{pegDiagram-2B236MQR-BgEa7yRL.js → pegDiagram-2B236MQR-B-8nGU3G.js} +1 -1
- package/static/assets/{pieDiagram-ENE6RG2P-DgA9LQlc.js → pieDiagram-ENE6RG2P-Di1V86Ag.js} +1 -1
- package/static/assets/{quadrantDiagram-ABIIQ3AL-C5K_r0Mx.js → quadrantDiagram-ABIIQ3AL-BknVxapP.js} +1 -1
- package/static/assets/{railroadDiagram-RFXS5EU6-Ckh_mLXW.js → railroadDiagram-RFXS5EU6-D7Vohrlg.js} +1 -1
- package/static/assets/{requirementDiagram-TGXJPOKE-CtII7Obm.js → requirementDiagram-TGXJPOKE-BlFXj6Li.js} +1 -1
- package/static/assets/{sankeyDiagram-HTMAVEWB-DdkySwbL.js → sankeyDiagram-HTMAVEWB-Ck_A1wxA.js} +1 -1
- package/static/assets/{sequenceDiagram-DBY2YBRQ-DEIBg3kY.js → sequenceDiagram-DBY2YBRQ-D4mu07Ol.js} +1 -1
- package/static/assets/{sizeCapture-X5ZJPWSS-D2MM9f6M.js → sizeCapture-X5ZJPWSS-BwempBoZ.js} +1 -1
- package/static/assets/{stateDiagram-2N3HPSRC-DHmJsAte.js → stateDiagram-2N3HPSRC-DSh1YMwZ.js} +1 -1
- package/static/assets/stateDiagram-v2-6OUMAXLB-DrMkFhr2.js +1 -0
- package/static/assets/{swimlanes-5IMT3BWC-BBPvUQvZ.js → swimlanes-5IMT3BWC-4EE1KLA5.js} +2 -2
- package/static/assets/swimlanesDiagram-G3AALYLV-DoZQ3XaY.js +8 -0
- package/static/assets/{timeline-definition-FHXFAJF6-CG_Q0YTD.js → timeline-definition-FHXFAJF6-DVZepywq.js} +1 -1
- package/static/assets/{vennDiagram-L72KCM5P-DlFSe4is.js → vennDiagram-L72KCM5P-YqnYvNJT.js} +1 -1
- package/static/assets/{wardleyDiagram-EHGQE667-BTlvtp2f.js → wardleyDiagram-EHGQE667-Dc6PvPZE.js} +1 -1
- package/static/assets/{xychartDiagram-FW5EYKEG-Bm14zLxj.js → xychartDiagram-FW5EYKEG-DLbY5Kg1.js} +1 -1
- package/static/index.html +1 -1
- package/static/assets/channel-CrYjYK1f.js +0 -1
- package/static/assets/classDiagram-OUVF2IWQ-DoLCCqXD.js +0 -1
- package/static/assets/classDiagram-v2-EOCWNBFH-DoLCCqXD.js +0 -1
- package/static/assets/stateDiagram-v2-6OUMAXLB-B6sumoG6.js +0 -1
- package/static/assets/swimlanesDiagram-G3AALYLV-DH2ts1oT.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.19",
|
|
6813
6813
|
description: "Compound Context Studio \u2014 self-hosted team context authoring for CO2 projects",
|
|
6814
6814
|
type: "module",
|
|
6815
6815
|
license: "MIT",
|
|
@@ -21875,6 +21875,7 @@ import { spawn as spawn2 } from "child_process";
|
|
|
21875
21875
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
21876
21876
|
import { existsSync as existsSync8, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "fs";
|
|
21877
21877
|
import { request as httpRequest2 } from "http";
|
|
21878
|
+
import { createServer as createNetServer } from "net";
|
|
21878
21879
|
import { join as join9 } from "path";
|
|
21879
21880
|
function svcError6(code, status) {
|
|
21880
21881
|
return Object.assign(new Error(code), { code, status });
|
|
@@ -21896,8 +21897,22 @@ process.once("SIGINT", () => {
|
|
|
21896
21897
|
killSpawnedApps();
|
|
21897
21898
|
process.exit(130);
|
|
21898
21899
|
});
|
|
21899
|
-
function
|
|
21900
|
-
|
|
21900
|
+
function persistGuardToken(db, projectId, token) {
|
|
21901
|
+
try {
|
|
21902
|
+
db.update(cicdSetups).set({ guardToken: token, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq(cicdSetups.projectId, projectId)).run();
|
|
21903
|
+
} catch {
|
|
21904
|
+
}
|
|
21905
|
+
}
|
|
21906
|
+
function cicdGuardTokenValid(db, projectId, token) {
|
|
21907
|
+
if (!token) return false;
|
|
21908
|
+
const mem = guardTokens.get(projectId);
|
|
21909
|
+
if (mem !== void 0) return mem === token;
|
|
21910
|
+
const row = db.select({ t: cicdSetups.guardToken }).from(cicdSetups).where(eq(cicdSetups.projectId, projectId)).get();
|
|
21911
|
+
if (row?.t) {
|
|
21912
|
+
guardTokens.set(projectId, row.t);
|
|
21913
|
+
return row.t === token;
|
|
21914
|
+
}
|
|
21915
|
+
return false;
|
|
21901
21916
|
}
|
|
21902
21917
|
function resolveCicdApp(db, workspacePath, projectId) {
|
|
21903
21918
|
const project = db.select().from(projects).where(eq(projects.id, projectId)).get();
|
|
@@ -21930,6 +21945,49 @@ function probeApp(port, timeoutMs = 1e3) {
|
|
|
21930
21945
|
req.end();
|
|
21931
21946
|
});
|
|
21932
21947
|
}
|
|
21948
|
+
function portFree(port) {
|
|
21949
|
+
return new Promise((resolve5) => {
|
|
21950
|
+
const srv = createNetServer();
|
|
21951
|
+
srv.once("error", () => resolve5(false));
|
|
21952
|
+
srv.once("listening", () => srv.close(() => resolve5(true)));
|
|
21953
|
+
srv.listen(port, "127.0.0.1");
|
|
21954
|
+
});
|
|
21955
|
+
}
|
|
21956
|
+
function ephemeralPort() {
|
|
21957
|
+
return new Promise((resolve5) => {
|
|
21958
|
+
const srv = createNetServer();
|
|
21959
|
+
srv.once("error", () => resolve5(0));
|
|
21960
|
+
srv.listen(0, "127.0.0.1", () => {
|
|
21961
|
+
const addr = srv.address();
|
|
21962
|
+
const p = addr && typeof addr === "object" ? addr.port : 0;
|
|
21963
|
+
srv.close(() => resolve5(p));
|
|
21964
|
+
});
|
|
21965
|
+
});
|
|
21966
|
+
}
|
|
21967
|
+
async function pickFreePort(preferred, avoid) {
|
|
21968
|
+
if (preferred && preferred !== avoid && await portFree(preferred)) return preferred;
|
|
21969
|
+
for (let i = 0; i < 20; i++) {
|
|
21970
|
+
const p = await ephemeralPort();
|
|
21971
|
+
if (p && p !== avoid) return p;
|
|
21972
|
+
}
|
|
21973
|
+
return preferred || DEFAULT_CICD_APP_PORT;
|
|
21974
|
+
}
|
|
21975
|
+
function persistConfigPort(location, newPort) {
|
|
21976
|
+
if (newPort === location.port) return;
|
|
21977
|
+
const configPath = join9(location.appDir, "cicd.config.json");
|
|
21978
|
+
let config = {};
|
|
21979
|
+
try {
|
|
21980
|
+
config = JSON.parse(readFileSync6(configPath, "utf8"));
|
|
21981
|
+
} catch {
|
|
21982
|
+
}
|
|
21983
|
+
config.port = newPort;
|
|
21984
|
+
try {
|
|
21985
|
+
writeFileSync5(configPath, `${JSON.stringify(config, null, 2)}
|
|
21986
|
+
`);
|
|
21987
|
+
} catch {
|
|
21988
|
+
}
|
|
21989
|
+
location.port = newPort;
|
|
21990
|
+
}
|
|
21933
21991
|
function toStatus2(location, running, projectId) {
|
|
21934
21992
|
return {
|
|
21935
21993
|
present: location.present,
|
|
@@ -21948,26 +22006,13 @@ async function getCicdAppStatus(db, workspacePath, projectId) {
|
|
|
21948
22006
|
async function startCicdApp(db, workspacePath, projectId, studioPort, port) {
|
|
21949
22007
|
const location = resolveCicdApp(db, workspacePath, projectId);
|
|
21950
22008
|
if (!location.present) throw svcError6("CICD_APP_MISSING", 404);
|
|
21951
|
-
const
|
|
21952
|
-
if (
|
|
21953
|
-
|
|
21954
|
-
let config = {};
|
|
21955
|
-
try {
|
|
21956
|
-
config = JSON.parse(readFileSync6(configPath, "utf8"));
|
|
21957
|
-
} catch {
|
|
21958
|
-
}
|
|
21959
|
-
config.port = targetPort;
|
|
21960
|
-
try {
|
|
21961
|
-
writeFileSync5(configPath, `${JSON.stringify(config, null, 2)}
|
|
21962
|
-
`);
|
|
21963
|
-
} catch {
|
|
21964
|
-
}
|
|
21965
|
-
location.port = targetPort;
|
|
21966
|
-
}
|
|
21967
|
-
if (await probeApp(targetPort)) {
|
|
21968
|
-
location.port = targetPort;
|
|
22009
|
+
const savedPort = port ?? location.port;
|
|
22010
|
+
if (await probeApp(savedPort)) {
|
|
22011
|
+
persistConfigPort(location, savedPort);
|
|
21969
22012
|
return toStatus2(location, true, projectId);
|
|
21970
22013
|
}
|
|
22014
|
+
const targetPort = port !== void 0 ? port : await pickFreePort(savedPort, studioPort);
|
|
22015
|
+
persistConfigPort(location, targetPort);
|
|
21971
22016
|
const existing = spawnedApps.get(projectId);
|
|
21972
22017
|
if (existing && existing.exitCode === null) {
|
|
21973
22018
|
try {
|
|
@@ -21993,20 +22038,22 @@ async function startCicdApp(db, workspacePath, projectId, studioPort, port) {
|
|
|
21993
22038
|
child.on("error", () => {
|
|
21994
22039
|
spawnedApps.delete(projectId);
|
|
21995
22040
|
guardTokens.delete(projectId);
|
|
22041
|
+
persistGuardToken(db, projectId, null);
|
|
21996
22042
|
});
|
|
21997
22043
|
child.on("exit", () => {
|
|
21998
22044
|
spawnedApps.delete(projectId);
|
|
21999
22045
|
guardTokens.delete(projectId);
|
|
22046
|
+
persistGuardToken(db, projectId, null);
|
|
22000
22047
|
});
|
|
22001
22048
|
spawnedApps.set(projectId, child);
|
|
22002
|
-
db.update(cicdSetups).set({ appPort: location.port, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq(cicdSetups.projectId, projectId)).run();
|
|
22049
|
+
db.update(cicdSetups).set({ appPort: location.port, guardToken: token, updatedAt: (/* @__PURE__ */ new Date()).toISOString() }).where(eq(cicdSetups.projectId, projectId)).run();
|
|
22003
22050
|
for (let attempt = 0; attempt < 20; attempt++) {
|
|
22004
22051
|
if (await probeApp(location.port, 500)) return toStatus2(location, true, projectId);
|
|
22005
22052
|
await new Promise((r) => setTimeout(r, 250));
|
|
22006
22053
|
}
|
|
22007
22054
|
return toStatus2(location, false, projectId);
|
|
22008
22055
|
}
|
|
22009
|
-
function stopCicdApp(projectId) {
|
|
22056
|
+
function stopCicdApp(db, projectId) {
|
|
22010
22057
|
const child = spawnedApps.get(projectId);
|
|
22011
22058
|
if (!child) return false;
|
|
22012
22059
|
try {
|
|
@@ -22015,6 +22062,7 @@ function stopCicdApp(projectId) {
|
|
|
22015
22062
|
}
|
|
22016
22063
|
spawnedApps.delete(projectId);
|
|
22017
22064
|
guardTokens.delete(projectId);
|
|
22065
|
+
persistGuardToken(db, projectId, null);
|
|
22018
22066
|
return true;
|
|
22019
22067
|
}
|
|
22020
22068
|
|
|
@@ -22701,6 +22749,7 @@ function pollCicdTracking(db, workspacePath, setupId, kind, notifyUserId) {
|
|
|
22701
22749
|
relatedEntityType: "cicd_setup",
|
|
22702
22750
|
relatedEntityId: setupId
|
|
22703
22751
|
});
|
|
22752
|
+
stopCicdApp(db, setup.projectId);
|
|
22704
22753
|
try {
|
|
22705
22754
|
runProjectGit(
|
|
22706
22755
|
projectDir2,
|
|
@@ -22854,7 +22903,7 @@ function cicdRoutes(db, workspacePath, studioPort) {
|
|
|
22854
22903
|
const user = c.get("user");
|
|
22855
22904
|
try {
|
|
22856
22905
|
const result = requestPlan(db, workspacePath, projectId, user.id);
|
|
22857
|
-
stopCicdApp(projectId);
|
|
22906
|
+
stopCicdApp(db, projectId);
|
|
22858
22907
|
return c.json(result, 201);
|
|
22859
22908
|
} catch (e) {
|
|
22860
22909
|
return errorResponse3(c, e);
|
|
@@ -22910,7 +22959,7 @@ function cicdRoutes(db, workspacePath, studioPort) {
|
|
|
22910
22959
|
const projectId = parseProjectId(c);
|
|
22911
22960
|
if (projectId === null) return c.json({ code: "INVALID_ID" }, 400);
|
|
22912
22961
|
try {
|
|
22913
|
-
stopCicdApp(projectId);
|
|
22962
|
+
stopCicdApp(db, projectId);
|
|
22914
22963
|
return c.json(await getCicdAppStatus(db, workspacePath, projectId));
|
|
22915
22964
|
} catch (e) {
|
|
22916
22965
|
return errorResponse3(c, e);
|
|
@@ -22943,7 +22992,7 @@ function cicdGuardRoutes(db, workspacePath) {
|
|
|
22943
22992
|
if (Number.isNaN(projectId)) return c.json({ code: "INVALID_ID" }, 400);
|
|
22944
22993
|
const bearer = c.req.header("authorization")?.replace(/^Bearer\s+/i, "");
|
|
22945
22994
|
const token = c.req.query("token") ?? bearer;
|
|
22946
|
-
if (!cicdGuardTokenValid(projectId, token) && !c.get("user")) {
|
|
22995
|
+
if (!cicdGuardTokenValid(db, projectId, token) && !c.get("user")) {
|
|
22947
22996
|
return c.json({ code: "UNAUTHORIZED" }, 401);
|
|
22948
22997
|
}
|
|
22949
22998
|
const rawVersion = c.req.query("versionId");
|
package/package.json
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"name": "co2-skills",
|
|
13
13
|
"source": "./",
|
|
14
14
|
"description": "Compound Context Skills — generates data models, HTML mockups, technical specifications, test specifications, and orchestrates full-stack development from Agile user stories.",
|
|
15
|
-
"version": "1.
|
|
15
|
+
"version": "1.8.2",
|
|
16
16
|
"author": {
|
|
17
17
|
"name": "Rashidee M.Rashid"
|
|
18
18
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "co2-skills",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.2",
|
|
4
4
|
"description": "Compound Context Skills — an opinionated context engineering methodology for AI-driven software development. Generates data models, HTML mockups, technical specifications, test specifications, and orchestrates full-stack development from Agile user stories.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Rashidee M.Rashid"
|
|
@@ -21,12 +21,12 @@ description: >
|
|
|
21
21
|
copy, docker run or kubectl apply, or none). Controls are disabled for any application that
|
|
22
22
|
has never completed a conductor-develop workflow run (checked against the studio's
|
|
23
23
|
/api/cicd-guard endpoint using the spawn-env token). Health/port probing marks apps already
|
|
24
|
-
running — even ones started outside this app. There is NO version selector
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
images are tagged <repo>/<app
|
|
28
|
-
Dockerfiles materialized before Build when missing; Build never
|
|
29
|
-
|
|
24
|
+
running — even ones started outside this app. There is NO version selector and there are NO
|
|
25
|
+
Build All / Start All / Stop All buttons — every control operates per application on the
|
|
26
|
+
current working tree, and the plan's commands run verbatim (no {VERSION} or other token
|
|
27
|
+
substitution) — docker images are tagged <repo>/<app>:latest when the plan's Docker section
|
|
28
|
+
is active (managed Dockerfiles materialized before Build when missing; Build never
|
|
29
|
+
pushes). When the approved plan declares an `## Infrastructure` list, the
|
|
30
30
|
main page shows an "Infrastructure" link that opens a separate status-only page listing every
|
|
31
31
|
supporting 3rd-party service with a live TCP-reachability indicator and a console link. Writes cicd/CICD_GENERATION.md with `**Status**:` IN PROGRESS →
|
|
32
32
|
COMPLETED so the studio watcher detects completion. Idempotent — re-running regenerates
|
|
@@ -66,18 +66,20 @@ Write `cicd/CICD_GENERATION.md`:
|
|
|
66
66
|
Read `references/cicd-app-template.md` for the complete file templates. Produce:
|
|
67
67
|
|
|
68
68
|
1. `cicd/app/cicd.config.json` — from the plan's tables:
|
|
69
|
-
`{ "port": <CI/CD app port>, "docker": { "registry" } | null, "apps": [{ "name", "folder",
|
|
69
|
+
`{ "port": <CI/CD app port>, "generatedAt": "<current ISO-8601 UTC>", "docker": { "registry" } | null, "apps": [{ "name", "folder",
|
|
70
70
|
"method", "port", "build", "start", "stop", "deployMethod", "deploy", "health", "image",
|
|
71
71
|
"dockerfile", "configs" }], "infra": [{ "id", "name",
|
|
72
|
-
"host", "port", "url", "description" }] }` — `
|
|
72
|
+
"host", "port", "url", "description" }] }` — `generatedAt` MUST be the actual time you generate
|
|
73
|
+
this file (a fresh value on EVERY run — never copy a previous one): the running server pins the
|
|
74
|
+
value it booted with and shows a "restart required" banner when a later regeneration moves it
|
|
75
|
+
forward, so an in-place-replaced-but-still-running app is self-diagnosing. `docker.registry` comes from the plan's
|
|
73
76
|
`## Docker` section (`null` when the plan says not dockerized); per-app `image` is that
|
|
74
77
|
section's Image column (`null` for non-dockerized apps) and `dockerfile` is
|
|
75
78
|
`{ "path": "Dockerfile", "content": "<body>" }` for a `managed` Dockerfile from the plan's
|
|
76
79
|
`## Dockerfiles` section, or `{ "path": "Dockerfile", "content": null }` for an `existing`
|
|
77
80
|
one (`null` entirely when the app has no image). Copy managed Dockerfile bodies verbatim.
|
|
78
|
-
Commands
|
|
79
|
-
|
|
80
|
-
semver, `v` prefix stripped, fallback `latest`) — never via any user-facing selector. `deployMethod` is the plan's Deploy Method column
|
|
81
|
+
Commands are copied verbatim from the plan and run as-is — there is NO `{VERSION}` token
|
|
82
|
+
or any other placeholder substitution. `deployMethod` is the plan's Deploy Method column
|
|
81
83
|
(`none|web-server|docker|kubernetes`) and `deploy` its command (`null` when the method is
|
|
82
84
|
`none`). The `infra` array comes from the plan's `## Infrastructure` table (one entry per
|
|
83
85
|
row); `url` is `null` when the Console URL cell is `-`. Emit `"infra": []` when the plan's
|
|
@@ -164,22 +166,15 @@ port, application count) and STOP — the studio starts/embeds the app.
|
|
|
164
166
|
(`<project>/<folder>`) as working directory — command paths are relative to the application
|
|
165
167
|
folder (e.g. `java -jar target\app.jar`), never to the project root. Deploy renders only
|
|
166
168
|
when the plan assigned a deploy method other than `none`.
|
|
167
|
-
- **NO version
|
|
168
|
-
any other version choice
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
every 30 s; latest first), minus its `v` prefix, falling back to `latest` when the studio
|
|
173
|
-
is unreachable. Never render a UI control for it and never check out code.
|
|
169
|
+
- **NO version UI, NO All-apps buttons, NO command substitution**: the page carries NO
|
|
170
|
+
version dropdown or any other version choice, and NO Build All / Start All / Stop All
|
|
171
|
+
buttons — Build/Start/Stop/Deploy are per-application only and always operate on the
|
|
172
|
+
CURRENT working tree, running the plan's commands verbatim (no `{VERSION}` or other token
|
|
173
|
+
substitution). Never render a version control and never check out code.
|
|
174
174
|
- **Dockerfile materialization**: before Build, when an app's `dockerfile.content` is set and
|
|
175
175
|
no file exists at its path in the application folder, the server writes it (never
|
|
176
176
|
overwriting an existing Dockerfile — mirror of the managed-config rule). Build never
|
|
177
177
|
pushes an image; pushing belongs to a `docker` deploy command.
|
|
178
|
-
- **All-apps controls**: the top bar carries **Build All**, **Start All** and **Stop All**
|
|
179
|
-
buttons. Build All builds eligible apps SEQUENTIALLY (one at a time — honest logs, no CPU
|
|
180
|
-
storm); Start All starts every eligible stopped app; Stop All stops every running app.
|
|
181
|
-
Eligible = develop-gate not failed, not busy, and (for build/start) not already running;
|
|
182
|
-
ineligible apps are skipped silently — per-tab badges show progress.
|
|
183
178
|
- Develop gate: controls stay disabled for an application that never completed a
|
|
184
179
|
conductor-develop run — checked via `${CO2_STUDIO_URL}/api/cicd-guard/${CO2_PROJECT_ID}`
|
|
185
180
|
with the CO2_GUARD_TOKEN from the spawn env (refreshed every 30 s, last answer cached).
|